/* DrumPath — visual system
 *
 * Built on GrooveMap's landing design language (public/index.html), not a
 * lookalike of it. The signatures carried over verbatim:
 *   · pill geometry (100px) for every button, chip, tag and label
 *   · cards at radius-xl with a 3px gradient edge on top, lift on hover
 *   · pill section labels in uppercase mono-ish tracking
 *   · 56px tinted icon tiles, stroke SVG inheriting currentColor
 *   · layered radial gradients + a masked grid for hero surfaces
 *   · the orange / purple / green / blue accent quartet
 *   · fade-in on scroll, visible-by-default without JS
 */

/* ============================================================
   Tokens — names mirror the landing page so both read the same
   ============================================================ */
:root {
    --bg-primary: #09090b;
    --bg-secondary: #0f0f13;
    --bg-card: #18181b;
    --bg-elevated: #1f1f23;
    --bg-raised: #26262b;

    --text-primary: #fafafa;
    --text-secondary: #a1a1aa;
    --text-muted: #71717a;
    --text-faint: #52525b;

    --accent-primary: #f97316;
    --accent-secondary: #fb923c;
    --accent-tertiary: #fdba74;
    --accent-glow: rgba(249, 115, 22, .25);
    --accent-glow-strong: rgba(249, 115, 22, .4);
    --accent-soft: rgba(249, 115, 22, .1);
    --accent-line: rgba(249, 115, 22, .2);

    --purple: #8b5cf6;
    --purple-light: #a78bfa;
    --purple-soft: rgba(139, 92, 246, .1);
    --purple-line: rgba(139, 92, 246, .2);

    --green: #22c55e;
    --green-light: #4ade80;
    --green-soft: rgba(34, 197, 94, .1);
    --green-line: rgba(34, 197, 94, .22);

    --blue: #3b82f6;
    --blue-light: #60a5fa;
    --blue-soft: rgba(59, 130, 246, .1);
    --blue-line: rgba(59, 130, 246, .2);

    --yellow: #f59e0b;
    --red: #ef4444;

    --border-color: rgba(255, 255, 255, .06);
    --border-hover: rgba(255, 255, 255, .12);
    --border-strong: rgba(255, 255, 255, .18);

    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-xl: 24px;
    --radius-2xl: 32px;
    --pill: 100px;

    --nav-h: 68px;
    --sat: env(safe-area-inset-top, 0px);
    --sab: env(safe-area-inset-bottom, 0px);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html { scroll-behavior: smooth; }

body {
    /* 'Pretendard Variable' is the family the CDN actually registers; the bare
       'Pretendard' below only ever matched a locally installed copy. */
    font-family: 'Outfit', 'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    background: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    font-size: 15px;
    overflow-x: hidden;
    word-break: keep-all;
    overflow-wrap: break-word;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    padding-top: calc(var(--nav-h) + var(--sat));
    padding-bottom: var(--sab);
}
body.no-scroll { overflow: hidden; }

.mono { font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, monospace; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
img, svg { display: block; max-width: 100%; }
::selection { background: rgba(249, 115, 22, .28); }
:focus-visible { outline: 2px solid var(--accent-secondary); outline-offset: 2px; border-radius: 4px; }

/* ============================================================
   Navigation
   ============================================================ */
nav.dp-nav {
    position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
    height: calc(var(--nav-h) + var(--sat));
    padding: var(--sat) 24px 0;
    display: flex; align-items: center; gap: 12px;
    background: rgba(9, 9, 11, .92);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border-bottom: 1px solid var(--border-color);
}

.dp-logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.dp-logo-mark { width: 30px; height: 30px; flex-shrink: 0; }
.dp-logo-word {
    font-size: 1.16rem; font-weight: 800; letter-spacing: -.035em;
    color: var(--text-primary); white-space: nowrap;
}
.dp-logo-word i { font-style: normal; color: var(--accent-primary); }

.nav-center { display: flex; align-items: center; gap: 4px; margin-left: 18px; }
.nav-link {
    padding: 8px 14px; border-radius: var(--pill);
    font-size: .9rem; font-weight: 500; color: var(--text-secondary);
    transition: color .2s, background .2s;
}
.nav-link:hover { color: var(--text-primary); background: rgba(255, 255, 255, .04); }
.nav-link.on { color: var(--accent-tertiary); background: var(--accent-soft); }

.nav-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.nav-account {
    display: flex; align-items: center; gap: 7px;
    padding: 6px 14px 6px 10px;
    border: 1px solid var(--border-color); border-radius: var(--pill);
    background: var(--bg-card);
    font-size: .8rem; color: var(--text-secondary);
    max-width: 210px; white-space: nowrap; overflow: hidden;
}
.nav-account .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--text-faint); flex-shrink: 0;
}
.nav-account.linked { color: var(--text-primary); }
.nav-account.linked .dot { background: var(--green); box-shadow: 0 0 8px rgba(34, 197, 94, .6); }
.nav-account span { overflow: hidden; text-overflow: ellipsis; }

.nav-cta {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 9px 18px;
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--pill);
    font-size: .82rem; font-weight: 600; color: var(--text-secondary);
    transition: all .2s; white-space: nowrap;
}
.nav-cta:hover { border-color: var(--accent-line); background: var(--accent-soft); color: var(--text-primary); }
.nav-cta .gm {
    font-family: 'JetBrains Mono', monospace; font-size: .74rem;
    letter-spacing: -.02em; color: var(--accent-primary);
}

/* --- Dropdown --- */
.dd { position: relative; }
.dd-btn {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 8px 13px; border-radius: var(--pill);
    border: 1px solid var(--border-color); background: var(--bg-card);
    font-size: .85rem; font-weight: 500; color: var(--text-secondary);
    transition: all .2s; white-space: nowrap;
}
.dd-btn:hover { border-color: var(--border-hover); color: var(--text-primary); }
.dd.open .dd-btn { border-color: var(--accent-line); color: var(--text-primary); background: var(--accent-soft); }
.dd-btn .caret { transition: transform .2s; }
.dd.open .dd-btn .caret { transform: rotate(180deg); }

.dd-menu {
    position: absolute; top: calc(100% + 10px); right: 0; z-index: 1200;
    min-width: 268px; max-height: min(60vh, 460px); overflow-y: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border-hover);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 60px -12px rgba(0, 0, 0, .7), 0 0 0 1px rgba(255, 255, 255, .02);
    padding: 8px;
    opacity: 0; transform: translateY(-6px) scale(.98);
    pointer-events: none;
    transition: opacity .18s ease, transform .18s cubic-bezier(.4, 0, .2, 1);
}
.dd.open .dd-menu { opacity: 1; transform: none; pointer-events: auto; }
.dd-menu.left { right: auto; left: 0; }

.dd-label {
    padding: 9px 12px 6px; font-size: .68rem; font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint);
}
.dd-item {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 9px 12px; border-radius: var(--radius-sm);
    font-size: .88rem; color: var(--text-secondary); text-align: left;
    transition: background .15s, color .15s;
}
.dd-item:hover { background: rgba(255, 255, 255, .05); color: var(--text-primary); }
.dd-item.on { background: var(--accent-soft); color: var(--accent-tertiary); }
.dd-item .lead {
    width: 22px; height: 22px; border-radius: 6px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    font-family: 'JetBrains Mono', monospace; font-size: .68rem; font-weight: 700;
    background: rgba(255, 255, 255, .06); color: var(--text-muted);
}
.dd-item.on .lead { background: var(--accent-line); color: var(--accent-tertiary); }
.dd-item .tail { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: .72rem; color: var(--text-faint); }
.dd-sep { height: 1px; background: var(--border-color); margin: 6px 4px; }

/* ============================================================
   Layout primitives
   ============================================================ */
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; }
.screen { display: none; }
.screen.on { display: block; }
.pad-b { padding-bottom: 100px; }

.section-label {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 6px 15px; border-radius: var(--pill);
    background: var(--bg-card); border: 1px solid var(--border-color);
    font-size: .72rem; font-weight: 700; color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: .09em;
}
.section-label.orange { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-tertiary); }
.section-label.purple { background: var(--purple-soft); border-color: var(--purple-line); color: var(--purple-light); }
.section-label.green  { background: var(--green-soft);  border-color: var(--green-line);  color: var(--green-light); }
.section-label.blue   { background: var(--blue-soft);   border-color: var(--blue-line);   color: var(--blue-light); }

.section-head { margin-bottom: 36px; }
.section-head h2 {
    font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.15rem);
    font-weight: 800; letter-spacing: -.03em; line-height: 1.22; margin-top: 16px;
}
.section-head p { color: var(--text-secondary); font-size: 1rem; margin-top: 12px; max-width: 620px; line-height: 1.7; }

.gradient-orange {
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-tertiary));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.gradient-purple {
    background: linear-gradient(135deg, var(--purple), var(--purple-light));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}

/* ============================================================
   Buttons — pill geometry, exactly as the landing page
   ============================================================ */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 13px 26px; border-radius: var(--pill);
    font-size: .94rem; font-weight: 600; letter-spacing: -.01em;
    border: 1px solid transparent; white-space: nowrap;
    transition: all .25s cubic-bezier(.4, 0, .2, 1);
}
.btn:disabled { opacity: .35; cursor: not-allowed; }
.btn svg { flex-shrink: 0; }

.btn-primary {
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
    color: #fff; box-shadow: 0 4px 16px var(--accent-glow);
}
.btn-primary:not(:disabled):hover { transform: translateY(-2px); box-shadow: 0 12px 32px var(--accent-glow-strong); }

.btn-secondary { background: var(--bg-card); color: var(--text-primary); border-color: var(--border-color); }
.btn-secondary:not(:disabled):hover { background: var(--bg-elevated); border-color: var(--border-hover); }

.btn-ghost { background: transparent; color: var(--text-secondary); border-color: var(--border-color); }
.btn-ghost:not(:disabled):hover { color: var(--text-primary); border-color: var(--border-hover); background: rgba(255, 255, 255, .03); }

.btn-lg { padding: 16px 32px; font-size: 1rem; }
.btn-sm { padding: 8px 15px; font-size: .82rem; }
.btn-icon { padding: 9px; width: 38px; height: 38px; border-radius: 50%; }

/* ============================================================
   Chips / tags — pills
   ============================================================ */
