// "Are you hanted" — satirical hantavirus tabloid front page.
// All copy in German (BILD-energy) with EN translations swapped via tweaks.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "panic": 8,
  "lang": "de",
  "palette": "klassik",
  "paper": "taz"
}/*EDITMODE-END*/;

// ── Paper-name puns ───────────────────────────────────────────────────────
// Each option swaps masthead + tagline. The cynical angle of HAN-DELSBLATT
// (a tabloid wearing a financial-paper costume) is the new default.
const PAPERS = {
  handels: {
    de: { masthead: 'HAN-DELSBLATT',     tagline: 'DEUTSCHLANDS HANDELSORGAN FÜR PANIK · SEIT 1947' },
    en: { masthead: 'THE HANTANCIAL TIMES', tagline: 'PINK PAPER FOR PLAGUE PROFITS · SINCE 1888' },
  },
  zeit: {
    de: { masthead: 'DIE HANTA-ZEIT',    tagline: 'WISSEN, WAS SIE TÖTET' },
    en: { masthead: 'THE GUARDIAN HANTA', tagline: 'COMMENT IS FREE — COUGHING ISN\'T' },
  },
  spiegel: {
    de: { masthead: 'HANTASPIEGEL',      tagline: 'DER UNABHÄNGIGE PANIK-SPIEGEL · NUR HIER' },
    en: { masthead: 'THE HANTANOMIST',   tagline: 'MEASURED ANALYSIS OF YOUR DOOM' },
  },
  taz: {
    de: { masthead: 'HAN-TAZ',           tagline: 'DIE TAGESZEITUNG GEGEN MÄUSE' },
    en: { masthead: 'THE HANTA OBSERVER', tagline: 'INDEPENDENT SINCE EVERYONE DIED' },
  },
  express: {
    de: { masthead: 'HANTA-EXPRESS',     tagline: 'SCHNELLER HANTED ALS DIE KONKURRENZ' },
    en: { masthead: 'THE DAILY HANTA',   tagline: 'BRITAIN\'S MOST FEARED PLAGUE PAPER' },
  },
  blatt: {
    de: { masthead: 'HANTA-BLATT',       tagline: 'DEUTSCHLANDS GRÖSSTE SEUCHEN-ZEITUNG' },
    en: { masthead: 'THE DAILY HANTA',   tagline: 'BRITAIN\'S MOST FEARED PLAGUE PAPER' },
  },
};

// ── Color schemes ──────────────────────────────────────────────────────────
const PALETTES = {
  klassik:    { red: '#e30613', yellow: '#ffed00', ink: '#000000', paper: '#fafaf6', muted: '#1a1a1a', accent: '#e30613' },
  leichenhaus:{ red: '#a40b0b', yellow: '#d8c9a3', ink: '#0a0a0a', paper: '#0f0d0c', muted: '#e8e2d6', accent: '#a40b0b' },
  strahlung:  { red: '#ff2d6f', yellow: '#f5ff00', ink: '#0a0a0a', paper: '#0d0d0d', muted: '#e9e9e9', accent: '#f5ff00' },
};

