/* 鱼の琴 · 宣纸 Ink & Paper (docs/design.md, design/concept-ink/tokens.md, docs/ui-structure.md). Light chrome only.
   Normal mode at 1194x834: score 592 · deck 127 (rail 20 + row A 50 + row B 56 + rule) · felt 3 · keys 112.
   Row A = information (song · prompt · steps · piano), row B = controls; the fullscreen dock is row B's focus subset. */
:root{
  --paper:#F4EFE4;--paper-raised:#FBF8F1;--sheet:#FFFDF7;--sunk:rgba(29,26,22,.055);--scrim:rgba(43,34,22,.30);
  --ink:#1D1A16;--ink-2:#4A443B;--ink-3:#736B5E;--ink-4:#B3AA9A;--rule:#DFD6C6;--rule-2:#CBBFAB;
  --cinnabar:#B23A29;--cinnabar-ink:#9A2F20;--cinnabar-wash:#F6E3DD;
  --jade:#2E7D5B;--jade-ink:#22604A;--jade-wash:#E1EEE5;
  --left:#2F5FA7;--left-ink:#244C88;--left-wash:#E3EAF6;--left-key:#7397D5;--left-key-top:#A3BCE6;
  --right:#C77A12;--right-ink:#8A520A;--right-wash:#F8EBD4;--right-key:#EDAA4A;--right-key-top:#F7CE88;
  --ok-key:#57A984;--ok-key-top:#8CC8AB;--wrong-key:#C4513F;--wrong-key-top:#DE8573;--keybed:#1D1A16;
  --font:-apple-system,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
  --r-sm:6px;--r-md:10px;--r-lg:14px;--r-xl:20px;
  --sh-1:0 1px 2px rgba(29,26,22,.07),0 1px 6px rgba(29,26,22,.05);
  --sh-float:0 12px 32px rgba(29,26,22,.16),0 2px 6px rgba(29,26,22,.08);
  --sh-sheet:0 -12px 40px rgba(29,26,22,.14),0 -1px 0 rgba(29,26,22,.04);
  --kb-h:112px;--felt:3px;
  --safe-t:env(safe-area-inset-top,0px);--safe-b:env(safe-area-inset-bottom,0px);--safe-l:env(safe-area-inset-left,0px);--safe-r:env(safe-area-inset-right,0px);
  --fibre:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .42 0 0 0 0 .35 0 0 0 0 .24 0 0 0 .055 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 10l5 5 5-5'/%3E%3C/svg%3E");
  --piano:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='6.5' width='17' height='11' rx='1.5'/%3E%3Cpath d='M8 12.5v5M12 12.5v5M16 12.5v5'/%3E%3Cpath d='M6.8 6.5v6h2.4v-6M14.8 6.5v6h2.4v-6' fill='black'/%3E%3C/svg%3E");
  --fish-mark:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 18'%3E%3Cpath fill-rule='evenodd' d='M8.6 9L3.2 3.4l2.4 5.6-2.4 5.6zM8.6 9a8.4 5.8 0 1 0 16.8 0a8.4 5.8 0 1 0-16.8 0zM20.9 6.1a1.2 1.2 0 1 0 0 2.4a1.2 1.2 0 1 0 0-2.4z'/%3E%3C/svg%3E");
}
/* 锦鲤池 koi-pond art: generated by design/koi-a/koi-art.cjs (docs/fish-koi-a.md); do not edit by hand. */
/* koi-art:begin */
:root{--koi-thumb:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-136.3 -29.1 141.3 55'%3E%3Cg fill='%23F4EFE4' stroke='%23F4EFE4' stroke-width='9' stroke-linejoin='round'%3E%3Cpath d='M0 0C0 0.3 -0.3 0.4 -0.5 0.8C-0.8 1.2 -1.1 2.1 -1.4 2.5C-1.7 3 -2 3.3 -2.4 3.7C-2.8 4.1 -3.3 4.5 -3.8 4.8C-4.3 5.2 -4.8 5.5 -5.4 5.8C-6.1 6.2 -6.8 6.6 -7.6 6.9C-8.4 7.2 -9.2 7.5 -10.2 7.8C-11.1 8.1 -12 8.5 -13.2 8.7C-14.4 9 -15.9 9.2 -17.2 9.3C-18.5 9.4 -19.7 9.4 -21 9.4C-22.3 9.4 -23.6 9.6 -24.9 9.5C-26.2 9.5 -27.4 9.3 -28.7 9.3C-30 9.2 -31.3 9.2 -32.5 9.2C-33.8 9.1 -35.1 9 -36.4 9C-37.6 8.9 -38.9 8.8 -40.2 8.6C-41.5 8.5 -42.7 8.2 -44 8C-45.3 7.8 -46.6 7.8 -47.8 7.6C-49.1 7.4 -50.4 7 -51.7 6.8C-53 6.5 -54.3 6.3 -55.6 6.1C-56.9 5.9 -58.2 5.7 -59.4 5.6C-60.7 5.4 -62 5.3 -63.3 5.1C-64.7 4.9 -66 4.6 -67.3 4.4C-68.6 4.2 -69.9 4 -71.2 3.9C-72.5 3.7 -73.9 3.5 -75.2 3.3C-76.5 3.1 -77.8 3 -79.2 2.9C-80.5 2.7 -81.8 2.7 -83.1 2.6C-84.5 2.6 -85.8 2.5 -87.1 2.5C-88.4 2.5 -89.8 2.5 -91.1 2.6C-92.4 2.6 -93.8 2.6 -95.1 2.7C-96.4 2.8 -98 3 -99 3C-100.1 3 -100.7 2.8 -101.6 2.7C-102.4 2.7 -104 3.3 -104.2 2.5C-104.3 1.8 -103.1 -1 -102.4 -1.9C-101.7 -2.8 -101.1 -2.6 -100 -3C-99 -3.3 -97.4 -3.6 -96.1 -4C-94.8 -4.3 -93.5 -4.7 -92.2 -5C-90.8 -5.3 -89.5 -5.7 -88.2 -6C-86.9 -6.4 -85.6 -6.7 -84.2 -7.1C-82.9 -7.4 -81.6 -7.8 -80.3 -8.2C-78.9 -8.6 -77.6 -9 -76.3 -9.4C-75 -9.8 -73.6 -10.2 -72.3 -10.6C-70.9 -10.9 -69.6 -11.3 -68.2 -11.6C-66.9 -12 -65.5 -12.4 -64.2 -12.7C-62.8 -13.1 -61.5 -13.3 -60.1 -13.6C-58.7 -13.8 -57.3 -14 -56 -14.3C-54.6 -14.5 -53.2 -14.8 -51.8 -15.1C-50.5 -15.3 -49.1 -15.7 -47.7 -15.9C-46.3 -16.1 -44.9 -16.1 -43.5 -16.2C-42.1 -16.3 -40.7 -16.5 -39.3 -16.6C-38 -16.7 -36.6 -16.7 -35.2 -16.6C-33.8 -16.6 -32.4 -16.5 -31 -16.4C-29.6 -16.3 -28.2 -16.1 -26.9 -16C-25.5 -15.9 -24.1 -15.8 -22.7 -15.6C-21.3 -15.4 -20 -15 -18.6 -14.7C-17.2 -14.4 -15.9 -14.2 -14.5 -13.8C-13.2 -13.4 -11.7 -12.8 -10.5 -12.3C-9.4 -11.7 -8.5 -11.1 -7.6 -10.6C-6.8 -10 -6 -9.5 -5.3 -8.9C-4.6 -8.4 -4 -7.8 -3.4 -7.2C-2.9 -6.7 -2.5 -6.3 -2.1 -5.8C-1.7 -5.3 -1.4 -4.8 -1.1 -4.3C-0.9 -3.8 -0.7 -3.4 -0.5 -2.9C-0.4 -2.3 -0.3 -1.4 -0.2 -0.9C-0.2 -0.5 0 -0.3 0 0Z'/%3E%3Cpath d='M-97 -3.2C-98.1 -4.2 -100.9 -3.2 -102.7 -3.5C-104.6 -3.7 -106.3 -4.1 -108.1 -4.6C-109.9 -5.2 -111.5 -6.1 -113.4 -6.8C-115.2 -7.4 -117.5 -8.1 -119.3 -8.5C-121.1 -8.9 -122.6 -9 -124.2 -9.2C-125.7 -9.4 -127.4 -9.7 -128.6 -9.7C-129.8 -9.7 -131.1 -9.4 -131.3 -9C-131.6 -8.6 -131.1 -8.1 -130.3 -7.4C-129.5 -6.7 -127.9 -5.9 -126.8 -4.9C-125.7 -4 -124.5 -2.7 -123.7 -1.6C-123 -0.6 -122.5 0.4 -122.1 1.4C-121.6 2.5 -120.9 3.4 -120.8 4.6C-120.6 5.7 -121 6.9 -121.2 8.2C-121.3 9.5 -121.5 11 -121.8 12.3C-122.1 13.6 -122.7 15 -123 16.2C-123.2 17.5 -123.2 18.8 -123.4 19.6C-123.5 20.4 -124.2 20.9 -123.8 20.9C-123.4 21 -121.9 20.4 -120.8 19.8C-119.8 19.2 -118.5 18.3 -117.4 17.3C-116.2 16.4 -115.1 15.2 -113.9 14.1C-112.7 12.9 -111.5 11.5 -110.2 10.3C-108.8 9 -107.3 7.8 -105.9 6.8C-104.4 5.7 -102.9 4.8 -101.3 4.1C-99.7 3.4 -96.9 3.6 -96.1 2.4C-95.4 1.2 -95.9 -2.3 -97 -3.2Z'/%3E%3C/g%3E%3Cpath d='M-97 -3.2C-98.1 -4.2 -100.9 -3.2 -102.7 -3.5C-104.6 -3.7 -106.3 -4.1 -108.1 -4.6C-109.9 -5.2 -111.5 -6.1 -113.4 -6.8C-115.2 -7.4 -117.5 -8.1 -119.3 -8.5C-121.1 -8.9 -122.6 -9 -124.2 -9.2C-125.7 -9.4 -127.4 -9.7 -128.6 -9.7C-129.8 -9.7 -131.1 -9.4 -131.3 -9C-131.6 -8.6 -131.1 -8.1 -130.3 -7.4C-129.5 -6.7 -127.9 -5.9 -126.8 -4.9C-125.7 -4 -124.5 -2.7 -123.7 -1.6C-123 -0.6 -122.5 0.4 -122.1 1.4C-121.6 2.5 -120.9 3.4 -120.8 4.6C-120.6 5.7 -121 6.9 -121.2 8.2C-121.3 9.5 -121.5 11 -121.8 12.3C-122.1 13.6 -122.7 15 -123 16.2C-123.2 17.5 -123.2 18.8 -123.4 19.6C-123.5 20.4 -124.2 20.9 -123.8 20.9C-123.4 21 -121.9 20.4 -120.8 19.8C-119.8 19.2 -118.5 18.3 -117.4 17.3C-116.2 16.4 -115.1 15.2 -113.9 14.1C-112.7 12.9 -111.5 11.5 -110.2 10.3C-108.8 9 -107.3 7.8 -105.9 6.8C-104.4 5.7 -102.9 4.8 -101.3 4.1C-99.7 3.4 -96.9 3.6 -96.1 2.4C-95.4 1.2 -95.9 -2.3 -97 -3.2Z' fill='%231D1A16' fill-opacity='.72'/%3E%3Cpath d='M-18.5 6.8C-18.3 8.1 -19.3 10.3 -20 11.7C-20.8 13.1 -21.7 14.3 -22.9 15.4C-24 16.4 -25.6 17.7 -27 18.1C-28.5 18.5 -30.3 18.1 -31.4 17.7C-32.5 17.2 -33 16.3 -33.5 15.5C-34 14.6 -34.6 13.6 -34.4 12.6C-34.2 11.6 -33.2 10.5 -32.1 9.6C-31 8.7 -29.2 7.9 -27.8 7.3C-26.4 6.7 -24.8 6.5 -23.7 5.9C-22.6 5.3 -22.1 3.6 -21.2 3.7C-20.4 3.8 -18.7 5.4 -18.5 6.8ZM-16.6 -11.7C-16.2 -13 -16.9 -15.2 -17.4 -16.8C-17.9 -18.4 -18.7 -20.1 -19.7 -21.3C-20.7 -22.5 -21.9 -23.4 -23.2 -23.8C-24.5 -24.3 -26.4 -24.4 -27.4 -24.1C-28.5 -23.8 -29.2 -23 -29.6 -22.2C-30 -21.3 -30.2 -20.1 -30.1 -19.1C-30 -18.1 -29.7 -17.2 -28.9 -16.3C-28.2 -15.4 -26.7 -14.4 -25.6 -13.7C-24.4 -12.9 -22.9 -12.5 -22 -11.7C-21 -10.9 -20.8 -9 -19.9 -9C-19 -9 -17 -10.4 -16.6 -11.7Z' fill='%231D1A16' fill-opacity='.6'/%3E%3Cpath d='M0 0C0 0.3 -0.3 0.4 -0.5 0.8C-0.8 1.2 -1.1 2.1 -1.4 2.5C-1.7 3 -2 3.3 -2.4 3.7C-2.8 4.1 -3.3 4.5 -3.8 4.8C-4.3 5.2 -4.8 5.5 -5.4 5.8C-6.1 6.2 -6.8 6.6 -7.6 6.9C-8.4 7.2 -9.2 7.5 -10.2 7.8C-11.1 8.1 -12 8.5 -13.2 8.7C-14.4 9 -15.9 9.2 -17.2 9.3C-18.5 9.4 -19.7 9.4 -21 9.4C-22.3 9.4 -23.6 9.6 -24.9 9.5C-26.2 9.5 -27.4 9.3 -28.7 9.3C-30 9.2 -31.3 9.2 -32.5 9.2C-33.8 9.1 -35.1 9 -36.4 9C-37.6 8.9 -38.9 8.8 -40.2 8.6C-41.5 8.5 -42.7 8.2 -44 8C-45.3 7.8 -46.6 7.8 -47.8 7.6C-49.1 7.4 -50.4 7 -51.7 6.8C-53 6.5 -54.3 6.3 -55.6 6.1C-56.9 5.9 -58.2 5.7 -59.4 5.6C-60.7 5.4 -62 5.3 -63.3 5.1C-64.7 4.9 -66 4.6 -67.3 4.4C-68.6 4.2 -69.9 4 -71.2 3.9C-72.5 3.7 -73.9 3.5 -75.2 3.3C-76.5 3.1 -77.8 3 -79.2 2.9C-80.5 2.7 -81.8 2.7 -83.1 2.6C-84.5 2.6 -85.8 2.5 -87.1 2.5C-88.4 2.5 -89.8 2.5 -91.1 2.6C-92.4 2.6 -93.8 2.6 -95.1 2.7C-96.4 2.8 -98 3 -99 3C-100.1 3 -100.7 2.8 -101.6 2.7C-102.4 2.7 -104 3.3 -104.2 2.5C-104.3 1.8 -103.1 -1 -102.4 -1.9C-101.7 -2.8 -101.1 -2.6 -100 -3C-99 -3.3 -97.4 -3.6 -96.1 -4C-94.8 -4.3 -93.5 -4.7 -92.2 -5C-90.8 -5.3 -89.5 -5.7 -88.2 -6C-86.9 -6.4 -85.6 -6.7 -84.2 -7.1C-82.9 -7.4 -81.6 -7.8 -80.3 -8.2C-78.9 -8.6 -77.6 -9 -76.3 -9.4C-75 -9.8 -73.6 -10.2 -72.3 -10.6C-70.9 -10.9 -69.6 -11.3 -68.2 -11.6C-66.9 -12 -65.5 -12.4 -64.2 -12.7C-62.8 -13.1 -61.5 -13.3 -60.1 -13.6C-58.7 -13.8 -57.3 -14 -56 -14.3C-54.6 -14.5 -53.2 -14.8 -51.8 -15.1C-50.5 -15.3 -49.1 -15.7 -47.7 -15.9C-46.3 -16.1 -44.9 -16.1 -43.5 -16.2C-42.1 -16.3 -40.7 -16.5 -39.3 -16.6C-38 -16.7 -36.6 -16.7 -35.2 -16.6C-33.8 -16.6 -32.4 -16.5 -31 -16.4C-29.6 -16.3 -28.2 -16.1 -26.9 -16C-25.5 -15.9 -24.1 -15.8 -22.7 -15.6C-21.3 -15.4 -20 -15 -18.6 -14.7C-17.2 -14.4 -15.9 -14.2 -14.5 -13.8C-13.2 -13.4 -11.7 -12.8 -10.5 -12.3C-9.4 -11.7 -8.5 -11.1 -7.6 -10.6C-6.8 -10 -6 -9.5 -5.3 -8.9C-4.6 -8.4 -4 -7.8 -3.4 -7.2C-2.9 -6.7 -2.5 -6.3 -2.1 -5.8C-1.7 -5.3 -1.4 -4.8 -1.1 -4.3C-0.9 -3.8 -0.7 -3.4 -0.5 -2.9C-0.4 -2.3 -0.3 -1.4 -0.2 -0.9C-0.2 -0.5 0 -0.3 0 0Z' fill='%231D1A16'/%3E%3Cellipse cx='-17' cy='-0.6' rx='5.6' ry='5.2' fill='%23C8432F'/%3E%3C/svg%3E");--seigaiha:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='24' viewBox='0 0 48 24'%3E%3Cg fill='none' stroke='%231D1A16' stroke-width='1.1'%3E%3Ccircle cx='24' cy='0' r='24' fill='%23F4EFE4'/%3E%3Ccircle cx='24' cy='0' r='18'/%3E%3Ccircle cx='24' cy='0' r='12'/%3E%3Ccircle cx='24' cy='0' r='6'/%3E%3Ccircle cx='0' cy='12' r='24' fill='%23F4EFE4'/%3E%3Ccircle cx='0' cy='12' r='18'/%3E%3Ccircle cx='0' cy='12' r='12'/%3E%3Ccircle cx='0' cy='12' r='6'/%3E%3Ccircle cx='48' cy='12' r='24' fill='%23F4EFE4'/%3E%3Ccircle cx='48' cy='12' r='18'/%3E%3Ccircle cx='48' cy='12' r='12'/%3E%3Ccircle cx='48' cy='12' r='6'/%3E%3Ccircle cx='24' cy='24' r='24' fill='%23F4EFE4'/%3E%3Ccircle cx='24' cy='24' r='18'/%3E%3Ccircle cx='24' cy='24' r='12'/%3E%3Ccircle cx='24' cy='24' r='6'/%3E%3C/g%3E%3C/svg%3E")}
.koiK{aspect-ratio:2.509}.koiK .kTail{transform-origin:25.8% 51.2%}.koiK .kFinL{transform-origin:82.7% 59.8%}.koiK .kFinR{transform-origin:84.4% 28.8%}
.koiS{aspect-ratio:2.473}.koiS .kTail{transform-origin:25.6% 51.6%}.koiS .kFinL{transform-origin:82.6% 60.1%}.koiS .kFinR{transform-origin:84.4% 29.5%}
.koiShade{aspect-ratio:2.797}.koiShade .kTail{transform-origin:24.0% 52.5%}.koiShade .kFinL{transform-origin:82.8% 67.9%}.koiShade .kFinR{transform-origin:83.3% 32.3%}
.koiLeap{aspect-ratio:1.795}.koiLeap .kTail{transform-origin:29.7% 41.7%}
.lotusA{aspect-ratio:1.17}
.lotusB{aspect-ratio:1.222}
.lotusBud{aspect-ratio:0.29}
/* koi-art:end */
*{box-sizing:border-box}
html,body{height:100%;min-height:0;margin:0}
body{background:var(--paper);color:var(--ink);font:400 14px/1.45 var(--font);-webkit-font-smoothing:antialiased;font-variant-numeric:tabular-nums;overflow:hidden;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}
h1,h2,h3,p{margin:0;font:inherit}
button,select,input,output{font:inherit;color:inherit}
button{background:none;border:0;margin:0;padding:0;border-radius:0;cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}
button:disabled{cursor:default}
input[type=text],input[type=password],input[type=number]{-webkit-user-select:text;user-select:text}
:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
[hidden]{display:none!important}
a{color:inherit;text-underline-offset:3px}
.sr-only{position:absolute!important;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.sprite{position:absolute;width:0;height:0;overflow:hidden}
.ic{width:20px;height:20px;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.ic.solid{fill:currentColor;stroke:none}
.ic.sm{width:16px;height:16px}
.ic.soft{color:var(--ink-3)}
.vr{width:1px;height:24px;background:var(--rule);flex:none}
.spacer{flex:1 1 0;min-width:0}

/* ---------- Buttons and controls ---------- */
.btn{height:44px;min-width:44px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 12px;border-radius:var(--r-md);font:500 13px/1 var(--font);color:var(--ink-2);white-space:nowrap;flex:none}
.btn.icon{padding:0;width:44px}
.btn:active:not(:disabled){background:var(--sunk)}
.btn:disabled{color:var(--ink-4)}
.btn.primary{background:var(--ink);color:var(--sheet);padding:0 18px 0 14px;font-weight:600;font-size:14px}
.btn.primary:disabled{background:var(--ink-4);color:var(--sheet)}
.btn.primary.isPlaying,.btn.primary.isPlaying:disabled{background:var(--paper-raised);color:var(--ink);box-shadow:inset 0 0 0 1.5px var(--ink)}
/* play/pause is one toggle: the icon follows data-state (play | pause | loading). */
.playToggle .onPause,.playToggle[data-state=pause] .onPlay{display:none}
.playToggle[data-state=pause] .onPause{display:block}
.playToggle[data-state=loading] .onPlay{opacity:.45}
.btn.outline{box-shadow:inset 0 0 0 1px var(--rule-2);background:var(--paper-raised);color:var(--ink-2)}
.btn.outline:disabled{background:none}
.btn.danger{background:var(--cinnabar);color:#FFF6EC;font-weight:600}
/* Pressed colours per variant (the plain .btn:active rule would turn 播放/保存/删除 into light text on paper). */
.btn.primary:active:not(:disabled){background:#3A342D}
.btn.primary.isPlaying:active:not(:disabled){background:var(--sunk)}
.btn.danger:active:not(:disabled){background:var(--cinnabar-ink)}
.btn.dangerText{color:var(--cinnabar-ink)}
.btn.follow{color:var(--ink-4)}
.btn.follow[aria-pressed=true]{color:var(--jade)}
.btn.follow:disabled{color:var(--ink-4);opacity:.6}
.cluster{display:flex;align-items:center;gap:2px;flex:none}
.pagelabel{font:500 12px/1.2 var(--font);color:var(--ink-3);min-width:44px;max-width:150px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* The step count is tappable: it turns into a number field to jump to a step (docs/ui-structure.md). */
.stepBox{display:inline-flex;align-items:center;justify-content:center;min-width:92px;flex:none}
.count{display:inline-block;height:44px;min-width:92px;padding:0 12px;border-radius:var(--r-md);background:var(--paper-raised);box-shadow:inset 0 0 0 1px var(--rule-2);font:600 15px/44px var(--font);color:var(--ink);text-align:center;white-space:nowrap}
.count b{font:inherit}
.count small{font:400 11px/1 var(--font);color:var(--ink-3);white-space:pre}
.count small::after{content:" 步"}
.count:active:not(:disabled){background:var(--sunk)}
.count:disabled{color:var(--ink-3);background:none;box-shadow:inset 0 0 0 1px var(--rule)}
.stepEditor{display:inline-flex;align-items:center;gap:8px}
.stepEditor .input{width:72px;text-align:center;padding:0 8px}
.stepTotal,.stepLead{font:400 12px/1 var(--font);color:var(--ink-3);white-space:nowrap}
.stepLead{margin-right:-2px}
.toggle{height:44px;display:inline-flex;align-items:center;gap:8px;padding:0 15px 0 13px;border-radius:22px;box-shadow:inset 0 0 0 1px var(--rule-2);font:500 13px/1 var(--font);color:var(--ink-2);flex:none;white-space:nowrap}
.toggle i{width:9px;height:9px;border-radius:50%;box-shadow:inset 0 0 0 1.5px currentColor}
.toggle[aria-pressed=true]{background:var(--jade-wash);box-shadow:inset 0 0 0 1px rgba(46,125,91,.38);color:var(--jade-ink)}
.toggle[aria-pressed=true] i{background:var(--jade);box-shadow:none}
.seg{display:inline-flex;height:44px;border-radius:12px;background:var(--sunk);flex:none}
.seg button{position:relative;isolation:isolate;height:44px;padding:0 14px;display:inline-flex;align-items:center;justify-content:center;gap:7px;font:500 13px/1 var(--font);color:var(--ink-3);white-space:nowrap}
.seg button[aria-pressed=true]{color:var(--ink)}
.seg button[aria-pressed=true]::before{content:'';position:absolute;inset:4px;border-radius:9px;background:var(--sheet);box-shadow:var(--sh-1),0 0 0 .5px rgba(29,26,22,.08);z-index:-1}
.hd{width:9px;height:9px;border-radius:50%;flex:none}
.hd.l{background:var(--left)}.hd.r{background:var(--right)}
.seg button[aria-pressed=false] .hd{background:none;box-shadow:inset 0 0 0 1.5px var(--ink-4)}
.range{position:relative;display:inline-flex;align-items:center;height:44px;width:132px;flex:none}
.range.wide{width:auto;flex:1 1 0;min-width:120px;max-width:260px}
.range input{-webkit-appearance:none;appearance:none;display:block;width:100%;height:44px;margin:0;padding:0;background:transparent;cursor:pointer;--f:.6}
.range input:focus-visible{outline-offset:-4px}
.range input::-webkit-slider-runnable-track{height:2px;border-radius:1px;background:linear-gradient(90deg,var(--ink) 0,var(--ink) calc(10px + (100% - 20px) * var(--f)),var(--rule-2) calc(10px + (100% - 20px) * var(--f)),var(--rule-2) 100%)}
.range input::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;margin-top:-9px;border-radius:50%;background:var(--sheet);box-shadow:0 0 0 1.5px var(--ink),0 2px 6px rgba(29,26,22,.18)}
.range input::-moz-range-track{height:2px;border-radius:1px;background:var(--rule-2)}
.range input::-moz-range-progress{height:2px;background:var(--ink)}
.range input::-moz-range-thumb{width:20px;height:20px;border:0;border-radius:50%;background:var(--sheet);box-shadow:0 0 0 1.5px var(--ink),0 2px 6px rgba(29,26,22,.18)}
.range input:disabled{cursor:default;opacity:.5}
.range .mark{position:absolute;left:calc(10px + (100% - 20px) * .6);top:17px;width:1px;height:10px;background:var(--ink-4);pointer-events:none;z-index:-1}
.select{position:relative;display:inline-flex;align-items:center;flex:none}
.select select{-webkit-appearance:none;appearance:none;height:44px;min-width:76px;margin:0;padding:0 36px 0 14px;border:0;border-radius:var(--r-md);background:var(--sheet);box-shadow:inset 0 0 0 1px var(--rule-2);font:600 14px/1 var(--font);color:var(--ink);cursor:pointer}
.select select:disabled{color:var(--ink-4);cursor:default}
.select::after{content:'';position:absolute;right:12px;top:50%;width:16px;height:16px;margin-top:-8px;background:var(--ink-3);-webkit-mask:var(--chevron) center/contain no-repeat;mask:var(--chevron) center/contain no-repeat;pointer-events:none}
.switch{display:inline-flex;align-items:center;gap:10px;height:44px;font:500 13px/1 var(--font);color:var(--ink-2)}
.switch i{position:relative;width:44px;height:26px;border-radius:13px;background:var(--rule-2);flex:none;transition:background .15s}
.switch i::after{content:'';position:absolute;left:3px;top:3px;width:20px;height:20px;border-radius:50%;background:var(--sheet);box-shadow:0 1px 3px rgba(29,26,22,.25);transition:left .15s}
.switch[aria-pressed=true] i{background:var(--jade)}
.switch[aria-pressed=true] i::after{left:21px}
.seal{display:inline-block;width:30px;height:30px;flex:none;border-radius:5px;background:var(--cinnabar);color:#FFF6EC;font:600 17px/30px var(--font);text-align:center;box-shadow:inset 0 0 0 2.5px var(--cinnabar),inset 0 0 0 3.5px rgba(255,246,236,.55)}
/* The brand mark (docs/brand.md): mark-small at 30 px in the song head, mark at 36 px in the drawer header. */
.brandMark{display:block;flex:none;width:30px;height:30px;color:var(--cinnabar);overflow:visible}
.sealButton .brandMark{width:36px;height:36px}
.seal.muted{background:var(--sunk);color:var(--ink-2);box-shadow:none;width:26px;height:26px;font-size:14px;line-height:26px}
.eyebrow{font:600 11px/1.5 var(--font);letter-spacing:.18em;color:var(--ink-3);margin-bottom:14px;display:flex;align-items:center;gap:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hint{font:400 12px/1.5 var(--font);color:var(--ink-3);margin-top:10px}

/* ---------- App frame ---------- */
.app{position:fixed;left:0;right:0;top:0;height:var(--app-height,100dvh);display:flex;flex-direction:column;background:var(--paper)}
.immersive .app{top:var(--app-top,0px);height:var(--app-height,100dvh)}
.lesson{position:relative;flex:1 1 0;min-height:0;background:var(--sheet);overflow:hidden}
.stage{position:absolute;inset:0;overflow:hidden}
.staffWrap{position:absolute;inset:0}
#notationFrame{display:block;width:100%;height:100%;border:0;background:var(--sheet)}
.lesson.libraryEmpty .staffWrap{visibility:hidden}

/* Empty library */
.empty{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;text-align:center;background:var(--sheet)}
.empty h2{font:600 18px/1.3 var(--font);letter-spacing:.06em;margin-top:18px}
.empty>p{font:400 14px/1.6 var(--font);color:var(--ink-3);margin-top:6px}
.emptyActions{display:flex;gap:12px;margin-top:22px}
.btn.wide{min-width:172px;height:48px;font-size:14px;color:var(--ink);background:var(--paper-raised)}
.empty .hint{margin-top:14px}

/* ---------- 锦鲤池 koi (docs/fish-koi-a.md): painted koi in parts; each part sways as its own layer ---------- */
.koi{position:relative;display:block;width:140px}
.koi .kp{position:absolute;inset:0;display:block}
.koi .kp svg{display:block;width:100%;height:100%;overflow:visible}
.koi .kTail{animation:koiTail 1.7s ease-in-out infinite alternate}
.koi .kFinL{animation:koiFinL 2.6s ease-in-out infinite alternate}
.koi .kFinR{animation:koiFinR 2.6s ease-in-out -.5s infinite alternate}
.koiSwim{position:relative;display:block;animation:koiDrift 8s ease-in-out infinite alternate}
/* The koi's soft shadow on the pond floor, just beneath it: it gives the paper water its depth. */
.koiFloor{position:absolute;left:3px;top:8px;display:block;width:100%;aspect-ratio:2.797;opacity:.08;pointer-events:none}
@keyframes koiTail{from{transform:rotate(-9deg)}to{transform:rotate(9deg)}}
@keyframes koiFinL{from{transform:rotate(-15deg)}to{transform:rotate(5deg)}}
@keyframes koiFinR{from{transform:rotate(15deg)}to{transform:rotate(-5deg)}}
@keyframes koiDrift{from{transform:translate(-3px,-5px) rotate(-2.5deg)}to{transform:translate(3px,5px) rotate(2.5deg)}}
/* Ambient motion runs only while idle: every pond carries .ambient, governed by the single contract at the end of
   this file (body.ambient-off / ambient-quiet / ambient-hidden / cloudLocked / immersive / reduced motion). */

/* Empty library: two koi around a lotus leaf, a slow ripple */
.emptyPond{position:relative;width:380px;height:200px;margin-bottom:2px}
.emptyPond .epLeaf{position:absolute;left:122px;top:38px;width:150px;aspect-ratio:1.17;transform:rotate(-16deg)}
.emptyPond .epBud{position:absolute;left:250px;top:-8px;height:104px;aspect-ratio:.29;transform:rotate(14deg)}
.emptyPond .epFish{position:absolute;display:block}
.emptyPond .epMark{position:absolute;left:338px;top:150px;width:40px;height:40px;opacity:.94}
.emptyPond .epFish.a{left:-6px;top:6px;transform:rotate(196deg) scaleY(-1)}
.emptyPond .epFish.a .koi{width:122px}
.emptyPond .epFish.b{left:212px;top:138px;transform:rotate(-14deg) scaleY(-1)}
.emptyPond .epFish.b .koi{width:110px}
.emptyPond .epFish.b .koiSwim{animation-duration:9.5s;animation-delay:-4s}
.emptyPond .epRing{position:absolute;left:197px;top:104px;width:340px;height:340px;margin:-170px;opacity:0;--ring-w:.42;animation:pondRing 10s cubic-bezier(.2,.6,.3,1) infinite}
.emptyPond .epRing.late{animation-delay:-5s}
.emptyPond .epRing svg{display:block;width:100%;height:100%}
@keyframes pondRing{0%{transform:scale(.2);opacity:0}14%{opacity:.32}100%{transform:scale(1);opacity:0}}
.immersive .emptyPond{display:none}
/* 小鱼动效 off: the pond rests like a painting (as under reduced motion). */
.ambient-off .emptyPond .epRing{opacity:.22;transform:scale(.6)}.ambient-off .emptyPond .epRing.late{opacity:0}

/* Completion: a koi leaps out of the water line (the score's bottom edge) with an ink splash and ripples (≤ 2 s,
   never blocks input). 鲤鱼跳龙门 — the carp that leaps the dragon gate. */
.celebration{position:absolute;left:0;right:0;bottom:0;height:250px;z-index:3;overflow:hidden;pointer-events:none;visibility:hidden}
.celebration.run{visibility:visible}
.celebration .leap{position:absolute;left:0;bottom:0;width:0;height:0;transform:translateX(31vw)}
.celebration .leapY{position:absolute;left:0;top:0;transform:translateY(90px)}
.celebration .leapKoi{position:absolute;left:-82px;top:-46px;width:164px;transform:rotate(-56deg)}
.celebration.run .leap{animation:leapX 1.2s linear .05s both}
.celebration.run .leapY{animation:leapY 1.2s .05s both}
.celebration.run .leapKoi{animation:leapTurn 1.2s .05s both}
.celebration .koi .leapTail{animation:none}
.celebration.run .koi .leapTail{animation:koiFlick .2s ease-in-out .05s 6 alternate both}
@keyframes leapX{from{transform:translateX(31vw)}to{transform:translateX(61vw)}}
@keyframes leapY{0%{transform:translateY(90px);animation-timing-function:cubic-bezier(.18,.72,.42,1)}50%{transform:translateY(-148px);animation-timing-function:cubic-bezier(.58,0,.82,.28)}100%{transform:translateY(96px)}}
@keyframes leapTurn{0%{transform:rotate(-56deg)}50%{transform:rotate(2deg)}100%{transform:rotate(60deg)}}
@keyframes koiFlick{from{transform:rotate(-12deg)}to{transform:rotate(12deg)}}
.celebration .splash{position:absolute;bottom:0;width:0;height:0}
.celebration .splash.a{left:31%}.celebration .splash.b{left:61%}
.celebration .splash i{position:absolute;left:0;top:0;width:0;height:0}
.celebration .splash i::before{content:'';position:absolute;left:-4px;top:-5px;width:8px;height:10px;border-radius:50% 50% 50% 50%/60% 60% 40% 40%;background:var(--ink);opacity:0}
.celebration .splash i:nth-child(1){transform:rotate(-66deg) scale(.75)}
.celebration .splash i:nth-child(2){transform:rotate(-40deg) scale(1.2)}
.celebration .splash i:nth-child(3){transform:rotate(-16deg) scale(.9)}
.celebration .splash i:nth-child(4){transform:rotate(10deg) scale(1.3)}
.celebration .splash i:nth-child(5){transform:rotate(34deg) scale(.8)}
.celebration .splash i:nth-child(6){transform:rotate(60deg) scale(1.05)}
.celebration .splash i:nth-child(3n)::before{background:var(--cinnabar)}
.celebration.run .splash.a i::before{animation:splash .64s cubic-bezier(.12,.72,.3,1) .1s both}
.celebration.run .splash.b i::before{animation:splash .64s cubic-bezier(.12,.72,.3,1) 1.14s both}
.celebration.run .splash i:nth-child(even)::before{animation-duration:.56s}
@keyframes splash{0%{transform:translateY(-4px) scale(.45);opacity:0}14%{opacity:.82}100%{transform:translateY(-62px) scale(.85);opacity:0}}
.celebration .cRing{position:absolute;bottom:-18px;width:200px;height:36px;margin-left:-100px;border-radius:50%;box-shadow:inset 0 0 0 1.5px rgba(29,26,22,.42);opacity:0}
.celebration .cRing.a{left:31%}.celebration .cRing.b{left:61%}
.celebration.run .cRing.a{animation:cRing .75s ease-out .08s both}
.celebration.run .cRing.a.late{animation-delay:.24s}
.celebration.run .cRing.b{animation:cRing .66s ease-out 1.14s both}
.celebration.run .cRing.b.late{animation-delay:1.3s}
@keyframes cRing{0%{transform:scale(.18);opacity:0}24%{opacity:1}100%{transform:scale(1);opacity:0}}

/* ---------- Deck: every control at the bottom (Q1) ---------- */
.deck{flex:none;position:relative;background:var(--paper) var(--fibre);border-top:1px solid var(--rule);padding:0 var(--safe-r) 0 var(--safe-l)}
.immersive .deck{display:none}
/* 鱼影: when the room is quiet a koi's shadow drifts beneath the rice paper of the deck, behind every control and never
   over the score or the keys: one crossing a minute (26 s), alternating direction and lane, pure CSS (120 s cycle, the
   first after 20 s). Practice or listening pause it and fade it out in .35 s (body.ambient-quiet, 15 s window, set by
   syncAmbientQuiet() in app.js); 小鱼动效 off and reduced motion remove it; fullscreen hides the deck. */
.deckPond{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none;opacity:.15;transition:opacity .35s ease-out}
.ambient-quiet .deckPond,.ambient-hidden .deckPond{opacity:0}
.ambient-off .deckPond{display:none}
.deckPond .deckKoi{position:absolute;left:100%;top:40px;display:block;animation:deckSwim 120s linear 20s infinite both}
.deckPond .deckKoiTurn{display:block;transform:scaleX(-1);animation:deckTurn 120s linear 20s infinite both,deckBeat 60s linear 20s infinite}
/* Its tail and fins move only while it crosses (two 26 s crossings in the 120 s cycle): out of sight they rest. deckBeat
   (every 60 s, in step with deckSwim and paused with it) marks each crossing; app.js restartDeckWag() adds .wag again. */
.deckPond .koi .kp{animation:none}
.deckPond .koi.wag .kTail{animation:koiTail 1.7s ease-in-out 16 alternate}
.deckPond .koi.wag .kFinL{animation:koiFinL 2.6s ease-in-out 10 alternate}
.deckPond .koi.wag .kFinR{animation:koiFinR 2.6s ease-in-out -.5s 10 alternate}
@keyframes deckBeat{from{opacity:1}to{opacity:1}}
.deckPond .koi{width:136px}
@keyframes deckSwim{0%{transform:translate(calc(0 * (100vw + 260px)),0px) rotate(0deg)}3.467%{transform:translate(calc(-0.16 * (100vw + 260px)),-9px) rotate(5deg)}6.933%{transform:translate(calc(-0.32 * (100vw + 260px)),-15px) rotate(-1deg)}10.4%{transform:translate(calc(-0.48 * (100vw + 260px)),-6px) rotate(-6deg)}13.867%{transform:translate(calc(-0.64 * (100vw + 260px)),8px) rotate(-3deg)}17.333%{transform:translate(calc(-0.8 * (100vw + 260px)),10px) rotate(4deg)}21.667%{transform:translate(calc(-1 * (100vw + 260px)),0px) rotate(6deg)}49.9%{transform:translate(calc(-1 * (100vw + 260px)),8px) rotate(0deg)}50%{transform:translate(calc(-1 * (100vw + 260px)),8px) rotate(0deg)}53.467%{transform:translate(calc(-0.84 * (100vw + 260px)),16px) rotate(4deg)}56.933%{transform:translate(calc(-0.68 * (100vw + 260px)),22px) rotate(1deg)}60.4%{transform:translate(calc(-0.52 * (100vw + 260px)),14px) rotate(-5deg)}63.867%{transform:translate(calc(-0.36 * (100vw + 260px)),0px) rotate(-4deg)}67.333%{transform:translate(calc(-0.2 * (100vw + 260px)),-2px) rotate(3deg)}71.667%{transform:translate(calc(0 * (100vw + 260px)),8px) rotate(5deg)}100%{transform:translate(0,8px) rotate(5deg)}0%,21.667%,50%,71.667%,100%{opacity:0}1.733%,19.933%,51.733%,69.933%{opacity:1}}
@keyframes deckTurn{0%,49.9%{transform:scaleX(-1)}50%,100%{transform:scaleX(1)}}
.rail{position:relative;z-index:2;height:20px;display:flex;align-items:center;gap:12px;padding:0 16px;font:500 11px/1 var(--font);color:var(--ink-3)}
.rail .t{width:36px;flex:none}
.rail .total{text-align:right}
.rail .vr{height:12px}
.seek{flex:1;position:relative;height:20px;min-width:80px}
.seek .track{position:absolute;left:0;right:0;top:9px;height:2px;background:var(--rule-2);border-radius:1px}
.seek .fill{position:absolute;left:0;top:9px;width:0;max-width:100%;height:2px;background:var(--ink);border-radius:1px;pointer-events:none}
.seek .fill::after{content:'';position:absolute;right:-17px;top:-6px;width:36px;height:14px;background:var(--koi-thumb) center/contain no-repeat}
#seek{position:absolute;left:-10px;top:-14px;width:calc(100% + 20px);height:44px;margin:0;padding:0;opacity:0;cursor:pointer;-webkit-appearance:none;appearance:none;background:transparent}
#seek::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:44px}
#seek:disabled{cursor:default}
.seek:focus-within .track{box-shadow:0 0 0 3px rgba(29,26,22,.10)}
.sync{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;flex:none}
.sync i{width:7px;height:7px;border-radius:50%;background:var(--jade);flex:none}
.sync:is([data-kind=saving],[data-kind=checking],[data-kind=sync-pending],[data-kind=busy],[data-kind=conflict]) i{background:none;box-shadow:inset 0 0 0 1.5px var(--ink-3)}
.sync:is([data-kind=offline],[data-kind=rejected],[data-kind=auth],[data-kind=anomaly]){color:var(--cinnabar-ink)}
.sync:is([data-kind=offline],[data-kind=rejected],[data-kind=auth],[data-kind=anomaly]) i{background:var(--cinnabar)}
.rowA,.rowB{display:flex;align-items:center;padding:0 12px 0 16px}
/* Row A sits above the rail (z-index 2): the seek's 44 px touch strip reaches into row A, and row A's own controls
   (song head, 上一步, step count, 下一步) must keep their full 44 px; the gaps between them still reach the seek. */
.rowA{position:relative;z-index:3;height:50px;gap:14px;pointer-events:none}
.rowA>*{pointer-events:auto}
.rowB{position:relative;height:56px;gap:10px;padding-right:8px}
.rowB::before{content:'';position:absolute;left:16px;right:16px;top:0;height:1px;background:var(--rule)}
.songhead{display:flex;align-items:center;gap:11px;height:44px;width:236px;flex:none;text-align:left;border-radius:var(--r-md);padding-right:8px;min-width:0}
.songhead .titles{min-width:0;display:block}
.songhead b{display:block;font:600 16px/1.25 var(--font);letter-spacing:.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.songhead small{display:block;font:400 11px/1.35 var(--font);color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.prompt{flex:1 1 0;min-width:0;display:flex;align-items:center;gap:8px;font:500 15px/1.3 var(--font);color:var(--ink);white-space:nowrap;overflow:hidden}
.prompt::before{content:'';width:8px;height:8px;border-radius:50%;flex:none;background:var(--ink-3)}
.prompt[data-tone=x]::before{background:var(--cinnabar)}
.prompt[data-tone=ok]::before{background:var(--jade)}
.prompt[data-tone=play]::before{width:0;height:0;border-radius:0;background:none;border-left:8px solid var(--ink);border-top:5px solid transparent;border-bottom:5px solid transparent}
.prompt>span{flex:none}
.prompt>span:last-child{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
.prompt em{font-style:normal;font-weight:700;font-size:14px;padding:2px 6px;border-radius:5px}
.prompt em.l{color:var(--left-ink);background:var(--left-wash)}
.prompt em.r{color:var(--right-ink);background:var(--right-wash)}
.prompt em.x{color:var(--cinnabar-ink);background:var(--cinnabar-wash)}
.prompt .sep{color:var(--ink-4);font-weight:400}
.prompt .soft{color:var(--ink-3);font-weight:400}
.speed{display:inline-flex;align-items:center;gap:12px;font:500 12px/1 var(--font);color:var(--ink-3);flex:none}
output{font:600 13px/1 var(--font);color:var(--ink);min-width:38px}
.midi{display:flex;align-items:center;flex:0 1 auto;min-width:0;margin-left:auto}
.midiButton{display:block;height:44px;max-width:260px;min-width:0;padding:0 12px;border-radius:var(--r-md);font:500 13px/44px var(--font);color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}
.midiButton::before{content:'';display:inline-block;vertical-align:middle;width:8px;height:8px;margin:-2px 8px 0 1px;border-radius:50%;background:none;box-shadow:inset 0 0 0 1.5px var(--ink-3)}
.midiButton::after{content:attr(data-device);margin-left:6px;font:400 12px/44px var(--font);color:var(--ink-3)}
.midiButton[data-status=idle]{box-shadow:inset 0 0 0 1px var(--rule-2);background:var(--paper-raised)}
.midiButton[data-status=idle]::before{width:20px;height:20px;margin:-3px 7px 0 -2px;border-radius:0;box-shadow:none;background:var(--ink-2);-webkit-mask:var(--piano) center/contain no-repeat;mask:var(--piano) center/contain no-repeat}
.midiButton[data-status=connected]::before{background:var(--jade);box-shadow:0 0 0 3px var(--jade-wash)}
.midiButton:is([data-status=disconnected],[data-status=error],[data-status=unsupported])::before{background:var(--cinnabar);box-shadow:none}
.midiButton:disabled{color:var(--ink-3)}
.cluster.tools .btn{padding:0 11px}
#play{width:108px;padding:0 12px}
.speed .range{width:112px}
.midiButton[data-device=""]::after{display:none}
.eyebrowName{letter-spacing:.02em;min-width:0;overflow:hidden;text-overflow:ellipsis}

/* ---------- Keyboard: A0–C8, 112 px, cinnabar key-slip felt ---------- */
.keyboard{position:relative;flex:none;display:flex;height:calc(var(--kb-h) + var(--felt) + var(--safe-b));padding:var(--felt) var(--safe-r) var(--safe-b) var(--safe-l);background:var(--keybed);isolation:isolate;touch-action:none;-webkit-user-select:none;user-select:none}
.keyboard::before{content:'';position:absolute;left:0;right:0;top:0;height:var(--felt);background:linear-gradient(var(--cinnabar),#8E2C1F);z-index:4}
.key{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;margin:0;border:0;font:600 9px/1 var(--font);touch-action:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;overflow:visible}
.key:focus-visible{outline:2px solid var(--ink);outline-offset:-3px}
.whiteKey{flex:1 1 0;min-width:0;height:100%;padding:0 0 10px;color:var(--ink-3);border-right:1px solid #D3C9B7;border-radius:0 0 4px 4px;background:linear-gradient(180deg,#EDE7DA 0,#FFFDF7 14%,#FFFDF7 84%,#F3EDE1 100%);box-shadow:inset 0 -6px 0 #E6DECE;z-index:1}
.whiteKey:last-child{border-right:0}
.blackKey{position:absolute;top:var(--felt);height:calc(var(--kb-h) * .63);padding:0 0 7px;z-index:2;color:#F4EFE4;border-radius:0 0 3px 3px;background:linear-gradient(90deg,#14120F,#2E2A25 55%,#171511);box-shadow:inset 0 -5px 0 #0A0908,0 3px 3px rgba(0,0,0,.32)}
.whiteKey{container-type:inline-size}.key .lb{letter-spacing:.02em;pointer-events:none;white-space:nowrap;font-size:clamp(5px,38cqi,9px)}@container (max-width:14px){.key .lb{letter-spacing:-.02em}}@container (max-width:10px){.key .lb{display:none}}
.key .lb.c4{color:var(--ink-2);font-weight:700}
.key .lb.a0{opacity:.6}
.key .nm{display:flex;flex-direction:column;align-items:center;pointer-events:none}
.key.matched .nm::before{content:'';width:5px;height:5px;border-radius:50%;background:currentColor}
.key.wrong .nm::before{content:'';width:8px;height:2px;border-radius:1px;background:currentColor;opacity:.9}
.whiteKey.target.low{background:linear-gradient(180deg,var(--left-key-top),var(--left-key) 70%);box-shadow:inset 0 -6px 0 var(--left);color:#0F2A55;border-right-color:#5C80BD}
.blackKey.target.low{background:linear-gradient(90deg,#22477F,#3A68B3 55%,#26508F);box-shadow:inset 0 -5px 0 #1A3766,0 3px 3px rgba(0,0,0,.32);color:#fff}
.whiteKey.target.high{background:linear-gradient(180deg,var(--right-key-top),var(--right-key) 70%);box-shadow:inset 0 -6px 0 var(--right);color:#4A2B00;border-right-color:#CF953F}
.blackKey.target.high{background:linear-gradient(90deg,#9A5C0B,#D08A27 55%,#A6650F);box-shadow:inset 0 -5px 0 #7A4806,0 3px 3px rgba(0,0,0,.32);color:#fff}
.whiteKey.matched{background:linear-gradient(180deg,#6FAE90 0,var(--ok-key-top) 10%,var(--ok-key) 70%);box-shadow:inset 0 -3px 0 var(--jade),inset 0 5px 6px -4px rgba(0,0,0,.25);color:#0B3A26;border-right-color:#479473;transform:translateY(1px)}
.blackKey.matched{background:linear-gradient(90deg,#255F46,#3F8A69 55%,#2A684D);box-shadow:inset 0 -3px 0 #1B4D38,inset 0 5px 6px -4px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.3);color:#fff;transform:translateY(1px)}
.whiteKey.wrong{background:linear-gradient(180deg,#C98375 0,var(--wrong-key-top) 10%,var(--wrong-key) 70%);box-shadow:inset 0 -3px 0 var(--cinnabar),inset 0 5px 6px -4px rgba(0,0,0,.25);color:#fff;border-right-color:#B9503F;transform:translateY(1px)}
.blackKey.wrong{background:linear-gradient(90deg,#7E2619,#B23A29 55%,#8A2B1E);box-shadow:inset 0 -3px 0 #5E1C12,inset 0 5px 6px -4px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.3);color:#fff;transform:translateY(1px)}
.keyboard.audition .whiteKey.target{transform:translateY(1px)}
.keyboard.audition .whiteKey.target.low{box-shadow:inset 0 -3px 0 var(--left),inset 0 5px 6px -4px rgba(0,0,0,.22)}
.keyboard.audition .whiteKey.target.high{box-shadow:inset 0 -3px 0 var(--right),inset 0 5px 6px -4px rgba(0,0,0,.18)}
.keyboard.audition .blackKey.target{transform:translateY(1px)}
/* Correct note: a soft jade water ripple from the key's lower third (CSS only, 300 ms, no layout shift) */
.key.matched::after{content:'';position:absolute;left:50%;bottom:22%;width:26px;height:26px;margin-left:-13px;border-radius:50%;box-shadow:0 0 0 2px rgba(46,125,91,.55),inset 0 0 6px rgba(140,200,171,.5);opacity:0;pointer-events:none;animation:ripple .3s ease-out}
.blackKey.matched::after{bottom:26%;width:20px;height:20px;margin-left:-10px;box-shadow:0 0 0 2px rgba(140,200,171,.7)}
@keyframes ripple{0%{opacity:.95;transform:scale(.3)}100%{opacity:0;transform:scale(1.55)}}

/* ---------- Fullscreen: four systems + keys; floating dock over the score's bottom edge ---------- */
.dock{position:absolute;left:50%;bottom:10px;z-index:40;display:none;align-items:center;gap:2px;min-height:0;padding:6px;border-radius:18px;background:rgba(251,248,241,.94);-webkit-backdrop-filter:blur(14px) saturate(1.1);backdrop-filter:blur(14px) saturate(1.1);box-shadow:var(--sh-float),0 0 0 1px rgba(203,191,171,.75);white-space:nowrap}
.immersive .fullscreenDock{display:flex;position:absolute;opacity:0;visibility:hidden;pointer-events:none;transform:translate(-50%,6px);transition:opacity .18s,transform .18s,visibility 0s linear .18s}
.immersive.toolsVisible .fullscreenDock{opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0);transition:opacity .18s,transform .18s,visibility 0s}
/* Dock = the focus subset of row B in the same order: [左手|右手] [‹ ›] [play/pause] [等待我弹奏] [− 85% +] [退出全屏]. */
.fullscreenDock .dockPages{gap:0}
.fullscreenDock .vr{height:26px;margin:0 6px}
.fullscreenDock .seg{background:none}
.fullscreenDock .btn.primary{width:56px;padding:0;border-radius:12px}
.stepper{display:inline-flex;align-items:center;height:44px;border-radius:12px;background:var(--sunk);flex:none}
.stepper .btn.icon{color:var(--ink)}
.stepper .btn.icon:disabled{color:var(--ink-4)}
.stepper output{min-width:50px;text-align:center;font:600 14px/1 var(--font);color:var(--ink)}

/* ---------- 设置 sheet (normal mode only): rises from the deck over the score; deck and keys stay usable ---------- */
.sheetScrim{position:absolute;inset:0;z-index:29;background:rgba(43,34,22,.16);animation:fadeIn .2s ease-out}
.sheet{position:absolute;left:calc(16px + var(--safe-l));right:calc(16px + var(--safe-r));bottom:0;z-index:30;max-height:calc(100% - 12px);overflow:auto;overscroll-behavior:contain;background:var(--paper-raised) var(--fibre);border-radius:var(--r-xl) var(--r-xl) 0 0;box-shadow:var(--sh-sheet),0 0 0 1px var(--rule);padding:0 28px 4px;animation:sheetIn .26s cubic-bezier(.2,.8,.2,1)}
.immersive .sheet,.immersive .sheetScrim{display:none!important}
@keyframes sheetIn{from{transform:translateY(28px);opacity:.3}to{transform:none;opacity:1}}
.sheet .grab{width:40px;height:4px;border-radius:2px;background:var(--rule-2);margin:8px auto 0}
.sheet>header{display:flex;align-items:center;justify-content:space-between;height:56px;border-bottom:1px solid var(--rule)}
.sheet h2{font:600 18px/1.2 var(--font);letter-spacing:.06em}
.sheet header p{font:400 12px/1.4 var(--font);color:var(--ink-3);margin-top:2px}
.sheet header .btn.icon{margin-right:-12px}
.sheetGrid{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr) minmax(0,1.12fr)}
.sheetGrid>.sec{min-width:0;padding:16px 22px 16px;border-left:1px solid var(--rule)}
.sheetGrid>.sec:nth-child(3n+1){border-left:0;padding-left:0}
.sheetGrid>.sec:nth-child(3n){padding-right:0}
.sheetGrid>.sec:nth-child(n+4){border-top:1px solid var(--rule)}
.sec .eyebrow{margin-bottom:8px}
.sec .line{display:flex;align-items:center;gap:12px;min-height:44px;min-width:0}
.sec .line+.line{margin-top:6px}
.sec .lbl{flex:none;width:var(--lbl,56px);font:500 13px/1.3 var(--font);color:var(--ink-2)}
.secPractice{--lbl:92px}
.sec .soft{min-width:0;font:400 12px/1.4 var(--font);color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sec .soft:empty{display:none}
.sec .hint{margin-top:6px}
.sec .clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.sec .switch{font-size:13px;color:var(--ink-3)}
.sec .switch[aria-pressed=true]{color:var(--ink-2)}
.sec .seg button{padding:0 13px}
.sec .select select{min-width:96px}
.secPractice .select select{min-width:84px}
.secSound .select:has(select[hidden]){display:none}
.deviceRow .select select{max-width:150px;text-overflow:ellipsis}
.deviceRow .btn.outline{margin-left:auto;padding:0 12px 0 10px;gap:5px}
.input{height:44px;width:84px;margin:0;padding:0 12px;border:0;border-radius:var(--r-md);background:var(--sheet);box-shadow:inset 0 0 0 1px var(--rule-2);font:600 16px/1 var(--font);color:var(--ink);-moz-appearance:textfield}
.input::-webkit-inner-spin-button,.input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.input:focus{outline:none;box-shadow:inset 0 0 0 1.5px var(--ink),0 0 0 4px rgba(29,26,22,.06)}
.slideRow{flex-wrap:nowrap;font:500 13px/1 var(--font);color:var(--ink-2)}
.slideRow .range.wide{max-width:none}
.pair{display:flex;gap:8px}
.pair .btn{flex:1 1 0;min-width:0;padding:0 8px}
.acct{display:flex;flex-wrap:wrap;gap:2px 4px;margin-left:-8px}
.acct .btn{padding:0 8px;font-size:13px;color:var(--ink)}
.acct .btn .ic{color:var(--ink-3)}
.acct .btn.dangerText,.acct .btn.dangerText .ic{color:var(--cinnabar-ink)}

/* ---------- Library drawer ---------- */
.drawerShade{position:fixed;inset:0;z-index:60;display:none;background:var(--scrim)}
.drawerShade.open{display:block;animation:fadeIn .2s ease-out}
.drawer{position:absolute;left:0;top:0;bottom:0;width:min(700px,100%);display:flex;flex-direction:column;padding-left:var(--safe-l);background:var(--paper-raised) var(--fibre);box-shadow:24px 0 60px rgba(29,26,22,.18),1px 0 0 var(--rule);animation:drawerIn .24s cubic-bezier(.2,.8,.2,1)}
@keyframes drawerIn{from{transform:translateX(-36px);opacity:.4}to{transform:none;opacity:1}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.drawer>header{position:relative;z-index:1;height:68px;flex:none;display:flex;align-items:center;gap:10px;padding:0 12px 0 16px;border-bottom:1px solid var(--rule)}
/* A 青海波 wave band fades into the header from the right; a still ink vignette (lotus, two koi) rests at the foot of the song list. */
.drawer>header::after{content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;background:var(--seigaiha) right -12px top -6px/36px 18px;opacity:.07;-webkit-mask-image:linear-gradient(270deg,#000 0,rgba(0,0,0,.6) 22%,transparent 58%);mask-image:linear-gradient(270deg,#000 0,rgba(0,0,0,.6) 22%,transparent 58%)}
.drawer .cols{position:relative;z-index:1}
.drawerPond{position:absolute;left:var(--safe-l);bottom:0;width:410px;height:290px;z-index:0;pointer-events:none;overflow:hidden}
.drawerPond svg{position:absolute;display:block}
.drawerPond .dpLeaf{left:-58px;bottom:-62px;width:250px;aspect-ratio:1.222;transform:rotate(-28deg);opacity:.68}
.drawerPond .dpBud{left:150px;bottom:18px;height:118px;aspect-ratio:.29;transform:rotate(16deg);opacity:.9}
.drawerPond .dpRing{left:232px;bottom:44px;width:176px;height:176px;--ring-w:.62;opacity:.16}
.drawerPond .dpKoi.a{left:262px;bottom:162px;width:118px;aspect-ratio:2.509;transform:rotate(166deg) scaleY(-1)}
.drawerPond .dpKoi.b{left:236px;bottom:56px;width:108px;aspect-ratio:2.473;transform:rotate(-12deg) scaleY(-1)}
.drawer>header h2{font:600 20px/1 var(--font);letter-spacing:.06em}
.drawer>header p{font:400 12px/1 var(--font);color:var(--ink-3);margin-left:4px}
.sealButton{width:44px;height:44px;display:grid;place-items:center;flex:none;border-radius:var(--r-md)}
.drawer .cols{flex:1;min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 290px}
.drawer .songsCol{display:flex;flex-direction:column;min-height:0;padding:18px 24px calc(20px + var(--safe-b)) 28px}
.drawer .sideCol{display:flex;flex-direction:column;min-height:0;overflow:auto;padding:18px 24px calc(18px + var(--safe-b));border-left:1px solid var(--rule)}
.library{display:flex;flex-direction:column;min-height:0;flex:0 1 auto;overflow:auto;overscroll-behavior:contain}
.libraryEmptyNote{padding:18px 16px;font:400 13px/1.6 var(--font);color:var(--ink-3);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.songRow{display:flex;align-items:center;gap:2px;min-height:60px;flex:none;position:relative;padding-right:4px;border-bottom:1px solid var(--rule)}
.songRow:first-child{border-top:1px solid var(--rule)}
.songRow .song{flex:1 1 0;min-width:0;min-height:58px;display:flex;flex-direction:column;justify-content:center;padding:8px 8px 8px 16px;text-align:left;border-radius:0}
.song strong{display:block;font:500 15px/1.3 var(--font);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* The meta wraps to a second line rather than cutting off where she is (「… · 练到第 40 步」). */
.song small{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font:400 11px/1.4 var(--font);color:var(--ink-3);margin-top:2px;overflow:hidden;overflow-wrap:break-word}
.song small .metaLine{display:block;margin-left:-1.2em}
.song small .metaPart{display:inline-block;margin-left:1.2em;white-space:nowrap}
.song small .metaSep{display:inline-block;width:1.2em;margin-left:-1.2em;text-align:center}
.songRow.active{background:var(--sheet)}
.songRow.active::before{content:'';position:absolute;left:0;top:10px;bottom:10px;width:3px;border-radius:2px;background:var(--cinnabar)}
.songRow.active strong{font-weight:650}
.songRow .btn{font-size:12px;padding:0 10px;color:var(--ink-3)}
.songRow .songDelete{color:var(--cinnabar-ink)}
.songRow .btn:disabled{color:var(--ink-4)}
.imports{display:flex;gap:10px;margin-top:16px;flex:none}
.imports button{flex:1 1 0;min-width:0;height:48px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 12px;border-radius:var(--r-md);font:500 13px/1 var(--font);color:var(--ink);box-shadow:inset 0 0 0 1px var(--rule-2);background:var(--sheet);white-space:nowrap}
.imports button:disabled{color:var(--ink-4)}
.songsCol>.hint{flex:none}
.dsec{flex:none}
.dsec+.dsec{margin-top:18px;padding-top:16px;border-top:1px solid var(--rule)}
.dsec .eyebrow{margin-bottom:10px}
.trackList{display:flex;flex-direction:column}
.trackOption{display:flex;align-items:center;gap:12px;min-height:44px;font:500 14px/1.3 var(--font);cursor:pointer}
.trackOption input{-webkit-appearance:none;appearance:none;width:22px;height:22px;margin:0;flex:none;border-radius:6px;box-shadow:inset 0 0 0 1.5px var(--ink-4);background:var(--sheet) center/16px no-repeat;cursor:pointer}
.trackOption input:checked{background-color:var(--ink);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFDF7' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 12.5l4 4 8-9'/%3E%3C/svg%3E");box-shadow:none}
.trackOption span{min-width:0;overflow-wrap:anywhere}
.trackOption small{display:block;font:400 11px/1.3 var(--font);color:var(--ink-3)}
.trackEmpty{font:400 12px/1.5 var(--font);color:var(--ink-3)}
.octave{display:flex;align-items:center;gap:8px}
.octave span{flex:1;text-align:center;font:600 14px/1 var(--font)}
.octave .btn{color:var(--ink)}
.statusLine,.statsLine{display:flex;align-items:baseline;gap:7px;margin-top:10px;font:400 12px/1.45 var(--font);color:var(--ink-2)}
.statusLine::before{content:'';width:7px;height:7px;border-radius:50%;flex:none;transform:translateY(-1px);background:var(--jade)}
.statusLine:is([data-kind=saving],[data-kind=checking])::before{background:none;box-shadow:inset 0 0 0 1.5px var(--ink-3)}
.statusLine:is([data-kind=busy],[data-kind=sync-pending]){color:var(--right-ink)}
.statusLine:is([data-kind=busy],[data-kind=sync-pending])::before{background:var(--right)}
.statusLine:is([data-kind=offline],[data-kind=conflict],[data-kind=auth],[data-kind=anomaly],[data-kind=rejected],[data-kind=error],[data-kind=warning]){color:var(--cinnabar-ink)}
.statusLine:is([data-kind=offline],[data-kind=conflict],[data-kind=auth],[data-kind=anomaly],[data-kind=rejected],[data-kind=error],[data-kind=warning])::before{background:var(--cinnabar)}
.statsLine{margin-top:0;align-items:center;gap:10px;font:500 14px/1.5 var(--font);color:var(--ink-2)}
.statsLine::before{content:'';width:22px;height:14px;flex:none;background:var(--ink-2);-webkit-mask:var(--fish-mark) center/contain no-repeat;mask:var(--fish-mark) center/contain no-repeat}
.statsLine:empty{display:none}
.credit{margin-top:auto;padding-top:14px;font:400 11px/1.5 var(--font);color:var(--ink-3)}
.credit a{color:var(--ink-2);display:inline-block;padding:14px 0;margin:-14px 0}/* 44 px tall targets, same line box */

/* ---------- Dialogs (above the drawer) ---------- */
.confirmShade{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:20px;background:rgba(43,34,22,.22);animation:fadeIn .16s ease-out}
.confirmCard{width:min(440px,100%);max-height:calc(var(--app-height,100dvh) - 40px);overflow:auto;padding:26px 28px 22px;border-radius:16px;background:var(--paper-raised) var(--fibre);box-shadow:var(--sh-float),0 0 0 1px var(--rule);color:var(--ink)}
.confirmCard h2{font:600 19px/1.3 var(--font);letter-spacing:.04em}
.confirmCard p{font:400 14px/1.7 var(--font);color:var(--ink-2);margin-top:10px;overflow-wrap:anywhere}
.confirmCard .renameField{display:block;font:500 12px/1 var(--font);color:var(--ink-3);margin-top:18px;letter-spacing:.06em}
.textField{display:block;width:100%;height:48px;margin:8px 0 0;padding:0 14px;border:0;border-radius:var(--r-md);background:var(--sheet);box-shadow:inset 0 0 0 1px var(--rule-2);font:500 16px/1 var(--font);color:var(--ink)}
.textField:focus{outline:none;box-shadow:inset 0 0 0 1.5px var(--ink),0 0 0 4px rgba(29,26,22,.06)}
.confirmCard .renameHint{font-size:12px;line-height:1.5;color:var(--ink-3);margin-top:8px}
.confirmCard .renameError{font-size:12px;line-height:1.5;color:var(--cinnabar-ink);min-height:18px;margin-top:8px}
.confirmActions{display:flex;justify-content:flex-end;gap:10px;margin-top:18px}
.confirmActions .btn{min-width:96px;font-size:14px}
.confirmActions .btn.primary{padding:0 18px}
.songRef{display:flex;align-items:center;gap:12px;margin-top:16px;padding:12px 14px;border-radius:var(--r-md);background:var(--sheet);box-shadow:inset 0 0 0 1px var(--rule)}
.songRef>span:last-child{min-width:0}
.songRef b{display:block;font:600 14px/1.3 var(--font);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.songRef small{display:block;font:400 11px/1.4 var(--font);color:var(--ink-3)}
.anomalyPanel{z-index:10020;background:rgba(43,34,22,.30)}
/* The anomaly card replaces the lock card behind it (one dialog, not two stacked). */
body:has(#anomalyPanel:not([hidden])) #authOverlay .authCard{visibility:hidden}
#passwordDialog,#logoutOthersDialog{z-index:84}

/* Easter egg: a little paper note over the score */
.loveNote{position:fixed;left:50%;top:40%;z-index:95;width:min(320px,calc(100vw - 32px));transform:translate(-50%,-50%);padding:24px 28px 22px;text-align:center;border-radius:16px;background:var(--paper-raised) var(--fibre);box-shadow:var(--sh-float),0 0 0 1px var(--rule);animation:noteIn .26s cubic-bezier(.2,.8,.2,1)}
.loveNote.inDrawer{left:calc((var(--safe-l) + min(700px,100vw))/2)}
.loveNote .pairGlyph{display:block;width:84px;height:84px;margin:-6px auto -4px}
.loveNote p{font:500 16px/1.7 var(--font);letter-spacing:.04em;margin-top:10px;color:var(--ink)}
.loveNote .heart{color:var(--cinnabar)}
@keyframes noteIn{from{opacity:0;transform:translate(-50%,-44%)}to{opacity:1;transform:translate(-50%,-50%)}}

/* ---------- Toast: above every layer (K15), including dialogs and the login ---------- */
.toast{position:fixed;inset:auto;top:calc(14px + var(--safe-t));left:50%;transform:translateX(-50%);z-index:10050;display:none;align-items:center;gap:9px;width:max-content;max-width:min(720px,calc(100vw - 32px));min-height:40px;margin:0;padding:10px 18px 10px 14px;border:0;border-radius:20px;background:rgba(29,26,22,.93);color:var(--sheet);font:500 13px/1.45 var(--font);box-shadow:0 8px 24px rgba(29,26,22,.24);overflow:visible;pointer-events:none}
.toast.show{display:flex;animation:toastIn .16s ease-out}
.toast::before{content:'';width:7px;height:7px;border-radius:50%;flex:none;background:#BDB4A4}
.toast[data-tone=ok]::before{background:#7FC7A4}
.toast[data-tone=error]::before{background:#E07A66}
.toast small{color:#BDB4A4;font:400 12px/1.4 var(--font)}
@keyframes toastIn{from{opacity:0;transform:translate(-50%,-6px)}to{opacity:1;transform:translateX(-50%)}}

/* ---------- Narrower viewports keep every control reachable (the target is 1194x834) ---------- */
@media (max-width:1100px){.rowA,.rowB{height:auto;flex-wrap:wrap;row-gap:2px;padding-top:3px;padding-bottom:3px}.rowA .prompt{order:5;flex:1 1 100%;min-height:40px}.songhead{width:auto;flex:1 1 200px;max-width:320px}.rowA>.vr{display:none}.rowA .midi{margin-left:auto}.rowB .spacer{display:none}.rowB .cluster.tools{margin-left:auto}.rowB>.vr{display:none}}
@media (max-width:1000px){.sheetGrid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.sheetGrid>.sec,.sheetGrid>.sec:nth-child(n){border-left:0;border-top:1px solid var(--rule);padding:14px 0}.sheetGrid>.sec:nth-child(even){border-left:1px solid var(--rule);padding-left:20px}.sheetGrid>.sec:nth-child(odd){padding-right:20px}.sheetGrid>.sec:nth-child(-n+2){border-top:0}}
@media (max-width:900px){.drawerPond{left:auto;right:0;transform:scaleX(-1) scale(.8);transform-origin:50% 100%}.drawer .cols{grid-template-columns:1fr;overflow:auto}.drawer .sideCol{border-left:0;border-top:1px solid var(--rule);overflow:visible}.drawer .songsCol{min-height:auto}}
@media (max-width:640px){.sheet{padding:0 18px 4px}.sheetGrid{grid-template-columns:minmax(0,1fr)}.sheetGrid>.sec:nth-child(n){border-left:0;padding:14px 0;border-top:1px solid var(--rule)}.sheetGrid>.sec:first-child{border-top:0}}

/* ---------- Touch and motion polish (lead plan TOP 5) ---------- */
/* iPad touch hygiene: no double-tap zoom wait anywhere, and the page itself never rubber-bands. */
html,body{touch-action:manipulation;overscroll-behavior:none}
/* Letter-spaced centred labels: the trailing letter-space is balanced by the same indent. */
.empty h2{text-indent:.06em}
.loveNote p{text-indent:.04em}
/* Tactile press (app.js wirePress): a finger presses a control in (data-pressed: 70 ms in, 160 ms out) and leaves an ink
   bloom that spreads from the touch point and fades (data-bloom, 380 ms). Transform and opacity only, no layout shift. */
.btn,.seg button,.toggle,.midiButton,.songhead,.songRow .song,.imports button,.sealButton,.count{transition:transform .16s cubic-bezier(.2,.8,.2,1)}
:is(.btn,.seg button,.toggle,.midiButton,.songhead,.songRow .song,.imports button,.sealButton,.count)[data-pressed]{transform:scale(.965);transition-duration:.07s}
.btn,.seg button,.toggle,.songhead,.songRow .song,.imports button,.sealButton,.count{position:relative;overflow:hidden}
:is(.btn,.seg button,.toggle,.songhead,.songRow .song,.imports button,.sealButton,.count)[data-bloom]::after{content:'';position:absolute;left:var(--bx,50%);top:var(--by,50%);width:var(--bs,88px);height:var(--bs,88px);margin:calc(var(--bs,88px) / -2) 0 0 calc(var(--bs,88px) / -2);border-radius:50%;background:radial-gradient(circle,rgba(29,26,22,.10) 0,rgba(29,26,22,.06) 42%,rgba(29,26,22,0) 70%);pointer-events:none;animation:inkBloom .38s cubic-bezier(.2,.8,.2,1) forwards}
:is(.btn.primary,.btn.danger)[data-bloom]::after{background:radial-gradient(circle,rgba(255,253,247,.26) 0,rgba(255,253,247,.12) 42%,rgba(255,253,247,0) 70%)}
.btn.primary.isPlaying[data-bloom]::after{background:radial-gradient(circle,rgba(29,26,22,.10) 0,rgba(29,26,22,.06) 42%,rgba(29,26,22,0) 70%)}
@keyframes inkBloom{from{opacity:1;transform:scale(.2)}to{opacity:0;transform:scale(1.6)}}
/* The 等待我弹奏 dot presses like a seal: it lands from 1.7× and a thin ring spreads to 16 px. */
.toggle i{position:relative}
.toggle[data-bloom] i{animation:dotSeal .24s cubic-bezier(.2,.8,.2,1)}
.toggle[data-bloom] i::after{content:'';position:absolute;inset:-.5px;border-radius:50%;box-shadow:0 0 0 1.5px currentColor;opacity:0;animation:dotRing .24s ease-out}
@keyframes dotSeal{from{transform:scale(1.7)}to{transform:none}}
@keyframes dotRing{from{opacity:.6;transform:scale(1)}to{opacity:0;transform:scale(1.8)}}
/* The speed thumb pulses once when it reaches or passes 100 % (a detent). */
.range input.pulse::-webkit-slider-thumb{animation:thumbPulse .16s ease-out}
.range input.pulse::-moz-range-thumb{animation:thumbPulse .16s ease-out}
@keyframes thumbPulse{from{transform:scale(1.15)}to{transform:none}}
/* Keys never feel dead: her own press (piano, touch, computer key) that is not a target, a match or a wrong note goes
   down a little in neutral paper or ink (also during audition, whose notes stay lit as targets). */
.key{transition:transform .05s linear}
.whiteKey.held:not(.target):not(.matched):not(.wrong){background:#E9E1D2;box-shadow:inset 0 4px 5px -4px rgba(0,0,0,.22),inset 0 -5px 0 #DDD3C1;transform:translateY(1px)}
.blackKey.held:not(.target):not(.matched):not(.wrong){background:#3A352E;box-shadow:inset 0 -4px 0 #0A0908,0 2px 2px rgba(0,0,0,.3);transform:translateY(1px)}
/* Layers close with a short reverse motion (app.js closeWithMotion: 160 ms, cubic-bezier(.4,0,1,1)); a closing layer is
   inert from its first frame and hidden at the end. 曲库 is already closed while its .closing layer fades. */
.drawerShade.closing{display:block;animation:layerOut .16s cubic-bezier(.4,0,1,1) forwards}
.drawerShade.closing .drawer{animation:drawerOut .16s cubic-bezier(.4,0,1,1) forwards}
.sheet.closing{animation:sheetOut .16s cubic-bezier(.4,0,1,1) forwards}
.sheetScrim.closing,.confirmShade.closing{animation:layerOut .16s cubic-bezier(.4,0,1,1) forwards}
.confirmShade.closing .confirmCard{animation:cardOut .16s cubic-bezier(.4,0,1,1) forwards}
.toast.show.closing{animation:toastOut .16s cubic-bezier(.4,0,1,1) forwards}
.loveNote.closing{animation:noteOut .16s cubic-bezier(.4,0,1,1) forwards}
@keyframes layerOut{from{opacity:1}to{opacity:0}}
@keyframes drawerOut{from{opacity:1;transform:none}to{opacity:.4;transform:translateX(-28px)}}
@keyframes sheetOut{from{opacity:1;transform:none}to{opacity:0;transform:translateY(24px)}}
@keyframes cardOut{from{transform:none}to{transform:scale(.97)}}
@keyframes toastOut{from{opacity:1;transform:translateX(-50%)}to{opacity:0;transform:translate(-50%,-6px)}}
@keyframes noteOut{from{opacity:1;transform:translate(-50%,-50%)}to{opacity:0;transform:translate(-50%,-46%)}}
/* Entering or leaving fullscreen: the score dims while it re-engraves for its new room (app.js beginRelayout), then fades
   back in 160 ms; leaving, the deck slides back in 180 ms. */
.staffWrap{transition:opacity .16s ease-out}
.staffWrap.relayout{opacity:.35;transition:none}
.deck.deckIn{animation:deckIn .18s cubic-bezier(.2,.8,.2,1)}
@keyframes deckIn{from{opacity:.4;transform:translateY(36px)}to{opacity:1;transform:none}}

/* ---------- 小鱼动效 hooks: ambient fish elements, or a container of them, carry .ambient (docs/handoff-structure.md) ----------
   .ambient-off (logic, from the 设置 switch) stops them; .ambient-quiet (UI, syncAmbientQuiet: playing, loading, an advancing
   or held step, any key, and 15 s after) and .ambient-hidden (page hidden) pause them.
   The rules reach every descendant and pseudo-element, so a pond container with animated parts is covered too. */
.ambient-off .ambient,.ambient-off .ambient *,.ambient-off .ambient::before,.ambient-off .ambient::after,.ambient-off .ambient *::before,.ambient-off .ambient *::after{animation:none!important;transition:none!important}
/* Pausing is one inherited switch: every pond part reads --amb-state (running | paused) once, through a static rule, and the
   state is set on a single ancestor (body for quiet and hidden), so a pause or resume never restyles the ponds by selector. */
.ambient,.ambient *,.ambient::before,.ambient::after,.ambient *::before,.ambient *::after{animation-play-state:var(--amb-state,running)!important}
body.ambient-quiet,body.ambient-hidden{--amb-state:paused}
/* A locked app (login, connection or anomaly overlay) and fullscreen pause the app's ponds; the login pond keeps swimming. */
.cloudLocked .app,.cloudLocked .drawerShade,.immersive .app,.immersive .drawerShade{--amb-state:paused}

/* ---------- prefers-reduced-motion: no transforms, opacity only ---------- */
@media (prefers-reduced-motion:reduce){
  .sheet,.drawer,.drawerShade.open,.confirmShade,.toast.show{animation:fadeIn .16s ease-out}
  .loveNote{animation:fadeIn .2s ease-out}
  .immersive .fullscreenDock,.immersive.toolsVisible .fullscreenDock{transform:translateX(-50%);transition:opacity .18s,visibility 0s linear .18s}
  .immersive.toolsVisible .fullscreenDock{transition:opacity .18s,visibility 0s}
  .key.matched::after{animation:fadeOut .3s ease-out;transform:scale(1.2)}
  .celebration.run i::before{animation:none;opacity:0}
  .celebration.run i{animation:none}
  .celebration .leap,.celebration.run .leap{transform:translateX(46vw);animation:none}
  .celebration .leapY,.celebration.run .leapY{transform:translateY(-118px);animation:none}
  .celebration.run .leapKoi{transform:none;animation:fadeOut 1.9s ease-out both}
  .celebration.run .leapTail{animation:none}
  .celebration.run .cRing{transform:none;animation:fadeOut 1.6s ease-out both}
  .switch i,.switch i::after{transition:none}
  .ambient,.ambient *,.ambient::before,.ambient::after,.ambient *::before,.ambient *::after{animation:none!important;transition:none!important}
  /* 锦鲤池: every koi, ripple and leaf rests in place; the deck shadow never swims. */
  .koi .kp,.koiSwim,.emptyPond .epRing,.deckPond .deckKoi{animation:none!important}
  .emptyPond .epRing{opacity:.22;transform:scale(.6)}.emptyPond .epRing.late{opacity:0}
  .deckPond{display:none}
  /* Touch and motion polish: colour only (no press scale, bloom, seal dot, thumb pulse, key travel, closing motion or dim). */
  [data-pressed]{transform:none!important}
  [data-bloom]::after,.toggle[data-bloom] i::after{content:none!important;animation:none!important}
  .toggle[data-bloom] i,.range input.pulse::-webkit-slider-thumb,.deck.deckIn{animation:none}
  .key{transition:none}
  .whiteKey.held,.blackKey.held{transform:none}
  .staffWrap.relayout{opacity:1}
}
@keyframes fadeOut{from{opacity:1}to{opacity:0}}

/* ---------- 朱笔圈点 (lane drill): 练一段 toggle (row B, after 等待我弹奏) and the passage prompt ---------- */
.drillToggle{gap:5px;padding:0 13px 0 9px}
.drillToggle .ic{width:19px;height:19px}
.drillToggle .ic .fill{fill:currentColor;stroke:none}
.prompt[data-tone=drill]::before{background:none;box-shadow:inset 0 0 0 1.5px var(--jade)}
.prompt .drillDots{display:inline-flex;align-items:center;gap:4px;margin-left:-2px}
.prompt .drillDots i{width:7px;height:7px;border-radius:50%;background:var(--ink-4)}
.prompt .drillDots i.ok{background:var(--jade)}
