# AP CS Curriculum Site — System Reference (v2)

**Purpose of this document:** This is the canonical technical reference for Erik Wiessmann's AP CS curriculum website (AP CSA, AP CSP, AP Cybersecurity — Academy at Palumbo). It is written to be handed directly to an AI assistant in a fresh conversation so that assistant can understand the entire system, its current content, and open work without needing prior context. It supersedes any earlier version of this document.

If you are an AI reading this for the first time: read this whole document before making any changes. The system has real architectural conventions (especially around how widgets are authored via spreadsheet rows) that are easy to violate by guessing, and real content already exists that shouldn't be silently overwritten.

---

## 1. The Two Visual Themes

| Theme | Used by | Look |
|---|---|---|
| **Dark / terminal** (`css/core.css`) | `day.html`, `test.html` | Near-black background, JetBrains Mono font, amber (`#E8A33D`) + teal (`#4FD1C5`) accents. This is where students actually do lesson work. |
| **Light / friendly** (self-contained `<style>` in each file) | `index.html`, `policies.html`, `ask-a-question.html`, `guide.html` | Soft lavender background (`#F6F4FC`), Fredoka font for headings, warm orange (`#F5A623`) accent, white rounded cards. |

**Rule of thumb:** if a page is where a student clicks in to *do* the day's work, it's dark. If it's a landing/reference/utility page around that work, it's light.

---

## 2. File Structure

```
/ (root)
├── index.html              — the course hub (light theme)
├── day.html                — the actual lesson page, spreadsheet-driven (dark theme)
├── policies.html            — course policies, rules pulled live from spreadsheet (light theme)
├── ask-a-question.html     — student help-queue / quick-questions page (light theme)
├── test.html                — internal widget catalog for testing (dark theme) — never linked from the live site
├── guide.html                — public documentation page (light theme), linked from index.html's Export section
├── js/                       — all widget + shared logic (22 files)
├── css/                      — all widget + shared styles (17 files)
└── documents/                — reference docs, not part of the live site itself
    ├── SYSTEM_REFERENCE.md   — this file
    ├── README.md
    ├── SETUP.md
    ├── PROJECT_KICKOFF.md
    └── Code.gs               — the actual backend script (copy; the deployed one lives in the Sheet's Apps Script editor)
```

`js/` files: `bits-visualizer.js`, `certificate.js`, `cipher-tool.js`, `core.js`, `day-loader.js`, `debug-challenge.js`, `ek-cards.js` (legacy, unused by `day.html`), `firewall-builder.js`, `hero.js`, `instruction.js`, `parsons.js`, `quiz.js`, `risk-matrix.js`, `rsa-tool.js`, `scenario.js`, `sheet-content.js`, `stepper.js`, `story.js`, `student-id.js`, `terminal.js`, `vocab.js`.

`css/` files: same widget names, one `.css` per widget, plus `core.css`. `hero.js` has no separate CSS file — its styles live in `core.css`.

---

## 3. How `day.html` Builds a Page

`day.html` has almost no fixed content — `js/day-loader.js` builds the entire page dynamically from spreadsheet data at load time.

### 3.1 Load sequence
1. Reads `?course=` and `?day=` from the URL.
2. Shows a full-screen loading overlay immediately.
3. Fetches the pacing tab, the daily-content tab, and (if needed) the terminal-steps tab, in parallel.
4. Finds the one pacing row matching **both** course and day (the pacing tab covers all 3 courses now — see §5.1).
5. Builds the hero (title, compact LO/EK reference lines, day/date pills).
6. Builds every widget for that day **in the exact spreadsheet row order** — reordering means reordering rows, not touching code.
7. Builds the certificate, tracking every trackable widget.
8. Fades out the loading overlay; four corner-flourish decorations draw in.

