# Gantt Chart Structure & JSON Schema

## Spreadsheet Layout

| Column | Content |
|--------|---------|
| A | Task name |
| B | Phase label (e.g. "Phase 1") |
| C | Est. Time (e.g. "2.5d", "6h") |
| D | Actual Time (filled in later) |
| E onwards | One column per working day, grouped by month |

- Row 1: Month headers (merged across their days), e.g. "April 2026"
- Row 2: Day numbers (e.g. 20, 21, 22 ...)
- Rows 3+: Task rows — colored cells mark when a task is active
- After tasks (one blank row, then):
  - **"Days planned"** row — per-day count of how many tasks are active that day
    (the resourcing/overlap load). Holiday and break rows are excluded.
  - **"Total estimated effort"** row — sum of all task `est_time` values expressed
    in days, shown in the Est. Time column. Hours are converted to days at
    8 h/day (`HOURS_PER_DAY` in the script). Holiday/break rows contribute nothing.
- At the bottom: a Legend section with color swatches

## Color Scheme (hex fills)

| Category | Hex |
|----------|-----|
| Data Loading | #2d9b8b (Fresh Teal) |
| Data Exploration & Design | #ffe000 (Sunny Yellow) |
| Data Modelling | #a976e5 (Lavender) |
| BI Reporting | #ed5c58 (Tingy Coral) |
| Holidays / Public Holiday | #D9D9D9 (grey) |
| Break / Other | #D9D9D9 (grey) |

> **Note — Gemma brand colours.** These category fills are the official Gemma
> accent/primary colours (see `document-branding/references/brand_guide.md`).
> Combining several accents in one chart is a deliberate data-viz exception to
> the brand's "accents are not mixed" rule, recorded as an `[IMPL]` exception in
> `brand_guide.md`. Keep these values in sync with `COLORS` in `generate_gantt.py`.

## JSON Input Schema

Only `project_name` and `tasks` reach the script. `client` and `start_date` are
context for the extraction step — keep them for traceability, but the script
ignores them.

```json
{
  "project_name": "Infoniqa – Non-Financial KPIs",
  "client": "Infoniqa",
  "start_date": "2026-04-20",
  "tasks": [
    {
      "name": "Data Loading — Redmine",
      "phase": "Phase 1",
      "category": "Data Loading",
      "est_time": "6h",
      "start_date": "2026-04-20",
      "end_date": "2026-04-21"
    },
    {
      "name": "🏖️ Holidays",
      "phase": "Break",
      "category": "Holidays",
      "est_time": "",
      "start_date": "2026-05-01",
      "end_date": "2026-05-01"
    }
  ]
}
```

### category values (controls cell color)

Use one of these strings verbatim. Any other value falls back to a colorless
`#EFEFEF` cell.

- `"Data Loading"`
- `"Data Exploration & Design"`
- `"Data Modelling"`
- `"BI Reporting"`
- `"Holidays"`
- `"Break"`

`"Holidays"` and `"Break"` are the two non-working categories: the script leaves
them out of the "Days planned" load and out of the effort total. Put every real
break row under `"Break"` so it is not counted as planned work.

### est_time format

One number plus `h` or `d`: `"6h"`, `"5d"`, `"2.5d"`. Compound or spelled-out
values (`"2d 4h"`, `"6 hours"`, `"3-5d"`) do not parse — the script warns on
stderr and counts them as 0 in the effort total. Normalise to a single value
during extraction.

### Date format
Always ISO 8601: `YYYY-MM-DD`. Only include working days (Mon–Fri).
