/* Chris, 2026-09-09: real phones (Galaxy S8+, iPhone SE, Galaxy Z Fold 5)
   were wrapping "LH Fingers"/"RH Fingers" and quality-row captions like
   "1-♭3-5-♭7" even at the narrow-width font sizes already tuned
   and Playwright-verified for those exact device widths (see the
   #wpl-scale-view-row / #wpl-quality-row2 breakpoint comments below).
   That mismatch (fits in headless Chromium, wraps on the real device) is
   the signature of mobile browsers' automatic "font boosting" /
   text-size-adjust, which silently inflates small text in narrow columns
   to keep it "readable" -- it only kicks in on real devices, so no
   desktop or emulator test catches it. Turning it off makes every font-
   size on this page render at exactly the px value authored, on every
   device, which is what all the hand-tuned breakpoints below already
   assume. */
#wpl-wrapper { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
#wpl-wrapper * { -webkit-tap-highlight-color: transparent; box-sizing: border-box; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
#wpl-wrapper button { font-family: inherit; }
body {
  box-sizing: border-box;
  background: #f2efe9;
  margin: 0;
  padding: 14px 16px;
}
#wpl-wrapper {
  --wpl-cream: #f2efe9;
  --wpl-card: #fffdf8;
  --wpl-ink: #2a2f34;
  --wpl-muted: #8a8b82;
  --wpl-tan: #efe9e0;
  --wpl-tan-hover: #e3dcd0;
  --wpl-tan-text: #55565a;
  /* Chris, 2026-09-12 (round B pick 6B): deep teal for every selected
     button, Play, the tempo slider and the step circles, so red and blue
     only ever mean right hand / left hand. The old brand blue #14A3C7
     sat right next to the left hand's sky blue (#38BDF8); deep teal is
     the same family as the logo, but darker and greener. */
  --wpl-accent: #0F766E;
  --wpl-accent-dark: #0B5F58;
  --wpl-line: #e5ded2;
  --wpl-prog-bg: #e2f5fa;
  --wpl-prog-hover: #cfeaf2;
  --wpl-prog-border: #cdeaf2;
  /* Practice hub tile tints (2026-09-09, Chris: "it's so bland. a
     little color could be nice") -- soft pastels in the same low-
     saturation family as --wpl-prog-bg above, so the grid gets variety
     without clashing with the cream/tan editorial look. Reused via
     nth-child on .practice-tile, 4-color cycle. */
  --wpl-tile-teal-bg: #e2f5fa;
  --wpl-tile-teal-border: #cdeaf2;
  --wpl-tile-peach-bg: #faece2;
  --wpl-tile-peach-border: #f2dcc9;
  --wpl-tile-sage-bg: #eaf3e2;
  --wpl-tile-sage-border: #dbe9cb;
  --wpl-tile-gold-bg: #faf2df;
  --wpl-tile-gold-border: #f0e2bf;
  --wpl-lift-shadow: 0 4px 10px rgba(42,38,32,0.10);
  --wpl-active-shadow: 0 3px 10px rgba(42,38,32,0.16);

  /* Chris, 2026-09-12 (round B pick 1C): DM Sans everywhere (titles stay
     Fraunces), so the app looks the same on every device instead of
     each one drawing its own system font. Loaded in index.html. */
  font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  /* DM Sans's default ("normal") line spacing is ~1.30x its size, taller
     than the ~1.19x of the San Francisco system font the layout was
     tuned on -- left at normal, every text line grew ~1px and the card
     grew 5-9px (measured 2026-09-12). Pinning the old spacing keeps every
     height exactly as it was. (Buttons keep their own line heights.) */
  line-height: 1.193;
  box-sizing: border-box;
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 16px 32px 16px;
  background: var(--wpl-card);
  border-radius: 20px;
  box-shadow: 0 4px 24px rgba(60,50,20,0.08);
  color: var(--wpl-ink);
  position: relative;
}

/* ---------- Header ---------- */
.wpl-header { text-align: center; margin-bottom: 8px; }
.wpl-eyebrow {
  font-size: 11px;
  font-weight: 700;
  color: var(--wpl-muted);
  text-transform: uppercase;
  letter-spacing: 1.6px;
  margin-bottom: 6px;
}
.wpl-title {
  font-family: 'Fraunces', Georgia, 'Times New Roman', serif;
  font-size: 32px;
  font-weight: 600;
  color: var(--wpl-ink);
  line-height: 1.15;
}
.wpl-subtitle { font-size: 14px; font-weight: 500; color: var(--wpl-muted); margin-top: 6px; }
/* A one-line "pick a key, then a chord type" hint briefly lived here,
   2026-09-07, to orient a cold first-time visitor -- cut again the same
   day once Chris set a hard rule: "the shell can't get bigger. it was
   the perfect size on my phone before." Every extra line has a real
   height cost, so the fix for "people don't understand the flow" has to
   come from the existing elements (step badges below, tab bar) rather
   than adding new ones. */
/* Overlaid on the card (position:absolute), not in normal flow — this is
   deliberate so the countdown never affects #wpl-wrapper's height or pushes
   the header/tabs/piano down (Chris: "doesn't effect the size, but sits in
   a visually good place"). #wpl-wrapper is given position:relative (see
   top of this file) so this positions relative to the card, not the page. */
.wpl-trial-note {
  display: none;
  position: absolute;
  top: 14px;
  right: 16px;
  z-index: 3;
  text-align: center;
  padding: 6px 12px;
  background: #e2f5fa;
  border: 1px solid #cdeaf2;
  border-radius: 10px;
  line-height: 1.3;
}
.wpl-trial-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--wpl-tan-text);
}
.wpl-trial-clock {
  display: block;
  font-size: 13px;
  font-weight: 800;
  color: var(--wpl-accent-dark);
  font-variant-numeric: tabular-nums;
}
/* Default (above the 520px breakpoint) reservation — covers the common
   embed widths between phone and the 1100px desktop max-width, where the
   card is narrower than the eyebrow text's clear margin needs. */

/* ---------- Top tab bar ---------- */
/* Chris, 2026-09-07: people he showed the app to cold "didn't notice the
   top tabs either" -- a plain text label + thin underline apparently
   reads as inert page furniture, not as "3 different tools live here,"
   next to the same large colored buttons that were already winning the
   attention fight against the numbered step labels. Chris's own
   reference: real manila folder tabs -- a raised, filled shape that
   visibly sits "in front of" and connects to whatever's open beneath it,
   with the other (unopened) folders receding behind the divider line.
   Implemented as a classic connected-tab shape rather than a literal
   folder's single-side notch (that notch is a physical filing detail,
   not a web-tab convention): each tab gets its own rounded-top card
   filled with the resting tan color; the active one switches to the
   same background as the content card below it and its bottom edge
   overlaps .wpl-tabbar's divider line (negative margin-bottom, exactly
   the line's own thickness) so the line visually disappears under it --
   the "this folder is open and connected to what you're looking at"
   effect.
   Round 2, Chris (screenshot): "the buttons are rounded, and the tabs
   are square. plus the way the tabs are connected looks weird with
   that gray line." Bumped radius 8px->12px to match .prog-tab's
   corner roundness -- the app's other most-rounded element -- so the
   tabs read as clearly rounded instead of near-square. Tried a
   shadow-only active state next (no border) -- Round 3, Chris
   (screenshot): "it sinks down and the lower shadow make it look
   separated from the lower body. it needs to be a part of the body."
   Tried giving every tab the same visible border next -- fixed the
   float, but then (Chris, describing the mental model precisely):
   "the body will have a line above it that's under the tabs. when a
   tab is clicked, the upper border will wrap over and around the
   clicked then continue until it reaches the other side." That's the
   actual spec: ONE continuous horizontal line (.wpl-tabbar's own
   border-bottom, edge to edge) is the top of the body and runs
   underneath every unclicked tab uninterrupted; only the active tab's
   own border detours up and over it -- left side up, across the top,
   down the right side -- before the line resumes. So only .active
   gets a real border (left/top/right); unclicked tabs are borderless
   fills that simply sit above the line. The active tab's own ::after
   strip erases the exact segment of that line running under it
   (rather than a margin-bottom trick, which previously shifted its
   whole box down under align-items:flex-end and misaligned its top
   with the other tabs -- Chris: "the top of the clicked tab is lower
   than the unclicked tabs"). Padding-top differs by exactly the
   border's own width (6.5px active vs 8px unclicked) so both states
   still resolve to the same total box height -- no jump when a
   different tab becomes active.
   Chris: "don't let the unclicked tabs touch. should be 1px space
   between them." Small explicit gap between every tab so the
   continuous line still shows through as a thread connecting them,
   rather than one solid tan bar. */
.wpl-tabbar {
  display: flex;
  align-items: flex-end;
  gap: 1px;
  border-bottom: 1.5px solid var(--wpl-line);
  margin-bottom: 10px;
}
.wpl-tab {
  position: relative;
  /* Border is reserved at full width on EVERY tab, all the time --
     transparent when inactive, colored in when active. Chris (zoomed
     screenshot, comparing an active vs. inactive shot of the same
     tab): "the tab text moves when it's clicked... the border should
     move around the tab to create the movement, not move the tab."
     The old rule gave only .active a border at all (none -> 1.5px on
     three sides). Since flex:1 1 0 divides the row's width among the
     tabs by their flex-basis, adding/removing that 1.5px border
     changed each tab's own box-sizing:border-box outer width slightly
     as it toggled -- so the active tab's box (and its centered label)
     visibly shifted sideways instead of staying put. Reserving the
     same border on every tab at all times means toggling .active only
     flips border-color and background; the box a tab occupies, and
     where its text centers within it, never changes. */
  border: 1.5px solid transparent;
  border-bottom: none;
  background: var(--wpl-tan);
  border-radius: 12px 12px 0 0;
  cursor: pointer;
  padding: 8px 16px 10px;
  /* Fixed height, not just matching padding, so the active tab's top
     lines up with the others pixel-for-pixel. A border-width-based
     padding offset (padding-top reduced by the border's own 1.5px)
     looked right in theory, but Chris still saw "the clicked tab's
     top is shorter" -- Chromium was rendering the 1.5px border as a
     rounded 1px (computed style confirmed it), so the offset was
     wrong by the difference. Height is an explicit box-sizing:border-
     box value instead (see #wpl-wrapper's global border-box reset at
     the top of this file) -- immune to how any given browser rounds a
     fractional border, since the border simply eats into the content
     area rather than changing the box's own total height. */
  height: 34px;
  font-size: 14px;
  font-weight: 700;
  color: var(--wpl-tan-text);
  letter-spacing: 0.2px;
  transition: color 0.15s ease, background 0.15s ease;
  display: flex;
  /* flex-end, not center: pins the label a fixed distance (this
     padding's own bottom value) above the tab's bottom edge. Chris
     (zoomed screenshot): "it looks like the clicked tab text moves up
     1px. it can't move at all." The active tab is 1px taller on
     purpose (see .active below); with vertical centering, that extra
     height moves the box's own midpoint, so the centered label rides
     up with it. Anchoring to the bottom instead means the extra
     height lands as invisible padding stacked above the label -- the
     label's own position never changes, since the tab's bottom edge
     is itself already pinned by .wpl-tabbar's align-items:flex-end
     regardless of which tab is active. */
  align-items: flex-end;
  justify-content: center;
  gap: 6px;
  /* Unlike .prog-tab/.step-btn/.key-tab (the sub-rows within a panel),
     this row never got flex:1 -- with exactly 4 tabs today the fixed
     padding + font sizes below happen to fit every breakpoint, but a
     future 5th tab would just overflow or wrap the bar (adding height)
     instead of sharing the row width like every other button row in the
     app already does. flex:1 1 0 + min-width:0 makes this row self-
     adjusting the same way, so adding a tab is a markup-only change here
     too, not a new round of breakpoint tuning. white-space:nowrap keeps
     a label + its "Soon" pill on one line instead of wrapping inside
     the tab and growing the bar's height once the button gets narrower
     with more tabs sharing the row. */
  flex: 1 1 0;
  min-width: 0;
  white-space: nowrap;
}
.wpl-tab.active {
  /* 1px taller than the unclicked tabs (Chris: "that will help make
     it stand out more"), added at the TOP only -- align-items:
     flex-end on .wpl-tabbar keeps every tab's bottom pinned to the
     same line regardless of height, so a taller active tab pokes up
     above its neighbors instead of also pushing its bottom (and the
     body-fusion seam) out of place. */
  height: 35px;
  border-color: var(--wpl-line);
  background: var(--wpl-card);
  color: var(--wpl-accent-dark);
}
/* Erases the .wpl-tabbar divider line directly under the active tab --
   the "line wraps over and around the clicked tab" effect -- without
   touching the tab's own box model. A small absolutely-positioned
   strip, sized to the tab's own width and colored to match the body,
   sits just below it and paints over that one segment of the line. */
