/* =====================================================================
   THEORY LOCK — modal shell, beat bar, piano, answer cards, stave,
                 lesson panel and the quit confirmation
   ===================================================================== */
#lockOverlay{position:fixed;inset:0;background:rgba(26,25,22,.55);backdrop-filter:blur(4px);
  display:none;align-items:center;justify-content:center;z-index:50;padding:var(--sp-3)}
#lockOverlay.show{display:flex;animation:lockFade var(--t-mid) ease}
@keyframes lockFade{from{opacity:0}to{opacity:1}}
.lock-modal{position:relative;background:var(--panel);border:1px solid var(--border);
  border-radius:var(--radius-xl);width:min(720px,100%);max-height:94vh;overflow:auto;
  box-shadow:var(--shadowLg);display:flex;flex-direction:column;
  animation:lockRise .32s var(--ease-pop)}
@keyframes lockRise{from{opacity:0;transform:translateY(26px) scale(.96)}to{opacity:1;transform:none}}

.lock-head{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--border);flex-wrap:wrap;
  position:sticky;top:0;background:var(--panel);z-index:2;
  /* the sheet's own top corners, not a compound of the radius token — that token is
     itself a four-value hand-cut corner and cannot be nested inside another shorthand */
  border-radius:4px 16px 0 0}
.lock-head h3{margin:0;font-size:19px;font-weight:700;color:var(--text)}
.lock-head .fam{color:var(--muted);font-family:var(--font-mono);font-size:10px;text-transform:uppercase;
  letter-spacing:.12em;font-weight:700;margin-top:3px}
/* Positioned, because the caller's effect overlays (stamp, shuffle caption, battery
   badge, middle-finger flash) are absolutely positioned inside it and should cover the
   question area rather than the whole modal including its header. */
.lock-body{position:relative;padding:var(--sp-4) var(--sp-5) var(--sp-5)}
.lock-prompt{font-size:clamp(16px,2.6vw,18px);font-weight:700;color:var(--text);margin:0 0 var(--sp-3);line-height:1.4}
.lock-quit{margin-left:6px;width:32px;height:32px;flex:none;border-radius:9px;border:1px solid var(--border);
  background:var(--panel2);color:var(--muted);font-size:.9rem;font-weight:900;cursor:pointer;line-height:1;
  display:grid;place-items:center;transition:all var(--t-fast)}
.lock-quit:hover{border-color:var(--bad);color:var(--bad);background:color-mix(in srgb,var(--bad) 12%,var(--panel2))}

.lock-actions{display:flex;justify-content:space-between;align-items:center;gap:var(--sp-3);
  margin-top:var(--sp-4);flex-wrap:wrap-reverse;padding-top:var(--sp-3);border-top:1px solid var(--border)}
.lock-actions .btn{flex:0 0 auto}
.lock-actions #lockSubmitBtn{margin-left:auto}
.lock-feedback{min-height:1.4em;font-size:14px;color:var(--body);margin-top:var(--sp-2)}
.lock-feedback.good{color:var(--good);font-weight:800;animation:popIn var(--t-mid) var(--ease-pop)}
.lock-feedback.bad{color:var(--bad);font-weight:800;animation:feedbackShake .34s ease}
@keyframes feedbackShake{0%,100%{transform:translateX(0)}
  20%{transform:translateX(-5px)}40%{transform:translateX(5px)}60%{transform:translateX(-3px)}80%{transform:translateX(3px)}}

/* ---------- beat bar ---------- */
.beatbar{display:flex;gap:4px;margin:var(--sp-2) 0 var(--sp-4)}
/* Time remaining is neutral information, so it is drawn in gold and only turns red
   when it is nearly gone. Painting the whole bar red at rest left the danger state
   with nowhere louder to go. */
.beatbar .seg{flex:1;height:9px;border-radius:5px;background:var(--gold);
  transition:background var(--t-mid) ease,opacity var(--t-mid) ease,transform var(--t-mid) var(--ease-pop)}
