/* INFINITAS PLAYBOOK visual system.

   A calm, dense base (neutral structure, quiet metadata) with deliberate light where the
   product's protagonists live: 成果 (achievements) are lit, 推移 (trends) are saturated
   and 蓄積 (accumulation) is coloured by what was accumulated. Lamp, rank and difficulty
   classes only define variables, so every component that shows the same state reads the
   same values (docs/design-principles.md §4).

   - Lamps and DJ ranks are embedded HUD lamps, never stickers: a held state is itself an
     achievement, a powered lamp in its full colour (glass lens, lit rim, inner core);
     reaching it inside the selected period drives the same lamp harder (white-hot rim,
     bloom, halo), which then keeps a slow gentle glow while on screen, and the highest
     lamps and ranks add a rare light sweep. The glow grows with the achievement's weight:
     first clear < HARD / EX-HARD < FULL COMBO < PERFECT; A/AA < AAA / MAX-N < MAX- < MAX.
   - ▲ marks a score or minimum BP set inside the period; its number stays ink and the light
     sits around it. Lamps and ranks never use it.
   - Difficulty colours the level numeral and the chart code (SPA, SPH, SPL …); in the ☆12
     tables SPH and SPL titles also take their colour (SPA titles stay ink).
   - FAILED is an unlit neutral grey and never a lamp achievement.
   - Ordinal scales: a warm ☆12 tier ramp, CPI groups shaded in their lamp basis, and the
     daily keystroke anchors at half the daily goal and the goal itself.
   - Figures use --font-num with tabular digits; Japanese text uses --font-ui.
   Canvas charts mirror these tokens in app.js (CANVAS_TOKENS). */

