/* global React, Icon, WaveBars, AILabel, AlbumCover, LANGS, BookmarkButton, ReadingModeToggle, DepthToggle, FeedbackBar, QuietAd */
const { useState, useEffect, useRef, useMemo } = React;

// ---- Song data (original fictional song) ----
// Each line's `t` is the ORIGINAL lyric in the composer's language (never translated).
// Each line's `m` map carries an interpretation per UI language (lang -> text).
// originalLang is the BCP code of the lyric text — UI surfaces this so the reader
// knows lyrics stay in the songwriter's language even when meaning is translated.
const SONG = {
  title: 'Cassette Light',
  artist: 'Vela Maren',
  album: 'Indoor Weather',
  year: 2025,
  genre: 'Indie pop',
  length: '3:42',
  seed: 0,
  originalLang: 'EN',
  summary: {
    EN: 'A quiet meditation on staying loyal to a memory longer than the memory deserves. Vela Maren uses the worn-out cassette as a metaphor for replaying a relationship — gentle, knowing, and finally letting the signal fade without bitterness.',
    ID: 'Renungan tenang tentang setia pada sebuah kenangan lebih lama dari yang sebenarnya pantas. Vela Maren memakai kaset yang sudah tipis sebagai perumpamaan: terus memutar ulang hubungan yang sudah selesai — lembut, sadar, dan akhirnya merelakannya tanpa kepahitan.',
    ES: 'Una meditación serena sobre seguir fiel a un recuerdo más tiempo del que merece. Vela Maren usa la cinta gastada como metáfora para revivir una relación: tierna, lúcida y, al fin, dejando que la señal se apague sin amargura.',
    PT: 'Uma meditação silenciosa sobre permanecer leal a uma memória mais tempo do que ela merece. Vela Maren usa a fita desgastada como metáfora para repetir um relacionamento — gentil, consciente e, no fim, deixando o sinal desaparecer sem amargura.',
    FR: 'Une méditation paisible sur la fidélité à un souvenir qui ne la mérite plus. Vela Maren se sert de la cassette usée comme d\'une métaphore : on rejoue la relation, doucement, lucidement, puis on laisse le signal s\'éteindre sans amertume.',
    JP: '記憶に必要以上に忠実でいることを静かに見つめた一曲。すり減ったカセットを比喩に、関係を繰り返し再生し、やがて静かに、苦さもなく信号を消していく姿を描く。',
  },
  themes: {
    EN: ['Memory', 'Nostalgia', 'Tape & analog', 'Loneliness as warmth', 'Communication breakdown'],
    ID: ['Memori', 'Nostalgia', 'Kaset & analog', 'Kesepian sebagai kehangatan', 'Putusnya komunikasi'],
    ES: ['Memoria', 'Nostalgia', 'Cinta y analógico', 'Soledad como calor', 'Comunicación rota'],
    PT: ['Memória', 'Nostalgia', 'Fita & analógico', 'Solidão como calor', 'Comunicação rompida'],
    FR: ['Mémoire', 'Nostalgie', 'Cassette & analogique', 'Solitude comme chaleur', 'Rupture de communication'],
    JP: ['記憶', 'ノスタルジー', 'テープとアナログ', '温もりとしての孤独', 'コミュニケーションの断絶'],
  },
  mood: {
    EN: ['Wistful', 'Tender', 'Quietly hopeful'],
    ID: ['Sendu', 'Lembut', 'Diam-diam berharap'],
    ES: ['Melancólico', 'Tierno', 'Esperanzado en voz baja'],
    PT: ['Melancólico', 'Terno', 'Esperançoso em voz baixa'],
    FR: ['Mélancolique', 'Tendre', 'Doucement plein d\'espoir'],
    JP: ['切なげ', '優しい', '静かに希望的'],
  },
  references: {
    EN: [
      { t: 'Cassette / tape metaphor', n: 'The titular cassette echoes a long lineage of analog-decay songwriting — physical fragility standing in for emotional fragility.' },
      { t: 'Porch light as waiting', n: 'A familiar Americana image, reframed for a narrator who knows the person isn\'t coming back.' },
      { t: 'Static = unspoken affection', n: 'Reuses radio noise the way earlier writers used phone-line crackle: love that didn\'t arrive cleanly.' },
    ],
    ID: [
      { t: 'Perumpamaan kaset / pita', n: 'Kaset judulnya menggemakan tradisi panjang lagu bertema pelapukan analog — kerapuhan fisik berdiri sebagai kerapuhan emosional.' },
      { t: 'Lampu teras sebagai menanti', n: 'Citra Amerikana yang akrab, ditata ulang untuk narator yang tahu orangnya tak akan kembali.' },
      { t: 'Bising = afeksi yang tak terucap', n: 'Menggunakan bising radio seperti penulis lama memakai bunyi sambungan telepon: cinta yang tak datang dengan jernih.' },
    ],
    ES: [
      { t: 'Metáfora del casete', n: 'El casete del título se inscribe en una larga tradición de canciones sobre decadencia analógica — la fragilidad física como fragilidad emocional.' },
      { t: 'Luz del porche como espera', n: 'Imagen familiar del americana, reescrita para una voz que ya sabe que la persona no vuelve.' },
      { t: 'Estática = afecto no dicho', n: 'Reutiliza el ruido de radio como otros usaron el crepitar del teléfono: amor que no llegó limpio.' },
    ],
    PT: [
      { t: 'Metáfora da fita', n: 'A fita do título dialoga com uma longa linhagem de canções sobre decadência analógica — fragilidade física como fragilidade emocional.' },
      { t: 'Luz da varanda como espera', n: 'Imagem familiar do americana, recontada por uma voz que já sabe que a pessoa não volta.' },
      { t: 'Estática = afeto não dito', n: 'Usa o chiado do rádio como autores anteriores usavam o estalido do telefone: amor que não chegou limpo.' },
    ],
    FR: [
      { t: 'Métaphore de la cassette', n: 'La cassette du titre s\'inscrit dans une longue lignée de chansons sur le délabrement analogique — fragilité physique pour fragilité émotionnelle.' },
      { t: 'Lampe du perron comme attente', n: 'Image familière de l\'americana, reprise pour une voix qui sait déjà que la personne ne reviendra pas.' },
      { t: 'Friture = affection tue', n: 'Réutilise le bruit radio comme d\'autres ont utilisé le grésillement du téléphone : un amour qui n\'est pas arrivé propre.' },
    ],
    JP: [
      { t: 'カセット／テープの比喩', n: 'タイトルのカセットは、アナログの劣化を題材にしてきた長い系譜と響き合う — 物理的な脆さが、感情の脆さとして立ち上がる。' },
      { t: '待つことのポーチライト', n: '見慣れたアメリカーナのイメージを、相手はもう戻らないと知っている語り手のために再構築する。' },
      { t: 'ノイズ＝語られない愛情', n: 'かつての書き手が電話線の雑音を使ったように、ラジオのノイズを再利用 — きれいに届かなかった愛として。' },
    ],
  },
  sections: [
    {
      label: { EN: 'Verse 1', ID: 'Bait 1', ES: 'Verso 1', PT: 'Verso 1', FR: 'Couplet 1', JP: 'バース 1' },
      lines: [
        { t: 'I keep the porch light on like I forgot the year',
          m: {
            EN: 'The narrator performs the rituals of waiting long after waiting is reasonable. The porch light is small, domestic, and quietly delusional — comfort doing the work of denial.',
            ID: 'Narator menjalankan ritual menunggu jauh setelah menunggu masuk akal. Lampu teras itu kecil, rumahan, dan diam-diam delusi — kenyamanan yang sebenarnya sedang menyangkal.',
            ES: 'La voz mantiene los rituales de esperar mucho después de que esperar tenga sentido. La luz del porche es pequeña, doméstica y silenciosamente ilusoria — el consuelo haciendo el trabajo de la negación.',
            PT: 'A voz mantém os rituais de esperar muito depois de esperar fazer sentido. A luz da varanda é pequena, doméstica e em silêncio ilusória — o conforto fazendo o trabalho da negação.',
            FR: 'Le narrateur exécute les rituels de l\'attente bien après que l\'attente soit raisonnable. La lampe du perron est petite, domestique, silencieusement illusoire — un confort qui fait le travail du déni.',
            JP: '語り手は、待つことが理に適わなくなった後も、待つための儀式を続ける。ポーチの明かりは小さく、家庭的で、密かに自己欺瞞 — 安らぎが否認の役目を果たしている。',
          } },
        { t: 'A house full of sweaters that still know your shape',
          m: {
            EN: 'Possessions retain a body the body has left. The image is tactile, not tragic — closer to muscle memory than mourning.',
            ID: 'Benda-benda menyimpan bentuk tubuh yang sudah tak ada di sana. Citra ini taktil, bukan tragis — lebih dekat ke memori otot daripada berkabung.',
            ES: 'Los objetos guardan un cuerpo que el cuerpo ya dejó. La imagen es táctil, no trágica — más cercana a la memoria muscular que al duelo.',
            PT: 'Os objetos guardam um corpo que o corpo já deixou. A imagem é tátil, não trágica — mais perto da memória muscular do que do luto.',
            FR: 'Les objets retiennent un corps que le corps a quitté. L\'image est tactile, pas tragique — plus proche de la mémoire musculaire que du deuil.',
            JP: '物が、身体が去った後の身体の形を覚えている。触覚的で、悲劇的ではない — 喪というより筋肉の記憶に近い。',
          } },
        { t: 'I burn the same coffee, I make the same shape',
          m: {
            EN: 'Repetition as a way to keep someone present. The narrator notices their own pattern, which is the first crack in it.',
            ID: 'Pengulangan sebagai cara menahan kehadiran seseorang. Narator menyadari polanya sendiri — itu retakan pertamanya.',
            ES: 'Repetir como forma de retener a alguien. La voz nota su propio patrón, y esa es la primera grieta.',
            PT: 'Repetir como forma de manter alguém presente. A voz percebe o próprio padrão — e essa é a primeira rachadura.',
            FR: 'Répéter pour garder quelqu\'un présent. Le narrateur remarque sa propre habitude — c\'est la première fissure.',
            JP: '誰かを残す手段としての反復。語り手が自分のパターンに気づく — それが最初のひび割れ。',
          } },
        { t: 'You drove like a question I never asked out loud',
          m: {
            EN: 'Recklessness reads as a kind of curiosity. The line quietly admits the narrator wanted more than they ever said.',
            ID: 'Kenekatan dibaca sebagai bentuk keingintahuan. Baris ini diam-diam mengakui narator menginginkan lebih dari yang pernah diucapkan.',
            ES: 'La temeridad se lee como curiosidad. El verso confiesa, en voz baja, que la voz quería más de lo que llegó a decir.',
            PT: 'A imprudência se lê como curiosidade. O verso confessa, baixinho, que a voz queria mais do que chegou a dizer.',
            FR: 'L\'audace se lit comme une curiosité. Le vers admet à voix basse que le narrateur voulait plus qu\'il n\'a jamais dit.',
            JP: '無謀さは一種の好奇心として読める。語り手が口にした以上に望んでいたことを、この一行はそっと認める。',
          } },
      ],
    },
    {
      label: { EN: 'Pre-chorus', ID: 'Pra-chorus', ES: 'Pre-estribillo', PT: 'Pré-refrão', FR: 'Pré-refrain', JP: 'プリコーラス' },
      lines: [
        { t: 'And I know the song by heart',
          m: {
            EN: 'A flag for self-awareness: the narrator can see the loop they\'re in.',
            ID: 'Tanda kesadaran diri: narator bisa melihat lingkaran tempatnya berada.',
            ES: 'Una señal de autoconciencia: la voz ve el bucle en el que está.',
            PT: 'Um sinal de autoconsciência: a voz vê o loop em que está.',
            FR: 'Un drapeau de conscience de soi : le narrateur voit la boucle dans laquelle il est.',
            JP: '自己認識の合図 — 語り手は自分が回っているループを見ている。',
          } },
        { t: 'But I keep mouthing every part',
          m: {
            EN: 'Knowing is not the same as stopping. A small, honest line about how recovery actually moves.',
            ID: 'Mengerti tidak sama dengan berhenti. Baris jujur tentang bagaimana pemulihan sebenarnya berjalan.',
            ES: 'Saberlo no es lo mismo que dejarlo. Verso pequeño y honesto sobre cómo se mueve de verdad la recuperación.',
            PT: 'Saber não é o mesmo que parar. Verso pequeno e honesto sobre como a recuperação se move de fato.',
            FR: 'Savoir n\'est pas s\'arrêter. Petit vers honnête sur la façon dont la guérison avance vraiment.',
            JP: '分かっていることと止められることは違う。回復が実際にはどう動くかについての、小さく正直な一行。',
          } },
      ],
    },
    {
      label: { EN: 'Chorus', ID: 'Chorus', ES: 'Estribillo', PT: 'Refrão', FR: 'Refrain', JP: 'コーラス' },
      lines: [
        { t: 'So play it back, cassette light',
          m: {
            EN: '"Cassette light" is the song\'s coined image — the faint glow of a tape deck mid-rewind, here turned into something to live by. It frames memory as a small lamp, not a sun.',
            ID: '"Cassette light" adalah citra ciptaan lagu ini — kilauan lemah dek kaset yang sedang rewind, di sini menjadi sesuatu untuk dijadikan pegangan. Memori dibingkai sebagai lampu kecil, bukan matahari.',
            ES: '"Cassette light" es la imagen acuñada por la canción — el brillo tenue de la pletina rebobinando, vuelto aquí algo por lo que vivir. Encuadra la memoria como una lámpara pequeña, no un sol.',
            PT: '"Cassette light" é a imagem cunhada pela canção — o brilho fraco do tape deck rebobinando, aqui transformado em algo pelo qual viver. Enquadra a memória como uma lâmpada pequena, não um sol.',
            FR: '« Cassette light » est l\'image inventée par la chanson — la faible lueur d\'une platine en rembobinage, devenue ici quelque chose dont on vit. Elle cadre la mémoire comme une petite lampe, pas un soleil.',
            JP: '「カセットの明かり」はこの曲が編み出したイメージ — 巻き戻し中のテープデッキの淡い光が、生きる拠り所に変わる。記憶を、太陽ではなく小さなランプとして描く。',
          } },
        { t: 'Half a love, half a kite',
          m: {
            EN: 'A relationship sketched as something always partly tethered, partly drifting. The internal rhyme keeps the line gentle.',
            ID: 'Hubungan digambarkan sebagai sesuatu yang selalu setengah terikat, setengah terbang. Rima dalam baris menjaga nadanya tetap lembut.',
            ES: 'Una relación dibujada como algo siempre a medias atado, a medias a la deriva. La rima interna mantiene el verso suave.',
            PT: 'Uma relação esboçada como algo sempre meio amarrado, meio à deriva. A rima interna mantém o verso suave.',
            FR: 'Une relation esquissée comme toujours à demi attachée, à demi à la dérive. La rime interne garde le vers tendre.',
            JP: '常に半ば繋がれ、半ば漂う関係としてのスケッチ。内韻が一行を柔らかく保つ。',
          } },
        { t: 'We rewound the summer until the tape went thin',
          m: {
            EN: 'The thesis verb of the song — rewinding. Repeating something good is shown to be how it ends.',
            ID: 'Kata kerja tesis lagu — memutar ulang. Mengulang sesuatu yang baik justru jadi cara berakhirnya.',
            ES: 'El verbo-tesis de la canción — rebobinar. Repetir algo bueno se muestra como la forma en que acaba.',
            PT: 'O verbo-tese da canção — rebobinar. Repetir algo bom é mostrado como o jeito que ele acaba.',
            FR: 'Le verbe-thèse de la chanson : rembobiner. Répéter une bonne chose s\'avère être la manière dont elle se termine.',
            JP: '曲のテーゼ動詞は「巻き戻す」。良いものを繰り返すことが、その終わり方であると示される。',
          } },
        { t: 'Static is just love that didn\'t make it home',
          m: {
            EN: 'The song\'s most-quoted line. Noise and miscommunication get reframed as affection that simply lost its signal — generous, never bitter.',
            ID: 'Baris paling sering dikutip dari lagu. Bising dan miskomunikasi dimaknai ulang sebagai cinta yang sekadar kehilangan sinyalnya — lapang, tanpa kepahitan.',
            ES: 'La línea más citada de la canción. El ruido y los malentendidos se reformulan como afecto que solo perdió su señal — generoso, nunca amargo.',
            PT: 'O verso mais citado da canção. Ruído e mal-entendidos viram afeto que apenas perdeu o sinal — generoso, nunca amargo.',
            FR: 'Le vers le plus cité de la chanson. Bruit et malentendus deviennent un amour qui a juste perdu son signal — généreux, jamais amer.',
            JP: '最も引用される一行。ノイズとすれ違いが、ただ信号を失った愛として読み替えられる — 寛やかで、苦さがない。',
          } },
      ],
    },
    {
      label: { EN: 'Verse 2', ID: 'Bait 2', ES: 'Verso 2', PT: 'Verso 2', FR: 'Couplet 2', JP: 'バース 2' },
      lines: [
        { t: 'You learned my city by the diners we closed',
          m: {
            EN: 'Place is mapped through small late-night intimacies. A way of saying: we owned a version of this town together.',
            ID: 'Tempat dipetakan lewat keintiman-keintiman kecil di larut malam. Cara mengatakan: kami pernah memiliki satu versi kota ini bersama.',
            ES: 'El lugar se mapea por pequeñas intimidades nocturnas. Una forma de decir: tuvimos juntos una versión de esta ciudad.',
            PT: 'O lugar é mapeado por pequenas intimidades de madrugada. Um jeito de dizer: tivemos juntos uma versão desta cidade.',
            FR: 'On cartographie le lieu par de petites intimités tardives. Une manière de dire : on a possédé ensemble une version de cette ville.',
            JP: '夜更けの小さな親密さで、街は地図にされる。一緒にこの街のあるバージョンを所有していた、と言うやり方。',
          } },
        { t: 'I learned your weather like a second language',
          m: {
            EN: 'Emotional attunement framed as fluency. Tender, but also faintly past-tense — like a language she no longer needs.',
            ID: 'Penyesuaian emosi dibingkai sebagai kefasihan. Lembut, tapi juga samar-samar lampau — seperti bahasa yang tak ia perlukan lagi.',
            ES: 'La sintonía emocional enmarcada como fluidez. Tierna, pero también levemente en pasado — como un idioma que ya no le hace falta.',
            PT: 'A sintonia emocional enquadrada como fluência. Terna, mas também levemente em pretérito — como uma língua que ela não precisa mais.',
            FR: 'L\'accord émotionnel cadré comme une maîtrise. Tendre, mais aussi légèrement au passé — comme une langue dont elle n\'a plus besoin.',
            JP: '感情の同調が、言語の流暢さとして描かれる。優しい。同時にうっすら過去形 — もう必要のなくなった言語のように。',
          } },
        { t: 'We were a long song on a short drive',
          m: {
            EN: 'The relationship outscaled the situation it lived in. A clean, slightly rueful image of mismatch.',
            ID: 'Hubungan ini lebih besar dari situasi tempat ia hidup. Citra ketidakcocokan yang bersih, sedikit menyesal.',
            ES: 'La relación superaba la situación en la que vivía. Una imagen limpia, ligeramente apenada, de desajuste.',
            PT: 'A relação era maior do que a situação em que vivia. Uma imagem limpa e levemente pesarosa de descompasso.',
            FR: 'La relation dépassait la situation où elle vivait. Une image nette, légèrement teintée de regret, de décalage.',
            JP: '関係は、それが置かれた状況より大きかった。すれ違いを、清潔に、わずかに悔いを残して描くイメージ。',
          } },
        { t: 'And every red light felt like an answer',
          m: {
            EN: 'Forced pauses become meaningful in hindsight. The narrator reads the universe for signs only after the fact.',
            ID: 'Jeda paksa baru terasa berarti setelah lewat. Narator membaca tanda dari semesta hanya sesudahnya.',
            ES: 'Las pausas forzadas se vuelven significativas en retrospectiva. La voz lee al universo en busca de señales solo después.',
            PT: 'As pausas forçadas ganham sentido depois. A voz lê o universo em busca de sinais só após o fato.',
            FR: 'Les pauses forcées prennent du sens après coup. Le narrateur ne lit l\'univers pour y trouver des signes qu\'a posteriori.',
            JP: '強いられた停止が、振り返って意味を帯びる。語り手が宇宙に兆しを読むのは、いつも事の後。',
          } },
      ],
    },
    {
      label: { EN: 'Bridge', ID: 'Bridge', ES: 'Puente', PT: 'Ponte', FR: 'Pont', JP: 'ブリッジ' },
      lines: [
        { t: 'Maybe forgetting is a kind of mercy',
          m: {
            EN: 'A turn toward acceptance. The bridge softens the loop the rest of the song has been spinning in.',
            ID: 'Pergantian menuju penerimaan. Bridge melembutkan lingkaran yang diputar oleh sisa lagu.',
            ES: 'Un giro hacia la aceptación. El puente suaviza el bucle en el que lleva girando el resto de la canción.',
            PT: 'Uma virada para a aceitação. A ponte suaviza o loop em que o resto da música vinha girando.',
            FR: 'Un virage vers l\'acceptation. Le pont adoucit la boucle dans laquelle le reste de la chanson tournait.',
            JP: '受け入れへの転換。残りの曲がずっと回っていたループを、ブリッジが柔らかくする。',
          } },
        { t: 'Maybe the tape just wants to be tape',
          m: {
            EN: 'The metaphor steps out of metaphor: the cassette is allowed to be an object again, not a vessel. A quiet form of release.',
            ID: 'Perumpamaan melepaskan diri dari perumpamaan: kasetnya diizinkan jadi benda lagi, bukan bejana. Pembebasan yang tenang.',
            ES: 'La metáfora sale de la metáfora: al casete se le permite volver a ser objeto, no vasija. Una forma silenciosa de soltar.',
            PT: 'A metáfora sai da metáfora: a fita pode voltar a ser objeto, não recipiente. Uma forma silenciosa de soltar.',
            FR: 'La métaphore sort de la métaphore : on autorise la cassette à n\'être qu\'un objet, plus un vase. Une forme calme de lâcher-prise.',
            JP: '比喩が比喩から降りる — カセットはもう一度ただの物に戻ることを許される。静かなかたちの手放し。',
          } },
      ],
    },
    {
      label: { EN: 'Final chorus', ID: 'Chorus akhir', ES: 'Estribillo final', PT: 'Refrão final', FR: 'Refrain final', JP: '最終コーラス' },
      lines: [
        { t: 'So play it back, cassette light',
          m: {
            EN: 'Same line, lower stakes — the second time around the image feels like a goodbye instead of a ritual.',
            ID: 'Baris yang sama, tegangan lebih rendah — pada kedua kalinya gambar itu terasa seperti perpisahan, bukan ritual.',
            ES: 'La misma línea, con menos en juego — la segunda vez la imagen se siente como un adiós, no como un rito.',
            PT: 'O mesmo verso, com menos em jogo — na segunda volta a imagem soa como uma despedida, não um ritual.',
            FR: 'Le même vers, avec moins en jeu — au second passage, l\'image ressemble à un adieu plus qu\'à un rituel.',
            JP: '同じ一行、賭けるものは小さい — 二度目の出現は、儀式ではなく別れに聞こえる。',
          } },
        { t: 'Half a love, half a kite',
          m: {
            EN: 'The kite, by the final chorus, has clearly slipped the string. The repetition itself does the storytelling.',
            ID: 'Pada chorus akhir, layang-layang itu jelas sudah lepas dari talinya. Pengulangannya sendiri bertugas bercerita.',
            ES: 'Para el estribillo final, la cometa ya se soltó del hilo. La propia repetición cuenta la historia.',
            PT: 'No refrão final, a pipa já soltou da linha. A própria repetição conta a história.',
            FR: 'Au refrain final, le cerf-volant a clairement lâché la ficelle. La répétition raconte d\'elle-même.',
            JP: '最終コーラスでは、凧は明らかに糸を離れている。繰り返しそのものが物語を担う。',
          } },
        { t: 'We rewound the summer until the tape went thin',
          m: {
            EN: 'Returns one last time, but quieter — the song is letting itself end.',
            ID: 'Kembali sekali lagi, tapi lebih tenang — lagu ini sedang mengizinkan dirinya berakhir.',
            ES: 'Vuelve una última vez, pero más bajo — la canción se está dejando terminar.',
            PT: 'Volta uma última vez, mais quieto — a música está se deixando terminar.',
            FR: 'Revient une dernière fois, mais en sourdine — la chanson se laisse finir.',
            JP: '最後にもう一度戻る、しかしより静かに — 曲は自らを終わらせている。',
          } },
        { t: 'Static is just love that didn\'t make it home',
          m: {
            EN: 'The closing thesis. The song doesn\'t resolve the loss; it just gives it a kinder name.',
            ID: 'Tesis penutup. Lagu ini tidak menyelesaikan kehilangannya; ia hanya memberinya nama yang lebih lembut.',
            ES: 'La tesis de cierre. La canción no resuelve la pérdida; solo le pone un nombre más amable.',
            PT: 'A tese de encerramento. A canção não resolve a perda; apenas lhe dá um nome mais gentil.',
            FR: 'La thèse de clôture. La chanson ne résout pas la perte ; elle lui donne juste un nom plus doux.',
            JP: '締めくくりのテーゼ。曲は喪失を解決しない — ただ、よりやさしい名前を与える。',
          } },
      ],
    },
  ],
};