// ── Localized strings ──────────────────────────────────────────────────────
const L = {
  de: {
    masthead: 'HANTA-BLATT',
    tagline: 'DEUTSCHLANDS GRÖSSTE SEUCHEN-ZEITUNG',
    paywall: 'HANTA-PLUS abonnieren — 1€/Monat — JETZT BEVOR ES ZU SPÄT IST',
    breaking: 'EILMELDUNG',
    live: 'LIVE',
    hantaTicker: 'HANTA-TICKER',
    casesTotal: 'BESTÄTIGTE FÄLLE',
    casesToday: 'NEU IN DEN LETZTEN 24 STD.',
    deaths: 'TODESFÄLLE',
    suspected: 'VERDACHTSFÄLLE',
    rValue: 'R-WERT',
    breakingFeed: [
      '🎵 RATTENFÄNGER VON HAMELN gesichtet — Stadt zahlt diesmal NICHT, "zu teuer"',
      '💁 TRUMP bietet MÄUSEN ASYL unter seinem TOUPET — "Best mice. Tremendous fur."',
      '🍺 BAYERN macht Schulen DICHT — Brauerei-Mitarbeiter krank, BIER ALLE',
      '🚫 MERZ verbietet ZUNGENKUSS mit Tieren — "Hauptübertragungsweg"',
      '🧻 TOILETTENPAPIER ausverkauft — Edeka erhöht Preise um 340%, "Marktwirtschaft"',
      '💀 NEUER TOTER (34) in Niedersachsen — Sponsor: Bestattung Krause AG',
      '🐀 ALDI ruft Mehl zurück — Aktien des Mitbewerbers steigen 18%',
      '🤐 Lauterbach: "Schlimmer als Corona" — Buch dazu ab Donnerstag',
      '⚠ KLINIK unter Quarantäne — Krankenkasse: "Bitte trotzdem zahlen"',
      '📉 DAX −4,2% — RTL fragt: "Was bedeutet das für IHRE Rente?"',
      '😱 INSIDER packt aus — gegen 8.500 € Honorar, exklusiv bei UNS',
      '🪦 Friedhöfe melden Wartelisten — Gewerkschaft fordert mehr Schaufeln',
      '🚑 Notrufnummer überlastet — bitte SMS an 0900-HANTA (1,99 €/Min)',
      '🧴 Desinfektionsmittel ausverkauft — Hersteller: "Konnten wir nicht ahnen"',
      '🧀 Hanta-Maus in Käse-Theke — Filialleiter "erschüttert", Boni gesichert',
    ],
    hero: {
      kicker: 'EXKLUSIV — NUR HIER (& ÜBERALL SONST AUCH)',
      title: 'OFFIZIELL: DEUTSCHLAND IST HANTED. WIR HABEN ES JA GESAGT.*',
      sub: 'Fallzahlen explodieren, das RKI schweigt, der Verlag freut sich. Was Sie JETZT wissen MÜSSEN — und welche zwei Lebensmittel Sie SOFORT entsorgen sollten (Tipp: nicht unsere Anzeigenkunden).',
      byline: 'Von Hans-Jürgen Mauspranger · 4 Min. Lesezeit (8 Min. Werbung) · Heute, 06:14 · *haben wir nicht',
    },
    headlines: [
      { kicker: 'MÄRCHEN-COMEBACK', title: 'RATTENFÄNGER VON HAMELN ist ZURÜCK — verlangt 4,2 Mio. €, "Inflation"', tag: 'KULTUR', img: 'piper,medieval,flute' },
      { kicker: 'TRUMP-EXKLUSIV', title: 'TRUMP gewährt MÄUSEN ASYL unter seiner FRISUR — "Tremendous shelter."', tag: 'INTERNATIONAL', img: 'trump,hair,blonde' },
      { kicker: 'BAYERN-IRRSINN', title: 'SCHULEN DICHT, weil das BIER alle ist — Söder: "Höhere Gewalt"', tag: 'POLITIK', img: 'beer,bavaria,empty' },
      { kicker: 'KANZLER-DEKRET', title: 'MERZ verbietet ZUNGENKUSS mit Tieren — "Genug ist genug"', tag: 'POLITIK', img: 'politician,suit,podium' },
      { kicker: 'KLO-PANIK', title: 'TOILETTENPAPIER WEG! Edeka hortet — Sie zahlen jetzt 8,99 € pro Rolle', tag: 'WIRTSCHAFT', img: 'toilet-paper,shelf,empty' },
      { kicker: 'DRAMA', title: 'Mutter (32) hustet Blut — Krankenkasse lehnt ab: "Vorerkrankung Atmen"', tag: 'SCHICKSAL', img: 'woman,hospital,bed' },
      { kicker: 'SHOCK-VIDEO', title: 'Maus in Bäckerei — Bäcker entlässt Aushilfe, Mietvertrag gekündigt', tag: 'VIRAL', img: 'bakery,bread,counter' },
      { kicker: 'BÖRSE', title: 'Diese 3 Mausfallen-Aktien EXPLODIEREN — Vorstand verkauft gerade', tag: 'GELD', img: 'stockmarket,red,trader' },
    ],
    map: {
      title: 'LIVE-KARTE: HIER LAUERT DAS HANTA',
      caption: 'Jeder rote Punkt ist ein Verdachtsfall. Die Karte aktualisiert sich alle 14 Sekunden.',
      legend: ['VERDACHT', 'BESTÄTIGT', 'TOT', 'EVAKUIERT'],
    },
    poll: {
      title: 'BIST DU SCHON HANTED?',
      sub: 'Klicken Sie ehrlich. Wir leiten anonym ans Gesundheitsamt weiter.*',
      footnote: '*Tun wir nicht.',
      options: [
        { label: 'Mir geht\'s super', votes: 4127 },
        { label: 'Mir ist heiß', votes: 18204 },
        { label: 'Ich huste seit gestern', votes: 41892 },
        { label: 'Ich sehe Mäuse, die nicht da sind', votes: 87603 },
        { label: 'Ich BIN eine Maus', votes: 219448 },
      ],
    },
    expert: {
      label: 'STIMME DES EXPERTEN (BEZAHLT)',
      quote: '"Wer diesen Artikel zu Ende liest, OHNE zu husten — ist wahrscheinlich schon tot. Oder hat eines meiner Bücher gekauft. Beides möglich. Buch-Link in der Bio."',
      name: 'Dr. Hans-Jürgen Mauspranger',
      title: 'Selbsternannter Hanta-Experte · 1.4M Follower · Dr. (TCM, Fernkurs, 3 Wochenenden)',
    },
    citizens: {
      title: 'STIMMEN AUS DEM VOLK (HONORAR: JE 25 €)',
      items: [
        { name: 'Renate, 71, Hamburg', quote: 'Nachbarin sah eine Maus. Heute ist sie tot. Eigentlich Krebs, aber das war BESTIMMT auch das Hanta.' },
        { name: 'Kevin-Steve, 28, Wolfsburg', quote: 'Habe meine Katze entlassen. Zu nett zu Mäusen. GoFundMe gestartet, weil Tierheim Geld will.' },
        { name: 'Petra, 54, Recklinghausen', quote: 'Habe 380 Rollen Klopapier. Mein Mann auch. Wir reden seit Mittwoch nicht mehr. UNS gehts BLENDEND.' },
        { name: 'Hans-Werner, 64, Pforzheim', quote: 'Früher hatten wir Hanta auch. Da hat man geschwiegen, geraucht und mit 58 wäre man eh tot gewesen.' },
      ],
    },
    shop: {
      title: 'HANTA-SHOP — ÜBERLEBEN SIE.',
      sub: 'Direkt aus der Redaktion empfohlen. Nicht von uns hergestellt. Nicht haftbar.',
      items: [
        { name: 'HantaShield™ FFP-9 Maske (Promi-Doc-Edition)', price: '149,99 €', tag: 'NUR NOCH 3', img: 'face-mask,n95,medical' },
        { name: 'Toilettenpapier „Letzte Reserve" 4-lagig', price: '89,99 €/Rolle', tag: 'PANIK-PREIS', img: 'toilet-paper,roll' },
        { name: 'Mäuseschreck PRO (Ultraschall, vermutlich)', price: '299,00 €', tag: 'BESTSELLER', img: 'speaker,electronics,gadget' },
        { name: 'Bunker-Brot 30J. haltbar (vom 2019)', price: '89,90 €/Stk', tag: 'NEU', img: 'bread,loaf,old' },
        { name: 'Kupfer-Tinktur „Anti-Hanta"', price: '64,50 €', tag: 'AUSVERKAUFT', img: 'bottle,vial,brown' },
        { name: 'Lebensversicherung HANTA-PLUS', price: 'ab 4,99/Mo', tag: 'KEINE AUSZAHLUNG', img: 'document,paper,signature' },
      ],
    },
    horoscope: {
      title: 'IHR HANTA-HOROSKOP',
      items: [
        { sign: 'Widder',  text: 'Sie sind heute besonders empfänglich. Lüften Sie nicht.' },
        { sign: 'Stier',   text: 'Eine Maus denkt an Sie. Wahrscheinlich Ihr Steuerberater.' },
        { sign: 'Zwillinge', text: 'Sie werden gehantet. Wahrscheinlich schon passiert.' },
        { sign: 'Krebs',   text: 'Husten Sie? Sie sollten husten.' },
        { sign: 'Löwe',    text: 'Mut hilft nicht. Kupfer hilft. Kupfer kostet 64,50 €. Siehe Seite 13.' },
        { sign: 'Jungfrau',text: 'Putzen ist gut. Schreien ist besser.' },
        { sign: 'Waage',   text: 'Wägen Sie ab: Leben oder Käse.' },
        { sign: 'Skorpion',text: 'Sie überleben. Der Rest nicht.' },
        { sign: 'Schütze', text: 'Schießen Sie auf Mäuse. Sicherheitshalber.' },
        { sign: 'Steinbock', text: 'Sie sind bereits hanted. Ihre Krankenkasse weiß es seit März.' },
        { sign: 'Wassermann', text: 'Wasser KOCHEN. Stadtwerke berechnen den Mehrverbrauch trotzdem.' },
        { sign: 'Fische',  text: 'Schwimmen Sie weg. So weit Sie können.' },
      ],
    },
    comments: {
      title: 'LESERBRIEFE — UNGEFILTERT',
      sub: '4.183 Kommentare · sortiert nach: WUTPEGEL',
      items: [
        { name: 'Frieda1947', meta: '✓ Verifiziert · 8.2k Likes', text: 'WANN endlich Lockdown??? Meine Enkel KÖNNTEN sterben und niemand tut WAS!!!! Schande Deutschland!!!' },
        { name: 'WahrheitsSucher_88', meta: '847 Likes', text: 'Hanta — anagram von ATHAN — siehe Offenbarung 9:11. Es kommt alles raus. WACHT AUF SCHAFE.' },
        { name: 'Manuela_aus_dem_Schwarzwald', meta: '12.4k Likes', text: 'Ich gehe seit 3 Wochen nicht mehr aus dem Haus. Mein Mann auch nicht. Wir essen Brot aus 2019. Es geht uns BLENDEND.' },
        { name: 'OlafScholz1948', meta: '–4.1k Likes · 47x GEMELDET · GELÖSCHT', text: 'Vielleicht einfach mal die Zahlen lesen statt panisch werd…' },
        { name: 'Renate_Reloaded', meta: '6.7k Likes', text: 'Mein Hausarzt sagte: "Sie haben nichts." Ich habe ihn ANGEZEIGT. Ich KENNE meinen Körper. Ich BIN hanted.' },
        { name: 'TruckerKurt_NRW', meta: '3.2k Likes', text: 'Hab heute auf der A2 eine Maus überfahren. WAR DAS BEWEIS GENUG??? Polizei wollte nichts wissen. Typisch.' },
      ],
    },
    korrektur: {
      title: 'KORREKTUR & FAKTENCHECK',
      body: 'In dieser Ausgabe wurden 47 Tatsachen überspitzt dargestellt. Zur Einordnung: Hantaviren werden in Deutschland fast ausschließlich durch Einatmen von Staub übertragen, der mit Ausscheidungen infizierter Nagetiere kontaminiert ist; eine Mensch-zu-Mensch-Übertragung kommt in Europa praktisch nicht vor. In Deutschland werden je nach Jahr 100–600 Fälle pro Jahr gemeldet (Quelle: RKI), hauptsächlich Puumala-Virus. Die meisten Verläufe sind grippeähnlich; schwere Verläufe sind selten. Vorbeugung: Keller und Schuppen vor dem Putzen ausgiebig lüften, Staub binden, Atemschutz tragen, Mäusebefall fachgerecht beseitigen. Es gibt keinen Anlass für Massenpanik. Bei Verdacht: Hausarzt, nicht Hanta-Hotline.',
      cta: 'Diese Korrektur erscheint absichtlich auf Seite 47 in Schriftgröße 6.',
    },
    foot: {
      imprint: 'Impressum · Redaktion: ein Praktikant · Auflage: solange der Strom hält',
      copyright: '© HANTA-BLATT · Alle Rechte vorbehalten · Niemand verklagt uns, weil alle hanted sind',
    },
  },
  en: {
    masthead: 'THE DAILY HANTA',
    tagline: 'BRITAIN\'S MOST FEARED PLAGUE PAPER',
    paywall: 'Subscribe to HANTA+ — £1/month — BEFORE THE COUGH GETS YOU',
    breaking: 'BREAKING',
    live: 'LIVE',
    hantaTicker: 'HANTA-TICKER',
    casesTotal: 'CONFIRMED CASES',
    casesToday: 'NEW IN LAST 24H',
    deaths: 'DEATHS',
    suspected: 'SUSPECTED',
    rValue: 'R-VALUE',
    breakingFeed: [
      '🔴 BREAKING: 4th region declares state of emergency',
      '⚠ NHS hospital under quarantine — staff "in tears"',
      '💀 ANOTHER DEAD in Manchester — he was only 34',
      '🐀 TESCO recalls flour — "mouse droppings suspected"',
      '😱 SAGE adviser: "It will be worse than Covid"',
      '🚨 SCHOOLS to shut from Monday — confirmed',
      '📉 FTSE plunges — Hanta-shock spooks markets',
      '🤐 WHISTLEBLOWER: "They\'ve been hiding numbers since March"',
      '⛪ Archbishop: "We pray for the nation"',
      '🧀 Hanta-mouse filmed in Sainsbury\'s cheese aisle',
    ],
    hero: {
      kicker: 'EXCLUSIVE — ONLY HERE!',
      title: 'IT\'S OFFICIAL: BRITAIN IS HANTED.',
      sub: 'Cases are exploding. The government won\'t answer. What you NEED to know NOW — and the two foods you should bin IMMEDIATELY.',
      byline: 'By H. J. Mousepranger · 4 min read · Today, 06:14',
    },
    headlines: [
      { kicker: 'CONFIRMED', title: 'First death in Yorkshire — doctors call it "the turning point"', tag: 'POLITICS' },
      { kicker: 'FLASH POLL', title: '67% of Brits now PANIC at sight of mice — and rising', tag: 'SOCIETY' },
      { kicker: 'REVEALED', title: 'WHAT WHITEHALL ISN\'T TELLING YOU — internal emails LEAKED', tag: 'EXCLUSIVE' },
      { kicker: 'DRAMA', title: 'Mum (32) coughs blood: "I thought it was just hay fever"', tag: 'REAL LIFE' },
      { kicker: 'DOCS WARN', title: '"Wear a mask OR DIE" — Celebrity GP doubles down', tag: 'HEALTH' },
      { kicker: 'SHOCK-VIDEO', title: 'Mouse runs through bakery — branch SHUT', tag: 'VIRAL' },
      { kicker: 'HOTSPOT', title: 'These 7 cities you should AVOID right now', tag: 'MAP' },
      { kicker: 'MONEY', title: 'Shares of these 3 mousetrap firms are EXPLODING', tag: 'MARKETS' },
    ],
    map: {
      title: 'LIVE MAP: WHERE HANTA IS LURKING',
      caption: 'Each red dot is a suspected case. Updates every 14 seconds.',
      legend: ['SUSPECTED', 'CONFIRMED', 'DEAD', 'EVACUATED'],
    },
    poll: {
      title: 'ARE YOU HANTED?',
      sub: 'Click honestly. We\'ll forward to the NHS anonymously.*',
      footnote: '*We won\'t.',
      options: [
        { label: 'I feel great', votes: 4127 },
        { label: 'I feel a bit hot', votes: 18204 },
        { label: 'I\'ve been coughing since yesterday', votes: 41892 },
        { label: 'I see mice that aren\'t there', votes: 87603 },
        { label: 'I AM a mouse', votes: 219448 },
      ],
    },
    expert: {
      label: 'EXPERT VOICE',
      quote: '"If you can finish reading this article WITHOUT coughing — you\'re probably already dead. Hanta works silently. It knows your name."',
      name: 'Dr. H. J. Mousepranger',
      title: 'Self-styled hanta-expert · 1.4M followers · PhD (TCM, online)',
    },
    citizens: {
      title: 'VOICES FROM THE PEOPLE',
      items: [
        { name: 'Doreen, 71, Hull', quote: 'My neighbour saw a mouse yesterday. TODAY she\'s dead. What does that tell us? EVERYTHING.' },
        { name: 'Kevin-Steve, 28, Slough', quote: 'I\'ve sacked the cat. Too soft on mice. Liability animal.' },
        { name: 'Anon., London', quote: 'I live in a flat. ABOVE me. 4th floor. Mice CAN climb, this is FACT.' },
        { name: 'Reg, 64, Bristol', quote: 'Back in our day we had hanta too. We just got on with it. Nowadays everyone\'s soft.' },
      ],
    },
    shop: {
      title: 'THE HANTA SHOP — SURVIVE.',
      sub: 'Editor\'s picks. Not made by us. We are not liable.',
      items: [
        { name: 'HantaShield™ FFP-9 mask',     price: '£149.99', tag: 'ONLY 3 LEFT', img: 'face-mask,n95,medical' },
        { name: 'MouseFear PRO (ultrasonic)',  price: '£299.00', tag: 'BESTSELLER', img: 'speaker,electronics,gadget' },
        { name: 'Bunker-Bread 30yr shelf life', price: '£89.90 ea', tag: 'NEW', img: 'bread,loaf,old' },
        { name: 'Copper Tincture "Anti-Hanta"', price: '£64.50',  tag: 'SOLD OUT', img: 'bottle,vial,brown' },
        { name: 'Guardian Angel sticker',      price: '£12.99',  tag: 'BLESSED', img: 'sticker,paper' },
        { name: 'HANTA+ life insurance',       price: 'from £4.99/mo', tag: 'NEW', img: 'document,paper,signature' },
      ],
    },
    horoscope: {
      title: 'YOUR HANTA HOROSCOPE',
      items: [
        { sign: 'Aries',  text: 'You\'re extra susceptible today. Don\'t open windows.' },
        { sign: 'Taurus', text: 'A mouse is thinking about you right now.' },
        { sign: 'Gemini', text: 'You will be hanted. Probably already happened.' },
        { sign: 'Cancer', text: 'Coughing? You should be coughing.' },
        { sign: 'Leo',    text: 'Bravery won\'t help. Copper will.' },
        { sign: 'Virgo',  text: 'Cleaning helps. Screaming helps more.' },
        { sign: 'Libra',  text: 'Weigh it: life, or cheese.' },
        { sign: 'Scorpio',text: 'You survive. The rest don\'t.' },
        { sign: 'Sagittarius',text: 'Shoot mice. Just to be safe.' },
        { sign: 'Capricorn',  text: 'You\'re already hanted. Accept it.' },
        { sign: 'Aquarius',   text: 'BOIL water. Even bottled.' },
        { sign: 'Pisces', text: 'Swim away. As far as you can.' },
      ],
    },
    comments: {
      title: 'READERS\' LETTERS — UNFILTERED',
      sub: '4,183 comments · sorted by: RAGE LEVEL',
      items: [
        { name: 'Doris1947', meta: '✓ Verified · 8.2k likes', text: 'WHEN will we get a lockdown??? My grandkids COULD die and nobody is doing ANYTHING!!!! Shame on Britain!!!' },
        { name: 'TruthSeeker_88', meta: '847 likes', text: 'Hanta — anagram of ATHAN — see Revelation 9:11. It\'s ALL coming out. WAKE UP SHEEPLE.' },
        { name: 'Margaret_in_Devon', meta: '12.4k likes', text: 'Haven\'t left the house in 3 weeks. Husband neither. Eating bread from 2019. Feeling FANTASTIC.' },
        { name: 'KeirStarmer1948', meta: '–4.1k likes · REPORTED', text: 'Maybe just read the actual numbers before pani…' },
        { name: 'Doris_Reloaded', meta: '6.7k likes', text: 'My GP said "you\'ve got nothing." I REPORTED him. I KNOW my body. I AM hanted.' },
        { name: 'TruckerKev_M62', meta: '3.2k likes', text: 'Ran over a mouse on the M62 today. WASN\'T THAT PROOF ENOUGH??? Police didn\'t care. Typical.' },
      ],
    },
    korrektur: {
      title: 'CORRECTION & FACT-CHECK',
      body: 'In this issue, 47 facts were dramatised for effect. For perspective: hantaviruses are spread almost exclusively by inhaling dust contaminated with the urine, droppings or saliva of infected rodents; person-to-person transmission is essentially absent in Europe. In Germany, depending on the year, roughly 100–600 cases are reported annually (source: RKI), mostly Puumala virus. Most infections are flu-like; severe cases are uncommon. Prevention: ventilate cellars and sheds before cleaning, dampen dust, wear a respirator, deal with rodent infestations professionally. There is no cause for mass panic. If concerned: see a doctor, not a hotline.',
      cta: 'This correction appears intentionally on page 47 in 6-point type.',
    },
    foot: {
      imprint: 'Imprint · Editorial: one intern · Circulation: until the power goes',
      copyright: '© THE DAILY HANTA · All rights reserved · Nobody sues us because everyone is hanted',
    },
  },
};

