🎮ArcadeLab

Jantjes nümmiger Blumenladen

by HyperPenguin12
757 lines47.4 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Jantjes nümmiger Blumenladen</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;0,9..144,900;1,9..144,500&family=Nunito:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
  :root{
    --paper:#F4EEE0; --paper-2:#EDE4D2;
    --sage-deep:#55654E; --sage:#7C8F70; --sage-soft:#DBE2CC;
    --clay:#B26B47; --rose-soft:#EBCBCB;
    --wood:#9C6B44; --wood-dark:#6E4A2B;
    --ink:#443A2E; --ink-soft:#7A6E5D; --gold:#C69A3E;
    --shadow:rgba(68,58,46,.16);
  }
  *{box-sizing:border-box;-webkit-tap-highlight-color:transparent;margin:0;padding:0}
  html,body{height:100%;overflow:hidden}
  body{font-family:'Nunito',sans-serif;color:var(--ink);
    background:radial-gradient(120% 80% at 50% -10%,#FBF6EA 0%,var(--paper) 45%,var(--paper-2) 100%);
    display:flex;justify-content:center;-webkit-user-select:none;user-select:none;touch-action:manipulation}
  #app{width:100%;max-width:480px;height:100dvh;height:100vh;position:relative;display:flex;flex-direction:column;
    padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)}

  /* ---------- header ---------- */
  header{position:relative;z-index:6;padding:10px 14px 6px;display:flex;align-items:center;justify-content:space-between;gap:8px}
  .brand h1{font-family:'Fraunces',serif;font-weight:900;font-size:15.5px;color:var(--sage-deep);line-height:1.1}
  .brand .sub{font-size:10px;color:var(--ink-soft);font-style:italic;font-family:'Fraunces',serif}
  .stats{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
  .chip{background:#fff;border:1.5px solid var(--paper-2);border-radius:999px;padding:4px 9px;font-weight:800;font-size:12px;
    display:flex;gap:4px;align-items:center;box-shadow:0 2px 0 var(--shadow)}
  .chip small{font-weight:700;color:var(--ink-soft);font-size:10px}
  .iconbtn{width:34px;height:34px;border-radius:11px;border:1.5px solid var(--paper-2);background:#fff;font-size:15px;
    box-shadow:0 2px 0 var(--shadow);cursor:pointer;display:grid;place-items:center}
  .iconbtn:active{transform:translateY(1px)}
  .repbar{height:5px;background:var(--paper-2);border-radius:99px;margin:4px 14px 0;overflow:hidden;position:relative;z-index:6}
  .repbar i{display:block;height:100%;background:linear-gradient(90deg,#C88686,#C69A3E);border-radius:99px;transition:width .5s ease}

  /* ====================== SCENE ====================== */
  .scene{position:relative;flex:1 1 auto;min-height:230px;margin:6px 12px 0;border-radius:20px 20px 0 0;overflow:hidden;
    background:linear-gradient(180deg,#E7D6BE 0%,#E0CBAC 58%,#D8BF9C 58.2%,#D3B893 100%);
    border:1.5px solid var(--paper-2);border-bottom:none}
  .wall{position:absolute;left:0;right:0;top:0;height:58%;
    background:repeating-linear-gradient(90deg,#EADBC4 0 14px,#E4D3B9 14px 28px);border-bottom:3px solid #C9A87E}
  .wall.fancy{background:
    radial-gradient(circle at 10px 10px, rgba(200,134,134,.18) 3px, transparent 4px) 0 0/34px 34px,
    repeating-linear-gradient(90deg,#F0DFC6 0 14px,#EAD7BB 14px 28px)}
  .wall::after{content:"";position:absolute;left:0;right:0;bottom:0;height:8px;background:#B98F60}
  .floor{position:absolute;left:0;right:0;bottom:0;height:42%;
    background:repeating-linear-gradient(90deg,rgba(122,83,52,.22) 0 2px,transparent 2px 46px),linear-gradient(180deg,#C79A6E,#B4855A)}

  /* window with sky */
  .window{position:absolute;left:5%;top:7%;width:19%;height:32%;border:5px solid #7C5A38;border-radius:8px 8px 3px 3px;
    overflow:hidden;z-index:2;box-shadow:0 3px 6px rgba(0,0,0,.15)}
  .sky{position:absolute;inset:0;transition:background 1s}
  .sky.morgen{background:linear-gradient(180deg,#BFE0EA,#F3E4C2)}
  .sky.mittag{background:linear-gradient(180deg,#9CCDE4,#D8ECF2)}
  .sky.abend{background:linear-gradient(180deg,#E7A97C,#F0D2A0)}
  .sky .sun{position:absolute;width:16px;height:16px;border-radius:50%;background:#FFE9A8;box-shadow:0 0 12px 4px rgba(255,233,168,.8);transition:left 1.4s ease,top 1.4s ease,background 1.4s,box-shadow 1.4s,opacity .8s}
  .sky.morgen .sun{left:10%;top:56%}
  .sky.mittag .sun{left:46%;top:10%}
  .sky.abend .sun{left:74%;top:58%;background:#FFB870;box-shadow:0 0 14px 5px rgba(255,160,80,.85)}
  .sky .cloud{position:absolute;left:8%;top:40%;font-size:14px;opacity:.9;transition:.8s}
  .sky .cloud2{position:absolute;right:6%;top:16%;font-size:12px;opacity:0;transition:.8s}
  .sky .rain{position:absolute;inset:0;opacity:0;transition:.8s;
    background:repeating-linear-gradient(105deg,rgba(110,145,180,.55) 0 1px,transparent 1px 7px);
    animation:rainfall .45s linear infinite}
  @keyframes rainfall{to{background-position:-12px 22px}}
  .sky.rainy{filter:saturate(.75) brightness(.9)}
  .sky.rainy .sun{opacity:.2}
  .sky.rainy .rain{opacity:1}
  .sky.rainy .cloud2{opacity:.95}
  .window .cross{position:absolute;left:50%;top:0;bottom:0;width:3px;background:#7C5A38;transform:translateX(-50%)}
  .window .crossh{position:absolute;top:50%;left:0;right:0;height:3px;background:#7C5A38;transform:translateY(-50%)}

  .hang{position:absolute;top:-2px;font-size:30px;z-index:2;filter:drop-shadow(0 3px 2px rgba(0,0,0,.12))}
  .hang.l{left:27%;transform:scaleX(-1)} .hang.r{right:28%}
  .lamp{position:absolute;top:-4px;left:50%;transform:translateX(-50%);z-index:2;font-size:26px;display:none}
  .lamp.on{display:block}
  .lamp::after{content:"";position:absolute;left:50%;top:22px;transform:translateX(-50%);width:70px;height:60px;
    background:radial-gradient(ellipse at top,rgba(255,230,160,.4),transparent 70%);pointer-events:none}

  .shelfBoard{position:absolute;left:27%;right:26%;height:8px;background:linear-gradient(180deg,#B07E4E,#8A5F38);
    border-radius:3px;box-shadow:0 3px 4px rgba(0,0,0,.15);z-index:2}
  .shelfBoard.s1{top:16%} .shelfBoard.s2{top:34%}
  .pots{position:absolute;left:27%;right:26%;display:flex;justify-content:space-around;align-items:flex-end;z-index:3}
  .pots.p1{top:5%} .pots.p2{top:23%}
  .pot{text-align:center;line-height:.8}
  .pot .bloom{font-size:20px;display:block}
  .pot .cup{display:inline-block;width:14px;height:10px;background:linear-gradient(180deg,#C6784E,#A85E38);border-radius:2px 2px 5px 5px;margin-top:-2px}
  .bigplants{position:absolute;right:6.5%;bottom:31%;font-size:34px;z-index:3;filter:drop-shadow(0 4px 3px rgba(0,0,0,.15))}
  .collie{position:absolute;left:2.5%;bottom:29.5%;z-index:4;filter:drop-shadow(0 3px 2px rgba(0,0,0,.15))}
  .collie .tail{transform-origin:78px 36px}
  .collie.awake .tail{animation:wag .32s ease-in-out infinite alternate}
  @keyframes wag{from{transform:rotate(-14deg)}to{transform:rotate(18deg)}}
  .collie .eye{transform-origin:22px 24px;transition:transform .3s}
  .collie.sleep .eye{transform:scaleY(.12)}
  .collie .zzz{opacity:0;transition:.5s}
  .collie.sleep .zzz{opacity:1;animation:zfloat 2.2s ease-in-out infinite}
  @keyframes zfloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
  @media (prefers-reduced-motion: reduce){.collie.awake .tail,.collie.sleep .zzz,.sky .rain{animation:none}}
  .frame{position:absolute;left:7%;top:44%;width:38px;height:30px;background:#EAD9BE;border:4px solid #9C7649;border-radius:3px;z-index:2;
    display:grid;place-items:center;font-size:14px}

  /* door */
  .door{position:absolute;right:4%;top:5%;width:21%;height:64%;z-index:2;
    background:linear-gradient(180deg,rgba(197,222,214,.75),rgba(170,205,197,.55));
    border:5px solid #7C5A38;border-radius:6px 6px 0 0;box-shadow:0 4px 8px rgba(0,0,0,.18);transform-origin:left center}
  .door.ring{animation:doorswing 1s ease}
  @keyframes doorswing{0%{transform:perspective(400px) rotateY(0)}30%{transform:perspective(400px) rotateY(-26deg)}100%{transform:perspective(400px) rotateY(0)}}
  .door .panes{position:absolute;inset:6px;display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr 1fr;gap:5px}
  .door .panes span{background:linear-gradient(135deg,rgba(255,255,255,.55),rgba(255,255,255,.12));border-radius:2px;border:1px solid rgba(124,90,56,.5)}
  .door .knob{position:absolute;left:3px;top:52%;width:7px;height:7px;border-radius:50%;background:#C69A3E;box-shadow:0 0 0 2px #8a6a2a}
  .door .sign{position:absolute;top:30%;left:50%;transform:translateX(-50%) rotate(-4deg);background:#B26B47;color:#FBF2E3;
    font-family:'Fraunces',serif;font-weight:700;font-size:8.5px;padding:2px 6px;border-radius:3px;white-space:nowrap}
  .bell{position:absolute;right:6%;top:2%;font-size:18px;z-index:4;transform-origin:top center}
  .bell.ring{animation:bellswing .8s ease}
  @keyframes bellswing{0%,100%{transform:rotate(0)}20%{transform:rotate(24deg)}45%{transform:rotate(-18deg)}70%{transform:rotate(10deg)}}

  .customer{position:absolute;left:50%;bottom:29%;transform:translateX(-50%);width:112px;z-index:4;
    filter:drop-shadow(0 6px 5px rgba(0,0,0,.18))}
  .customer.enter{animation:cust .55s cubic-bezier(.34,1.4,.6,1)}
  @keyframes cust{0%{opacity:0;transform:translateX(-50%) translateY(16px)}100%{opacity:1;transform:translateX(-50%) translateY(0)}}

  .bubble{position:absolute;left:8px;top:6px;max-width:64%;z-index:5;background:#fff;border:1.5px solid var(--paper-2);
    border-radius:13px;padding:7px 10px;box-shadow:0 4px 10px rgba(0,0,0,.12);display:none}
  .bubble.show{display:block}
  .bubble .nm{font-size:10px;font-weight:800;color:var(--sage-deep);letter-spacing:.3px}
  .bubble .g{font-size:12px;line-height:1.3;display:block}
  .bubble .mood{display:block;margin-top:2px;font-family:'Fraunces',serif;font-style:italic;font-size:12px;color:var(--clay);font-weight:600}
  .bubble .wants{display:flex;gap:4px;margin-top:5px;flex-wrap:wrap}
  .wantTag{font-size:10px;font-weight:800;padding:2px 7px;border-radius:999px;background:var(--rose-soft);color:#8a4b4b;border:1.5px solid #e3b6b6}
  .wantTag.count{background:var(--sage-soft);color:#4c5a44;border-color:#c7d3b5}
  .wantTag.hate{background:#f3dede;color:#a04040;border-color:#dfb0b0;text-decoration:line-through}
  .wantTag.recipe{background:#fff4d6;color:#8a6a1a;border-color:#e8d090;font-size:12px}
  .bubble::after{content:"";position:absolute;bottom:-8px;left:26px;width:14px;height:14px;background:#fff;
    border-right:1.5px solid var(--paper-2);border-bottom:1.5px solid var(--paper-2);transform:rotate(45deg)}
  .qcount{position:absolute;right:8px;top:6px;z-index:5;background:rgba(68,58,46,.55);color:#f3e7cf;font-size:10px;font-weight:800;
    padding:3px 9px;border-radius:999px}

  .counter{position:absolute;left:0;right:0;bottom:0;height:31%;z-index:5}
  .counter .top{position:absolute;top:0;left:0;right:0;height:14px;background:linear-gradient(180deg,#C79A6E,#A9794E);
    border-top:2px solid #D9B98C;border-radius:4px 4px 0 0;box-shadow:0 -2px 4px rgba(0,0,0,.12)}
  .counter .front{position:absolute;top:14px;left:0;right:0;bottom:0;
    background:repeating-linear-gradient(90deg,rgba(255,255,255,.05) 0 2px,transparent 2px 38px),linear-gradient(180deg,#8A5F38,#6E4A2B)}
  .counter .front::before,.counter .front::after{content:"";position:absolute;top:8px;bottom:8px;width:30%;
    border:2px solid rgba(255,255,255,.12);border-radius:5px}
  .counter .front::before{left:8%} .counter .front::after{right:8%}
  .deco-radio{position:absolute;left:7%;bottom:27%;font-size:24px;z-index:6;display:none}
  .deco-cat{position:absolute;right:7%;bottom:27.5%;font-size:26px;z-index:6;display:none;animation:catbreathe 3s ease-in-out infinite}
  @keyframes catbreathe{0%,100%{transform:scale(1)}50%{transform:scale(1.04)}}
  .deco-radio.on,.deco-cat.on{display:block}
  .deco-window{position:absolute;left:5%;top:41%;font-size:15px;z-index:3;display:none}
  .deco-window.on{display:block}

  .vase{position:absolute;left:50%;bottom:25%;transform:translateX(-50%);z-index:6;min-width:110px;max-width:74%;min-height:48px;
    display:flex;align-items:flex-end;justify-content:center;flex-wrap:wrap;gap:0 1px;padding:6px 10px 8px}
  .vase::before{content:"";position:absolute;bottom:-4px;left:50%;transform:translateX(-50%);width:50px;height:28px;
    background:linear-gradient(180deg,#8FA3D6,#5E74B0);border-radius:8px 8px 14px 14px;
    box-shadow:inset 0 3px 6px rgba(255,255,255,.35),0 3px 4px rgba(0,0,0,.2);z-index:-1}
  .vase .hint{font-size:10.5px;color:#5b4a34;background:rgba(255,255,255,.75);padding:3px 8px;border-radius:8px;text-align:center}
  .stem{font-size:31px;line-height:.85;cursor:pointer;animation:pop .35s cubic-bezier(.34,1.56,.64,1);transform-origin:bottom center;margin:0 -2px}
  @keyframes pop{0%{transform:scale(.2) translateY(16px);opacity:0}100%{transform:scale(1);opacity:1}}

  .tint{position:absolute;inset:0;pointer-events:none;z-index:7;transition:background 1.2s}
  .tint.morgen{background:linear-gradient(180deg,rgba(255,214,150,.13),transparent 60%)}
  .tint.mittag{background:transparent}
  .tint.abend{background:linear-gradient(180deg,rgba(230,140,80,.16),rgba(90,60,90,.10))}

  /* ====================== BOTTOM PANEL ====================== */
  .panel{z-index:6;background:linear-gradient(180deg,#6E4A2B,#5C3D22);padding:9px 12px calc(11px + env(safe-area-inset-bottom));
    border-top:3px solid #8A5F38;margin:0 12px;border-radius:0 0 4px 4px;min-height:172px;display:flex;flex-direction:column}
  .pLabel{font-size:10px;font-weight:800;letter-spacing:.8px;text-transform:uppercase;color:#e7d4b6;margin:0 2px 7px;display:flex;justify-content:space-between;align-items:center}
  .tabs{display:flex;gap:6px;margin-bottom:8px}
  .tab{flex:1;border:none;border-radius:10px;padding:8px;font-family:'Nunito';font-weight:800;font-size:12.5px;cursor:pointer;
    background:rgba(255,255,255,.12);color:#e7d4b6}
  .tab.active{background:#F7F0E2;color:var(--ink)}
  .scrollBox{overflow-y:auto;max-height:150px;-webkit-overflow-scrolling:touch}
  .rowItem{display:flex;align-items:center;gap:8px;background:#F7F0E2;border-radius:12px;padding:7px 9px;margin-bottom:6px}
  .rowItem .em{font-size:22px}
  .rowItem .info{flex:1;min-width:0}
  .rowItem .n{font-size:12px;font-weight:800}
  .rowItem .d{font-size:10px;color:var(--ink-soft)}
  .buyB{border:none;border-radius:10px;padding:7px 11px;font-family:'Nunito';font-weight:800;font-size:12px;cursor:pointer;
    background:var(--gold);color:#fff;box-shadow:0 2px 0 rgba(0,0,0,.2);white-space:nowrap}
  .buyB:disabled{opacity:.45}
  .buyB:active{transform:translateY(1px)}
  .stockN{font-size:11px;font-weight:800;background:var(--sage-soft);color:#4c5a44;border-radius:999px;padding:2px 8px}

  .shelfGrid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
  .flower{background:#F7F0E2;border:1.5px solid #E4D3B9;border-radius:14px;padding:6px 3px 5px;text-align:center;cursor:pointer;
    box-shadow:0 3px 0 rgba(0,0,0,.22);position:relative}
  .flower:active{transform:translateY(2px)}
  .flower .em{font-size:24px;line-height:1;display:block}
  .flower .nm{font-size:9px;font-weight:700;color:var(--ink-soft)}
  .flower.empty{opacity:.42;filter:grayscale(.7)}
  .flower .stk{position:absolute;top:-7px;right:-4px;background:var(--sage-deep);color:#fff;font-size:10px;font-weight:800;
    padding:1px 6px;border-radius:999px;border:1.5px solid #F7F0E2}
  .flower .stk.zero{background:#a04040}
  .actions{display:flex;gap:8px;margin-top:9px}
  .btn{flex:1;border:none;border-radius:13px;padding:11px;font-family:'Nunito';font-weight:800;font-size:14px;cursor:pointer;box-shadow:0 3px 0 rgba(0,0,0,.22)}
  .btn:active{transform:translateY(2px)}
  .btn.primary{background:#DDBB77;color:#4a3413}
  .btn.ghost{background:#F7F0E2;color:var(--ink);flex:0 0 50px}

  /* ---------- overlays ---------- */
  .overlay{position:absolute;inset:0;z-index:30;background:rgba(68,58,46,.44);backdrop-filter:blur(3px);
    display:grid;place-items:center;padding:20px;animation:fade .25s ease}
  @keyframes fade{from{opacity:0}to{opacity:1}}
  .card{background:var(--paper);border-radius:22px;padding:24px 20px;max-width:340px;width:100%;text-align:center;
    box-shadow:0 12px 40px rgba(0,0,0,.3);border:1.5px solid #fff;max-height:82vh;overflow-y:auto}
  .card .big{font-size:46px}
  .card h2{font-family:'Fraunces',serif;font-weight:900;font-size:21px;color:var(--sage-deep);margin:5px 0 4px}
  .card p{font-size:13px;color:var(--ink-soft);line-height:1.55;margin-bottom:8px}
  .card .letter{background:#FFFDF6;border:1.5px solid #E8DCC2;border-radius:12px;padding:12px 13px;font-family:'Fraunces',serif;
    font-style:italic;font-size:13.5px;line-height:1.6;color:var(--ink);text-align:left;margin:8px 0 12px}
  .stars{font-size:24px;letter-spacing:3px;margin:5px 0}
  .reward{display:inline-flex;gap:6px;align-items:center;background:#fff;border:1.5px solid var(--paper-2);border-radius:999px;
    padding:7px 15px;font-weight:800;font-size:15px;margin:6px 0 12px;color:var(--gold)}
  .sumRow{display:flex;justify-content:space-between;font-size:13.5px;padding:5px 4px;border-bottom:1px dashed #ddd0b8}
  .sumRow b.plus{color:#4c7a44} .sumRow b.minus{color:#a04040}
  .fbtn{border:none;border-radius:14px;padding:12px 20px;font-family:'Nunito';font-weight:800;font-size:15px;
    background:var(--sage-deep);color:#fbf7ec;cursor:pointer;box-shadow:0 3px 0 rgba(0,0,0,.18);width:100%;margin-top:10px}
  .fbtn:active{transform:translateY(2px)}
  .toast{position:absolute;left:50%;bottom:40%;transform:translateX(-50%);background:var(--ink);color:#fff;padding:8px 14px;
    border-radius:12px;font-size:12.5px;font-weight:700;z-index:20;box-shadow:0 6px 18px rgba(0,0,0,.25);opacity:0;pointer-events:none;transition:.25s;white-space:nowrap}
  .toast.show{opacity:1}
  @media (prefers-reduced-motion: reduce){.door.ring,.bell.ring,.customer.enter,.deco-cat{animation:none}}
</style>
</head>
<body>
<div id="app">
  <header>
    <div class="brand"><h1>Jantjes nümmiger Blumenladen</h1><div class="sub" id="weatherLine">☀️ Tag 1 · sonnig</div></div>
    <div class="stats">
      <div class="chip">🪙 <span id="coins">40</span></div>
      <div class="chip">❤️ <span id="rep">0</span> <small>Lv<span id="lvl">1</span></small></div>
      <button class="iconbtn" id="music">🔈</button>
    </div>
  </header>
  <div class="repbar"><i id="repFill" style="width:0%"></i></div>

  <div class="scene" id="scene">
    <div class="wall" id="wall"></div>
    <div class="floor"></div>
    <div class="window"><div class="sky morgen" id="sky"><div class="sun"></div><div class="cloud">☁️</div><div class="cloud2">🌧️</div><div class="rain"></div></div><div class="cross"></div><div class="crossh"></div></div>
    <div class="hang l">🪴</div><div class="hang r">🌿</div>
    <div class="lamp" id="lampDeco">🏮</div>
    <div class="shelfBoard s1"></div><div class="shelfBoard s2"></div>
    <div class="pots p1" id="pots1"></div><div class="pots p2" id="pots2"></div>
    <div class="bigplants">🪴</div>
    <div class="collie sleep" id="collie"><svg viewBox="0 0 100 52" width="74" xmlns="http://www.w3.org/2000/svg">
      <path class="tail" d="M78 34 q15 -11 19 -2 q-9 2 -13 9 z" fill="#8A5A2B"/>
      <ellipse cx="52" cy="38" rx="30" ry="13" fill="#8A5A2B"/>
      <ellipse cx="54" cy="43" rx="25" ry="7" fill="#F3E9D5"/>
      <path d="M30 44 h20 a3 3 0 0 1 0 6 h-20 z" fill="#F3E9D5"/>
      <circle cx="24" cy="26" r="13" fill="#8A5A2B"/>
      <path d="M24 26 l-12 9 q-1 -15 12 -15 z" fill="#F3E9D5"/>
      <ellipse cx="13" cy="31" rx="5.5" ry="4" fill="#F3E9D5"/>
      <circle cx="10.5" cy="30" r="1.9" fill="#3a2d20"/>
      <path d="M15 14 l7 8 -10 3 z" fill="#6E4423"/>
      <path d="M31 13 l3 10 -10 -1 z" fill="#6E4423"/>
      <ellipse class="eye" cx="22" cy="24" rx="2" ry="2.4" fill="#3a2d20"/>
      <text class="zzz" x="33" y="13" font-size="10">💤</text>
    </svg></div>
    <div class="frame">🖼️</div>
    <div class="deco-window" id="windowDeco">💐✨</div>
    <div class="bell" id="bell">🔔</div>
    <div class="door" id="door">
      <div class="panes"><span></span><span></span><span></span><span></span><span></span><span></span></div>
      <div class="knob"></div><div class="sign">Geöffnet</div>
    </div>
    <div class="customer" id="customer"></div>
    <div class="bubble" id="bubble">
      <span class="nm" id="custName"></span>
      <span class="g" id="greeting"></span>
      <span class="mood" id="moodLine"></span>
      <span class="wants" id="wants"></span>
    </div>
    <div class="qcount" id="qcount"></div>
    <div class="counter"><div class="top"></div><div class="front"></div></div>
    <div class="deco-radio" id="radioDeco">📻</div>
    <div class="deco-cat" id="catDeco">🐈</div>
    <div class="vase" id="vase"></div>
    <div class="tint morgen" id="tint"></div>
  </div>

  <div class="panel" id="panel"></div>
  <div class="toast" id="toast"></div>
  <div id="ovHost"></div>
</div>

<script>
/* ============================================================
   JANTJES NÜMMIGER BLUMENLADEN — Management Edition
============================================================ */
const $=id=>document.getElementById(id);
const rnd=a=>a[Math.floor(Math.random()*a.length)];
const ri=(a,b)=>a+Math.floor(Math.random()*(b-a+1));

/* ---------------- DATA ---------------- */
const FLOWERS=[
  {id:'sun',   em:'🌻',name:'Sonnenblume',mood:'fröhlich',  cost:2, lvl:1},
  {id:'tulip', em:'🌷',name:'Tulpe',      mood:'fröhlich',  cost:2, lvl:1},
  {id:'daisy', em:'🌼',name:'Margerite',  mood:'ruhig',     cost:2, lvl:1},
  {id:'rose',  em:'🌹',name:'Rose',       mood:'romantisch',cost:3, lvl:1},
  {id:'blossom',em:'🌸',name:'Kirschblüte',mood:'zart',     cost:3, lvl:2},
  {id:'hya',   em:'🪻',name:'Hyazinthe',  mood:'ruhig',     cost:3, lvl:2},
  {id:'lav',   em:'🌾',name:'Lavendel',   mood:'zart',      cost:4, lvl:3},
  {id:'hibi',  em:'🌺',name:'Hibiskus',   mood:'wild',      cost:4, lvl:3},
  {id:'kam',   em:'💮',name:'Kamelie',    mood:'edel',      cost:5, lvl:4},
  {id:'lotus', em:'🪷',name:'Darias Lotus', mood:'edel',      cost:5, lvl:99, story:true},
];
const MOODCOPY={'fröhlich':'etwas Fröhliches ☀️','ruhig':'was Ruhiges & Sanftes 🌾','romantisch':'etwas Romantisches ❤️',
  'zart':'was ganz Zartes 🌸','wild':'was Wildes & Buntes 🌿','edel':'etwas richtig Edles ✨'};
const GREET=['Hallo! Ich hätte gern','Moin! Für heute bitte','Hey! Ich bräuchte','Schönen Tag! Ich suche','Hallöchen, ich möchte'];
const NAMES=['Frau Petersen','Herr Albrecht','Ida','Ole','Frau Yilmaz','Bosse','Greta','Herr Nowak','Tine','Fiete','Marla','Dr. Lund'];
const REGULARS=[
  {name:'Frau Petersen',line:'Wie immer für meinen Küchentisch, min Deern!'},
  {name:'Fiete',line:'Für Oma im Heim — die freut sich jede Woche.'},
  {name:'Dr. Lund',line:'Fürs Wartezimmer. Die Patienten sollen sich wohlfühlen.'},
];
const DECOR=[
  {id:'lamp',  em:'🏮',name:'Alte Papierlampe', cost:25, d:'+2 🪙 pro Verkauf (gemütliches Licht)'},
  {id:'radio', em:'📻',name:'Röhrenradio',      cost:35, d:'Bessere Trinkgelder (+4 🪙 bei 3⭐)'},
  {id:'cat',   em:'🐈',name:'Kater Muckel',     cost:45, d:'+1 ❤️ pro Gast (alle lieben ihn)'},
  {id:'wall',  em:'🌸',name:'Blümchentapete',   cost:55, d:'+2 ❤️ pro Tag'},
  {id:'window',em:'💐',name:'Schaufenster-Deko',cost:70, d:'+1 Gast pro Tag'},
];
const LVL_REP=[0,12,28,50,80,999];
const LVL_UNLOCK={2:'🌸 Kirschblüte & 🪻 Hyazinthe',3:'🌾 Lavendel & 🌺 Hibiskus',4:'💮 Kamelie'};

/* ---------------- STORY ---------------- */
const STORY={
  1:{em:'✉️',title:'Ein Brief von Daria',letter:'Min leve Jantje,<br>wenn du das liest, gehört der Laden dir. All die Nachmittage bei Tee und Kuchen, unsere Ausflüge ans Watt und in die Gärten — du warst mir wie Familie. Der Laden ist nümmig und schief, aber er hat Herz, genau wie du. Gieß die Pflanzen, sei lieb zu den Leuten, und schau irgendwann mal unter die dritte Diele hinterm Tresen.<br><br>Deine Nachbarin Daria 🌷',btn:'Für Daria. 🌷'},
  3:{em:'🗞️',title:'Schlechte Nachrichten',letter:'Frau Petersen kommt aufgeregt rein:<br>„Jantje! Hast du\'s gehört? Am Marktplatz macht der <b>BLUMEN-DISCOUNT 3000</b> auf. Alles aus Plastik-Gewächshäusern, aber billig! Du musst dir was einfallen lassen, Kind."',btn:'Wir schaffen das.'},
  6:{em:'😤',title:'Besuch vom Discount',letter:'Ein Mann im glänzenden Anzug lehnt am Türrahmen. Dirk, Filialleiter vom Discount 3000:<br>„Süßer Laden. Riecht nach... Vergangenheit. Ich geb dir vier Wochen, dann kauf ich die Bude als Lagerfläche."<br>Die Türglocke bimmelt wütend, als er geht.',btn:'Niemals, Dirk.'},
  9:{em:'📓',title:'Die dritte Diele',letter:'Du erinnerst dich an Darias Brief und hebelst die dritte Diele hinterm Tresen hoch. Darunter: ihr altes Notizbuch — und Samen! Zwischen den Seiten ein Foto von euch beiden auf einem eurer Ausflüge.<br><br>„Mein Lotus. Blüht nur für Leute, die den Laden wirklich lieben. — D."<br><br>🪷 <b>Darias Lotus freigeschaltet!</b> (Stimmung: edel)',btn:'Danke, Daria. 🪷',unlock:'lotus'},
  12:{em:'📯',title:'Das Stadtfest',letter:'Ein Aushang am Rathaus: In 3 Tagen ist <b>Stadtfest</b> — und der Festausschuss sucht einen Blumenladen für den großen Festschmuck. Der Discount 3000 hat sich auch beworben...<br><br>Sammle Herzen & fülle dein Lager. Tag 15 wird groß!',btn:'Ich bin bereit!'},
  15:{em:'🎪',title:'FESTTAG!',letter:'Der Festausschuss steht vor der Tür: „Wir geben beiden Läden eine Chance. Drei Festbuketts, heute. Wer uns verzaubert, schmückt das Fest."<br><br>Heute zählt jedes Blütenblatt. Viel Glück, Jantje!',btn:'Los geht\'s! 🎪',festival:true},
  16:{em:'🏆',title:'Der Laden lebt',letter:'Die Stadt hat gewählt: <b>Jantjes nümmiger Blumenladen</b> schmückt das Fest! Dirk packt drüben schon Kartons — „Kein Markt für Qualität", murmelt er. Doch, Dirk. Genau dafür.<br><br>Daria wäre stolz. Der Laden läuft weiter, so lange du magst. 🌷',btn:'Weiterspielen ∞'},
};

/* ---------------- STATE ---------------- */
let S={
  coins:40, day:1, rep:0, lvl:1,
  stock:{sun:4,tulip:4,daisy:3,rose:3},
  unlocked:{sun:1,tulip:1,daisy:1,rose:1},
  decor:{},
  vase:[], req:null,
  phase:'morning',              // morning | serving
  custLeft:0, custTotal:0,
  dayEarn:0, dayTips:0, dayStock:0, dayServed:0, dayStars:0,
  weather:'sonnig',
  musicOn:false,
  festivalDone:false,
};
const rent=()=>8+Math.floor((S.day-1)/5)*2;

/* ---------------- HELPERS ---------------- */
function lvlFor(rep){let l=1;for(let i=1;i<LVL_REP.length;i++)if(rep>=LVL_REP[i])l=i+1;return Math.min(l,5);}
function unlockByLvl(){FLOWERS.forEach(f=>{if(!f.story&&f.lvl<=S.lvl)S.unlocked[f.id]=1;});}
function ownedFlowers(){return FLOWERS.filter(f=>S.unlocked[f.id]);}
function availMoods(){return [...new Set(ownedFlowers().map(f=>f.mood))];}
let toastT;function toast(m){const t=$('toast');t.textContent=m;t.classList.add('show');clearTimeout(toastT);toastT=setTimeout(()=>t.classList.remove('show'),1700);}

/* ---------------- SCENE RENDER ---------------- */
const BLOOMS=['🌻','🌷','🌹','🌼','🌸','🪻','🌺','💐','🌾','🏵️'];
function fillPots(){['pots1','pots2'].forEach(pid=>{const r=$(pid);r.innerHTML='';
  for(let i=0;i<5;i++){const p=document.createElement('div');p.className='pot';
    p.innerHTML=`<span class="bloom">${rnd(BLOOMS)}</span><span class="cup"></span>`;r.appendChild(p);}});}
function setDaylight(k){$('sky').className='sky '+k+(S.weather==='regnerisch'?' rainy':'');$('tint').className='tint '+k;}
function renderDecor(){
  $('lampDeco').classList.toggle('on',!!S.decor.lamp);
  $('radioDeco').classList.toggle('on',!!S.decor.radio);
  $('catDeco').classList.toggle('on',!!S.decor.cat);
  $('windowDeco').classList.toggle('on',!!S.decor.window);
  $('wall').classList.toggle('fancy',!!S.decor.wall);
}
function renderStats(){
  $('coins').textContent=S.coins;$('rep').textContent=S.rep;$('lvl').textContent=S.lvl;
  const cur=LVL_REP[S.lvl-1],nxt=LVL_REP[S.lvl]||S.rep+1;
  $('repFill').style.width=Math.min(100,(S.rep-cur)/(nxt-cur)*100)+'%';
  const wi={'sonnig':'☀️','regnerisch':'🌧️','mild':'⛅'}[S.weather];
  $('weatherLine').textContent=`${wi} Tag ${S.day} · ${S.weather} · Miete ${rent()}🪙`;
}
function renderVase(){
  const v=$('vase');v.innerHTML='';
  if(S.vase.length===0){const h=document.createElement('span');h.className='hint';
    h.textContent=S.phase==='serving'?'Tippe unten Blumen an 🌿':'Gleich öffnet der Laden…';v.appendChild(h);}
  S.vase.forEach((f,i)=>{const s=document.createElement('span');s.className='stem';s.textContent=f.em;
    s.onclick=()=>{S.vase.splice(i,1);S.stock[f.id]=(S.stock[f.id]||0)+1;renderVase();renderPanel();blip(520,.06);};
    v.appendChild(s);});
}

/* ---------------- CUSTOMER GEN ---------------- */
const SKIN=['#F6D2B0','#EEC095','#E0AC82','#C68642','#A9713F','#8D5524'];
const HAIRC=['#2B2118','#43301E','#6A4423','#8A5A2B','#C9A24B','#B0413E','#9AA0A6','#E7DFCC','#3B3B44'];
const SHIRT=['#7C8F70','#B26B47','#C88686','#6E88A8','#C69A3E','#8A6FA0','#5E9E86','#B5546A','#4F6C8C'];
function customerSVG(){
  const skin=rnd(SKIN),hair=rnd(HAIRC),shirt=rnd(SHIRT);
  const style=ri(0,4),glasses=Math.random()<.32,hat=Math.random()<.22,beard=(style!==1&&Math.random()<.22);
  const blush=Math.random()<.5?`<circle cx="41" cy="50" r="4" fill="#E88" opacity=".35"/><circle cx="77" cy="50" r="4" fill="#E88" opacity=".35"/>`:'';
  let hairSVG='';
  if(style===0)hairSVG=`<path d="M34 40a25 25 0 0 1 50 0v6c-6-10-14-13-25-13S40 36 34 46z" fill="${hair}"/>`;
  else if(style===1)hairSVG=`<path d="M32 40a27 27 0 0 1 54 0v34c0 4-6 4-6-2V44c-4-9-13-13-21-13S42 35 38 44v30c0 6-6 6-6 2z" fill="${hair}"/>`;
  else if(style===2)hairSVG=`<circle cx="59" cy="18" r="9" fill="${hair}"/><path d="M34 42a25 25 0 0 1 50 0v4c-6-11-14-14-25-14S40 35 34 46z" fill="${hair}"/>`;
  else if(style===4)hairSVG=`<g fill="${hair}"><circle cx="40" cy="30" r="10"/><circle cx="59" cy="22" r="11"/><circle cx="78" cy="30" r="10"/><circle cx="34" cy="42" r="8"/><circle cx="84" cy="42" r="8"/></g>`;
  const hatSVG=hat?`<path d="M30 34h58l-6-4c0-14-46-14-46 0z" fill="#6E4A2B"/><rect x="42" y="14" width="34" height="18" rx="4" fill="#7A5334"/><rect x="42" y="27" width="34" height="5" fill="#C69A3E"/>`:'';
  const glSVG=glasses?`<g fill="none" stroke="#3a3a3a" stroke-width="2.5"><circle cx="46" cy="52" r="8"/><circle cx="72" cy="52" r="8"/><path d="M54 52h10M38 50l-6-2M80 50l6-2"/></g>`:'';
  const beardSVG=beard?`<path d="M40 56c2 16 34 16 38 0-4 12-10 16-19 16s-15-4-19-16z" fill="${hair}"/>`:'';
  return `<svg viewBox="0 0 118 120" width="112" xmlns="http://www.w3.org/2000/svg">
    <path d="M18 120c0-26 18-40 41-40s41 14 41 40z" fill="${shirt}"/>
    <path d="M50 66h18v14a9 9 0 0 1-18 0z" fill="${skin}"/>
    <ellipse cx="59" cy="50" rx="27" ry="28" fill="${skin}"/>
    ${beardSVG}${blush}
    <circle cx="49" cy="50" r="3" fill="#3a2d20"/><circle cx="69" cy="50" r="3" fill="#3a2d20"/>
    <path d="M52 62q7 5 14 0" stroke="#a5744e" stroke-width="2.4" fill="none" stroke-linecap="round"/>
    ${hairSVG}${glSVG}${hatSVG}</svg>`;
}

/* ---------------- REQUEST GEN ---------------- */
function moodBias(){
  const m=availMoods();
  let pool=[...m];
  if(S.weather==='sonnig')pool=pool.concat(m.filter(x=>x==='fröhlich'||x==='wild'));
  if(S.weather==='regnerisch')pool=pool.concat(m.filter(x=>x==='ruhig'||x==='zart'));
  return rnd(pool);
}
function genRequest(){
  const festival=S.req&&S.req.festival;
  const roll=Math.random();
  const owned=ownedFlowers();
  if(S.day>=4&&roll<0.22){ // exact recipe order
    const picks={};const kinds=ri(2,Math.min(3,owned.length));
    const shuffled=[...owned].sort(()=>Math.random()-.5).slice(0,kinds);
    let total=0;shuffled.forEach(f=>{const n=ri(1,2);picks[f.id]=n;total+=n;});
    return {type:'recipe',recipe:picks,count:total,name:rnd(NAMES),
      pay:1.4,line:'Ich hab da einen ganz genauen Wunsch:'};
  }
  if(S.day>=3&&roll<0.42){ // picky
    const mood=moodBias();
    const others=availMoods().filter(m=>m!==mood);
    return {type:'picky',mood,hate:rnd(others),count:ri(3,5),name:rnd(NAMES),
      pay:1.2,line:rnd(GREET)+'…'};
  }
  if(S.day>=5&&roll<0.55){ // regular, bigger reward
    const r=rnd(REGULARS);
    return {type:'mood',mood:moodBias(),count:ri(3,5),name:r.name,regular:true,pay:1.3,line:r.line};
  }
  return {type:'mood',mood:moodBias(),count:ri(3,5),name:rnd(NAMES),pay:1,line:rnd(GREET)+'…'};
}
function genFestivalRequest(i){
  const moods=availMoods();
  return {type:'mood',mood:rnd(moods),count:ri(5,7),name:'Festausschuss '+(i+1)+'/3',pay:2.2,
    line:'Ein Festbukett bitte — und zwar ein richtig gutes!',festival:true};
}

/* ---------------- FLOW ---------------- */
function startDay(){
  S.phase='morning';S.vase=[];
  S.dayEarn=0;S.dayTips=0;S.dayStock=0;S.dayServed=0;S.dayStars=0;
  S.weather=rnd(['sonnig','sonnig','mild','regnerisch']);
  const isFest=S.day===15&&!S.festivalDone;
  S.custTotal=isFest?3:Math.min(6,3+Math.floor(S.day/4)+(S.decor.window?1:0));
  S.custLeft=S.custTotal;
  setDaylight('morgen');
  $('bubble').classList.remove('show');$('customer').innerHTML='';$('qcount').textContent='';
  if(S.decor.wall){S.rep+=2;}
  checkLevel(false);
  renderStats();renderVase();renderPanel();
  if(STORY[S.day]&&!(S.day===15&&S.festivalDone))showStory(S.day);
}
function openShop(){
  const totalStock=Object.values(S.stock).reduce((a,b)=>a+b,0);
  if(totalStock<3){toast('Kauf erst ein bisschen Ware ein 🌱');return;}
  S.phase='serving';setDaylight('mittag');
  renderPanel();nextCustomer();
}
function nextCustomer(){
  if(S.custLeft<=0){endDay();return;}
  if(S.custLeft<=Math.ceil(S.custTotal/3))setDaylight('abend');
  ringDoor();
  const isFest=S.day===15&&!S.festivalDone;
  S.req=isFest?genFestivalRequest(S.custTotal-S.custLeft):genRequest();
  S.vase=[];renderVase();
  $('qcount').textContent=`Gast ${S.custTotal-S.custLeft+1}/${S.custTotal}`;
  setTimeout(()=>{
    const c=$('customer');c.innerHTML=customerSVG();
    c.classList.remove('enter');void c.offsetWidth;c.classList.add('enter');
    const r=S.req;
    $('custName').textContent=r.name;
    $('greeting').textContent=r.line;
    let wants='';
    if(r.type==='recipe'){
      $('moodLine').textContent='„Genau so, bitte!"';
      wants=Object.entries(r.recipe).map(([id,n])=>{const f=FLOWERS.find(x=>x.id===id);
        return `<span class="wantTag recipe">${n}× ${f.em}</span>`;}).join('');
    }else{
      $('moodLine').textContent='„'+MOODCOPY[r.mood]+'"';
      wants=`<span class="wantTag">Stimmung: ${r.mood}</span><span class="wantTag count">${r.count} Blumen</span>`;
      if(r.type==='picky')wants+=`<span class="wantTag hate">bloß nicht: ${r.hate}</span>`;
    }
    $('wants').innerHTML=wants;
    $('bubble').classList.add('show');
    renderPanel();
  },320);
}
function give(){
  if(S.vase.length===0){toast('Leg erst Blumen rein 🌿');return;}
  const r=S.req;let stars=1,base=0;
  if(r.type==='recipe'){
    const have={};S.vase.forEach(f=>have[f.id]=(have[f.id]||0)+1);
    let ok=0,need=0;
    Object.entries(r.recipe).forEach(([id,n])=>{need+=n;ok+=Math.min(n,have[id]||0);});
    const extra=S.vase.length-ok;
    const ratio=ok/need;
    stars=ratio>=1&&extra===0?3:ratio>=0.7?2:1;
    base=8+ok*4;
  }else{
    const match=S.vase.filter(f=>f.mood===r.mood).length;
    const ratio=match/S.vase.length,countOk=S.vase.length===r.count;
    stars=ratio>=0.8?3:ratio>=0.5?2:1;
    if(stars===3&&!countOk)stars=2;
    base=6+match*3+(countOk?4:0)+(ratio>=0.8?5:0);
    if(r.type==='picky'&&S.vase.some(f=>f.mood===r.hate)){stars=1;base=Math.floor(base*0.5);}
  }
  let coins=Math.round(base*(r.pay||1));
  if(S.decor.lamp)coins+=2;
  let tip=0;
  if(stars===3){tip=5+(S.decor.radio?4:0);if(Math.random()<.3)tip+=3;}
  let hearts=stars+(S.decor.cat?1:0);
  S.coins+=coins+tip;S.rep+=hearts;
  S.dayEarn+=coins;S.dayTips+=tip;S.dayServed++;S.dayStars+=stars;
  S.custLeft--;
  checkLevel(true);renderStats();
  showResult(stars,coins,tip,hearts,r);
}
function endDay(){
  S.phase='morning';
  const m=rent();
  const net=S.dayEarn+S.dayTips-m;
  S.coins=Math.max(0,S.coins-m);
  const avg=S.dayServed?(S.dayStars/S.dayServed):0;
  const isFest=S.day===15&&!S.festivalDone;
  let festLine='';
  if(isFest){S.festivalDone=true;
    festLine=avg>=2.3?'<div class="sumRow"><span>🎪 Festwertung</span><b class="plus">GEWONNEN!</b></div>'
      :'<div class="sumRow"><span>🎪 Festwertung</span><b>knapp… aber dein Herz zählt ❤️</b></div>';}
  const ov=document.createElement('div');ov.className='overlay';
  ov.innerHTML=`<div class="card"><div class="big">🌙</div><h2>Feierabend — Tag ${S.day}</h2>
    <div class="sumRow"><span>Verkäufe (${S.dayServed} Gäste)</span><b class="plus">+${S.dayEarn} 🪙</b></div>
    <div class="sumRow"><span>Trinkgeld</span><b class="plus">+${S.dayTips} 🪙</b></div>
    <div class="sumRow"><span>Wareneinkauf heute</span><b class="minus">−${S.dayStock} 🪙</b></div>
    <div class="sumRow"><span>Miete</span><b class="minus">−${m} 🪙</b></div>
    <div class="sumRow"><span><b>Bilanz</b></span><b class="${net>=0?'plus':'minus'}">${net>=0?'+':''}${net} 🪙</b></div>
    ${festLine}
    <p style="margin-top:10px">⭐ Ø ${avg.toFixed(1)} · ❤️ ${S.rep} gesammelt</p>
    <button class="fbtn" id="nd">Schlafen 😴 → Tag ${S.day+1}</button></div>`;
  $('ovHost').appendChild(ov);
  ov.querySelector('#nd').onclick=()=>{ov.remove();S.day++;startDay();};
}
function checkLevel(announce){
  const nl=lvlFor(S.rep);
  if(nl>S.lvl){S.lvl=nl;unlockByLvl();
    if(announce){chime();
      const u=LVL_UNLOCK[nl];
      setTimeout(()=>toast(`Level ${nl}! ${u?'Neu im Großmarkt: '+u:''}`),400);}
    else unlockByLvl();}
}
function showResult(stars,coins,tip,hearts,r){
  stars>=2?chime():blip(300,.12);
  const emoji=stars===3?'🥰':stars===2?'😊':'🙂';
  const title=stars===3?'Wunderschön!':stars===2?'Sehr hübsch!':'Na ja…';
  let msg=stars===3?'Genau richtig — das nehme ich mit Freude mit!':stars===2?'Das passt schön, danke!':'Hm, nicht ganz meins. Trotzdem danke.';
  if(r.type==='picky'&&stars===1)msg='Da ist ja genau das drin, was ich NICHT wollte!';
  if(r.festival&&stars===3)msg='Oho! DAS ist Festniveau!';
  const ov=document.createElement('div');ov.className='overlay';
  ov.innerHTML=`<div class="card"><div class="big">${emoji}</div><h2>${title}</h2>
    <div class="stars">${'⭐'.repeat(stars)+'☆'.repeat(3-stars)}</div>
    <p>${msg}${tip?'<br><b>+ Trinkgeld '+tip+' 🪙</b> 💛':''}</p>
    <div class="reward">🪙 +${coins+tip} &nbsp;·&nbsp; ❤️ +${hearts}</div>
    <button class="fbtn" id="nx">${S.custLeft>0?'Nächste:r Gast 🔔':'Feierabend 🌙'}</button></div>`;
  $('ovHost').appendChild(ov);
  ov.querySelector('#nx').onclick=()=>{ov.remove();nextCustomer();};
}
function showStory(day){
  const st=STORY[day];if(!st)return;
  const ov=document.createElement('div');ov.className='overlay';
  ov.innerHTML=`<div class="card"><div class="big">${st.em}</div><h2>${st.title}</h2>
    <div class="letter">${st.letter}</div>
    <button class="fbtn" id="sb">${st.btn}</button></div>`;
  $('ovHost').appendChild(ov);
  ov.querySelector('#sb').onclick=()=>{
    if(st.unlock){S.unlocked[st.unlock]=1;renderPanel();}
    ov.remove();chime();};
}
function ringDoor(){
  const d=$('door'),b=$('bell');
  d.classList.remove('ring');b.classList.remove('ring');void d.offsetWidth;
  d.classList.add('ring');b.classList.add('ring');doorBell();
}

/* ---------------- PANEL ---------------- */
let tab='markt';
function renderPanel(){
  const p=$('panel');
  if(S.phase==='morning'){
    p.innerHTML=`<div class="pLabel"><span>☀️ Morgen — Tag ${S.day}</span><span>${S.custTotal} Gäste erwartet</span></div>
      <div class="tabs">
        <button class="tab ${tab==='markt'?'active':''}" data-t="markt">🌱 Großmarkt</button>
        <button class="tab ${tab==='deko'?'active':''}" data-t="deko">🛋️ Deko</button>
      </div>
      <div class="scrollBox" id="box"></div>
      <div class="actions"><button class="btn primary" id="open">Laden öffnen 🔔</button></div>`;
    p.querySelectorAll('.tab').forEach(b=>b.onclick=()=>{tab=b.dataset.t;renderPanel();});
    p.querySelector('#open').onclick=openShop;
    const box=p.querySelector('#box');
    if(tab==='markt'){
      ownedFlowers().forEach(f=>{
        const row=document.createElement('div');row.className='rowItem';
        row.innerHTML=`<span class="em">${f.em}</span>
          <div class="info"><div class="n">${f.name}</div><div class="d">${f.mood} · ${f.cost} 🪙/Stück</div></div>
          <span class="stockN">${S.stock[f.id]||0}×</span>
          <button class="buyB" ${S.coins<f.cost?'disabled':''}>+1</button>
          <button class="buyB" ${S.coins<f.cost*3?'disabled':''}>+3</button>`;
        const[b1,b3]=row.querySelectorAll('.buyB');
        b1.onclick=()=>buyStock(f,1);b3.onclick=()=>buyStock(f,3);
        box.appendChild(row);
      });
      const locked=FLOWERS.filter(f=>!S.unlocked[f.id]&&!f.story);
      if(locked.length){const hint=document.createElement('div');
        hint.style.cssText='font-size:10.5px;color:#e7d4b6;text-align:center;padding:4px';
        hint.textContent='🔒 Mehr Sorten ab Level '+locked[0].lvl+' (Herzen sammeln!)';box.appendChild(hint);}
    }else{
      DECOR.forEach(d=>{
        const owned=S.decor[d.id];
        const row=document.createElement('div');row.className='rowItem';
        row.innerHTML=`<span class="em">${d.em}</span>
          <div class="info"><div class="n">${d.name}</div><div class="d">${d.d}</div></div>
          ${owned?'<span class="stockN">✓ da</span>':`<button class="buyB" ${S.coins<d.cost?'disabled':''}>${d.cost} 🪙</button>`}`;
        if(!owned)row.querySelector('.buyB').onclick=()=>{
          S.coins-=d.cost;S.decor[d.id]=1;renderStats();renderDecor();renderPanel();chime();
          toast(d.name+' steht jetzt im Laden ✨');};
        box.appendChild(row);
      });
    }
  }else{
    p.innerHTML=`<div class="pLabel"><span>💐 Bukett binden</span><span>${S.vase.length} in der Vase</span></div>
      <div class="shelfGrid" id="grid"></div>
      <div class="actions">
        <button class="btn ghost" id="clear">🗑️</button>
        <button class="btn primary" id="give">Überreichen 💐</button>
      </div>`;
    const g=p.querySelector('#grid');
    ownedFlowers().forEach(f=>{
      const st=S.stock[f.id]||0;
      const el=document.createElement('div');el.className='flower'+(st===0?' empty':'');
      el.innerHTML=`<span class="em">${f.em}</span><span class="nm">${f.name}</span>
        <span class="stk ${st===0?'zero':''}">${st}</span>`;
      el.onclick=()=>{
        if(st===0){toast('Ausverkauft — morgen nachkaufen 🌱');blip(200,.1);return;}
        if(S.vase.length>=8){toast('Die Vase ist voll 🌼');return;}
        S.stock[f.id]--;S.vase.push(f);renderVase();renderPanel();blip(660,.05);
      };
      g.appendChild(el);
    });
    p.querySelector('#clear').onclick=()=>{S.vase.forEach(f=>S.stock[f.id]++);S.vase=[];renderVase();renderPanel();};
    p.querySelector('#give').onclick=give;
  }
}
function buyStock(f,n){
  const c=f.cost*n;if(S.coins<c)return;
  S.coins-=c;S.dayStock+=c;S.stock[f.id]=(S.stock[f.id]||0)+n;
  renderStats();renderPanel();blip(740,.05);
}

/* ---------------- LOFI AUDIO ---------------- */
let AC,master,warm,vinylGain,musicTimer,barN=0,noiseBuf;
function initAudio(){
  if(AC)return;
  AC=new (window.AudioContext||window.webkitAudioContext)();
  master=AC.createGain();master.gain.value=0;
  warm=AC.createBiquadFilter();warm.type='lowpass';warm.frequency.value=2400;warm.Q.value=.5;
  master.connect(warm);warm.connect(AC.destination);
  noiseBuf=AC.createBuffer(1,AC.sampleRate,AC.sampleRate);
  const d=noiseBuf.getChannelData(0);for(let i=0;i<d.length;i++)d[i]=Math.random()*2-1;
  const vs=AC.createBufferSource();vs.buffer=noiseBuf;vs.loop=true;
  const vf=AC.createBiquadFilter();vf.type='highpass';vf.frequency.value=5000;
  vinylGain=AC.createGain();vinylGain.gain.value=0.006;
  vs.connect(vf);vf.connect(vinylGain);vinylGain.connect(warm);vs.start();
}
function note(freq,t,dur=2.4,vol=.16,type='sine',cut=1500){const o=AC.createOscillator(),g=AC.createGain(),lp=AC.createBiquadFilter();
  o.type=type;o.frequency.value=freq;lp.type='lowpass';lp.frequency.value=cut;
  g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(vol,t+.08);g.gain.exponentialRampToValueAtTime(.0001,t+dur);
  o.connect(lp);lp.connect(g);g.connect(master);o.start(t);o.stop(t+dur+.1);}
function noiseHit(t,dur,vol,type,freq,q){const s=AC.createBufferSource();s.buffer=noiseBuf;
  const f=AC.createBiquadFilter();f.type=type;f.frequency.value=freq;if(q)f.Q.value=q;
  const g=AC.createGain();g.gain.setValueAtTime(vol,t);g.gain.exponentialRampToValueAtTime(.0001,t+dur);
  s.connect(f);f.connect(g);g.connect(master);s.start(t);s.stop(t+dur+.05);}
function kick(t){const o=AC.createOscillator(),g=AC.createGain();o.type='sine';
  o.frequency.setValueAtTime(115,t);o.frequency.exponentialRampToValueAtTime(45,t+.12);
  g.gain.setValueAtTime(.22,t);g.gain.exponentialRampToValueAtTime(.0001,t+.22);
  o.connect(g);g.connect(master);o.start(t);o.stop(t+.24);}
function snare(t){noiseHit(t,.16,.09,'bandpass',1700,.7);}
function hat(t,v){noiseHit(t,.045,v,'highpass',7500);}
const PROG=[[261.63,329.63,392.00,493.88],[220.00,261.63,329.63,392.00],[293.66,349.23,440.00,523.25],[196.00,246.94,293.66,392.00]];
const BASS=[130.81,110.00,146.83,98.00];
const EIGHTH=0.405;
function playBar(){
  const t0=AC.currentTime+.06,q=EIGHTH*2,ch=PROG[barN%4],bs=BASS[barN%4];
  ch.forEach(f=>note(f,t0,q*4+.3,.045,'triangle',1400));
  note(bs,t0,q*1.7,.17,'sine',500);note(bs,t0+q*2,q*1.7,.15,'sine',500);
  for(let s=0;s<8;s++){const t=t0+s*EIGHTH+(s%2?0.05:0);
    if(s===0||s===4)kick(t);if(s===2||s===6)snare(t);hat(t,s%2?.045:.03);}
  if(Math.random()<.6){const m=ch[1+ri(0,2)]*(Math.random()<.5?2:1);
    note(m,t0+EIGHTH*ri(1,6),1.2,.06,'sine',2200);}
  barN++;
}
function musicOn(){initAudio();if(AC.state==='suspended')AC.resume();
  master.gain.cancelScheduledValues(AC.currentTime);master.gain.linearRampToValueAtTime(.85,AC.currentTime+1.5);
  playBar();musicTimer=setInterval(playBar,EIGHTH*8*1000);S.musicOn=true;$('music').textContent='🔊';}
function musicOff(){if(master)master.gain.linearRampToValueAtTime(0,AC.currentTime+.5);clearInterval(musicTimer);S.musicOn=false;$('music').textContent='🔈';}
function blip(f,d){if(!AC)return;note(f,AC.currentTime+.01,d+.15,.10,'sine');}
function chime(){if(!AC){initAudio();AC.resume();}const t=AC.currentTime;[523.25,659.25,783.99].forEach((f,i)=>note(f,t+i*.09,1.2,.12,'triangle'));}
function doorBell(){if(!AC)return;const t=AC.currentTime;[1318.5,1760,1318.5].forEach((f,i)=>note(f,t+i*.10,.5,.09,'sine'));}
$('music').onclick=()=>S.musicOn?musicOff():musicOn();

/* ---------------- START ---------------- */
function showIntro(){
  const ov=document.createElement('div');ov.className='overlay';
  ov.innerHTML=`<div class="card"><div class="big">🌷</div><h2>Jantjes nümmiger Blumenladen</h2>
    <p>Du bist Jantje. Deine Nachbarin Daria — bei der du so oft zu Tee und Kuchen warst und mit der du unzählige Ausflüge gemacht hast — hat dir ihren winzigen, schiefen, wunderbaren Blumenladen vermacht. Und ein Geheimnis dazu.</p>
    <div class="letter" style="font-style:normal">
      🌱 <b>Morgens:</b> Ware & Deko einkaufen<br>
      🔔 <b>Tagsüber:</b> Gäste bedienen, Buketts binden<br>
      🌙 <b>Abends:</b> Bilanz — Miete nicht vergessen!<br>
      ❤️ <b>Herzen</b> leveln dich hoch → neue Blumen<br>
      📖 <b>Story:</b> Ein Discount-Riese will dich plattmachen…
    </div>
    <button class="fbtn" id="go">Schlüssel umdrehen 🔑</button></div>`;
  $('ovHost').appendChild(ov);
  ov.querySelector('#go').onclick=()=>{ov.remove();musicOn();startDay();};
}
function collieLoop(){
  const c=$('collie');if(!c)return;
  const sleep=Math.random()<.55;
  c.className='collie '+(sleep?'sleep':'awake');
  setTimeout(collieLoop, sleep?ri(7000,13000):ri(3500,6500));
}
fillPots();renderDecor();renderStats();renderVase();renderPanel();collieLoop();showIntro();
</script>
</body>
</html>

Game Source: Jantjes nümmiger Blumenladen

Creator: HyperPenguin12

Libraries: none

Complexity: complex (757 lines, 47.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: jantjes-n-mmiger-blumenladen-hyperpenguin12" to link back to the original. Then publish at arcadelab.ai/publish.