/*  Booth channel visuals.

    Everything here is inline SVG plus CSS. No chart library, on purpose:

      - the rotation replaces the page every 8-15 seconds, and a JS charting
        library would need tearing down and re-initialising on every one of those,
        against a Blazor circuit that is already re-rendering the DOM;
      - a kiosk may have no outbound network, so a CDN is not available and even a
        vendored bundle is weight for shapes that are a few hundred bytes of SVG;
      - sizes here are em off .kiosk, so the visuals rescale with the panel exactly
        like the type does. A canvas would need re-measuring per resolution.

    Animation is CSS keyframes with no JS trigger. Blazor builds fresh DOM for each
    page, so the animation simply plays on mount — there is nothing to reset.  */

/* ── palette ──────────────────────────────────────────────────────────────── */

.kiosk {
    /*  Semantic first: these mean a thing and must stay stable across pages, so a
        colour learned on one screen still reads on the next. */
    --pdag-p: #F59E0B;   /* Pichhda      */
    --pdag-d: #A78BFA;   /* Dalit        */
    --pdag-a: #2DD4BF;   /* Alpasankhyak */
    --pdag-g: #60A5FA;   /* General      */

    --male:   #38BDF8;
    --female: #FB7185;
    --other:  #FBBF24;

    --rel-hindu:   #FB923C;
    --rel-muslim:  #34D399;
    --rel-other:   #C084FC;
    --rel-unknown: #8e96a6;

    --track: rgba(255, 255, 255, .09);
    --ink-dim: #94A3B8;

    /*  Per-page accent, overridden below. Gives each screen its own hue so the
        rotation reads as distinct pages rather than one page with new numbers. */
    --accent: #4ADE80;
    --accent-2: #22D3EE;
}