// ── Helpers ─────────────────────────────────────────────────────────────────
const fmtNum = (n) => Math.floor(n).toLocaleString('de-DE');
const cls = (...xs) => xs.filter(Boolean).join(' ');

function useNow(stepMs = 1000) {
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), stepMs);
    return () => clearInterval(id);
  }, [stepMs]);
  return now;
}

// Live counter — bigger panic = faster ticking. Persists in sessionStorage so
// refresh doesn't reset the spectacle.
function useTicker(initial, ratePerSec, key) {
  const [v, setV] = React.useState(() => {
    try {
      const cached = sessionStorage.getItem(key);
      if (cached) return Number(cached);
    } catch (e) {}
    return initial;
  });
  React.useEffect(() => {
    const id = setInterval(() => {
      setV((cur) => {
        const next = cur + ratePerSec * 0.2 * (0.6 + Math.random() * 0.8);
        try { sessionStorage.setItem(key, String(next)); } catch (e) {}
        return next;
      });
    }, 200);
    return () => clearInterval(id);
  }, [ratePerSec, key]);
  return v;
}

// ── Sub-components ──────────────────────────────────────────────────────────

function UtilityBar({ t, now, lang, panic }) {
  const dateStr = now.toLocaleString(lang === 'de' ? 'de-DE' : 'en-GB', {
    weekday: 'long', day: '2-digit', month: 'long', year: 'numeric',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
  });
  return (
    <div className="util">
      <div className="util-l">
        <span className="util-live"><i />{t.live}</span>
        <span className="util-date">{dateStr.toUpperCase()}</span>
        <span className="util-panic">PANIK-INDEX&nbsp;<b>{panic}/10</b></span>
      </div>
      <div className="util-r">
        <span>WETTER: 14° · Mäusewarnung GELB</span>
        <span>BÖRSE: DAX −4,2%</span>
        <span className="util-pay">★ {t.paywall} ★</span>
      </div>
    </div>
  );
}