.wpl-tab.active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1.5px;
  height: 1.5px;
  background: var(--wpl-card);
}
.wpl-tab-soon {
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--wpl-muted);
  background: var(--wpl-tan);
  border-radius: 5px;
  padding: 2px 5px;
}
.wpl-tab.active .wpl-tab-soon { color: var(--wpl-accent-dark); background: #e6f6fb; }

/* ---------- Shared instrument block (key selector + readout + piano) —
   stays in the same position across every tab; only .wpl-panel below it
   swaps content. ---------- */
.wpl-instrument { margin-bottom: 10px; }
/* Chris (desktop only): "there is not enough space above #1 and below
   the tabs. move #1 and everything in this image down a few pixels so
   everything is closer to #2. but don't move #2." #1 sits right below
   .wpl-tabbar's own margin-bottom, and #2 sits right below
   .wpl-instrument's -- moving #1 down without moving #2 means growing
   the first gap and shrinking the second by the same amount, so the
   total space the whole instrument block occupies (and therefore where
   #2 lands) never changes, only where #1 sits within it. */
@media (min-width: 521px) {
  .wpl-tabbar { margin-bottom: 14px; }
  .wpl-instrument { margin-bottom: 6px; }
}

/* ---------- Section labels ---------- */
/* Chris, 2026-09-07: people he sent the app to cold "didn't really
   understand the flow... their eyes were naturally drawn to the big blue
   buttons instead of working from the top down." These labels are the
   only thing telling someone there IS a top-down order (1, then 2, then
   3), but at 11px muted gray they lost every visual fight against the
   large colored buttons directly below them.
   First pass enlarged the label text itself (11px->13px) and its
   margin (5px->7px) too, on top of adding the circle -- but that grew
   the shell's total height, and Chris set a hard rule right after:
   "the shell can't get bigger. it was the perfect size on my phone
   before." So font-size/margin-bottom here are back to their exact
   original values (measured: at these values the circle adds zero net
   height to the row, since a small circle fits inside the existing
   line box). All the emphasis now comes from the circle alone --
   .step-num, sized to stay within that existing row height.
   Circle color: teal (var(--wpl-accent)) was the same color as an
   already-selected/active button directly below it, so the badge
   visually blended into the button row instead of standing apart from
   it -- Chris: "should the 1,2,3 buttons be in red?" Switched to the
   app's existing right-hand/chord-tone red (#f87171) rather than
   inventing a new color, since it's already established elsewhere in
   this same UI as "a note of emphasis," just applied to step numbers
   instead of piano keys -- and unlike teal, it never appears on any
   button in this row, so it can't blend in the same way. */
.section-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--wpl-muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-bottom: 5px;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Chris, 2026-09-12: the 1, 2, 3 step circles were removed from every
   section label ("maybe i don't need those 1,2,3 circles now"). The
   labels keep their exact size, so the card doesn't change. Their SVG
   markup and styles live in git history (checkpoint bcb1b12) if they're
   ever wanted back. On desktop the 14px circle was what set the row's
   height (the text alone is about 13px), so the label keeps a 14px
   minimum there. */
@media (min-width: 521px) {
  .section-label { min-height: 14px; }
}

/* ---------- Shared button feel: progression tabs, key tabs, step buttons
   all hover/select the same way — a quiet lift, no colored ring. Progression
   tabs get their own light-blue rest color (matching the reference); key
   tabs and step buttons share the tan. ---------- */
.prog-tab, .key-tab, .step-btn {
  border: 1.5px solid var(--wpl-line);
  cursor: pointer;
  text-align: center;
  color: var(--wpl-ink);
  min-width: 0;
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease, color 0.15s ease;
}
.prog-tab.active, .key-tab.active, .step-btn.active {
  background: var(--wpl-accent); color: #fff; border-color: var(--wpl-accent);
  box-shadow: var(--wpl-active-shadow);
  transform: translateY(0);
  /* Chris, 2026-09-06, from a frame-by-frame screen recording: "the blue
     buttons taking 9 frames [0.15s] to fade in... gives the appearance of
     being late when the piano lights up completely in 1 or 2 frames." The
     OLD button fading back to tan still uses the shared 0.15s transition
     above (the plain, non-.active rule) -- Chris confirmed that half
     "looks good" (the previous selection melting away). Only fading IN
     was the problem: CSS applies whichever transition value is declared
     on the state a property is arriving at, so redeclaring transition
     here without background makes a newly-active button's fill snap on
     immediately instead of easing in -- matching how its own text color
     and the piano's own highlight already snap with no transition at all
     (see .prog-tab.active .num etc. above and light() in piano-engine.js).
     border-color/box-shadow/transform keep easing at 0.15s -- only the
     background fill itself was the one reading as late. */
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

/* ---------- Progression tabs ---------- */
.prog-tab {
  flex: 1; border-radius: 12px; padding: 14px 6px;
  background: var(--wpl-prog-bg); border-color: var(--wpl-prog-border);
  /* center (not flex-start): all 5 progression names are now single
     words (Foundation/Smooth/Soft/Big/Full), so every button's .num +
     .name block is the same height and centers identically — no more
     risk of a 2-line-wrapped name pushing one button's .num out of
     line with the rest. If a future name gets long enough to wrap
     again, revisit this (see git history 2026-08-30 for the prior
     flex-start fix and why it was needed then). */
  display: flex; flex-direction: column; justify-content: center;
  min-height: 78px;
}
/* Chris, 2026-09-06, from a screen recording: "on the bottom row it's
   correct, the button text changes from white to black right when you
   click it... but on the top row... the black text slowly fades to
   white making it look like the button is very slightly delayed."
   .step-btn.active already sets its own .n/.l colors explicitly further
   below, which is why it snaps instantly -- .prog-tab .num/.name never
   had that, so they were only ever inheriting .prog-tab.active's own
   `color: #fff`, which fades over the shared 0.15s color transition
   above. Explicit color here (like .step-btn's) makes it snap the same
   way, since neither .num nor .name declares its own transition. */
.prog-tab.active .num { color: #fff; }
.prog-tab.active .name { color: rgba(255,255,255,0.85); }
.prog-tab .num { font-weight: 800; font-size: 22px; line-height: 1.2; }
/* margin-top: 4px matches the vertical gap every other "label + caption"
   pairing in the app uses (.step-btn's flex gap:4px, both for the
   Progressions step-row and the Chords position row) — Chris, 2026-09-02:
   "make it all equal" after comparing this row's spacing to the step-row's
   and finding this one tighter (was 2px here, 1px on the Chords quality
   row's override below — now both 4px, one shared standard). */
.prog-tab .name { font-size: 12px; font-weight: 600; opacity: 0.9; margin-top: 4px; }

/* ---------- Key tabs ---------- */
.key-tab {
  flex: 1; border-radius: 8px; padding: 9px 0; font-weight: 600; font-size: 13px;
  background: var(--wpl-tan); color: var(--wpl-tan-text);
  white-space: nowrap;
  /* Fixed (px, not ratio) line-height so box height depends only on
     padding, never on font-size — .key-tab-wide sets a smaller
     font-size for 2-letter labels (Db/Eb/F#/Ab/Bb) and, since it never
     redeclares line-height, inherits this exact px value, keeping its
     row the same height as the 1-letter keys next to it. */
  line-height: 15px;
  /* Chris, 2026-09-06: "the choose a key buttons need to have the black
     text change to white immediately when clicking a new button like the
     bottom 2 rows do, instead of fading into white." .step-btn/.prog-tab
     snap instantly because their visible text lives in child .n/.l/.num/
     .name spans that never declare their own transition — explicit color
     set directly on those children (see .step-btn.active .n etc. above)
     just switches with no animation. .key-tab has no such child span;
     its label is a plain text node directly inside .key-tab itself, so
     it was inheriting the shared .prog-tab/.key-tab/.step-btn color
     transition below and fading over 0.15s instead of snapping. Redeclare
     the transition here without color (background/border/shadow/transform
     still ease) to opt this element back out of the color fade. */
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.key-tab.locked { opacity: 0.55; }

/* ---------- Step buttons ---------- */
.step-btn {
  flex: 1;
  border-radius: 8px;
  padding: 9px 4px 7px;
  background: var(--wpl-tan);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 60px;
}
.step-btn.active .n { color: #fff; }
.step-btn.active .l { color: rgba(255,255,255,0.85); }
/* RH/LH-colored hand initials on the Scales "Choose a View" buttons
   (see handColoredLabel/VIEW_HAND_CLASS in ui.js). Plain colored text --
   two earlier contrast attempts (a white text-shadow halo, then a solid
   white badge) both read as visual smudges once Chris saw them rendered.
   Chris, 2026-09-06: "keep the red and blue colors as is, but when it's
   clicked blue, then maybe make them a whiter red and whiter blue, that
   will make it pop more against a blue background" -- the .active
   overrides below lighten each hue only while its own button is the
   selected one (background becomes --wpl-accent teal), same idea as
   .n/.l above but a separate rule since these spans need their own hue,
   not white. */
.wpl-hand-rh { color: #f87171; }
.wpl-hand-lh { color: #38bdf8; }
.step-btn.active .wpl-hand-rh { color: #fecaca; }
.step-btn.active .wpl-hand-lh { color: #bfdbfe; }
/* Chris, 2026-09-06: "the buttons should pulse like the piano notes do" +
   "does the top row [Chords' Choose a Position] need the same highlight"
   as the bottom row already had -- .prog-tab never had a .playing rule at
   all (only .step-btn did), even though ui.js already toggles "playing"
   onto #wpl-position-tabs2's .prog-tab buttons every step (Chords tab)
   same as it does for .step-btn (Progressions' chord row); it was just
   silently doing nothing. Added .prog-tab.playing here so Chords' step
   highlight works the same way Progressions' already did, plus an
   animation so each new step's ring blooms in and settles rather than
   just snapping on -- same idea as the piano keys' own played-note flash
   (flashPlayedKey in piano-engine.js), so a step changing on the buttons
   reads as part of the same "this just played" language as the keys
   lighting up, not a separate effect. ui.js removes then re-adds the
   class on a fresh element each step (not the same node twice in a row
   in the common case), which is what lets this animation restart cleanly
   without needing piano-engine.js's reflow trick.
*/
.step-btn.playing, .prog-tab.playing {
  border-color: #f59e0b;
  box-shadow: 0 0 0 1px rgba(245,158,11,0.35);
  animation: wpl-step-pulse 1s ease-out;
}
/* Chris, 2026-09-06: "the gold ring is still too big. reduce it by
   another 50%." Resting ring down from 2px to 1px, pulse peak from 3px
   to 1.5px -- same proportions as before, just half the spread. */
@keyframes wpl-step-pulse {
  from { box-shadow: 0 0 0 1.5px rgba(245,158,11,0.6); }
  to { box-shadow: 0 0 0 1px rgba(245,158,11,0.35); }
}
/* Chris, 2026-09-02, follow-up (after reviewing Chords v2): "the Major/Minor
   titles on the OLD Chords tab's bottom row look bigger/bolder than Chords
   v2's... make that change on chords2 to match" + "make the progressions
   tab lower row title match... so the shell fonts are the same size" +
   later, after a scoped fix still left Practice's row out: "why do I keep
   having to tell you to resize text? they should be the same on all the
   tabs... unless the text on a button is too long." So this is the BASE
   rule now (not a per-ID override) — every consumer of plain .step-btn .n
   automatically gets the same weight/size with no per-tab patching needed:
   Progressions' #wpl-step-row, Chords v2's #wpl-quality-row2, AND
   Practice's #wpl-scale-hands-row/#wpl-scale-direction-row/
   #wpl-scale-octaves-row (Right/Both, Up/Down/Up-Dn, 1-2-3 8ve) all share
   this one rule. Matches the OLD Chords tab's #wpl-chord-quality-tabs
   .prog-tab .num sizing exactly (800/16px desktop, see that rule further
   down) — that's the one row this whole family is modeled on. Captions
   (.l) are untouched; only rows that use them (step-row, quality-row2) are
   affected, at 12px as before. */
.step-btn .n { font-weight: 800; color: var(--wpl-ink); font-size: 16px; line-height: 1.2; }
/* Chris, 2026-09-04: top-row subtitle (.prog-tab .name) and bottom-row
   subtitle (.step-btn .l) read as two different colors -- .name has no
   color of its own (inherits --wpl-ink at 0.9 opacity from the shared
   .prog-tab/.key-tab/.step-btn rule above), .l was hardcoded to
   --wpl-muted (gray). Matching .l to the same treatment (drop the
   hardcoded color, add the same 0.9 opacity) makes every subtitle the
   same color everywhere .step-btn .l is used -- Progressions' step-row,
   Chords v2's quality-row2, Scales' view-row. */
.step-btn .l { font-size: 12px; opacity: 0.9; line-height: 1.2; }

/* Full label ("Right Hand", "Left Hand") vs short label ("RH", "LH") --
   Chris, 2026-09-04: short only on mobile, full on desktop. Short is the
   default/safe-everywhere text; full switches on at each consumer's own
   min-width query below. (The Choose a Scale row's title used to need
   this too, to abbreviate "Pentatonic" to "Pent" -- Chris, 2026-09-05,
   moved "Pentatonic" down to the button's subtitle instead, always
   spelled out, so that row no longer uses these classes at all -- see
   .wpl-name-wide below.) */
.wpl-lbl-full { display: none; }
.wpl-lbl-short { display: inline; }
/* Scale readout ("C", "Cm", ...): spelled-out Major/Minor only above the
   readout box's own mobile/desktop split (140px box starts here, same
   breakpoint the .wpl-readout-row mobile override below uses) -- Chris,
   2026-09-05: "on desktop... change the text back from C to C Major and
   Cm to C Minor." Below this width the compact "C"/"Cm" stays (matches
   the smaller 112px/88px boxes at narrower widths). */
@media (min-width: 521px) {
  #wpl-chord-name .wpl-lbl-full { display: inline; }
  #wpl-chord-name .wpl-lbl-short { display: none; }
  /* Chris, 2026-09-10: "you changed it on desktop too. i didn't want
     that. just mobile." -- same full/short split as #wpl-chord-name
     above, so desktop (plenty of room) keeps "RH Fingers"/"LH Fingers"
     spelled out (SCALE_VIEWS' nFull in ui.js) and only mobile drops to
     the short "RH"/"LH" (nShort), which is what .wpl-lbl-short/.wpl-
     lbl-full already default to below this width. */
  #wpl-scale-view-row .wpl-lbl-full { display: inline; }
  #wpl-scale-view-row .wpl-lbl-short { display: none; }
  /* Practice's Choose hands row (2026-09-12): "Left Hand Octave" / "Left
     Hand Top Note" / "Right Hand" on desktop, "LH Oct" / "LH Top" / "RH"
     on mobile, same split. */
  .wpl-practice-hands-row .wpl-lbl-full { display: inline; }
  .wpl-practice-hands-row .wpl-lbl-short { display: none; }
}
/* On-key note/number labels: same 11px/10px (white/black) at every width
   until now -- Chris, 2026-09-05: "on desktop only, can you make the note
   names/number names any bigger?" Base size (mobile/tablet keys are too
   narrow for anything bigger) stays the original inline-style values;
   only bumped from 521px up, same "desktop" threshold as the readout
   box above. */
.key-label-white { font-size: 11px; }
.key-label-black { font-size: 10px; }
@media (min-width: 521px) {
  .key-label-white { font-size: 15px; }
  .key-label-black { font-size: 13px; }
}
/* Choose a Scale row's title (.num) is always "Major"/"Minor" now --
   Chris, 2026-09-05, reordering the row into 2 groups of 3 -- so it no
   longer needs its own downscoped size like the old "Pentatonic" title
   did; it just uses the same .prog-tab .num sizing as every other row
   (12px/14px/22px across the same 340/520 breakpoints).
   "Pentatonic" moved down to be the subtitle (.name) instead, always
   spelled out rather than abbreviated to "Pent". .wpl-name-wide still
   flags just those two buttons' subtitles (see SCALE_TYPES "wide" flag
   in ui.js) for the 521-619px tier below, where "Scale"/"Blues" keep the
   row's normal, larger .name size and only "Pentatonic" needs its own
   smaller size to avoid wrapping.
   Below 521px, though, Chris, 2026-09-05 (mobile only): "make Pentatonic
   bigger... then reduce the subtitles Scale and Blues to be the same
   size as Pentatonic" -- so the two mobile tiers (see #wpl-scale-type-row
   .prog-tab .name further down, at the 341-520px and <=340px breakpoints)
   drop the .wpl-name-wide restriction and size ALL THREE subtitles
   (Scale/Pentatonic/Blues) the same, at the largest size that still fits
   "Pentatonic" -- the longest of the three -- at that tier's narrowest
   real device width (Playwright-verified: candidate sizes tested against
   the button's actual measured width, with wrap/overflow checked directly
   via scrollHeight and scrollWidth, at real phone widths 320-520px rather
   than the full theoretical breakpoint range, since e.g. 341-359px has no
   real device and its own tighter "pinch" from the row's padding/gap
   shrinking right at that boundary). 9px still covers 521-619px; 620px+
   is wide enough for the row's own normal .name size (12px) with real
   margin, so no override is needed there. */
@media (min-width: 521px) and (max-width: 619px) {
  #wpl-scale-type-row .prog-tab .name.wpl-name-wide { font-size: 9px; }
}
/* "Right Hand"/"Left Hand" became "RH Fingers"/"LH Fingers" (Chris,
   2026-09-04), shown at every width (nFull===nShort for this row again --
   "Finger Patterns" briefly lived here on 2026-09-05 but Chris caught it:
   "no! not 'finger patterns' on the bottom row! only the scale / view
   part above the piano," so this row is back to "RH Fingers"/"LH
   Fingers", see SCALE_VIEWS in ui.js) -- so unlike "Note"/"Number", this
   row's text can't just avoid wrapping by getting shorter below some
   breakpoint on its own; the fix here is shrinking the row's own
   .step-btn .n font at the widths where it otherwise wraps.
   Chris, 2026-09-05 (mobile only): "make the titles bigger, but don't
   let LH and RH fingers wrap... make the bottom subtitles the same text
   size as the top row subtitles." The old flat 8px/7px for all of
   <=520px actually still wrapped "RH Fingers"/"LH Fingers" at real
   320px-wide phones (Playwright-confirmed: scrollHeight jumps to a 2nd
   line at 8px there), so the <=520px tier is now split into four
   narrower bands, each the largest .n size that keeps "RH Fingers" on
   one line at that band's narrowest *real* device width (forced
   white-space:nowrap + measured scrollWidth against the button's actual
   width, tested across every common phone width 320-520px, not the
   theoretical breakpoint continuum): 7px for <=340px (real floor: 320px);
   8px for 341-374px (real floor: 360px); 8.25px for 375-411px (real
   floor: 375px); 9px for 412-520px (real floor: 412px). .l (the
   subtitle) is set to match the Choose a Scale row's own new unified
   subtitle size at the same two breakpoints it uses (7.75px <=340px,
   8px 341-520px) per Chris's "same as the top row" instruction, rather
   than following the .n tiers above -- both sets of numbers were
   verified together (all six buttons' .n and .l, at every real width
   320-520px) with zero wraps or overflow. 10px/9px still covers
   521-589px; 12px/10px still covers 590-899px; 900px+ keeps the row's
   standard, unscoped 16px/12px (already verified to fit there). */
/* Chris, 2026-09-10: "scales RH part is still wrapping" -- these tiers
   were tuned to the thinnest margin that measured as safe (as little as
   ~1-2px of clearance per the notes above), and that margin isn't
   holding up on Chris's real phones (rendering differences the headless
   measurements didn't catch). Dropping by a real, deliberate amount at
   every tier below instead of shaving another fraction of a px, so
   there's actual room to spare rather than a hairline fit. */
/* .n (the title) has no per-tier override here anymore -- Chris,
   2026-09-10: "make the bottom row of buttons just say RH and LH...
   increase the text size back to a good size where 'number' is the
   longest one." Dropping "Fingers" (see SCALE_VIEWS in ui.js) makes
   "Number" (6 letters) this row's longest label instead of "RH
   Fingers"/"LH Fingers" (10), so the row's normal shared .step-btn .n
   size (11px mobile / 9px <=340px / 16px desktop) now has real room to
   spare at every width -- no narrow-width shrinking needed. Only .l
   (the subtitle, "2 Octaves" etc) still needs its own smaller tiers. */
/* Chris, 2026-09-10: "make it 1px smaller on mobile. number is end to
   end" -- 1px below whatever this row's .n would otherwise inherit from
   the shared .step-btn .n mobile sizing (9px <=340px, 11px 341-520px),
   now that "Number" is the row's longest mobile label. */
@media (max-width: 340px) {
  #wpl-scale-view-row .step-btn .n { font-size: 8px; }
  #wpl-scale-view-row .step-btn .l { font-size: 7.75px; }
}
@media (min-width: 341px) and (max-width: 520px) {
  #wpl-scale-view-row .step-btn .n { font-size: 10px; }
}
/* Chris, 2026-09-10: "the 3 other tabs are still wrapping the bottom
   row" -- Chords v2's #wpl-quality-row2 has the exact same problem as
   #wpl-scale-view-row above (a long caption -- "1-♭3-5-♭7" for Min7 --
   wrapping to a 2nd line), but it only ever got a font-size fix at the
   <=340px tier (see the "extra-narrow safety net" block further down).
   341-520px silently fell back to the shared 10px .step-btn .l, which
   wraps "1-♭3-5-♭7" well before 340px, let alone up to 520 -- exactly
   the gap that let this keep happening on real 360-412px phones (Galaxy
   S8+, iPhone SE, Galaxy Z Fold 5) even after every other narrow-width
   fix. Filling in the same three tiers #wpl-scale-view-row already uses,
   at the same 8px value that row's own testing already verified fits a
   caption this length at these widths. */
@media (min-width: 341px) and (max-width: 374px) {
  #wpl-scale-view-row .step-btn .l { font-size: 8px; }
  #wpl-quality-row2 .step-btn .l { font-size: 8px; }
}
@media (min-width: 375px) and (max-width: 411px) {
  #wpl-scale-view-row .step-btn .l { font-size: 8px; }
  #wpl-quality-row2 .step-btn .l { font-size: 8px; }
}
@media (min-width: 412px) and (max-width: 520px) {
  #wpl-scale-view-row .step-btn .l { font-size: 8px; }
  #wpl-quality-row2 .step-btn .l { font-size: 8px; }
}
@media (min-width: 521px) and (max-width: 589px) {
  #wpl-scale-view-row .step-btn .l { font-size: 9px; }
}
@media (min-width: 590px) and (max-width: 899px) {
  #wpl-scale-view-row .step-btn .l { font-size: 10px; }
}

/* Choose a View row: every button now has both a title and a subtitle
   (Note/Names, Scale/Degrees, RH/1 Octave, etc, 2026-09-04) so plain
   .step-btn centering (shared rule above) already puts every title on
   the same plane -- no per-row override needed, same as Progressions'
   own step-row. (Earlier attempt used flex-start to compensate for 2
   of the 6 buttons having no subtitle; not needed once the labels
   were split.) */

/* ---------- Chord / number readout ---------- */
/* Two equal-width columns spanning the row edge to edge (no gap, no side
   padding), each value centered within its own half -- Chris, 2026-09-07:
   the old approach (both boxes a fixed width just big enough for "Root
   High"/"Root Low", grouped near the middle via justify-content:center +
   gap) left "D♭m Pentatonic" too little room and it silently wrapped to a
   hidden 2nd line on phones. Rather than keep shrinking text to fit a
   narrow box, each column now stretches "from the center of the screen to
   the end of the screen on both sides" (Chris) at every width, desktop
   included, and the same row is shared by all three tabs (Chord/Number,
   Position/Number, Scale/View) so this applies everywhere uniformly. No
   horizontal padding here means the columns reach the exact same
   left/right edges as the piano frame below.
   (A hand-with-numbered-fingers icon briefly lived in this row too,
   2026-09-05 -- removed again: even capped at a modest max size it was
   still taller than the readout boxes on every tier, which grew the
   row's own height and visibly pushed the piano down every time a
   Fingers view was selected. Chris: "your hand is too big which pushed
   that row down which made the entire shell move down... just delete
   the hand." A real PNG image, sized to fit inside the existing box
   height instead of growing it, is the likely next approach if he
   supplies one -- flagged here so the reasoning isn't lost if it comes
   back.) */
/* Chris (zoomed screenshot, desktop): "i was wrong about moving the
   scale / view out so much, it needs to be moved back in toward the
   middle. close enough that the text doesn't wrap." Each side used to
   be a fixed width:50% of the whole instrument card (set in index.html
   inline, up to 1100px wide -- see #wpl-wrapper's max-width), so
   "Scale" and "View" centered themselves at each half's own midpoint --
   roughly a quarter of the way in from either edge, with a wide gap of
   empty space between them.
   First fix dropped the fixed width entirely so each side would
   shrink-wrap its own label -- but a shrink-wrapped, centered-as-a-pair
   layout re-centers on every render, so a short value ("D♭m") sits in a
   different spot than a long one ("D♭m Pentatonic") even though it's
   the same on-screen column -- Chris: "they're moving in and out
   depending on the text length. they need to be fixed at the closest
   position where the longest text doesn't wrap" (both mobile and
   desktop). Fixed here instead: each column gets an explicit width
   (see the breakpoint overrides below), sized to the single widest
   string this app can ever put in EITHER column -- measured directly
   via Playwright by rendering every real chord name (transposed
   through all 12 keys), position name, scale name, and view label into
   the actual #wpl-chord-name element at each font-size tier and taking
   the max scrollWidth, then padded a bit further for safety margin
   against font differences on Chris's own device (see this file's git
   history/PR notes for the measured numbers). Text still centers
   within that fixed box via text-align:center (in index.html), so a
   short value just sits centered in the same unmoving box instead of
   shrinking the box around itself. */
.wpl-readout-row { display: flex; align-items: flex-end; justify-content: center; gap: 64px; margin: 8px 0 6px; }
.wpl-readout-row > div { width: 160px; }
.wpl-readout-label { font-size: 13px; color: var(--wpl-muted); }
/* Numbers/symbols ("C", "1", "5/7") read fine big and bold; full words
   ("Root High", "1st Inv") look oversized/heavy at the same size, so
   ui.js's setReadoutValue() adds this class whenever the raw value is
   longer than any real number/symbol ever gets (see ui.js comment). */
/* Chris, 2026-09-12 audit: the readout used to jump between a big size
   (short values like "C", "1") and a small one (words like "Root Low",
   "Notes") -- 24 vs 18px here, 19 vs 15 on phones -- so the same value
   looked different from screen to screen. Now one size per screen width,
   the largest that still fits the widest value this app can show
   ("D♭m Pentatonic") in its fixed box, with room to spare for device
   font differences: 20px here (widest measured 147 of 160px). */
.wpl-readout-row #wpl-chord-name, .wpl-readout-row #wpl-step-label,
#wpl-chord-name.long-label, #wpl-step-label.long-label { font-size: 20px !important; }

/* ---------- Piano ---------- */
/* Chris, 2026-09-12 (phone video): with the phone piano zoom, the keys
   showed through the frame's right edge, so the right border looked
   thinner than the left. The frame's edge is now a real border (same
   color, same width as the old padding, so the same size): overflow
   clips inside a border, never inside padding, so the zoomed piano
   always sits inside an even frame. */
#wpl-piano-frame { background: #1e293b; border-radius: 12px; padding: 0; border: 5px solid #1e293b; }
/* Each white/black key's own look (piano-engine.js's buildKeyboard,
   which still sets each key's position/size inline since that's
   computed per-key -- only the shared, static look lives here). Bottom
   corners are rounded on every key, matching a real keyboard's rounded
   key-fronts. Top corners stay square on every key EXCEPT the two at
   the very outer ends of the whole keyboard (.wpl-key-first/-last,
   added by buildKeyboard) -- Chris (zoomed screenshot): "the first and
   last piano keys need to be rounded at the top to match the round at
   the bottom. don't round the top of any of the middle keys though,
   just the first and last." Matching the bottom radius exactly (5px
   white / 4px black) so the outer corner reads as one continuous
   curve, not two different curves stacked. */
.wpl-white-key {
  flex: 1;
  background: linear-gradient(#fff, #f1f5f9);
  border: 1px solid #cbd5e1;
  border-radius: 0 0 5px 5px;
  position: relative;
  cursor: pointer;
}
.wpl-white-key.wpl-key-first { border-top-left-radius: 5px; }
.wpl-white-key.wpl-key-last { border-top-right-radius: 5px; }
.wpl-black-key {
  background: linear-gradient(#1e293b, #334155);
  border: 1px solid #000;
  border-radius: 0 0 4px 4px;
  cursor: pointer;
}
#wpl-status {
  text-align: center;
  font-size: 12px;
  color: var(--wpl-muted);
  margin-top: 3px;
  height: 18px;
  line-height: 18px;
  overflow: hidden;
}

/* Opt-in divider inside a button row (Scales' two rows, 2026-09-12) --
   same look as the key row's natural/accidental divider. It never grows,
   so the buttons on either side keep sharing the leftover width, and a
   partner row with a divider in the same spot keeps every button lined
   up. See makeRowDivider() in ui.js and CLAUDE.md's layout rules. */
.wpl-row-divider {
  flex: 0 0 3px;
  align-self: stretch;
  margin: 6px 3px;
  background: var(--wpl-line);
  border-radius: 2px;
}

/* ---------- Playback bar ---------- */
.wpl-playbar {
  display: flex;
  align-items: center;
  gap: 18px;
  background: var(--wpl-tan);
  border: 1.5px solid var(--wpl-line);
  border-radius: 12px; /* 2026-09-12 audit: was 14; the app uses 8 / 12 / 20 */
  padding: 9px 18px;
  margin-top: 22px;
  flex-wrap: wrap;
}
.wpl-bpm {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 180px;
}
.wpl-bpm-label { font-size: 12px; font-weight: 700; color: var(--wpl-tan-text); white-space: nowrap; }
.wpl-bpm-value { font-size: 13px; font-weight: 800; color: var(--wpl-ink); width: 40px; text-align: right; font-variant-numeric: tabular-nums; }
.wpl-bpm input[type="range"] {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 5px;
  border-radius: 3px;
  background: linear-gradient(var(--wpl-accent),var(--wpl-accent)) 0/var(--pct,50%) 100% no-repeat, #ddd2b3;
  outline: none;
}
.wpl-bpm input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--wpl-accent);
  border: 2.5px solid var(--wpl-card);
  box-shadow: 0 1px 4px rgba(60,50,20,0.3);
  cursor: pointer;
  margin-top: -0.5px;
}
.wpl-bpm input[type="range"]::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--wpl-accent); border: 2.5px solid var(--wpl-card);
  box-shadow: 0 1px 4px rgba(60,50,20,0.3); cursor: pointer;
}

.wpl-playbar-controls { display: flex; align-items: center; gap: 8px; }
.wpl-toggle-btn {
  display: flex; align-items: center; gap: 6px;
  border: 1.5px solid var(--wpl-line);
  background: var(--wpl-card);
  color: var(--wpl-tan-text);
  font-weight: 700;
  /* Kept at 12.5px (2026-09-12 audit): matching Play's 13px made these
     buttons 1px taller, which grew the card on every screen. */
  font-size: 12.5px;
  padding: 9px 13px;
  border-radius: 8px; /* 2026-09-12 audit: was 9; the app uses 8 / 12 / 20 */
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease, color 0.15s ease;
  white-space: nowrap;
  min-width: 0;
}
.wpl-toggle-btn.active { background: var(--wpl-accent); border-color: var(--wpl-accent); color: #fff; box-shadow: var(--wpl-active-shadow); }
.wpl-toggle-btn .wpl-icon { font-size: 14px; line-height: 1; }

.wpl-play-btn {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  border: 1.5px solid var(--wpl-accent);
  background: var(--wpl-accent);
  color: #fff;
  font-weight: 800;
  font-size: 13px;
  padding: 10px 18px;
  border-radius: 8px; /* 2026-09-12 audit: was 9; the app uses 8 / 12 / 20 */
  cursor: pointer;
  box-shadow: var(--wpl-active-shadow);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
  white-space: nowrap;
  min-width: 132px;
}
.wpl-play-btn.playing { background: #f59e0b; border-color: #f59e0b; }

/* ---------- Panels (the shell doesn't move on tab switch — the key
   selector + piano above this live in .wpl-instrument and never move at
   all, regardless of tab) ----------
   All 4 panels are stacked in the SAME grid cell (grid-row/grid-column:1),
   so .wpl-panels sizes itself to whichever panel is naturally tallest —
   automatically, with no min-height number to keep in sync by hand. Only
   the active one is visible; the rest sit underneath at visibility:hidden
   (not display:none, which would remove them from the height calculation
   entirely and defeat the whole point). Chris (2026-08-28): "I don't want
   to keep adjusting vertical heights for things" when adding future tabs
   or content — this is the fix, do NOT go back to per-panel min-height
   tuning. Adding tab #5 someday just means adding a 5th `.wpl-panel` in
   here; its height is picked up automatically. */
.wpl-panels { display: grid; }
/* .wpl-content-area: a plain block, one level up from .wpl-panels --
   NOT a tallest-wins grid itself. #wpl-instrument (piano/keys -- its own
   top-level block now, no longer nested inside .wpl-standard-view) and
   #wpl-standard-view (just Chords/Progressions/Scales now) stack in
   normal document flow, one after the other, and BOTH always reserve
   their real height even while hidden (visibility, not display:none --
   same trick .wpl-panel uses below, and Chris 2026-09-09: "the card
   needs to be the same size on every tab"). That combined height is
   what makes the card the same size on every tab, always, with no
   hand-picked number to keep in sync.
   #wpl-panel-practice is absolutely positioned on top of BOTH of them
   combined (inset:0 relative to THIS element) -- so by default Practice
   gets the FULL card, exactly like the hub did before Speed Drills
   existed (Chris spent a lot of rounds getting that tile grid to fill
   the card nicely -- losing that space to a dead gap where the
   instrument WOULD be was the bug in the first pass of this rework,
   2026-09-10: "it's ruined"). Only when a Speed Drill un-hides
   #wpl-instrument does ui.js's syncPracticePanelTop() push Practice's
   own top down by the instrument's real (measured) height, so the
   drill's content starts right where the instrument ends instead of
   overlapping it or leaving its own gap -- Chris, 2026-09-10: "the piano
   and speed and key picker needs to default back to their regular place
   when not hidden. anything in their way needs to move somewhere
   else." */
.wpl-content-area { position: relative; }
#wpl-instrument.wpl-instrument-hidden {
  visibility: hidden;
  pointer-events: none;
}
/* #wpl-panel-practice is deliberately taken OUT of normal document flow
   with position:absolute -- see .wpl-content-area's comment above for
   why (Practice never affects the card's height, and covers the full
   card by default). overflow-y:auto means a future module whose content
   is genuinely taller than the budget scrolls INSIDE its own box
   instead of pushing the card taller. */
#wpl-panel-practice {
  position: absolute;
  inset: 0;
  overflow-y: auto;
}
.wpl-panel {
  grid-row: 1;
  grid-column: 1;
  visibility: hidden;
  pointer-events: none;
  /* Without this, a grid item's default min-width:auto lets its own
     content's min-content size force the shared grid COLUMN wider than
     the wrapper — the width equivalent of the height "tallest wins"
     trick above, but as a bug: one panel's non-wrapping row (e.g. the
     Scales settings row) can blow out the column and drag every OTHER
     panel to that same width, overflowing the card on narrow screens
     (Chris, 2026-08-31 — caught via Playwright at 390px). min-width:0
     lets the item shrink to the column's real size so its own flex/text
     content wraps instead. */
  min-width: 0;
}
.wpl-panel.active {
  visibility: visible;
  pointer-events: auto;
}

/* ---------- Coming soon panel ---------- */
.wpl-soon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  text-align: center;
  color: var(--wpl-muted);
}
.wpl-soon-icon { font-size: 34px; margin-bottom: 12px; }
.wpl-soon-title { font-size: 17px; font-weight: 700; color: var(--wpl-ink); margin-bottom: 6px; }
.wpl-soon-text { font-size: 13.5px; max-width: 380px; margin: 0 auto; line-height: 1.5; }

/* ---------- Practice hub (tile grid, replaces old single "coming soon"
   panel) ----------
   Tiles reuse the app's existing visual language rather than inventing a
   new one: resting tan fill + var(--wpl-line) border, same as .key-tab/
   .step-btn; the "Soon" pill is the exact .wpl-tab-soon class the tab bar
   already uses. Only .practice-tile itself, the grid, and the sub-view
   toggle are new. Grid starts at 2 columns (fits the narrowest phones
   this app supports) and moves to 3 at the same 521px breakpoint several
   other rows in this file already switch at, rather than introducing a
   new one Chris would have to reason about separately. */
.practice-hub {
  /* height:100% makes this fill #wpl-panel-practice's fixed, resolved
     height (from its own position:absolute; inset:0) instead of sitting
     only as tall as 7-8 tiles naturally need with dead space below --
     Chris, 2026-09-09: "distributed evenly across the card". grid-auto-
     rows:1fr splits that full height evenly across however many rows
     the tile count and column-count breakpoint produce; a future 9th or
     10th tile just adds another equal-share row, no number to retune.
     align-items:center then keeps each .practice-tile at its own natural
     (moderate) size floating centered in that evenly-shared row band,
     instead of stretching to fill it edge to edge -- Chris, 2026-09-09:
     "they seem kind of big now" -- so the rows still spread evenly top
     to bottom across the full card, but the tile boxes themselves don't
     inflate to match. */
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  /* minmax(0, 1fr), not plain 1fr -- Chris, 2026-09-10: increasing
     row-gap pushed the grid taller than #wpl-panel-practice's fixed
     height and triggered its scrollbar ("that's not what I wanted").
     Plain 1fr rows never shrink below their own tiles' natural min-
     height, so a big enough gap or tile size has no room left and
     overflows. minmax(0, 1fr) lets rows compress instead, so this grid
     can never push the fixed card taller no matter how gap/tile-size
     get dialed in from here. */
  grid-auto-rows: minmax(0, 1fr);
  align-items: center;
  height: 100%;
  /* Chris, 2026-09-10: "the inner gap of both, needs to be the same
     outer gap" -- the outer gap is #wpl-wrapper'''s own left/right
     padding (10px at this width, see the <=520px #wpl-wrapper rule),
     which is the actual outer boundary around this grid since neither
     #wpl-panel-practice nor .practice-hub add any padding of their
     own. Matching row-gap/column-gap to that same 10px makes the
     spacing between tiles read as the same rhythm as the spacing
     around them, instead of tiles nearly touching inside a much wider
     border. */
  row-gap: 12px;
  column-gap: 2px;
}
.practice-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  /* Chris, 2026-09-10: "when i say reduce the gap, i mean make the
     modules bigger to make the gap smaller" -- growing the tile itself
     (not shrinking row-gap/column-gap) is now the standing way to close
     up perceived empty space on this grid. */
  padding: 17.82px 8px;
  width: 92%;
  justify-self: center;
  border-radius: 12px;
  border: 1.5px solid var(--wpl-line);
  background: var(--wpl-tan);
  text-align: center;
}
.practice-tile-icon { font-size: 21px; line-height: 1; }
.practice-tile-label { font-size: 12.5px; font-weight: 700; color: var(--wpl-ink); line-height: 1.25; }
/* Chris: "the spacing isn't even between the 6 mods." Some titles are
   one line, some are a manual two-line break (see SPEED_DRILL_MODULES
   in ui.js) -- align-items:center on .practice-hub centers each tile's
   own natural height within its row band, so a one-line tile and a
   two-line tile in the same row end up with different-looking gaps
   above/below even though the row bands themselves are equal. Reserving
   full two-line height for every title (whether it wraps or not) makes
   all six tiles the same height, so the gaps between them read as even.
   Chris, follow-up: "the top 2 module titles are 1 line, but they are
   sitting too high... either center it or move it on the 2nd line,
   whatever is cleanest" -- a one-line title inside that reserved
   two-line box was top-aligned by default (plain block flow), so it
   looked stuck to the top instead of sharing the box evenly the way a
   two-line title naturally does. flex + center centers it vertically
   within the same reserved height instead. */
#wpl-speed-drills-grid .practice-tile-label {
  min-height: 2.5em;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* All six subtitles are one line now (Chris: "i don't like the sub
   titles being 2 lines reword them so they fit on 1 line") -- no more
   two-line reservation needed, just enough for one line so nothing
   clips. */
#wpl-speed-drills-grid .practice-tile-subtitle { min-height: 1.3em; display: block; }
#wpl-speed-drills-grid .practice-tile-label-desktop { display: none; }
#wpl-speed-drills-grid .practice-tile-subtitle-desktop { display: none; }
/* Chris: "these 6 mods are too tall, shorten them a little bit." Less
   top/bottom padding than the default tile (17.82px) -- scoped to this
   grid only, the same way the height reservations above are. */
#wpl-speed-drills-grid .practice-tile { padding-top: 12px; padding-bottom: 12px; }
/* Chris, 2026-09-10: gave each Speed Drills module a title + a one-line
   subtitle describing what it trains -- plain PRACTICE_TILES entries
   (top-level hub) never set this, so it just doesn't render for them. */
.practice-tile-subtitle { font-size: 11px; /* 2026-09-12 audit: was 10.5 */ font-weight: 500; color: var(--wpl-ink); opacity: 0.65; line-height: 1.2; }
/* Soft color per tile (Chris, 2026-09-09: "it's so bland") -- a
   repeating 4-tint cycle so the grid reads as a coordinated set
   rather than one flat block of --wpl-tan. data-soon="true"'s
   existing opacity:0.6 mutes the not-yet-built tiles automatically,
   same as it already muted the icon/label. */
.practice-tile:nth-child(4n+1) { background: var(--wpl-tile-teal-bg); border-color: var(--wpl-tile-teal-border); }
.practice-tile:nth-child(4n+2) { background: var(--wpl-tile-peach-bg); border-color: var(--wpl-tile-peach-border); }
.practice-tile:nth-child(4n+3) { background: var(--wpl-tile-sage-bg); border-color: var(--wpl-tile-sage-border); }
.practice-tile:nth-child(4n+4) { background: var(--wpl-tile-gold-bg); border-color: var(--wpl-tile-gold-border); }
/* Only a tile with real content behind it (Speed Drills, Progression
   Drills today) is clickable -- everything still marked "soon" gets the
   same non-interactive treatment the tab bar's own "Soon" pill implies,
   so there's no dead click to discover on a tile that does nothing. */
.practice-tile[data-soon="false"] {
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.practice-tile[data-soon="true"] { opacity: 0.6; }
.practice-tile .wpl-tab-soon { margin-top: 1px; }

/* Sub-views (one per clickable tile) and the hub grid are never both
   visible -- same idea as .wpl-panel's own active-panel toggle, just
   nested one level deeper inside the Practice panel. */
.practice-subview { display: none; }
/* height:100% so a subview's own .practice-hub grid (Speed Drills'
   module list) can resolve ITS OWN height:100% against something real,
   same as the top-level #wpl-practice-hub does directly against
   #wpl-panel-practice -- otherwise that inner grid has no defined
   height to stretch against and its tiles just pack tight instead of
   spreading out with the same rhythm as the top-level hub. Chris:
   "the speed drills modules spacing need to be the same as the
   practice modules spacing." */
.practice-subview.active { display: block; height: 100%; }
/* Chris: "no small back buttons anywhere. it all needs to be the long
   one on the other modules." Matches .wpl-back-home-btn's own fix --
   a <button> with display:block still shrink-wraps to its content
   instead of filling the row the way a div would, so width:100% is
   needed explicitly. */
.practice-back { display: block; width: 100%; box-sizing: border-box; margin-bottom: 12px; }
/* One more nesting level, inside Speed Drills specifically: its own
   7-module grid vs. an individual module's practice screen (a playbar +
   back button -- the piano itself is #wpl-instrument, shared and shown
   above, not part of this level at all) -- same show/hide-one-at-a-time
   idea as .practice-subview just above. */
.speed-drill-level { display: none; }
.speed-drill-level.active { display: block; }
/* The grid-level screen (module tiles + bottom Back button) is the one
   .speed-drill-level that needs to actually fill the subview's height
   for its grid to match the top-level hub's spacing -- the 1-5 screen
   (the OTHER .speed-drill-level) has its own natural top-to-bottom
   layout and must not be touched, hence targeting this by id instead
   of broadening the shared .speed-drill-level.active rule above. flex
   column + the grid flexing (not the level itself) leaves the Back
   button its own natural height instead of the grid trying to claim
   the full 100% and pushing the button (and the card's scrollbar) past
   the bottom. */
#wpl-speed-drills-grid-level.active {
  display: flex;
  flex-direction: column;
  height: 100%;
}
#wpl-speed-drills-grid-level .practice-hub {
  flex: 1;
  min-height: 0;
}
.drill-back-btn { display: block; width: 100%; box-sizing: border-box; margin: 18px 0 0; }
/* Chris, 2026-09-10: "back button needs to sit at the bottom. don't put
   it under something, put it above something so it's not constantly
   moving up." The button used to just be the last thing in the markup,
   so its position depended on how tall the content above it happened to
   be (Notes/Numbers/Fingers labels aren't all the same height) -- it
   would visibly jump every time that content changed. Flex column +
   margin-top:auto pins it to the true bottom of the card instead: any
   leftover height becomes space ABOVE the button, not below it, so the
   button itself never moves. */
#wpl-speed-drill-1-5.active {
  display: flex;
  flex-direction: column;
  height: 100%;
}
#wpl-speed-drill-1-5 .wpl-playbar {
  margin-top: auto;
}
/* Progression Drills got the same two-level grid-then-detail shape as
   Speed Drills (2026-09-11) -- these rules replace the single flat
   .practice-back pin above with the exact same per-level pattern
   #wpl-speed-drills-grid-level/#wpl-speed-drill-1-5 already use, so
   #wpl-practice-sub-progdrills itself goes back to the plain
   .practice-subview.active (display:block) default and each inner
   level handles its own height/back-button pinning. */
#wpl-progdrills-grid-level.active {
  display: flex;
  flex-direction: column;
  height: 100%;
}
#wpl-progdrills-grid-level .practice-hub {
  flex: 1;
  min-height: 0;
}
#wpl-progdrill-pattern.active {
  display: flex;
  flex-direction: column;
  height: 100%;
}
#wpl-progdrill-pattern .wpl-playbar {
  margin-top: auto;
}