:root {
  color-scheme: light;

  /* Neutral structure */
  --bg: #eef0f3;
  --surface: #ffffff;
  --surface-2: #f6f7f9;
  --surface-3: #eaedf1;
  --line: #e1e4e9;
  --line-2: #cdd2d9;
  --ink: #151b24;
  --ink-2: #465160;
  --ink-3: #66717f;
  --ink-4: #a0a8b3;
  --focus: #2563eb;

  /* Score event accent: a score set inside the selected period (▲, gains). */
  --event: #e8590c;
  --event-ink: #c2410c;
  --event-tint: #fff1e6;
  /* The distance to the nearest DJ rank (MAX-188, AAA+140) keeps its historical red. */
  --rank-delta: #d0283a;

  /* Activity amount ramp (calendar): the activity chart's blue, deepening with volume. */
  --ramp-1: #e3edff;
  --ramp-2: #bcd3ff;
  --ramp-3: #7fa9fb;
  --ramp-4: #3f77f0;
  --ramp-5: #2552c9;
  --ramp-6: #183a96;

  --font-ui: "BIZ UDPGothic", "Yu Gothic UI", "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, system-ui, sans-serif;
  /* Figures: a technical grotesk with true tabular digits; Japanese falls back to the UI face. */
  --font-num: Bahnschrift, "DIN Alternate", "Segoe UI Variable Display", "Segoe UI", "SF Pro Display", "BIZ UDPGothic", "Yu Gothic UI", system-ui, sans-serif;

  /* Nothing visible goes below --fs-unit; that size is reserved for captions fused to a number. */
  --fs-unit: 10px;
  --fs-micro: 10px;
  --fs-meta: 11px;
  --fs-control: 12px;
  --fs-body: 13px;

  --radius-lg: 12px;
  --radius: 10px;
  --radius-sm: 7px;
  --radius-xs: 5px;
  --shadow-card: 0 1px 2px rgba(21, 27, 36, 0.04), 0 3px 10px rgba(21, 27, 36, 0.035);
  --shadow-pop: 0 18px 44px rgba(21, 27, 36, 0.18), 0 3px 8px rgba(21, 27, 36, 0.08);
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23465160' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ---------- Semantic palettes (variables only) ---------- */

/* Per lamp: --lamp-off (the lamp body, unpowered), --lamp (the same body switched on),
   --lamp-hi (its lit top), --lamp-on (label text, identical in both states), --lamp-glow (how
   far a lit lamp throws light), --lamp-tint/--lamp-ink (surfaces that only carry the lamp's
   colour, such as cards) and --light (the colour a row or plate lit by this lamp takes). */
/* Only FULL COMBO / PERFECT own a gradient body. Every other lamp resets it so a pill nested
   inside a FULL COMBO/PERFECT row or card keeps its own glass instead of inheriting the row's. */
:is(.lamp-none, .lamp-failed, .lamp-assist, .lamp-easy, .lamp-clear, .lamp-hard, .lamp-exhard) {
  --lamp-body: initial; --lamp-sheen: initial; --lamp-sheen-soft: initial;
}
.lamp-none { --lamp-off: #d3d8de; --lamp: #c3c9d0; --lamp-hi: #e2e6ea; --lamp-tint: #f2f4f6; --lamp-ink: #58616d; --lamp-on: #4a5360; --lamp-glow: 0px; }
/* FAILED is a played-but-unlit lamp: neutral grey, a shade darker than NO PLAY, never glowing. */
.lamp-failed { --lamp-off: #8f96a0; --lamp: #8d949d; --lamp-hi: #b3b9c0; --lamp-tint: #eceef1; --lamp-ink: #4f5761; --lamp-on: #ffffff; --lamp-glow: 0px; }
.lamp-assist { --light: #b35ee0; --lamp-off: #9b4fc5; --lamp: #b35ee0; --lamp-hi: #ebbcff; --lamp-tint: #f6ecfb; --lamp-ink: #7d2f9e; --lamp-on: #ffffff; --lamp-glow: 8px; }
/* ASSIST CLEAR not counted as an achievement: still its own state, never grey like FAILED
   nor blue like CLEAR — a low-saturation mauve leaning to pink — quieter and unable to light. */
body.assist-achievement-off .lamp-assist { --light: #ad86a8; --lamp-off: #a7809f; --lamp: #ad86a8; --lamp-hi: #e2cadc; --lamp-tint: #f7eff4; --lamp-ink: #6f4d69; --lamp-on: #ffffff; --lamp-glow: 0px; }
.lamp-easy { --light: #2cb14a; --lamp-off: #2f9a47; --lamp: #2cb14a; --lamp-hi: #93f0a0; --lamp-tint: #e8f7ea; --lamp-ink: #1d6b2b; --lamp-on: #ffffff; --lamp-glow: 9px; }
.lamp-clear { --light: #168fff; --lamp-off: #168fff; --lamp: #168fff; --lamp-hi: #9dd6ff; --lamp-tint: #e6f1fc; --lamp-ink: #155a9e; --lamp-on: #ffffff; --lamp-glow: 9px; }
.lamp-hard { --light: #ee3a4d; --lamp-off: #d03848; --lamp: #ee3a4d; --lamp-hi: #ffadb5; --lamp-tint: #fdebed; --lamp-ink: #a51e2c; --lamp-on: #ffffff; --lamp-glow: 12px; }
.lamp-exhard { --light: #ffc400; --lamp-off: #e9ae00; --lamp: #ffc400; --lamp-hi: #fff4a8; --lamp-tint: #fff6d6; --lamp-ink: #7d5700; --lamp-on: #2e2200; --lamp-glow: 13px; }
.lamp-fullcombo {
  --light: #9b7bff; --lamp-off: #8a60d6; --lamp: #8a60d6; --lamp-hi: #c9b3ff; --lamp-tint: #f2edfc; --lamp-ink: #5b3b9f; --lamp-on: #1d1633; --lamp-glow: 15px;
  --lamp-body: linear-gradient(100deg, #e98c9d, #e9c35a 27%, #86cc78 50%, #69b5dc 73%, #a18ae6);
  --lamp-sheen: linear-gradient(100deg, #ff8fa2, #ffd45e 27%, #8fe07a 50%, #6fcaf5 73%, #b39bff);
  --lamp-sheen-soft: linear-gradient(100deg, #ffe6ea, #fff4d2 27%, #e6f7dc 50%, #def2fc 73%, #ede6ff);
}
.lamp-perfect {
  --light: #f2b000; --lamp-off: #d9a10a; --lamp: #e0a000; --lamp-hi: #fff0a8; --lamp-tint: #fff5d3; --lamp-ink: #7a5400; --lamp-on: #362500; --lamp-glow: 17px;
  --lamp-body: linear-gradient(110deg, #f1d67a, #dcaa14 30%, #f4e3a4 50%, #cf9606 72%, #ecce68);
  --lamp-sheen: linear-gradient(110deg, #fff3b0, #f6c21a 30%, #fffbe3 50%, #e8a800 72%, #ffe883);
  --lamp-sheen-soft: linear-gradient(110deg, #fffbee, #fff0c0 38%, #fffbee 58%, #ffeaa8);
}

.difficulty-b { --difficulty: #3f8a36; }
.difficulty-n { --difficulty: #2a78c2; }
.difficulty-h { --difficulty: #b07a00; --difficulty-text: #9a6200; }
.difficulty-a { --difficulty: #cf3642; }
.difficulty-l { --difficulty: #8141d0; --difficulty-text: #7433c4; }
.difficulty-other { --difficulty: var(--ink-2); }

/* Appropriate CPI names its lamp basis by colour (text-safe inks of the lamp hues). */
.cpi-lamp-easy { --cpi: #1f8a33; }
.cpi-lamp-clear { --cpi: #1766c0; }
.cpi-lamp-hard { --cpi: #cc2535; }
.cpi-lamp-exhard { --cpi: #a96f00; }
.cpi-lamp-fullcombo { --cpi: #6a3fc0; }

/* BPI is contextual, not an achievement ladder: three broad bands (0–10 blue, >10–20
   green, >20 orange) plus a subdued negative tone. Unavailable BPI has no band. */
.bpi-negative { --bpi: #7d8594; --bpi-tint: #f1f2f5; }
.bpi-blue { --bpi: #1f63c9; --bpi-tint: #e8f0fd; }
.bpi-green { --bpi: #17864a; --bpi-tint: #e5f5eb; }
.bpi-orange { --bpi: #d0620a; --bpi-tint: #fff0e2; }

/* ---------- Base ---------- */

* { box-sizing: border-box; }

html {
  font-size: 16px;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  position: relative;
  min-width: 320px;
  margin: 0;
  color: var(--ink);
  background: var(--bg);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: 1.5;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Stage lighting behind the content: two cool washes and a faint warm floor on a fixed
   layer (the cards scroll over it, a restrained depth cue), plus a barely-there dot grid
   that fades out downward. It sits behind every surface and never under text directly. */
body::before {
  content: "";
  position: fixed;
  z-index: -1;
  inset: 0;
  background:
    radial-gradient(1100px 520px at 6% -10%, rgba(58, 116, 255, 0.13), transparent 64%),
    radial-gradient(900px 520px at 98% -4%, rgba(160, 104, 255, 0.1), transparent 62%),
    radial-gradient(1200px 520px at 52% 112%, rgba(255, 156, 64, 0.07), transparent 64%),
    linear-gradient(180deg, #e9edf4 0%, var(--bg) 42%, #edeef2 100%);
  pointer-events: none;
}

body::after {
  content: "";
  position: fixed;
  z-index: -1;
  inset: 0;
  background-image: radial-gradient(rgba(38, 58, 102, 0.07) 1px, transparent 1.2px);
  background-size: 22px 22px;
  -webkit-mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.35) 55%, transparent 90%);
  mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.35) 55%, transparent 90%);
  pointer-events: none;
}

button,
input,
select {
  color: inherit;
  font: inherit;
}

button,
select { cursor: pointer; }

button:focus-visible,
input:focus-visible,
select:focus-visible,
a:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

input[type="checkbox"] { accent-color: var(--ink); }

.skip-link {
  position: fixed;
  z-index: 100;
  top: 10px;
  left: 10px;
  padding: 10px 14px;
  color: #fff;
  background: var(--ink);
  border-radius: 8px;
  transform: translateY(-150%);
  transition: transform 160ms var(--ease);
}

.skip-link:focus { transform: translateY(0); }

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

[hidden] { display: none !important; }

body.drawer-open,
body.settings-open { overflow: hidden; }

/* ---------- Shared controls ---------- */

.icon-button,
.text-button,
.nav-button,
.filter-reset,
.primary-button,
.drawer-close {
  min-height: 32px;
  border: 1px solid var(--line-2);
  border-radius: 8px;
  color: var(--ink-2);
  background: var(--surface);
  font-size: var(--fs-control);
  font-weight: 700;
  transition:
    background-color 140ms var(--ease),
    border-color 140ms var(--ease),
    color 140ms var(--ease),
    transform 120ms var(--ease);
}

.icon-button:hover:not(:disabled),
.text-button:hover:not(:disabled),
.nav-button:hover:not(:disabled),
.filter-reset:hover:not(:disabled),
.drawer-close:hover {
  border-color: var(--ink-4);
  color: var(--ink);
  background: var(--surface-2);
}

.icon-button:active:not(:disabled),
.text-button:active:not(:disabled),
.nav-button:active:not(:disabled),
.filter-reset:active:not(:disabled),
.primary-button:active,
.drawer-close:active { transform: translateY(1px); }

.primary-button {
  margin-top: 16px;
  padding: 0 18px;
  border-color: var(--ink);
  color: #fff;
  background: var(--ink);
}

.primary-button:hover { background: #283241; }

.segmented-control,
.metric-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: 9px;
  background: var(--surface-3);
  box-shadow: inset 0 0 0 1px rgba(21, 27, 36, 0.04);
}

.segmented-control button,
.metric-toggle button {
  min-height: 30px;
  padding: 0 11px;
  border: 0;
  border-radius: 7px;
  color: var(--ink-3);
  background: transparent;
  font-size: var(--fs-control);
  font-weight: 700;
  white-space: nowrap;
  transition: background-color 140ms var(--ease), color 140ms var(--ease), box-shadow 140ms var(--ease);
}

.segmented-control button:hover:not(.is-active),
.metric-toggle button:hover:not(.is-active) {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.6);
}

.segmented-control button.is-active,
.metric-toggle button.is-active {
  color: var(--ink);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(21, 27, 36, 0.14), 0 0 0 1px rgba(21, 27, 36, 0.05);
}

/* Sliding active pill while a segmented choice glides (see glideSegmentedControls). */
.segmented-control.is-gliding { position: relative; }
.segmented-control.is-gliding button { position: relative; }
.segmented-control.is-gliding button.is-active { background: transparent; box-shadow: none; }
.segmented-glide {
  position: absolute;
  border-radius: 7px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(21, 27, 36, 0.14), 0 0 0 1px rgba(21, 27, 36, 0.05);
  pointer-events: none;
}

.metric-toggle button {
  min-height: 26px;
  padding: 0 10px;
  font-size: var(--fs-meta);
}

/* Occasional display settings fold into one quiet button; the panel floats over the content
   instead of permanently taking a row. */
.options-popover {
  position: relative;
  flex: 0 0 auto;
}

.options-popover > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line-2);
  border-radius: 8px;
  color: var(--ink-2);
  background: var(--surface);
  cursor: pointer;
  font-size: var(--fs-meta);
  font-weight: 700;
  list-style: none;
  user-select: none;
  white-space: nowrap;
  transition: border-color 140ms var(--ease), background-color 140ms var(--ease), color 140ms var(--ease);
}

.options-popover > summary::-webkit-details-marker { display: none; }

.options-popover > summary::after {
  content: "";
  width: 10px;
  height: 6px;
  background: var(--chevron) no-repeat center / 10px 6px;
  transition: transform 160ms var(--ease);
}

.options-popover > summary:hover,
.options-popover[open] > summary {
  border-color: var(--ink-4);
  color: var(--ink);
  background: var(--surface-2);
}

.options-popover[open] > summary::after { transform: rotate(180deg); }

.options-popover-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 6px);
  right: 0;
  display: grid;
  gap: 9px;
  min-width: 220px;
  margin: 0;
  padding: 11px 12px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-pop);
  animation: pop-in 140ms var(--ease);
}

/* One select grammar: native control, quiet chevron, no platform chrome. */
.select-field select,
.sort-field select,
.achievement-filterbar select,
.tier-cpi-control select,
.tier-ac-scope,
.web-export-history select,
.web-export-row select,
.song-number-filters select {
  appearance: none;
  -webkit-appearance: none;
  background: var(--surface) var(--chevron) no-repeat right 9px center / 10px 6px;
}

.section-kicker,
.eyebrow {
  color: var(--ink-3);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.08em;
}

.section-kicker {
  font-family: var(--font-num);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

