<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" />

  <title>Cutie's Hobby Horse</title>

  <style>

    :root {

      --bg1: #fff2fb;

      --bg2: #e9f8ff;

      --pink: #ff7fc8;

      --pink-deep: #ff5bb3;

      --lav: #b888ff;

      --mint: #91f3d1;

      --butter: #ffe58a;

      --ink: #6f4774;

      --white: #ffffff;

      --danger: #ff4757;

      --shadow: rgba(164, 90, 150, 0.18);

    }


    * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

    html, body {

      margin: 0;

      height: 100%;

      overflow: hidden;

      touch-action: none;

      font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

      background: linear-gradient(180deg, var(--bg1), var(--bg2));

      color: var(--ink);

    }


    #app {

      position: relative;

      width: 100vw;

      height: 100vh;

      overflow: hidden;

      background:

        radial-gradient(circle at top, rgba(255,255,255,0.85), rgba(255,255,255,0) 40%),

        linear-gradient(180deg, #fff5fd 0%, #eaf9ff 100%);

    }


    canvas {

      width: 100%;

      height: 100%;

      display: block;

    }


    .hud {

      position: absolute;

      inset: 0;

      pointer-events: none;

      display: flex;

      flex-direction: column;

      justify-content: space-between;

      padding: max(14px, env(safe-area-inset-top)) 14px max(86px, calc(86px + env(safe-area-inset-bottom))) 14px;

    }


    .topbar {

      display: flex;

      justify-content: space-between;

      align-items: flex-start;

      gap: 10px;

    }


    .card {

      pointer-events: auto;

      background: rgba(255,255,255,0.62);

      border: 1px solid rgba(255,255,255,0.7);

      backdrop-filter: blur(10px);

      border-radius: 22px;

      box-shadow: 0 10px 24px var(--shadow);

    }


    .title-card {

      padding: 12px 14px;

      max-width: 65%;

    }


    .score-card {

      padding: 12px 14px;

      min-width: 110px;

      text-align: right;

      display: flex;

      flex-direction: column;

      gap: 8px;

      align-items: flex-end;

    }


    .title {

      font-size: 22px;

      font-weight: 900;

      letter-spacing: -0.05em;

      line-height: 1;

      margin: 0 0 4px 0;

    }


    .subtitle {

      font-size: 12px;

      opacity: 0.82;

      line-height: 1.3;

      margin: 0;

    }


    .score-label {

      font-size: 11px;

      text-transform: uppercase;

      letter-spacing: 0.1em;

      opacity: 0.7;

    }


    .score-value {

      font-size: 34px;

      font-weight: 900;

      letter-spacing: -0.06em;

      line-height: 1;

      margin-top: 4px;

    }


    .bottombar {

      display: grid;

      grid-template-columns: minmax(260px, 1fr) auto;

      gap: 10px;

      align-items: end;

    }


    .mode-group {

      display: flex;

      gap: 8px;

      align-items: center;

      pointer-events: auto;

      flex-wrap: wrap;

      justify-content: flex-end;

    }


    .mode-pill {

      pointer-events: auto;

      border: 0;

      border-radius: 999px;

      background: rgba(255,255,255,0.72);

      color: var(--ink);

      padding: 12px 14px;

      font-size: 13px;

      font-weight: 900;

      letter-spacing: -0.02em;

      box-shadow: 0 10px 20px var(--shadow);

      cursor: pointer;

      min-width: 82px;

    }


    .mode-pill.active {

      background: linear-gradient(180deg, #b888ff, #8f64e8);

      color: var(--white);

      box-shadow: 0 14px 28px rgba(143, 100, 232, 0.24);

    }


    .info-card {

      padding: 12px 14px;

      min-height: 72px;

      display: flex;

      flex-direction: column;

      justify-content: center;

    }


    .info-main {

      font-size: 16px;

      font-weight: 800;

      line-height: 1.15;

      margin-bottom: 4px;

    }


    .info-sub {

      font-size: 12px;

      opacity: 0.75;

      line-height: 1.25;

    }


    .action-btn {

      background: linear-gradient(180deg, var(--pink), var(--pink-deep));

      padding: 16px 22px;

      min-width: 112px;

    }


    .reset-pill {

      min-width: 74px;

    }


    


    .mode-pill:active, .top-reset:active { transform: scale(0.98); }


    .center-tip {

      position: absolute;

      left: 50%;

      top: 17%;

      transform: translateX(-50%);

      pointer-events: none;

      background: rgba(255,255,255,0.7);

      border-radius: 999px;

      padding: 10px 14px;

      font-size: 14px;

      font-weight: 800;

      color: var(--ink);

      box-shadow: 0 8px 18px var(--shadow);

      animation: tipBob 1.8s ease-in-out infinite;

    }


    @keyframes tipBob {

      0%, 100% { transform: translateX(-50%) translateY(0); }

      50% { transform: translateX(-50%) translateY(-6px); }

    }


    .top-reset {

      pointer-events: auto;

      border: 0;

      background: rgba(255,255,255,0.72);

      color: var(--ink);

      border-radius: 999px;

      padding: 8px 12px;

      font-size: 12px;

      font-weight: 800;

      box-shadow: 0 8px 18px var(--shadow);

      cursor: pointer;

    }


    @media (max-width: 760px) {

      .bottombar {

        grid-template-columns: 1fr;

      }

      .info-card {

        grid-column: 1 / -1;

      }

      .mode-group {

        display: grid;

        grid-template-columns: repeat(3, 1fr);

        width: 100%;

      }

      .mode-pill, .top-reset {

        width: 100%;

      }

      .title-card { max-width: 58%; }

      .score-value { font-size: 30px; }

    }

  </style>

</head>

<body>

  <div id="app">

    <canvas id="game" width="720" height="1280"></canvas>


    <div class="hud">

      <div class="topbar">

        <div class="card title-card">

          <div class="title">Cutie's Hobby Horse</div>

          <p class="subtitle" id="titleSubtitle">A tiny three-mode toy. Arcade chases points, Zen grows a flower bed, and Bubble Pop turns the whole thing into a pastel popping picnic.</p>

        </div>

        <div class="card score-card" id="scoreCard">

          <div class="score-label" id="scoreLabel">Cute Points</div>

          <div class="score-value" id="scoreValue">0</div>

          <button class="top-reset" id="resetBtn">Reset</button>

        </div>

      </div>


      <div class="bottombar">

        <div class="card info-card">

          <div class="info-main" id="infoMain">Tap Cutie to make her boing.</div>

          <div class="info-sub" id="infoSub">Tap Cutie. Catch hearts. Dropping them costs points!</div>

        </div>

        <div class="mode-group" id="modeGroup">

          <button class="mode-pill active" id="modeArcade" data-mode="arcade">Arcade</button>

          <button class="mode-pill" id="modeZen" data-mode="zen">Zen</button>

          <button class="mode-pill" id="modeBubble" data-mode="bubble">Bubble</button>

        </div>

      </div>

    </div>


    <div class="center-tip" id="centerTip">Tap Cutie</div>

  </div>


  <script>

    const SAVE_KEY = 'cutiesHobbyHorseModes_v1';

    const CUTIE_IMAGE_SRC = './cutie-hobby-horse.png';

    const MODES = {

      ARCADE: 'arcade',

      ZEN: 'zen',

      BUBBLE: 'bubble'

    };

    const MODE_ORDER = [MODES.ARCADE, MODES.ZEN, MODES.BUBBLE];


    const app = document.getElementById('app');

    const canvas = document.getElementById('game');

    const ctx = canvas ? canvas.getContext('2d') : null;

    const titleSubtitle = document.getElementById('titleSubtitle');

    const scoreCard = document.getElementById('scoreCard');

    const scoreLabel = document.getElementById('scoreLabel');

    const scoreValue = document.getElementById('scoreValue');

    const infoMain = document.getElementById('infoMain');

    const infoSub = document.getElementById('infoSub');

    const centerTip = document.getElementById('centerTip');

    

    const modeGroup = document.getElementById('modeGroup');

    const modeArcade = document.getElementById('modeArcade');

    const modeZen = document.getElementById('modeZen');

    const modeBubble = document.getElementById('modeBubble');

    const resetBtn = document.getElementById('resetBtn');


    if (!app || !canvas || !ctx) throw new Error('Canvas not available.');


    function freshState() {

      return {

        score: 0,

        best: 0,

        taps: 0,

        caughtHearts: 0,

        currentMode: MODES.ARCADE,

        lastPlayed: Date.now()

      };

    }


    function load() {

      try {

        const raw = localStorage.getItem(SAVE_KEY);

        const parsed = raw ? Object.assign(freshState(), JSON.parse(raw)) : freshState();

        if (!MODE_ORDER.includes(parsed.currentMode)) parsed.currentMode = MODES.ARCADE;

        return parsed;

      } catch {

        return freshState();

      }

    }


    let state = load();

    let W = 720;

    let H = 1280;

    let last = performance.now();

    let pulse = 0;

    let sparkleTimer = 0;

    let cloudShift = 0;

    let cutieImageReady = false;


    const cutieImage = new Image();

    cutieImage.onload = () => { cutieImageReady = true; };

    cutieImage.onerror = () => {

      cutieImageReady = false;

      console.warn('Could not load Cutie image at', CUTIE_IMAGE_SRC);

    };

    cutieImage.src = CUTIE_IMAGE_SRC;


    const game = {

      cutie: {

        x: W * 0.5,

        y: H * 0.64,

        bounce: 0,

        tilt: 0,

        gallop: 0

      },

      hearts: [],

      pops: [],

      stars: [],

      fallingHearts: [],

      flowers: [],

      bubbles: [],

      nextHeartSpawn: performance.now() + 900,

      nextBubbleSpawn: performance.now() + 800

    };


    function save() {

      state.lastPlayed = Date.now();

      localStorage.setItem(SAVE_KEY, JSON.stringify(state));

    }


    function resize() {

      const dpr = Math.min(window.devicePixelRatio || 1, 2);

      const rect = app.getBoundingClientRect();

      canvas.width = Math.round(rect.width * dpr);

      canvas.height = Math.round(rect.height * dpr);

      ctx.setTransform(1, 0, 0, 1, 0, 0);

      ctx.scale(dpr, dpr);

      W = rect.width;

      H = rect.height;

      game.cutie.x = W * 0.5;

      game.cutie.y = H * 0.64;

      layoutByMode();

    }


    function layoutByMode() {

      scoreCard.style.display = state.currentMode === MODES.ZEN ? 'none' : 'block';

      scoreLabel.textContent = state.currentMode === MODES.BUBBLE ? 'Bubble Score' : 'Cute Points';


      modeArcade.classList.toggle('active', state.currentMode === MODES.ARCADE);

      modeZen.classList.toggle('active', state.currentMode === MODES.ZEN);

      modeBubble.classList.toggle('active', state.currentMode === MODES.BUBBLE);


      if (state.currentMode === MODES.ARCADE) {

        titleSubtitle.textContent = 'Arcade mode is the score-chasing one. Tap Cutie, catch falling hearts, and do not let them hit the ground.';

        infoMain.textContent = 'Tap Cutie to make her boing.';

        infoSub.textContent = 'Tap Cutie. Catch hearts. Dropping them costs points!';

        centerTip.textContent = 'Tap Cutie';

      } else if (state.currentMode === MODES.ZEN) {

        titleSubtitle.textContent = 'Zen Garden is the calm mode. Hearts fall on their own and turn into a layered flower bed as they land.';

        infoMain.textContent = 'Zen Garden is running.';

        infoSub.textContent = 'No score. Tap anywhere to help the garden bloom.';

        centerTip.textContent = 'Just relax';

      } else {

        titleSubtitle.textContent = 'Bubble Pop Picnic is the playful mode. Pop floating bubbles for points, hearts, hugs, and golden little bonuses.';

        infoMain.textContent = 'Bubble Pop Picnic is live.';

        infoSub.textContent = 'Tap bubbles for points. Golden ones feel extra nice.';

        centerTip.textContent = 'Pop bubbles';

      }

    }


    function setMode(nextMode) {

      if (!MODE_ORDER.includes(nextMode) || nextMode === state.currentMode) return;

      state.currentMode = nextMode;

      game.fallingHearts = [];

      game.hearts = [];

      game.pops = [];

      game.stars = [];

      game.bubbles = [];

      if (state.currentMode !== MODES.ZEN) game.flowers = [];

      game.nextHeartSpawn = performance.now() + 900;

      game.nextBubbleSpawn = performance.now() + 800;

      centerTip.style.display = 'block';

      layoutByMode();

      save();

    }


    function cycleMode() {

      const idx = MODE_ORDER.indexOf(state.currentMode);

      setMode(MODE_ORDER[(idx + 1) % MODE_ORDER.length]);

    }


    function spawnHeartBurst(x, y, golden = false) {

      game.hearts.push({

        x,

        y,

        vx: (Math.random() - 0.5) * 0.7,

        vy: -1.8 - Math.random() * 0.9,

        drift: (Math.random() - 0.5) * 0.2,

        life: 1,

        size: golden ? 20 : 15 + Math.random() * 6,

        golden,

        wobble: Math.random() * Math.PI * 2

      });

    }


    function spawnPop(text, x, y, color, size = 24, big = false) {

      game.pops.push({

        x, y, text, color, life: 1, size, big,

        driftX: (Math.random() - 0.5) * (big ? 0.6 : 0.3)

      });

    }


    function spawnStars(x, y, count) {

      for (let i = 0; i < count; i += 1) {

        game.stars.push({

          x, y,

          vx: (Math.random() - 0.5) * 2.6,

          vy: -Math.random() * 2.8,

          life: 1,

          size: 2.5 + Math.random() * 3.5,

          hue: i % 3

        });

      }

    }


    function spawnFallingHeart(now) {

      const scale = Math.min(W / 720, H / 1280);

      const golden = Math.random() < 0.15;

      game.fallingHearts.push({

        x: Math.random() * (W - 80) + 40,

        y: -50,

        vy: (Math.random() * 3 + 4) * scale,

        size: (golden ? 24 : 18) * scale,

        golden,

        wobble: Math.random() * Math.PI * 2,

        wobbleSpeed: (Math.random() - 0.5) * 0.005,

        planted: false

      });

      game.nextHeartSpawn = now + 600 + Math.random() * 1200;

    }


    function spawnBubble(now) {

      const scale = Math.min(W / 720, H / 1280);

      const typeRoll = Math.random();

      const kind = typeRoll < 0.15 ? 'gold' : typeRoll < 0.55 ? 'heart' : typeRoll < 0.8 ? 'hug' : 'spark';

      const radius = (kind === 'gold' ? 26 : 22 + Math.random() * 8) * scale;

      game.bubbles.push({

        x: Math.random() * (W - 100) + 50,

        y: H + 60,

        vx: (Math.random() - 0.5) * 0.22 * scale,

        vy: -(1.4 + Math.random() * 1.2) * scale,

        r: radius,

        kind,

        life: 1,

        wobble: Math.random() * Math.PI * 2,

        wobbleSpeed: (Math.random() - 0.5) * 0.003,

        hue: Math.random()

      });

      game.nextBubbleSpawn = now + 380 + Math.random() * 700;

    }


    function drawHeart(x, y, size, fill) {

      ctx.save();

      ctx.translate(x, y);

      ctx.fillStyle = fill;

      ctx.beginPath();

      ctx.moveTo(0, size * 0.25);

      ctx.bezierCurveTo(size * 0.9, -size * 0.45, size * 1.25, size * 0.55, 0, size * 1.45);

      ctx.bezierCurveTo(-size * 1.25, size * 0.55, -size * 0.9, -size * 0.45, 0, size * 0.25);

      ctx.fill();

      ctx.restore();

    }


    function drawStar(x, y, r, color) {

      ctx.save();

      ctx.translate(x, y);

      ctx.fillStyle = color;

      ctx.beginPath();

      for (let i = 0; i < 10; i += 1) {

        const ang = -Math.PI / 2 + i * Math.PI / 5;

        const rad = i % 2 === 0 ? r : r * 0.45;

        const px = Math.cos(ang) * rad;

        const py = Math.sin(ang) * rad;

        if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);

      }

      ctx.closePath();

      ctx.fill();

      ctx.restore();

    }


    function drawFlower(x, y, size, color) {

      ctx.save();

      ctx.translate(x, y);

      ctx.strokeStyle = '#7cc8a3';

      ctx.lineWidth = Math.max(2, size * 0.12);

      ctx.beginPath();

      ctx.moveTo(0, 0);

      ctx.lineTo(0, size * 1.6);

      ctx.stroke();

      ctx.fillStyle = color;

      for (let i = 0; i < 5; i += 1) {

        const a = (Math.PI * 2 * i) / 5;

        ctx.beginPath();

        ctx.ellipse(Math.cos(a) * size * 0.55, Math.sin(a) * size * 0.55, size * 0.45, size * 0.28, a, 0, Math.PI * 2);

        ctx.fill();

      }

      ctx.fillStyle = '#ffe58a';

      ctx.beginPath();

      ctx.arc(0, 0, size * 0.35, 0, Math.PI * 2);

      ctx.fill();

      ctx.restore();

    }


    function drawCloud(x, y, scale) {

      ctx.save();

      ctx.translate(x, y);

      ctx.scale(scale, scale);

      ctx.fillStyle = 'rgba(255,255,255,0.88)';

      ctx.beginPath();

      ctx.arc(0, 18, 22, 0, Math.PI * 2);

      ctx.arc(24, 6, 28, 0, Math.PI * 2);

      ctx.arc(54, 18, 20, 0, Math.PI * 2);

      ctx.fill();

      ctx.restore();

    }


    function roundRectPath(x, y, w, h, r) {

      ctx.beginPath();

      ctx.moveTo(x + r, y);

      ctx.arcTo(x + w, y, x + w, y + h, r);

      ctx.arcTo(x + w, y + h, x, y + h, r);

      ctx.arcTo(x, y + h, x, y, r);

      ctx.arcTo(x, y, x + w, y, r);

      ctx.closePath();

    }


    function drawBackground(dt) {

      const sky = ctx.createLinearGradient(0, 0, 0, H);

      sky.addColorStop(0, '#fff2fb');

      sky.addColorStop(1, '#dff7ff');

      ctx.fillStyle = sky;

      ctx.fillRect(0, 0, W, H);


      ctx.fillStyle = 'rgba(255,255,255,0.55)';

      ctx.beginPath();

      ctx.arc(W * 0.18, H * 0.1, 48, 0, Math.PI * 2);

      ctx.fill();

      ctx.beginPath();

      ctx.arc(W * 0.82, H * 0.14, 34, 0, Math.PI * 2);

      ctx.fill();


      cloudShift += dt * 0.006;

      drawCloud((W * 0.1 + cloudShift * 8) % (W + 120) - 60, H * 0.14, 1.2);

      drawCloud((W * 0.55 + cloudShift * 5) % (W + 120) - 40, H * 0.19, 0.9);

      drawCloud((W * 0.8 + cloudShift * 6) % (W + 120) - 50, H * 0.11, 1.05);


      ctx.fillStyle = '#c6f7df';

      ctx.beginPath();

      ctx.ellipse(W * 0.5, H * 0.92, W * 0.62, H * 0.11, 0, 0, Math.PI * 2);

      ctx.fill();


      ctx.fillStyle = '#a7edc8';

      ctx.beginPath();

      ctx.ellipse(W * 0.5, H * 0.98, W * 0.82, H * 0.14, 0, 0, Math.PI * 2);

      ctx.fill();


      if (state.currentMode === MODES.ZEN) {

        game.flowers.forEach(f => drawFlower(f.x, f.y, f.size, f.color));

      }


      for (let i = 0; i < 8; i += 1) {

        const fx = i * (W / 7) + (i % 2) * 20;

        ctx.fillStyle = i % 2 ? '#ff9ed8' : '#ffe48f';

        ctx.beginPath();

        ctx.arc(fx, H * 0.88 + Math.sin(i + cloudShift * 0.03) * 8, 6 + (i % 3), 0, Math.PI * 2);

        ctx.fill();

      }

    }


    function drawCutie(now) {

      const u = game.cutie;

      const bob = Math.sin(now * 0.0045) * 3 - u.bounce * 12;

      const tilt = Math.sin(now * 0.0035) * 0.008 - u.tilt * 0.03;

      const trot = Math.sin(now * 0.012 + u.gallop);

      const scale = Math.min(W / 720, H / 1280);

      const hop = Math.max(0, trot) * 2.5 * scale;

      const jointX = u.x;

      const jointY = u.y + bob - hop;


      ctx.save();

      ctx.translate(jointX, jointY);

      ctx.rotate(tilt * 0.45);

      ctx.scale(1 + pulse * 0.015, 1 - pulse * 0.015);


      if (cutieImageReady) {

        const drawH = 300 * scale;

        const aspect = cutieImage.naturalWidth > 0 && cutieImage.naturalHeight > 0 ? cutieImage.naturalWidth / cutieImage.naturalHeight : 0.72;

        const drawW = drawH * aspect;

        ctx.drawImage(cutieImage, -drawW * 0.5, -drawH * 0.72, drawW, drawH);

      } else {

        ctx.fillStyle = '#fffdfd';

        ctx.strokeStyle = '#e7c5da';

        ctx.lineWidth = 4;

        roundRectPath(-48 * scale, -120 * scale, 96 * scale, 140 * scale, 32 * scale);

        ctx.fill();

        ctx.stroke();


        ctx.fillStyle = '#8b5d41';

        roundRectPath(-8 * scale, 0, 16 * scale, 220 * scale, 5 * scale);

        ctx.fill();

      }


      ctx.restore();

    }


    function boop(pointerX = game.cutie.x, pointerY = game.cutie.y - 40) {

      state.taps += 1;

      pulse = 1;

      game.cutie.bounce = 1;

      game.cutie.tilt = 1;

      game.cutie.gallop += 0.8;

      centerTip.style.display = 'none';


      if (state.currentMode === MODES.ZEN) {

        const scale = Math.min(W / 720, H / 1280);

        const golden = Math.random() < 0.18;

        game.fallingHearts.push({

          x: game.cutie.x + (Math.random() - 0.5) * 18,

          y: game.cutie.y - 110,

          vy: (1.8 + Math.random() * 1.2) * scale,

          size: (golden ? 22 : 18) * scale,

          golden,

          wobble: Math.random() * Math.PI * 2,

          wobbleSpeed: (Math.random() - 0.5) * 0.005,

          planted: false

        });

        spawnStars(pointerX, pointerY, 8);

        infoMain.textContent = 'Cutie helped the garden bloom.';

        infoSub.textContent = 'One more heart for the flower bed.';

        save();

        return;

      }


      if (state.currentMode === MODES.BUBBLE) {

        state.score += 1;

        scoreValue.textContent = state.score.toLocaleString();

        spawnPop('+1', pointerX, pointerY, '#ff5bb3', 22, false);

        spawnStars(pointerX, pointerY, 6);

        infoMain.textContent = 'Cutie helped pop the picnic.';

        infoSub.textContent = 'Tap bubbles too for bigger bonuses.';

        save();

        return;

      }


      let gained = 1;

      let label = '+1';

      let popColor = '#ff5bb3';

      let popSize = 24;

      if (Math.random() < 0.16) {

        gained = 3;

        label = 'Sweet! +3';

        popColor = '#b888ff';

        popSize = 28;

      }

      if (Math.random() < 0.06) {

        gained = 5;

        label = 'Golden boop! +5';

        popColor = '#ffb703';

        popSize = 30;

      }

      const hugBurst = Math.random() < 0.12;


      state.score += gained;

      state.best = Math.max(state.best, state.score);

      scoreValue.textContent = state.score.toLocaleString();


      spawnHeartBurst(game.cutie.x + (Math.random() - 0.5) * 18, game.cutie.y - 58, gained >= 5);

      if (gained >= 3) spawnHeartBurst(game.cutie.x + (Math.random() - 0.5) * 26, game.cutie.y - 48);

      if (gained >= 5) spawnHeartBurst(game.cutie.x + (Math.random() - 0.5) * 34, game.cutie.y - 38, true);

      spawnPop(label, pointerX, pointerY, popColor, popSize, false);

      if (hugBurst) {

        spawnPop('CUTIE GETS A HUG!!!', pointerX, pointerY - 34, '#ff5bb3', 42, true);

        spawnStars(pointerX, pointerY - 8, 14);

      }

      spawnStars(pointerX, pointerY, gained >= 5 ? 12 : 7);


      if (gained >= 5) {

        infoMain.textContent = 'Cutie did a legendary boing.';

        infoSub.textContent = 'You found a golden heart. Keep catching them!';

      } else if (state.score > 0 && state.score % 25 === 0) {

        infoMain.textContent = `Tiny milestone: ${state.score} Cute Points.`;

        infoSub.textContent = 'Still pointless. Still slightly fun. Watch the drops!';

      } else {

        infoMain.textContent = 'Tap Cutie to make her boing.';

        infoSub.textContent = 'Tap Cutie. Catch hearts. Dropping them costs points!';

      }


      save();

    }


    function pointerPos(ev) {

      const rect = canvas.getBoundingClientRect();

      return { x: ev.clientX - rect.left, y: ev.clientY - rect.top };

    }


    function handleArcadeTap(p) {

      let heartCaught = false;

      for (let i = game.fallingHearts.length - 1; i >= 0; i -= 1) {

        const h = game.fallingHearts[i];

        const wobbleX = Math.sin(h.wobble) * 20;

        const dxHeart = p.x - (h.x + wobbleX);

        const dyHeart = p.y - h.y;

        const heartRadius = h.size * 1.1;

        if ((dxHeart * dxHeart) + (dyHeart * dyHeart) <= heartRadius * heartRadius) {

          game.fallingHearts.splice(i, 1);

          const bonus = h.golden ? 10 : 4;

          state.score += bonus;

          state.caughtHearts += 1;

          scoreValue.textContent = state.score.toLocaleString();

          spawnPop(h.golden ? 'Golden Heart! +10' : 'Caught! +4', p.x, p.y, h.golden ? '#ffb703' : '#ff5bb3', h.golden ? 30 : 24, h.golden);

          spawnStars(p.x, p.y, h.golden ? 16 : 9);

          infoMain.textContent = h.golden ? 'Golden heart caught!' : 'Heart caught!';

          infoSub.textContent = h.golden ? 'That one was worth a lot. Watch the ground!' : 'Nice catch. Don\'t let the rest drop!';

          save();

          heartCaught = true;

          break;

        }

      }

      if (heartCaught) return true;


      const scale = Math.min(W / 720, H / 1280);

      const headCenterX = game.cutie.x;

      const headCenterY = game.cutie.y - 92 * scale;

      const dx = p.x - headCenterX;

      const dy = p.y - headCenterY;

      const distance = Math.sqrt(dx * dx + dy * dy);

      const hitRadius = 62 * scale;

      if (distance <= hitRadius) {

        boop(p.x, p.y);

        return true;

      }

      spawnPop('Miss!', p.x, p.y, 'rgba(111, 71, 116, 0.55)', 18, false);

      return false;

    }


    function handleBubbleTap(p) {

      for (let i = game.bubbles.length - 1; i >= 0; i -= 1) {

        const b = game.bubbles[i];

        const bubbleX = b.x + Math.sin(b.wobble) * 14;

        const dx = p.x - bubbleX;

        const dy = p.y - b.y;

        if ((dx * dx) + (dy * dy) <= b.r * b.r) {

          game.bubbles.splice(i, 1);

          let value = 2;

          let text = '+2';

          let color = '#91f3d1';

          if (b.kind === 'heart') {

            value = 5; text = 'Heart Bubble! +5'; color = '#ff7fc8';

            spawnHeartBurst(bubbleX, b.y, false);

          } else if (b.kind === 'hug') {

            value = 8; text = 'HUG! +8'; color = '#ff5bb3';

          } else if (b.kind === 'gold') {

            value = 12; text = 'Golden Pop! +12'; color = '#ffb703';

            spawnHeartBurst(bubbleX, b.y, true);

          } else {

            value = 3; text = 'Sparkle! +3'; color = '#b888ff';

          }

          state.score += value;

          scoreValue.textContent = state.score.toLocaleString();

          spawnPop(text, bubbleX, b.y, color, b.kind === 'gold' ? 30 : 24, b.kind !== 'spark');

          spawnStars(bubbleX, b.y, b.kind === 'gold' ? 16 : 10);

          infoMain.textContent = b.kind === 'gold' ? 'Golden bubble popped!' : b.kind === 'hug' ? 'Cutie gets a hug!!!' : 'Bubble popped!';

          infoSub.textContent = 'Keep the picnic going.';

          save();

          return true;

        }

      }


      const scale = Math.min(W / 720, H / 1280);

      const headCenterX = game.cutie.x;

      const headCenterY = game.cutie.y - 92 * scale;

      const dx = p.x - headCenterX;

      const dy = p.y - headCenterY;

      const distance = Math.sqrt(dx * dx + dy * dy);

      if (distance <= 62 * scale) {

        boop(p.x, p.y);

        return true;

      }

      spawnPop('plip', p.x, p.y, 'rgba(111, 71, 116, 0.38)', 16, false);

      return false;

    }


    function tapped(ev) {

      ev.preventDefault();

      const p = pointerPos(ev);

      if (state.currentMode === MODES.ZEN) {

        boop(p.x, p.y);

        return;

      }

      if (state.currentMode === MODES.ARCADE) handleArcadeTap(p);

      else handleBubbleTap(p);

    }


    function drawBubble(b) {

      const x = b.x + Math.sin(b.wobble) * 14;

      const y = b.y;

      ctx.save();

      ctx.translate(x, y);

      ctx.globalAlpha = 0.88;

      const stroke = b.kind === 'gold' ? '#ffb703' : b.kind === 'heart' ? '#ff7fc8' : b.kind === 'hug' ? '#ff5bb3' : '#b888ff';

      ctx.fillStyle = 'rgba(255,255,255,0.22)';

      ctx.strokeStyle = stroke;

      ctx.lineWidth = Math.max(2, b.r * 0.1);

      ctx.beginPath();

      ctx.arc(0, 0, b.r, 0, Math.PI * 2);

      ctx.fill();

      ctx.stroke();

      ctx.beginPath();

      ctx.arc(-b.r * 0.25, -b.r * 0.25, b.r * 0.28, 0, Math.PI * 2);

      ctx.fillStyle = 'rgba(255,255,255,0.35)';

      ctx.fill();


      if (b.kind === 'heart') drawHeart(0, 2, b.r * 0.45, '#ff7fc8');

      else if (b.kind === 'gold') drawStar(0, 2, b.r * 0.45, '#ffb703');

      else if (b.kind === 'hug') {

        ctx.fillStyle = '#ff5bb3';

        ctx.font = `900 ${Math.max(12, b.r * 0.55)}px Inter, sans-serif`;

        ctx.textAlign = 'center';

        ctx.fillText('♡', 0, b.r * 0.2);

      } else drawStar(0, 2, b.r * 0.38, '#b888ff');

      ctx.restore();

    }


    function updateArcade(dt, now) {

      if (now > game.nextHeartSpawn) spawnFallingHeart(now);

    }


    function updateZen(dt, now) {

      if (now > game.nextHeartSpawn) spawnFallingHeart(now);

    }


    function updateBubble(dt, now) {

      if (now > game.nextBubbleSpawn) spawnBubble(now);

    }


    function updateAndDrawEffects(dt, now) {

      if (state.currentMode === MODES.ARCADE || state.currentMode === MODES.ZEN) {

        game.fallingHearts.forEach(h => {

          h.y += h.vy * dt * 0.06;

          h.wobble += h.wobbleSpeed * dt;

          const wobbleX = Math.sin(h.wobble) * 20;

          ctx.save();

          ctx.translate(h.x + wobbleX, h.y);

          let drawSize = h.size;

          if (h.golden) drawSize += Math.sin(now * 0.01) * 2;

          drawHeart(0, 0, drawSize, h.golden ? '#ffb703' : '#ff7fc8');

          ctx.restore();

        });


        if (state.currentMode === MODES.ARCADE) {

          game.fallingHearts = game.fallingHearts.filter(h => {

            if (h.y >= H + 60) {

              const penalty = h.golden ? 5 : 2;

              state.score = Math.max(0, state.score - penalty);

              scoreValue.textContent = state.score.toLocaleString();

              spawnPop(`-${penalty}`, h.x, H - 40, '#ff4757', 24, true);

              save();

              return false;

            }

            return true;

          });

        } else {

          game.fallingHearts = game.fallingHearts.filter(h => {

            if (h.y >= H - 230) {

              if (game.flowers.length > 60) game.flowers.shift();

              const flowerBaseY = H - 305 + Math.random() * 62;

              game.flowers.push({

                x: Math.max(34, Math.min(W - 34, h.x + Math.sin(h.wobble) * 20)),

                y: flowerBaseY,

                size: h.golden ? 14 : 10 + Math.random() * 4,

                color: h.golden ? '#ffe58a' : Math.random() < 0.5 ? '#ff9ed8' : '#b888ff'

              });

              return false;

            }

            return true;

          });

        }

      }


      if (state.currentMode === MODES.BUBBLE) {

        game.bubbles.forEach(b => {

          b.wobble += b.wobbleSpeed * dt;

          b.x += b.vx * dt * 0.06;

          b.y += b.vy * dt * 0.06;

          b.x = Math.max(40, Math.min(W - 40, b.x));

          drawBubble(b);

        });

        game.bubbles = game.bubbles.filter(b => b.y + b.r > -40);

      }


      game.hearts.forEach(h => {

        h.wobble += dt * 0.01;

        h.x += (h.vx + Math.sin(h.wobble) * h.drift) * dt * 0.06;

        h.y += h.vy * dt * 0.06;

        h.life -= 0.012 * dt * 0.06;

        ctx.save();

        ctx.globalAlpha = Math.max(0, h.life);

        ctx.translate(h.x, h.y);

        ctx.rotate(Math.sin(h.wobble) * 0.15);

        drawHeart(0, 0, h.size, h.golden ? '#ffd84d' : '#ff7fc8');

        ctx.restore();

      });

      game.hearts = game.hearts.filter(h => h.life > 0);


      const starColors = ['#ffe58a', '#b888ff', '#91f3d1'];

      game.stars.forEach(s => {

        s.x += s.vx * dt * 0.06;

        s.y += s.vy * dt * 0.06;

        s.vy += 0.035 * dt * 0.06;

        s.life -= 0.018 * dt * 0.06;

        ctx.save();

        ctx.globalAlpha = Math.max(0, s.life);

        drawStar(s.x, s.y, s.size, starColors[s.hue]);

        ctx.restore();

      });

      game.stars = game.stars.filter(s => s.life > 0);


      game.pops.forEach(p => {

        p.x += p.driftX;

        p.y -= (p.big ? 0.55 : 0.3) * dt * 0.06;

        p.life -= (p.big ? 0.014 : 0.018) * dt * 0.06;

        ctx.save();

        ctx.globalAlpha = Math.max(0, p.life);

        ctx.fillStyle = p.color;

        ctx.textAlign = 'center';

        ctx.lineWidth = p.big ? 8 : 4;

        ctx.strokeStyle = 'rgba(255,255,255,0.92)';

        ctx.font = `900 ${p.size}px Inter, sans-serif`;

        if (p.big) ctx.strokeText(p.text, p.x, p.y);

        ctx.fillText(p.text, p.x, p.y);

        ctx.restore();

      });

      game.pops = game.pops.filter(p => p.life > 0);

    }


    function tick(now) {

      const dt = Math.min(32, now - last);

      last = now;

      pulse = Math.max(0, pulse - 0.028 * dt * 0.06);

      game.cutie.bounce = Math.max(0, game.cutie.bounce - 0.03 * dt * 0.06);

      game.cutie.tilt = Math.max(0, game.cutie.tilt - 0.032 * dt * 0.06);

      game.cutie.gallop *= 0.992;

      sparkleTimer += dt;


      switch (state.currentMode) {

        case MODES.ZEN:

          updateZen(dt, now);

          break;

        case MODES.BUBBLE:

          updateBubble(dt, now);

          break;

        case MODES.ARCADE:

        default:

          updateArcade(dt, now);

          break;

      }


      drawBackground(dt);

      drawCutie(now);

      updateAndDrawEffects(dt, now);


      if (sparkleTimer > 1200) {

        sparkleTimer = 0;

        if (Math.random() < 0.7) {

          spawnStars(game.cutie.x + (Math.random() - 0.5) * 90, game.cutie.y - 140 + Math.random() * 40, 3);

        }

      }


      requestAnimationFrame(tick);

    }


    function resetGame() {

      const mode = state.currentMode;

      state = freshState();

      state.currentMode = mode;

      scoreValue.textContent = '0';

      game.hearts = [];

      game.pops = [];

      game.stars = [];

      game.fallingHearts = [];

      game.flowers = [];

      game.bubbles = [];

      game.nextHeartSpawn = performance.now() + 900;

      game.nextBubbleSpawn = performance.now() + 800;

      centerTip.style.display = 'block';

      layoutByMode();

      save();

    }


    canvas.addEventListener('pointerdown', tapped, { passive: false });

    modeArcade.addEventListener('click', () => setMode(MODES.ARCADE));

    modeZen.addEventListener('click', () => setMode(MODES.ZEN));

    modeBubble.addEventListener('click', () => setMode(MODES.BUBBLE));

    resetBtn.addEventListener('click', resetGame);

    window.addEventListener('resize', resize);

    window.addEventListener('orientationchange', resize);


    resize();

    scoreValue.textContent = state.score.toLocaleString();

    layoutByMode();

    requestAnimationFrame(tick);

  </script>

</body>

</html>