/* ---------- Home hub (replaces the top tab bar) ----------
   Chris, 2026-09-10: "what if i got rid of the tabs all together and
   started with 'What do you want to learn?' then below just 4
   modules... that way people will have a sense of what they're getting
   into before they are overwhelmed." Reuses .practice-hub's own tile
   look (.practice-tile) but its OWN grid wrapper, not .practice-hub
   itself -- .practice-hub's height:100% only resolves against
   #wpl-panel-practice's fixed absolute box, while #wpl-home-hub gets the
   exact same position:absolute;inset:0 treatment directly, one level up,
   so it fills that same shared card height itself (Chris, 2026-09-10:
   "the home card needs to be the same size as the module cards").
   flex+justify-content:center vertically centers the heading/tiles
   inside that full height instead of them sitting pinned to the top with
   dead space below -- Chris: "move the 'what do you want to learn' down
   and space out the modules." */
/* Chris, 2026-09-10: "the 4 modules need to fill up the whole card,
   with a good amount of space around them. I like the vertical center
   space between them. make that the exterior padding on the outside
   and inside." One spacing value (HOME_GAP) used for both the card's
   outer padding and the gap between tiles, so the rhythm reads as one
   consistent grid rather than tiles floating in leftover space. The
   tile grid is flex:1 so it stretches to fill whatever's left after the
   heading, instead of being sized by its own content and centered in a
   taller box (the earlier justify-content:center approach). */
#wpl-home-hub {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 40px;
}
.wpl-home-heading {
  text-align: center;
  font-size: 18px; /* 2026-09-12 audit: was 17 */
  font-weight: 700;
  color: var(--wpl-ink);
  margin-bottom: 40px;
  flex: none;
}
.wpl-home-tiles {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 40px;
}
/* Home tiles fill their whole grid cell (unlike .practice-hub's tiles,
   which stay a fixed natural size centered in extra row space) --
   overriding .practice-tile's own width:92%/justify-self:center, which
   exist so Practice's grids can leave a small visible gap around each
   tile without a real CSS gap. Scoped to the home hub only. */
#wpl-home-tiles .practice-tile {
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: 8px;
  box-sizing: border-box;
  justify-self: stretch;
}
/* Chris: "switch the scales and progressions image and color, but not
   the title." HOME_TILES in ui.js keeps its original label/grid order
   (icon swap only) -- color comes from :nth-child position, so swapping
   it needs its own override here, scoped to the home hub only. Position
   2 = Progressions (was peach, now sage/Scales' old color); position 3 =
   Scales (was sage, now peach/Progressions' old color). */
#wpl-home-tiles .practice-tile:nth-child(2) { background: var(--wpl-tile-sage-bg); border-color: var(--wpl-tile-sage-border); }
#wpl-home-tiles .practice-tile:nth-child(3) { background: var(--wpl-tile-peach-bg); border-color: var(--wpl-tile-peach-border); }
/* Chris, 2026-09-10: "on desktop make change progressions to chord
   progressions." Mobile default shows the short label; the existing
   desktop breakpoint below swaps which span is visible. */
.practice-tile-label-desktop { display: none; }
/* Chris: "put it at the bottom left" -- unlike .drill-back-btn's
   centered auto margin, this is a plain left-aligned block. */
/* Chris: "make the back to home button stretch across the bottom like
   this one on practice for every other module." Practice's copy only
   stretches full-width because its parent (#wpl-practice-hub-level) is
   a flex column, which stretches flex-item children by default -- the
   standard view's copy sits in a plain block parent, where a <button>
   with display:block still shrink-wraps its content instead of filling
   the container the way a div would. Explicit width:100% makes both
   copies match regardless of what kind of parent they're in. */
.wpl-back-home-btn { display: block; width: 100%; box-sizing: border-box; margin: 18px 0 0; }
/* Practice's own top-level hub needs the same grid-plus-button-below
   layout #wpl-speed-drills-grid-level already solved one level down --
   flex column so the tile grid takes only the space the button doesn't
   need, instead of the grid's height:100% pushing the button past the
   bottom into a scrollbar. */
#wpl-practice-hub-level {
  display: flex;
  flex-direction: column;
  height: 100%;
}
#wpl-practice-hub-level .practice-hub {
  flex: 1;
  min-height: 0;
}

@media (hover: hover) and (pointer: fine) {
  .practice-tile[data-soon="false"]:hover {
    box-shadow: var(--wpl-lift-shadow);
    border-color: var(--wpl-accent);
  }
  /* Chris: "prog and scales don't have the blue border on hover like the
     other 2 do." The color-swap override further down sets border-color
     directly with an ID-scoped selector, which (ID beats any number of
     classes) also beats this rule's border-color even while hovering.
     Matching that ID scope here, still inside the same hover-capable
     guard, restores the highlight on just those two tiles. */
  #wpl-home-tiles .practice-tile[data-soon="false"]:hover { border-color: var(--wpl-accent); }
}

@media (min-width: 521px) {
  /* Chris, 2026-09-10: "desktop still looks the same. gaps need to be
     smaller, or modules need to be bigger." The gap itself was already
     small (3px/6px) -- what actually reads as empty space on a wide,
     tall card is grid-auto-rows:1fr splitting the height evenly across
     rows while align-items:center holds each tile at a modest natural
     size, so most of a tall row band goes unused no matter how tiny
     the gap between rows is. Growing the tile itself (padding + width)
     is what actually shrinks that visual gap on desktop. */
  .practice-hub { grid-template-columns: repeat(3, 1fr); row-gap: 8px; column-gap: 10px; }
  .practice-tile { padding: 36px 12px; width: 90%; }
  .practice-tile-icon { font-size: 32px; }
  .practice-tile-label { font-size: 15px; }
  .practice-tile-label-mobile { display: none; }
  .practice-tile-label-desktop { display: inline; }
  /* Chris: "reduce the desktop spacing / gap between the top 3 and
     bottom 3 mods." Same root cause as the comment above (1fr rows
     splitting a tall card evenly, most of each row band going unused)
     -- scoped here to just the Speed Drills grid since its 2-row, 3-
     column layout is the one Chris flagged, without touching the main
     8-tile Practice hub. auto rows (sized to content) + an explicit
     row-gap makes the gap a fixed, small value instead of one that
     grows with the card's height; align-content:center keeps the
     compact 2-row block centered in the leftover space rather than
     stuck to the top or bottom. */
  #wpl-speed-drills-grid {
    grid-auto-rows: auto;
    align-content: center;
    row-gap: 28px;
  }
  /* Flip which span shows once the mobile-only <br> stacking (and
     Inversion Shifting's shortened subtitle) is no longer needed --
     Chris: "don't stack the titles on desktop, it's not needed. only
     for mobile." display:flex (not inline) on the label restores the
     same vertical-centering trick the base rule uses. */
  #wpl-speed-drills-grid .practice-tile-label-mobile { display: none; }
  #wpl-speed-drills-grid .practice-tile-label-desktop { display: flex; }
  #wpl-speed-drills-grid .practice-tile-subtitle-mobile { display: none; }
  #wpl-speed-drills-grid .practice-tile-subtitle-desktop { display: block; }
}

/* ---------- Demo lock overlay (covers readout + piano + legend + status
   when a locked key is tapped in demo mode; the key-selector row above it
   stays outside this wrapper and always clickable). ---------- */
