* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
        'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    background-color: #f5f5f5;
    padding: 20px;
    color: #2c3e50;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    overflow: hidden;
}

.header {
    position: relative;
    background-color: #2c3e50;
    color: white;
    padding: 20px;
    text-align: center;
}

/* Link to the source repo, tucked into the header's top-right corner. Inherits the
   header's white via currentColor and dims slightly until hover/focus. */
.source-link {
    position: absolute;
    top: 16px;
    right: 16px;
    display: inline-flex;
    color: white;
    opacity: 0.7;
    transition: opacity 0.15s ease;
}

.source-link:hover,
.source-link:focus-visible {
    opacity: 1;
}

.header h1 {
    margin: 0;
    font-size: 28px;
}

.header p {
    margin-top: 4px;
    opacity: 0.85;
    font-size: 14px;
}

.app {
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

#abc-textarea {
    width: 100%;
    height: 160px;
    padding: 12px;
    border: 1px solid #bdc3c7;
    border-radius: 4px;
    font-family: 'Courier New', monospace;
    font-size: 13px;
    resize: vertical;
    line-height: 1.5;
}

#abc-textarea:focus {
    outline: none;
    border-color: #3498db;
    box-shadow: 0 0 5px rgba(52, 152, 219, 0.3);
}

.input-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
}

#copy-link {
    padding: 6px 12px;
    background-color: #3498db;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 13px;
}

#copy-link:hover {
    background-color: #2980b9;
}

.copy-status {
    font-size: 13px;
    color: #27ae60;
}

.warnings {
    color: #b9770e;
    font-family: 'Courier New', monospace;
    font-size: 12px;
    white-space: pre-wrap;
}

.warnings:empty {
    display: none;
}

/* Notation area. With viewportHorizontal, abcjs makes #paper the scrolling
   viewport and renders the music into an inner .abcjs-inner div. */
#paper {
    position: relative;
    width: 100%;
    min-height: 200px;
    /* Room below the notation for the rhythm-game tap markers, which sit just under
       the lyrics; without it #paper's vertical clip (forced by overflow-x: auto)
       would hide them. */
    padding-bottom: 22px;
    background-color: #fafafa;
    border-radius: 4px;
    overflow-x: auto;
}

/* Let the inner div grow to the notation's natural (one-line) width so #paper
   overflows and scrolls horizontally instead of squishing the tune. */
#paper .abcjs-inner {
    display: inline-block;
    width: max-content !important;
    padding: 12px;
    position: relative; /* anchors the absolutely-positioned bouncing ball */
}

#audio-section {
    border-top: 1px solid #eee;
    padding-top: 12px;
}

/* Highlight the note currently sounding. */
#paper .abcjs-highlight {
    fill: #e74c3c;
}

.mix-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 20px;
    margin-top: 12px;
    font-size: 13px;
}

.mix-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}

/* Push the Rhythm game toggle to the far right of the controls row. */
.mix-toggle-end {
    margin-left: auto;
}

.mix-slider {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #555;
}

.mix-slider input[type="range"] {
    width: 120px;
}

/* The bouncing ball, overlaid on the notation and hopping lyric to lyric. */
.bouncing-ball {
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #ff7b7b, #e74c3c 70%);
    box-shadow: 0 0 8px rgba(231, 76, 60, 0.75);
    pointer-events: none;
    z-index: 10;
    opacity: 0;
    left: 0;
    top: 0;
}

/* Vertical "hop": ball rises then falls to land on the next syllable. */
@keyframes ball-hop {
    0%   { transform: translateY(0); }
    50%  { transform: translateY(-22px); }
    100% { transform: translateY(0); }
}

/* --- Rhythm game mode ---------------------------------------------------- */

.game-panel {
    margin-top: 12px;
    padding: 10px 12px;
    background: #f0f6ff;
    border: 1px solid #d6e4ff;
    border-radius: 4px;
}

.game-panel[hidden] {
    display: none;
}

.game-hint {
    font-size: 13px;
    color: #445;
}

/* Blindfold (hard) mode toggle. Sits at the panel's right edge so it lines up
   under the "Rhythm game" toggle above it (which mix-toggle-end pushes right).
   .mix-toggle is display:flex, so the label spans the panel and justify-content
   moves its contents — box and text together — over to that edge. */
.blindfold-toggle {
    justify-content: flex-end;
    margin-top: 8px;
    /* Cancel the panel's right padding + 1px border so the toggle lines up exactly
       with the "Rhythm game" checkbox above it, which sits flush to the controls
       row's edge. */
    margin-right: -13px;
    font-size: 13px;
    font-weight: 600;
    /* Dark grey-red, to read as the harder/riskier option. */
    color: #7a3b3b;
}

/* Tint the checkbox itself to match the label. accent-color styles the native
   control's checked fill, so it stays a normal checkbox with no custom markup. */
.blindfold-toggle input[type="checkbox"] {
    accent-color: #7a3b3b;
}

/* Shown only while the mix is faded out, so the player knows the silence is
   intentional — the ball and highlighting are gone at the same time. */
.game-status {
    min-height: 18px;
    margin-top: 8px;
    font-size: 13px;
    font-weight: 600;
    color: #445;
}