function Masthead({ t, panic }) {
  return (
    <header className="mast">
      <div className="mast-row">
        <div className="mast-left">
          <div className="mast-logo">
            <span className="mast-logo-bg">{t.masthead}</span>
          </div>
          <div className="mast-tag">{t.tagline}</div>
        </div>
        <div className="mast-right">
          <div className="mast-edition">AUSGABE Nr. <b>14.872</b></div>
          <div className="mast-price">0,90 € · 1,20 SFr · 0,80 £ · oder Ihr Leben</div>
          <div className="mast-warn" data-shake={panic >= 7 ? '1' : '0'}>
            ⚠ HANTA-WARNSTUFE&nbsp;<b>{Math.min(5, Math.max(1, Math.ceil(panic / 2)))}/5</b>
          </div>
        </div>
      </div>
      <nav className="mast-nav">
        {['HANTA-TICKER','POLITIK','PANIK','SCHICKSAL','SHOP','HOROSKOP','LESER','BUNKER','TV','SPORT','APOKALYPSE'].map((x) => (
          <a key={x} href="#" onClick={(e) => e.preventDefault()}>{x}</a>
        ))}
      </nav>
    </header>
  );
}

function BreakingTicker({ t, panic }) {
  const feed = t.breakingFeed;
  // Continuous marquee — duplicate feed so the loop seam is invisible.
  // Speed scales with panic: low panic = stately crawl, high panic = manic.
  const durSec = Math.max(36, 160 - panic * 10);
  return (
    <div className="brk" data-shake={panic >= 8 ? '1' : '0'}>
      <div className="brk-tag">{t.breaking}</div>
      <div className="brk-marquee">
        <div className="brk-track" style={{ animationDuration: durSec + 's' }}>
          {[...feed, ...feed].map((line, k) => (
            <span key={k} className="brk-item">{line}</span>
          ))}
        </div>
      </div>
      <div className="brk-pulse">●</div>
    </div>
  );
}