.wpl-instrument-display { position: relative; }
.wpl-instrument-display.locked .wpl-instrument-content {
  filter: blur(2.5px);
  opacity: 0.35;
  pointer-events: none;
  user-select: none;
}
.wpl-lock-overlay {
  display: none;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 5;
}
.wpl-lock-overlay.show { display: flex; }
.wpl-lock-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  background: var(--wpl-card);
  border: 1.5px solid var(--wpl-line);
  border-radius: 16px;
  padding: 22px 30px;
  box-shadow: 0 10px 28px rgba(42,38,32,0.22);
}
.wpl-lock-icon { font-size: 28px; }
.wpl-lock-title { font-size: 19px; font-weight: 800; color: var(--wpl-ink); }
.wpl-lock-cta {
  display: inline-block;
  margin-top: 4px;
  padding: 13px 30px;
  background: var(--wpl-accent);
  border: 1.5px solid var(--wpl-accent);
  color: #fff;
  font-weight: 800;
  font-size: 15.5px;
  border-radius: 10px;
  text-decoration: none;
  box-shadow: var(--wpl-active-shadow);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

/* ---------- Hover states — real hover devices only ----------
   iOS Safari (and touch browsers generally) fires a CSS :hover state on tap
   and never clears it until a different element is tapped, since touch has
   no mouseleave. Every "lift" effect below was written assuming hover means
   "the cursor is over this" — on a phone it instead meant "the last thing I
   tapped," so tapping Click made Loop look permanently hovered until the
   next tap moved the stuck state onto Loop. Scoping all of it to
   (hover: hover) and (pointer: fine) means only real mice/trackpads ever
   see these effects; touch just gets the immediate tap feedback with
   nothing left stuck afterward. */
@media (hover: hover) and (pointer: fine) {
  .wpl-tab:hover { color: var(--wpl-ink); }
  .wpl-tab.active:hover { color: var(--wpl-accent-dark); }
  .prog-tab:hover, .key-tab:hover, .step-btn:hover {
    box-shadow: var(--wpl-lift-shadow);
    transform: translateY(-1.5px);
  }
  .prog-tab.active:hover, .key-tab.active:hover, .step-btn.active:hover { background: var(--wpl-accent-dark); border-color: var(--wpl-accent-dark); }
  .prog-tab:hover { background: var(--wpl-prog-hover); border-color: var(--wpl-prog-hover); }
  .key-tab:hover { background: var(--wpl-tan-hover); border-color: var(--wpl-tan-hover); }
  .step-btn:hover { background: var(--wpl-tan-hover); border-color: var(--wpl-tan-hover); }
  .wpl-toggle-btn:hover { background: var(--wpl-tan-hover); border-color: var(--wpl-tan-hover); box-shadow: var(--wpl-lift-shadow); transform: translateY(-1.5px); }
  .wpl-toggle-btn.active:hover { background: var(--wpl-accent-dark); border-color: var(--wpl-accent-dark); }
  .wpl-play-btn:hover { background: var(--wpl-accent-dark); border-color: var(--wpl-accent-dark); transform: translateY(-1.5px); }
  .wpl-play-btn.playing:hover { background: #d97706; border-color: #d97706; }
  .wpl-lock-cta:hover { background: var(--wpl-accent-dark); border-color: var(--wpl-accent-dark); transform: translateY(-1.5px); }
}

@media (max-width: 520px) {
  html, body { overflow-x: hidden; }
  #wpl-wrapper { padding: 16px 10px 18px !important; width: 100%; max-width: 100%; overflow-x: hidden; }
  /* Chris, 2026-09-10: "10px all around" -- mobile only, 40px stays the
     desktop default set above. Then: "move title down more and add more
     space between the top 2 and bottom 2 -- don't move bottom 2 down
     any more, just move top 2 up." align-content:end (was center) pins
     the two-row block to the BOTTOM of the tiles area, so growing
     row-gap only pushes the top row further up instead of pushing both
     rows apart from a centered midpoint. */
  #wpl-home-hub { padding: 40px 10px 10px; }
  .wpl-home-heading { margin-bottom: 26px; }
  .wpl-home-tiles {
    row-gap: 30px;
    column-gap: 10px;
    grid-template-rows: repeat(2, auto);
    align-content: start;
  }
  #wpl-home-tiles .practice-tile { height: 150px; }
  .wpl-trial-note { top: 8px; right: 6px; padding: 4px 7px; border-radius: 8px; }
  .wpl-trial-label { font-size: 7.5px; }
  .wpl-trial-clock { font-size: 10.5px; }
  /* Keep the centered eyebrow/title/subtitle clear of the corner badge —
     without this, centered header text can run right under it on narrow
     screens (the badge is position:absolute, so it doesn't reserve space
     on its own). */
  #wpl-wrapper.wpl-has-trial-badge .wpl-header { padding-right: 100px; }
  /* Chris, 2026-09-02: "the black border around the piano on mobile seems
     too thick. it looks more proportionate on desktop." Desktop's frame
     padding-to-frame-height ratio is 5/(118+2*5) ≈ 3.9%; mobile's old 4px
     padding on a 55px piano (frame = 55+2*4 = 63px) was ≈ 6.3% — visibly
     chunkier. "don't want that to move everyting up in the shell" — so the
     FRAME's total height must stay exactly 63px through every pass below.
     First pass used padding:2.5px + piano height:58px (58+2*2.5=63) which
     matched the ratio, but Chris then noticed "why does the left border
     seem thicker than the right border" — a real, measured artifact: a
     2.5px (half-pixel) padding lands asymmetrically on the device pixel
     grid, rendering 2 solid dark pixels on the left edge vs only 1 on the
     right (confirmed via pixel-level screenshot scanning). Second pass
     went to a whole-number padding:2px + piano height:59px (59+2*2=63,
     frame height still exactly preserved), which fixed the left/right
     symmetry — but Chris then noticed "the rounded corners are getting
     cut off" on mobile: with only 2px of dark material and an 8px
     border-radius shared by both the frame and the inner piano, there was
     barely any dark corner left to show the curve — the piano's own key
     content pokes almost right up to the rounded edge (confirmed via a
     15x cropped zoom of the top-left corner: the blue highlighted key's
     rounded top corner is nearly flush with the frame's own curve, with
     only a sliver of dark pixels between them). Chris: "make the mobile
     border 1px bigger all the way around" — padding goes to 3px (still a
     whole number, so the left/right pixel-symmetry fix above is
     unaffected) and piano height compensates down to 57px (57+2*3=63,
     frame height still exactly the same 63px as every prior pass — "don't
     want that to move everyting up in the shell" still holds). border-
     radius stays 8px (unchanged) since it already matches #wpl-piano's
     own fixed inline border-radius:8px (index.html) — shrinking it would
     make the outer frame corner tighter than the inner piano's corner,
     which looks wrong. */
  #wpl-piano { height: 57px !important; }
  #wpl-piano span { font-size: 6px !important; }
  /* Black-key labels with an accidental ("E♭", "♭3", "C♯") stack on two
     lines here instead of running side by side -- a black key at this
     width is only ~6px wide, not enough for 2 characters at any legible
     size. Stacked in the label's own left-to-right order (see
     setKeyLabelText in piano-engine.js), so "E♭" is E-over-♭ but "♭3" is
     ♭-over-3 -- Chris, 2026-09-05: "stack the black keys on mobile," then
     "I dont' like 7b where 7 is on top of the flat. it needs to be
     reversed to flat is on top." Accidental line bumped from 5px to 6px
     the same day: "make the # and flat symbols on mobile be larger.
     there is more room to fit them." */
  #wpl-piano .key-label-stacked { display: flex !important; flex-direction: column; align-items: center; justify-content: center; gap: 0; }
  #wpl-piano .key-label-stacked .key-label-main { display: block; line-height: 1; }
  #wpl-piano .key-label-stacked .key-label-accidental { display: block; line-height: 1; font-size: 6px !important; }
  #wpl-piano-frame { padding: 0 !important; border-width: 3px !important; border-radius: 8px !important; }
  /* Chris (zoomed screenshot): "mobile keys at the bottom are too
     rounded, make them more like the desktop." Desktop's 5px/4px radius
     is a fixed value, but mobile keys are much narrower (many more keys
     fit in a lot less width), so that same absolute radius reads as a
     proportionally bigger curve relative to a key's own width -- more
     rounded-looking even though the number never changed. Scaled down
     to keep the same look-and-feel as desktop at mobile's narrower key
     width. */
  .wpl-white-key { border-radius: 0 0 3px 3px; }
  .wpl-white-key.wpl-key-first { border-top-left-radius: 3px; }
  .wpl-white-key.wpl-key-last { border-top-right-radius: 3px; }
  .wpl-black-key { border-radius: 0 0 2px 2px; }
  .wpl-title { font-size: 22px; }
  .prog-tab { padding: 8px 2px; min-height: 59px; }
  .prog-tab .num { font-size: 14px; }
  /* Chris, 2026-09-02: "the sub fonts on the top row can be a little
     bigger... make sure 'foundation' text looks good cause that's the
     longest text" — shared by Progressions' own top row (5 buttons, the
     tighter fit) and Chords v2's position row (4 buttons, more room per
     button). Playwright nowrap-probe against Progressions' narrower
     buttons — the binding case — at this breakpoint: "Foundation" fits up
     to 10.5px (57.16px natural vs 58.41px available) and wraps at 10.75px;
     10px keeps a real ~4px margin below that, still a clear step up from
     the old 8px. */
  .prog-tab .name { font-size: 10px; }
  .key-tab { padding: 6px 0; font-size: 11px; line-height: 12px; }
  /* 2-letter keys (Db/Eb/F#/Ab/Bb) keep the SAME font-size as the
     1-letter keys (Chris: text must match, not shrink) — only
     letter-spacing tightens slightly so the extra character still fits
     the same flex:1 box without wrapping. */
  .key-tab.key-tab-wide { letter-spacing: -0.3px; }
  #wpl-key-tabs { gap: 3px !important; }
  .step-btn { padding: 5px 1px; min-height: 44px; }
  /* Base rule now (see the desktop rule above), so every .step-btn .n
     consumer (step-row, quality-row2, Practice's Hand/Direction/Octaves)
     moves together automatically. */
  .step-btn .n { font-size: 11px; }
  .step-btn .l { font-size: 10px; }
  /* #wpl-position-tabs2 / #wpl-quality-row2 (Chords v2's own two rows)
     added to this shared list — Chris, 2026-09-02: "the mobile horizontal
     spacing between the buttons are not the same on progressions, practice
     and chords2" — these two rows were left out when this rule was first
     written (before Chords v2 existed) and were still falling back to
     their own 8px inline gap (index.html) at this breakpoint instead of
     shrinking with every other row's buttons. */
  /* Practice's two rows per drill screen added 2026-09-12 so they match
     Chords/Progressions/Scales exactly on mobile too. */
  #wpl-prog-tabs, #wpl-step-row, #wpl-scale-type-row, #wpl-scale-view-row, #wpl-position-tabs2, #wpl-quality-row2,
  #wpl-drill-view-row, #wpl-drill-hands-row, #wpl-progdrill-prog-tabs, #wpl-progdrill-hands-row { gap: 4px !important; }
  #wpl-prog-tabs, #wpl-scale-type-row, #wpl-position-tabs2, #wpl-drill-view-row, #wpl-progdrill-prog-tabs { margin-bottom: 6px !important; }
  /* Chris, 2026-09-01: "why do you have 3 rows of buttons? make it
     identical to chords/progressions that have 2 rows" — this row (Hand +
     Direction + Octaves) used to force Octaves onto its own 2nd line here
     via .wpl-scale-break, with its own compact min-height/font tuned just
     to fit that 2-line layout in Practice's height budget. Now that
     index.html removed the forced break, this row is a single line of 8
     .step-btns exactly like Progressions' #wpl-step-row / Chords' two
     rows — it inherits their shared .step-btn sizing above (gap/margin
     folded into the same selectors) instead of a bespoke override, and
     with only ONE line instead of two, Practice's total height only
     shrinks further below Chords/Progressions's shared max, so there's
     no shell-height risk (confirmed via qa/check-dimensions.js). */
  /* Chris: "same with mobile, move in not as much as desktop but closer
     toward the middle, but don't let the text wrap." Smaller gap than
     desktop's since the whole card is already much narrower here --
     the two sides sit closer to the middle by default at this width,
     so they need less pulling-in than the up-to-1100px-wide desktop
     card does. */
  .wpl-readout-row { margin: 4px 0 2px; gap: 20px; }
  .wpl-readout-row > div { height: 40px !important; width: 130px; }
  /* Choose a Scale row's subtitles (Scale/Pentatonic/Blues), unified --
     see the .wpl-name-wide comment further up for the full breakdown.
     This is the 341-520px tier (8px), scoped generically (not just
     .wpl-name-wide) so Scale/Blues shrink down to match Pentatonic
     instead of keeping the unscoped 10px .prog-tab .name mobile size. */
  #wpl-scale-type-row .prog-tab .name { font-size: 8px; }
  /* line-height is a fixed px value (not the desktop inline style's em-relative
     1.3), shared by both the normal and .long-label sizes below, so a
     short value ("C4") and a long one ("2nd Inv") sit on the same vertical
     center in the readout instead of the smaller font pulling its line up
     (Chris: "it sits higher than C4... it needs to be centered horizontally
     with C4"). */
  /* One readout size (2026-09-12 audit, see the desktop rule): 16px fits
     the widest value in the 130px phone box with ~10px to spare. */
  .wpl-readout-row #wpl-chord-name, .wpl-readout-row #wpl-step-label { font-size: 16px !important; line-height: 25px !important; }
  .wpl-readout-row #wpl-chord-name.long-label, .wpl-readout-row #wpl-step-label.long-label { font-size: 16px !important; line-height: 25px !important; }
  /* Chris: "the spacing above #2 and below the buttons of #3 could be
     a tiny bit less. it looks like bigger gaps there than the rest."
     This 16px mobile-only override was bigger than the 10px desktop
     uses -- with #wpl-status hidden right below it on mobile (see
     below), that extra margin read as leftover empty space rather
     than intentional breathing room. Matched back to desktop's 10px. */
  .wpl-instrument { margin-bottom: 10px; }
  .wpl-legend { font-size: 12px !important; gap: 14px !important; }
  .wpl-legend-dot { width: 9px !important; height: 9px !important; margin-right: 4px !important; }
  /* Chris: keep the "Tap any chord..." hint on desktop, but drop it on
     mobile — narrow screens don't have room for it without crowding the
     "3." heading above the step/position row. */
  #wpl-status { display: none; }
  .wpl-tab { padding: 7px 5px 9px; height: 27px; font-size: 9.5px; gap: 3px; }
  .wpl-tab.active { height: 28px; }
  .wpl-tab-soon { font-size: 7px; padding: 1.5px 3px; }
  .wpl-lock-card { padding: 16px 18px; }
  .wpl-lock-title { font-size: 16px; }
  .wpl-lock-cta { padding: 11px 22px; font-size: 14px; }
  /* Same note as .wpl-instrument above -- 22px above the tempo bar
     (the desktop value, inherited here by default) read as an
     oversized gap once the buttons/labels around it are mobile-sized. */
  .wpl-playbar { gap: 12px; padding: 10px 12px; margin-top: 14px; }
  .wpl-bpm { min-width: 100%; order: 1; }
  .wpl-playbar-controls { order: 2; width: 100%; justify-content: space-between; }
  /* :not(...) -- this rule exists to make Loop/Click share a horizontal
     row evenly on mobile, but every "back" button also carries
     .wpl-toggle-btn for its look while sitting in a VERTICAL flex
     column (#wpl-practice-hub-level, #wpl-speed-drill-1-5,
     #wpl-practice-sub-progdrills) -- flex:1 there means "grow to fill
     remaining height" instead of "share row width", stretching the
     button into a huge empty box instead of a normal-sized button
     pinned to the bottom by its own margin-top:auto. Found three times
     now (.wpl-back-home-btn, then .drill-back-btn and .practice-back)
     as each button got the same bottom-pinned flex-column treatment --
     excluding all three here instead of one at a time next time this
     happens. */
  .wpl-toggle-btn:not(.wpl-back-home-btn):not(.drill-back-btn):not(.practice-back), .wpl-play-btn { flex: 1; justify-content: center; min-width: 0; }
}

/* Extra-narrow safety net (2026-08-29): some embed contexts (e.g. a course
   platform sidebar that does not collapse on mobile) can hand our iframe
   less width than a normal phone screen. Below this the 520px mobile
   sizing can still be too wide for Loop/Click/Play or the progression/
   chord rows to fit without clipping, so shrink further and let the
   playbar buttons wrap onto a second line as a last resort instead of
   being cut off. */
@media (max-width: 340px) {
  #wpl-wrapper { padding: 12px 6px 14px !important; }
  .wpl-trial-note { top: 5px; right: 4px; padding: 2px 4px; border-radius: 6px; }
  .wpl-trial-label { display: none; }
  .wpl-trial-clock { font-size: 8px; }
  /* Header text itself also shrinks a touch here, on top of the smaller
     badge/reservation, specifically so "Worship Piano Lab" and the eyebrow
     still fit on one line each at the narrowest widths we support (as low
     as 288px — the ThriveCart-sidebar-squeeze case) instead of wrapping to
     a 2nd line, which would grow the header's height and start moving
     everything below it — the exact "changes the vertical layout" problem
     this badge is required not to cause. */
  #wpl-wrapper.wpl-has-trial-badge .wpl-header { padding-right: 50px; }
  #wpl-wrapper.wpl-has-trial-badge .wpl-eyebrow { font-size: 9px; letter-spacing: 1px; margin-bottom: 4px; }
  #wpl-wrapper.wpl-has-trial-badge .wpl-title { font-size: 18px; }
  #wpl-wrapper.wpl-has-trial-badge .wpl-subtitle { font-size: 11px; margin-top: 3px; }
  .prog-tab { padding: 6px 1px; min-height: 52px; }
  .prog-tab .num { font-size: 12px; }
  /* Same "a little bigger, keep Foundation on one line" request as the 520px
     rule above — Playwright nowrap-probe at this narrower breakpoint:
     "Foundation" fits up to 8.75px (47.64px natural vs 48.80px available,
     ~1.2px margin) and wraps at 9px, so 8.5px keeps a real ~2.5px margin
     while still stepping up from the old 7px. */
  .prog-tab .name { font-size: 8.5px; }
  .step-btn { padding: 4px 1px; min-height: 40px; }
  /* Same value the shared 520px rule above already uses, so no change is
     needed stepping down into this breakpoint. */
  .step-btn .n, .step-btn .l { font-size: 9px; }
  .key-tab { font-size: 9.5px; padding: 5px 0; line-height: 11px; }
  .key-tab.key-tab-wide { letter-spacing: -0.4px; }
  #wpl-key-tabs { gap: 2px !important; }
  /* Same fix as the 520px rule above: #wpl-position-tabs2 / #wpl-quality-row2
     added so Chords v2's own two rows shrink their gap along with every
     other row at this breakpoint too. */
  #wpl-prog-tabs, #wpl-step-row, #wpl-scale-type-row, #wpl-scale-view-row, #wpl-position-tabs2, #wpl-quality-row2,
  #wpl-drill-view-row, #wpl-drill-hands-row, #wpl-progdrill-prog-tabs, #wpl-progdrill-hands-row { gap: 3px !important; }
  /* Chords v2 (#wpl-panel-chords2): quality row's captions are plain
     .step-btn .l, which is 9px at this breakpoint (shared rule above) —
     fine for Progressions' own short captions ("2m", "1/3"), but Min7's
     "1-♭3-5-♭7" wraps to a 2nd line at 9px in this row's slightly
     different padding (Playwright probe: needs ≤8.25px to clear 35.3px of
     available width; wraps at 8.5px). Chris, 2026-09-02: "reduce text
     size only as much as to prevent wrapping... only on a smaller phone"
     — so this ONLY overrides the caption size, ONLY at this narrowest
     breakpoint (nothing changes at 340–520px or desktop, where it already
     fits at the shared 10px/12px sizes with room to spare), and only down
     to 8px — 2px of real margin below the exact 8.25px threshold, not
     shaved to the bone. */
  #wpl-quality-row2 .step-btn .l { font-size: 8px; }
  .wpl-readout-row > div { height: 34px !important; width: 100px; }
  /* Overrides the 341-520px tier's 8px above -- this narrowest phone
     tier (real floor: 320px) has slightly less room, so Scale/Pentatonic/
     Blues are unified at 7.75px here instead (see .wpl-name-wide comment
     near the 521-619px tier for the full breakdown). */
  #wpl-scale-type-row .prog-tab .name { font-size: 7.75px; }
  /* One readout size (2026-09-12 audit): same 11.5px the long values
     already used here, the size proven to fit the 100px box. */
  .wpl-readout-row #wpl-chord-name, .wpl-readout-row #wpl-step-label { font-size: 11.5px !important; }
  /* 11.5px. Was briefly dropped to 10px to squeeze "Finger Patterns" into
     this 88px box on real phones, but Chris shortened the View readout's
     finger-view label to "Fingers" instead (see VIEW_READOUT_LABELS in
     ui.js) -- same length as "Numbers", which already fit fine here --
     so the extra shrink is no longer needed and this reverts to the size
     tuned for the longest Scale readout values (Cm Pentatonic, etc). */
  .wpl-readout-row #wpl-chord-name.long-label, .wpl-readout-row #wpl-step-label.long-label { font-size: 11.5px !important; }
  .wpl-playbar { gap: 6px; padding: 8px 8px; }
  .wpl-playbar-controls { gap: 5px; flex-wrap: wrap; row-gap: 6px; }
  .wpl-toggle-btn, .wpl-play-btn { padding: 7px 5px; font-size: 10.5px; }
  .wpl-play-btn { min-width: 0; }
}
@media (max-height: 500px) {
  #wpl-piano { height: 95px !important; }
  #wpl-piano span { font-size: 8px !important; }
  /* Same two-line stacking as the portrait tier above -- landscape phones
     hit this height-based query at widths this app never gave black-key
     labels any stacking treatment at all, so a 2-character "C♯"/"♭3" ran
     side by side and didn't fit a black key here either (~10-14px wide)
     -- Chris, 2026-09-05: "on mobile landscape, the black notes are not
     stacked." Slightly bigger than the portrait tier's sizes since these
     keys have a bit more room. */
  #wpl-piano .key-label-stacked { display: flex !important; flex-direction: column; align-items: center; justify-content: center; gap: 0; }
  #wpl-piano .key-label-stacked .key-label-main { display: block; line-height: 1; }
  #wpl-piano .key-label-stacked .key-label-accidental { display: block; line-height: 1; font-size: 7px !important; }
  #wpl-piano-frame { padding: 0 !important; border-width: 4px !important; border-radius: 8px !important; }
}

/* ---------- Tile grids: one tile size everywhere (2026-09-12) ----------
   Chris: "i think theyre all random sizes. seems like should be 1 size for
   all of them and then adjust based on how many courses / drills." Every
   tile screen (Home, Practice, Speed Drills, Progression Drills) gets the
   same tile size at a given screen width and the same gap, and wraps
   automatically: sizeTileGrids() in ui.js picks how many tiles go per row
   from the tile count. Phones: 3 or fewer tiles stack full width (Chris:
   "i liked them stacked on top of each other"), otherwise 2 per row.
   Desktop: up to 3 per row, balanced (4 = 2x2, 8 = 3+3+2). A leftover
   tile in the last row is centered, and rows spread evenly down the card.
   Sizes measured to fit the fullest screen (Practice's 8 tiles, 4 rows on
   a phone) from 288px up with no clipped text. A NEW tile screen: add its
   grid id to these selector lists and to TILE_GRID_IDS in ui.js. Kept at
   the end of the file on purpose so it wins over the older per-grid tile
   rules above. */
#wpl-home-hub { padding-left: 0; padding-right: 0; }
#wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid {
  --tile-gap: 10px;
  --tile-h: 100px;
  --max-cols: var(--max-cols-phone, 2);
  --cols: var(--cols-phone, 2);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: space-evenly;
  gap: var(--tile-gap);
  width: calc((100% - (var(--max-cols) - 1) * var(--tile-gap)) / var(--max-cols) * var(--cols) + (var(--cols) - 1) * var(--tile-gap));
  margin-left: auto;
  margin-right: auto;
}
#wpl-home-tiles > .practice-tile,
#wpl-practice-hub > .practice-tile,
#wpl-speed-drills-grid > .practice-tile,
#wpl-progdrills-grid > .practice-tile {
  flex: 0 0 auto;
  width: calc((100% - (var(--cols) - 1) * var(--tile-gap)) / var(--cols));
  height: var(--tile-h);
  min-height: 0;
  box-sizing: border-box;
  padding: 6px 8px;
  margin: 0;
}
/* With one fixed tile size, live tiles no longer need the invisible
   "Coming Soon" pill that used to keep them as tall as the "soon" ones. */