.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 12px; border-radius: var(--pill);
    background: rgba(255, 255, 255, .05); border: 1px solid var(--border-color);
    font-size: .74rem; font-weight: 600; color: var(--text-muted);
}
.chip.mono { font-family: 'JetBrains Mono', monospace; letter-spacing: -.02em; }
.chip.orange { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-tertiary); }
.chip.purple { background: var(--purple-soft); border-color: var(--purple-line); color: var(--purple-light); }
.chip.green  { background: var(--green-soft);  border-color: var(--green-line);  color: var(--green-light); }
.chip.blue   { background: var(--blue-soft);   border-color: var(--blue-line);   color: var(--blue-light); }
.chip.teal   { background: rgba(45, 212, 191, .1); border-color: rgba(45, 212, 191, .22); color: #5eead4; }
a.chip { transition: all .2s; }
a.chip:hover { border-color: var(--border-hover); color: var(--text-primary); }

/* ============================================================
   Hero
   ============================================================ */
.hero { position: relative; padding: 92px 0 64px; overflow: hidden; }
.hero-bg {
    position: absolute; inset: 0; pointer-events: none;
    background:
        radial-gradient(ellipse 55% 45% at 22% 8%, rgba(249, 115, 22, .16), transparent 62%),
        radial-gradient(ellipse 40% 35% at 78% 22%, rgba(139, 92, 246, .09), transparent 65%),
        radial-gradient(ellipse 40% 30% at 55% 95%, rgba(59, 130, 246, .05), transparent);
}
.hero-grid {
    position: absolute; inset: 0; pointer-events: none;
    background-image:
        linear-gradient(rgba(255, 255, 255, .018) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .018) 1px, transparent 1px);
    background-size: 56px 56px;
    -webkit-mask-image: radial-gradient(ellipse 75% 70% at 35% 30%, #000, transparent);
    mask-image: radial-gradient(ellipse 75% 70% at 35% 30%, #000, transparent);
}
.hero-inner { position: relative; z-index: 1; max-width: 1180px; margin: 0 auto; padding: 0 24px; }

/* Text on the left, a live pattern preview on the right — the landing page
   fills this same slot with its product video. */
.hero-split {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 486px);
    gap: 52px; align-items: center;
}
.hero-visual { position: relative; }
.hero-visual .stage { box-shadow: 0 0 0 1px rgba(255, 255, 255, .03), 0 34px 80px -20px rgba(0, 0, 0, .7), 0 0 90px -30px var(--accent-glow); }
.hero-visual .score-pad { padding: 16px 18px 18px; }
/* 10차컷 lesson demo is a portrait phone capture (584×1274). hero-reel.css
   declares the generic reel 16:9, so the tall variant re-derives its height
   from the real ratio and lets width do the sizing — a phone-shaped card. */
.hero-visual .reel--tall { aspect-ratio: 584 / 1274; width: min(100%, 290px); margin: 0 auto; }
.hero-cap {
    display: flex; align-items: center; gap: 8px;
    padding: 11px 18px; border-top: 1px solid var(--border-color);
    background: var(--bg-secondary); font-size: .78rem; color: var(--text-muted);
}
.hero-cap .beat {
    width: 7px; height: 7px; border-radius: 50%; background: var(--accent-primary);
    box-shadow: 0 0 10px var(--accent-glow-strong); animation: pulse 1.3s ease-in-out infinite;
}
.hero-cap .mono { color: var(--accent-tertiary); font-weight: 700; }

.hero-badge {
    display: inline-flex; align-items: center; gap: 9px;
    padding: 6px 16px 6px 11px; border-radius: var(--pill);
    background: var(--purple-soft); border: 1px solid var(--purple-line);
    font-size: .8rem; font-weight: 500; color: var(--purple-light);
}
.hero-badge .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--purple-light); box-shadow: 0 0 8px var(--purple-light);
    animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .6; transform: scale(.85); } }

.hero h1 {
    font-size: clamp(2.1rem, 1.4rem + 3.4vw, 3.5rem);
    font-weight: 800; line-height: 1.12; letter-spacing: -.04em; margin-top: 26px;
}
.hero .lede {
    font-size: clamp(.98rem, .94rem + .3vw, 1.1rem); color: var(--text-secondary);
    line-height: 1.8; margin-top: 20px; max-width: 620px;
}
.hero .lede strong { color: var(--text-primary); font-weight: 600; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 34px; }

/* Stat row */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 56px; }
.stat { text-align: left; }
.stat b {
    display: block; font-size: 2.4rem; font-weight: 800;
    letter-spacing: -.035em; line-height: 1.1;
}
.stat span { color: var(--text-muted); font-size: .84rem; font-weight: 500; }
.stat.orange b { color: var(--accent-primary); }
.stat.green b { color: var(--green); }
.stat.purple b { color: var(--purple-light); }
.stat.blue b { color: var(--blue-light); }

/* ============================================================
   Cards
   ============================================================ */
.card {
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-xl); padding: 28px;
}

.tile-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.tile {
    position: relative; overflow: hidden; text-align: left; width: 100%;
    padding: 34px 30px; background: var(--bg-card);
    border: 1px solid var(--border-color); border-radius: var(--radius-xl);
    transition: transform .3s, border-color .3s, background .3s;
}
.tile:hover { transform: translateY(-6px); border-color: var(--border-hover); }
.tile::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; }
.tile.orange::before { background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); }
.tile.purple::before { background: linear-gradient(90deg, var(--purple), var(--purple-light)); }
.tile.green::before  { background: linear-gradient(90deg, var(--green), var(--green-light)); }
.tile.blue::before   { background: linear-gradient(90deg, var(--blue), var(--blue-light)); }

.tile-icon {
    width: 56px; height: 56px; border-radius: var(--radius-lg);
    display: flex; align-items: center; justify-content: center; margin-bottom: 22px;
}
.tile-icon svg { width: 27px; height: 27px; stroke: currentColor; fill: none; stroke-width: 1.6; }
.tile.orange .tile-icon { background: rgba(249, 115, 22, .12); color: var(--accent-primary); }
.tile.purple .tile-icon { background: rgba(139, 92, 246, .12); color: var(--purple-light); }
.tile.green  .tile-icon { background: rgba(34, 197, 94, .12);  color: var(--green); }
.tile.blue   .tile-icon { background: rgba(59, 130, 246, .12); color: var(--blue-light); }

.tile h3 { font-size: 1.24rem; font-weight: 700; letter-spacing: -.02em; }
.tile .sub { font-size: .84rem; font-weight: 600; margin-top: 6px; }
.tile.orange .sub { color: var(--accent-tertiary); }
.tile.purple .sub { color: var(--purple-light); }
.tile.green .sub { color: var(--green-light); }
.tile p { color: var(--text-secondary); font-size: .9rem; line-height: 1.75; margin-top: 14px; }

.meter { height: 6px; border-radius: var(--pill); background: rgba(255, 255, 255, .07); overflow: hidden; margin-top: 20px; }
.meter i { display: block; height: 100%; border-radius: var(--pill); transition: width .5s cubic-bezier(.4, 0, .2, 1); }
.tile.orange .meter i { background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); }
.tile.purple .meter i { background: linear-gradient(90deg, var(--purple), var(--purple-light)); }
.tile.green .meter i  { background: linear-gradient(90deg, var(--green), var(--green-light)); }

/* Continue card */
.resume {
    display: grid; grid-template-columns: 1fr auto; gap: 28px; align-items: center;
    padding: 30px 34px; border-radius: var(--radius-2xl);
    background: linear-gradient(135deg, rgba(249, 115, 22, .11), rgba(249, 115, 22, .02) 60%);
    border: 1px solid var(--accent-line);
    box-shadow: 0 0 70px -30px var(--accent-glow);
}
.resume h3 { font-size: 1.45rem; font-weight: 700; letter-spacing: -.025em; margin-top: 12px; }
.resume p { color: var(--text-secondary); font-size: .94rem; margin-top: 8px; }
.resume .chips { margin-top: 16px; }

/* Step cards */
.step-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.step {
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-xl); overflow: hidden; transition: transform .3s, border-color .3s;
}
.step:hover { transform: translateY(-4px); border-color: var(--border-hover); }
.step-visual {
    height: 132px; background: var(--bg-secondary); position: relative;
    display: flex; align-items: center; justify-content: center;
}
.step-num {
    position: absolute; top: 14px; left: 14px;
    width: 30px; height: 30px; border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
    display: flex; align-items: center; justify-content: center;
    font-family: 'JetBrains Mono', monospace; font-size: .78rem; font-weight: 800; color: #fff;
}
.step-ico {
    width: 56px; height: 56px; border-radius: var(--radius-lg);
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 8px 24px var(--accent-glow);
}
.step-ico svg { width: 27px; height: 27px; stroke: #fff; stroke-width: 1.6; fill: none; }
.step-body { padding: 20px 22px 24px; }
.step-body h4 { font-size: 1.02rem; font-weight: 700; letter-spacing: -.02em; }
.step-body p { color: var(--text-secondary); font-size: .85rem; line-height: 1.7; margin-top: 8px; }

/* GrooveMap bridge */
.bridge {
    position: relative; overflow: hidden;
    display: grid; grid-template-columns: 1fr auto; gap: 30px; align-items: center;
    padding: 38px; border-radius: var(--radius-2xl);
    background: var(--bg-secondary); border: 1px solid var(--border-color);
}
.bridge::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(ellipse 50% 90% at 88% 50%, rgba(139, 92, 246, .10), transparent 70%);
}
.bridge > * { position: relative; z-index: 1; }
.bridge h3 { font-size: 1.32rem; font-weight: 700; letter-spacing: -.025em; margin-top: 14px; }
.bridge p { color: var(--text-secondary); font-size: .93rem; margin-top: 10px; max-width: 580px; line-height: 1.75; }

/* ============================================================
   Curriculum
   ============================================================ */
.tabs { display: flex; gap: 8px; margin: 30px 0 26px; overflow-x: auto; padding-bottom: 2px; }
.tab {
    display: inline-flex; align-items: center; gap: 9px;
    padding: 10px 18px; border-radius: var(--pill);
    border: 1px solid var(--border-color); background: var(--bg-card);
    font-size: .9rem; font-weight: 600; color: var(--text-secondary);
    transition: all .2s; white-space: nowrap;
}
.tab:hover { border-color: var(--border-hover); color: var(--text-primary); }
.tab.on {
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
    border-color: transparent; color: #fff;
    box-shadow: 0 4px 16px var(--accent-glow);
}
.tab .cnt { font-family: 'JetBrains Mono', monospace; font-size: .74rem; opacity: .72; }