.game-status-blind {
    color: #7a3b3b; /* same grey-red as the blindfold toggle */
    animation: blind-pulse 1.6s ease-in-out infinite;
}

@keyframes blind-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
    .game-status-blind {
        animation: none;
    }
}

kbd {
    display: inline-block;
    padding: 1px 6px;
    font-family: 'Courier New', monospace;
    font-size: 12px;
    background: #fff;
    border: 1px solid #bcc;
    border-bottom-width: 2px;
    border-radius: 3px;
}

/* Big tap target for touch devices — the on-screen stand-in for the Space key.
   Hidden on pointer/hover ("desktop") devices, where Space is more precise; shown
   only where there's no fine pointer, so mobile users get a comfortable target. */
.game-tap {
    display: none;
    width: 100%;
    margin-top: 10px;
    padding: 22px;
    font-size: 20px;
    font-weight: 600;
    color: #fff;
    background: #2f6fed;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    /* Keep double-tap-to-zoom / text selection / tap highlight from interfering
       with rapid rhythmic tapping. */
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
}

.game-tap:active {
    background: #1f57c8;
    transform: scale(0.98);
}

@media (hover: none) and (pointer: coarse) {
    .game-tap {
        display: block;
    }
    /* On touch, the button replaces the keyboard hint. */
    .game-hint {
        display: none;
    }
}

.game-score {
    margin-top: 8px;
    font-size: 14px;
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
}

.game-score:empty {
    display: none;
}

.game-score .score-onTime { color: #27ae60; }
.game-score .score-early  { color: #2980b9; }
.game-score .score-late   { color: #d35400; }
.game-score .score-miss   { color: #c0392b; }
.game-score .score-avg    { color: #555; font-variant-numeric: tabular-nums; }

/* Persisted best round. */
.game-best {
    margin-top: 6px;
    font-size: 13px;
    color: #555;
}
.game-best:empty { display: none; }
.game-best .best-label { color: #888; }
.game-best .best-score { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Confetti burst for a new best: a full-viewport overlay of falling bits. */
.confetti-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    overflow: hidden;
    z-index: 1000;
}
.confetti-bit {
    position: absolute;
    top: -20px;
    border-radius: 1px;
    opacity: 0.9;
    animation-name: confetti-fall;
    animation-timing-function: ease-in;
    animation-fill-mode: forwards;
}
@keyframes confetti-fall {
    0%   { transform: translate(0, 0) rotate(0deg); opacity: 0.95; }
    100% {
        transform: translate(var(--drift, 0), 105vh) rotate(var(--spin, 360deg));
        opacity: 0.9;
    }
}
@media (prefers-reduced-motion: reduce) {
    .confetti-layer { display: none; }
}

/* Tap verdict markers, dropped under each syllable in the scrolling inner div: a
   centered "perfect" reference line with the verdict glyph shifted left (early) or
   right (late) of it by how far off the tap was. The marker's own center is anchored
   under the syllable, so the line lands exactly under it. */
.tap-marker {
    position: absolute;
    transform: translateX(-50%);
    width: 0; /* zero-width so the centered box and glyph reference the syllable center */
    height: 16px;
    line-height: 1;
    pointer-events: none;
    z-index: 9;
    animation: tap-pop 140ms ease-out;
}

/* The "perfect" target box, drawn empty under each syllable ahead of the tap and
   centered on the syllable. A tap "checks" it with a glyph. */
.tap-box {
    position: absolute;
    left: 0;
    top: 0;
    width: 14px;  /* matches JS BOX_PX */
    height: 14px;
    box-sizing: border-box;
    border: 1px solid rgba(0, 0, 0, 0.4);
    border-radius: 3px;
    transform: translateX(-50%);
    z-index: 0;
}

/* The verdict glyph checking the box, shifted horizontally (inline transform) off
   box-center by how early/late the tap was. */
.tap-glyph {
    position: absolute;
    left: 0;
    top: 1px;
    font-size: 12px;
    font-weight: bold;
    z-index: 1;
    /* Center the glyph on x=0 (the syllable/box center); the inline translateX adds
       the early/late offset on top of this. */
    margin-left: -0.35em;
}

.tap-marker.tap-on-time .tap-glyph { color: #27ae60; }
.tap-marker.tap-early   .tap-glyph { color: #2980b9; }
.tap-marker.tap-late    .tap-glyph { color: #d35400; }
.tap-marker.tap-miss    .tap-glyph { color: #c0392b; opacity: 0.7; }

/* Tint the box border to match the verdict once checked. */
.tap-marker.tap-on-time .tap-box { border-color: rgba(39, 174, 96, 0.7); }
.tap-marker.tap-early   .tap-box { border-color: rgba(41, 128, 185, 0.7); }
.tap-marker.tap-late    .tap-box { border-color: rgba(211, 84, 0, 0.7); }
.tap-marker.tap-miss    .tap-box { border-color: rgba(192, 57, 43, 0.55); }

@keyframes tap-pop {
    0%   { transform: translateX(-50%) scale(0.4); opacity: 0; }
    100% { transform: translateX(-50%) scale(1); opacity: 1; }
}