.beatbar .seg.spent{background:var(--border);opacity:.45;transform:scaleY(.62)}
.beatbar.danger .seg:not(.spent){background:var(--accentBright);
  animation:beatUrgent .55s ease-in-out infinite alternate}
@keyframes beatUrgent{from{filter:brightness(1)}to{filter:brightness(1.4)}}
/* Frozen by the caller: the bar frosts over rather than merely desaturating. */
.beatbar.frozen{filter:saturate(.35) brightness(1.1)}
.beatbar.frozen .seg:not(.spent){background:color-mix(in srgb,var(--chaos) 45%,#fff)}
/* A segment handed back by the "extra beats" effect pops in. */
.beatbar .seg.gained{animation:segGained .5s var(--ease-pop)}
@keyframes segGained{0%{transform:scaleY(.3);filter:brightness(2)}100%{transform:scaleY(1);filter:brightness(1)}}

/* ---------- piano ----------
   Two octaves cannot fit a phone: at 375px the white keys came out 20px wide and the
   black ones 13px, which is not a target a thumb can hit. The keyboard now keeps its
   keys finger-sized and pans horizontally inside its own wrapper instead of shrinking
   — the min-width is below a desktop modal's width, so nothing changes on a large
   screen. `pan-x` rather than `none` so the swipe that scrolls it is allowed through
   while a tap still plays the note. */
.piano-wrap{overflow-x:auto;overflow-y:hidden;padding:var(--sp-2) 0;
  -webkit-overflow-scrolling:touch;touch-action:pan-x;overscroll-behavior-x:contain}
/* 600px keeps the black keys — the narrowest target on the board — above the 24px
   floor a thumb needs: 15 white keys at ~40px, and black keys at the usual ~62% of
   that is ~25px. Anything less and the accidentals become the un-hittable ones. */
.piano{position:relative;min-width:600px;height:clamp(108px,22vw,160px);display:flex;user-select:none;touch-action:pan-x;
  border-radius:10px;overflow:hidden;border:1px solid var(--pianoBorder);
  box-shadow:var(--shadow)}
.pkey{position:relative;flex:1;border-right:1px solid var(--pianoBorder);border-top:1px solid var(--pianoBorder);
  background:linear-gradient(180deg,var(--pianoWhite),color-mix(in srgb,var(--pianoWhite) 88%,#000));
  cursor:pointer;display:flex;align-items:flex-end;justify-content:center;padding-bottom:6%;
  font-size:clamp(.42rem,1.6vw,.62rem);font-weight:800;color:var(--pianoWhiteText);
  transition:background var(--t-fast) ease,transform var(--t-fast) ease,box-shadow var(--t-fast) ease}
.pkey:last-child{border-right:none}
.pkey:hover:not(.disabled){background:color-mix(in srgb,var(--accent) 14%,var(--pianoWhite))}
.pkey:active:not(.disabled){transform:translateY(2px)}
/* Width and left are both written by renderPiano() from the same white-key count, so
   they can never disagree; the 6.4% here is only a fallback if that ever fails to run. */
.pkey.black{position:absolute;top:0;width:6.4%;height:60%;
  background:linear-gradient(180deg,color-mix(in srgb,var(--pianoBlack) 82%,#fff),var(--pianoBlack));
  border:1px solid var(--pianoBorder);border-top:none;border-radius:0 0 4px 4px;z-index:2;
  color:var(--pianoBlackText);font-size:clamp(.38rem,1.3vw,.5rem);padding-bottom:8%;
  box-shadow:0 2px 5px rgba(26,25,22,.34)}
.pkey.black:hover{background:color-mix(in srgb,var(--accent) 28%,var(--pianoBlack))}
.pkey.active{background:var(--accentGrad)!important;color:var(--accentText)!important;
  box-shadow:inset 0 0 0 2px color-mix(in srgb,#000 22%,var(--accent));animation:keyStrike .22s var(--ease-pop)}
@keyframes keyStrike{0%{transform:translateY(3px) scaleY(.97)}100%{transform:none}}
/* Confirmed right — you have been told this one belongs, so it reads as settled and
   you are not tempted to take it back off. Green because that is the site's "good"
   ink; the gold "locked" ring means something else (the caller pinned it). */
.pkey.confirmed{box-shadow:inset 0 0 0 3px var(--good)}
.pkey.confirmed::after{content:"✓";position:absolute;top:4px;left:50%;transform:translateX(-50%);
  font-size:.62rem;font-weight:950;color:var(--good);line-height:1}
.pkey.black.confirmed::after{top:auto;bottom:26%;color:#fff}
.pkey.locked{box-shadow:inset 0 0 0 2px var(--gold)}
.pkey.candidate{box-shadow:inset 0 0 0 3px var(--gold);
  background:color-mix(in srgb,var(--gold) 32%,var(--pianoWhite))!important}
.pkey.black.candidate{background:color-mix(in srgb,var(--gold) 55%,var(--pianoBlack))!important}
.pkey.disabled{opacity:.32;pointer-events:none}
.piano.lowbattery{filter:brightness(.55) saturate(.5)}
.piano-context{text-align:center;color:var(--muted);font-size:.76rem;margin-top:var(--sp-2)}
/* already-rejected keys stay marked so the same mistake isn't repeated:
   a marker, not a repaint — the previous version flooded the key background.
   NB: do NOT give this rule a `position` of its own. `.pkey.wrong` and `.pkey.black`
   have identical specificity, so a later `position:relative` here beat the black key's
   `position:absolute` and threw every struck-out black key out of the keyboard and into
   the flex row. The base .pkey rule is already positioned, which is all ::before needs. */
.pkey.wrong::before{content:"";position:absolute;left:50%;transform:translateX(-50%);
  bottom:14%;width:56%;height:3px;border-radius:2px;background:var(--bad)}
.pkey.black.wrong::before{bottom:8%;width:70%;background:color-mix(in srgb,var(--bad) 60%,#fff)}

/* ---------- answer / quiz cards ---------- */
.qcard-grid{display:grid;gap:8px;justify-content:stretch}
.qcard{position:relative;border:1px solid var(--cardOutline);border-radius:var(--radius-sm);
  background:var(--cardTop);
  padding:10px 6px;text-align:center;cursor:pointer;font-weight:800;font-size:.82rem;
  color:var(--cardFooterText);min-height:56px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  transition:outline var(--t-fast) ease,opacity var(--t-fast) ease,
             transform var(--t-fast) var(--ease-out),box-shadow var(--t-fast) ease;
  animation:popIn .26s var(--ease-out) backwards}
.qcard:hover{transform:translateY(-3px);box-shadow:0 10px 20px rgba(0,0,0,.18)}
.qcard small{display:block;font-weight:700;color:var(--cardFooterMuted);font-size:.5rem;
  letter-spacing:.05em;text-transform:uppercase}
.qcard.confirmed{outline:3px solid var(--good);outline-offset:1px;
  background:color-mix(in srgb,var(--good) 14%,var(--cardTop))}
.qcard.confirmed::after{content:"✓";position:absolute;top:4px;right:7px;font-size:.78rem;
  font-weight:950;color:var(--good)}
.qcard.locked{outline:3px solid var(--gold)}
.qcard.removed{opacity:.18;pointer-events:none;transform:scale(.94)}
.qcard.candidate2{outline:2px dashed var(--gold)}
/* Selection must always be legible, including on top of a narrowed "likely answers"
   highlight — the gold dashed outline used to win and you could not tell which of the
   two you had actually picked. */
.qcard.selected{outline:3px solid var(--accent);outline-offset:1px;
  background:color-mix(in srgb,var(--accent) 20%,var(--cardTop));
  box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 28%,transparent)}
.qcard.selected::before{content:"✓";position:absolute;top:4px;left:7px;font-size:.78rem;
  font-weight:950;color:var(--accent)}
.qcard.selected.candidate2{outline:3px solid var(--accent)}
/* "already tried and rejected" — a marker, not a repaint. */
.qcard.wrong{border-color:var(--bad);opacity:.72}
.qcard.wrong::after{content:"✕";position:absolute;top:4px;right:7px;font-size:.72rem;font-weight:950;color:var(--bad)}
.bureau-code{display:block;font-size:.56rem;font-weight:900;letter-spacing:.06em;color:var(--cardFooterMuted)}
.bureau-note{display:block;font-size:1rem;font-weight:950;line-height:1.1}

/* ---------- sheet music ---------- */
.staff-wrap{display:flex;justify-content:center;padding:var(--sp-2) 0 var(--sp-3)}
#staffSvg{max-width:100%;height:auto;width:min(100%,340px)}

/* ---------- teaching / lesson panel ---------- */
.lesson{margin:var(--sp-3) 0 0;padding:var(--sp-3);border-radius:var(--radius-md);
  border:1.5px solid color-mix(in srgb,var(--good) 55%,var(--border));
  background:color-mix(in srgb,var(--good) 10%,var(--panel2));display:grid;gap:6px;
  animation:riseIn .28s var(--ease-out)}
.lesson.hint{border-color:color-mix(in srgb,var(--gold) 60%,var(--border));
  background:color-mix(in srgb,var(--gold) 12%,var(--panel2))}
.lesson-head{font-size:.7rem;font-weight:950;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.lesson-body{font-size:.86rem;line-height:1.55}
.lesson-body b{color:var(--text)}
.lesson-go{justify-self:start;margin-top:4px}

/* ---------- quit confirmation ---------- */
.quit-confirm{position:absolute;inset:0;display:grid;place-items:center;z-index:20;
  background:color-mix(in srgb,var(--panel) 82%,transparent);border-radius:var(--radius-lg);
  animation:lockFade var(--t-fast) ease}
.quit-confirm .qc-card{max-width:320px;text-align:center;padding:var(--sp-4);border-radius:var(--radius-md);
  background:var(--panel);border:1.5px solid var(--border);box-shadow:var(--shadowLg);
  animation:popIn var(--t-mid) var(--ease-pop)}
.quit-confirm h4{margin:0 0 6px;font-size:1rem;font-weight:950}
.quit-confirm p{margin:0 0 var(--sp-3);color:var(--muted);font-size:.82rem;line-height:1.45}
.qc-row{display:flex;gap:var(--sp-2);justify-content:center;flex-wrap:wrap}

@media(max-width:600px){
  .lock-body{padding:var(--sp-3) var(--sp-4) var(--sp-4)}
  .lock-head{padding:var(--sp-3) var(--sp-4)}
}

/* The caller's "check my work" tally. Shared by every surface — it belongs to the
   lock rather than to the answer cards, which is where it used to be trapped. */
.check-result{
  text-align:center;margin-top:8px;
  font-family:var(--font-mono);font-size:.78rem;font-weight:700;
  letter-spacing:.04em;color:var(--muted);
}

/* A key the caller has ruled out. Deliberately NO position/transform — a struck black
   key with a position of its own leaves the keyboard entirely (see .pkey.wrong above).
   Opacity, a muted face and a line through the label carry it safely. */
.pkey.removed{opacity:.34;cursor:not-allowed;pointer-events:none;text-decoration:line-through}
.pkey.removed:not(.black){background:var(--panel2);color:var(--muted)}
.pkey.removed.black{background:color-mix(in srgb,var(--pianoBlack) 55%,var(--panel2))}

/* ---------- font emergency ----------
   The caller changes the typeface of the entire question panel. It has to beat the
   game's own font rules, which are set per-component (.btn is Space Mono, the prompt
   is the display face), so it applies to every descendant and carries !important —
   nothing else in here fights it, and it is removed the moment the lock closes.
   Note NAMES are untouched: this makes the sheet look unwell, not the answer unknowable. */
.lock-modal.font-panic, .lock-modal.font-panic *{
  font-family:var(--panicFont) !important;
  text-transform:none !important;
}
/* A settle rather than a skew: the modal scrolls its own overflow, so animating its
   geometry would hand it scrollbars for the duration. */
@keyframes fontPanicSwap{from{opacity:.5}to{opacity:1}}
.lock-modal.font-panic-in{animation:fontPanicSwap .45s ease-out}