.module {
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-xl); margin-bottom: 16px; overflow: hidden;
    transition: border-color .25s;
}
.module.open { border-color: var(--border-hover); }
.module-head {
    display: flex; align-items: center; gap: 16px; width: 100%;
    padding: 20px 24px; text-align: left; transition: background .2s;
}
.module-head:hover { background: var(--bg-elevated); }
.module-badge {
    width: 42px; height: 42px; border-radius: var(--radius-md); flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    font-family: 'JetBrains Mono', monospace; font-weight: 800; font-size: 1.05rem;
}
.module-badge.basics { background: rgba(45, 212, 191, .12); color: #2dd4bf; }
.module-badge.technique { background: rgba(249, 115, 22, .12); color: var(--accent-primary); }
.module-badge.time { background: rgba(59, 130, 246, .12); color: var(--blue-light); }
.module-badge.coordination { background: rgba(139, 92, 246, .12); color: var(--purple-light); }
.module-badge.vocabulary { background: rgba(34, 197, 94, .12); color: var(--green); }
.module-head .t { flex: 1; min-width: 0; }
.module-head .t b { display: block; font-size: 1.04rem; font-weight: 700; letter-spacing: -.02em; }
.module-head .t span { font-size: .82rem; color: var(--text-muted); }
.module-ring { flex-shrink: 0; display: flex; align-items: center; gap: 10px; }
.module-ring .n { font-family: 'JetBrains Mono', monospace; font-size: .78rem; color: var(--text-muted); }
.module-head .caret { color: var(--text-muted); transition: transform .25s; flex-shrink: 0; }
.module.open .caret { transform: rotate(90deg); }
.module-body { display: none; border-top: 1px solid var(--border-color); }
.module.open .module-body { display: block; }

.lesson-row {
    display: flex; align-items: center; gap: 15px; width: 100%;
    padding: 14px 24px; text-align: left;
    border-bottom: 1px solid rgba(255, 255, 255, .035);
    transition: background .18s, opacity .18s;
}
.lesson-row:last-child { border-bottom: none; }
.lesson-row:hover { background: var(--bg-elevated); }
.lesson-row.locked { opacity: .55; }
.lesson-row.locked:hover { opacity: .82; }

.state {
    width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border: 1.5px solid var(--border-strong); font-size: .72rem; color: var(--text-muted);
}
.state.done { border-color: var(--green); background: var(--green-soft); color: var(--green); }
.state.mastered { border-color: var(--accent-primary); background: var(--accent-soft); color: var(--accent-tertiary); }
.state.open { border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-soft); }

.lesson-row .lid {
    font-family: 'JetBrains Mono', monospace; font-size: .74rem;
    color: var(--text-faint); width: 56px; flex-shrink: 0;
}
.lesson-row .info { flex: 1; min-width: 0; }
/* 제목에도 말줄임이 필요하다. .side 가 flex-shrink:0 이라 칩이 여럿 붙는 행에서는
   폰에서 .info 가 20px 남짓까지 눌리고, 그러면 제목이 한 글자씩 세로로 쌓인다
   (연주 패턴이 없는 107개 레슨 전부가 해당 — 8/10 실측). */
.lesson-row .info b {
    display: block; font-size: .94rem; font-weight: 600; letter-spacing: -.01em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lesson-row .info span {
    display: block; font-size: .81rem; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lesson-row .side { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

.diff { display: flex; gap: 2.5px; }
.diff i { width: 4px; height: 13px; border-radius: 2px; background: rgba(255, 255, 255, .12); }
.diff i.on { background: var(--accent-primary); }

.milestone {
    display: flex; align-items: center; gap: 15px; padding: 15px 24px;
    background: linear-gradient(90deg, rgba(139, 92, 246, .09), transparent 65%);
    border-bottom: 1px solid rgba(255, 255, 255, .035);
    border-left: 2px solid var(--purple);
}
.milestone .ic {
    width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
    background: rgba(139, 92, 246, .14); color: var(--purple-light);
    display: flex; align-items: center; justify-content: center;
}
.milestone .ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.milestone .info { flex: 1; min-width: 0; }
.milestone .info b { display: block; font-size: .9rem; font-weight: 600; color: var(--purple-light); }
.milestone .info span { font-size: .8rem; color: var(--text-muted); }

/* ============================================================
   Lesson
   ============================================================ */
.lesson-hero { position: relative; padding: 40px 0 28px; overflow: hidden; }
.lesson-hero::before {
    content: ''; position: absolute; inset: -60px 0 auto; height: 240px; pointer-events: none;
    background: radial-gradient(ellipse 60% 100% at 18% 0%, rgba(249, 115, 22, .10), transparent 65%);
}
.lesson-hero > * { position: relative; }
/* The lesson's main instrument, sunk into the hero on the right. It sits far
   enough back not to fight the title, and is dropped entirely below 1080px
   where the text reflows across the full width. */
.hero-kit {
    position: absolute; right: -40px; top: 50%; transform: translateY(-50%);
    width: 340px; height: 340px; opacity: .3; pointer-events: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 45%);
    mask-image: linear-gradient(90deg, transparent, #000 45%);
}
@media (max-width: 1080px) { .hero-kit { display: none; } }
.crumb { display: flex; align-items: center; gap: 9px; font-size: .82rem; color: var(--text-muted); flex-wrap: wrap; }
.crumb a { transition: color .2s; }
.crumb a:hover { color: var(--text-primary); }
.crumb .sep { color: var(--text-faint); }
.crumb-mod { display: inline-flex; align-items: center; gap: 6px; }
.crumb-mod .emblem { opacity: .9; }
.lesson-hero h1 {
    font-size: clamp(1.5rem, 1.15rem + 1.7vw, 2.3rem);
    font-weight: 800; letter-spacing: -.035em; line-height: 1.18; margin-top: 14px;
}
.lesson-hero .goal { color: var(--text-secondary); font-size: 1.02rem; margin-top: 10px; }
.lesson-hero .chips { margin-top: 18px; }

/* Progress inside the module — the curriculum screen showed it, the lesson
   screen (where the time is actually spent) did not. */
.mod-rail {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin-top: 20px;
}
.mod-rail-track {
    width: 168px; height: 4px; border-radius: var(--pill); overflow: hidden;
    background: rgba(255, 255, 255, .08); flex-shrink: 0;
}
.mod-rail-track i {
    display: block; height: 100%; border-radius: var(--pill);
    background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
    transition: width .5s cubic-bezier(.4, 0, .2, 1);
}
.mod-rail-num { font-size: .76rem; font-weight: 700; color: var(--text-secondary); }
.mod-rail-sub { font-size: .78rem; color: var(--text-muted); }

.lesson-grid { display: grid; grid-template-columns: minmax(0, 1fr) 352px; gap: 26px; align-items: start; }
/* Teaching note + common mistakes sit under the stage, two-up, so the left
   column reaches roughly the aside's height instead of leaving a black void —
   worst on the 107 lessons that carry text but no playable pattern. */
.lesson-read {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
    gap: 18px; margin-top: 18px; align-items: start;
}
.lesson-read .panel { margin: 0; }

/* Stage — window chrome borrowed from the landing's score-window */
.stage {
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-xl); overflow: hidden;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .03), 0 30px 70px -24px rgba(0, 0, 0, .6);
}
.stage-bar {
    display: flex; align-items: center; gap: 8px;
    padding: 12px 18px; background: var(--bg-secondary);
    border-bottom: 1px solid var(--border-color);
}
.stage-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.stage-dot.r { background: #ff5f57; } .stage-dot.y { background: #febc2e; } .stage-dot.g { background: #28c840; }
.stage-title {
    margin-left: 10px; font-size: .78rem; font-weight: 500; color: var(--text-muted);
    font-family: 'JetBrains Mono', monospace;
}
/* 페이저·✕ 는 폭을 양보하지 않는다 — 줄어드는 쪽은 레슨 제목이다(ellipsis).
   양보하게 두면 320px 에서 "1 / 2" 필이 두 줄로 접히며 바가 통째로 두 줄이 된다.
   🔴 여기에 .page-pill{white-space:nowrap} 을 더하지 말 것. 그러면 필의 min-content
   가 커져 바 → 카드 폭으로 전파되고, 카드가 넓어지면 vf 가 bars-per-row 를 1→2 로
   올려 행이 줄고, 행이 줄면 페이지가 1이 되어 updatePageLabel 이 페이저를 숨기고,
   그러면 다시 좁아진다 — 자유 연습 화면이 영원히 리플로한다(8/09 실측: pages 2↔1,
   카드 폭 423↔370 이 0.5초 주기로 무한 반복). 위 두 줄만으로 접힘은 이미 막힌다. */
.stage-bar .right { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }
#pager-group { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.page-pill {
    font-family: 'JetBrains Mono', monospace; font-size: .72rem; color: var(--text-muted);
    padding: 4px 11px; border-radius: var(--pill);
    background: rgba(255, 255, 255, .05); border: 1px solid var(--border-color);
}
.pager {
    width: 28px; height: 28px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-color); color: var(--text-muted);
    transition: all .18s;
}
.pager:hover:not(:disabled) { border-color: var(--border-hover); color: var(--text-primary); background: rgba(255, 255, 255, .04); }
.pager:disabled { opacity: .3; cursor: not-allowed; }

/* ---- 레슨 정체성 + 레슨 시트 (2026-08-09) ----
   풀스크린 플레이어가 히어로를 가리므로 stage-bar 가 "무슨 레슨 · 몇 번째"를
   대신 말한다. 그 표시 자체가 레슨 드롭다운의 트리거다(app.js renderLessonMenu):
   정체성과 메뉴가 한 몸이라 플레이어 안에서 레슨을 갈아탈 수 있다. */
.lesson-dd { flex: 0 1 auto; min-width: 0; margin-left: 8px; }
.lesson-pick {
    max-width: 100%; padding: 5px 10px; gap: 8px;
    /* body 의 1.6 을 승계하면 트리거 하나가 바를 5.7px 부풀린다(8/09 실측). */
    font-size: .82rem; font-weight: 600; line-height: 1.25;
    background: transparent; border-color: transparent;
    color: var(--text-secondary);
}
.lesson-pick:hover { background: var(--bg-card); border-color: var(--border-hover); }
.lp-pos {
    flex-shrink: 0; font-size: .68rem; font-weight: 700; letter-spacing: -.02em;
    color: var(--accent-tertiary); background: var(--accent-soft);
    border-radius: var(--pill); padding: 2px 7px;
}
.lp-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lesson-pick .caret { flex-shrink: 0; opacity: .7; }

/* 플레이어에서 .stage 는 position:fixed + z-index:1500 이라 **스태킹 컨텍스트를
   만든다** — 시트는 그 안에서 .flow-chat(6) 정도와만 겨루므로 1500 을 넘길 필요가
   없고, 넘길 수도 없다. 카드 상태에서는 상속된 1200 이 nav(1000) 를 이긴다.
   여기서 굳이 값을 두는 건 플레이어 안 겨루기를 못박기 위해서다.
   🔴 시트를 body 로 포털시키면 그때는 진짜로 플레이어 뒤로 숨는다 — 옮기지 말 것. */
.lesson-dd .dd-menu {
    z-index: 1600; min-width: min(304px, calc(100vw - 30px));
    overscroll-behavior: contain;
}
.lm-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lm-mark { flex-shrink: 0; display: flex; font-size: .8rem; color: var(--text-faint); }
.lm-mark.orange { color: var(--accent-primary); }
.lm-mark.green { color: var(--green); }