function CounterStrip({ t, panic }) {
  // Numbers chosen to sound terrifying but plausible-ish for a tabloid
  const total      = useTicker(184_731, 1.4 + panic * 0.6, 'hanta_total');
  const today      = useTicker(  3_412, 0.25 + panic * 0.18, 'hanta_today');
  const deaths     = useTicker(    284, 0.04 + panic * 0.03, 'hanta_dead');
  const suspected  = useTicker(471_022, 3.2 + panic * 1.1,  'hanta_susp');
  const r          = (1.4 + (panic - 5) * 0.12 + Math.sin(Date.now() / 4000) * 0.04).toFixed(2);
  return (
    <div className="ctr">
      <div className="ctr-title">
        <span className="ctr-pip" />
        {t.hantaTicker}<span className="ctr-mini">— UPDATE LIVE</span>
      </div>
      <div className="ctr-grid">
        <CtrCell label={t.casesTotal}   value={fmtNum(total)}     trend="+1,8%"  big />
        <CtrCell label={t.casesToday}   value={fmtNum(today)}     trend="+340%" alarm />
        <CtrCell label={t.deaths}       value={fmtNum(deaths)}    trend="+12"   alarm />
        <CtrCell label={t.suspected}    value={fmtNum(suspected)} trend="+6,4%" />
        <CtrCell label={t.rValue}       value={r}                 trend="STEIGEND" alarm />
      </div>
    </div>
  );
}