#wpl-home-tiles > .practice-tile .wpl-tab-soon[style*="hidden"],
#wpl-practice-hub > .practice-tile .wpl-tab-soon[style*="hidden"],
#wpl-speed-drills-grid > .practice-tile .wpl-tab-soon[style*="hidden"],
#wpl-progdrills-grid > .practice-tile .wpl-tab-soon[style*="hidden"] { display: none; }
/* Chris, 2026-09-12: "it's a big block with a little title." Bigger icon
   and title on every tile screen. To make room in the busiest tiles
   (Speed Drills: icon, 2-line title, subtitle, Soon badge), the old
   two-line title reservation is dropped (the fixed tile size already
   keeps tiles equal) and the "Soon" badge sits in the top-right corner
   instead of taking its own line. Measured: no clipped text and the
   badge never touches the icon or title, from 288px wide to desktop. */
#wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { position: relative; }
#wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label, #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { min-height: 0; }
#wpl-home-tiles > .practice-tile .wpl-tab-soon, #wpl-practice-hub > .practice-tile .wpl-tab-soon, #wpl-speed-drills-grid > .practice-tile .wpl-tab-soon, #wpl-progdrills-grid > .practice-tile .wpl-tab-soon { position: absolute; top: 5px; right: 5px; margin: 0; }
/* Chris, 2026-09-12: tapping a not-ready ("soon") tile used to do nothing.
   Now the tile brightens and a "Coming Soon" overlay fades in over it for
   about 2 seconds (wireSoonTiles() in ui.js adds/removes .wpl-show-soon).
   It sits on top of the tile, so it never needs its own line of space. */
.practice-tile[data-soon="true"] { cursor: pointer; transition: opacity 0.25s ease; }
.practice-tile[data-soon="true"].wpl-show-soon { opacity: 1; }
.practice-tile .wpl-soon-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.88);
  color: var(--wpl-ink);
  font-size: 13px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.practice-tile.wpl-show-soon .wpl-soon-overlay { opacity: 1; }
@media (min-width: 521px) {
  .practice-tile .wpl-soon-overlay { font-size: 16px; }
}
#wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { font-size: 24px; }
#wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 14px; }
/* Small phones: slightly shorter tiles and tighter text so Practice's 4
   rows still fit (406px available at 320px wide). Same size on every
   screen at these widths. */
@media (max-width: 374px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --tile-h: 94px; --tile-gap: 8px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 4px 6px; gap: 2px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { font-size: 21px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 13px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 9.5px; }
}
@media (min-width: 521px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --max-cols: 3; --cols: var(--cols-desk, 3); --tile-h: 140px; --tile-gap: 12px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { font-size: 40px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 18px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 12px; }
}

/* ---------- Tap zones (2026-09-12 audit) ----------
   Apple's guideline is a 44px touch target; on phones the key row is
   26px tall and Loop / Click / Back 35px, Play 38px. Rather than making
   the buttons bigger (which would make the card taller and move every
   layout Chris has tuned), each one gets an invisible 44px tall touch
   zone centered on it. The key row's zone reaches into the "Choose a
   key" label and the readout, neither of which is tappable, so nothing
   steals a tap. Width stays the button's own width. */
.key-tab, .wpl-toggle-btn, .wpl-play-btn { position: relative; }
.key-tab::after, .wpl-toggle-btn::after, .wpl-play-btn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: max(100%, 44px);
  transform: translateY(-50%);
}
/* Back buttons sit at the very bottom of the card, so their zone grows
   upward only (there's 18px of empty space above them). Centered, it
   hung ~5px below the card and made the Practice screens think their
   content overflowed, which showed a scrollbar on desktop. */
.drill-back-btn::after, .wpl-back-home-btn::after { top: auto; bottom: 0; transform: none; }

/* ---------- Tile icons + More coming tile (round B, 2026-09-12) ----------
   Pick 2B: matching line icons (ICONS in ui.js) instead of emoji. An
   icon is 1em square, so the existing icon font sizes per screen width
   (24 / 21 / 40px) size it, and it takes a deeper shade of its tile's
   tint. Pick 3B: tiles still being built are hidden and listed on one
   dashed "More coming" tile, the same size as the others. */
.practice-tile-icon svg { width: 1em; height: 1em; display: block; }
.practice-tile:nth-child(4n+1) .practice-tile-icon { color: #0F766E; }
.practice-tile:nth-child(4n+2) .practice-tile-icon { color: #B4582F; }
.practice-tile:nth-child(4n+3) .practice-tile-icon { color: #4A7F37; }
.practice-tile:nth-child(4n+4) .practice-tile-icon { color: #97710F; }
/* Home swaps tiles 2 and 3's tints (see #wpl-home-tiles above); the icons follow. */
#wpl-home-tiles .practice-tile:nth-child(2) .practice-tile-icon { color: #4A7F37; }
#wpl-home-tiles .practice-tile:nth-child(3) .practice-tile-icon { color: #B4582F; }
.practice-tile.wpl-more-tile {
  background: transparent;
  border: 1.5px dashed var(--wpl-line);
  cursor: default;
}
.practice-tile.wpl-more-tile .practice-tile-icon { color: var(--wpl-muted); }
.practice-tile.wpl-more-tile .practice-tile-label { color: var(--wpl-tan-text); }

/* ---------- Phone piano zoom (round B pick 4B, 2026-09-12) ----------
   setPianoRange() in piano-engine.js adds .wpl-piano-zoomed on phones
   and sets --piano-zoom (35 / keys shown) and --piano-shift (first key
   shown / 35). The keyboard is drawn wider by the zoom and slid left, and
   the frame clips the rest. Key letters grow with the zoom (capped), and
   the slide is a short ease that switches off for reduced motion. */
#wpl-piano { transition: transform 0.25s ease, width 0.25s ease; }
#wpl-piano-frame.wpl-piano-zoomed { overflow: hidden; }
#wpl-piano-frame.wpl-piano-zoomed #wpl-piano {
  width: calc(100% * var(--piano-zoom));
  transform: translateX(calc(-100% * var(--piano-shift)));
}
#wpl-piano-frame.wpl-piano-zoomed #wpl-piano span { font-size: calc(6px * min(var(--piano-zoom), 1.6)) !important; }
#wpl-piano-frame.wpl-piano-zoomed #wpl-piano .key-label-stacked .key-label-accidental { font-size: calc(6px * min(var(--piano-zoom), 1.6)) !important; }
@media (prefers-reduced-motion: reduce) {
  #wpl-piano { transition: none; }
}

/* ---------- Demo countdown in the eyebrow line (2026-09-12) ----------
   Chris: the corner badge (.wpl-trial-note, now unused) pushed the title
   off center on phones. In the demo the countdown replaces the small
   "Worship Piano Academy" line above the title (startTrialCountdown in
   ui.js): same font size and line height as the eyebrow, one line,
   centered, so the header is exactly the full app's size. The old
   .wpl-has-trial-badge rules no longer apply (that class isn't added). */
.wpl-eyebrow.wpl-eyebrow-trial { white-space: nowrap; }
.wpl-eyebrow-clock {
  color: var(--wpl-accent-dark);
  text-transform: none;
  letter-spacing: 0.4px;
  font-variant-numeric: tabular-nums;
}

/* ---------- List tiles (home screen pick C, 2026-09-12) ----------
   Chris picked "List" on the Home Screen Picks page, then: "the rest of
   the modules should look like that now." Every tile screen (Home,
   Practice, Speed Drills, Progression Drills) shows wide rows: the icon
   in a white box on the left, the name and a short line, an arrow on the
   right (none on the dashed More coming tile). Phones: one row per tile;
   desktop: two per row (sizeTileGrids in ui.js). The rows sit together as
   one group, centered in the space, and on Home the question sits right
   above them. Full titles show at every width, since a row has room.
   Kept last so it wins over the Tile grids rules above. Card size
   unchanged. */
#wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid {
  --tile-h: 80px;
  /* Chris, 2026-09-12 (after card size E): "the home buttons on mobile
     seem too close together now." Wider gaps, same row size. */
  --tile-gap: 16px;
  align-content: center;
}
#wpl-home-hub { justify-content: center; padding-top: 8px; padding-bottom: 20px; }
#wpl-home-hub .wpl-home-heading { margin-bottom: 16px; }
#wpl-home-tiles { flex: none; }
#wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  /* Two rows (name, short line) centered as a pair; the icon and arrow
     span both. The name hugs the bottom of its row and the line the top,
     so they stay together however tall the icon makes the rows. */
  grid-template-rows: auto auto;
  align-content: center;
  column-gap: 14px;
  row-gap: 0;
  align-items: center;
  justify-items: start;
  text-align: left;
  padding: 0 14px;
}
#wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon {
  grid-column: 1; grid-row: 1 / -1;
  display: grid; place-items: center;
  width: 48px; height: 48px; border-radius: 12px;
  background: #fff; box-shadow: 0 1px 2px rgba(42, 38, 32, 0.08);
  font-size: 26px;
}
#wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { grid-column: 2; grid-row: 1; align-self: end; min-height: auto; font-size: 16px; line-height: 1.2; }
#wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { grid-column: 2; grid-row: 2; align-self: start; min-height: auto; margin-top: 3px; font-size: 12.5px; line-height: 1.25; }
#wpl-home-tiles > .practice-tile .practice-tile-label-mobile, #wpl-practice-hub > .practice-tile .practice-tile-label-mobile, #wpl-speed-drills-grid > .practice-tile .practice-tile-label-mobile, #wpl-progdrills-grid > .practice-tile .practice-tile-label-mobile, #wpl-home-tiles > .practice-tile .practice-tile-subtitle-mobile, #wpl-practice-hub > .practice-tile .practice-tile-subtitle-mobile, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle-mobile, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle-mobile { display: none; }
#wpl-home-tiles > .practice-tile .practice-tile-label-desktop, #wpl-practice-hub > .practice-tile .practice-tile-label-desktop, #wpl-speed-drills-grid > .practice-tile .practice-tile-label-desktop, #wpl-progdrills-grid > .practice-tile .practice-tile-label-desktop, #wpl-home-tiles > .practice-tile .practice-tile-subtitle-desktop, #wpl-practice-hub > .practice-tile .practice-tile-subtitle-desktop, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle-desktop, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle-desktop { display: block; }
#wpl-home-tiles > .practice-tile::after, #wpl-practice-hub > .practice-tile::after, #wpl-speed-drills-grid > .practice-tile::after, #wpl-progdrills-grid > .practice-tile::after {
  content: ""; grid-column: 3; grid-row: 1 / -1;
  width: 20px; height: 20px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238A8B82' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}
#wpl-home-tiles > .practice-tile.wpl-more-tile::after, #wpl-practice-hub > .practice-tile.wpl-more-tile::after, #wpl-speed-drills-grid > .practice-tile.wpl-more-tile::after, #wpl-progdrills-grid > .practice-tile.wpl-more-tile::after { display: none; }
@media (max-width: 374px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --tile-h: 74px; --tile-gap: 12px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 0 10px; column-gap: 11px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { width: 42px; height: 42px; font-size: 23px; border-radius: 11px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 15px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 11.5px; }
}
@media (min-width: 521px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --max-cols: 2; --cols: var(--cols-desk, 2); --tile-h: 116px; --tile-gap: 18px; max-width: 840px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 0 20px; column-gap: 18px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { width: 62px; height: 62px; font-size: 34px; border-radius: 16px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 19px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 14px; margin-top: 4px; }
  #wpl-home-tiles > .practice-tile::after, #wpl-practice-hub > .practice-tile::after, #wpl-speed-drills-grid > .practice-tile::after, #wpl-progdrills-grid > .practice-tile::after { width: 24px; height: 24px; }
}

/* ---------- Screen slide (2026-09-12) ----------
   slideScreen() in ui.js adds one of these to #wpl-content-area (all of
   the card under the header) on every screen change: from the right when
   going deeper, from the left on Back, with a fade. The wrapper clips
   sideways so the slide never flashes a scrollbar. */
#wpl-wrapper { overflow-x: clip; }
/* Pick B, same speed: slideScreen() in ui.js slides the real area and a
   still copy of the old screen (.wpl-slide-ghost) side by side. */
.wpl-slide-ghost { position: absolute; margin: 0; pointer-events: none; z-index: 2; }
@media (min-width: 521px) {
  /* Chris: "desktop, they seem a little low. can you move them up a
     little?" The tile groups sit a bit above the middle on desktop. */
  #wpl-home-hub { padding-bottom: 100px; }
  #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { padding-bottom: 60px; box-sizing: border-box; }
}

/* ---------- Demo exit button (2026-09-12) ----------
   Chris: the Demo C Only page will have no site header or footer, so the
   demo's home screen gets a back button like every other screen, pinned
   to the same spot at the bottom of the card. It's a link to the website
   (#wpl-demo-exit), shown only in demo mode. The tile group moves up a
   little in the demo so it never crowds the button. */
#wpl-demo-exit {
  position: absolute;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  margin: 0;
  text-align: center;
  text-decoration: none;
  color: var(--wpl-ink);
}
#wpl-demo-exit[hidden], #wpl-home-hub:not(.active) + #wpl-demo-exit { display: none; }
#wpl-wrapper.wpl-demo #wpl-home-hub { padding-bottom: 64px; }
@media (min-width: 521px) {
  #wpl-wrapper.wpl-demo #wpl-home-hub { padding-bottom: 120px; }
}

/* ---------- Card size E (2026-09-12) ----------
   Card Size Picks: Chris picked E, "more space, not bigger buttons...
   stretched piano looks bad" (the Demo C Only page lost its site header
   and footer, and students open the app full screen, so the card has
   room to be taller). Nothing changes size: every button, row and the
   piano stay exactly as they were. The card grows by three equal gaps,
   --wpl-gap-x each: below the key and piano block (group 1), above the
   play bar, and above the Back button. The header stays tight above the
   key row. Width only (never viewport height): inside an embed the
   iframe's height follows the app's own height, so a height rule could
   feed back on itself. Practice/drill screens follow automatically:
   they start where the standard view starts (syncPracticePanelTop) and
   fill the same height, with their play bar pinned to the bottom. */