### 3.2 Pacing lookup (course + day together)
```js
const pacingRow = pacingRows.find(r => String(r.day) === String(day) && courseKeyMatches_(r.course, course));
```
`courseKeyMatches_()` normalizes both sides (lowercase, strip whitespace) so `"ap csa"`, `"AP CSA"`, `"apcsa"` all match the internal key `apcsa`. Necessary because day numbers repeat across the 3 courses in the merged pacing tab.

### 3.3 Row-order rendering
- Reorder widgets by reordering spreadsheet rows — there is no ordering column.
- The same widget type can repeat on the same day; each instance gets its own container id (`quiz`, `quiz-2`, ...) and certificate entry.
- Widgets that don't self-title (`terminal`, `risk`, `cipher`, `rsa`, `firewall`, `rule`) get a page-supplied heading. Self-titling widgets (`story`, `instruction`, `vocab`, `scenario`, `quiz`, `bits`) get a bare container.
- Empty containers (unrecognized widget name, or a standing widget whose URL isn't configured) get their whole wrapper removed, not left as an empty box.

### 3.4 The `after_certificate` exception
An `instruction` row with `field_d` set to exactly `after_certificate` renders in a separate slot genuinely *after* the certificate, regardless of its row position — the mechanism for "here's exactly what to do with the rest of class." Checked **only** for `widgetname === 'instruction'`.

### 3.5 Missing content
No daily-content rows for an existing pacing day → "Content Coming Soon." No pacing row at all → "No day scheduled here yet."

---

## 4. The Widget Library (16 types)

Every widget is one row: `course | day | widgetname | field_a | field_b | field_c | field_d | notes`. Correct answers are always listed **first** in the sheet — the widget shuffles display order itself. `;;` separates list items; `::` separates sub-fields within one item.

**A convention established this session, applied wherever a day has a real hands-on task**: that task's `instruction` row's title (`field_b`) is literally **"Today's Task"**, with `field_a` (the tag) describing what the task actually is (e.g. `"Build: About Me (ASCII)"`). Use this consistently for any new day-with-a-task content.

| Widget | Tracked? | Fields |
|---|---|---|
| `story` | No | `field_a`=paragraphs (`;;`), `field_d`=tab label |
| `instruction` | No | `field_a`=tag, `field_b`=title, `field_c`=paragraphs (HTML allowed — bold, links, video `<iframe>` embeds render responsively), `field_d`=`after_certificate` flag (optional) |
| `vocab` | Yes | `field_a`=terms (`;;`), each `term::emoji::definition` |
| `scenario` | Yes | `field_a`=brief, `field_b`=question, `field_c`=choices (`;;`), each `text::feedback::resolution`, best first |
| `quiz` | Yes | `field_a`=heading, `field_b`=questions (`;;`) in 3 formats (legacy plain MC, `mc::...::retry` for silent-retry, `fill::...` for fill-in-blank), `field_c`=`sequential` for one-at-a-time "Silent Teacher" mode with slide transitions |
| `terminal` | Yes | `field_a`=step IDs (`;;`, from terminal-steps tab), `field_b`=filesystem template |
| `bits` | Yes | No fields — student toggles real header bits for width/height/color depth |
| `risk` | Yes | `field_a`=scenarios (`;;`), each `text::likelihood::impact::feedback` |
| `cipher` | Yes | No fields — simple inclusion |
| `rsa` | Yes | `field_a`=optional custom prime list (default `11,13,17,19,23,29`) |
| `firewall` | Yes | `field_a`=goal, `field_b`=rules (`;;` `src::port::action`), `field_c`=tests (`;;` `description::expectedAction`) |
| `rule` (Class Rules) | No | Flag row only — no fields. Text always pulled live from the rules tab. Per-day opt-in. |
| `message` (General Messages) | No | **Not a row at all** — standing feature, shows automatically on every day, but only when the messages tab has real content (invisible when empty, unlike `rule`). |
| `stepper` (Trace/Predict) | Yes | `field_a`=language, `field_b`=code lines, `field_c`=steps (`lineIndex::key=val|...::note`), `field_d`=`auto` or `quiz::key=opt1,opt2;...`. One language per row. |
| `parsons` | Yes | `field_a`=goal, `field_b`=`functionSignature::successOutput`, `field_c`=chunks (`id::text`), `field_d`=template (`F::text::indent` / `S::correctChunkId::indent`). One language per row. |
| `debug` | Yes | `field_a`=goal, `field_b`=code lines, `field_c`=`bugLineIndex::correctFix::wrongFix1::wrongFix2`, `field_d`=language. One language per row. |

**Removed widget**: `hunt` (Find & Fix) was built, used, then deliberately deleted. Files no longer exist; old rows with `widgetname=hunt` are safely ignored.

---

## 5. Spreadsheet Tabs — CURRENT STATE (post-consolidation)

### 5.1 Public tabs (published to web, read by the website via TSV)

| Tab | Read by | Columns | Status |
|---|---|---|---|
| **pacing timeline** | `index.html`, `day.html` | `course, day, date, weekday, term, unit, topic_activity, day_type, learning_objective, essential_knowledge, mapping_note, possible_lab_project` (+ Cyber-only `scenario, hands_on_activity, hands_on_note`) | **MERGED — live.** All 3 courses in one tab, `course` = `"ap csa"`/`"ap csp"`/`"ap cyber"` (case/space-insensitive matching). Real URL wired into both `PACING_TSV_URL` (day.html) and `SHEETS.pacing` (index.html) — no longer placeholders. `possible_lab_project`/Cyber-only columns exist but aren't wired into any widget — the `after_certificate` instruction mechanism is the current answer to that need. |
| **vocabulary** (+ cheat sheets) | `index.html` | `type, course, category, term, description, example` | **MERGED — live.** `type` = `vocab` or `cheat...` (matched via `startsWith('cheat')`). `course` supports `;`-separated multi-course rows for BOTH types now. Real URL wired into `SHEETS.vocab_cheats`. **Known content bug in the live data**: some Markdown cheat rows have a blank `course` column — blank does NOT mean "show everywhere," it matches nothing, so those rows currently show for no course. Also: one row has a blank `term`, two rows show literal `#ERROR!` text. Not code bugs — need fixing in the sheet. |
| **daily content** | `day.html` | `course, day, widgetname, field_a, field_b, field_c, field_d, notes` | Master content tab. `day = -1` reserved for test data. |
| **frqs** | `index.html` | `course, unit, question, excellent student response, weak student response` | Considered for merging with Quiz Questions, **decided against** — column shapes diverge too much (5 vs. up to 11 columns). Header must say exactly `course`. |
| **quiz questions** | `index.html` | `course, unit, question, option_a...option_h` | Standalone MCQ tab, distinct from the daily-content `quiz` widget. `option_a` always correct; 3 of up to 7 wrong options chosen at random per attempt. |
| **resources** | `index.html` | name/description/url | |
| **terminal-steps** | `day.html` | `step_id, label, command, arg, require_arg_match, require_cwd, notes, fs_template` | ~77 steps, 8 filesystem templates. Never set `require_cwd` on a `cd` step (it logs post-move cwd). |
| **classroom rules** | `day.html`, `policies.html` | One column: `rule` | Live, correct header. |

### 5.2 Functionally-private tabs (read only by `Code.gs` via the Sheets API, NOT a published TSV — even though they may sit visually among the public tabs in the sheet's tab bar)

| Tab | Columns | Notes |
|---|---|---|
| **rank_titles** | `course, title, stars_to_get_title` | `course` uses the plain key with no spaces (`apcsa`/`apcsp`/`apcyber`) — intentionally different from the pacing tab's `"ap csa"` style, matches `Code.gs`'s exact string comparison. |
| **bulletquestions** | `course, question` | Powers Ask-a-Question's Quick Logistics buttons. **Known live bug**: one row has `"ap apcyber"` (stray space) instead of `"apcyber"` — silently breaks 2 Cybersecurity quick-questions. Needs a manual cell fix. |
| **help_queue, roster, star_log, star_totals** | — | Live operational student data. Never publish these. |

**Merging rank_titles + bulletquestions was discussed and deferred** — feasible (similar per-course-list shape) but requires editing the live `Code.gs` backend, a higher-stakes change than the frontend-only merges above. Not done yet, not rejected either.

---

## 6. `ask-a-question.html`

Backed by `Code.gs`, deployed separately as an Apps Script Web App (`API_URL` constant at the top of the file). Not a TSV page.

- **Quick Logistics** — instant, no queue, no credit; from `bulletquestions`.
- **Need Help** mode — joins the visible queue (name shown, not the question).
- **Verbal Question** mode — logs an already-asked-out-loud question, for credit, no queue.
- Auto-fills the student's name from `localStorage` if already confirmed elsewhere on the site.

---

## 7. Hero, Certificate, Idle Behavior

- Hero shows compact, low-emphasis bordered **Learning Objective:** / **Essential Knowledge:** lines from the pacing row — deliberately not prose, students skim past them.
- `ek-cards.js`/`.css` still exist but aren't used by `day.html` anymore (legacy, kept for `test.html`).
- Certificate auto-fills the student's name from the confirmed student-ID lookup.
- Idle timer: 5 min toast → 8 min red pulse → 10 min calm gray fog (timer keeps counting throughout; only the visual de-escalates). Fully suppressed once the certificate is complete.
- Loading overlay: present immediately on parse, removed only once `buildDayPage()` resolves.
- Corner flourish: 4 HUD-style decorations, self-draw only after the loading overlay lifts.

---

## 8. Real-World Curriculum Content — Current State

### 8.1 What actually exists
Real widget content exists for **days 1-5, all 3 courses** (`week1_all_courses_final.tsv`, 94 rows — this is the authoritative, validated version; supersedes any earlier `daily_content_week1.tsv`). The full-year pacing guides exist for all 3 courses (~173 days each), but days 6+ have no daily-content rows yet — ongoing work, not a bug.

### 8.2 Real signup info baked into Day 1 for all 3 courses
- **Codio signup** (same link for every course): `https://codio.com/p/signup?courseToken=import-polygon-alfonso-book`
- **AP Classroom codes**: CSA `96JAY6` · CSP (per period) Period 02 `RN4GDQ`, Period 06 `JAQQXZ`, Period 07 `3QPPM3` · Cyber `GE9W77`

### 8.3 Each course's Week 1 shape
- **CSA** — Java, terminal-based. Real builds: Hello World lab (day 2, real external URL), ASCII-art About Me (day 3), ASCII seating chart via `println` (day 4-5). Genuinely complete and approved by the teacher — treat as stable, don't rework without explicit instruction.
- **CSP** — HTML/CSS, Codio-based. Real builds: About Me page (day 3), Seating Chart page (day 5) — both use the actual template files described in §8.4. Days 2 and 4 currently have **no** "Today's Task" (day 2 is a Big Ideas overview, day 4 is an academic-integrity discussion) — this was flagged to the teacher and intentionally left open pending his decision on whether to invent tasks for those two days.
- **Cyber** — no Codio needed for week 1, so real tasks start day 1. Day 1: real local-IP network-mapping walk-around (see §8.5 for exactly why this activity is built the way it is). Day 2: finish-the-map checkpoint. Day 3: Digital Footprint slide build (uses the real shared deck, §8.6). Day 4: present slides to class. Day 5: submit map photo (slide already lives in the shared deck, nothing separate to submit for that part).

### 8.4 Template files built this session (all tested, all delivered)
- **CSP About Me page** (`index.html` + `style.css`) — Palumbo teal/silver/black theme (real school colors, confirmed via search: teal/silver/black, Griffin mascot), external stylesheet with CSS custom properties at the top so colors are easy to reskin in one place, actual school logo linked live from `palumbo.philasd.org`, photo degrades gracefully to a plain circle if `img/face.png` is missing rather than a broken-image icon.
- **CSP Seating Chart page** (`index.html` + `style.css`) — built to the teacher's **exact real classroom geometry**: 4 columns, 35 total seats (5 wall + 9 + 9 center-facing-each-other + 12 window), wall column starts 2 rows down from the top, both center columns start at the top, real aisle gaps (separate spacer grid columns, not just wider uniform spacing) between wall↔center and center↔window (but not between the two center columns, since they're a facing pair), entrance marked with a left-pointing arrow under the wall column, teacher's table spanning the two center columns at the bottom. Every seat is directly `contenteditable`. The CSS variable controlling seat size/spacing (`--seat-size`, `--seat-gap`) is the one the day 5 instructions point students to find and tweak.
- **Cyber Digital Footprint slide template** (`digital-footprint-template.pptx`) — one slide, 4 prompt cards (icon + question + one-line hint + dashed answer line), all format-constrained to a number or a single letter, a `Name:` field at the top. Built with `pptxgenjs`, passed structural validation and visual QA (an early draft had a real overlap bug — a 2-line hint colliding with its answer line — caught and fixed before delivery).