function CtrCell({ label, value, trend, big, alarm }) {
  return (
    <div className={cls('ctr-cell', big && 'is-big', alarm && 'is-alarm')}>
      <div className="ctr-cell-l">{label}</div>
      <div className="ctr-cell-v">{value}</div>
      <div className="ctr-cell-t">{trend}</div>
    </div>
  );
}

function Hero({ t, panic }) {
  return (
    <section className="hero">
      <div className="hero-img">
        <img src="https://loremflickr.com/1100/720/supermarket,empty,shelves,panic?lock=99" alt="" className="tabloid-img" />
        <div className="hero-img-credit">© Leser-Reporter / 47€ Honorar · SYMBOLBILD</div>
        <div className="hero-img-overlay">EXKLUSIV-MATERIAL</div>
      </div>
      <div className="hero-txt">
        <div className="hero-kicker" data-shake={panic >= 9 ? '1' : '0'}>{t.hero.kicker}</div>
        <h1 className="hero-h">{t.hero.title}</h1>
        <p className="hero-sub">{t.hero.sub}</p>
        <div className="hero-byline">{t.hero.byline}</div>
        <ul className="hero-bullets">
          <li>+++ Merz: „Tier-Zungenküsse ab sofort STRAFBAR" +++</li>
          <li>+++ Klopapier-Notstand: Welche 3 Alternativen Sie JETZT brauchen +++</li>
          <li>+++ Bayern hat kein Bier mehr — die wahren Gründe +++</li>
        </ul>
      </div>
    </section>
  );
}