#wpl-wrapper { --wpl-gap-x: 24px; }
@media (max-width: 520px) { #wpl-wrapper { --wpl-gap-x: 32px; } }
@media (max-width: 340px) { #wpl-wrapper { --wpl-gap-x: 8px; } }
#wpl-instrument { padding-bottom: var(--wpl-gap-x); }
.wpl-panel .wpl-playbar { margin-top: calc(22px + var(--wpl-gap-x)); }
@media (max-width: 520px) { .wpl-panel .wpl-playbar { margin-top: calc(14px + var(--wpl-gap-x)); } }
#wpl-content-area .wpl-back-home-btn:not(.wpl-demo-exit), #wpl-content-area .drill-back-btn { margin-top: calc(18px + var(--wpl-gap-x)); }

/* ---------- Bigger list rows on phones (2026-09-12) ----------
   Chris (phone screenshot, a 360px wide Android, after card size E):
   "4 boxes still too small and close together. Same for the rest of the
   mods." The taller card left the rows floating in empty space, so on
   phones every tile screen gets taller rows, wider gaps, and a slightly
   bigger icon and text to fill them. Screens 340px and narrower keep the
   smaller rows (the demo's home rows must still clear its back button
   there). Desktop unchanged.
   Then: "need like 50% bigger. maybe not that big." 110px rows is the most
   that fits Home's four rows plus the question in the card (and clear of
   the demo's back button, which moves 8px closer), about 20% bigger. */
@media (max-width: 520px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --tile-h: 104px; --tile-gap: 16px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 0 16px; column-gap: 16px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { width: 60px; height: 60px; font-size: 34px; border-radius: 16px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 19px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 14px; }
}
@media (max-width: 374px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --tile-h: 104px; --tile-gap: 16px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 0 14px; column-gap: 14px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { width: 58px; height: 58px; font-size: 32px; border-radius: 15px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 18px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 13.5px; }
}
@media (max-width: 340px) {
  #wpl-home-tiles, #wpl-practice-hub, #wpl-speed-drills-grid, #wpl-progdrills-grid { --tile-h: 76px; --tile-gap: 12px; }
  #wpl-home-tiles > .practice-tile, #wpl-practice-hub > .practice-tile, #wpl-speed-drills-grid > .practice-tile, #wpl-progdrills-grid > .practice-tile { padding: 0 10px; column-gap: 11px; }
  #wpl-home-tiles > .practice-tile .practice-tile-icon, #wpl-practice-hub > .practice-tile .practice-tile-icon, #wpl-speed-drills-grid > .practice-tile .practice-tile-icon, #wpl-progdrills-grid > .practice-tile .practice-tile-icon { width: 42px; height: 42px; font-size: 23px; border-radius: 11px; }
  #wpl-home-tiles > .practice-tile .practice-tile-label, #wpl-practice-hub > .practice-tile .practice-tile-label, #wpl-speed-drills-grid > .practice-tile .practice-tile-label, #wpl-progdrills-grid > .practice-tile .practice-tile-label { font-size: 15px; }
  #wpl-home-tiles > .practice-tile .practice-tile-subtitle, #wpl-practice-hub > .practice-tile .practice-tile-subtitle, #wpl-speed-drills-grid > .practice-tile .practice-tile-subtitle, #wpl-progdrills-grid > .practice-tile .practice-tile-subtitle { font-size: 11.5px; }
}
/* Chris: "on live demo c the back button will be in the way of the bottom
   module." Rows 104px (from 110) and more room above the demo's back
   button, about 40px between it and the last row. */
@media (min-width: 341px) and (max-width: 520px) {
  #wpl-wrapper.wpl-demo #wpl-home-hub { padding-bottom: 72px; }
}
/* Chris: "the space under what do you want to learn, and above the back
   button should be equal." Phones, demo home only: the rows sit at the
   bottom of the hub, one --demo-gap above the back button (padding =
   button height + gap), and the question sits one --demo-gap above the
   rows, so the two spaces match. Desktop keeps its centered layout. */
@media (max-width: 520px) {
  #wpl-wrapper.wpl-demo { --demo-gap: 28px; --demo-exit-h: 36px; }
  #wpl-wrapper.wpl-demo #wpl-home-hub { justify-content: flex-end; padding-bottom: calc(var(--demo-exit-h) + var(--demo-gap)); }
  #wpl-wrapper.wpl-demo #wpl-home-hub .wpl-home-heading { margin-bottom: var(--demo-gap); }
}
@media (max-width: 340px) {
  #wpl-wrapper.wpl-demo { --demo-gap: 24px; --demo-exit-h: 29px; }
}

/* ---------- Back button arrow (2026-09-12) ----------
   Chris: "the back arrow... sits below. can you center it vertically?"
   The "←" text character sat low in DM Sans (and every device draws a
   font character a little differently). It's now a drawn arrow
   (.wpl-back-arrow in index.html, same line style as the tile icons),
   and the back buttons center the arrow and the words on one line. The
   arrow is shorter than the text line, so the buttons keep their height. */
.wpl-back-home-btn, .drill-back-btn, .practice-back { display: flex; align-items: center; justify-content: center; gap: 6px; }
/* Chris: "the text should be centered on the back button. the arrow
   shouldn't be included in that." The arrow's negative left margin cancels
   its own width plus the gap, so only the words are centered and the
   arrow hangs just to their left. */
.wpl-back-arrow { display: block; flex: none; width: 1.1em; height: 1.1em; margin-left: calc(-1.1em - 6px); }
.wpl-back-arrow svg { display: block; width: 100%; height: 100%; }
#wpl-demo-exit[hidden], #wpl-home-hub:not(.active) + #wpl-demo-exit { display: none; }
/* Chris: "the arrow is still too low. should be centered with a caps
   letter, not a small case letter." Flex centering put the arrow on the
   middle of the whole text line, which in DM Sans sits a hair below the
   middle of a capital letter (measured 0.38px low at 12.5px). The arrow
   is lifted by that amount (0.03em, so it scales with the text), and the
   demo's link gets the same line height as the back buttons so the same
   lift fits it too. Transforms don't affect layout, so nothing moves. */
.wpl-back-arrow { transform: translateY(-0.03em); }
#wpl-demo-exit { line-height: normal; }

/* ---------- Practice Path (2026-09-13) ----------
   Chris's level map, the first screen now (ui.js "Practice Path"). The
   map fills the card like every other screen and scrolls inside itself,
   so the card never changes size. Popups sit in the middle of the card
   over a slight blur (Chris: centered, not bottom sheets). */
#wpl-wrapper {
  --pp-gold: #F2B518; --pp-gold-dark: #C8940E; --pp-gold-bg: #faf2df; --pp-gold-border: #f0e2bf; --pp-gold-ink: #7a5a06;
  --pp-locked: #D9D2C5; --pp-locked-ink: #A39C90; --pp-red: #b4432f;
  --pp-peach-bg: #faece2; --pp-peach-border: #f2dcc9; --pp-peach-ink: #9a4f1c;
  --pp-sage-bg: #eaf3e2; --pp-sage-border: #dbe9cb; --pp-sage-ink: #3f6b22;
}
#wpl-panel-path { position: absolute; inset: 0; display: flex; flex-direction: column; min-height: 0; }
#wpl-wrapper.wpl-demo #wpl-panel-path { padding-bottom: 46px; }
.pp-top { flex: none; display: flex; align-items: center; gap: 8px; padding: 2px 0 10px; }
.pp-spacer { flex: 1; }
.pp-stat { display: inline-flex; align-items: center; gap: 6px; background: var(--wpl-card); border: 1px solid var(--wpl-line); border-radius: 999px; padding: 3px 10px 3px 5px; font-weight: 800; font-size: 13.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pp-flame { width: 20px; height: 20px; fill: #F97316; }
.pp-ringwrap { position: relative; width: 28px; height: 28px; }
.pp-ringwrap svg { display: block; }
.pp-ringwrap span { position: absolute; inset: 0; display: grid; place-items: center; font-size: 9.5px; font-weight: 800; }
.pp-ring-bg { stroke: var(--wpl-tan); }
.pp-ring-fg { stroke: var(--wpl-accent); transition: stroke-dashoffset 0.7s cubic-bezier(0.3, 0.7, 0.2, 1); }
.pp-tools { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--wpl-line); background: var(--wpl-tan); color: var(--wpl-ink); border-radius: 999px; padding: 7px 12px; font-size: 13px; font-weight: 700; cursor: pointer; white-space: nowrap; }
.pp-tools .pp-ico { width: 15px; height: 15px; }

.pp-chaps { flex: none; display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 10px; scrollbar-width: none; }
.pp-chaps::-webkit-scrollbar { display: none; }
.pp-chip { flex: none; border: 2px solid var(--wpl-line); background: var(--wpl-card); border-radius: 12px; padding: 5px 10px 4px; cursor: pointer; text-align: left; min-width: 72px; color: var(--wpl-ink); }
.pp-chip .k { display: block; font-size: 10.5px; font-weight: 800; color: var(--wpl-muted); letter-spacing: 0.04em; }
.pp-chip .n { display: block; font-weight: 800; font-size: 13.5px; white-space: nowrap; }
.pp-chip .s { display: flex; align-items: center; gap: 3px; font-size: 10.5px; font-weight: 700; color: var(--wpl-tan-text); margin-top: 1px; font-variant-numeric: tabular-nums; }
.pp-chip .s svg { width: 11px; height: 11px; }
.pp-chip .s .pp-star { fill: var(--pp-gold); stroke: var(--pp-gold-dark); stroke-width: 1.5; }
.pp-chip.locked { background: var(--wpl-tan); }
.pp-chip.locked .n { color: var(--pp-locked-ink); }
.pp-chip.badge .n { color: var(--wpl-accent); }
.pp-chip.sel { border-color: var(--wpl-ink); box-shadow: var(--wpl-lift-shadow); }

.pp-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-bottom: 10px; }
.pp-placecard { display: flex; align-items: center; gap: 12px; background: var(--pp-gold-bg); border: 1px solid var(--pp-gold-border); border-radius: 14px; padding: 10px 12px; }
.pp-placetxt { flex: 1; min-width: 0; font-size: 13.5px; line-height: 1.3; }
.pp-placetxt b { display: block; font-size: 14.5px; }
.pp-placetxt span { color: var(--wpl-tan-text); }
.pp-placecard button, .pp-testout button { flex: none; border: 0; background: var(--wpl-ink); color: #fff; font-weight: 700; border-radius: 10px; padding: 9px 13px; cursor: pointer; font-size: 13.5px; white-space: nowrap; }
.pp-card { background: var(--wpl-card); border: 1px solid var(--wpl-line); border-radius: 16px; padding: 14px; margin: 10px 0 4px; transition: opacity 0.2s ease; }
.pp-whead { display: flex; gap: 12px; align-items: flex-start; }
.pp-wbadge { flex: none; width: 48px; height: 48px; border-radius: 14px; background: var(--wpl-accent); color: #fff; display: grid; place-items: center; font-family: 'Fraunces', Georgia, serif; font-size: 21px; box-shadow: 0 3px 0 var(--wpl-accent-dark); }
.pp-whead.locked .pp-wbadge { background: var(--pp-locked); color: var(--pp-locked-ink); box-shadow: 0 3px 0 #C4BBAC; }
.pp-wtxt { min-width: 0; }
.pp-wt { font-size: 11px; font-weight: 700; color: var(--wpl-tan-text); letter-spacing: 0.04em; }
.pp-wname { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 19px; line-height: 1.15; margin-top: 2px; }
.pp-wskill { font-size: 13px; color: var(--wpl-tan-text); margin-top: 3px; line-height: 1.3; }
.pp-bar { height: 8px; background: var(--wpl-tan); border-radius: 99px; overflow: hidden; margin-top: 10px; }
.pp-bar i { display: block; height: 100%; background: var(--pp-gold); border-radius: 99px; transition: width 0.6s ease; }
.pp-wmeta { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 12.5px; color: var(--wpl-tan-text); margin-top: 6px; font-variant-numeric: tabular-nums; }
.pp-msg { margin-top: 12px; background: var(--wpl-tan); border-radius: 12px; padding: 10px 12px; font-size: 13.5px; color: var(--wpl-tan-text); line-height: 1.35; }
.pp-msg.ok { background: var(--pp-sage-bg); border: 1px solid var(--pp-sage-border); color: var(--pp-sage-ink); }
.pp-testout { margin-top: 12px; display: flex; align-items: center; gap: 10px; background: var(--pp-gold-bg); border: 1px solid var(--pp-gold-border); border-radius: 12px; padding: 9px 10px 9px 12px; font-size: 13px; line-height: 1.3; }
.pp-testout span { flex: 1; }
.pp-tag { display: inline-flex; align-items: center; font-size: 11.5px; font-weight: 700; border-radius: 999px; padding: 2px 9px; border: 1px solid transparent; white-space: nowrap; }
.pp-tag.dark, .pp-tag.t-boss { background: var(--wpl-ink); color: #fff; }
.pp-tag.t-game, .pp-tag.practice { background: var(--pp-peach-bg); border-color: var(--pp-peach-border); color: var(--pp-peach-ink); }
.pp-tag.t-play, .pp-tag.good { background: var(--wpl-prog-bg); border-color: var(--wpl-prog-border); color: var(--wpl-accent-dark); }
.pp-tag.t-lesson, .pp-tag.almost { background: var(--pp-gold-bg); border-color: var(--pp-gold-border); color: var(--pp-gold-ink); }
.pp-tag.t-song { background: var(--pp-sage-bg); border-color: var(--pp-sage-border); color: var(--pp-sage-ink); }

.pp-map { position: relative; width: 100%; max-width: 380px; margin: 0 auto; transition: opacity 0.2s ease; }
.pp-fading { opacity: 0; }
.pp-path { position: absolute; inset: 0; width: 100%; height: 100%; }
.pp-path path { fill: none; stroke: #ddd3c3; stroke-width: 7; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.pp-node { position: absolute; width: 54px; height: 54px; margin: -27px 0 0 -27px; border-radius: 50%; border: 0; padding: 0; display: grid; place-items: center; font-family: inherit; font-weight: 800; font-size: 18px; color: #fff; background: var(--wpl-accent); box-shadow: 0 4px 0 var(--wpl-accent-dark), 0 8px 14px rgba(15, 118, 110, 0.22); cursor: pointer; font-variant-numeric: tabular-nums; transition: transform 0.15s ease; }
.pp-node.keyn { font-family: 'Fraunces', Georgia, serif; font-size: 19px; font-weight: 600; }
.pp-node:active { transform: translateY(2px); }
.pp-node.locked { background: var(--pp-locked); color: var(--pp-locked-ink); box-shadow: 0 4px 0 #C4BBAC; }
.pp-node.open { background: var(--wpl-card); color: var(--wpl-accent); box-shadow: 0 0 0 3px var(--wpl-accent) inset, 0 4px 0 var(--wpl-prog-border); }
/* Chris: the next level glows softly and slowly instead of flashing. */
.pp-node.current { background: var(--pp-gold); color: var(--wpl-ink); box-shadow: 0 4px 0 var(--pp-gold-dark); animation: pp-breathe 2.8s ease-in-out infinite; }
@keyframes pp-breathe { 50% { box-shadow: 0 4px 0 var(--pp-gold-dark), 0 0 0 7px rgba(242, 181, 24, 0.22); } }
.pp-node.boss { width: 64px; height: 64px; margin: -32px 0 0 -32px; }
.pp-node .pp-ico { width: 22px; height: 22px; }
.pp-nstars { position: absolute; bottom: -14px; left: 50%; transform: translateX(-50%); display: flex; gap: 1px; }
.pp-star { width: 13px; height: 13px; fill: var(--pp-gold); stroke: var(--pp-gold-dark); stroke-width: 1.5; }
.pp-star.off { fill: var(--pp-locked); stroke: #C4BBAC; }
.pp-nlabel { position: absolute; font-size: 12px; font-weight: 700; color: var(--wpl-tan-text); line-height: 1.25; width: 118px; transform: translateY(-50%); pointer-events: none; }
.pp-nlabel em { display: table; font-style: normal; margin-top: 3px; background: var(--pp-gold-bg); border: 1px solid var(--pp-gold-border); color: var(--pp-gold-ink); border-radius: 99px; padding: 0 7px; font-size: 10.5px; }
.pp-nlabel[style*="right"] em { margin-left: auto; }
.pp-bump { animation: pp-bump 0.5s ease; }
@keyframes pp-bump { 40% { transform: scale(1.08); } }
.pp-testing { text-align: center; margin-top: 18px; }
.pp-link { border: 0; background: none; color: var(--wpl-tan-text); font-weight: 600; font-size: 12.5px; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; padding: 6px; }

/* All tools (the old home tiles): its own back button, in the same spot
   as the demo's, and the same spacing the demo home already uses. */
#wpl-content-area #pp-tools-back { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; width: auto; margin: 0; text-align: center; line-height: normal; }
#wpl-home-hub:not(.active) ~ #pp-tools-back { display: none; }
#wpl-wrapper #wpl-home-hub { padding-bottom: 64px; }
@media (min-width: 521px) { #wpl-wrapper #wpl-home-hub { padding-bottom: 120px; } }
@media (max-width: 520px) {
  #wpl-wrapper { --demo-gap: 28px; --demo-exit-h: 36px; }
  #wpl-wrapper #wpl-home-hub { justify-content: flex-end; padding-bottom: calc(var(--demo-exit-h) + var(--demo-gap)); }
  #wpl-wrapper #wpl-home-hub .wpl-home-heading { margin-bottom: var(--demo-gap); }
}
@media (max-width: 340px) { #wpl-wrapper { --demo-gap: 24px; --demo-exit-h: 29px; } }
/* Demo: "Back to Worship Piano Academy" sits under the map now. */
#wpl-wrapper:not(.wpl-on-path) #wpl-demo-exit { display: none !important; }
#wpl-wrapper.wpl-on-path #wpl-demo-exit:not([hidden]) { display: flex !important; }

/* Popups */
.pp-overlay { position: absolute; inset: 0; z-index: 20; border-radius: 20px; background: rgba(42, 47, 52, 0.32); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); display: flex; align-items: center; justify-content: center; padding: 14px; }
.pp-sheet { background: var(--wpl-card); width: 100%; max-width: 480px; max-height: 100%; overflow-y: auto; border-radius: 22px; padding: 18px 16px; box-shadow: 0 14px 44px rgba(0, 0, 0, 0.22); animation: pp-up 0.26s cubic-bezier(0.2, 0.8, 0.2, 1); line-height: 1.35; }
@keyframes pp-up { from { transform: scale(0.96); opacity: 0; } }
.pp-sheetin { transition: opacity 0.2s ease; }
.pp-sheetin.out { opacity: 0; transition-duration: 0.14s; }
.pp-sheet .pp-lvl { font-size: 12.5px; font-weight: 700; color: var(--wpl-tan-text); }
.pp-sheet h3 { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 22px; line-height: 1.2; margin: 4px 0 8px; }
.pp-tags { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.pp-best { display: inline-flex; gap: 2px; }
.pp-best .pp-star { width: 16px; height: 16px; }
.pp-what { font-size: 14.5px; margin: 0 0 4px; color: var(--wpl-ink); }
.pp-soon { font-size: 13.5px; color: var(--wpl-tan-text); margin: 10px 0 0; }
.pp-primary { display: block; width: 100%; margin-top: 14px; border: 0; background: var(--wpl-accent); color: #fff; font-weight: 800; font-size: 15.5px; border-radius: 12px; padding: 13px; cursor: pointer; text-align: center; text-decoration: none; font-family: inherit; }
.pp-close { display: block; width: 100%; margin-top: 10px; border: 1px solid var(--wpl-line); background: var(--wpl-card); color: var(--wpl-ink); border-radius: 12px; padding: 11px; font-weight: 700; font-size: 14.5px; cursor: pointer; }
.pp-testrow { margin-top: 12px; border: 1px dashed #c9b99f; border-radius: 12px; padding: 10px; }
.pp-testrow > span { display: block; font-size: 12px; font-weight: 700; color: var(--pp-peach-ink); margin-bottom: 8px; }
.pp-starbtns { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.pp-starbtns button { border: 1px solid var(--pp-gold-border); background: var(--pp-gold-bg); border-radius: 12px; padding: 10px 4px; cursor: pointer; display: flex; justify-content: center; gap: 2px; }
.pp-starbtns .pp-star { width: 18px; height: 18px; }
.pp-rate { display: grid; gap: 8px; margin-top: 6px; }
.pp-rate button { display: flex; align-items: center; gap: 12px; text-align: left; border: 2px solid var(--wpl-line); background: var(--wpl-card); border-radius: 12px; padding: 11px 12px; font-weight: 700; font-size: 15px; cursor: pointer; color: var(--wpl-ink); }
.pp-rstars { display: inline-flex; gap: 2px; width: 62px; flex: none; }
.pp-rstars .pp-star { width: 18px; height: 18px; }

/* Quiz */
.pp-sheet.quiz { min-height: min(460px, 100%); }
.pp-qprog { display: flex; justify-content: space-between; font-size: 12.5px; font-weight: 700; color: var(--wpl-tan-text); font-variant-numeric: tabular-nums; }
.pp-qdots { display: flex; gap: 4px; margin: 8px 0 12px; flex-wrap: wrap; }
.pp-qdots i { width: 10px; height: 10px; border-radius: 50%; background: var(--wpl-tan); }
.pp-qdots i.r { background: var(--wpl-accent); }
.pp-qdots i.h { background: var(--pp-gold); }
.pp-qdots i.w { background: var(--pp-red); }
.pp-qtags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.pp-qtopic { background: var(--wpl-tan); color: var(--wpl-ink); font-weight: 800; font-size: 13px; border-radius: 8px; padding: 3px 10px; }
.pp-qkey { background: var(--wpl-accent); color: #fff; font-weight: 800; font-size: 13px; border-radius: 8px; padding: 3px 10px; }
.pp-qtext { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 21px; line-height: 1.25; margin: 0 0 12px; white-space: nowrap; overflow: hidden; }
.pp-qopts { display: grid; gap: 8px; }
/* Chris: no A, B, C letters (they clash with note names), a plain circle
   instead, teal when right and red when wrong. */
.pp-qopt { text-align: left; border: 2px solid var(--wpl-line); background: var(--wpl-card); color: var(--wpl-ink); border-radius: 12px; padding: 12px 14px; font-weight: 700; font-size: 15px; cursor: pointer; display: flex; gap: 10px; align-items: center; }
.pp-dot { flex: none; width: 22px; height: 22px; border-radius: 50%; border: 2px solid #d6cdbd; }
.pp-qopt.right { border-color: var(--wpl-accent); background: var(--wpl-prog-bg); }
.pp-qopt.right .pp-dot { background: var(--wpl-accent); border-color: var(--wpl-accent); }
.pp-qopt.wrong { border-color: var(--pp-red); background: #fbe9e5; }
.pp-qopt.wrong .pp-dot { background: var(--pp-red); border-color: var(--pp-red); }
.pp-qopt:disabled { cursor: default; }
/* Chris picked A: a tried wrong answer keeps a soft red outline (not grey). */
.pp-qopt.out { border-color: #e7b3a8; background: #fdf3f0; color: #8a3a2a; }
.pp-qopt.out .pp-dot { border-color: #e7b3a8; background: #f3cfc6; }
.pp-listenrow { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; font-size: 12.5px; color: var(--wpl-tan-text); }
.pp-listen { flex: none; border: 0; background: var(--wpl-accent); color: #fff; font-weight: 800; border-radius: 12px; padding: 11px 16px; cursor: pointer; font-size: 15px; }
/* Chris (phone test, Sept 14): the piano looked stretched tall on phones.
   It keeps the desktop shape (about 3.4 times wider than tall) at every width. */
.pp-kb { position: relative; aspect-ratio: 3.4 / 1; display: flex; margin: 4px 0 12px; user-select: none; -webkit-user-select: none; touch-action: manipulation; }
.pp-kb .w { flex: 1; border: 1px solid #cfc6b6; border-radius: 0 0 7px 7px; background: #fff; margin-right: 1px; cursor: pointer; position: relative; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 6px; font-size: 10.5px; font-weight: 700; color: var(--wpl-muted); }
.pp-kb .b { position: absolute; top: 0; height: 60%; background: #2a2f34; border-radius: 0 0 5px 5px; cursor: pointer; z-index: 2; }
.pp-kb .w.tonic { color: var(--wpl-accent); font-size: 13px; font-weight: 800; }
.pp-kb .b i { position: absolute; bottom: 5px; left: 0; right: 0; text-align: center; font-style: normal; font-size: 11px; font-weight: 800; color: #9ee0d8; }
.pp-kb .ok { background: var(--wpl-accent) !important; color: #fff !important; }
.pp-kb .ok i { color: #fff; }
.pp-kb .bad { background: var(--pp-red) !important; color: #fff; }
.pp-kb .shake { animation: pp-shake 0.4s ease; }
@keyframes pp-shake { 20%, 60% { transform: translateX(-4px); } 40%, 80% { transform: translateX(4px); } }
/* Chris (Sept 14): one gold glow, then a slow fade (it used to pulse twice). */
.pp-kb.win .ok { animation: pp-glow 1.8s ease-out 1; }
@keyframes pp-glow { 12% { box-shadow: 0 0 16px 5px rgba(242, 181, 24, 0.9); } }
.pp-qfeed { min-height: 22px; font-weight: 700; font-size: 14.5px; margin-top: 10px; }
.pp-qfeed.r { color: var(--wpl-accent); }
.pp-qfeed.w { color: var(--pp-red); }
.pp-qact { margin-top: 12px; display: flex; gap: 8px; }
.pp-qact button { flex: 1; border: 0; background: var(--wpl-ink); color: #fff; font-weight: 800; border-radius: 12px; padding: 12px; cursor: pointer; font-size: 15px; }
.pp-qact button.sec { background: var(--wpl-tan); color: var(--wpl-ink); }
.pp-result { text-align: center; padding: 6px 0; }
.pp-big { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 28px; line-height: 1.1; margin: 6px 0 8px; }
.pp-topics { text-align: left; margin: 12px 0 4px; display: grid; gap: 6px; }
.pp-topics div { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 14px; font-weight: 600; padding: 6px 10px; background: #fbf8f2; border-radius: 10px; }

.pp-banner { position: absolute; left: 50%; top: 22%; transform: translate(-50%, 0); z-index: 30; background: var(--wpl-ink); color: #fff; border-radius: 18px; padding: 14px 20px; text-align: center; font-family: 'Fraunces', Georgia, serif; font-size: 21px; line-height: 1.2; box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); animation: pp-pop 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.3); width: max-content; max-width: 86%; }
.pp-banner small { display: block; font-family: 'DM Sans', sans-serif; font-size: 13.5px; font-weight: 500; opacity: 0.85; margin-top: 4px; }
@keyframes pp-pop { from { transform: translate(-50%, 10px) scale(0.9); opacity: 0; } }
.pp-confetti-box { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 25; border-radius: 20px; }
.pp-confetti { position: absolute; top: -14px; width: 9px; height: 14px; border-radius: 2px; animation: pp-fall linear forwards; }
@keyframes pp-fall { to { transform: translateY(900px) rotate(720deg); } }
@media (prefers-reduced-motion: reduce) {
  .pp-node.current, .pp-bump, .pp-banner, .pp-sheet { animation: none !important; }
}

/* Hint (Chris, 2026-09-13): one octave of the scale, the 1 at both ends. */
.pp-qtags { align-items: center; }
.pp-qact button.hint { display: flex; align-items: center; justify-content: center; gap: 6px; background: var(--wpl-prog-bg); color: var(--wpl-accent-dark); box-shadow: inset 0 0 0 1.5px var(--wpl-prog-border); }
.pp-qact button.hint svg { width: 17px; height: 17px; flex: none; }
.pp-qact button.hint:disabled { cursor: default; opacity: 1; box-shadow: inset 0 0 0 1.5px var(--wpl-accent); }
.pp-hintbox .pp-kb { pointer-events: none; margin: 0 0 12px; }
.pp-kb .w.hint-on { background: #cdeaf2; color: var(--wpl-accent-dark); font-size: 12px; }
/* Medium green (Chris's A/B, pick B): the dark green is saved for the 1s,
   so a black scale key never looks like a 1. */
.pp-kb .b.hint-on { background: #5FB3A8; }
.pp-kb .w.hint-one { background: var(--wpl-accent); color: #fff; font-size: 13px; font-weight: 800; }
.pp-kb .b.hint-one { background: var(--wpl-accent-dark); }
.pp-kb .b.hint-on i, .pp-kb .b.hint-one i { color: #fff; }
.pp-kb .w.hint-ring { box-shadow: inset 0 0 0 3px var(--wpl-accent); color: var(--wpl-accent); font-size: 13px; font-weight: 800; }
.pp-kb .b.hint-ring { box-shadow: 0 0 0 2px var(--wpl-accent); }

/* Chris (2026-09-13): titles and the lines under them wrapped at the wrong
   points ("needs to be a smoother wrap"). Titles and centered lines split
   into even lines; left aligned text avoids a lonely last word. */
.pp-big, .pp-sheet h3, .pp-wname, .pp-banner, .pp-result .pp-what, .pp-qtext, .pp-nlabel { text-wrap: balance; }
.pp-qopt { text-wrap: pretty; }
.pp-what, .pp-wskill, .pp-msg, .pp-soon, .pp-placetxt, .pp-testout span { text-wrap: pretty; }

/* Games (2026-09-13 night): same popup as the placement test. */
.pp-qprog { gap: 10px; }
.pp-qhead { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-qright { flex: none; }
.pp-timer { color: var(--wpl-accent); font-variant-numeric: tabular-nums; }
/* Step Jump: the note you start from. */
.pp-kb .w.from { background: var(--pp-gold-bg); box-shadow: inset 0 0 0 2px var(--pp-gold); }
.pp-kb .b.from { background: #8a6d1f; }
/* Shape Spotter: the chord being shown (the app's chord tone red). */
.pp-kb .w.lit { background: #f87171; }
.pp-kb .b.lit { background: #dc4c4c; }
.pp-showbox .pp-kb { pointer-events: none; }
.pp-bigstars { display: flex; justify-content: center; gap: 6px; margin: 8px 0 2px; }
.pp-bigstars .pp-star { width: 42px; height: 42px; }

/* Rhythm Tap (2026-09-13 night): 8 beat lights and a big pad. */
.pp-rbeats { display: flex; gap: 8px; justify-content: center; margin: 6px 0 14px; }
.pp-rbeats i { width: 16px; height: 16px; border-radius: 50%; background: var(--wpl-tan); transition: background 0.1s ease, transform 0.1s ease; }
.pp-rbeats i.now { background: var(--wpl-prog-border); transform: scale(1.2); }
.pp-rbeats i.r { background: var(--wpl-accent); }
.pp-rbeats i.h { background: var(--pp-gold); }
.pp-rbeats i.w { background: var(--pp-red); }
.pp-rpad { display: block; width: 100%; height: 150px; border: 0; border-radius: 22px; background: var(--wpl-prog-bg); color: var(--wpl-accent-dark); font-family: inherit; font-size: 22px; font-weight: 800; cursor: pointer; touch-action: manipulation; -webkit-user-select: none; user-select: none; box-shadow: inset 0 0 0 2px var(--wpl-prog-border); }
.pp-rpad.live { background: var(--wpl-accent); color: #fff; box-shadow: 0 5px 0 var(--wpl-accent-dark); }
.pp-rpad.hit { animation: pp-hit 0.15s ease; }
@keyframes pp-hit { 50% { transform: translateY(3px) scale(0.98); } }

/* Hint centered under Next and Stop (Chris, Sept 14). Hidden once the
   question is answered. */
.pp-hintrow { display: flex; justify-content: center; margin-top: 12px; }
.pp-hint { display: inline-flex; align-items: center; gap: 6px; border: 0; border-radius: 999px; padding: 9px 22px; font-family: inherit; font-weight: 800; font-size: 14.5px; cursor: pointer; background: var(--wpl-prog-bg); color: var(--wpl-accent-dark); box-shadow: inset 0 0 0 1.5px var(--wpl-prog-border); }
.pp-hint svg { width: 16px; height: 16px; flex: none; }
.pp-hint:disabled { cursor: default; box-shadow: inset 0 0 0 1.5px var(--wpl-accent); }
.pp-hint[hidden] { display: none; }

/* Hint on an answer button question: one wrong answer crossed out. */
.pp-qopt.struck { border-color: var(--wpl-line); background: var(--wpl-tan); color: var(--wpl-muted); text-decoration: line-through; }
.pp-qopt.struck .pp-dot { background: transparent; border-color: #d6cdbd; }

/* Key words in questions: bold teal (Chris's pick B, Sept 14). */
.pp-kw { color: var(--wpl-accent); font-weight: 800; }

/* Chris (Sept 14): the question popup never changes size. Every question
   is one fixed height (tall enough for the hint piano), and the hint piano
   has a saved spot just above the Hint button, so nothing moves when it
   appears. */
.pp-sheet.quiz { height: min(590px, 100%); min-height: 0; display: flex; flex-direction: column; }
.pp-sheet.quiz > .pp-sheetin { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.pp-qfoot { margin-top: auto; }
.pp-hintbox { visibility: hidden; margin-top: 12px; }
.pp-hintbox.on { visibility: visible; }
.pp-hintbox .pp-kb { margin: 0; }
/* Nothing inside the fixed height popup gets squeezed. */
.pp-sheet.quiz > .pp-sheetin > * { flex-shrink: 0; }

/* ---------- Practice Path layout A + list and colorful map (Chris, Sept 14) ----------
   One slim top row, one chapter title with arrows, then the levels as a
   lesson list (grown ups) or a colorful map (Kids mode later). Level
   colors by type: lesson gold, play teal, game peach, song sage, badge ink. */
.pp-top { gap: 6px; }
.pp-seg { display: inline-flex; background: var(--wpl-tan); border-radius: 999px; padding: 3px; gap: 2px; }
.pp-seg button { border: 0; background: none; width: 34px; height: 28px; border-radius: 999px; display: grid; place-items: center; color: var(--wpl-tan-text); cursor: pointer; }
.pp-seg button[aria-pressed="true"] { background: var(--wpl-card); color: var(--wpl-accent); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
.pp-seg .pp-ico, .pp-toolsbtn .pp-ico { width: 16px; height: 16px; }
.pp-toolsbtn { flex: none; width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--wpl-line); background: var(--wpl-tan); display: grid; place-items: center; color: var(--wpl-ink); cursor: pointer; padding: 0; }
.pp-chapbar { flex: none; display: flex; align-items: center; gap: 8px; background: var(--wpl-tan); border-radius: 14px; padding: 6px; }
.pp-arr { flex: none; width: 34px; height: 34px; border-radius: 50%; border: 0; background: var(--wpl-card); color: var(--wpl-accent); font-size: 22px; line-height: 1; font-weight: 800; cursor: pointer; display: grid; place-items: center; padding: 0 0 3px; font-family: inherit; }
.pp-arr:disabled { opacity: 0.35; cursor: default; }
.pp-chaptitle { flex: 1; min-width: 0; border: 0; background: none; cursor: pointer; text-align: center; font-family: inherit; color: var(--wpl-ink); padding: 0; transition: opacity 0.2s ease; }
.pp-chaptitle small { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: 0.05em; color: var(--wpl-tan-text); }
.pp-chaptitle b { display: block; font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 18px; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pp-chbar { flex: none; margin: 8px 4px 10px; height: 6px; }
#pp-note:not(:empty) { margin-bottom: 10px; }
.pp-chstars { font-size: 13.5px; }
.pp-chlist { margin-top: 14px; display: grid; gap: 4px; }
.pp-chrow { display: flex; align-items: center; gap: 10px; width: 100%; border: 0; background: none; border-radius: 10px; padding: 7px 8px; cursor: pointer; text-align: left; font-family: inherit; color: var(--wpl-ink); font-size: 14px; font-weight: 600; }
.pp-chrow .n { width: 22px; font-weight: 800; color: var(--wpl-muted); font-variant-numeric: tabular-nums; }
.pp-chrow .t { flex: 1; }
.pp-chrow .s { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; font-weight: 700; color: var(--wpl-tan-text); font-variant-numeric: tabular-nums; }
.pp-chrow .s svg { width: 13px; height: 13px; }
.pp-chrow.sel { background: var(--wpl-prog-bg); }
.pp-chrow.locked { color: var(--pp-locked-ink); }

/* Lesson list */
.pp-list { display: grid; gap: 8px; transition: opacity 0.2s ease; }
.pp-row { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; border: 1px solid var(--wpl-line); background: var(--wpl-card); border-radius: 14px; padding: 10px 12px; cursor: pointer; font-family: inherit; color: var(--wpl-ink); }
.pp-ricon { flex: none; width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center; }
.pp-ricon .pp-ico { width: 20px; height: 20px; }
.pp-keyface { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 18px; line-height: 1; }
.pp-rtxt { flex: 1; min-width: 0; }
.pp-rtxt b { display: block; font-size: 14.5px; font-weight: 700; line-height: 1.25; text-wrap: pretty; }
.pp-rtxt small { display: block; font-size: 12px; color: var(--wpl-tan-text); margin-top: 2px; }
.pp-rend { flex: none; display: inline-flex; gap: 1px; color: var(--pp-locked-ink); }
.pp-rend .pp-ico { width: 18px; height: 18px; }
.pp-rend .pp-star { width: 15px; height: 15px; }
.pp-row.current { border-color: var(--pp-gold); box-shadow: 0 0 0 3px var(--pp-gold-bg); }
.pp-row.current small { color: var(--pp-gold-ink); font-weight: 700; }
.pp-row.locked { background: transparent; }
.pp-row.locked .pp-ricon, .pp-row.locked .pp-rtxt { opacity: 0.5; }
.t-lesson .pp-ricon { background: var(--pp-gold-bg); color: var(--pp-gold-ink); box-shadow: inset 0 0 0 1px var(--pp-gold-border); }
.t-play .pp-ricon { background: var(--wpl-prog-bg); color: var(--wpl-accent-dark); box-shadow: inset 0 0 0 1px var(--wpl-prog-border); }
.t-game .pp-ricon { background: var(--pp-peach-bg); color: var(--pp-peach-ink); box-shadow: inset 0 0 0 1px var(--pp-peach-border); }
.t-song .pp-ricon { background: var(--pp-sage-bg); color: var(--pp-sage-ink); box-shadow: inset 0 0 0 1px var(--pp-sage-border); }
.t-boss .pp-ricon { background: var(--wpl-ink); color: #fff; }

/* Colorful map */
.pp-map .pp-path path { stroke: var(--wpl-accent); stroke-opacity: 0.35; stroke-width: 6; stroke-dasharray: 1 14; stroke-linecap: round; }
.pp-map .pp-node { border: 3px solid transparent; font-family: inherit; }
.pp-map .pp-node .pp-ico { width: 22px; height: 22px; }
.pp-map .pp-node .pp-keyface { font-size: 19px; }
.pp-map .pp-node.t-lesson { background: var(--pp-gold-bg); border-color: var(--pp-gold-border); color: var(--pp-gold-ink); box-shadow: 0 4px 0 var(--pp-gold-border); }
.pp-map .pp-node.t-play { background: var(--wpl-prog-bg); border-color: var(--wpl-prog-border); color: var(--wpl-accent-dark); box-shadow: 0 4px 0 var(--wpl-prog-border); }
.pp-map .pp-node.t-game { background: var(--pp-peach-bg); border-color: var(--pp-peach-border); color: var(--pp-peach-ink); box-shadow: 0 4px 0 var(--pp-peach-border); }
.pp-map .pp-node.t-song { background: var(--pp-sage-bg); border-color: var(--pp-sage-border); color: var(--pp-sage-ink); box-shadow: 0 4px 0 var(--pp-sage-border); }
.pp-map .pp-node.t-boss { background: var(--wpl-ink); border-color: var(--wpl-ink); color: #fff; box-shadow: 0 4px 0 #14171a; }
/* Chris (Sept 14): finished = green (done), gold = pending. 3 stars is a
   solid green stop; 1 or 2 stars a light green one (can replay for 3). */
.pp-map .pp-node.done:not(.t-boss) { background: #7fb069; border-color: #5a8a45; color: #fff; box-shadow: 0 4px 0 #5a8a45; }
.pp-map .pp-node.done.part:not(.t-boss) { background: var(--pp-sage-bg); border-color: #9cc485; color: var(--pp-sage-ink); box-shadow: 0 4px 0 #9cc485; }
.pp-map .pp-node.current { border-color: var(--pp-gold); box-shadow: 0 4px 0 var(--pp-gold-dark); animation: pp-glow2 2.8s ease-in-out infinite; }
@keyframes pp-glow2 { 50% { box-shadow: 0 4px 0 var(--pp-gold-dark), 0 0 0 8px rgba(242, 181, 24, 0.25); } }
.pp-map .pp-node.locked { opacity: 0.55; }
.pp-nlock { position: absolute; right: -4px; bottom: -4px; width: 20px; height: 20px; border-radius: 50%; background: var(--wpl-card); color: var(--pp-locked-ink); display: grid; place-items: center; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); }
.pp-nlock .pp-ico { width: 12px !important; height: 12px !important; }
.pp-nlabel small { display: block; font-size: 10.5px; font-weight: 700; color: var(--wpl-muted); }
@media (prefers-reduced-motion: reduce) { .pp-map .pp-node.current { animation: none; } }

/* Desktop: the chapter bar and the list sit in a comfortable middle column. */
@media (min-width: 521px) {
  #wpl-panel-path .pp-chapbar, #pp-note, #pp-levels.pp-list { width: 100%; max-width: 620px; margin-left: auto; margin-right: auto; }
  #wpl-panel-path .pp-chbar { width: calc(100% - 8px); max-width: 612px; margin: 8px auto 10px; }
}

/* Tiny lessons (Chris, Sept 14): big one octave pianos, wrapped headings. */
.pp-tinyh { font-family: 'Fraunces', Georgia, serif; font-weight: 600; font-size: 21px; line-height: 1.25; margin: 0 0 8px; text-wrap: balance; }
.pp-tinyp { margin-bottom: 10px; }
.pp-tinyarrow { text-align: center; font-weight: 800; color: var(--wpl-accent); font-size: 14px; margin-top: 8px; white-space: pre; }
.pp-kb.range { width: 100%; }
.pp-kb.range .w { font-size: 12px; }
.pp-kb .b i { line-height: 1.1; }
.pp-kb .b.hint-on i, .pp-kb .b.from i { color: #fff; }

/* Chris (Sept 14): the serif question text was hard to read (the "4").
   Questions and lesson headings use the app's clean DM Sans instead. */
.pp-qtext, .pp-tinyh { font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-weight: 800; letter-spacing: -0.01em; }

/* Placement result: where you are on the whole path (Chris, Sept 14). */
.pp-journey { margin: 10px 0 6px; text-align: left; }
.pp-jbar { position: relative; height: 10px; background: var(--wpl-tan); border-radius: 99px; margin: 14px 8px 0; }
.pp-jbar i { position: absolute; left: 0; top: 0; bottom: 0; background: var(--pp-gold); border-radius: 99px; }
.pp-jbar b { position: absolute; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; background: var(--wpl-accent); box-shadow: 0 0 0 4px var(--wpl-card), 0 2px 6px rgba(0, 0, 0, 0.2); }
.pp-jbar b::after { content: "You're here"; position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%); white-space: nowrap; font-size: 11px; font-weight: 800; color: var(--wpl-accent); }
.pp-jlabels { display: flex; justify-content: space-between; font-size: 11px; font-weight: 700; color: var(--wpl-muted); margin: 6px 2px 0; }
.pp-jtext { font-size: 14px; font-weight: 600; margin: 8px 0 0; text-align: center; text-wrap: balance; }
.pp-journey { padding-top: 14px; }
/* Keep "You're here" inside the card at either end of the bar. */
.pp-jbar b.s::after { left: -4px; transform: none; }
.pp-jbar b.e::after { left: auto; right: -4px; transform: none; }

/* ----- Placement result topics: compact chips so the card never scrolls (Chris, Sept 14) ----- */
.pp-topics { grid-template-columns: 1fr 1fr; gap: 5px; margin: 8px 0 0; }
.pp-topics div { justify-content: flex-start; gap: 6px; font-size: 13px; padding: 4px 7px; border-radius: 9px; min-width: 0; }
.pp-topics div span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-topics i, .pp-tlegend i { flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; border: 1px solid transparent; }
.pp-topics i svg, .pp-tlegend i svg { width: 12px; height: 12px; }
.pp-topics .good i, .pp-tlegend .good i { background: var(--wpl-prog-bg); border-color: var(--wpl-prog-border); color: var(--wpl-accent-dark); }
.pp-topics .almost i, .pp-tlegend .almost i { background: var(--pp-gold-bg); border-color: var(--pp-gold-border); color: var(--pp-gold-ink); }
.pp-topics .practice i, .pp-tlegend .practice i { background: var(--pp-peach-bg); border-color: var(--pp-peach-border); color: var(--pp-peach-ink); }
.pp-tlegend { display: flex; justify-content: center; flex-wrap: wrap; gap: 4px 14px; margin: 6px 0 0; font-size: 12px; font-weight: 600; color: var(--wpl-muted, #6b6b6b); }
.pp-tlegend span { display: inline-flex; align-items: center; gap: 5px; }
.pp-tlegend i { width: 15px; height: 15px; }
.pp-tlegend i svg { width: 10px; height: 10px; }
@media (max-width: 340px) {
  .pp-topics { gap: 4px; margin-top: 6px; grid-template-columns: minmax(max-content, 1fr) minmax(max-content, 1fr); }
  .pp-topics div { font-size: 12px; gap: 5px; padding: 3px 6px; }
  .pp-topics i { width: 16px; height: 16px; }
  .pp-topics i svg { width: 10px; height: 10px; }
  .pp-tlegend { gap: 4px 10px; font-size: 11.5px; margin-top: 5px; }
}

/* Tiny lesson "The notes repeat": each C to B group in its own color (Chris, Sept 14):
   gold, blue, peach, so gold and peach are never side by side */
.pp-kb .w.band1 { background: var(--wpl-tile-teal-bg); border-color: #9fd3e0; color: var(--wpl-accent-dark); }
.pp-kb .w.band0 { background: var(--wpl-tile-gold-bg); border-color: #e3c98a; color: var(--pp-gold-ink); }
.pp-kb .w.band2 { background: var(--wpl-tile-peach-bg); border-color: #e6bf9f; color: var(--pp-peach-ink); }
.pp-kb .w.band3 { background: var(--wpl-tile-sage-bg); border-color: #bcd6a4; color: #3f6b2a; }
.pp-kb .pp-kbp { position: absolute; top: 34%; transform: translate(-50%, -50%); color: var(--wpl-accent); font-size: 19px; line-height: 1; z-index: 3; pointer-events: none; }
/* Chris (Sept 14): a teal key phrase ("groups of 2") never breaks across two lines. */
.pp-kw { white-space: nowrap; }
.pp-kw.gold { color: #8a6d1f; }
/* Sharp and flat symbols: a font that has them, a touch bigger, not bold (Chris, Sept 14). */
svg.pp-acc { width: 0.6em; height: 0.95em; vertical-align: -0.1em; margin: 0 0.02em; fill: none; stroke: currentColor; stroke-linecap: round; overflow: visible; }
.pp-bigsym { font-family: "Apple Symbols", "Segoe UI Symbol", "Noto Sans Symbols 2", "DejaVu Sans", serif; font-size: 84px; line-height: 1; text-align: center; color: var(--wpl-accent); margin: 2px 0 12px; }
/* Chris (Sept 14): the heading font joined "fl" into one odd letter ("flats"). No joined letters. */
#wpl-wrapper { font-variant-ligatures: no-common-ligatures; }

/* ----- Practice Path: Daily practice button + summary (Chris's pick A, Sept 14) ----- */
.pp-daily { flex: none; display: flex; align-items: center; gap: 10px; width: 100%; border: 0; border-radius: 14px; background: var(--wpl-accent); color: #fff; padding: 8px 12px; margin: 0 0 10px; cursor: pointer; text-align: left; font-family: inherit; }
.pp-daily .pp-dring { flex: none; display: grid; }
.pp-daily .pp-ring-bg { stroke: rgba(255, 255, 255, 0.3); }
.pp-daily .pp-ring-fg { stroke: var(--pp-gold); }
.pp-daily .pp-dtxt { min-width: 0; }
.pp-daily b { display: block; font-size: 15.5px; font-weight: 800; }
.pp-daily small { display: block; font-size: 12.5px; font-weight: 600; opacity: 0.9; }
.pp-daily .pp-dgo { margin-left: auto; font-size: 24px; line-height: 1; font-weight: 700; }
.pp-daily.empty { background: var(--wpl-tan); color: var(--wpl-ink); cursor: default; }
.pp-daily.empty .pp-ring-bg { stroke: var(--wpl-line); }
.pp-daily:focus-visible { outline: 3px solid var(--pp-gold); outline-offset: 2px; }
.pp-dspeed { text-align: left; margin-top: 12px; display: grid; gap: 5px; }
.pp-dspeed div { display: flex; justify-content: space-between; gap: 10px; font-size: 14px; font-weight: 600; padding: 6px 10px; background: #fbf8f2; border-radius: 10px; font-variant-numeric: tabular-nums; }
.pp-dspeed .pp-dsh { background: none; padding: 0 10px; font-size: 12px; font-weight: 700; color: var(--wpl-muted); }
/* Chris (Sept 14): the main button in a question or lesson is teal, and it
   looks faded until it can be tapped. */
.pp-qact button:not(.sec):not(.hint) { background: var(--wpl-accent); }
.pp-qact button:disabled:not(.hint) { opacity: 0.4; cursor: default; }

/* Chris (Sept 14): Stop was right next to Next. It's now a small X in the
   top right corner of every lesson and question popup, away from Next and
   Hint. (The button still says "Stop" for screen readers.) */
.pp-sheet.quiz { position: relative; }
.pp-sheet.quiz .pp-qprog { padding-right: 40px; }
.pp-qact [data-quit], .pp-qact [data-tquit], .pp-qact [data-rquit] { position: absolute; top: 14px; right: 14px; flex: none !important; width: 32px; height: 32px; padding: 0 !important; border-radius: 50%; font-size: 0 !important; display: grid; place-items: center; background: var(--wpl-tan) !important; color: var(--wpl-ink) !important; z-index: 2; }
.pp-qact [data-quit]::before, .pp-qact [data-tquit]::before, .pp-qact [data-rquit]::before { content: ""; position: absolute; inset: 0; margin: auto; width: 12px; height: 12px; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1.5 1.5l9 9M10.5 1.5l-9 9' stroke='black' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1.5 1.5l9 9M10.5 1.5l-9 9' stroke='black' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; }
.pp-qact [data-quit]:focus-visible, .pp-qact [data-tquit]:focus-visible, .pp-qact [data-rquit]:focus-visible { outline: 3px solid var(--pp-gold); outline-offset: 2px; }

/* Lesson list (Chris, Sept 14): finished lessons are green. 3 stars = green
   card; 1 or 2 stars = white card with a gold "Replay for 3 stars". */
.pp-row.done .pp-ricon { background: #7fb069; color: #fff; box-shadow: none; }
.pp-row.done.full { background: var(--pp-sage-bg); border-color: var(--pp-sage-border); }
.pp-row .pp-more { color: var(--pp-gold-ink); font-weight: 700; }
/* Chris (Sept 14): hover effects, and a clear Start tag on the next lesson. */
.pp-row { transition: border-color 0.15s ease, box-shadow 0.15s ease; }
.pp-map .pp-node { transition: transform 0.15s ease; }
@media (hover: hover) {
  .pp-row:not(.locked):hover { border-color: var(--wpl-accent); box-shadow: 0 2px 10px rgba(15, 118, 110, 0.16); }
  .pp-map .pp-node:not(.locked):hover { transform: translateY(-2px) scale(1.08); }
}
.pp-row.current { background: #fff8e6; }
.pp-rgo { display: inline-block; background: var(--wpl-accent); color: #fff; font-weight: 800; font-size: 13px; border-radius: 999px; padding: 5px 13px; }
.pp-nnext { position: absolute; bottom: calc(100% + 9px); left: 50%; transform: translateX(-50%); background: var(--wpl-accent); color: #fff; font-family: 'DM Sans', sans-serif; font-size: 12px; font-weight: 800; line-height: 1; padding: 5px 11px; border-radius: 999px; white-space: nowrap; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18); pointer-events: none; }
/* Written hint for answer button questions (Chris, Sept 14). */
.pp-hinttext { margin: 0; padding: 10px 12px; border-radius: 12px; background: var(--wpl-prog-bg); box-shadow: inset 0 0 0 1.5px var(--wpl-prog-border); color: var(--wpl-accent-dark); font-weight: 700; font-size: 14.5px; text-align: center; text-wrap: balance; }
.pp-hintbox:not(.on) .pp-hinttext { visibility: hidden; }
/* Timed games: star goals on the how to play screen (Chris, Sept 14). */
.pp-bgoals { display: grid; gap: 6px; margin: 14px auto 0; max-width: 260px; text-align: left; }
.pp-bgoals div { display: flex; align-items: center; gap: 12px; background: #fbf8f2; border-radius: 10px; padding: 7px 12px; font-weight: 700; font-size: 14.5px; }
.pp-bgoals .pp-star { width: 18px; height: 18px; }

/* ----- Lesson list, Chris's pick (Sept 14): D3 with D2's boxes ----- */
.pp-list .pp-row.pp-c0 { background: var(--wpl-tile-teal-bg); border-color: var(--wpl-tile-teal-border); }
.pp-list .pp-row.pp-c1 { background: var(--wpl-tile-peach-bg); border-color: var(--wpl-tile-peach-border); }
.pp-list .pp-row.pp-c2 { background: var(--wpl-tile-sage-bg); border-color: var(--wpl-tile-sage-border); }
.pp-list .pp-row.pp-c3 { background: var(--wpl-tile-gold-bg); border-color: var(--wpl-tile-gold-border); }
.pp-list .pp-ricon { position: relative; color: #fff; box-shadow: none; border-radius: 12px; }
.pp-list .pp-c0 .pp-ricon { background: #0F766E; }
.pp-list .pp-c1 .pp-ricon { background: #d9824a; }
.pp-list .pp-c2 .pp-ricon { background: #6fa05a; }
.pp-list .pp-c3 .pp-ricon { background: #e0a810; }
.pp-list .pp-ricon svg, .pp-map .pp-node > svg { width: 21px; height: 21px; }
.pp-gl { font-weight: 800; font-size: 14px; letter-spacing: -0.02em; line-height: 1; display: inline-flex; }
.pp-gl svg.pp-acc { width: 13px !important; height: 20px !important; vertical-align: 0; margin: 0; }
.pp-rcheck { position: absolute; right: -5px; bottom: -5px; width: 18px; height: 18px; border-radius: 50%; background: #7fb069; color: #fff; display: grid; place-items: center; font-size: 10px; font-weight: 900; border: 2px solid var(--wpl-card); }
.pp-list .pp-rtxt { flex: 1 1 auto; min-width: 0; overflow: hidden; }
.pp-list .pp-row { min-width: 0; max-width: 100%; }
.pp-list .pp-rend { flex: none; }
.pp-list .pp-rtxt small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pp-rchev { color: var(--wpl-muted); font-size: 20px; font-weight: 700; line-height: 1; }
.pp-list .pp-row.locked { filter: grayscale(1); opacity: 0.55; }
.pp-list .pp-row.locked .pp-ricon, .pp-list .pp-row.locked .pp-rtxt { opacity: 1; }
/* the next lesson: a big card with its own Start button */
.pp-hero { display: block; width: 100%; text-align: left; border: 2px solid var(--pp-gold); background: #fff5dc; box-shadow: 0 0 0 3px var(--pp-gold-bg); border-radius: 18px; padding: 14px; font-family: inherit; color: var(--wpl-ink); cursor: pointer; transition: box-shadow 0.15s ease; }
.pp-hero .pp-hrow { display: flex; align-items: center; gap: 12px; }
.pp-hero .pp-ricon { width: 54px; height: 54px; border-radius: 14px; flex: none; display: grid; place-items: center; }
.pp-hero .pp-ricon svg { width: 26px; height: 26px; }
.pp-hero .pp-gl { font-size: 17px; }
.pp-hero .pp-rtxt { min-width: 0; }
.pp-hero .pp-hup { display: block; font-style: normal; font-size: 11px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--pp-gold-ink); }
.pp-hero b { display: block; font: 800 19px 'Fraunces', Georgia, serif; line-height: 1.2; }
.pp-hero small { display: block; font-size: 13px; color: var(--wpl-tan-text); font-weight: 600; margin-top: 2px; }
.pp-hgo { display: block; margin-top: 12px; background: var(--wpl-accent); color: #fff; border-radius: 12px; padding: 11px; text-align: center; font-weight: 800; font-size: 15px; }
@media (hover: hover) { .pp-hero:hover { box-shadow: 0 0 0 3px var(--pp-gold-bg), 0 6px 16px rgba(42, 38, 32, 0.12); } }
.pp-hero:focus-visible { outline: 3px solid var(--wpl-accent); outline-offset: 2px; }
/* Chris (Sept 14): no X or Stop button; tap outside the card to close. */
.pp-qact [data-quit], .pp-qact [data-tquit], .pp-qact [data-rquit] { display: none !important; }
.pp-sheet.quiz .pp-qprog { padding-right: 0; }
/* Chris (Sept 14): keys were hard to hit on his phone. On phones the
   question card sits closer to the edges, the piano runs almost edge to
   edge, and the 2 octave piano is a little taller (bigger keys). */
@media (max-width: 520px) {
  .pp-overlay { padding: 6px; }
  .pp-sheet { padding: 16px 12px; }
  .pp-sheet .pp-kb { margin-left: -6px; margin-right: -6px; }
  .pp-sheet .pp-kb:not(.range) { aspect-ratio: 2.94 / 1; }
}
/* A right key with no name on it is the lighter green (Chris, Sept 14). */
.pp-kb .b.ok.plain { background: #5FB3A8 !important; }
.pp-kb .w.ok.plain { background: #bfe6df !important; }
/* Chris (Sept 14): a horizontal scroll bar on phones. Rows stretched to fit
   their full description; they now stay inside the list and trim with "...". */
.pp-list { grid-template-columns: minmax(0, 1fr); min-width: 0; }
.pp-list > * { min-width: 0; max-width: 100%; }
.pp-hero small { white-space: normal; }
#pp-scroll { overflow-x: hidden; }
/* Lesson list edges (Chris, Sept 14, picks A + C): a soft fade only once you
   scroll (top), and at the bottom while there's more below; finished lessons
   a little dimmer so Up next stands out. */
#pp-scroll.fade-top { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.45) 0, #000 20px); mask-image: linear-gradient(to bottom, rgba(0,0,0,0.45) 0, #000 20px); }
#pp-scroll.fade-bottom { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 20px), rgba(0,0,0,0.45) 100%); mask-image: linear-gradient(to bottom, #000 calc(100% - 20px), rgba(0,0,0,0.45) 100%); }
#pp-scroll.fade-top.fade-bottom { -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.45) 0, #000 20px, #000 calc(100% - 20px), rgba(0,0,0,0.45) 100%); mask-image: linear-gradient(to bottom, rgba(0,0,0,0.45) 0, #000 20px, #000 calc(100% - 20px), rgba(0,0,0,0.45) 100%); }
/* Chris (Sept 14): done lessons keep full color; locked ones go grey so the two never look alike. */

/* ---------- Practice Path: lesson pieces bar (Chris's pick P2, Sept 14) ---------- */
.pp-segbar { flex: none; display: flex; gap: 3px; margin: 8px 4px 10px; }
.pp-segbar i { flex: 1; height: 8px; border-radius: 4px; background: var(--wpl-tan); }
.pp-segbar i.s0 { background: #0F766E; }
.pp-segbar i.s1 { background: #d9824a; }
.pp-segbar i.s2 { background: #6fa05a; }
.pp-segbar i.s3 { background: #e0a810; }
.pp-segbar i.now { background: #fff; box-shadow: inset 0 0 0 2px #F2B518; }
@media (min-width: 521px) {
  #wpl-panel-path .pp-segbar { width: calc(100% - 8px); max-width: 612px; margin: 8px auto 10px; }
}

/* Chris (Sept 14): a tapped black key's name reads C♯ on one line. */
.pp-kb .b i { white-space: nowrap; }

/* Chris (Sept 14): the Up next line wraps instead of cutting off. Every Up
   next card keeps room for two lines so the card is the same size each time. */
.pp-list .pp-hero .pp-rtxt { display: flex; flex-direction: column; justify-content: center; }
@media (max-width: 520px) { .pp-list .pp-hero .pp-rtxt { min-height: 103px; } }
.pp-list .pp-hero .pp-rtxt small { white-space: normal; overflow: hidden; text-overflow: clip; line-height: 1.35; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; }
@media (max-width: 340px) { .pp-list .pp-hero .pp-rtxt { min-height: 137px; } }

/* Chris (Sept 14): keep questions swap with no fade; just the question pops in. */
.pp-qtext.pp-qpop { animation: pp-qpop 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.3); transform-origin: left center; }
@keyframes pp-qpop { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .pp-qtext.pp-qpop { animation: none; } }

/* Chris (Sept 14): the answer's name on the lit key (Two names: D flat). */
.pp-kb .b.tlabel i { color: #fff; white-space: nowrap; }

/* ---------- Practice Path: Up next unlock (Chris's picks C + S5, Sept 14) ---------- */
.pp-list .pp-row.pp-rowin { animation: pp-rowin 0.4s ease; }
.pp-list .pp-row.pp-rowin .pp-rcheck { animation: pp-cpop 0.45s cubic-bezier(0.3, 1.6, 0.5, 1) 0.2s both; }
@keyframes pp-rowin { from { opacity: 0.35; } to { opacity: 1; } }
@keyframes pp-cpop { from { transform: scale(0); } to { transform: scale(1); } }
.pp-hero.pp-unlock { animation: pp-unlock 0.55s ease; }
@keyframes pp-unlock { from { filter: grayscale(1); opacity: 0.55; } to { filter: none; opacity: 1; } }
.pp-hero.pp-sweep { position: relative; overflow: hidden; }
.pp-hero.pp-sweep::after { content: ""; position: absolute; inset: 0; pointer-events: none; transform: translateX(-110%); animation: pp-sweep 0.7s ease-out forwards;
  background: linear-gradient(100deg, transparent 20%, rgba(255, 230, 150, 0.85) 45%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 230, 150, 0.85) 55%, transparent 80%); }
@keyframes pp-sweep { to { transform: translateX(110%); } }
.pp-hero.pp-glow { animation: pp-heroglow 1.1s ease-out; }
@keyframes pp-heroglow { 0% { box-shadow: 0 0 0 3px var(--pp-gold-bg); } 40% { box-shadow: 0 0 0 10px rgba(242, 181, 24, 0.35); } 100% { box-shadow: 0 0 0 3px var(--pp-gold-bg); } }
@media (prefers-reduced-motion: reduce) { .pp-row.pp-rowin, .pp-row.pp-rowin .pp-rcheck, .pp-hero.pp-unlock, .pp-hero.pp-sweep::after, .pp-hero.pp-glow { animation: none; } }

/* Chris (Sept 14): on the Up next card the icon is never the same green as Start;
   a teal tile's icon turns gold there. */
.pp-list .pp-hero.pp-c0 .pp-ricon { background: #e0a810; }

/* Beta launch footer (Sept 14): Early Access line + where progress saves. */
.pp-foot { text-align: center; font-size: 12px; line-height: 1.5; color: var(--wpl-tan-text); font-weight: 600; margin: 18px 8px 6px; }
.pp-foot b { color: var(--pp-gold-ink); font-weight: 800; letter-spacing: 0.02em; }

/* Beta: chapters not built yet say Coming soon (Sept 14). */
.pp-soontag { font-style: normal; font-size: 10.5px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--pp-gold-ink); }
.pp-msg b { font-weight: 800; }

/* Daily practice summary: Retry the ones you missed (Sept 14). */
.pp-dretry { display: block; width: 100%; margin: 14px 0 0; }

/* Streak and goal chips explain themselves (Chris, Sept 14): hover on computers, tap on phones. */
#wpl-panel-path .pp-stat { position: relative; cursor: pointer; }
#wpl-panel-path .pp-stat:focus-visible { outline: 3px solid var(--wpl-accent); outline-offset: 2px; }
.pp-tip { position: absolute; left: 0; top: calc(100% + 10px); z-index: 40; width: 220px; padding: 10px 12px; border-radius: 12px; background: var(--wpl-ink); color: #fff; font-size: 12.5px; line-height: 1.4; font-weight: 500; text-align: left; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); opacity: 0; visibility: hidden; transform: translateY(-4px); transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s; pointer-events: none; white-space: normal; }
.pp-tip b { display: block; font-size: 13px; font-weight: 800; margin-bottom: 2px; color: #fff; }
.pp-tip::before { content: ""; position: absolute; left: 18px; top: -6px; border: 6px solid transparent; border-top: 0; border-bottom-color: var(--wpl-ink); }
#wpl-panel-path .pp-stat.open .pp-tip { opacity: 1; visibility: visible; transform: none; }
@media (hover: hover) { #wpl-panel-path .pp-stat:hover .pp-tip { opacity: 1; visibility: visible; transform: none; } }

/* Speed Drills not ready yet (Chris, Sept 14): faded, their date in gold, no arrow, no hover. */
.practice-tile[data-soon="date"] { opacity: 0.62; cursor: default; }
.practice-tile[data-soon="date"] .wpl-soon-date { color: var(--pp-gold-ink); font-weight: 800; }
#wpl-speed-drills-grid > .practice-tile[data-soon="date"]::after { display: none; }

/* Beta footer: Send feedback (Sept 14). */
.pp-foot .pp-feedback { display: inline-block; margin-top: 10px; padding: 8px 16px; border-radius: 999px; border: 1.5px solid var(--wpl-accent); color: var(--wpl-accent); font-weight: 800; font-size: 13px; text-decoration: none; }
.pp-foot .pp-feedback:focus-visible { outline: 3px solid var(--pp-gold); outline-offset: 2px; }
@media (hover: hover) { .pp-foot .pp-feedback:hover { background: var(--wpl-accent); color: #fff; } }
.pp-foot small { display: block; margin-top: 6px; font-size: 11.5px; font-weight: 600; }

/* Feedback popup (Sept 14). */
.pp-foot button.pp-feedback { background: transparent; font-family: inherit; cursor: pointer; }
.pp-fbmail { display: block; text-align: center; text-decoration: none; }
.pp-fbcopy { display: flex; align-items: center; gap: 8px; margin-top: 10px; padding: 8px 8px 8px 12px; border: 1.5px solid var(--wpl-tan); border-radius: 12px; font-size: 13.5px; font-weight: 700; }
.pp-fbcopy span { flex: 1; min-width: 0; overflow-wrap: anywhere; user-select: all; -webkit-user-select: all; }
.pp-fbcopy button { flex: none; border: 0; border-radius: 9px; background: var(--wpl-accent); color: #fff; font: 800 13px inherit; font-family: inherit; padding: 7px 12px; cursor: pointer; }

/* All tools, labeled (Chris, Sept 14): the only way back to the home tiles. */
.pp-toolsbtn.pp-toolslbl { width: auto; height: 34px; border-radius: 999px; padding: 0 12px 0 10px; display: inline-flex; align-items: center; gap: 6px; font: 700 13px inherit; font-family: inherit; white-space: nowrap; }
@media (hover: hover) { .pp-toolsbtn.pp-toolslbl:hover { border-color: var(--wpl-accent); color: var(--wpl-accent); } }
.pp-toolsbtn:focus-visible { outline: 3px solid var(--wpl-accent); outline-offset: 2px; }

/* ---------- Student sign in (Sept 14) ---------- */
.pp-input { display: block; width: 100%; box-sizing: border-box; margin-top: 14px; border: 1.5px solid var(--wpl-line); border-radius: 12px; padding: 12px 14px; font-family: inherit; font-size: 16px; font-weight: 600; background: #fff; color: var(--wpl-ink); }
.pp-input:focus { outline: none; border-color: var(--wpl-accent); box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.18); }
.pp-input.pp-codein { text-align: center; letter-spacing: 0.4em; font-size: 24px; font-weight: 800; padding-left: 0.4em; }
.pp-authmsg { min-height: 1.35em; margin-top: 8px; font-size: 13px; font-weight: 600; color: #b4532a; }
.pp-authmsg.ok { color: var(--wpl-accent); }
.pp-authform .pp-primary { margin-top: 6px; }
.pp-primary:disabled { opacity: 0.6; cursor: default; }
.pp-authlinks { display: flex; justify-content: space-between; gap: 10px; margin-top: 12px; }
.pp-linkbtn { border: 0; background: none; padding: 0; color: var(--wpl-accent); font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.pp-foot .pp-linkbtn { font-size: 12px; }

/* Welcome / About the Beta (Sept 14). */
.pp-hello, .pp-soonlist { display: grid; gap: 8px; margin-top: 12px; }
.pp-hello > div, .pp-soonlist > div { display: grid; gap: 1px; padding: 9px 12px; border-radius: 12px; background: var(--wpl-tan); font-size: 13px; line-height: 1.35; }
.pp-hello b, .pp-soonlist b { font-size: 14px; font-weight: 800; color: var(--wpl-ink); }
.pp-hello span, .pp-soonlist span { color: var(--wpl-tan-text); font-weight: 600; }
.pp-hello .pp-hprice { background: var(--pp-gold-bg); border: 1px solid var(--pp-gold-border); }
.pp-soonhead { margin-top: 16px; }
.pp-soonlist > div { background: transparent; border: 1px solid var(--wpl-line); padding: 8px 12px; }
.pp-hsig { margin: 10px 0 0; text-align: center; font-family: 'Fraunces', Georgia, serif; font-weight: 800; font-size: 15px; color: var(--wpl-tan-text); }
.pp-foot .pp-aboutbeta { display: block; margin: 10px auto 0; }

/* Welcome: the three rows are real buttons now; Coming soon is a plain list (Chris, Sept 14). */
.pp-hello > button { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; font-family: inherit; cursor: pointer; padding: 10px 12px; border-radius: 12px; border: 1.5px solid var(--wpl-line); background: #fff; color: var(--wpl-ink); }
.pp-hello > button > span { flex: 1; min-width: 0; display: grid; gap: 1px; font-size: 13px; line-height: 1.35; }
.pp-hello > button i { flex: none; font-style: normal; font-size: 22px; font-weight: 800; color: var(--wpl-accent); line-height: 1; }
@media (hover: hover) { .pp-hello > button:hover { border-color: var(--wpl-accent); } }
.pp-hello > button:focus-visible { outline: 3px solid var(--wpl-accent); outline-offset: 2px; }
.pp-soonlist { gap: 6px; }
.pp-soonlist > div { border: 0; background: transparent; padding: 2px 0 2px 16px; position: relative; }
.pp-soonlist > div::before { content: ""; position: absolute; left: 2px; top: 8px; width: 7px; height: 7px; border-radius: 50%; background: var(--pp-gold); }

/* Daily bar (Chris's pick A, Sept 14): streak, goal ring and Daily practice in one teal bar, left of All tools. */
.pp-top .pp-daily { flex: 1; width: auto; min-width: 0; margin: 0; height: 48px; padding: 0 10px 0 8px; gap: 9px; }
.pp-top .pp-daily.empty { background: var(--wpl-accent); color: #fff; cursor: pointer; }
.pp-top .pp-daily.empty .pp-ring-bg { stroke: rgba(255, 255, 255, 0.3); }
.pp-top .pp-daily .pp-dtxt { flex: 1; min-width: 0; line-height: 1.15; }
.pp-top .pp-daily b { font-size: 14.5px; }
.pp-top .pp-daily small { font-size: 12px; font-weight: 700; color: #cfeae4; opacity: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pp-top .pp-daily .pp-dgo { margin-left: 0; font-size: 22px; }
.pp-dfl svg { width: 12px; height: 12px; vertical-align: -1px; margin-right: 2px; }
.pp-top .pp-toolslbl { height: 48px; }

/* Daily bar: the explanation line may wrap to 2 lines on small phones (never cut off); All tools grows with it. */
#wpl-panel-path .pp-top { align-items: stretch; }
.pp-top .pp-daily { height: auto; min-height: 48px; padding-top: 6px; padding-bottom: 6px; }
.pp-top .pp-daily small { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.2; }
.pp-top .pp-toolslbl { height: auto; min-height: 48px; }

/* Welcome Coming soon, with life (Chris, Sept 14: "add some life"): colored icon tiles on tinted rows like the lesson list; Kids mode in rainbow letters. */
.pp-soonlist { gap: 8px; }
.pp-soonlist > .pp-soonitem { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 14px; border: 1px solid transparent; }
.pp-soonlist > .pp-soonitem::before { display: none; }
.pp-soonitem.pp-c0 { background: var(--wpl-tile-teal-bg); border-color: var(--wpl-tile-teal-border); }
.pp-soonitem.pp-c1 { background: var(--wpl-tile-peach-bg); border-color: var(--wpl-tile-peach-border); }
.pp-soonitem.pp-c2 { background: var(--wpl-tile-sage-bg); border-color: var(--wpl-tile-sage-border); }
.pp-soonitem.pp-c3 { background: var(--wpl-tile-gold-bg); border-color: var(--wpl-tile-gold-border); }
.pp-soonico { flex: none; width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center; color: #fff; }
.pp-soonico svg { width: 20px; height: 20px; }
.pp-soonitem.pp-c0 .pp-soonico { background: #0F766E; }
.pp-soonitem.pp-c1 .pp-soonico { background: #d9824a; }
.pp-soonitem.pp-c2 .pp-soonico { background: #6fa05a; }
.pp-soonitem.pp-c3 .pp-soonico { background: #e0a810; }
.pp-soonitem > span:last-child { display: grid; gap: 1px; min-width: 0; font-size: 13px; line-height: 1.35; }
.pp-kids { font-family: 'Fredoka', 'DM Sans', system-ui, sans-serif; font-weight: 700; font-size: 18px; letter-spacing: 1.5px; line-height: 1.2; }
.pp-kids i { font-style: normal; display: inline-block; }

/* Coming soon icons: always white on their colored squares (the shared icons carry their own colors). */
.pp-soonico svg, .pp-soonico svg * { stroke: #fff !important; }
.pp-soonico svg [fill]:not([fill="none"]) { fill: #fff !important; }

/* Daily bar only as wide as its text (Chris, Sept 14); All tools stays at the right. On small phones it still shrinks and wraps. */
.pp-top .pp-daily { flex: 0 1 auto; margin-right: auto; }

/* Tap pianos show which key you're on (Chris, Sept 14). Computers only; lesson pianos ignore
   the mouse (pointer-events: none), and lit keys (right, hint, start note) keep their colors. */
@media (hover: hover) {
  .pp-kb .w:not(.ok):not(.hint-on):not(.hint-one):not(.from):not(.lit):hover { background: #d8efea; }
  .pp-kb .b:not(.ok):not(.hint-on):not(.hint-one):not(.from):not(.lit):hover { background: #3f6f69; }
}

/* Quick fix (Chris, Sept 14): no scroll bar locked to the card on computers. The lesson list and
   long popups still scroll with the mouse wheel or trackpad; the edge fade shows there's more. */
@media (hover: hover) and (pointer: fine) {
  #pp-scroll, .pp-sheet { scrollbar-width: none; }
  #pp-scroll::-webkit-scrollbar, .pp-sheet::-webkit-scrollbar { display: none; }
}