### 8.5 Why the IP walk-around uses local device IPs specifically
Explicitly **not** phone IPs (cellular carriers use CGNAT — many phones share one public IP, which would break the "map who's where" premise) and **not** Codio/cyber-range IPs (real, but a cloud container's IP has no relationship to physical seat position). Real local IP via each student's own Chromebook: Quick Settings → click the network → click it again (or the gear icon) → **Network** section shows **IP Address**. No terminal required, though ChromeOS does have one at Ctrl+Alt+T (`ipconfig` works there too) if wanted for atmosphere. Worth testing on one device before building a whole lesson around it, in case district device-management policy hides that settings page.

### 8.6 Digital Footprint — the real shared deck
Students don't each create a separate file — they duplicate a template slide inside one shared, real Google Slides deck the teacher already created:
`https://docs.google.com/presentation/d/1GK-qc_PPLsn4Eq3oXwBymE7p-wemTSpRz3p61Sdo0p8/edit?usp=sharing`
This is wired into the actual Day 3 instruction row (wrapped in a proper `<a>` tag). Because it's one shared deck, Day 5's submission instruction was corrected to *not* ask for "the link to your slide" (there isn't a separate one) — it just asks the student to confirm their slide is present in the shared deck.

---

## 9. Known Pending Items
1. `GENERAL_MESSAGES_TSV_URL` in `day.html` — still a placeholder. Needs the messages tab created (`date`/`message` columns), published, URL sent over.
2. `"ap apcyber"` typo in `bulletquestions` — breaks 2 Cybersecurity quick-questions live.
3. Vocabulary/cheat sheet content bugs (§5.1): blank-course Markdown rows, one blank `term`, two `#ERROR!` cells.
4. CSP days 2 and 4 have no "Today's Task" — open question for the teacher, not yet resolved.
5. Real content for days 6+, all 3 courses — ongoing, expected work, not a gap in the system.

## 10. Known, Accepted Limitations
- `stepper`/`parsons`/`debug` each support one language per spreadsheet row (multi-language only exists in hand-authored JS configs, e.g. `test.html`).
- No open-ended "write real code, see it render live" widget exists by design — for that, linking to an external tool (Codio, the Range, etc.) alongside a short "lecture" page is the intended pattern, not something to build into this system.
- The teacher's pacing philosophy: the 5 spreadsheet "days" per week don't map 1:1 to class periods — he moves through them at whatever pace feels natural. What matters is the whole week's content roughly matching total weekly class time, not each individual day filling one period evenly.
