<!doctype html>

<html lang="en">

<head>

  <meta charset="utf-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <title>Daily Fix for 26</title>

  <meta name="description" content="12-month challenge tracker: tick off daily reps, swaps, and notes." />

  <style>

    :root{

      --bg:#0b0f14; --card:#121826; --muted:#9aa4b2; --text:#e6edf3;

      --accent:#58a6ff; --ok:#3fb950; --warn:#d29922; --bad:#f85149;

    }

    *{box-sizing:border-box}

    body{

      margin:0;

      font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;

      background:linear-gradient(180deg,#080b10,#0b0f14);

      color:var(--text)

    }

    header{

      position:sticky; top:0; z-index:10;

      background:rgba(11,15,20,.9);

      backdrop-filter: blur(10px);

      border-bottom:1px solid rgba(255,255,255,.06);

    }

    .wrap{max-width:980px;margin:0 auto;padding:16px}

    h1{font-size:20px;margin:0;letter-spacing:.3px}

    .sub{color:var(--muted);font-size:13px;margin-top:6px}

    .grid{display:grid;gap:12px}

    @media(min-width:900px){.grid{grid-template-columns:1.15fr .85fr}}

    .card{

      background:rgba(18,24,38,.92);

      border:1px solid rgba(255,255,255,.07);

      border-radius:16px;

      box-shadow:0 10px 30px rgba(0,0,0,.25)

    }

    .card .hd{padding:14px 14px 0 14px}

    .card .bd{padding:14px}

    .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}

    button, input, textarea{

      font:inherit;

      border-radius:12px;

      border:1px solid rgba(255,255,255,.10);

      background:#0c111b;

      color:var(--text);

      padding:10px 12px

    }

    button{cursor:pointer}

    button.small{padding:8px 10px;border-radius:12px;font-size:13px}

    textarea{width:100%;min-height:72px;resize:vertical}

    .pill{

      display:inline-flex;gap:8px;align-items:center;

      padding:8px 10px;border-radius:999px;

      border:1px solid rgba(255,255,255,.10);

      background:#0c111b;color:var(--muted);font-size:13px

    }

    .pill strong{color:var(--text)}

    .table{width:100%;border-collapse:separate;border-spacing:0 10px}

    .table th{color:var(--muted);font-weight:600;font-size:12px;text-align:left;padding:0 10px}

    .table td{

      padding:12px 10px;background:#0c111b;

      border-top:1px solid rgba(255,255,255,.07);

      border-bottom:1px solid rgba(255,255,255,.07)

    }

    .table td:first-child{

      border-left:1px solid rgba(255,255,255,.07);

      border-top-left-radius:14px;border-bottom-left-radius:14px

    }

    .table td:last-child{

      border-right:1px solid rgba(255,255,255,.07);

      border-top-right-radius:14px;border-bottom-right-radius:14px

    }

    .chk{transform:scale(1.35)}

    .muted{color:var(--muted)}

    .kpi{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}

    .kpi .box{padding:12px;background:#0c111b;border:1px solid rgba(255,255,255,.07);border-radius:14px}

    .kpi .num{font-size:20px;font-weight:800}

    .kpi .lbl{font-size:12px;color:var(--muted)}

    .tabs{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}

    .tab{

      padding:10px 12px;border-radius:12px;

      border:1px solid rgba(255,255,255,.10);background:transparent

    }

    .tab.active{background:#0c111b;border-color:rgba(88,166,255,.35)}

    .footer-note{font-size:12px;color:var(--muted);line-height:1.4}

    .hide{display:none!important}

    .two{display:grid;gap:10px}

    @media(min-width:700px){.two{grid-template-columns:1fr 1fr}}

    /* Season banner + totals + progress */

    .banner{

      display:flex;align-items:center;justify-content:space-between;gap:10px;

      padding:12px 14px;border-radius:16px;

      background:linear-gradient(135deg, rgba(88,166,255,.18), rgba(63,185,80,.10));

      border:1px solid rgba(255,255,255,.10);

      margin-bottom:12px;

    }

    .banner .title{font-weight:900;letter-spacing:.2px}

    .banner .meta{font-size:12px;color:var(--muted);margin-top:2px}

    .totals{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}

    .totals .box{padding:12px;background:#0c111b;border:1px solid rgba(255,255,255,.07);border-radius:14px}

    .totals .num{font-size:18px;font-weight:900}

    .totals .lbl{font-size:12px;color:var(--muted)}

    .mini-card{

      background:#0c111b;

      border:1px solid rgba(255,255,255,.07);

      border-radius:14px;

      box-shadow:none;

      margin-top:10px;

    }

  </style>

</head>

<body>

<header>

  <div class="wrap">

    <div class="row" style="justify-content:space-between;">

      <div>

        <h1>Daily Fix for 26</h1>

        <div class="sub">Jan–Jun: 50 reps each • Jul–Dec: 100 reps each • Tick, swap, note.</div>

      </div>

      <div class="row">

        <button class="tab active" data-view="today">Today</button>

        <button class="tab" data-view="calendar">Calendar</button>

        <button class="tab" data-view="rules">Rules</button>

        <button class="tab" data-view="settings">Settings</button>

      </div>

    </div>

  </div>

</header>

<main class="wrap grid">

  <!-- LEFT -->

  <section class="card">

    <div class="hd">

      <div class="row" style="justify-content:space-between;">

        <div class="pill"><strong id="dateLabel">—</strong><span class="muted" id="dayLabel">—</span></div>

        <div class="row">

          <button class="small" id="prevDay">◀</button>

          <button class="small" id="goToday">Today</button>

          <button class="small" id="nextDay">▶</button>

        </div>

      </div>

    </div>

    <div class="bd">

      <!-- TODAY -->

      <div id="view-today">

        <div class="banner">

          <div>

            <div class="title" id="seasonTitle">—</div>

            <div class="meta" id="seasonMeta">—</div>

          </div>

          <div class="pill"><span class="muted">Reps per movement</span> <strong id="seasonReps">—</strong></div>

        </div>

        <div class="kpi">

          <div class="box"><div class="num" id="kpiDone">0</div><div class="lbl">Movements done today</div></div>

          <div class="box"><div class="num" id="kpiTotal">0</div><div class="lbl">Total movements today</div></div>

          <div class="box"><div class="num" id="kpiReps">0</div><div class="lbl">Reps completed today</div></div>

        </div>

        <div class="totals">

          <div class="box"><div class="num" id="totWeek">0</div><div class="lbl">Reps this week</div></div>

          <div class="box"><div class="num" id="totMonth">0</div><div class="lbl">Reps this month</div></div>

          <div class="box"><div class="num" id="totYear">0</div><div class="lbl">Reps this year</div></div>

        </div>

        <!-- Year progress -->

        <div class="mini-card">

          <div class="bd" style="padding:14px">

            <div style="display:flex;justify-content:space-between;align-items:center;gap:12px">

              <div>

                <strong>Year progress</strong>

                <div class="muted" style="font-size:12px">Total reps completed this year</div>

              </div>

              <strong id="yearReps">0</strong>

            </div>

            <div style="height:10px"></div>

            <div style="background:#111827;border-radius:999px;overflow:hidden;height:10px">

              <div id="yearBar" style="height:100%;width:0%;background:linear-gradient(90deg,#58a6ff,#3fb950)"></div>

            </div>

            <div class="muted" style="font-size:12px;margin-top:8px">

              Target is based on your movements and the 50→100 reps schedule.

            </div>

          </div>

        </div>

        <!-- Best week -->

        <div class="mini-card">

          <div class="bd" style="padding:14px">

            <strong>Best week (all time)</strong>

            <div class="muted" style="font-size:12px">Highest reps in any Mon–Sun week</div>

            <div style="font-size:22px;font-weight:900;margin-top:6px" id="bestWeek">0</div>

          </div>

        </div>

        <div style="height:12px"></div>

        <table class="table" id="todayTable">

          <thead>

            <tr>

              <th style="width:80px;">Done</th>

              <th>Movement</th>

              <th style="width:120px;">Reps</th>

              <th>Swap / Notes</th>

            </tr>

          </thead>

          <tbody></tbody>

        </table>

        <div class="two">

          <div class="card" style="background:transparent;border:none;box-shadow:none">

            <div class="footer-note">

              <div><strong>Photo check-in (optional):</strong> take a private photo at the <strong>start</strong> (optional) and then <strong>once each month</strong> (only share if you want).</div>

            </div>

          </div>

          <div class="card" style="background:transparent;border:none;box-shadow:none">

            <div class="row" style="justify-content:flex-end;">

              <button id="markAll" class="small">Mark all done</button>

              <button id="clearDay" class="small">Clear today</button>

            </div>

            <div class="footer-note" style="margin-top:10px">

              Progress saves automatically on this device.

              <br/>Publish the file to share a link for others.

            </div>

          </div>

        </div>

      </div>

      <!-- CALENDAR -->

      <div id="view-calendar" class="hide">

        <div class="row" style="justify-content:space-between;">

          <div class="pill"><strong id="monthLabel">—</strong></div>

          <div class="row">

            <button class="small" id="prevMonth">◀</button>

            <button class="small" id="nextMonth">▶</button>

          </div>

        </div>

        <div style="height:12px"></div>

        <div id="calendarGrid" class="grid" style="grid-template-columns:repeat(7,1fr);gap:8px"></div>

        <div style="height:10px"></div>

        <div class="footer-note">

          Tap any day to open it. Green = all movements done. Yellow = partial. Red = none.

        </div>

      </div>

      <!-- RULES -->

      <div id="view-rules" class="hide">

        <h3 style="margin:0 0 10px 0">Rules</h3>

        <div class="mini-card">

          <div class="bd" style="padding:14px">

            <ol style="margin:0;padding-left:18px;line-height:1.5">

              <li><strong>Daily goal:</strong> complete Squats, Press ups, Sit ups / crunches.</li>

              <li><strong>Reps:</strong> <strong>50</strong> each day from <strong>Jan–Jun</strong>, then <strong>100</strong> each day from <strong>Jul–Dec</strong>.</li>

              <li><strong>If you’re injured/limited:</strong> swap the movement for a safe alternative (record it in “Swap / Notes”).</li>

              <li><strong>Missed day:</strong> mark it honestly. No pressure to double up.</li>

              <li><strong>Photos:</strong> optional start photo + monthly private check-in.</li>

            </ol>

            <div style="margin-top:10px" class="muted">Not medical advice — if pain is sharp/worsening, get it checked.</div>

          </div>

        </div>

      </div>

      <!-- SETTINGS -->

      <div id="view-settings" class="hide">

        <h3 style="margin:0 0 10px 0">Settings</h3>

        <div class="mini-card">

          <div class="bd" style="padding:14px">

            <div class="two">

              <div>

                <div class="muted" style="font-size:12px;margin-bottom:6px">Challenge start date</div>

                <input type="date" id="startDate" />

              </div>

              <div>

                <div class="muted" style="font-size:12px;margin-bottom:6px">Duration (months)</div>

                <input type="number" id="durationMonths" min="1" max="24" />

              </div>

            </div>

            <div style="height:12px"></div>

            <div class="muted" style="font-size:12px;margin-bottom:6px">Movements (names only)</div>

            <div id="movesList"></div>

            <div style="height:10px"></div>

            <div class="row">

              <button id="addMove" class="small">+ Add movement</button>

              <button id="resetDefaults" class="small">Reset defaults</button>

            </div>

            <hr style="border:none;border-top:1px solid rgba(255,255,255,.08);margin:14px 0"/>

            <div class="muted" style="font-size:12px;margin-bottom:6px">Export / Import (backup)</div>

            <textarea id="exportBox" placeholder="Click Export to generate a backup blob. Paste here then Import to restore."></textarea>

            <div class="row" style="margin-top:10px">

              <button id="btnExport" class="small">Export</button>

              <button id="btnImport" class="small">Import</button>

              <button id="btnWipe" class="small" title="Clears all saved progress on this device">Wipe this device</button>

            </div>

          </div>

        </div>

        <div style="height:12px"></div>

        <div class="footer-note">

          <strong>Publish tip:</strong> Netlify → drag & drop this HTML file → share the link → “Add to Home Screen”.

        </div>

      </div>

    </div>

  </section>

  <!-- RIGHT -->

  <aside class="card">

    <div class="hd"><h3 style="margin:0;padding:14px 14px 0 14px;">Quick stats</h3></div>

    <div class="bd">

      <div class="row" style="justify-content:space-between;">

        <div class="pill"><span class="muted">Streak (all done)</span> <strong id="streak">0</strong></div>

        <div class="pill"><span class="muted">This month</span> <strong id="monthPct">0%</strong></div>

        <div class="pill"><span class="muted">All time</span> <strong id="allPct">0%</strong></div>

      </div>

      <div style="height:12px"></div>

      <div class="mini-card">

        <div class="bd" style="padding:14px">

          <div style="font-weight:700;margin-bottom:6px">Publish (easiest)</div>

          <ol style="margin:0;padding-left:18px;line-height:1.55" class="muted">

            <li>Go to <strong>netlify.com</strong> and create a free account.</li>

            <li>Drag & drop this <strong>daily-fix-26.html</strong> file into “Sites”.</li>

            <li>Netlify gives you a link — share it.</li>

            <li>On phone: open link → menu → <strong>Add to Home Screen</strong>.</li>

          </ol>

        </div>

      </div>

      <div style="height:12px"></div>

      <div class="mini-card">

        <div class="bd" style="padding:14px">

          <div style="font-weight:700;margin-bottom:6px">Important note</div>

          <div class="muted" style="line-height:1.55">

            This version saves progress on <strong>each person’s own phone</strong>.

            If you want a shared leaderboard later, we can add logins + a database.

          </div>

        </div>

      </div>

    </div>

  </aside>

</main>

<script>

  const LS_KEY = "dailyFix26_vFinal";

  // Reps rule: Jan–Jun = 50, Jul–Dec = 100

  function repsForDate(dateObj){

    const month = dateObj.getMonth() + 1; // 1-12

    return (month <= 6) ? 50 : 100;

  }

  function seasonLabel(dateObj){

    const month = dateObj.getMonth() + 1;

    if (month <= 6) return { title: "50s Season", meta: "Jan – Jun (build the habit)" };

    return { title: "100s Season", meta: "Jul – Dec (turn it up)" };

  }

  const DEFAULTS = {

    // Default to Jan 1 of the current year

    startDate: (() => {

      const d = new Date();

      return `${d.getFullYear()}-01-01`;

    })(),

    durationMonths: 12,

    movements: [

      { name: "Squats" },

      { name: "Press ups" },

      { name: "Sit ups / crunches" }

    ],

    days: {} // "YYYY-MM-DD": { done:[bool...], notes:[""...] }

  };

  function load(){

    try{

      const raw = localStorage.getItem(LS_KEY);

      if (!raw) return structuredClone(DEFAULTS);

      const parsed = JSON.parse(raw);

      return { ...structuredClone(DEFAULTS), ...parsed };

    }catch{

      return structuredClone(DEFAULTS);

    }

  }

  function save(){ localStorage.setItem(LS_KEY, JSON.stringify(state)); }

  let state = load();

  let current = new Date();

  let calMonth = new Date(current.getFullYear(), current.getMonth(), 1);

  const $ = (id) => document.getElementById(id);

  function ymd(d){

    const z = new Date(d);

    const yyyy = z.getFullYear();

    const mm = String(z.getMonth()+1).padStart(2,"0");

    const dd = String(z.getDate()).padStart(2,"0");

    return `${yyyy}-${mm}-${dd}`;

  }

  function niceDate(d){

    return d.toLocaleDateString(undefined, { weekday:"short", year:"numeric", month:"short", day:"numeric" });

  }

  function ensureDay(key){

    if (!state.days[key]){

      state.days[key] = {

        done: state.movements.map(()=>false),

        notes: state.movements.map(()=> "")

      };

    } else {

      // keep arrays aligned if movements change

      const m = state.movements.length;

      state.days[key].done = (state.days[key].done || []).slice(0,m);

      state.days[key].notes = (state.days[key].notes || []).slice(0,m);

      while (state.days[key].done.length < m) state.days[key].done.push(false);

      while (state.days[key].notes.length < m) state.days[key].notes.push("");

    }

  }

  function daySummary(key){

    ensureDay(key);

    const d = state.days[key];

    const total = state.movements.length;

    const doneCount = d.done.filter(Boolean).length;

    const dateObj = new Date(key + "T00:00:00");

    const repsEach = repsForDate(dateObj);

    const reps = d.done.reduce((acc, isDone)=> acc + (isDone ? repsEach : 0), 0);

    return { total, doneCount, reps, repsEach };

  }

  // Monday-based week start

  function startOfWeek(dateObj){

    const d = new Date(dateObj);

    const day = d.getDay(); // 0 Sun, 1 Mon...

    const diff = (day === 0) ? -6 : (1 - day);

    d.setDate(d.getDate() + diff);

    d.setHours(0,0,0,0);

    return d;

  }

  function repsTotalForRange(startDateObj, endDateObj){

    let total = 0;

    for (let d = new Date(startDateObj); d <= endDateObj; d.setDate(d.getDate()+1)){

      const key = ymd(d);

      total += daySummary(key).reps;

    }

    return total;

  }

  function totalYearTarget(year){

    // total possible reps in that year based on schedule + movement count

    let total = 0;

    for (let m = 1; m <= 12; m++){

      const daysInMonth = new Date(year, m, 0).getDate();

      const repsEach = (m <= 6) ? 50 : 100;

      total += daysInMonth * repsEach * state.movements.length;

    }

    return total;

  }

  function bestWeekEver(){

    let best = 0;

    const keys = Object.keys(state.days);

    if (!keys.length) return 0;

    keys.sort();

    for (const k of keys){

      const d = new Date(k + "T00:00:00");

      const ws = startOfWeek(d);

      const we = new Date(ws); we.setDate(ws.getDate()+6);

      const total = repsTotalForRange(ws, we);

      if (total > best) best = total;

    }

    return best;

  }

  function withinChallenge(key){

    const start = new Date(state.startDate + "T00:00:00");

    const end = new Date(start);

    end.setMonth(end.getMonth() + Number(state.durationMonths||12));

    const d = new Date(key + "T00:00:00");

    return d >= start && d < end;

  }

  function calcStreak(){

    // streak counts consecutive days ending today where ALL movements are done

    let streak = 0;

    let d = new Date();

    while (true){

      const key = ymd(d);

      if (!withinChallenge(key)) break;

      const sum = daySummary(key);

      if (sum.total > 0 && sum.doneCount === sum.total) streak++;

      else break;

      d.setDate(d.getDate()-1);

    }

    return streak;

  }

  function pctForRange(keys){

    let total = 0, done = 0;

    for (const k of keys){

      const sum = daySummary(k);

      total += sum.total;

      done += sum.doneCount;

    }

    return total ? Math.round((done/total)*100) : 0;

  }

  function setView(name){

    document.querySelectorAll(".tab").forEach(b=>{

      b.classList.toggle("active", b.dataset.view === name);

    });

    ["today","calendar","rules","settings"].forEach(v=>{

      $("view-"+v).classList.toggle("hide", v !== name);

    });

    if (name === "settings") renderSettings();

    if (name === "calendar") renderCalendar();

  }

  function renderHeader(){

    $("dateLabel").textContent = niceDate(current);

    $("dayLabel").textContent = ymd(current);

  }

  function renderSeason(){

    const s = seasonLabel(current);

    $("seasonTitle").textContent = s.title;

    $("seasonMeta").textContent = s.meta;

    $("seasonReps").textContent = repsForDate(current);

  }

  function renderTotals(){

    const today = new Date(current);

    const weekStart = startOfWeek(today);

    const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate()+6);

    const monthStart = new Date(today.getFullYear(), today.getMonth(), 1);

    const monthEnd = new Date(today.getFullYear(), today.getMonth()+1, 0);

    const yearStart = new Date(today.getFullYear(), 0, 1);

    const yearEnd = new Date(today.getFullYear(), 11, 31);

    $("totWeek").textContent = repsTotalForRange(weekStart, weekEnd).toLocaleString();

    $("totMonth").textContent = repsTotalForRange(monthStart, monthEnd).toLocaleString();

    $("totYear").textContent = repsTotalForRange(yearStart, yearEnd).toLocaleString();

  }

  function renderYearProgress(){

    const year = current.getFullYear();

    const yearStart = new Date(year,0,1);

    const yearEnd = new Date(year,11,31);

    const done = repsTotalForRange(yearStart, yearEnd);

    const target = totalYearTarget(year);

    const pct = target ? Math.min(100, Math.round((done/target)*100)) : 0;

    $("yearReps").textContent = done.toLocaleString();

    $("yearBar").style.width = pct + "%";

  }

  function renderBestWeek(){

    $("bestWeek").textContent = bestWeekEver().toLocaleString();

  }

  function renderToday(){

    const key = ymd(current);

    ensureDay(key);

    const d = state.days[key];

    const tbody = $("todayTable").querySelector("tbody");

    tbody.innerHTML = "";

    const repsEach = repsForDate(current);

    state.movements.forEach((mv, i) => {

      const tr = document.createElement("tr");

      const td0 = document.createElement("td");

      const cb = document.createElement("input");

      cb.type = "checkbox";

      cb.className = "chk";

      cb.checked = !!d.done[i];

      cb.addEventListener("change", () => {

        d.done[i] = cb.checked;

        save();

        renderAll();

      });

      td0.appendChild(cb);

      const td1 = document.createElement("td");

      td1.innerHTML = `<div style="font-weight:700">${mv.name}</div><div class="muted" style="font-size:12px">Daily</div>`;

      const td2 = document.createElement("td");

      td2.innerHTML = `<div style="font-weight:800;font-size:16px">${repsEach}</div><div class="muted" style="font-size:12px">reps</div>`;

      const td3 = document.createElement("td");

      const note = document.createElement("input");

      note.placeholder = "Swap / notes (e.g., wall sit, incline press-ups, etc.)";

      note.value = d.notes[i] || "";

      note.style.width = "100%";

      note.addEventListener("input", () => {

        d.notes[i] = note.value;

        save();

      });

      td3.appendChild(note);

      tr.appendChild(td0); tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3);

      tbody.appendChild(tr);

    });

    const sum = daySummary(key);

    $("kpiDone").textContent = sum.doneCount;

    $("kpiTotal").textContent = sum.total;

    $("kpiReps").textContent = sum.reps.toLocaleString();

  }

  function renderCalendar(){

    $("monthLabel").textContent = calMonth.toLocaleDateString(undefined, { year:"numeric", month:"long" });

    const grid = $("calendarGrid");

    grid.innerHTML = "";

    const first = new Date(calMonth.getFullYear(), calMonth.getMonth(), 1);

    const startDay = first.getDay(); // 0 Sun

    const start = new Date(first);

    start.setDate(first.getDate() - startDay);

    const dayNames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];

    dayNames.forEach(n=>{

      const el = document.createElement("div");

      el.className = "muted";

      el.style.fontSize = "12px";

      el.style.padding = "0 4px";

      el.textContent = n;

      grid.appendChild(el);

    });

    for (let i=0; i<42; i++){

      const d = new Date(start);

      d.setDate(start.getDate() + i);

      const key = ymd(d);

      const sum = daySummary(key);

      let color = "rgba(248,81,73,.20)", border = "rgba(248,81,73,.35)";

      if (sum.doneCount === 0) { }

      else if (sum.doneCount < sum.total) { color = "rgba(210,153,34,.20)"; border="rgba(210,153,34,.45)"; }

      else { color = "rgba(63,185,80,.20)"; border="rgba(63,185,80,.45)"; }

      const cell = document.createElement("button");

      cell.style.padding = "10px 8px";

      cell.style.borderRadius = "14px";

      cell.style.border = `1px solid ${border}`;

      cell.style.background = color;

      cell.style.textAlign = "left";

      cell.style.minHeight = "64px";

      const inMonth = d.getMonth() === calMonth.getMonth();

      cell.style.opacity = inMonth ? "1" : ".45";

      const isToday = key === ymd(new Date());

      const isCurrent = key === ymd(current);

      cell.innerHTML = `

        <div style="display:flex;justify-content:space-between;align-items:center;gap:8px;">

          <div style="font-weight:800">${d.getDate()}</div>

          <div class="muted" style="font-size:11px">${sum.doneCount}/${sum.total}</div>

        </div>

        <div class="muted" style="font-size:11px;margin-top:6px">${sum.reps.toLocaleString()} reps</div>

      `;

      if (isToday) cell.style.boxShadow = "0 0 0 2px rgba(88,166,255,.35) inset";

      if (isCurrent) cell.style.outline = "2px solid rgba(255,255,255,.18)";

      cell.addEventListener("click", () => {

        current = new Date(d);

        setView("today");

        renderAll();

      });

      grid.appendChild(cell);

    }

  }

  function renderStats(){

    $("streak").textContent = calcStreak();

    // Month %

    const now = new Date();

    const first = new Date(now.getFullYear(), now.getMonth(), 1);

    const last = new Date(now.getFullYear(), now.getMonth()+1, 0);

    const keys = [];

    for (let d=new Date(first); d<=last; d.setDate(d.getDate()+1)){

      const k = ymd(d);

      if (withinChallenge(k)) keys.push(k);

    }

    $("monthPct").textContent = `${pctForRange(keys)}%`;

    // All time %

    const start = new Date(state.startDate + "T00:00:00");

    const end = new Date(start);

    end.setMonth(end.getMonth() + Number(state.durationMonths||12));

    const allKeys = [];

    for (let d=new Date(start); d<end; d.setDate(d.getDate()+1)){

      allKeys.push(ymd(d));

    }

    $("allPct").textContent = `${pctForRange(allKeys)}%`;

  }

  function renderSettings(){

    $("startDate").value = state.startDate;

    $("durationMonths").value = state.durationMonths;

    const list = $("movesList");

    list.innerHTML = "";

    state.movements.forEach((mv, idx) => {

      const wrap = document.createElement("div");

      wrap.style.display = "grid";

      wrap.style.gridTemplateColumns = "1fr auto";

      wrap.style.gap = "10px";

      wrap.style.marginBottom = "10px";

      const name = document.createElement("input");

      name.value = mv.name;

      name.placeholder = "Movement name";

      name.addEventListener("input", () => {

        mv.name = name.value;

        save();

        renderAll();

      });

      const del = document.createElement("button");

      del.textContent = "Remove";

      del.className = "small";

      del.addEventListener("click", () => {

        state.movements.splice(idx, 1);

        Object.keys(state.days).forEach(k=>{

          const d = state.days[k];

          if (d){

            d.done.splice(idx,1);

            d.notes.splice(idx,1);

          }

        });

        save();

        renderAll();

      });

      wrap.appendChild(name);

      wrap.appendChild(del);

      list.appendChild(wrap);

    });

  }

  function renderAll(){

    renderHeader();

    renderSeason();

    renderTotals();

    renderYearProgress();

    renderBestWeek();

    renderToday();

    renderStats();

    renderCalendar();

  }

  // Tabs

  document.querySelectorAll(".tab").forEach(btn=>{

    btn.addEventListener("click", ()=> setView(btn.dataset.view));

  });

  // Day nav

  $("prevDay").addEventListener("click", ()=>{ current.setDate(current.getDate()-1); renderAll(); });

  $("nextDay").addEventListener("click", ()=>{ current.setDate(current.getDate()+1); renderAll(); });

  $("goToday").addEventListener("click", ()=>{ current = new Date(); renderAll(); });

  // Month nav

  $("prevMonth").addEventListener("click", ()=>{ calMonth = new Date(calMonth.getFullYear(), calMonth.getMonth()-1, 1); renderCalendar(); });

  $("nextMonth").addEventListener("click", ()=>{ calMonth = new Date(calMonth.getFullYear(), calMonth.getMonth()+1, 1); renderCalendar(); });

  // Actions

  $("markAll").addEventListener("click", ()=>{

    const key = ymd(current);

    ensureDay(key);

    state.days[key].done = state.movements.map(()=>true);

    save(); renderAll();

  });

  $("clearDay").addEventListener("click", ()=>{

    const key = ymd(current);

    ensureDay(key);

    state.days[key].done = state.movements.map(()=>false);

    state.days[key].notes = state.movements.map(()=> "");

    save(); renderAll();

  });

  // Settings changes

  $("startDate").addEventListener("change", ()=>{

    state.startDate = $("startDate").value || state.startDate;

    save(); renderAll();

  });

  $("durationMonths").addEventListener("change", ()=>{

    state.durationMonths = Number($("durationMonths").value || 12);

    save(); renderAll();

  });

  $("addMove").addEventListener("click", ()=>{

    state.movements.push({ name:"New movement" });

    Object.keys(state.days).forEach(k=>{

      ensureDay(k);

      state.days[k].done.push(false);

      state.days[k].notes.push("");

    });

    save(); renderAll(); setView("settings");

  });

  $("resetDefaults").addEventListener("click", ()=>{

    state.movements = structuredClone(DEFAULTS.movements);

    Object.keys(state.days).forEach(k=> ensureDay(k));

    save(); renderAll(); setView("settings");

  });

  // Export/Import/Wipe

  $("btnExport").addEventListener("click", ()=>{

    const blob = { version: "final", exportedAt: new Date().toISOString(), state };

    $("exportBox").value = btoa(unescape(encodeURIComponent(JSON.stringify(blob))));

  });

  $("btnImport").addEventListener("click", ()=>{

    try{

      const raw = $("exportBox").value.trim();

      if (!raw) return alert("Paste an export first.");

      const json = decodeURIComponent(escape(atob(raw)));

      const blob = JSON.parse(json);

      if (!blob.state) return alert("That export doesn't look right.");

      state = blob.state;

      save();

      current = new Date();

      calMonth = new Date(current.getFullYear(), current.getMonth(), 1);

      renderAll();

      alert("Imported ✅");

    }catch(e){

      alert("Import failed. Make sure you pasted the full export text.");

    }

  });

  $("btnWipe").addEventListener("click", ()=>{

    if (!confirm("Wipe all progress saved on THIS device?")) return;

    localStorage.removeItem(LS_KEY);

    state = load();

    save();

    current = new Date();

    calMonth = new Date(current.getFullYear(), current.getMonth(), 1);

    renderAll();

  });

  // Init

  save();

  renderAll();

</script>

</body>

</html>