.k-page[data-p="overview"]  { --accent:#4ADE80; --accent-2:#22D3EE; }
.k-page[data-p="gender"]    { --accent:#38BDF8; --accent-2:#FB7185; }
.k-page[data-p="religion"]  { --accent:#FB923C; --accent-2:#34D399; }
.k-page[data-p="cast"]      { --accent:#FBBF24; --accent-2:#F59E0B; }
.k-page[data-p="pdag"]      { --accent:#A78BFA; --accent-2:#60A5FA; }
.k-page[data-p="lifestage"] { --accent:#2DD4BF; --accent-2:#4ADE80; }
.k-page[data-p="decade"]    { --accent:#22D3EE; --accent-2:#38BDF8; }
.k-page[data-p="age"]       { --accent:#818CF8; --accent-2:#C084FC; }
.k-page[data-p="household"] { --accent:#F472B6; --accent-2:#FB923C; }
.k-page[data-p="family"]    { --accent:#34D399; --accent-2:#2DD4BF; }
.k-page[data-p="couples"]   { --accent:#FB7185; --accent-2:#C084FC; }
.k-page[data-p="surnames"]  { --accent:#FCD34D; --accent-2:#FB923C; }
.k-page[data-p="rolls"]     { --accent:#60A5FA; --accent-2:#818CF8; }
.k-page[data-p="result"]    { --accent:#F97316; --accent-2:#DC2626; }

/*  The Form 20 screens on the Election channel. Warmer than the roll-derived
    pages beside them, so a glance tells you whether you are looking at what
    was counted or at who is registered now. */
.k-page[data-p="ac-result"]   { --accent:#F97316; --accent-2:#DC2626; }
.k-page[data-p="ac-strength"] { --accent:#22C55E; --accent-2:#EAB308; }
.k-page[data-p="pol-profile"]  { --accent:#A78BFA; --accent-2:#60A5FA; }
.k-page[data-p="pol-pdag"]     { --accent:#F59E0B; --accent-2:#A78BFA; }
.k-page[data-p="pol-religion"] { --accent:#FB923C; --accent-2:#34D399; }
.k-page[data-p="pol-roll"]     { --accent:#2DD4BF; --accent-2:#38BDF8; }
.k-page[data-p="ac-overview"]  { --accent:#4ADE80; --accent-2:#22D3EE; }
.k-page[data-p="ac-quality"]   { --accent:#22D3EE; --accent-2:#818CF8; }
.k-page[data-p="ac-gender"]    { --accent:#38BDF8; --accent-2:#FB7185; }
.k-page[data-p="ac-religion"]  { --accent:#FB923C; --accent-2:#34D399; }
.k-page[data-p="ac-lifestage"] { --accent:#2DD4BF; --accent-2:#4ADE80; }
.k-page[data-p="ac-decade"]    { --accent:#22D3EE; --accent-2:#38BDF8; }
.k-page[data-p="ac-age"]       { --accent:#818CF8; --accent-2:#C084FC; }
.k-page[data-p="ac-household"] { --accent:#F472B6; --accent-2:#FB923C; }
.k-page[data-p="ac-family"]    { --accent:#34D399; --accent-2:#2DD4BF; }
.k-page[data-p="ac-couples"]   { --accent:#FB7185; --accent-2:#C084FC; }
.k-page[data-p="ac-rolls"]     { --accent:#60A5FA; --accent-2:#818CF8; }
.k-page[data-p="cast-bucket"]  { --accent:#FBBF24; --accent-2:#A78BFA; }
.k-page[data-p="cast-top"]     { --accent:#FCD34D; --accent-2:#F59E0B; }
.k-page[data-p="cast-surname"] { --accent:#F59E0B; --accent-2:#FB923C; }
.k-page[data-p="cast-names"]   { --accent:#FCD34D; --accent-2:#FB923C; }

/* ── page scaffold ────────────────────────────────────────────────────────── */

/*  Every booth page is a grid of bands. The band count varies per page, which is
    what stops the rotation looking like one template with different numbers. */
.k-page {
    min-height: 0;
    height: 100%;
    display: grid;
    gap: .7em;
    /*  minmax(0, ..) on both axes. A bare 1fr track is minmax(auto, 1fr), whose
        auto floor lets a panel's content push the track past its share — which
        silently drove the bottom row of every page below the canvas. */
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
}

.k-panel {
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /*  No gap: flex gap is Chrome 84 and the TV is Chrome 66. The spacing lives
        in a successor-margin rule at end of file — safe here because every
        absolutely-positioned visual (.k-gauge, .k-glbl, .k-track > span) sits
        inside an in-flow wrapper, never as a direct panel child. */
    padding: .7em .85em;
    /*  The panel is the boundary. Anything that does not fit is a layout bug to be
        seen and fixed, not something to let spill over its neighbour. */
    overflow: hidden;
    border-radius: .6em;
    background: linear-gradient(160deg, rgba(255,255,255,.055), rgba(255,255,255,.02));
    border: .07em solid rgba(255, 255, 255, .07);
}

.k-panel.tight { padding: .5em .6em; }

/*  A panel that leads with its accent, for the one thing on the page that matters
    most. Used sparingly — at most one or two per screen. */
.k-panel.lead {
    background: linear-gradient(160deg,
        color-mix(in srgb, var(--accent) 20%, transparent),
        rgba(255,255,255,.02));
    border-color: color-mix(in srgb, var(--accent) 38%, transparent);
}

/*  color-mix voids the whole declaration where unsupported, so the plain values
    above stay as the floor and these only upgrade it. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
    .k-panel.lead { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.22); }
}

.k-cap {
    /*  Never shrink. Flex children shrink by default, so an over-full panel
        silently ate its caption instead of showing an overflow: on the Form 20
        result screen "Won this booth · 2022" measured 0px tall while the figure
        below it stayed full size — the label that says what the number MEANS was
        the first thing to go. Now it holds and a too-tall panel is visible as
        the layout bug it is. */
    flex: 0 0 auto;
    font-size: .82em;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--ink-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.k-fig {
    font-size: 2.1em;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.k-fig.sm  { font-size: 1.55em; }
.k-fig.lg  { font-size: 3.1em; }
/*  3.5em, not 4.2em. At 4.2 the hero figure alone was 101px and pushed the
    Voter Roll "Active" panel 15px past its box — with the caption no longer able
    to absorb it by collapsing, that became visible clipping. Still the largest
    figure on any screen. */
.k-fig.hero{ font-size: 3.5em; }
.k-fig.acc { color: var(--accent); }

.k-note { font-size: .85em; color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* ── animation ────────────────────────────────────────────────────────────── */

@keyframes k-rise   { from { opacity:0; transform:translateY(.6em); } to { opacity:1; transform:none; } }
@keyframes k-grow-w { from { width: 0; } }
@keyframes k-sweep  { from { stroke-dashoffset: var(--len); } }
@keyframes k-grow-h { from { transform: scaleY(0); } }
@keyframes k-pop    { from { opacity:0; transform:scale(.85); } to { opacity:1; transform:none; } }

.k-panel { animation: k-rise .5s both; }
/*  Staggered so the screen assembles rather than snapping in. Capped low: past a
    few hundred ms the rotation is already partway through its dwell. */
.k-page > *:nth-child(1) { animation-delay: .02s }
.k-page > *:nth-child(2) { animation-delay: .06s }
.k-page > *:nth-child(3) { animation-delay: .10s }
.k-page > *:nth-child(4) { animation-delay: .14s }
.k-page > *:nth-child(5) { animation-delay: .18s }
.k-page > *:nth-child(6) { animation-delay: .22s }
.k-page > *:nth-child(7) { animation-delay: .26s }
.k-page > *:nth-child(8) { animation-delay: .30s }
.k-page > *:nth-child(n+9) { animation-delay: .34s }

@media (prefers-reduced-motion: reduce) {
    .k-panel, .k-arc, .k-col > i, .k-bar > i, .k-waffle b { animation: none !important; }
}

/* ── gauge / donut (SVG) ──────────────────────────────────────────────────── */

/*  The gauge is an SVG arc with an HTML label sitting over it. Razor reserves
    <text> as a control tag, and HTML text scales in em with the rest of the kiosk
    rather than being pinned to the viewBox. */
/*  flex:1 with a zero basis, so the gauge takes the space left over rather than
    claiming its intrinsic SVG height and growing the panel. */
.k-gwrap { position: relative; display: grid; place-items: center; min-height: 0; flex: 1 1 0; }

/*  Absolutely positioned, not just height:100%.

    An <svg> with a viewBox carries an intrinsic aspect ratio. As an in-flow child
    of a grid whose area height is indefinite during layout, height:100% resolves to
    auto and the aspect ratio wins: in a 407px-wide panel the 100x56 viewBox forced
    228px against a 138px box, overflowing every gauge panel by 74px.

    Against the relatively-positioned wrap, top:0; right:is; bottom:a; left:definite;
 inset: 0 is a definite containing block,
    so the SVG takes the box it is given and preserveAspectRatio letterboxes the arc
    inside it. */
.k-gauge {
    position: absolute;
    top:0; right:0; bottom:0; left:0;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.k-arc-track { fill: none; stroke: var(--track); stroke-linecap: round; }

.k-arc {
    fill: none;
    stroke-linecap: round;
    animation: k-sweep 1s cubic-bezier(.22,.61,.36,1) both;
}

.k-slice {
    fill: none;
    animation: k-pop .55s both;
    transform-origin: center;
}

.k-glbl {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
    line-height: 1.15;
    pointer-events: none;
    max-width: 92%;
}

/*  A half-donut's label belongs in the well under the arc, not at the centre of
    the circle the arc is a slice of. */
.k-gwrap.half .k-glbl { bottom: 2%; }
.k-gwrap.full .k-glbl { top: 50%; transform: translate(-50%, -50%); }

.k-glbl .v {
    font-size: 1.75em;
    font-weight: 800;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.k-glbl .l {
    font-size: .8em;
    color: var(--ink-dim);
    margin-top: .1em;
    /*  Two lines at most: a long caption must not grow the gauge and push the
        panel past its grid row. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── ranked horizontal bars ───────────────────────────────────────────────── */

/*  Rows share the panel rather than each claiming a fixed height. Eight bars and
    four bars both fill the same panel exactly, so the page never has to know in
    advance how many rows the data will produce. */
.k-bars {
    display: grid;
    gap: .3em;
    align-content: stretch;
    grid-auto-rows: minmax(0, 1fr);
    min-height: 0;
    flex: 1 1 0;
}

/*  Three columns without the rank badge, four with it. The template must match
    the children actually rendered: a four-column template over three children
    puts the track in the rank slot and clips every label. */
.k-bar.no-rank { grid-template-columns: 1fr auto auto; }

.k-bar {
    display: grid;
    grid-template-columns: 1.9em 1fr auto auto;
    align-items: center;
    gap: .6em;
    padding: .15em .5em;
    border-radius: .4em;
    background: rgba(255,255,255,.035);
    min-height: 0;
    overflow: hidden;
}

.k-rank {
    font-family: var(--mono, monospace);
    font-size: .8em;
    font-weight: 700;
    text-align: center;
    padding: .15em 0;
    border-radius: .3em;
    background: rgba(255,255,255,.08);
    color: var(--ink-dim);
}

.k-bar:nth-child(1) .k-rank { background: rgba(252,211,77,.25); color: #FCD34D; }
.k-bar:nth-child(2) .k-rank { background: rgba(203,213,225,.22); color: #CBD5E1; }
.k-bar:nth-child(3) .k-rank { background: rgba(251,146,60,.22); color: #FB923C; }

.k-bar .k-track {
    position: relative;
    height: 100%;
    max-height: 1.7em;
    min-height: .9em;
    border-radius: .3em;
    background: var(--track);
    overflow: hidden;
    min-width: 0;
}

.k-bar .k-track > i {
    display: block;
    height: 100%;
    border-radius: .3em;
    background: linear-gradient(90deg, var(--c, var(--accent)), color-mix(in srgb, var(--c, var(--accent)) 55%, #0b0f14));
    animation: k-grow-w .85s cubic-bezier(.22,.61,.36,1) both;
}

@supports not (color: color-mix(in srgb, red 50%, blue)) {
    .k-bar .k-track > i { background: var(--c, var(--accent)); }
}

.k-bar .k-track > span {
    position: absolute;
    top:0; right:0; bottom:0; left:0;
    inset: 0;
    display: flex;
    align-items: center;
    padding-left: .6em;
    font-size: .92em;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.k-bar .k-n { font-weight: 800; font-variant-numeric: tabular-nums; }
.k-bar .k-p { font-size: .85em; color: var(--ink-dim); font-variant-numeric: tabular-nums; min-width: 3.6em; text-align: right; }

/* ── vertical columns ─────────────────────────────────────────────────────── */

.k-cols {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    align-items: end;
    gap: .35em;
    min-height: 0;
    height: 100%;
}

.k-col { display: grid; grid-template-rows: 1fr auto; gap: .3em; min-height: 0; height: 100%; }

.k-col .k-stack {
    display: flex;
    flex-direction: column-reverse;
    justify-content: flex-start;
    align-self: end;
    width: 100%;
    border-radius: .3em .3em 0 0;
    overflow: hidden;
    transform-origin: bottom;
    animation: k-grow-h .8s cubic-bezier(.22,.61,.36,1) both;
}

.k-col .k-seg { width: 100%; }

.k-col .k-cl {
    text-align: center;
    font-size: .8em;
    color: var(--ink-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.k-col .k-cv {
    text-align: center;
    font-size: .9em;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ── composition strip ────────────────────────────────────────────────────── */

/*  Never shrink, for the same reason as .k-cap: a meter squeezed to zero height
    is not a smaller meter, it is a missing one. The Kpi component sets an
    explicit height on it and flex was overruling that silently. */
.k-strip { flex: 0 0 auto; }

.k-strip { display: flex; height: 1.05em; border-radius: .3em; overflow: hidden; background: var(--track); }
.k-strip > i { height: 100%; animation: k-grow-w .8s cubic-bezier(.22,.61,.36,1) both; }

.k-legend {
    display: flex;
    flex-wrap: wrap;
    font-size: .82em;
    color: var(--ink-dim);
    /*  wrap-gap fallback for Chrome 66: half-margins on items, negative
        half-margins here, so outer edges stay flush. No gap property — margins
        carry the spacing on every browser, or modern ones would double up. */
    margin: -.075em -.45em;
}

.k-legend > * { margin: .075em .45em; }
.k-legend span { display: inline-flex; align-items: center; white-space: nowrap; }
.k-legend span > * + * { margin-left: .35em; }
.k-legend b { width: .62em; height: .62em; border-radius: .16em; display: inline-block; }
.k-legend em { font-style: normal; color: var(--text, #e2e8f0); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── waffle ───────────────────────────────────────────────────────────────── */

/*  A hundred cells, one per percent. The most literal way to show a share, and it
    reads from across a room without a legend or an axis. */
/*  The cells are empty elements with no intrinsic size, so the grid must be given
    a definite height or all ten rows collapse and the panel renders blank — which
    is exactly what happened: `margin: 0 auto` cancels the flex stretch, so with
    height:auto the width fit-content'd to zero and aspect-ratio then produced zero
    height. flex + explicit 1fr rows makes the height definite on every browser;
    the square is a progressive enhancement below, never load-bearing. */
.k-waffle {
    flex: 1 1 0;
    min-height: 0;
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    grid-template-rows: repeat(10, 1fr);
    gap: .16em;
    width: 100%;
}

.k-waffle b {
    display: block;
    border-radius: .12em;
    background: var(--track);
    animation: k-pop .5s both;
}

/* ── heat tiles (age) ─────────────────────────────────────────────────────── */

.k-heat { display: grid; gap: .22em; align-content: center; min-height: 0; }

.k-cell {
    border-radius: .28em;
    padding: .3em .1em;
    text-align: center;
    background: var(--track);
    animation: k-pop .45s both;
}

.k-cell .a { display: block; font-size: .78em; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.k-cell .n { display: block; font-size: 1.02em; font-weight: 800; font-variant-numeric: tabular-nums; }

/* ── podium (top 3) ───────────────────────────────────────────────────────── */

.k-podium {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .5em;
    align-items: stretch;
    min-height: 0;
    flex: 1 1 0;
}

.k-pod {
    display: grid;
    align-content: center;
    gap: .1em;
    padding: .35em .5em;
    min-height: 0;
    overflow: hidden;
    border-radius: .5em .5em .3em .3em;
    text-align: center;
    background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.03));
    border-top: .22em solid var(--c, var(--accent));
}

.k-pod .p-n { font-size: 1.25em; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.k-pod .p-v { font-size: 1.5em; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--c, var(--accent)); }
.k-pod .p-s { font-size: .8em; color: var(--ink-dim); }

/* ── span helpers ─────────────────────────────────────────────────────────── */

.c2{grid-column:span 2}.c3{grid-column:span 3}.c4{grid-column:span 4}.c5{grid-column:span 5}
.c6{grid-column:span 6}.c7{grid-column:span 7}.c8{grid-column:span 8}.c9{grid-column:span 9}
.c12{grid-column:span 12}
.r2{grid-row:span 2}.r3{grid-row:span 3}

/* ── booth teams ──────────────────────────────────────────────────────────── */

/*  People, not figures — so the layout is a list of faces rather than a chart.
    Rows share the card height, so a booth with two members and one with six both
    fill their card instead of one leaving a gap. */
.k-team {
    display: grid;
    gap: .35em;
    align-content: stretch;
    grid-auto-rows: minmax(0, 1fr);
    min-height: 0;
    flex: 1 1 0;
}

.k-mem {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: .6em;
    padding: .25em .45em;
    border-radius: .45em;
    background: rgba(255, 255, 255, .04);
    min-height: 0;
    overflow: hidden;
}

/*  Someone who has stepped back this cycle stays on the list, greyed. Removing
    them would lose the record of who was there — BoothWeb treats leaving as a
    toggle for the same reason. */
.k-mem.off { opacity: .45; }

.k-mem-photo {
    width: 2.4em;
    height: 2.4em;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(255, 255, 255, .07);
    border: .08em solid color-mix(in srgb, var(--accent) 35%, transparent);
}

@supports not (color: color-mix(in srgb, red 50%, blue)) {
    .k-mem-photo { border-color: rgba(255,255,255,.2); }
}

/*  Initials when there is no photograph. Better than an empty circle or a generic
    silhouette: at a glance it still distinguishes one person from another. */
.k-mem-initials {
    display: grid;
    place-items: center;
    font-family: var(--mono, monospace);
    font-size: .85em;
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--accent);
}

.k-mem-text { display: grid; gap: .05em; min-width: 0; }

.k-mem-name {
    font-size: 1.05em;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}



/*  gap fallback: flex gap is Chrome 84 and the TV is Chrome 66 —
    successor margins are the same geometry on every browser. */

.k-mem-sub > * + * { margin-left: .55em; }

.k-mem-role {
    font-family: var(--sans, system-ui, sans-serif);
    color: var(--accent);
    overflow: hidden;
    text-overflow: ellipsis;
}

/*  The booth's own figures, above its people. A roster card that lists names
    alone says who is there but nothing about what they are working with; on a
    full-height card there is room for both, and the pairing is the point of the
    screen. Fixed height — .k-team flexes into whatever is left. */
.k-bstat {
    flex: 0 0 auto;
    display: grid;
    gap: .4em;
    padding: .5em .6em;
    margin-bottom: .5em;
    border-radius: .5em;
    background: rgba(255, 255, 255, .05);
    border: .06em solid rgba(255, 255, 255, .08);
}

.k-bstat-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .4em;
}

.k-bs { display: grid; gap: .05em; min-width: 0; }

.k-bs b {
    font-size: 1.35em;
    font-weight: 700;
    line-height: 1.05;
    font-variant-numeric: tabular-nums;
    color: var(--accent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.k-bs i {
    font-style: normal;
    font-family: var(--mono, monospace);
    font-size: .68em;
    font-size: max(.68em, 12px);
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-dim);
    white-space: nowrap;
}



/*  gap fallback: flex gap is Chrome 84 and the TV is Chrome 66 —
    successor margins are the same geometry on every browser. */

.k-bstat-foot > * + * { margin-left: .7em; }

/*  The swatch carries the same male/female colours as every other screen, so the
    two numbers do not need labels to be read. */
.k-bs-m { display: inline-flex; align-items: center; white-space: nowrap; }
.k-bs-m > * + * { margin-left: .3em; }

.k-bs-m em {
    width: .55em;
    height: .55em;
    border-radius: .12em;
    background: var(--c, currentColor);
    flex: 0 0 auto;
}

.k-bs-cast {
    margin-left: auto;
    font-family: var(--sans, system-ui, sans-serif);
    color: var(--accent-2, var(--accent));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/*  PDAG named, not just banded. Four colours with no legend are unreadable on a
    screen that replaces itself every fifteen seconds. */
.k-pdag-key {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .3em;
    font-family: var(--mono, monospace);
    font-size: .72em;
    font-size: max(.72em, 13px);
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
}

.k-pk { display: flex; align-items: center; min-width: 0; white-space: nowrap; }
.k-pk > * + * { margin-left: .28em; }
.k-pk em { width: .5em; height: .5em; border-radius: .1em; background: var(--c, currentColor); flex: 0 0 auto; }
.k-pk b { color: var(--ink-strong, #E2E8F0); font-weight: 700; margin-left: auto; }

.k-cast-hd {
    flex: 0 0 auto;
    font-family: var(--mono, monospace);
    font-size: .7em;
    font-size: max(.7em, 13px);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin: .55em 0 .3em;
}

/*  Castes as a cloud: size and weight carry the share, so five tags in a row
    answer "does one caste dominate here?" faster than five ranked bars, and take
    a third of the height on a card that also has to hold a team. */
.k-cloud {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    min-width: 0;
    margin: -.14em -.16em;
}

.k-cloud > * { margin: .14em .16em; }



/*  gap fallback: flex gap is Chrome 84 and the TV is Chrome 66 —
    successor margins are the same geometry on every browser. */

.k-tag > * + * { margin-left: .3em; }

.k-tag b {
    font-family: var(--mono, monospace);
    font-size: .78em;
    font-weight: 700;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

/*  Staffing status. Derived from the active headcount, not stored — the rule
    lives in BoothTeam.Strength. "Inactive" is grey rather than a deeper red than
    "weak": it is a different problem, not a worse one. */


/*  gap fallback: flex gap is Chrome 84 and the TV is Chrome 66 —
    successor margins are the same geometry on every browser. */

.k-bstatus > * + * { margin-left: .4em; }

.k-bst-dot {
    width: .55em;
    height: .55em;
    border-radius: 50%;
    background: var(--s-c, #94A3B8);
    box-shadow: 0 0 .5em var(--s-c, #94A3B8);
    flex: 0 0 auto;
}

.k-bst-l {
    text-transform: uppercase;
    font-weight: 700;
    color: var(--s-c, #94A3B8);
    white-space: nowrap;
}

.k-bst-n {
    color: var(--ink-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.k-bstatus[data-s="strong"]   { --s-c: #34D399; }
.k-bstatus[data-s="fair"]     { --s-c: #FBBF24; }
.k-bstatus[data-s="weak"]     { --s-c: #FB7185; }
.k-bstatus[data-s="inactive"] { --s-c: #94A3B8; }

/*  Separates the figures above from the people below. */
.k-team-hd {
    flex: 0 0 auto;
    font-family: var(--mono, monospace);
    font-size: .7em;
    font-size: max(.7em, 13px);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-dim);
    padding-bottom: .3em;
    margin-bottom: .35em;
    border-bottom: .06em solid rgba(255, 255, 255, .1);
}

/*  Square where supported (Chrome 88+). Width follows the definite height set
    above, so this can only ever narrow the grid — it cannot zero it. */
@supports (aspect-ratio: 1) {
    .k-waffle { width: auto; aspect-ratio: 1; margin: 0 auto; }
}

.k-page[data-p="team-summary"] { --accent:#F472B6; --accent-2:#DB2777; }
.k-page[data-p="team-roster"]  { --accent:#F472B6; --accent-2:#C084FC; }
.k-page[data-p="team"]         { --accent:#F472B6; --accent-2:#DB2777; }

/*  RELOCATED base rules.

    These eight blocks lived beside their successor-margin companions mid-file,
    where the browser's parser dropped them for a reason that resisted an hour of
    byte-level forensics: the file is valid UTF-8, braces, parens and comments all
    balance, the served bytes match disk, and each block parses cleanly in
    isolation - yet in situ the parsed sheet skipped exactly these rules. Parked
    at end-of-file, where a probe rule demonstrably parses, until the cause is
    understood. Do not inline them back without re-measuring the parsed sheet. */

.k-mem-sub {
    display: flex;
    font-family: var(--mono, monospace);
    font-size: .82em;
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
}

.k-bstat-foot {
    display: flex;
    align-items: center;
    font-family: var(--mono, monospace);
    font-size: .78em;
    font-size: max(.78em, 13px);
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
    min-width: 0;
}

.k-bstatus {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    margin: -.15em 0 .5em;
    font-family: var(--mono, monospace);
    font-size: .74em;
    font-size: max(.74em, 14px);
    letter-spacing: .04em;
    min-width: 0;
}

.k-tag {
    display: inline-flex;
    align-items: baseline;
    max-width: 100%;
    padding: .14em .45em;
    border-radius: .8em;
    /*  em alone put the smallest tag at 11px on a 720p panel — the floor is
        absolute; 1080p is unaffected by it. */
    font-size: var(--s, 1em);
    font-size: max(var(--s, 1em), 14px);
    line-height: 1.25;
    color: var(--ink-strong, #e2e8f0);
    opacity: var(--o, 1);
    background: rgba(255, 255, 255, .07);
    border: .05em solid rgba(255, 255, 255, .1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*  .28em rather than .35em: several lead panels sat 3-15px over their box, and
    the spacing between four stacked children is where that came from. Tighter
    here costs nothing legible and gives every dense panel headroom. */
.k-panel > * + * { margin-top: .28em; }

/* ── Work channel ──────────────────────────────────────────────────────────
   Field progress bars and call-outcome chips. Appended at end of file, like
   the other relocated blocks — see the note above them. */

.k-page[data-p="work"] { --accent:#A78BFA; --accent-2:#38BDF8; }

.k-wfields {
    flex: 1 1 0;
    display: grid;
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
    min-height: 0;
}

/*  Label, track, count, percent. Fixed columns so the numbers form a rule down
    the right edge and can be compared without reading each row. */
/*  Half-width panel now that progress and calling share one screen, so the
    label and figure columns are tighter than they were at full width. */
.k-wf {
    display: grid;
    grid-template-columns: 7.2em 1fr 6.6em 3.4em;
    align-items: center;
    min-width: 0;
}

.k-wf > * + * { margin-left: .7em; }

.k-wf-l {
    font-size: .95em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.k-wf-track {
    position: relative;
    height: .8em;
    border-radius: .4em;
    background: var(--track);
    overflow: hidden;
    min-width: 0;
}

/*  Two bars in one track. <i> is everything known about the field and is
    dimmed; <b> is the share a person entered and sits over it at full strength.
    Siblings, not nested: opacity on a parent fades its children too, and the
    whole point is that one of them stays bright. Where the team entered
    everything the two are the same width and the bar reads as solid, so a
    booth with no import looks exactly as it did before.

    No color-mix() or rgb(x / a) here — the television runs Chrome 66, which
    supports neither. Opacity on the element does the same job everywhere.     */
.k-wf-track > i,
.k-wf-track > b {
    position: absolute;
    top: 0; bottom: 0; left: 0;
    display: block;
    border-radius: .4em;
    background: var(--b, var(--accent));
    animation: k-grow-w .8s cubic-bezier(.22,.61,.36,1) both;
}

/*  .48 rather than .32: at a third strength every hue collapsed to the same
    grey-mauve across a lit room, which is the whole reason to have hues.      */
.k-wf-track > i { opacity: .48; }

/*  Banded by completeness rather than one accent for all: on a wall the useful
    signal is which fields are behind, and colour carries that faster than
    comparing bar lengths.

    The two bars band on DIFFERENT numbers, so their selectors are separate.
    data-band-all colours the dim bar by everything known — its colour then
    agrees with its own length. data-band colours the bright bar by the team's
    own share. Banding both on the human share turned every bar on 429 of 448
    booths the same rose while reading 89 per cent.                            */
.k-wf[data-band-all="done"] .k-wf-track > i { --b: #34D399; }
.k-wf[data-band-all="good"] .k-wf-track > i { --b: #38BDF8; }
.k-wf[data-band-all="part"] .k-wf-track > i { --b: #FBBF24; }
.k-wf[data-band-all="thin"] .k-wf-track > i { --b: #FB7185; }

.k-wf[data-band="done"] .k-wf-track > b { --b: #34D399; }
.k-wf[data-band="good"] .k-wf-track > b { --b: #38BDF8; }
.k-wf[data-band="part"] .k-wf-track > b { --b: #FBBF24; }
.k-wf[data-band="thin"] .k-wf-track > b { --b: #FB7185; }

/*  The figure carries the band too, so a row reads as one colour from label to
    percentage and the eye can sort the panel without measuring bars. A field at
    a flat zero stays dim: colouring nothing rose only shouts about work nobody
    has reached yet.                                                           */
.k-wf[data-band-all="done"] .k-wf-p { color: #34D399; }
.k-wf[data-band-all="good"] .k-wf-p { color: #38BDF8; }
.k-wf[data-band-all="part"] .k-wf-p { color: #FBBF24; }
.k-wf[data-band-all="thin"] .k-wf-p { color: #FB7185; }
.k-wf[data-zero="1"] .k-wf-p { color: var(--ink-dim); }

/*  Two figures sharing one c3 tile — active and complete. Equal flex basis so
    the pair splits the tile evenly whatever the digit count, and margin rather
    than gap: the television runs Chrome 66, where flex gap does nothing.      */
.k-fpair {
    display: flex;
    align-items: baseline;
}

.k-fpair-i {
    flex: 1 1 0;
    min-width: 0;
}

.k-fpair-i + .k-fpair-i { margin-left: .6em; }

/*  Tighter than a solo figure: two of them share the width a single one had. */
.k-fpair .k-fig.lg { font-size: 1.9em; }

/*  Sits inside .k-cap, which is already uppercase and letter-spaced. */
.k-wf-key { color: var(--ink-dim); }

.k-wf-n {
    font-family: var(--mono, monospace);
    font-size: .85em;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

.k-wf-n em { font-style: normal; color: var(--ink-dim); }

.k-wf-p {
    font-family: var(--mono, monospace);
    font-size: .92em;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}

/*  Outcome chips. A grid rather than a wrapped cloud: outcomes are a short
    fixed set and equal cells let two booths be compared at a glance, which a
    cloud that reflows on every count cannot. */
.k-wstat {
    flex: 1 1 0;
    display: grid;
    /*  Two columns, not four: the chips share the screen with the field list
        now, so each has half the width. Eight outcomes fill four rows. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
    min-height: 0;
}

.k-ws {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    min-width: 0;
    padding: .25em .6em;
    border-radius: .4em;
    background: rgba(255, 255, 255, .05);
    border-left: .25em solid var(--c, #94A3B8);
    margin: .12em;
}

.k-ws > * + * { margin-left: .5em; }

.k-ws-l {
    font-size: .9em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.k-ws-n {
    font-family: var(--mono, monospace);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--c, var(--accent));
    white-space: nowrap;
}

.k-ws-p {
    font-family: var(--mono, monospace);
    font-size: .82em;
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/*  "3 / 9" — the denominator sits quieter than the figure it qualifies. */
.k-of-n { font-size: .5em; font-weight: 600; color: var(--ink-dim); }

/*  The call panel with nothing logged yet. Distributed rather than stacked at
    the top: this sits beside the nine-row field list, and content that stops a
    third of the way down reads as a card that failed to load rather than one
    with nothing to report. */
.k-wempty {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-height: 0;
}

.k-wempty-mid {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 0;
    text-align: center;
}

/*  Dimmed, not accented. It is a count of work not yet done — it should read as
    the absence it is, not compete with the figures that carry real progress. */
.k-wempty-fig {
    font-size: 3.2em;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
}

.k-wempty-l {
    font-size: .95em;
    color: var(--ink-dim);
    margin-top: .3em;
}

.k-wempty-bot { flex: 0 0 auto; }
.k-wempty-bot > * + * { margin-top: .35em; }