/* 시범 화면 소개 — 학생이 이 레슨을 처음 만나는 화면이라 제목·순번·목표를 한 번에
   말한다. 다른 단계에서는 stage-bar 한 줄이 이어받고, 플레이어 밖에서는 히어로가
   같은 말을 하고 있으므로 숨긴다.
   두 단계인 이유: 3-D 가 올라온 화면의 밑변은 #stage3d 이고 거기엔 바닥이 없다
   (flex:1 + min-height:0 — 캔버스가 줄어들 뿐). 반대로 3-D 가 아직 안 뜬 시범
   "대기" 화면은 악보가 그 자리를 지키고 악보에는 단단한 최소 높이가 있다.
   8/09 실측: 대기에서도 목표를 편 채로 두면 48곡 중 B-A-10(가장 긴 악보)이
   605/592 로 13px 넘쳐 내부 스크롤로 강등됐다. 대기에서 접으면 78 → 46.6px.
   :has() 미지원 브라우저에서는 접힌 쪽으로 고정된다 = 절대 넘치지 않는 열화. */
.lesson-ident { display: none; padding: 0 16px; }
.li-mod { font-size: .72rem; font-weight: 700; letter-spacing: .04em; color: var(--accent-tertiary); }
/* 320px 에서 긴 제목이 두 줄로 감기면 소개 블록이 78 → 103px 로 부푼다. */
.li-title {
    font-size: 1.08rem; font-weight: 800; line-height: 1.25; margin: 1px 0 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.li-goal {
    margin: 4px 0 0; font-size: .84rem; line-height: 1.5; color: var(--text-muted);
    display: none; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
body.flow-demo .lesson-ident { display: block; }
body.flow-demo:has(.stage3d:not([hidden])) .lesson-ident { padding: 2px 16px 0; }
body.flow-demo:has(.stage3d:not([hidden])) .li-title { font-size: 1.24rem; margin-top: 3px; }
body.flow-demo:has(.stage3d:not([hidden])) .li-goal { display: -webkit-box; }

/* ---- Score sheet (VexFlow staff notation) ----
   The renderer draws black ink on transparent, so the staff sits on paper
   rather than on the near-black card. It also matches GrooveMap's own score
   surface, so a student moving between the two products sees one notation. */
.score-pad { position: relative; padding: 18px 18px 16px; }
.dp-score { display: block; width: 100%; }
.dp-sheet-viewport {
    position: relative; overflow: hidden;
    background: #fff; border-radius: var(--radius-lg);
    padding: 10px 12px;
    /* content-box on purpose: score.js sets height to exactly N rows of staff,
       and the global border-box would eat that budget with the padding. */
    box-sizing: content-box;
    scroll-behavior: smooth;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06), 0 12px 30px -14px rgba(0, 0, 0, .8);
}
@media (prefers-reduced-motion: reduce) { .dp-sheet-viewport { scroll-behavior: auto; } }
.dp-sheet-stack { position: relative; }
.dp-sheet { position: relative; }
.dp-sheet .vf-system { justify-content: flex-start; }
.dp-sheet-fail {
    color: #52525b; font-size: .88rem; text-align: center;
    padding: 26px 16px; line-height: 1.7;
}
/* Overlay: everything we draw on top of the renderer's SVG. It is a sibling of
   the render target because renderAll() empties its own container. */
.dp-sheet-overlay { position: absolute; inset: 0; pointer-events: none; }

.dp-cursor {
    position: absolute; width: 2px; margin-left: -1px; z-index: 4;
    background: var(--accent-primary);
    box-shadow: 0 0 7px rgba(249, 115, 22, .75);
}
.dp-cursor > i {
    position: absolute; left: 50%; top: -1px; transform: translateX(-50%);
    width: 0; height: 0; border-left: 5px solid transparent;
    border-right: 5px solid transparent; border-top: 7px solid var(--accent-primary);
}
/* dp-warp lives for one row crossing only (score.js _warpCursor): the frame
   the cursor changes rows slides instead of teleporting; every other frame
   has no transition, so normal movement stays instant. */
.dp-cursor.dp-warp {
    transition: left .14s cubic-bezier(.22, .61, .36, 1),
                top .14s cubic-bezier(.22, .61, .36, 1);
}
/* 즉각 타격 펄스(2026-08-22, score.js pulseCursor): CRNN 판정 링은 ~490ms
   뒤에 오므로 그 전에 "들었다"를 커서 글로우 확장으로 즉시 인정한다.
   판정색(초록/빨강)과 절대 겹치지 않게 커서 고유의 주황만 쓴다. */
.dp-cursor.dp-hit { animation: dpCursorHit .22s ease-out; }
@keyframes dpCursorHit {
    from { box-shadow: 0 0 0 7px rgba(249, 115, 22, .5), 0 0 20px rgba(249, 115, 22, .95); }
    to   { box-shadow: 0 0 0 0 rgba(249, 115, 22, 0), 0 0 7px rgba(249, 115, 22, .75); }
}
@media (prefers-reduced-motion: reduce) { .dp-cursor.dp-hit { animation: none; } }
.dp-cursor-bar {
    position: absolute; z-index: 1; border-radius: 3px;
    background: rgba(249, 115, 22, .09);
    transition: left .16s ease, top .16s ease, width .16s ease;
}

.dp-bar-tint { position: absolute; z-index: 0; border-radius: 3px; }
.dp-bar-tint.is-rest  { background: rgba(9, 9, 11, .05); }
.dp-bar-tint.is-muted { background: rgba(59, 130, 246, .07); }
.dp-bar-tint.is-tutor { background: rgba(139, 92, 246, .09); }
/* Right-aligned: the row's first bar already carries the measure number at
   its top-left, and the two collided. */
.dp-bar-tag {
    position: absolute; right: 5px; top: 0;
    font-family: 'Outfit', 'Pretendard Variable', sans-serif;
    font-size: 9.5px; font-weight: 700; font-style: normal;
    letter-spacing: .02em; color: #a1a1aa; white-space: nowrap;
}
.dp-bar-tint.is-tutor .dp-bar-tag { color: #8b5cf6; }
.dp-bar-tint.is-muted .dp-bar-tag { color: #3b82f6; }

/* Judgment rings — one per lesson note, positioned on its own notehead and
   only re-classed afterwards (scoring repaints ~11×/s). */
.dp-judge {
    position: absolute; z-index: 3;
    width: 16px; height: 16px; margin: -8px 0 0 -8px;
    border-radius: 50%; border: 1.8px solid transparent;
    display: none;
}
.dp-sheet-overlay.judging .dp-judge { display: block; }
.dp-judge.is-pending { border-color: rgba(113, 113, 122, .35); }
/* Not a verdict — the mic cannot hear this drum. Dashed so it never reads as
   the solid grey of 놓침. */
.dp-judge.is-unscored { border-color: rgba(113, 113, 122, .5); border-style: dashed; }
.dp-judge.is-perfect { border-color: #16a34a; background: rgba(22, 163, 74, .14); }
.dp-judge.is-good    { border-color: #65a30d; background: rgba(101, 163, 13, .14); }
.dp-judge.is-ok      { border-color: #d97706; background: rgba(217, 119, 6, .14); }
.dp-judge.is-wrong   { border-color: #dc2626; background: rgba(220, 38, 38, .16); }
.dp-judge.is-miss    { border-color: #a1a1aa; background: rgba(161, 161, 170, .16); }
/* is-awaiting — "the detector has not answered yet", NOT a verdict.
 * The CRNN path commits a frame ~490 ms after the stick hits it, which is longer
 * than WINDOWS.max (180 ms). Without this state the cursor would paint every
 * note grey (= 놓침) before the answer arrives and then repaint it green, and a
 * student reads a green-turns-red flip as their own mistake. Blue, slow, and
 * never counted: finishScoring converts any survivor to miss. */
.dp-judge.is-awaiting {
    border-color: rgba(59, 130, 246, .55);
    background: rgba(59, 130, 246, .10);
    animation: dpJudgeWait 1.1s ease-in-out infinite;
}
@keyframes dpJudgeWait {
    0%, 100% { opacity: .45; }
    50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .dp-judge.is-awaiting { animation: none; opacity: .7; } }
.dp-judge.flash { animation: dpJudgePop .24s ease-out; }
@keyframes dpJudgePop {
    from { transform: scale(1.85); opacity: .35; }
    to   { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .dp-judge.flash { animation: none; } }

/* 점수·등급 히어로(2026-08-12 — "100점 + S/A/B/C"): 판정 말풍선·결과 페이지
   공용. 등급 레터가 주인공, 점수는 mono 카운트업. */
.vd-score {
    display: flex; align-items: baseline; gap: 7px;
    margin: 0 0 7px; line-height: 1;
}
.vd-grade {
    font-size: 1.9rem; font-weight: 800; font-style: normal;
    -webkit-text-stroke: 0;
}
.vd-grade.g-S { color: #fbbf24; text-shadow: 0 0 14px rgba(251, 191, 36, .55); }
.vd-grade.g-A { color: #4ade80; }
.vd-grade.g-B { color: #60a5fa; }
.vd-grade.g-C { color: #a1a1aa; }
.vd-pts { font-size: 1.45rem; font-weight: 800; color: var(--text-primary); }
.vd-unit { font-size: .8rem; font-style: normal; color: var(--text-muted); }
.vd-score.vd-hero { margin-top: 12px; }
.vd-score.vd-hero .vd-grade { font-size: 2.6rem; }
.vd-score.vd-hero .vd-pts { font-size: 2rem; }

/* The lane grid named each limb down its left edge; on a staff that fact lives
   in vertical position, so it is restated once — drawn where it actually sits. */
/* Judgment key — only on the result screen, where the rings carry meaning. */
.judge-key { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 13px; }
.jk {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 11px; border-radius: var(--pill);
    background: rgba(255, 255, 255, .04); border: 1px solid var(--border-color);
    font-size: .74rem; font-weight: 600; color: var(--text-secondary); line-height: 1.5;
}
.jk i {
    width: 11px; height: 11px; border-radius: 50%; border: 1.8px solid currentColor;
    flex-shrink: 0;
}
.jk.is-perfect { color: #22c55e; } .jk.is-good { color: #84cc16; }
.jk.is-ok      { color: #f59e0b; } .jk.is-wrong { color: #ef4444; }
.jk.is-miss    { color: #a1a1aa; }
.jk.is-unscored { color: #71717a; }
.jk.is-unscored i { border-style: dashed; }

.dp-score-legend {
    display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
    margin-top: 13px;
}
.dp-leg {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 11px 3px 5px; border-radius: var(--pill);
    background: rgba(255, 255, 255, .04); border: 1px solid var(--border-color);
    font-size: .74rem; font-weight: 600; color: var(--text-secondary);
    line-height: 1;
}
.dp-leg-staff { width: 13px; height: 23px; overflow: visible; color: var(--leg); flex-shrink: 0; }
/* Photo of the real drum, between the staff position and the name — the chip
   then reads left to right as: where the note sits, what it is, what it is
   called. The images already fade to transparent, so no frame is needed. */
.dp-leg-pic {
    width: 30px; height: 30px; flex-shrink: 0; object-fit: cover;
    margin: -4px -2px -4px 1px;
}
.dp-leg-staff .ln line { stroke: rgba(255, 255, 255, .18); stroke-width: 1.4; }
.dp-leg-staff .hd path { stroke: var(--leg); stroke-width: 2.6; stroke-linecap: round; fill: none; }
.dp-leg-staff .hd ellipse { fill: var(--leg); }

.tempo-row {
    display: flex; align-items: center; gap: 16px;
    padding: 15px 22px; border-top: 1px solid var(--border-color);
}
.tempo-row label { font-size: .78rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .08em; }
.tempo-row input[type=range] { flex: 1; min-width: 90px; accent-color: var(--accent-primary); height: 4px; }
.tempo-val {
    font-family: 'JetBrains Mono', monospace; font-size: 1rem; font-weight: 700;
    color: var(--accent-tertiary); min-width: 116px; text-align: right;
}

.transport {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding: 18px 22px; border-top: 1px solid var(--border-color); background: var(--bg-secondary);
}
.transport .spacer { flex: 1; min-width: 8px; }
.transport .btn { padding-left: 20px; padding-right: 20px; }
/* Keep the toggles together so one of them never orphans onto its own line. */
.toggle-group { display: flex; gap: 8px; flex-wrap: nowrap; }

.toggle {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 15px; border-radius: var(--pill);
    border: 1px solid var(--border-color); background: var(--bg-card);
    font-size: .82rem; font-weight: 500; color: var(--text-secondary); transition: all .2s;
}
.toggle:hover { border-color: var(--border-hover); color: var(--text-primary); }
.toggle.on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-tertiary); }
.toggle .box {
    width: 15px; height: 15px; border-radius: 5px; flex-shrink: 0;
    border: 1.5px solid currentColor; display: flex; align-items: center; justify-content: center;
    font-size: 9px; line-height: 1;
}

/* Handedness. A segmented pair, not two independent toggles: exactly one of
   the two is always true, and a checkbox pair would allow "neither". Shown
   only on lessons that print R/L letters (app.js handPicker). */
.hand-pick {
    display: inline-flex; align-items: center; flex-wrap: nowrap;
    border: 1px solid var(--border-color); border-radius: var(--pill);
    background: var(--bg-card); overflow: hidden;
}
.hand-pick-cap {
    padding: 0 10px 0 14px; font-size: .72rem; font-weight: 600;
    letter-spacing: .04em; color: var(--text-muted); white-space: nowrap;
}
.hand-opt {
    padding: 8px 14px; border: 0; background: transparent;
    font-size: .82rem; font-weight: 500; color: var(--text-secondary);
    transition: all .2s; white-space: nowrap;
}
.hand-opt:hover { color: var(--text-primary); }
.hand-opt.on { background: var(--accent-soft); color: var(--accent-tertiary); font-weight: 600; }

.actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
    margin-top: 18px; padding: 20px 24px;
    background: var(--bg-card); border: 1px solid var(--border-color); border-radius: var(--radius-xl);
}

/* Aside */
.panel {
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-xl); padding: 24px; margin-bottom: 16px;
}
.panel h4 {
    display: flex; align-items: center; gap: 9px; margin-bottom: 15px;
    font-size: .72rem; font-weight: 700; letter-spacing: .1em;
    text-transform: uppercase; color: var(--text-muted);
}
.panel h4 .bar { width: 3px; height: 13px; border-radius: 2px; background: var(--accent-primary); }
.panel h4.purple .bar { background: var(--purple); }
.panel h4.yellow .bar { background: var(--yellow); }
.panel h4.blue .bar { background: var(--blue); }
.panel p { font-size: .9rem; color: var(--text-secondary); line-height: 1.78; }
.panel .src { font-size: .84rem; color: var(--text-muted); }
.panel ul { list-style: none; display: flex; flex-direction: column; gap: 11px; }
.panel li { display: flex; gap: 10px; font-size: .88rem; color: var(--text-secondary); line-height: 1.65; }
.panel li::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--yellow); flex-shrink: 0; margin-top: 8px; }

.quote {
    padding: 14px 16px; border-left: 2px solid var(--accent-primary);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: var(--bg-secondary); font-size: .9rem; color: var(--text-secondary);
    font-style: italic; line-height: 1.7;
}

.crit-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.crit {
    padding: 13px 10px; text-align: center;
    background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-md);
}
.crit b { display: block; font-family: 'JetBrains Mono', monospace; font-size: 1.05rem; font-weight: 700; }
.crit span { font-size: .68rem; color: var(--text-muted); }

/* ============================================================
   Tutor
   ============================================================ */
.tutor-say { display: flex; align-items: flex-start; gap: 13px; }
.tutor-say.pop .tutor-bubble { animation: bubbleIn .34s cubic-bezier(.34, 1.4, .64, 1); }
@keyframes bubbleIn { from { opacity: 0; transform: translateY(6px) scale(.97); } to { opacity: 1; transform: none; } }

.tutor-face {
    width: 52px; height: 52px; border-radius: 50%; flex-shrink: 0;
    object-fit: cover; object-position: 50% 22%;
    border: 1.5px solid var(--border-hover);
    background: var(--bg-elevated);
    box-shadow: 0 6px 18px -6px rgba(0, 0, 0, .8);
}
.tutor-say.compact .tutor-face { width: 38px; height: 38px; }

.tutor-bubble {
    /* Hug the text — flex:1 made it stretch across wide cards and read as an
       empty input field. */
    position: relative; flex: 0 1 auto; min-width: 0; max-width: 560px;
    padding: 13px 16px; border-radius: 4px 14px 14px 14px;
    background: var(--bg-elevated); border: 1px solid var(--border-color);
}
.tutor-bubble::before {
    content: ''; position: absolute; left: -6px; top: 12px;
    width: 10px; height: 10px; transform: rotate(45deg);
    background: var(--bg-elevated);
    border-left: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
}
.tutor-bubble p { font-size: .89rem; color: var(--text-secondary); line-height: 1.7; }
.tutor-name {
    display: block; font-size: .68rem; font-weight: 700; letter-spacing: .1em;
    text-transform: uppercase; color: var(--accent-tertiary); margin-bottom: 5px;
}

/* Result screen — the tutor delivers the diagnosis in person. */
.verdict-band {
    display: grid; grid-template-columns: 196px minmax(0, 1fr); gap: 28px; align-items: center;
}
.verdict-portrait { position: relative; line-height: 0; }
.verdict-portrait img {
    /* height:auto is load-bearing — the <img height> attribute would otherwise
       become the CSS height and stretch the portrait. */
    width: 100%; height: auto; display: block;
    border-radius: var(--radius-xl);
    border: 1px solid var(--border-hover);
    box-shadow: 0 24px 60px -22px rgba(0, 0, 0, .85);
}
.verdict-portrait::after {
    content: ''; position: absolute; inset: 0; border-radius: var(--radius-xl); pointer-events: none;
    background: linear-gradient(180deg, transparent 55%, rgba(24, 24, 27, .75) 100%);
}

/* ============================================================
   Emblems & fingerprints
   ============================================================ */
.emblem { display: block; }
.fingerprint { display: block; flex-shrink: 0; }
.fingerprint.muted { opacity: .72; }

.fp-holder {
    width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, .035);
    border: 1px solid var(--border-color);
    transition: border-color .2s, background .2s;
}
.lesson-row:hover .fp-holder { border-color: var(--border-hover); background: rgba(255, 255, 255, .06); }
.fp-holder.lg { width: 56px; height: 56px; }

.module-badge .emblem { width: 22px; height: 22px; }

/* ============================================================
   Notices
   ============================================================ */
.notice {
    display: flex; gap: 14px; padding: 18px 20px;
    border-radius: var(--radius-lg); border: 1px solid var(--border-color);
    background: var(--bg-secondary); font-size: .89rem; color: var(--text-secondary); line-height: 1.7;
}
.notice b { color: var(--text-primary); font-weight: 600; }
.notice a { color: var(--accent-tertiary); text-decoration: underline; text-underline-offset: 2px; }
.notice .ic {
    width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
}
.notice .ic svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.notice.warn { border-color: rgba(245, 158, 11, .26); background: rgba(245, 158, 11, .06); }
.notice.warn .ic { background: rgba(245, 158, 11, .14); color: var(--yellow); }
.notice.info { border-color: var(--blue-line); background: var(--blue-soft); }
.notice.info .ic { background: rgba(59, 130, 246, .14); color: var(--blue-light); }
.notice.plain .ic { background: rgba(255, 255, 255, .06); color: var(--text-muted); }
.notice.purple { border-color: var(--purple-line); background: var(--purple-soft); }
.notice.purple .ic { background: rgba(139, 92, 246, .16); color: var(--purple-light); }

/* ============================================================
   Modal
   ============================================================ */
.modal-scrim {
    position: fixed; inset: 0; z-index: 2000;
    background: rgba(9, 9, 11, .78);
    backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
    display: flex; align-items: center; justify-content: center; padding: 22px;
    opacity: 0; pointer-events: none; transition: opacity .22s ease;
}
.modal-scrim.on { opacity: 1; pointer-events: auto; }

.modal {
    width: 100%; max-width: 560px; max-height: 88vh;
    display: flex; flex-direction: column;
    background: var(--bg-card); border: 1px solid var(--border-hover);
    border-radius: var(--radius-2xl);
    box-shadow: 0 40px 100px -20px rgba(0, 0, 0, .8), 0 0 0 1px rgba(255, 255, 255, .03);
    transform: translateY(14px) scale(.97);
    transition: transform .28s cubic-bezier(.34, 1.4, .64, 1);
}
.modal-scrim.on .modal { transform: none; }
.modal.wide { max-width: 640px; }

.modal-head {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 26px 26px 0;
}
.modal-head .mic {
    width: 44px; height: 44px; border-radius: var(--radius-md); flex-shrink: 0;
    background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 8px 22px var(--accent-glow);
}
.modal-head .mic svg { width: 22px; height: 22px; stroke: #fff; fill: none; stroke-width: 1.7; }
.modal-head h3 { font-size: 1.2rem; font-weight: 700; letter-spacing: -.025em; }
.modal-head p { color: var(--text-secondary); font-size: .9rem; margin-top: 6px; line-height: 1.7; }
.modal-close {
    margin-left: auto; width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--text-muted); transition: all .18s;
}
.modal-close:hover { background: rgba(255, 255, 255, .07); color: var(--text-primary); }

.modal-body { padding: 20px 26px; overflow-y: auto; display: flex; flex-direction: column; gap: 13px; }
.modal-foot {
    display: flex; gap: 11px; flex-wrap: wrap; justify-content: flex-end;
    padding: 18px 26px 26px; border-top: 1px solid var(--border-color); margin-top: 4px;
}
.modal-foot .left { margin-right: auto; }

/* ---- Count-in ----
   Sits on the score, not in a modal: during the count the student needs to see
   the bar they are about to play. Used by 따라 치기, 시범 듣기 and 채점 alike. */
.countin {
    position: absolute; inset: 0; z-index: 6;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px; border-radius: var(--radius-lg);
    background: radial-gradient(ellipse 70% 70% at 50% 45%, rgba(9, 9, 11, .84), rgba(9, 9, 11, .62));
    backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
    pointer-events: none;
}
.countin[hidden] { display: none; }
.countin-n {
    font-weight: 800; font-size: clamp(3.4rem, 13vw, 6rem); line-height: 1;
    letter-spacing: -.06em;
    background: linear-gradient(135deg, var(--accent-tertiary), var(--accent-primary));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.countin-dots { display: flex; gap: 8px; }
.countin-dots i {
    width: 9px; height: 9px; border-radius: 50%;
    background: rgba(255, 255, 255, .16); transition: background .12s, box-shadow .12s;
}
.countin-dots i.on {
    background: var(--accent-primary); box-shadow: 0 0 10px var(--accent-glow-strong);
}
.countin-cap {
    font-size: .82rem; font-weight: 600; letter-spacing: .04em; color: var(--text-secondary);
}
.countin.last .countin-cap { color: var(--accent-tertiary); }
/* One pulse per beat — the class is re-added each time so it retriggers. */
.countin.tick .countin-n { animation: countPop .34s cubic-bezier(.2, .8, .3, 1); }
@keyframes countPop {
    from { transform: scale(1.28); opacity: .45; }
    to   { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .countin.tick .countin-n { animation: none; } }

.level {
    height: 10px; border-radius: var(--pill); overflow: hidden;
    background: rgba(255, 255, 255, .07);
}
.level i {
    display: block; height: 100%; width: 0;
    background: linear-gradient(90deg, var(--green), var(--yellow) 72%, var(--red));
    transition: width .07s linear;
}
.hit-dots { display: flex; gap: 8px; justify-content: center; }
.hit-dots i {
    width: 11px; height: 11px; border-radius: 50%;
    background: rgba(255, 255, 255, .1); transition: all .2s;
}
.hit-dots i.on { background: var(--green); box-shadow: 0 0 12px rgba(34, 197, 94, .6); transform: scale(1.15); }

/* ============================================================
   Toast
   ============================================================ */
.toast-stack {
    position: fixed; left: 50%; bottom: calc(28px + var(--sab)); z-index: 3000;
    transform: translateX(-50%);
    display: flex; flex-direction: column; gap: 10px; align-items: center;
    pointer-events: none;
}
.toast {
    display: flex; align-items: center; gap: 11px;
    padding: 12px 22px; border-radius: var(--pill);
    background: var(--bg-elevated); border: 1px solid var(--border-hover);
    box-shadow: 0 18px 44px -12px rgba(0, 0, 0, .7);
    font-size: .88rem; font-weight: 500; color: var(--text-primary);
    animation: toastIn .34s cubic-bezier(.34, 1.4, .64, 1);
    max-width: 90vw;
}
.toast.out { animation: toastOut .28s ease forwards; }
.toast .tic {
    width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: .68rem; background: var(--green); color: #06240f; font-weight: 800;
}
.toast.orange .tic { background: var(--accent-primary); color: #fff; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes toastOut { to { opacity: 0; transform: translateY(10px); } }

/* ============================================================
   Result
   ============================================================ */
.axis-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.axis {
    position: relative; overflow: hidden;
    padding: 28px; background: var(--bg-card);
    border: 1px solid var(--border-color); border-radius: var(--radius-xl);
}
.axis::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--text-faint); }
.axis.pass::before { background: linear-gradient(90deg, var(--green), var(--green-light)); }
.axis.fail::before { background: linear-gradient(90deg, var(--yellow), #fbbf24); }
.axis .label { font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted); }
.axis .value {
    font-family: 'JetBrains Mono', monospace; font-size: 2.5rem; font-weight: 800;
    letter-spacing: -.04em; line-height: 1.1; margin-top: 10px;
}
.axis.pass .value { color: var(--green); }
.axis.fail .value { color: var(--yellow); }
.axis .sub { font-size: .8rem; color: var(--text-muted); margin-top: 4px; }
.axis .verdict {
    display: inline-flex; align-items: center; gap: 6px; margin-top: 14px;
    padding: 4px 12px; border-radius: var(--pill); font-size: .74rem; font-weight: 700;
}
.axis.pass .verdict { background: var(--green-soft); border: 1px solid var(--green-line); color: var(--green-light); }
.axis.fail .verdict { background: rgba(245, 158, 11, .1); border: 1px solid rgba(245, 158, 11, .26); color: var(--yellow); }

canvas.hist { display: block; width: 100%; height: 130px; }

.rx {
    position: relative; overflow: hidden;
    padding: 30px; border-radius: var(--radius-2xl);
    background: linear-gradient(135deg, rgba(249, 115, 22, .11), rgba(249, 115, 22, .02) 62%);
    border: 1px solid var(--accent-line);
    box-shadow: 0 0 70px -30px var(--accent-glow);
}
.rx h3 { font-size: 1.28rem; font-weight: 700; letter-spacing: -.025em; margin-top: 14px; }
.rx p { color: var(--text-secondary); font-size: .94rem; margin-top: 10px; line-height: 1.75; max-width: 640px; }
.rx .cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }

/* ============================================================
   Footer
   ============================================================ */
footer.dp-foot { border-top: 1px solid var(--border-color); padding: 44px 24px 40px; margin-top: 70px; }
.foot-inner {
    max-width: 1180px; margin: 0 auto;
    display: flex; flex-wrap: wrap; align-items: center; gap: 18px;
    color: var(--text-muted); font-size: .84rem;
}
.foot-inner .brand { display: flex; align-items: center; gap: 9px; }
.foot-inner .brand svg { width: 22px; height: 22px; }
.foot-links { margin-left: auto; display: flex; flex-wrap: wrap; gap: 24px; }
.foot-links a { transition: color .2s; }
.foot-links a:hover { color: var(--text-secondary); }

/* ============================================================
   Boot / fade-in
   ============================================================ */
.boot { padding: 120px 24px; text-align: center; color: var(--text-muted); }
.spinner {
    width: 34px; height: 34px; margin: 0 auto 18px; border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, .08); border-top-color: var(--accent-primary);
    animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.fade-in { opacity: 1; transform: none; }
html.js .fade-in {
    opacity: 0; transform: translateY(22px);
    transition: opacity .65s cubic-bezier(.4, 0, .2, 1), transform .65s cubic-bezier(.4, 0, .2, 1);
}
html.js .fade-in.visible { opacity: 1; transform: none; }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1080px) {
    /* minmax(0,…) — 그냥 1fr 이면 트랙의 자동 최소치가 min-content 라 카드가
       뷰포트보다 넓어진다(8/09: 390px 에서 stage 395 / wrap 375, 가로 스크롤).
       데스크톱 선언(css:627)이 이미 같은 이유로 minmax(0,1fr) 을 쓰고 있다. */
    .lesson-grid { grid-template-columns: minmax(0, 1fr); }
    .step-grid { grid-template-columns: repeat(2, 1fr); }
    .hero-split { grid-template-columns: 1fr; gap: 38px; }
}
@media (max-width: 860px) {
    .tile-grid { grid-template-columns: 1fr; }
    .resume, .bridge { grid-template-columns: 1fr; }
    .axis-grid { grid-template-columns: 1fr; }
    .verdict-band { grid-template-columns: 112px minmax(0, 1fr); gap: 18px; }
}
@media (max-width: 720px) {
    nav.dp-nav { padding-left: 16px; padding-right: 16px; gap: 8px; }
    .nav-center { display: none; }
    .nav-account { display: none; }
    .dp-logo-word { font-size: 1.05rem; }
    .wrap, .hero-inner { padding: 0 16px; }
    .hero { padding: 56px 0 44px; }
    .stat-grid { grid-template-columns: repeat(2, 1fr); gap: 18px; margin-top: 40px; }
    .stat b { font-size: 1.9rem; }
    .step-grid { grid-template-columns: 1fr; }
    .lesson-row .lid { display: none; }
    .hero-cta { flex-direction: column; align-items: stretch; }
    .hero-cta .btn { width: 100%; }
    /* The old 16:9 reel was hidden on phones; the portrait 10차컷 demo IS
       phone-native content, so it stays — just a notch narrower. */
    .hero-visual .reel--tall { width: min(74vw, 264px); }
    .toggle-group { flex-wrap: wrap; }
    .resume, .bridge { padding: 24px; }
    .foot-links { margin-left: 0; gap: 16px; }
    .modal { max-height: 92vh; }
    .modal-head, .modal-body, .modal-foot { padding-left: 20px; padding-right: 20px; }
}
@media (max-width: 460px) {
    .crit-grid { grid-template-columns: 1fr; }
    .tile { padding: 26px 22px; }
    .transport { padding: 16px; }
    .modal-foot { flex-direction: column-reverse; }
    .modal-foot .btn { width: 100%; }
    .modal-foot .left { margin-right: 0; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
    html.js .fade-in { opacity: 1; transform: none; }
}

/* ============================================================
   Lesson flow — 시범 → 연습 → 도전 (dp-flow.js). Pills and accents
   follow the landing signature: 100px radii, orange accent family.
   ============================================================ */
.flow-strip { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    padding: 10px 16px 2px; }
.flow-rail { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.flow-rail .fstep { display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid var(--border-color); background: var(--bg-card);
    color: var(--text-muted); border-radius: 100px; padding: 5px 13px;
    font-size: .78rem; font-weight: 700; }
.flow-rail .fstep .n { width: 16px; height: 16px; border-radius: 100px;
    background: var(--bg-raised); display: inline-flex; align-items: center;
    justify-content: center; font-size: .64rem; color: var(--text-muted); }
.flow-rail .fstep.on { background: var(--accent-soft);
    border-color: var(--accent-glow-strong); color: var(--accent-tertiary); }
.flow-rail .fstep.on .n { background: var(--accent-primary); color: #0b0b0d; }
.flow-rail .fsep { color: var(--text-muted); font-size: .78rem; }
.flow-count { color: var(--accent-secondary); font-weight: 800;
    font-size: .85rem; min-height: 1.2em; }
.flow-bubble { display: flex; gap: 11px; align-items: flex-start;
    margin: 12px 16px 0; background: var(--bg-card);
    border: 1px solid var(--accent-line); border-left: 3px solid var(--accent-primary);
    border-radius: 14px; padding: 12px 15px; line-height: 1.65; font-size: .92rem; }
.flow-bubble[hidden] { display: none; }
.flow-bubble .fb-ic { color: var(--accent-primary); flex: none; margin-top: 2px; }
.flow-ctl { display: flex; gap: 8px; flex-wrap: wrap; padding: 12px 16px 0; }
.flow-ctl:empty { display: none; }
.flow-chat { display: grid; gap: 9px; margin: 12px 16px 0;
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: 14px; padding: 12px 14px; }
.flow-chat[hidden] { display: none; }
.flow-chat .fc-log { display: grid; gap: 6px; max-height: 190px;
    overflow-y: auto; font-size: .86rem; }
.flow-chat .fc-log:empty { display: none; }
.flow-chat .fc-q { justify-self: end; background: var(--accent-soft);
    border: 1px solid var(--accent-line); border-radius: 13px 13px 4px 13px;
    padding: 6px 12px; max-width: 85%; }
.flow-chat .fc-a { justify-self: start; background: var(--bg-secondary);
    border: 1px solid var(--border-color); border-radius: 13px 13px 13px 4px;
    padding: 6px 12px; max-width: 85%; color: var(--text-secondary); }
.flow-chat .fc-row { display: flex; gap: 8px; }
.flow-chat .fc-row input { flex: 1; background: var(--bg-secondary);
    color: var(--text-primary); border: 1px solid var(--border-color);
    border-radius: 100px; padding: 9px 15px; font: inherit; font-size: .86rem; }
.flow-chat .fc-note { margin: 0; font-size: .74rem; color: var(--text-muted); }

/* 3-D demonstrator panel — visible during the 시범 stage only. A capped
   height band, never aspect-ratio:1/1 (on a phone that made the 3-D the
   biggest thing on screen — the opposite of the score-first priority). */
.stage3d { margin: 10px 16px 0; height: min(36vh, 320px); background: #0d0d12;
    border: 1px solid var(--border-color); border-radius: 14px; overflow: hidden; }
.stage3d[hidden] { display: none; }
.stage3d canvas { display: block; width: 100%; height: 100%; }

/* ============================================================
   Guided-flow fullscreen player — QA 2026-08-09.
   단계=한 화면(스크롤 0), 전환은 큰 버튼 하나. The dp-flow machine is
   untouched: app.js flips body.flow-* classes from the snapshot and this
   block turns the .stage card into a viewport-filling player. The hero,
   강사 노트, 흔한 실수 and the aside stay in normal document flow behind
   it and come back through the ✕ (free-practice) path.
   ============================================================ */
/* ✕ 는 플레이어가 떠 있을 때만 의미가 있다 — 흐름 플레이어(flow-on)와 설명
   플레이어(guide-on) 둘 다. 하나만 적으면 설명 레슨에서 나갈 길이 사라진다. */
body:not(.flow-on):not(.guide-on) #flow-exit { display: none; }

body.flow-on { overflow: hidden; }
body.flow-on .stage {
    /* above the site nav (1000) — the player owns the screen; below the
       modal layer (2000) so the mic dialogs still land on top */
    position: fixed; inset: 0; z-index: 1500;
    display: flex; flex-direction: column;
    border: 0; border-radius: 0; box-shadow: none;
    background: var(--bg-primary);
}
body.flow-on .stage-bar,
body.flow-on .flow-strip,
body.flow-on .lesson-ident { flex: none; }

/* 플레이어에서는 신호등 점을 접고 그 폭을 레슨 이름에 넘긴다 — 390px 에서
   긴 제목이 살아남는 유일한 방법이다. 시범 화면의 소개 블록은 flex:1 인 3-D
   에서 높이를 빌리므로 스크롤 예산에는 손대지 않는다. */
body.flow-on .stage-dot { display: none; }
body.flow-on .lesson-dd { margin-left: 0; }
/* .stage 가 inset:0 이라 설치형 PWA 에서는 바가 노치 밑으로 들어간다. */
body.flow-on .stage-bar { padding-top: calc(12px + var(--sat, 0px)); }

/* 폰 — 페이저까지 접어 제목에 넘긴다(8/09 실측: 제목 폭 91 → 226px, 28자짜리
   가장 긴 제목도 전문이 보인다). 재생 중에는 커서가 페이지를 스스로 넘기므로
   잃는 것은 "대기 화면에서 2쪽을 미리 넘겨 보기" 하나뿐이다.
   updatePageLabel 은 인라인 display 만 건드리므로(app.js) 이 규칙과 충돌하지 않는다. */
@media (max-width: 560px) {
    body.flow-on #pager-group { display: none; }
}

/* footer order: tempo / transport / THE button at the very bottom edge */
body.flow-on .stage > * { order: 0; }
body.flow-on .tempo-row { order: 1; }
body.flow-on .transport { order: 2; }
body.flow-on .flow-ctl  { order: 3; }

/* middle — whichever of 3-D / score is on duty fills what is left */
body.flow-on .stage3d { flex: 1 1 auto; min-height: 0; height: auto; margin: 10px 14px; }
body.flow-on .score-pad {
    flex: 1 1 auto; min-height: 0; overflow: auto;
    display: flex; flex-direction: column; justify-content: center;
    width: 100%; max-width: 880px; margin: 0 auto;
    padding: 6px 14px 6px;   /* the card's 18px eats the phone's height budget */
}

/* 시범 화면 — the 3-D demonstrator is the protagonist (설계 2026-08-05 §5:
   폰에서 악보+3D 동시 배치 불가). If the 3-D never mounted (stage3d stays
   [hidden]) the score keeps the room instead of leaving a hole. */
body.flow-demo:has(.stage3d:not([hidden])) .score-pad { display: none; }
body.flow-demo .tempo-row, body.flow-demo .transport { display: none; }

/* 대기 화면 — the big button IS the transport; hide the redundant row.
   The tempo slider stays: slowing down before 연습 시작 is a real need. */
body.flow-wait .transport { display: none; }

/* 도전 화면 — mid-take the tempo cannot change anyway */
body.flow-challenge .tempo-row { display: none; }

/* 판정 화면 (③) — rings on the sheet + the tutor's words, one viewport */
body.flow-verdict .tempo-row, body.flow-verdict .transport { display: none; }
/* 판정 패드는 도전보다 몇 px 얕다(점수 말풍선이 큼) — 도전 폭 재렌더로
   +4px 커진 행높이가 여기서만 넘친다(flow_e2e 실측 353 vs 349). 시트 상하
   패딩을 2px 씩 줄여 흡수 — JS 재레이아웃 없이 결정론적으로 맞춘다. */
body.flow-verdict .dp-sheet-viewport { padding-top: 6px; padding-bottom: 6px; }
body.flow-on .flow-bubble {
    flex: none; overflow: auto; max-height: 28vh;
    width: calc(100% - 28px); max-width: 880px; margin: 10px auto 0;
}

/* footer rows share the score's column on wide screens */
body.flow-on .tempo-row, body.flow-on .transport {
    flex: none; width: 100%; max-width: 880px;
    margin-left: auto; margin-right: auto;
}
body.flow-on .flow-ctl {
    flex: none; width: 100%; max-width: 880px; margin: 0 auto;
    padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px));
}
body.flow-on .flow-ctl .btn.btn-primary {
    flex: 1 1 100%; justify-content: center;
    padding: 15px 22px; font-size: 1.02rem;
}
body.flow-on .flow-ctl .btn.btn-secondary,
body.flow-on .flow-ctl .btn.btn-ghost { flex: 1 1 auto; justify-content: center; }

/* chat rides as a sheet above the footer instead of stretching the column */
body.flow-on .flow-chat {
    position: absolute; left: 12px; right: 12px; z-index: 6;
    bottom: calc(86px + env(safe-area-inset-bottom, 0px));
    margin: 0 auto; padding: 12px; max-width: 560px;
    background: var(--bg-card); border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .7);
}

/* ============================================================
   설명 레슨 플레이어 (module S — 시작하기, 2026-08-10)
   스틱 그립·킷 명칭·음표·악보를 튜터가 카드로 넘기며 설명한다. 흐름 기계
   (dp-flow)는 관여하지 않는다: 여기엔 시범도 채점도 없고 body.guide-on 하나로
   .stage 카드를 풀스크린 플레이어로 바꾼다 — 구조는 flow 플레이어와 같은 규칙
   (fixed inset:0 · z-index 1500 · 세로 flex · 맨 아래가 큰 버튼)을 따른다.
   ============================================================ */
body.guide-on { overflow: hidden; }
body.guide-on .stage.guide {
    position: fixed; inset: 0; z-index: 1500;
    display: flex; flex-direction: column;
    border: 0; border-radius: 0; box-shadow: none;
    background: var(--bg-primary);
}
body.guide-on .stage-bar { flex: none; padding-top: calc(12px + var(--sat, 0px)); }
body.guide-on .stage-dot { display: none; }
body.guide-on .lesson-dd { margin-left: 0; }

/* 카드 진행 막대 — 몇 장 남았는지가 보여야 끝까지 넘긴다. */
.guide-prog { flex: none; height: 3px; background: rgba(255, 255, 255, .07); }
.guide-prog i {
    display: block; height: 100%; width: 0;
    background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
    transition: width .3s cubic-bezier(.4, 0, .2, 1);
}

/* 무대 — 한 화면의 주인공은 하나. 그림도 악보도 없는 카드에서는 튜터가
   여기를 통째로 차지한다(사용자 2026-08-10: "캐릭터가 화면을 다 채우면서"). */
.guide-hero {
    flex: 1 1 auto; min-height: 0; position: relative;
    display: flex; align-items: center; justify-content: center;
    padding: 10px 16px 0; overflow: hidden;
}
.guide-hero .guide-art { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 16px; }
.guide-hero .guide-art.kit { filter: drop-shadow(0 20px 40px rgba(0, 0, 0, .55)); }
/* 도해 상자는 SVG 와 **같은 비율**이어야 한다. 비율이 어긋나면 SVG 가 스스로
   letterbox 를 만들어 그림이 상자 한가운데에서 작아진다(8/10: 폰에서 358x583
   상자에 358x295 만 그려지고 나머지가 검은 여백이었다).
   폭을 먼저 채우고 높이는 비율로 따라오게 두되, 무대보다 높아지면 max-height 가
   되받아 폭까지 함께 줄인다 — 그래서 어느 화면에서도 letterbox 가 0 이다. */
.guide-hero .guide-diagram {
    /* 상자는 무대를 그대로 채우고, 안의 사진·SVG 가 스스로 비율을 지킨다.
       상자 비율을 맞추려 들면 폰/데스크톱 중 한쪽에서 반드시 어긋난다. */
    width: 100%; height: 100%;
}
/* 세로가 긴 화면에서는 사진을 조금 더 키운다. img 는 object-fit:cover, SVG 는
   preserveAspectRatio="slice" 라 둘이 **같은 방식으로** 잘려 좌표가 어긋나지 않는다. */


.guide-hero .guide-score { width: 100%; max-width: 620px; }
/* 반신은 아래로 붙여 얼굴이 위쪽에 오게 하고, 바닥은 배경으로 페이드시켜
   잘린 느낌 대신 화면에 서 있는 느낌을 준다. */
.guide-hero.bust { padding: 0; align-items: stretch; justify-content: center; }
.guide-hero.bust .guide-bust {
    /* 🔴 object-fit:cover 로 폭을 채우지 말 것. 무대는 폰에서만 세로로 길다 —
       데스크톱에서는 가로 2.1:1 이라 cover 가 이미지 세로의 38%만 남기고 머리와
       눈만 보인다(8/10 프로덕션 실측: 1440x900 에서 visibleFrac 0.38).
       무대 **높이**에 맞추고 폭은 따라오게 두면, 폰에서는 폭이 넘쳐 화면을 채우고
       (넘친 만큼만 overflow:hidden 이 자름) 데스크톱에서는 전신이 가운데 선다.
       원본이 416x520 뿐이라 폭으로 늘리면 흐려지는 문제도 같이 없어진다. */
    height: 100%; width: auto; max-width: none; object-fit: contain;
    -webkit-mask-image: linear-gradient(180deg, #000 72%, transparent 99%);
    mask-image: linear-gradient(180deg, #000 72%, transparent 99%);
}

/* ---- 설명 사진 (glyphs.js kitTop / stickGrip) ----
   위에서 본 킷은 **실사 사진**이고, 그 위에 스포트라이트를 얹어 지금 말하는 악기
   하나만 밝힌다(사용자 2026-08-10: 벡터 도해는 "허접하다" 판정). 사진은 원본
   1920x1280 이고 SVG viewBox 가 그 픽셀을 그대로 쓰므로, 좌표는 glyphs.js 의
   실측표 하나만 고치면 된다. */
.kitshot { position: relative; width: 100%; height: 100%; border-radius: 16px; overflow: hidden; }
/* contain — 사진과 SVG 가 **같은 방식으로** 맞춰져야 좌표가 어긋나지 않는다.
   cover 로 자르면 데스크톱(가로 2.15:1)에서 라이드와 스네어가 프레임 밖으로 나갔다. */
.kitshot img { width: 100%; height: 100%; object-fit: contain; display: block; }
.kitshot svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.kitshot .ks-dim { fill: rgba(6, 6, 8, .82); transition: opacity .3s; }
.kitshot .ks-ring {
    fill: none; stroke: var(--accent-primary); stroke-width: 7;
    filter: drop-shadow(0 0 18px rgba(249, 115, 22, .85));
}
.kitshot .ks-dot { fill: none; stroke: rgba(255, 255, 255, .34); stroke-width: 4; stroke-dasharray: 14 12; }
.kitshot .ks-lab {
    /* viewBox 가 1920 폭이라 폰에서 5.4배 축소된다 — 라벨은 그만큼 크게 그려야
       읽힌다(46px -> 폰에서 8.6px 였다). */
    fill: #fafafa; font-size: 76px; font-weight: 800; text-anchor: middle;
    paint-order: stroke; stroke: rgba(0, 0, 0, .8); stroke-width: 11; stroke-linejoin: round;
}
.kitshot .ks-lab.on { fill: var(--accent-tertiary); font-size: 104px; }

.stickmap { width: 100%; height: 100%; display: block; }
.stickmap .sk-stick rect, .stickmap .sk-stick path, .stickmap .sk-stick circle {
    fill: #d6b98f; stroke: #8a6f4c; stroke-width: 1.5;
}
.stickmap .sk-mark {
    fill: var(--accent-soft); stroke: var(--accent-primary);
    stroke-width: 2.5; stroke-dasharray: 5 4;
}
.stickmap .sk-dim path { stroke: var(--text-muted); stroke-width: 1.5; fill: none; }
.stickmap .sk-dim text { fill: var(--text-muted); font-size: 13px; font-weight: 700; text-anchor: middle; }
.stickmap .sk-lab { fill: var(--text-muted); font-size: 13px; font-weight: 700; text-anchor: middle; }
.stickmap .sk-lab.on { fill: var(--accent-tertiary); font-size: 14px; }

/* 말 — 카드 제목과 본문. 무대에 그림이 있을 때만 작은 아바타가 붙는다. */
.guide-say {
    flex: none; display: flex; gap: 12px; align-items: flex-start;
    width: 100%; max-width: 620px; margin: 0 auto;
    padding: 12px 16px 0; overflow: auto; max-height: 46vh;
}
.guide-say .guide-avatar {
    width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
    object-fit: cover; border: 1px solid var(--accent-line);
}
.guide-say .gs-text { min-width: 0; }
.guide-say h2 {
    font-size: 1.16rem; font-weight: 800; line-height: 1.3; margin: 0 0 5px;
    letter-spacing: -.02em;
}
.guide-say p { margin: 0; font-size: .94rem; line-height: 1.68; color: var(--text-secondary); }

body.guide-on .flow-ctl {
    flex: none; width: 100%; max-width: 620px; margin: 0 auto;
    padding: 12px 16px calc(14px + env(safe-area-inset-bottom, 0px));
}
body.guide-on .flow-ctl .btn.btn-primary {
    flex: 1 1 60%; justify-content: center; padding: 15px 22px; font-size: 1.02rem;
}
body.guide-on .flow-ctl .btn.btn-secondary { flex: 1 1 auto; justify-content: center; }

/* 넓은 화면에서는 그림과 말이 나란히 — 세로로만 쌓으면 데스크톱에서 텅 빈다. */
@media (min-width: 900px) and (orientation: landscape) {
    body.guide-on .stage.guide { display: grid; grid-template-rows: auto auto 1fr auto; }
    .guide-say { max-height: none; padding-bottom: 4px; }
    .guide-hero.bust .guide-bust { max-height: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .dp-cursor.dp-warp, .dp-cursor-bar { transition: none; }
    .guide-prog i { transition: none; }
}

/* ---- 시범 카메라 선택 칩 (2026-08-10, rig VIEWS 교습 3종) ---- */
.flow-views { display: flex; gap: 8px; justify-content: center; width: 100%; margin: 0 0 8px; }
.flow-views .vchip {
    padding: 7px 14px; border-radius: 999px; font-size: .82rem; cursor: pointer;
    border: 1px solid #3a3a44; background: #1b1b22; color: #c8c8d0;
}
.flow-views .vchip.on { border-color: #f97316; color: #fff; background: #2a2118; }

/* ============================================================
   시범 컷신 대화창 — 강사 초상+타자 자막 (2026-08-11 사용자 지시,
   "플스/닌텐도 자막과 얼굴처럼"). 3-D 무대 하단에 얹힌다.
   ============================================================ */
.stage3d { position: relative; }
/* 시범 시작 로딩 오버레이(2026-08-12 — "로딩중을 먼저 띄우고 재생"). 컷신과
   같은 문법: 연희 초상 + 스피너. 캐시 히트면 app.js 가 아예 안 띄운다. */
.stage-loading {
    position: absolute; inset: 0; z-index: 6;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px; background: #0d0d12;
}
.stage-loading[hidden] { display: none; }
.sl-face {
    width: 72px; height: 72px; border-radius: 14px;
    object-fit: cover; object-position: top; background: #17181d;
    border: 2px solid rgba(255, 255, 255, .22);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
}
.sl-spin {
    width: 22px; height: 22px; border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, .18); border-top-color: var(--accent-primary, #f97316);
    animation: sl-rot .8s linear infinite;
}
@keyframes sl-rot { to { transform: rotate(360deg); } }
.sl-text { margin: 0; color: rgba(255, 255, 255, .75); font-size: .92rem; }
@media (prefers-reduced-motion: reduce) { .sl-spin { animation: none; } }
.cine {
    /* 좌하단 정박 + 폭 상한: 전폭 바는 시범을 가린다(2026-08-11 사용자).
     * 오른쪽(스네어·양손 액션 영역)을 비워 둔다. */
    position: absolute; left: 12px; bottom: 12px; right: auto; z-index: 5;
    max-width: min(58%, 540px);
    display: flex; align-items: flex-end; gap: 10px; cursor: pointer;
}
.cine[hidden] { display: none; }
.cine-face {
    width: 64px; height: 64px; border-radius: 12px; flex: none;
    object-fit: cover; object-position: top; background: #17181d;
    border: 2px solid rgba(255, 255, 255, .22);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
    transition: width .25s ease, height .25s ease, border-radius .25s ease;
}
.cine-box {
    position: relative; flex: 1; min-width: 0;
    background: rgba(10, 11, 16, .78);
    border: 1px solid rgba(255, 255, 255, .16); border-radius: 12px;
    padding: 9px 34px 11px 14px;
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
/* 접힘: 마지막 대사 후 초상 칩만 남긴다. 탭 = 다시 펼침. */
.cine.min .cine-box { display: none; }
.cine.min .cine-face { width: 46px; height: 46px; border-radius: 50%; opacity: .92; }
@media (prefers-reduced-motion: reduce) { .cine-face { transition: none; } }
.cine-name {
    display: inline-block; font-size: .72rem; font-weight: 700;
    letter-spacing: .06em; color: var(--accent-secondary, #fb923c);
    margin-bottom: 2px;
}
.cine-text {
    margin: 0; font-size: .95rem; line-height: 1.5; color: #f2f1ee;
    min-height: 1.5em; word-break: keep-all;
}
.cine-adv {
    position: absolute; right: 12px; bottom: 8px; font-size: .78rem;
    color: #f2f1ee; opacity: .85;
    animation: cineAdv 1s ease-in-out infinite;
}
.cine-adv[hidden] { display: none; }
@keyframes cineAdv { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(3px); } }
@media (prefers-reduced-motion: reduce) { .cine-adv { animation: none; } }
@media (max-width: 560px) {
    .cine { max-width: 78%; }
    .cine-face { width: 52px; height: 52px; border-radius: 10px; }
    .cine-text { font-size: .86rem; }
}

/* ============================================================
   연습 PiP — 악보와 시범 3-D 동시(2026-08-11 사용자 지시. 08-05 의
   "3-D 는 시범 전용" 배타 규칙을 이 지시로 개정). 무대가 화면 우하단
   미니뷰로 떠 있고, 같은 transport 클록이라 강사가 재생과 동기로 친다.
   ============================================================ */
body.flow-practice .stage3d {
    position: fixed; right: 14px;
    bottom: calc(152px + env(safe-area-inset-bottom, 0px));    /* TEMPO 행 위 */
    width: clamp(180px, 30vw, 320px); aspect-ratio: 16 / 10; height: auto;
    margin: 0; flex: none; z-index: 40;
    border-radius: 12px; overflow: hidden;
    border: 1px solid rgba(255, 255, 255, .16);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .5);
    /* 표시 전용 — 어떤 뷰포트에서도 아래 컨트롤의 클릭을 가로채면 안 된다
     * (prod_smoke 폰 뷰포트에서 정지 버튼 클릭 차단 적발, 2026-08-11). */
    pointer-events: none;
}
body.flow-practice .cine { display: none; }
@media (max-width: 560px) {
    /* 폰: 컨트롤 블록(TEMPO+transport)이 높아 PiP 를 그 위로 올린다 —
     * 시각적으로도 정지 버튼을 덮지 않는다. */
    body.flow-practice .stage3d { width: 34vw; bottom: calc(206px + env(safe-area-inset-bottom, 0px)); }
}

/* 판정 말풍선의 강사 얼굴(표정 = 결과). 아이콘은 TUTOR 부재 폴백. */
.fb-face {
    width: 44px; height: 44px; border-radius: 50%; flex: none;
    object-fit: cover; object-position: top;
    border: 1px solid rgba(255, 255, 255, .2); background: #17181d;
}
.fb-face[hidden] { display: none; }