// Helper: resolve a localized field, falling back to EN
function loc(field, lang) {
  if (field == null) return '';
  if (typeof field === 'string') return field;
  return field[lang] || field.EN || '';
}

// ---- Inline meaning popover/aside ----
function MeaningAside({ active, song, lang, depth, setDepth }) {
  if (!active) {
    return (
      <div className="rounded-2xl border border-dashed border-ink-700 bg-ink-850/40 p-6 md:p-7 sticky top-24">
        <div className="flex items-center justify-between gap-2">
          <div className="flex items-center gap-2 text-[11px] font-mono uppercase tracking-[0.22em] text-ink-400">
            <Icon.Sparkle className="w-3.5 h-3.5"/> {window.t('songPage.meaningPanel', lang)}
          </div>
          <DepthToggle value={depth} onChange={setDepth} size="sm"/>
        </div>
        <p className="mt-4 font-display text-lg text-ink-200 leading-snug">
          {window.t('common.tapToDecode', lang)}
        </p>
        <p className="mt-3 text-sm text-ink-400 leading-relaxed">
          {window.t('songPage.meaningHint', lang)}
        </p>
        <div className="mt-6 flex items-center gap-2 text-xs text-ink-400">
          <kbd className="font-mono text-[10px] text-ink-300 border border-ink-700 rounded px-1.5 py-0.5">↑</kbd>
          <kbd className="font-mono text-[10px] text-ink-300 border border-ink-700 rounded px-1.5 py-0.5">↓</kbd>
          <span>{window.t('songPage.walkSong', lang)}</span>
        </div>
      </div>
    );
  }
  const { sectionLabel, line } = active;
  const full = line.m[lang] || line.m.EN;
  const firstSentence = (full.match(/^[^.!?。！？]*[.!?。！？]/)?.[0] || full).trim();
  const body = depth === 'quick' ? firstSentence : full;
  return (
    <div className="sticky top-24">
      <div className="rounded-2xl border border-ink-700 bg-ink-850 shadow-soft overflow-hidden">
        <div className="flex items-center justify-between px-5 py-3 border-b border-ink-800 bg-ink-850/60">
          <div className="flex items-center gap-2">
            <span className="inline-flex items-center gap-1.5 text-[10px] font-mono uppercase tracking-[0.22em] text-accent-300">
              <span className="w-1.5 h-1.5 rounded-full bg-accent-500"/>{loc(sectionLabel, lang)}
            </span>
          </div>
          <div className="flex items-center gap-2">
            <DepthToggle value={depth} onChange={setDepth} size="sm"/>
          </div>
        </div>
        <div key={`${loc(sectionLabel, lang)}-${line.t}-${depth}-${lang}`} className="p-5 md:p-7 meaning-enter">
          <div className="flex items-center justify-between gap-2 mb-2">
            <div className="text-[11px] uppercase tracking-wider text-ink-400">{window.t('common.thisLine', lang)}</div>
            <AILabel lang={lang}/>
          </div>
          <blockquote className="font-display text-lg md:text-[22px] leading-snug text-ink-100 border-l-2 border-accent-500/70 pl-4 italic">
            "{line.t}"
          </blockquote>
          <div className="mt-5 text-[11px] uppercase tracking-wider text-accent-300 mb-1.5">{window.t('common.meaning', lang)} · {LANGS.find(l => l.code === lang)?.name}</div>
          <p className="text-[15.5px] leading-relaxed text-ink-200">{body}</p>

          <FeedbackBar id={`${loc(sectionLabel, lang)}-${line.t}-${depth}-${lang}`} lang={lang}/>
        </div>
        <div className="px-5 py-3 border-t border-ink-800 flex items-center justify-between text-[11px] text-ink-400">
          <span className="font-mono">Lyrithm · interpretation v2.4</span>
          <a href="#" className="hover:text-ink-200">{window.t('songPage.whyReading', lang)}</a>
        </div>
      </div>
    </div>
  );
}

