/* Keyboard Tester (RT-FUN-106).
 *
 * Contrast rule (docs/contrast-conventions.md): values on the navy
 * --rt-result-bg use --rt-result-ink, labels use --rt-result-muted, and
 * neither token appears on a light surface. The keyboard itself is a light
 * surface, so it uses --rt-ink* throughout.
 *
 * CLS rule (docs/cls-conventions.md): every panel that fills in from a
 * keypress reserves its height up front, so the page never reflows after
 * first paint. The keyboard is drawn during script execution rather than
 * revealed later, and its container carries a min-height for the window
 * between parse and draw.
 */

#kbt { --kbt-key: 44px; }

/* ---------- controls ---------- */

.kbt-controls {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
    padding: 12px 14px; margin-bottom: 14px;
    background: var(--rt-surface-2); border: 1px solid var(--rt-hairline); border-radius: 8px;
}
.kbt-controls label {
    font-family: var(--rt-font-mono); font-size: 11.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--rt-ink-muted);
}
.kbt-controls select {
    padding: 8px 10px; font-size: 15px; border-radius: 6px;
    border: 1px solid var(--rt-hairline); background: var(--rt-surface); color: var(--rt-ink);
}
.kbt-btn {
    margin-left: auto; padding: 8px 16px; font-size: 14px; font-weight: 600;
    border-radius: 6px; cursor: pointer;
    border: 1px solid var(--rt-hairline); background: var(--rt-surface); color: var(--rt-ink);
}
.kbt-btn:hover { border-color: var(--rt-accent); color: var(--rt-accent); }

/* ---------- the keyboard ---------- */

/* The board is wider than the content column and must scroll INSIDE this
   wrapper rather than widening the page. `min-width: 0` is the load-bearing
   line: without it the wrapper inherits min-width:auto from its flex/grid
   ancestors, refuses to shrink below its content, and the overflow escapes to
   the body — which is exactly what happened on the first pass. */
.kbt-board-wrap { overflow-x: auto; max-width: 100%; min-width: 0; padding-bottom: 6px; }
#kbt { min-width: 0; max-width: 100%; }
#kbt-board {
    display: flex; gap: 18px; align-items: flex-start;
    /* WRAPS rather than scrolling wherever it can. The main block alone is
       ~710px and fits the content column; the nav and numpad clusters drop to
       a second line rather than sitting off the right edge where the first
       working version left them — a numpad you have to scroll sideways to find
       is a numpad most people will not test. */
    flex-wrap: wrap;
    min-height: 250px;
}
.kbt-side { display: flex; flex-direction: row; gap: 18px; align-items: flex-start; }
.kbt-block { display: flex; flex-direction: column; gap: 4px; }
.kbt-block--nav .kbt-row, .kbt-block--numpad .kbt-row { justify-content: flex-start; }
.kbt-row { display: flex; gap: 4px; }

.kbt-key {
    display: flex; align-items: flex-start; justify-content: flex-start;
    flex: 0 0 auto;             /* width comes from app.js — see keyEl() */
    height: var(--kbt-key); padding: 5px 6px;
    border: 1px solid var(--rt-hairline); border-bottom-width: 2px; border-radius: 5px;
    background: var(--rt-surface); color: var(--rt-ink);
    font-family: var(--rt-font-mono); font-size: 11px; line-height: 1.1;
    user-select: none; transition: background-color .06s linear, color .06s linear;
}
.kbt-key-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The function row packs 16 keys across the same width the number row uses for
   15, so it needs the smaller type to keep "PrtSc" and "ScrLk" whole. */
.kbt-block--main .kbt-row:first-child .kbt-key { font-size: 10px; padding: 5px 3px; }


/* Tested-once: a quiet ledger of what has registered this session. */
.kbt-key.is-tested { background: var(--rt-surface-2); color: var(--rt-ink-soft); border-color: var(--rt-hairline); }

/* Currently down: the accent, and the only strong colour on the board. */
.kbt-key.is-down {
    background: var(--rt-accent); color: #fff; border-color: var(--rt-accent);
    transform: translateY(1px); border-bottom-width: 1px;
}
/* Stuck beats down — a stuck key IS down, and the warning is what matters. */
.kbt-key.is-stuck {
    background: #b91c1c; color: #fff; border-color: #b91c1c;
    animation: kbt-pulse 1.1s ease-in-out infinite;
}
@keyframes kbt-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .62; } }
@media (prefers-reduced-motion: reduce) { .kbt-key.is-stuck { animation: none; } }

/* ---------- panels ---------- */

.kbt-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 16px; }
@media (max-width: 720px) { .kbt-panels { grid-template-columns: 1fr; } }

.kbt-panel {
    padding: 14px; border: 1px solid var(--rt-hairline); border-radius: 8px;
    background: var(--rt-surface);
}
.kbt-panel h3 {
    margin: 0 0 10px; font-family: var(--rt-font-mono); font-size: 11.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--rt-ink-muted);
}
.kbt-panel--event { min-height: 232px; }
.kbt-panel--held { min-height: 150px; }
.kbt-panel--repeat { min-height: 150px; }
.kbt-panel--cov { min-height: 128px; }
.kbt-panel--stuck { min-height: 96px; }