function HeadlineGrid({ t }) {
  const items = t.headlines;
  return (
    <section className="grid">
      {items.map((h, i) => (
        <article key={i} className={cls('card', i === 0 && 'card-lg', i === 3 && 'card-dark')}>
          <div className="card-img">
            <img src={`https://loremflickr.com/640/480/${encodeURIComponent(h.img || 'news')}?lock=${i+1}`} alt="" className="tabloid-img" />
          </div>
          <div className="card-body">
            <span className="card-tag">{h.tag}</span>
            <span className="card-kicker">{h.kicker}</span>
            <h3 className="card-h">{h.title}</h3>
          </div>
        </article>
      ))}
    </section>
  );
}

function HotspotMap({ t, panic }) {
  // Random red dots that pulse
  const dots = React.useMemo(() => {
    const d = [];
    for (let i = 0; i < 80 + panic * 8; i++) {
      d.push({ x: Math.random() * 100, y: Math.random() * 100,
               r: 2 + Math.random() * 6, lvl: Math.random() });
    }
    return d;
  }, [panic]);
  return (
    <section className="map-sec">
      <div className="map-head">
        <h2>{t.map.title}</h2>
        <div className="map-legend">
          {t.map.legend.map((l, i) => (
            <span key={l} className={'map-lg map-lg-' + i}><i />{l}</span>
          ))}
        </div>
      </div>
      <div className="map">
        <svg viewBox="0 0 100 100" preserveAspectRatio="none" className="map-svg">
          {/* fake "germany" outline */}
          <path d="M 35 8 L 55 5 L 70 12 L 78 22 L 82 35 L 85 50 L 80 65 L 78 78 L 70 88 L 55 92 L 40 90 L 25 80 L 18 65 L 15 48 L 18 32 L 25 18 Z"
                fill="rgba(255,255,255,.04)" stroke="rgba(255,255,255,.2)" strokeWidth=".3" />
          {dots.map((d, i) => (
            <circle key={i} cx={d.x} cy={d.y} r={d.r * 0.3}
                    fill={d.lvl > 0.95 ? '#fff' : d.lvl > 0.7 ? '#ffed00' : '#e30613'}
                    opacity={0.7 + Math.random() * 0.3}>
              <animate attributeName="r" values={`${d.r * 0.2};${d.r * 0.5};${d.r * 0.2}`}
                       dur={(2 + Math.random() * 3) + 's'} repeatCount="indefinite" />
            </circle>
          ))}
        </svg>
        <div className="map-overlay">
          <div className="map-pin" style={{ left: '52%', top: '18%' }}>
            <div className="map-pin-d" /><div className="map-pin-l">HAMBURG · 14 NEU</div>
          </div>
          <div className="map-pin" style={{ left: '60%', top: '60%' }}>
            <div className="map-pin-d" /><div className="map-pin-l">MÜNCHEN · 38 NEU</div>
          </div>
          <div className="map-pin" style={{ left: '38%', top: '40%' }}>
            <div className="map-pin-d" /><div className="map-pin-l">DÜSSELDORF · 22 NEU</div>
          </div>
        </div>
      </div>
      <div className="map-cap">{t.map.caption}</div>
    </section>
  );
}

function Poll({ t }) {
  const [picked, setPicked] = React.useState(null);
  const total = t.poll.options.reduce((s, o) => s + o.votes, 0);
  return (
    <section className="poll" id="poll">
      <h2 className="poll-h">{t.poll.title}</h2>
      <p className="poll-sub">{t.poll.sub}</p>
      <div className="poll-list">
        {t.poll.options.map((o, i) => {
          const pct = (o.votes / total) * 100;
          const on = picked === i;
          return (
            <button key={i} className={cls('poll-row', on && 'is-on')}
                    onClick={() => setPicked(i)}>
              <div className="poll-row-bg" style={{ width: pct.toFixed(1) + '%' }} />
              <span className="poll-row-l">{o.label}</span>
              <span className="poll-row-r">{fmtNum(o.votes)} · {pct.toFixed(1)}%</span>
            </button>
          );
        })}
      </div>
      <p className="poll-foot">{t.poll.footnote} · {fmtNum(total)} Stimmen heute</p>
    </section>
  );
}

function ExpertQuote({ t }) {
  return (
    <section className="expert">
      <div className="expert-label">{t.expert.label}</div>
      <blockquote className="expert-q">{t.expert.quote}</blockquote>
      <div className="expert-meta">
        <div className="expert-avatar">
          <img src="https://loremflickr.com/200/200/scientist,old,man,glasses?lock=42" alt="" className="tabloid-img" />
        </div>
        <div>
          <div className="expert-name">{t.expert.name}</div>
          <div className="expert-title">{t.expert.title}</div>
        </div>
      </div>
    </section>
  );
}