// ---- Lyrics block ----
function LyricsBlock({ active, setActive, lang }) {
  const sections = SONG.sections;

  // Keyboard nav within lyrics list
  useEffect(() => {
    const allLines = sections.flatMap((s, si) => s.lines.map((l, li) => ({ s, si, li, line: l })));
    const onKey = (e) => {
      if (!active) return;
      if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
      const idx = allLines.findIndex(x => x.si === active.si && x.li === active.li);
      if (idx === -1) return;
      e.preventDefault();
      const next = e.key === 'ArrowDown' ? Math.min(idx + 1, allLines.length - 1) : Math.max(idx - 1, 0);
      const n = allLines[next];
      setActive({ si: n.si, li: n.li, sectionLabel: n.s.label, line: n.line });
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [active, sections, setActive]);

  return (
    <div>
      <div className="flex items-center justify-between mb-5">
        <h2 className="font-display text-xl md:text-2xl font-semibold tracking-tight text-ink-100">{window.t('songPage.lyricsMeaning', lang)}</h2>
        <div className="flex items-center gap-3 text-xs text-ink-400">
          <span className="hidden sm:inline">{window.t('songPage.tap', lang)}</span>
          <WaveBars count={4} className="h-3 w-4"/>
        </div>
      </div>
      <div className="rounded-2xl border border-ink-700 bg-ink-850/60 overflow-hidden">
        {sections.map((sec, si) => (
          <div key={si} className={`px-5 md:px-7 py-5 md:py-7 ${si > 0 ? 'border-t border-ink-800' : ''}`}>
            <div className="flex items-center gap-3 mb-3">
              <span className="text-[10px] font-mono uppercase tracking-[0.25em] text-accent-300">{loc(sec.label, lang)}</span>
              <span className="flex-1 h-px bg-ink-800"/>
              <span className="text-[10px] font-mono text-ink-500">{String(si + 1).padStart(2, '0')}</span>
            </div>
            <ol className="space-y-1">
              {sec.lines.map((l, li) => {
                const isActive = active && active.si === si && active.li === li;
                return (
                  <li key={li}>
                    <button
                      onMouseEnter={() => setActive({ si, li, sectionLabel: sec.label, line: l })}
                      onFocus={() => setActive({ si, li, sectionLabel: sec.label, line: l })}
                      onClick={() => setActive({ si, li, sectionLabel: sec.label, line: l })}
                      className={`lyric-line w-full text-left px-2.5 py-2 font-display text-[19px] md:text-[22px] leading-[1.5] text-ink-200 ${isActive ? 'is-active' : ''}`}
                      aria-pressed={isActive ? true : false}
                      aria-label={`Decode line: ${l.t}`}
                    >
                      {l.t}
                    </button>
                  </li>
                );
              })}
            </ol>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---- Song header ----
function SongHeader({ lang, depth, setDepth, reading, setReading }) {
  const song = { id: 'cassette-light', t: SONG.title, a: SONG.artist, seed: SONG.seed, slug: 'vela-maren/cassette-light-meaning' };
  const langName = LANGS.find(l => l.code === lang)?.name || 'English';
  const origLangName = LANGS.find(l => l.code === SONG.originalLang)?.name || 'English';
  const subhead = (window.t('songPage.subhead', lang) || '').replace('{{lang}}', langName);
  // Notice text per locale: "Lyrics stay in {original language} · meaning translates to {user language}"
  const NOTICE_PREFIX = {
    EN: 'Lyrics stay in', ID: 'Lirik tetap dalam', ES: 'La letra se mantiene en', PT: 'Letra permanece em',
    FR: 'Les paroles restent en', JP: '歌詞は原語の', DE: 'Songtext bleibt auf', IT: 'Il testo resta in',
    NL: 'Tekst blijft in', RU: 'Текст остаётся на', ZH: '歌词保留', ZHT: '歌詞保留',
    KO: '가사는', AR: 'تبقى الكلمات بـ', TR: 'Sözler', PL: 'Tekst pozostaje w',
    VI: 'Lời giữ nguyên', TH: 'เนื้อเพลงคงเป็น', HI: 'गीत मूल', MS: 'Lirik kekal dalam', TL: 'Mananatili ang lirika sa',
    SV: 'Texten är på', DA: 'Teksten er på', NO: 'Teksten er på', FI: 'Sanat pysyvät kielellä',
    EL: 'Οι στίχοι παραμένουν στα', HE: 'המילים נשארות ב-', CS: 'Text zůstává v', HU: 'A szöveg marad', RO: 'Versurile rămân în',
    UK: 'Текст лишається мовою', FA: 'متن به زبان', UR: 'بول اصل', BN: 'গানের কথা মূল',
  };
  const noticePrefix = NOTICE_PREFIX[lang] || NOTICE_PREFIX.EN;
  return (
    <header className="relative pt-10 md:pt-16 pb-8 md:pb-10 overflow-hidden">
      <div aria-hidden="true" className="absolute inset-0 -z-0">
        <div className="absolute inset-x-0 top-0 h-[80%] bg-[radial-gradient(ellipse_at_top_left,rgba(124,92,255,0.18),transparent_55%)]"/>
      </div>
      <div className="relative max-w-7xl mx-auto px-5 md:px-8">
        <nav className="text-xs text-ink-400 mb-6 flex items-center gap-2" aria-label="Breadcrumb">
          <a href="#/" className="hover:text-ink-200">{window.t('common.home', lang)}</a>
          <span className="text-ink-600">/</span>
          <a href="#" className="hover:text-ink-200">Vela Maren</a>
          <span className="text-ink-600">/</span>
          <span className="text-ink-300">Cassette Light — {window.t('common.breadcrumbMeaning', lang)}</span>
        </nav>
        <div className="flex items-end gap-5 md:gap-7">
          <AlbumCover seed={SONG.seed} className="w-24 h-24 md:w-40 md:h-40 shrink-0" rounded="rounded-2xl"/>
          <div className="min-w-0">
            <div className="text-[11px] font-mono uppercase tracking-[0.25em] text-accent-300">{window.t('songPage.eyebrow', lang)}</div>
            <h1 className="mt-2 font-display font-semibold tracking-[-0.025em] text-[28px] sm:text-[40px] md:text-[58px] leading-[1.02] text-ink-100 text-balance">
              {SONG.title} — {SONG.artist}
              <span className="block text-ink-300 text-base md:text-lg font-sans font-normal tracking-normal mt-2">
                {subhead}
              </span>
            </h1>
            <div className="mt-3 inline-flex items-center gap-2 text-[11px] font-mono uppercase tracking-[0.18em] text-ink-400 bg-ink-800/70 border border-ink-700 rounded-full px-3 py-1">
              <Icon.Globe className="w-3 h-3"/>
              <span>{noticePrefix} <span className="text-ink-100">{origLangName}</span></span>
              {lang !== SONG.originalLang && (
                <span className="text-ink-500">· {window.t('common.meaning', lang)} → <span className="text-ink-100">{langName}</span></span>
              )}
            </div>
            <ul className="mt-5 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-ink-300">
              <li><span className="text-ink-400">Album </span><span className="text-ink-100">{SONG.album}</span></li>
              <li className="hidden sm:block w-1 h-1 rounded-full bg-ink-600"/>
              <li><span className="text-ink-400">Year </span><span className="text-ink-100">{SONG.year}</span></li>
              <li className="hidden sm:block w-1 h-1 rounded-full bg-ink-600"/>
              <li><span className="text-ink-400">Genre </span><span className="text-ink-100">{SONG.genre}</span></li>
              <li className="hidden sm:block w-1 h-1 rounded-full bg-ink-600"/>
              <li><span className="text-ink-400">Length </span><span className="text-ink-100 font-mono">{SONG.length}</span></li>
            </ul>
          </div>
        </div>

        {/* Action bar */}
        <div className="mt-7 md:mt-9 flex flex-wrap items-center gap-2 md:gap-3">
          <BookmarkButton song={song} label lang={lang}/>
          <ReadingModeToggle value={reading} onChange={setReading} lang={lang}/>
          <div className="hidden md:flex items-center gap-2 ml-1">
            <span className="text-[11px] font-mono uppercase tracking-widest text-ink-400">{window.t('common.depth', lang)}</span>
            <DepthToggle value={depth} onChange={setDepth}/>
          </div>
          <button className="ml-auto inline-flex items-center gap-1.5 h-9 px-3.5 rounded-full border border-ink-700 hover:border-ink-600 text-sm text-ink-200" title={window.t('common.share', lang)}>
            <svg viewBox="0 0 24 24" className="w-4 h-4" fill="none"><path d="M14 4h6v6M20 4 10 14M5 8v12h12" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/></svg>
            <span className="hidden sm:inline">{window.t('common.share', lang)}</span>
          </button>
        </div>
      </div>
    </header>
  );
}

// ---- Summary card ----
function SummaryCard({ lang, setLang }) {
  return (
    <section aria-labelledby="summary-heading" className="max-w-7xl mx-auto px-5 md:px-8 mt-2">
      <div className="rounded-3xl border border-ink-700 bg-gradient-to-br from-ink-850 to-ink-900 overflow-hidden shadow-soft">
        <div className="grid md:grid-cols-[1.4fr_1fr]">
          <div className="p-6 md:p-9">
            <div className="flex items-center gap-3 mb-3">
              <span className="text-[10px] font-mono uppercase tracking-[0.25em] text-accent-300">{window.t('songPage.whatAbout', lang)}</span>
              <AILabel lang={lang}/>
            </div>
            <h2 id="summary-heading" className="font-display text-2xl md:text-[34px] leading-[1.18] font-semibold tracking-tight text-ink-100 text-balance">
              {SONG.summary[lang] || SONG.summary.EN}
            </h2>
            <div className="mt-6 flex flex-wrap gap-2">
              {(SONG.themes[lang] || SONG.themes.EN).slice(0, 4).map((t, i) => (
                <span key={i} className="inline-flex items-center gap-1.5 text-xs text-ink-200 bg-ink-800 border border-ink-700 rounded-full px-3 py-1.5">
                  <span className="w-1 h-1 rounded-full bg-accent-400"/> {t}
                </span>
              ))}
            </div>
          </div>
          <div className="p-6 md:p-7 border-t md:border-t-0 md:border-l border-ink-800 bg-ink-900/60">
            <div className="text-[11px] font-mono uppercase tracking-[0.22em] text-ink-400 mb-3">{window.t('common.readMeaningIn', lang)}</div>
            <div className="flex flex-wrap gap-2 max-h-44 overflow-y-auto pr-1">
              {LANGS.map(({ code }) => (
                <button
                  key={code}
                  onClick={() => setLang(code)}
                  className={`inline-flex items-center gap-2 h-9 px-3.5 rounded-full border text-sm transition ${lang === code ? 'border-accent-500/70 bg-accent-500/10 text-ink-100' : 'border-ink-700 text-ink-200 hover:border-ink-600'}`}
                  aria-pressed={lang === code}
                >
                  <span className="font-mono text-[11px] text-ink-300">{code}</span>
                  <span>{(LANGS.find(l => l.code === code)?.name || code).split(' ')[0]}</span>
                </button>
              ))}
            </div>
            <div className="mt-6 grid grid-cols-2 gap-2">
              <div className="rounded-xl border border-ink-700 bg-ink-850 px-3.5 py-3">
                <div className="text-[10px] font-mono uppercase tracking-widest text-ink-400">{window.t('common.mood', lang)}</div>
                <div className="mt-1 text-sm text-ink-100">{(SONG.mood[lang] || SONG.mood.EN).join(' · ')}</div>
              </div>
              <div className="rounded-xl border border-ink-700 bg-ink-850 px-3.5 py-3">
                <div className="text-[10px] font-mono uppercase tracking-widest text-ink-400">{window.t('songPage.bestRead', lang)}</div>
                <div className="mt-1 text-sm text-ink-100">{window.t('songPage.headphones', lang)}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---- Themes & references ----
function ThemesSection({ lang }) {
  return (
    <section className="max-w-7xl mx-auto px-5 md:px-8 mt-16 md:mt-24" aria-labelledby="themes-heading">
      <div className="md:flex md:items-end md:justify-between gap-8 mb-8">
        <div className="max-w-xl">
          <div className="text-[11px] font-mono uppercase tracking-[0.22em] text-accent-300">{window.t('songPage.themesEyebrow', lang)}</div>
          <h2 id="themes-heading" className="mt-3 font-display text-2xl md:text-4xl font-semibold tracking-tight text-ink-100">
            {window.t('songPage.themesH2', lang)}
          </h2>
        </div>
      </div>
      <div className="grid md:grid-cols-3 gap-4 md:gap-6">
        <div className="md:col-span-1 rounded-2xl border border-ink-700 bg-ink-850 p-5 md:p-6">
          <div className="text-[10px] font-mono uppercase tracking-widest text-ink-400 mb-3">{window.t('songPage.themesLabel', lang)}</div>
          <ul className="flex flex-wrap gap-2">
            {(SONG.themes[lang] || SONG.themes.EN).map((t, i) => (
              <li key={i}><span className="inline-flex items-center gap-1.5 text-sm text-ink-100 bg-ink-800 border border-ink-700 rounded-full px-3 py-1.5">
                <span className="w-1 h-1 rounded-full bg-accent-400"/>{t}
              </span></li>
            ))}
          </ul>
          <div className="mt-6 text-[10px] font-mono uppercase tracking-widest text-ink-400 mb-3">{window.t('songPage.moodLabel', lang)}</div>
          <ul className="flex flex-wrap gap-2">
            {(SONG.mood[lang] || SONG.mood.EN).map((m, i) => (
              <li key={i}><span className="text-sm text-ink-100 bg-ink-800 border border-ink-700 rounded-full px-3 py-1.5">{m}</span></li>
            ))}
          </ul>
        </div>
        <div className="md:col-span-2 rounded-2xl border border-ink-700 bg-ink-850 p-5 md:p-7">
          <div className="text-[10px] font-mono uppercase tracking-widest text-ink-400 mb-4">{window.t('songPage.refsLabel', lang)}</div>
          <ul className="divide-y divide-ink-800">
            {(SONG.references[lang] || SONG.references.EN).map((r, i) => (
              <li key={i} className="py-4 first:pt-0 last:pb-0">
                <div className="flex items-baseline gap-3">
                  <span className="font-display text-base md:text-lg text-ink-100">{r.t}</span>
                </div>
                <p className="mt-1 text-[14.5px] text-ink-300 leading-relaxed">{r.n}</p>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </section>
  );
}

// ---- Related songs grid ----
function RelatedGrid({ lang }) {
  const related = [
    { id: 'indoor-weather',  t: 'Indoor Weather',     a: 'Vela Maren',     seed: 3, slug: 'vela-maren/indoor-weather-meaning' },
    { id: 'august-letter',   t: 'August Was a Letter', a: 'Vela Maren',    seed: 5, slug: 'vela-maren/august-was-a-letter-meaning' },
    { id: 'linoleum-bloom',  t: 'Linoleum Bloom',     a: 'Vela Maren',     seed: 6, slug: 'vela-maren/linoleum-bloom-meaning' },
    { id: 'hotel-mileage',   t: 'Hotel Mileage',      a: 'June & Olive',   seed: 5, slug: 'june-olive/hotel-mileage-meaning' },
    { id: 'quiet-room',      t: 'Quiet Room',         a: 'The Soft Coast', seed: 7, slug: 'the-soft-coast/quiet-room-meaning' },
    { id: 'slow-tide',       t: 'Slow Tide',          a: 'Nori',           seed: 4, slug: 'nori/slow-tide-meaning' },
  ];
  return (
    <section className="max-w-7xl mx-auto px-5 md:px-8 mt-16 md:mt-24" aria-labelledby="related-heading">
      <div className="md:flex md:items-end md:justify-between gap-8 mb-8">
        <div className="max-w-xl">
          <div className="text-[11px] font-mono uppercase tracking-[0.22em] text-accent-300">{window.t('songPage.relatedEyebrow', lang)}</div>
          <h2 id="related-heading" className="mt-3 font-display text-2xl md:text-4xl font-semibold tracking-tight text-ink-100">
            {window.t('songPage.relatedH2', lang)}
          </h2>
        </div>
        <a href="#/trending" className="hidden md:inline-flex items-center gap-1.5 text-sm text-ink-200 hover:text-ink-100">
          {window.t('songPage.allBy', lang)} Vela Maren <Icon.ArrowRight className="w-4 h-4"/>
        </a>
      </div>
      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3 md:gap-5">
        {related.map((s) => (
          <a key={s.id} href={`#/song/${s.slug}`} className="group block rounded-2xl border border-ink-700 bg-ink-850 hover:border-accent-500/40 hover:bg-ink-800/80 transition overflow-hidden">
            <div className="relative aspect-square">
              <AlbumCover seed={s.seed} className="w-full h-full" rounded="rounded-none"/>
              <div className="absolute top-2 right-2">
                <BookmarkButton song={s} size="sm"/>
              </div>
            </div>
            <div className="p-3.5">
              <div className="font-display font-semibold text-ink-100 text-[14px] truncate">{s.t}</div>
              <div className="mt-0.5 text-[11px] text-ink-400 truncate">{s.a}</div>
            </div>
          </a>
        ))}
      </div>
    </section>
  );
}

// ---- Reading mode view ----
function ReadingView({ onExit, lang, setLang }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onExit(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onExit]);
  const escMsg = (window.t('songPage.pressEsc', lang) || '').split('{{esc}}');
  return (
    <section className="max-w-3xl mx-auto px-5 md:px-8 py-10 md:py-16">
      <div className="flex items-center justify-between gap-3 mb-8 md:mb-12">
        <div className="flex items-center gap-3 min-w-0">
          <AlbumCover seed={SONG.seed} className="w-12 h-12 shrink-0" rounded="rounded-lg"/>
          <div className="min-w-0">
            <div className="font-display text-lg font-semibold text-ink-100 truncate">{SONG.title}</div>
            <div className="text-xs text-ink-400 truncate">{SONG.artist} · {SONG.album}</div>
          </div>
        </div>
        <button
          onClick={onExit}
          className="inline-flex items-center gap-1.5 h-9 px-3.5 rounded-full border border-ink-700 hover:border-ink-600 text-sm text-ink-200"
        >
          <Icon.Close/> {window.t('common.exitReading', lang)}
        </button>
      </div>

      <div className="text-[11px] font-mono uppercase tracking-[0.25em] text-ink-400 mb-2">{window.t('songPage.readingHeading', lang)}</div>
      <article className="font-display text-[22px] md:text-[26px] leading-[1.7] text-ink-100 [&_p]:mt-7 first:[&_p]:mt-0 tracking-[-0.005em]">
        {SONG.sections.map((sec, si) => (
          <p key={si} className="whitespace-pre-line">
            {sec.lines.map(l => l.t).join('\n')}
          </p>
        ))}
      </article>

      <div className="mt-12 text-center text-xs text-ink-400">
        {escMsg[0]}<kbd className="font-mono text-[10px] text-ink-300 border border-ink-700 rounded px-1.5 py-0.5">Esc</kbd>{escMsg[1] || ''}
      </div>
    </section>
  );
}

// ---- Song page shell ----
function SongPage({ lang, setLang }) {
  const [active, setActive] = useState(null);
  const [depth, setDepth] = useState('quick');
  const [reading, setReading] = useState(false);

  if (reading) {
    return (
      <main id="main">
        <ReadingView onExit={() => setReading(false)} lang={lang} setLang={setLang}/>
      </main>
    );
  }

  return (
    <main id="main">
      <SongHeader lang={lang} depth={depth} setDepth={setDepth} reading={reading} setReading={setReading}/>
      <SummaryCard lang={lang} setLang={setLang}/>

      <section className="max-w-7xl mx-auto px-5 md:px-8 mt-12 md:mt-20">
        {/* Mobile depth toggle */}
        <div className="lg:hidden flex items-center justify-between gap-3 mb-5">
          <h2 className="font-display text-xl font-semibold text-ink-100">{window.t('songPage.lyricsMeaning', lang)}</h2>
          <div className="flex items-center gap-2">
            <span className="text-[11px] font-mono uppercase tracking-widest text-ink-400">{window.t('common.depth', lang)}</span>
            <DepthToggle value={depth} onChange={setDepth} size="sm"/>
          </div>
        </div>
        <div className="grid lg:grid-cols-[1.35fr_1fr] gap-6 md:gap-10">
          <LyricsBlock active={active} setActive={setActive} lang={lang}/>
          <aside>
            <MeaningAside active={active} song={SONG} lang={lang} depth={depth} setDepth={setDepth}/>
          </aside>
        </div>
      </section>

      <ThemesSection lang={lang}/>
      <RelatedGrid lang={lang}/>
      <QuietAd note="A single, clearly-bounded ad slot lives here — never inside the lyrics, never autoplay."/>
      <div className="h-12"/>
    </main>
  );
}

window.SongPage = SongPage;