.kbt-hint { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--rt-ink-soft); }

/* key/value table for the last event */
.kbt-kv { margin: 0; }
.kbt-kv-row { display: grid; grid-template-columns: 84px 1fr; gap: 10px; padding: 5px 0; }
.kbt-kv-row + .kbt-kv-row { border-top: 1px solid var(--rt-hairline); }
.kbt-kv dt {
    font-family: var(--rt-font-mono); font-size: 12px; font-weight: 700; color: var(--rt-ink-muted);
}
.kbt-kv dd { margin: 0; min-width: 0; }
.kbt-kv dd code {
    display: inline-block; font-family: var(--rt-font-mono); font-size: 13px;
    color: var(--rt-ink); word-break: break-all;
}
.kbt-kv-note { display: block; font-size: 12px; line-height: 1.45; color: var(--rt-ink-soft); }

/* held keys */
.kbt-held-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
#kbt-held-count { font-family: var(--rt-font-mono); font-size: 30px; font-weight: 700; color: var(--rt-ink); }
#kbt-held-split { font-size: 13px; color: var(--rt-ink-soft); }
.kbt-chip {
    display: inline-block; margin: 0 5px 5px 0; padding: 3px 8px;
    font-family: var(--rt-font-mono); font-size: 12px;
    border: 1px solid var(--rt-hairline); border-radius: 999px;
    background: var(--rt-surface-2); color: var(--rt-ink);
}
.kbt-chip--mod { border-color: var(--rt-accent); color: var(--rt-accent); }

/* rollover verdict — the one true result box, so result tokens apply here */
.kbt-result {
    margin-top: 16px; padding: 18px;
    background: var(--rt-result-bg); border-radius: 10px;
}
.kbt-result-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
#kbt-rollover-verdict {
    font-family: var(--rt-font-mono); font-size: 22px; font-weight: 700; color: var(--rt-result-ink);
}
#kbt-rollover-detail { margin: 0; font-size: 14px; line-height: 1.6; color: var(--rt-result-muted); }
.kbt-result h3 {
    margin: 0 0 8px; font-family: var(--rt-font-mono); font-size: 11.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--rt-result-muted);
}
.kbt-badge {
    padding: 2px 9px; border-radius: 999px;
    font-family: var(--rt-font-mono); font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
}
.kbt-badge--ok { background: #065f46; color: #d1fae5; }
.kbt-badge--soft { background: #78350f; color: #fef3c7; }

/* repeat-rate stats */
.kbt-stat { display: inline-block; margin: 0 22px 8px 0; }
.kbt-stat-num { display: block; font-family: var(--rt-font-mono); font-size: 22px; font-weight: 700; color: var(--rt-ink); }
.kbt-stat-lbl { display: block; font-size: 12.5px; color: var(--rt-ink-soft); }

/* coverage bar */
.kbt-cov-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 8px; }
#kbt-cov-count { font-family: var(--rt-font-mono); font-size: 18px; font-weight: 700; color: var(--rt-ink); }
.kbt-cov-bar { height: 8px; border-radius: 999px; background: var(--rt-surface-2); overflow: hidden; }
#kbt-cov-bar-fill { height: 100%; width: 0; background: var(--rt-accent); transition: width .18s ease; }
#kbt-cov-msg { margin: 8px 0 0; font-size: 13px; line-height: 1.6; color: var(--rt-ink-soft); }
#kbt-cov-msg code {
    font-family: var(--rt-font-mono); font-size: 12px;
    padding: 1px 5px; border-radius: 4px; background: var(--rt-surface-2); color: var(--rt-ink);
}

/* stuck-key panel */
#kbt-stuck.is-alert { border-color: #b91c1c; background: #fef2f2; }
#kbt-stuck p { margin: 0 0 6px; font-size: 13.5px; line-height: 1.55; color: var(--rt-ink); }
#kbt-stuck code {
    font-family: var(--rt-font-mono); font-size: 12.5px;
    padding: 1px 5px; border-radius: 4px; background: var(--rt-surface-2); color: var(--rt-ink);
}

/* history */
#kbt-history { min-height: 34px; }

/* the "some keys never reach the page" note */
.kbt-caveats { margin-top: 14px; }
.kbt-caveats summary { cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--rt-ink); }
.kbt-caveats ul { margin: 10px 0 0; padding-left: 20px; }
.kbt-caveats li { margin-bottom: 6px; font-size: 13.5px; line-height: 1.55; color: var(--rt-ink-soft); }
.kbt-caveats code {
    font-family: var(--rt-font-mono); font-size: 12.5px;
    padding: 1px 5px; border-radius: 4px; background: var(--rt-surface-2); color: var(--rt-ink);
}