function Citizens({ t }) {
  return (
    <section className="cit">
      <h2 className="cit-h">{t.citizens.title}</h2>
      <div className="cit-grid">
        {t.citizens.items.map((c, i) => (
          <div key={i} className="cit-card">
            <div className="cit-quote">„{c.quote}"</div>
            <div className="cit-by">— {c.name}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function PanicShop({ t }) {
  return (
    <section className="shop">
      <div className="shop-head">
        <h2>{t.shop.title}</h2>
        <p>{t.shop.sub}</p>
      </div>
      <div className="shop-grid">
        {t.shop.items.map((p, i) => (
          <div key={i} className="shop-card">
            <div className="shop-img">
              <img src={`https://loremflickr.com/320/320/${encodeURIComponent(p.img || 'product')}?lock=${i+10}`} alt="" className="tabloid-img tabloid-img--product" />
              <div className="shop-tag">{p.tag}</div>
            </div>
            <div className="shop-name">{p.name}</div>
            <div className="shop-row">
              <div className="shop-price">{p.price}</div>
              <button className="shop-btn">JETZT KAUFEN ↗</button>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

function Horoscope({ t }) {
  return (
    <section className="horo">
      <h2 className="horo-h">{t.horoscope.title}</h2>
      <div className="horo-grid">
        {t.horoscope.items.map((h, i) => (
          <div key={i} className="horo-card">
            <div className="horo-sign">♈ {h.sign}</div>
            <div className="horo-txt">{h.text}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function Comments({ t }) {
  return (
    <section className="cmt">
      <div className="cmt-head">
        <h2>{t.comments.title}</h2>
        <span>{t.comments.sub}</span>
      </div>
      <div className="cmt-list">
        {t.comments.items.map((c, i) => (
          <div key={i} className="cmt-row">
            <div className="cmt-avatar">{c.name[0]}</div>
            <div className="cmt-body">
              <div className="cmt-meta">
                <b>{c.name}</b><span>{c.meta}</span>
              </div>
              <div className="cmt-text">{c.text}</div>
              <div className="cmt-actions">
                <span>▲ Like</span><span>▼</span><span>↩ Antworten</span>
                <span>⚠ Melden</span>
              </div>
            </div>
          </div>
        ))}
      </div>
      <div className="cmt-more">2.117 weitere Kommentare laden ▾</div>
    </section>
  );
}

function Korrektur({ t }) {
  return (
    <section className="korr">
      <div className="korr-tag">{t.korrektur.title}</div>
      <p className="korr-body">{t.korrektur.body}</p>
      <p className="korr-cta">{t.korrektur.cta}</p>
    </section>
  );
}

function Footer({ t }) {
  return (
    <footer className="foot">
      <div className="foot-l">{t.foot.imprint}</div>
      <div className="foot-r">{t.foot.copyright}</div>
    </footer>
  );
}

// ── Root ────────────────────────────────────────────────────────────────────
function App() {
  const [tw, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const baseT = L[tw.lang];
  const paper = (PAPERS[tw.paper] && PAPERS[tw.paper][tw.lang]) || PAPERS.handels[tw.lang];
  const t = { ...baseT, masthead: paper.masthead, tagline: paper.tagline };
  const pal = PALETTES[tw.palette];
  const now = useNow(1000);
  const panic = tw.panic;

  // Push palette + panic into CSS variables so styles can react.
  React.useEffect(() => {
    const r = document.documentElement;
    r.style.setProperty('--c-red',    pal.red);
    r.style.setProperty('--c-yellow', pal.yellow);
    r.style.setProperty('--c-ink',    pal.ink);
    r.style.setProperty('--c-paper',  pal.paper);
    r.style.setProperty('--c-muted',  pal.muted);
    r.style.setProperty('--c-accent', pal.accent);
    r.style.setProperty('--panic',    String(panic));
    r.dataset.palette = tw.palette;
    r.dataset.panic = String(panic);
  }, [pal, panic, tw.palette]);

  return (
    <div className="page" data-palette={tw.palette} data-panic={panic}>
      <UtilityBar t={t} now={now} lang={tw.lang} panic={panic} />
      <Masthead t={t} panic={panic} />
      <BreakingTicker t={t} panic={panic} />
      <CounterStrip t={t} panic={panic} />
      <Hero t={t} panic={panic} />
      <HeadlineGrid t={t} />
      <HotspotMap t={t} panic={panic} />
      <Poll t={t} />
      <ExpertQuote t={t} />
      <Citizens t={t} />
      <PanicShop t={t} />
      <Horoscope t={t} />
      <Comments t={t} />
      <Korrektur t={t} />
      <Footer t={t} />

      <TweaksPanel title="Hanta-Tweaks">
        <TweakSection label="Zeitungstitel">
          <TweakSelect label="Titel" value={tw.paper}
                       options={[
                         { value: 'handels', label: 'HAN-DELSBLATT' },
                         { value: 'zeit',    label: 'DIE HANTA-ZEIT' },
                         { value: 'spiegel', label: 'HANTASPIEGEL' },
                         { value: 'taz',     label: 'HAN-TAZ' },
                         { value: 'express', label: 'HANTA-EXPRESS' },
                         { value: 'blatt',   label: 'HANTA-BLATT (alt)' },
                       ]}
                       onChange={(v) => setTweak('paper', v)} />
        </TweakSection>
        <TweakSection label="Panik-Level">
          <TweakSlider label="Panik" value={tw.panic} min={0} max={10} step={1}
                       onChange={(v) => setTweak('panic', v)} />
        </TweakSection>
        <TweakSection label="Sprache / Language">
          <TweakRadio label="Sprache" value={tw.lang}
                      options={[{ value: 'de', label: 'DE' }, { value: 'en', label: 'EN' }]}
                      onChange={(v) => setTweak('lang', v)} />
        </TweakSection>
        <TweakSection label="Farbschema">
          <TweakRadio label="Schema" value={tw.palette}
                      options={[
                        { value: 'klassik',     label: 'Klassik' },
                        { value: 'leichenhaus', label: 'Morgue' },
                        { value: 'strahlung',   label: 'Strahlung' },
                      ]}
                      onChange={(v) => setTweak('palette', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
