{
  "spec": "quick-html/v0",
  "id": "race-training-calendar",
  "version": "0.1.0",
  "name": "Race Training Calendar",
  "description": "Print-and-pin training calendar for a race build-up: A4 landscape, a dashboard page with pace zones and week rhythm, a day-by-day calendar with tick boxes and write-in km/pace fields, and an optional strength log. Marathon, half, 10K, triathlon run block.",
  "useWhen": [
    "marathon training plan",
    "training calendar",
    "race build-up",
    "half marathon plan",
    "running schedule to print",
    "hardloopschema",
    "trainingsschema",
    "10k plan",
    "wall planner"
  ],
  "category": "printable",
  "language": "en",
  "page": {
    "size": "A4",
    "orientation": "landscape"
  },
  "modules": [
    "edit-overlay",
    "print-helper"
  ],
  "slots": [
    {
      "name": "doc-title",
      "type": "text",
      "required": true,
      "example": "Marathon Training Plan — 12 weeks",
      "notes": "Browser tab title; used as the download filename."
    },
    {
      "name": "plan-eyebrow",
      "type": "text",
      "required": true,
      "example": "Training plan · 12 weeks · Mon 10 Aug – Sun 1 Nov 2026",
      "notes": "Length of the block and its first and last day."
    },
    {
      "name": "title-main",
      "type": "text",
      "required": true,
      "example": "Coastal City",
      "notes": "First half of the headline, in ink. Rendered uppercase."
    },
    {
      "name": "title-accent",
      "type": "text",
      "required": true,
      "example": "Marathon",
      "notes": "Second half of the headline, in rust. Rendered uppercase. Keep the two parts short — the headline is one line."
    },
    {
      "name": "stat-1-label",
      "type": "text",
      "required": true,
      "example": "race day"
    },
    {
      "name": "stat-1-value",
      "type": "text",
      "required": true,
      "example": "Sun 1 Nov"
    },
    {
      "name": "stat-2-label",
      "type": "text",
      "required": true,
      "example": "goal time"
    },
    {
      "name": "stat-2-value",
      "type": "text",
      "required": true,
      "example": "4:00:00"
    },
    {
      "name": "stat-3-label",
      "type": "text",
      "required": true,
      "example": "goal pace"
    },
    {
      "name": "stat-3-value",
      "type": "text",
      "required": true,
      "example": "5:41 /km"
    },
    {
      "name": "stat-4-label",
      "type": "text",
      "required": true,
      "example": "volume"
    },
    {
      "name": "stat-4-value",
      "type": "text",
      "required": true,
      "example": "30 → 56 km/wk",
      "notes": "Four header stats, values max ~14 characters or the header wraps."
    },
    {
      "name": "zone-easy-name",
      "type": "text",
      "required": true,
      "example": "easy"
    },
    {
      "name": "zone-easy-range",
      "type": "text",
      "required": true,
      "example": "6:10 – 6:40 /km"
    },
    {
      "name": "zone-easy-use",
      "type": "text",
      "required": true,
      "example": "long runs, recovery · deliberately slow"
    },
    {
      "name": "zone-goal-name",
      "type": "text",
      "required": true,
      "example": "goal pace"
    },
    {
      "name": "zone-goal-range",
      "type": "text",
      "required": true,
      "example": "5:38 – 5:43 /km"
    },
    {
      "name": "zone-goal-use",
      "type": "text",
      "required": true,
      "example": "blocks inside medium and long runs · the core of the plan"
    },
    {
      "name": "zone-quality-name",
      "type": "text",
      "required": true,
      "example": "threshold"
    },
    {
      "name": "zone-quality-range",
      "type": "text",
      "required": true,
      "example": "5:10 – 5:20 /km"
    },
    {
      "name": "zone-quality-use",
      "type": "text",
      "required": true,
      "example": "tempo blocks, comfortably hard"
    },
    {
      "name": "zone-interval-name",
      "type": "text",
      "required": true,
      "example": "interval"
    },
    {
      "name": "zone-interval-range",
      "type": "text",
      "required": true,
      "example": "4:50 – 5:05 /km"
    },
    {
      "name": "zone-interval-use",
      "type": "text",
      "required": true,
      "example": "kilometre repeats",
      "notes": "The four zones are the legend for the coloured bars in the calendar: easy = sage, goal = rust, threshold = amber, interval = dark. Keep that order and meaning."
    },
    {
      "name": "week-rhythm",
      "type": "html",
      "required": true,
      "example": "<dl><dt>Tuesday</dt><dd>quality session: intervals or tempo</dd></dl>",
      "notes": "Keep the dl/dt/dd structure: dt = when, dd = what. 3–5 pairs."
    },
    {
      "name": "rhythm-note",
      "type": "text",
      "required": false,
      "example": "Never strength training the day before the long run.",
      "notes": "One or two sentences of caveats under the rhythm table."
    },
    {
      "name": "key-principle",
      "type": "html",
      "required": true,
      "example": "<p><strong>The goal-pace blocks are the core.</strong> Keep easy kilometres genuinely easy.</p>",
      "notes": "The single most important instruction for this athlete, in the rust callout. One or two short paragraphs."
    },
    {
      "name": "checkpoint-heading",
      "type": "text",
      "required": false,
      "example": "Checkpoint after the test (week 8)",
      "notes": "Inside the checkpoint-block optional."
    },
    {
      "name": "checkpoint",
      "type": "html",
      "required": false,
      "example": "<dl><dt>1:53 – 1:56</dt><dd>hold <strong>4:00 pacing</strong> (5:41 /km)</dd></dl>",
      "notes": "Decision rules after the mid-plan test: dt = the result, dd = what it means for race pacing. Inside the checkpoint-block optional."
    },
    {
      "name": "back-off",
      "type": "html",
      "required": true,
      "example": "<ul><li><strong>Pain that changes how you run:</strong> cut the session, don't push through.</li></ul>",
      "notes": "2–3 li items. Signals that the plan should be scaled back."
    },
    {
      "name": "practical",
      "type": "html",
      "required": true,
      "example": "<ul><li><strong>Race shoes:</strong> at least three long runs in the shoes you plan to race in.</li></ul>",
      "notes": "3–5 li items: fuelling, shoes, travel, returning from injury."
    },
    {
      "name": "away-heading",
      "type": "text",
      "required": false,
      "example": "Away block — pick what fits",
      "notes": "Inside the away-block optional."
    },
    {
      "name": "away-sub",
      "type": "text",
      "required": false,
      "example": "For travel days marked on the calendar: run when it works, by feel.",
      "notes": "Inside the away-block optional."
    },
    {
      "name": "plan-footer",
      "type": "text",
      "required": false,
      "example": "Prepared 14 Aug 2026 · adjust the plan to your life, not the other way around"
    },
    {
      "name": "calendar-heading",
      "type": "text",
      "required": true,
      "example": "Training calendar"
    },
    {
      "name": "calendar-note",
      "type": "text",
      "required": false,
      "example": "Coastal City Marathon · goal 4:00 (5:41 /km)"
    },
    {
      "name": "calendar-footer",
      "type": "text",
      "required": false,
      "example": "Coastal City Marathon · Sunday 1 November 2026"
    },
    {
      "name": "strength-heading",
      "type": "text",
      "required": false,
      "example": "Strength training · weight log",
      "notes": "Inside the strength-log optional."
    },
    {
      "name": "strength-subtitle",
      "type": "text",
      "required": false,
      "example": "1× per week, ~40 min · Tuesday after the run or Wednesday",
      "notes": "Inside the strength-log optional."
    },
    {
      "name": "strength-progression",
      "type": "html",
      "required": false,
      "example": "<p><strong>Progression:</strong> add weight gradually up to the peak week.</p>",
      "notes": "Inside the strength-log optional."
    },
    {
      "name": "strength-focus",
      "type": "html",
      "required": false,
      "example": "<p>Technique before weight: the last repetition should be hard but clean.</p>",
      "notes": "Inside the strength-log optional."
    },
    {
      "name": "strength-footer",
      "type": "text",
      "required": false,
      "example": "Coastal City Marathon · Sunday 1 November 2026",
      "notes": "Inside the strength-log optional."
    }
  ],
  "repeats": [
    {
      "name": "week",
      "description": "One calendar row per training week, in chronological order. Each row has a gutter cell plus seven day cells (Mon–Sun).",
      "min": 4,
      "max": 30,
      "slots": [
        {
          "name": "week-class",
          "type": "text",
          "required": true,
          "example": "gut",
          "notes": "Attribute slot on the gutter cell. Always starts with 'gut'. Add one accent for special weeks: 'gut g-recovery' (recovery/down week), 'gut g-peak' (highest volume), 'gut g-taper', 'gut g-test' (test/time-trial week), 'gut g-event' (travel or event week), 'gut g-race' (race week). Ordinary build weeks are just 'gut'."
        },
        {
          "name": "week-number",
          "type": "number",
          "required": true,
          "example": "1",
          "notes": "The word 'wk' is fixed in the template — put only the number here."
        },
        {
          "name": "week-range",
          "type": "text",
          "required": true,
          "example": "10 – 16 Aug",
          "notes": "Monday to Sunday of that week; add the month on both sides when the week spans two months ('31 Aug – 6 Sep')."
        },
        {
          "name": "week-tag",
          "type": "text",
          "required": false,
          "example": "base",
          "notes": "Short phase label: base, build, recovery, test week, peak week, taper, race week, travel. Leave empty to hide the tag."
        },
        {
          "name": "week-target",
          "type": "text",
          "required": true,
          "example": "target ~30 km",
          "notes": "Planned volume for the week. Compute it as the sum of that week's sessions."
        },
        {
          "name": "mon-class",
          "type": "text",
          "required": true,
          "example": "day rest",
          "notes": "Attribute slot on the Monday cell — see the class vocabulary in agentInstructions. Same for tue/wed/thu/fri/sat/sun."
        },
        {
          "name": "mon-date",
          "type": "text",
          "required": true,
          "example": "10",
          "notes": "Day of the month; add a short month name on the 1st of a month ('1 Sep')."
        },
        {
          "name": "mon-note",
          "type": "text",
          "required": false,
          "example": "",
          "notes": "Small rust flag line above the session: 'long run', 'in race shoes', 'test gels', 'moved from Saturday'. Leave empty and it disappears."
        },
        {
          "name": "mon-session",
          "type": "text",
          "required": true,
          "example": "Rest",
          "notes": "The session itself, telegraphic: '12 km easy', '3 × 3 km at goal pace, 1 km jog (11 km)', 'Rest', 'Strength ~40 min'. Max ~40 characters."
        },
        {
          "name": "mon-pace",
          "type": "text",
          "required": false,
          "example": "",
          "notes": "Target pace for this session, copied from the matching zone. Empty on rest and event days."
        },
        {
          "name": "tue-class",
          "type": "text",
          "required": true,
          "example": "day t-quality"
        },
        {
          "name": "tue-date",
          "type": "text",
          "required": true,
          "example": "11"
        },
        {
          "name": "tue-note",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "tue-session",
          "type": "text",
          "required": true,
          "example": "6 × 2' brisk, 2' jog (7 km)"
        },
        {
          "name": "tue-pace",
          "type": "text",
          "required": false,
          "example": "by feel, brisk"
        },
        {
          "name": "wed-class",
          "type": "text",
          "required": true,
          "example": "day rest"
        },
        {
          "name": "wed-date",
          "type": "text",
          "required": true,
          "example": "12"
        },
        {
          "name": "wed-note",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "wed-session",
          "type": "text",
          "required": true,
          "example": "Strength ~40 min"
        },
        {
          "name": "wed-pace",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "thu-class",
          "type": "text",
          "required": true,
          "example": "day t-easy"
        },
        {
          "name": "thu-date",
          "type": "text",
          "required": true,
          "example": "13"
        },
        {
          "name": "thu-note",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "thu-session",
          "type": "text",
          "required": true,
          "example": "8 km easy"
        },
        {
          "name": "thu-pace",
          "type": "text",
          "required": false,
          "example": "6:10 – 6:40 /km"
        },
        {
          "name": "fri-class",
          "type": "text",
          "required": true,
          "example": "day rest"
        },
        {
          "name": "fri-date",
          "type": "text",
          "required": true,
          "example": "14"
        },
        {
          "name": "fri-note",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "fri-session",
          "type": "text",
          "required": true,
          "example": "Rest"
        },
        {
          "name": "fri-pace",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "sat-class",
          "type": "text",
          "required": true,
          "example": "day t-easy"
        },
        {
          "name": "sat-date",
          "type": "text",
          "required": true,
          "example": "15"
        },
        {
          "name": "sat-note",
          "type": "text",
          "required": false,
          "example": "long run"
        },
        {
          "name": "sat-session",
          "type": "text",
          "required": true,
          "example": "12 km easy"
        },
        {
          "name": "sat-pace",
          "type": "text",
          "required": false,
          "example": "6:10 – 6:40 /km"
        },
        {
          "name": "sun-class",
          "type": "text",
          "required": true,
          "example": "day rest"
        },
        {
          "name": "sun-date",
          "type": "text",
          "required": true,
          "example": "16"
        },
        {
          "name": "sun-note",
          "type": "text",
          "required": false,
          "example": ""
        },
        {
          "name": "sun-session",
          "type": "text",
          "required": true,
          "example": "Rest"
        },
        {
          "name": "sun-pace",
          "type": "text",
          "required": false,
          "example": ""
        }
      ]
    },
    {
      "name": "away-session",
      "description": "One line per optional session in the away/travel block. Inside the away-block optional.",
      "min": 1,
      "max": 6,
      "slots": [
        {
          "name": "away-item",
          "type": "text",
          "required": true,
          "example": "30–40 min easy (5–7 km)",
          "notes": "A session that works without a known route or a track. Keep it time-based."
        }
      ]
    },
    {
      "name": "exercise",
      "description": "One row per strength exercise. Inside the strength-log optional.",
      "min": 3,
      "max": 12,
      "slots": [
        {
          "name": "exercise-name",
          "type": "text",
          "required": true,
          "example": "Goblet squat or Bulgarian split squat"
        },
        {
          "name": "exercise-why",
          "type": "text",
          "required": true,
          "example": "base leg strength",
          "notes": "Three to six words on why a runner does this."
        },
        {
          "name": "exercise-sets",
          "type": "text",
          "required": true,
          "example": "3 × 8–10"
        }
      ]
    }
  ],
  "optionals": [
    {
      "name": "checkpoint-block",
      "description": "Decision rules after a mid-plan test race or time trial. Keep it only if the plan actually contains a test session; remove it otherwise."
    },
    {
      "name": "away-block",
      "description": "Menu of flexible sessions for travel days. Keep it only if the calendar contains 'day ev' travel days; remove it otherwise."
    },
    {
      "name": "strength-log",
      "description": "Final page: strength exercises × 15 week columns to write weights into. Remove it if the plan has no strength work."
    }
  ],
  "agentInstructions": "This is a wall calendar, not a table of text — it is meant to be printed, ticked off with a pen and hung up. Fill it in this order.\n\n1. HEADER. title-main + title-accent together are the race name (rendered uppercase, keep them short). plan-eyebrow states the block length and its first and last day. The four stat pairs are normally race day, goal time, goal pace and volume build (start → peak).\n\n2. PACE ZONES. Derive the four zones from the goal time. The zones are the legend for the calendar's coloured bars, so keep their meaning: easy (sage), goal pace (rust), threshold (amber), interval (dark). Use one unit consistently everywhere — km or miles — including in every session and week target.\n\n3. CALENDAR. Duplicate the 'week' row once per training week, in chronological order (min 4, max 30), and delete every data-example row. Per row: week-number is only the number ('wk' is fixed in the template); week-range is Monday–Sunday; week-target is the planned volume, which you compute as the sum of that week's sessions.\n\nEvery day cell has a class attribute slot ({day}-class) that drives its colour and layout. Use exactly one of:\n  · 'day rest' — rest day or strength-only day. Tick box and write-in boxes are hidden automatically; the text renders small and italic ('Rest', 'Strength ~40 min').\n  · 'day t-easy' — easy / recovery / long easy run (sage bar)\n  · 'day t-quality' — tempo or threshold work (amber bar)\n  · 'day t-interval' — intervals, repeats (dark bar)\n  · 'day t-goal' — goal-pace work, including long runs finishing at goal pace (rust bar)\n  · 'day t-test' — test race or time trial (blue, tinted cell)\n  · 'day ev' — travel, holiday, wedding, festival: a day the plan works around. Put the event name in {day}-session; tick boxes hide automatically.\n  · 'day t-race' — race day itself. Put the race name in {day}-note and '42.2 km · goal 4:00' style content in {day}-session.\nDo not put anything else in these slots and do not invent class names.\n\nKeep {day}-session under ~40 characters so the cell stays readable. {day}-note is the small rust flag above it ('long run', 'in race shoes', 'test gels and drinks', 'moved from Saturday') — leave it empty and it disappears, same for {day}-pace on rest and event days. {day}-date is the day of the month; add a short month name on the first of a month ('1 Sep').\n\nMark special weeks through week-class: 'gut g-recovery', 'gut g-peak', 'gut g-taper', 'gut g-test', 'gut g-event', 'gut g-race'. Ordinary weeks are just 'gut'. week-tag is the matching short label, or empty.\n\n4. OPTIONAL BLOCKS. Remove checkpoint-block if the plan has no mid-plan test; remove away-block if there are no travel days; remove strength-log if there is no strength work. The strength log has 15 fixed week columns — for a longer plan, say so to the user rather than adding columns.\n\n5. SANITY-CHECK THE PLAN before you deliver it: volume builds by roughly 10% per week, a recovery week every third or fourth week, hard days never back to back, the long run never the day after strength work, a real taper in the final two to three weeks, and the race in the last row. If the user's request implies an unsafe ramp, say so instead of printing it silently. This is a training aid, not medical advice — for injury, illness or a first marathon after a long break, suggest they check the plan with a coach or physio.\n\nLANGUAGE: you may translate the fixed labels (weekday headers, 'week', 'logged', legend, panel headings) into the user's language, and should when the user's content is in that language. Never translate the class vocabulary — 'day t-goal', 'gut g-peak' and friends must stay exactly as written.\n\nPAGE 1 PANELS: week-rhythm is a dl (dt = when, dd = what). key-principle is the one thing this athlete must not get wrong. back-off and practical are short ul lists. Keep page 1 to a single sheet: four zones, three columns, roughly 4 + 3 + 4 items.",
  "license": "MIT",
  "authors": [
    {
      "github": "bvrln"
    }
  ]
}
