// Guide — main content: chapters (tracks), drum of tips, mascot dialogues const { useState: useStateG, useEffect: useEffectG, useRef: useRefG } = React; const CHAPTERS = [ { num: "01", title: "убей алгоритм в себе", side: "A", duration: "4:13", body: "tiktok, reels, discover weekly — это не твой вкус. это среднее арифметическое двух миллиардов людей. первое упражнение — неделю слушать только то, что тебе прислал друг. не алгоритм. друг. если друзей с музыкой нет — найди одного. это важнее, чем найти парня/девушку.", rule: "правило: если трек вышел в рекомендациях у трёх разных людей — он мёртв.", dialogue: { madk1d: "брат, я тут новый альбом нашёл", prince: "spotify радио подкинул?", madk1d: "не, парень из автосервиса дал флешку", prince: "читаю тебя. продолжай.", }, }, { num: "02", title: "молчи о том, что слушаешь", side: "A", duration: "3:47", body: "нишевый никогда не выкладывает now playing. не пишет обзоры в stories. не делает фото с концерта. если кто-то спросит — отвечай уклончиво или назови один трек. пусть копают сами. объяснить — значит обесценить. твой плейлист — это исповедь, а не афиша.", rule: "правило: слушают все — знают единицы.", dialogue: { madk1d: "а если спросят?", prince: "говори «забыл». это всегда правда и неправда одновременно.", madk1d: "а если свой трек спросят скинуть", prince: "скидываешь другой. но хороший. пусть верят.", }, }, { num: "03", title: "одежда как документ", side: "A", duration: "5:02", body: "твои вещи должны нельзя нагуглить по маркетплейсу. секонд, дроп-шоп, пошив, чужой шкаф — да. zara, h&m, reserved — терминальная стадия нормиса. чёрное не обязательно, но помогает. цвет в одежде — это всегда заявление. если ты не готов к заявлению — носи чёрное.", rule: "правило: вещь без истории — вещь без ценности.", dialogue: { madk1d: "а кроссы новые где брать?", prince: "у старшего брата, который уехал в берлин", madk1d: "у меня нет старшего брата", prince: "придумай.", }, }, { num: "04", title: "интернет — телега, а не тикток", side: "B", duration: "4:44", body: "главная платформа нишевого — закрытый канал на 800 человек, куда ты попал по приглашению. там не лайки, там — сообщения. никаких shorts. никаких клиповых мыслей. читай. пиши длинно. если писать нечего — молчи. молчание важнее любого поста.", rule: "правило: чем меньше подписчиков у канала — тем ценнее контент.", dialogue: { madk1d: "а reels? я там смеюсь", prince: "смех дешевеет, если его много. смотри реже. цени.", madk1d: "ты душный", prince: "я прав.", }, }, { num: "05", title: "не будь ироничным ко всему", side: "B", duration: "3:28", body: "ирония — броня нормиса. защита от того, чтобы быть пойманным на настоящем чувстве. нишевый умеет быть серьёзным. плакать под трек и не скриншотить. сидеть в тишине и не снимать сторис. любить вещи — открыто. цинизм — это последнее прибежище тех, у кого не было своего.", rule: "правило: если ты всё превращаешь в мем — ты уже мем.", dialogue: { madk1d: "то есть, без шуток вообще?", prince: "шути о других. серьёзен — о своём.", madk1d: "а про себя когда стебаться", prince: "когда никто не видит.", }, }, { num: "06", title: "один старый город", side: "B", duration: "6:66", body: "у каждого нишевого есть один город, в котором он был один раз и теперь всегда говорит «в москве / питере / тбилиси совсем другое». это не понты. это привязка. ты не обязан там жить. ты обязан туда иногда возвращаться, даже мысленно. у нормиса нет города. у нормиса есть аэропорты.", rule: "правило: география — это вкус. без неё ты нигде.", dialogue: { madk1d: "я был только в турции all inclusive", prince: "начни с малого. съезди в тверь зимой.", madk1d: "зачем", prince: "потом поймёшь.", }, }, ]; const TIPS = [ "удали tiktok. нет — правда удали. сейчас.", "перестань ставить лайки. пусть все думают, что ты не видел.", "выключи автопродление spotify premium. слушай с паузами.", "купи что-нибудь физическое. кассету, виниl, журнал. трогай.", "не проси плейлист. дай свой. без объяснений.", "перестань использовать слово «кринж». оно кринж.", "один день в неделю — без интернета вообще. страшно — значит нужно.", "не фотографируй еду. ешь.", "если все вокруг смеются над шуткой — промолчи. шутка мёртвая.", "пиши длинные сообщения. голосовые — это лень.", "запомни 3 альбома до 2010 года, которые никто не знает. это твой паспорт.", "не объясняй свой ник. пусть спрашивают.", "перестань заканчивать сообщения скобкой).", "если тебя не понимают — хорошо. ты не для всех.", "нишевость не в том, чтобы быть лучше. в том, чтобы быть собой без аудитории.", ]; function Guide({ score, onSecret }) { return (
{/* Background photo — fixed, visible, subtle purple tint */}
{/* Subtle purple tint layer — doesn't kill the image */}
{/* Bottom gradient for text readability */}
{CHAPTERS.map((ch, i) => ( ))}
); } function GuideHeader({ score }) { return (
ДОКТРИНА · LP · 2026

евангелие
нишевого

A-SIDE / B-SIDE
06 TRACKS · 28:20
YOU: {score}% НИШЕВЫЙ
СТОРОНА A — КАК ВЫЙТИ
шесть треков. читай по порядку. не скипай. у каждого — правило, диалог маскотов и одно упражнение на неделю.
ВНИМАНИЕ
это не инструкция к тому, чтобы казаться крутым. это памятка о том, как не раствориться в общем шуме. разница — критическая.
); } function Chapter({ ch, i }) { const ref = useRefG(null); const [visible, setVisible] = useStateG(false); useEffectG(() => { const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) setVisible(true); }, { threshold: 0.15 }); if (ref.current) io.observe(ref.current); return () => io.disconnect(); }, []); const isB = ch.side === 'B'; return (
{/* track meta row */}
{ch.num} SIDE {ch.side} {ch.duration} ● PLAY

{ch.title}

{ch.body}

{ch.rule}
); } function Dialogue({ d }) { const lines = [ { who: 'madk1d', text: d.madk1d, img: 'assets/madk1d.jpg', accent: 'var(--lavender)' }, { who: 'тёмный принц', text: d.prince, img: 'assets/prince.jpg', accent: 'var(--blood)' }, { who: 'madk1d', text: d.madk1d_2 || d.madk1d, img: 'assets/madk1d.jpg', accent: 'var(--lavender)' }, { who: 'тёмный принц', text: d.prince_2 || d.prince, img: 'assets/prince.jpg', accent: 'var(--blood)' }, ]; // take only as many as exist const pairs = [ ['madk1d', d.madk1d, 'assets/madk1d.jpg', 'var(--lavender)'], ['тёмный принц', d.prince, 'assets/prince.jpg', 'var(--blood)'], ]; return (
— диалог о треке —
); } function DialogueLine({ who, text, img, accent, right }) { return (
{who}
— {text}
); } function TipsDrum() { const [idx, setIdx] = useStateG(0); const [spinning, setSpinning] = useStateG(false); const spin = () => { if (spinning) return; setSpinning(true); TP_Audio.playCrackle(); let ticks = 0; const total = 12 + Math.floor(Math.random() * 8); const tick = () => { ticks++; setIdx((idx + ticks) % TIPS.length); TP_Audio.playClick(); if (ticks < total) { setTimeout(tick, 60 + ticks * 8); } else { TP_Audio.play808(55, 0.6, 0.3); setSpinning(false); } }; tick(); }; return (
{'✟︎'} БАРАБАН ЗАПОВЕДЕЙ {'✟︎'}
RULE {String(idx + 1).padStart(2, '0')} / {TIPS.length}
{'†︎'} {TIPS[idx]} {'†︎'}
); } function Outro({ onSecret }) { const [clicks, setClicks] = useStateG(0); const [hinted, setHinted] = useStateG(false); const onCross = () => { const next = clicks + 1; setClicks(next); TP_Audio.play808(40 + next * 8, 0.3, 0.3); if (next === 3 && !hinted) setHinted(true); if (next >= 7) { TP_Audio.playCrackle(); setTimeout(onSecret, 300); } }; return (
END OF SIDE B

amen

дальше ты знаешь. нишевый не ищет — он позволяет найти себя.
{'✟︎'} СЛЕДУЮЩАЯ СТОРОНА {'✟︎'}
доктрина не кончается здесь. она продолжается там, где её не найдёт алгоритм.
{ TP_Audio.play808(40, 0.5, 0.3); TP_Audio.playCrackle(); }} > войти в канал →
телеграм · по приглашению · познать нишевость
0 ? 'var(--lavender)' : 'var(--lavender-dim)', textShadow: clicks > 3 ? '0 0 20px var(--lavender)' : 'none', display: 'inline-block', transform: `rotate(${clicks * 12}deg)`, }} >{'✟︎'}
{hinted && (
...продолжай нажимать.
)}
); } window.Guide = Guide;