1	<!DOCTYPE html>
2
3
4	    <meta charset="UTF-8">
5	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6	    <title>APEX Professional Portfolio</title>
7	    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
8
242
243
244
245
246

247
248
249
250
251

252	            Dashboard
253

254	                <button class="nav-drop-btn active">Portfolio ▾</button>
255

256	                    S1 · Servant Leadership
257	                    S2
258	                    S3 · Career Exploration
259	                    S4 · Technical Skills
260	                    S5 · Professional Skills
261

262

263

264	                <button class="nav-drop-btn">Playbooks ▾</button>
265

266	                    Video Production
267	                    Social Media
268	                    Marketing Analytics
269	                    Podcast Production
270	                    Graphic Design
271	                    Photography
272

273

274

275

276
277
278
279

280
APEX Professional Portfolio

281
Innovation. Leadership. Professionalism.

282

283
284
285

286

287
🎓

288

289	            <input type="text" class="associate-name" id="userName" placeholder="Associate Name" onblur="saveProfile()">
290	            <input type="text" class="associate-strand" id="userStrand" placeholder="APEX Strand (e.g., Business Startup)" onblur="saveProfile()">
291

292

293
294

295
296

297	                Standard 1
298
Servant Leadership & Client Relationships

299
Documenting community impact, professional communication, and project stewardship.

300

301
In Progress

302
303
304
305

306	                Standard 2
307
Professionalism & Ethics

308
Demonstrating integrity, punctuality, and professional workplace behaviors.

309

310
In Progress

311
312
313
314

315	                Standard 3
316
Career Exploration

317
Researching career paths, building a personal brand, and connecting with professionals in your field.

318

319
In Progress

320
321
322
323

324	                Standard 4
325
Technical Skills

326
Mastering industry-specific tools and platforms relevant to your APEX strand.

327

328
In Progress

329
330
331
332

333	                Standard 5
334
Professional Skills

335
Developing the communication, collaboration, and workplace habits that define a standout professional.

336

337
In Progress

338
339

340
341
342	        © 2026 Waukee APEX Aspiring Professional Portfolio | Built for Excellence
343
344

345
346
Profile Saved ✓

347
348
384
385
400
407
408

<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>APEX Professional Portfolio</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">

            Dashboard

                <button class="nav-drop-btn active">Portfolio ▾</button>

                    S1 · Servant Leadership
                    S2
                    S3 · Career Exploration
                    S4 · Technical Skills
                    S5 · Professional Skills

                <button class="nav-drop-btn">Playbooks ▾</button>

                    Video Production
                    Social Media
                    Marketing Analytics
                    Podcast Production
                    Graphic Design
                    Photography

APEX Professional Portfolio

Innovation. Leadership. Professionalism.

🎓

            <input type="text" class="associate-name" id="userName" placeholder="Associate Name" onblur="saveProfile()">
            <input type="text" class="associate-strand" id="userStrand" placeholder="APEX Strand (e.g., Business Startup)" onblur="saveProfile()">

                Standard 1

Servant Leadership & Client Relationships

Documenting community impact, professional communication, and project stewardship.

In Progress

                Standard 2

Professionalism & Ethics

Demonstrating integrity, punctuality, and professional workplace behaviors.

In Progress

                Standard 3

Career Exploration

Researching career paths, building a personal brand, and connecting with professionals in your field.

In Progress

                Standard 4

Technical Skills

Mastering industry-specific tools and platforms relevant to your APEX strand.

In Progress

                Standard 5

Professional Skills

Developing the communication, collaboration, and workplace habits that define a standout professional.

In Progress

        © 2026 Waukee APEX Aspiring Professional Portfolio | Built for Excellence

Profile Saved ✓


Ignore standard 2 for now. I don't have the content for it but I want to keep it. Let's start by having you fix this stuff.   Project Task Boards and Review Prep are on an old nav. Both pages were ski…


Now I have a complete picture. Let me note a few things:

1. The Portfolio page has `status-${i}` IDs on the standard cards. The Dashboard also has a similar loadProfile function that looks for `status-${i}` but those don't exist on the dashboard (the dashboard's standard cards are in the portfolio page, not the dashboard).

Actually wait - looking at the dashboard script (line 554-563):
```javascript
for (let i = 1; i <= 5; i++) {
    const stdData = localStorage.getItem(`apex_std${i}_state_v6`);
    if (stdData) {
        const badge = document.getElementById(`status-${i}`);
        if (badge) {
            badge.innerText = 'Evidence Logged';
            badge.classList.add('complete');
        }
    }
}
```

The `status-${i}` elements don't exist on the dashboard, so the badge lookup fails silently and nothing happens. The progress badges only live on the Portfolio page, which has the same buggy code trying to update elements that aren't there. So both pages are attempting to update status badges that don't exist in their respective DOM structures.

Looking at the actual storage keys being used, there's a mismatch problem: S3 stores data under `apex_v37` but the code looks for `apex_std3_state_v6`, S5 stores under `apex_std5_v6` but the code queries `apex_std5_state_v6`, and S4 uses `apex_std4_state_v6` which at least aligns with what S4BSU writes. The Portfolio page has duplicate nav dropdown scripts too, so I'll consolidate those.

For the Review Prep page, S3's key mismatch should probably stay as-is since S3 is phase-based rather than card-based, but S5 needs to switch from `apex_std5_state_v6` to `apex_std5_v6` to match what's actually stored, while S4's key is already correct.

The Dashboard's progress badge code is trying to update elements with IDs like `status-0`, `status-1`, etc., but those don't exist on the dashboard itself, so that whole section is just dead code.

Now I'm mapping out the full scope: I need to add dropdown styling and HTML structure to both Review Prep and Task Boards pages, keeping the existing nav layout (logo, title, nav-links) and placing the dropdowns within the nav-links alongside the Copy and Print buttons.

For the S4BSU localStorage issue, I'm consolidating the save logic into a single `saveS4BSUState()` function that gets called from `saveLink()`, `saveReflection()`, and `markDone()`, then updating the page load to read from the unified key with fallback migration from the old individual keys.

On the data side, I need to fix the S5 key in Review Prep from `apex_std5_state_v6` to `apex_std5_v6`, and correct how the S4 format is being read since the current code is trying to access `Object.values()` on the state object.

For the Portfolio page, I need to fix the badge update logic in `loadProfile` and address issues with S3 data loading.

Then there's the bigger refactor: creating a shared Kanban file to standardize S1 and S5, updating S5's naming conventions, and consolidating duplicate dropdown logic. This is substantial work, so I should prioritize carefully and tackle the fixes systematically rather than trying to do everything at once. Building out the dropdown menus... Building dropdown menus for the navigation... I'm checking the Review Prep file to see if there's an active state styling for the navigation links, but it looks like that CSS rule might not be defined there since we're not on the Dashboard page. I found the `.nav-links a.active` rule in Task Boards, so I can use that as an anchor point for the dropdown CSS instead. Review Prep ends properly with closing tags, and Task Boards ends at line 293. Now I'm ready to start making the edits across all files—I'll tackle Review Prep first with its CSS and navigation updates, then move through Task Boards, the S4BSU localStorage refactor, and clean up the Portfolio page's duplicate dropdown scripts.

For S4BSU, I'm mapping out the existing function structure to understand where the localStorage changes need to go—looking at `saveLink`, `bookmarkCard`, `addToQueue`, `toggleCard`, and the other utility functions that handle card state management.

The key refactor is replacing direct localStorage calls in `saveLink()` and `saveReflection()` with centralized state saving, then rewriting `saveStd4State()` entirely to handle the new approach.

Writing the unified state function... Writing the handler functions...

Now I'm working through the initialization logic. I need to load the unified state at startup, then update how the card processing loop reads saved values instead of hitting localStorage directly for each field. After rebuilding all the cards, I'll save the state once at the end.

The key shift is that link restoration currently happens before card rebuild on the original HTML inputs, but since the rebuild creates new inputs, I need to apply the saved link value during that rebuild process. For the other fields like done status and notes, I'll read them from the loaded state object during the card transform instead of making individual localStorage calls. Now I need to add the migration save at the end of initialization and update the card rebuild to restore the saved link value, while removing the old link restoration logic that's no longer needed. I need to locate the specific sections in the `window.onload` function that handle link restoration, state loading, and the final save call. Since the file has been modified with new features, I'll use string matching to find the exact code blocks rather than relying on line numbers, then make the targeted replacements to unify the state management. I need to add `saveS4BSUState()` to the initialization function before loading the queue, and I'm verifying that the `markDone` function already calls it when cards are duplicated, so the state persists correctly. The `bookmarkCard` function pulls text from the card's heading elements.

Now I'm planning the refactoring to consolidate S1 and S5 with `apex-kanban.js`. The main challenge is unifying the functions that differ between them—`saveToStorage` needs to handle state saving and button feedback, `createCardHTML` needs to reference the config's page URL, and `duplicateCard` currently reads different text selectors depending on the page (`.brief-text` for S1, `.instruction-text` for S5), so I'll standardize on `.brief-text` to make it work across both. I also need to ensure `bookmarkCard` and `addToQueue` have access to the config's page URL.

Initializing the kanban system... Writing the card creation logic... Building the card structure...

Now I'm adding a function to duplicate cards by extracting the original card's data and creating a new one with a unique ID, then inserting it right after the source card.

Next, I'm building the logic to add a brand new custom card to a column—it gets a timestamp-based ID and placeholder text, then I'm resetting all the filters back to "all" and clearing the search to show everything fresh.

Still writing card functions... Still writing the bookmark and progress tracking logic... Writing filter logic... Writing queue display logic... Writing initialization logic... I'm noticing a mismatch between how S1 identifies columns and what the shared JS expects—S1 uses `id="col-servant"` but the filter function looks for `data-col` attributes, so I need to add those data attributes to S1's column divs to make everything work together. The `init` function in the shared file uses the data-col selector for placing no-match messages, which will work once I add the `data-col` attributes to S1's columns. I should also refactor the initialization loop to iterate over `config.cols` instead of `Object.keys(state)`, since that's more reliable when the stored state might have different keys—I'll clear each column's container and repopulate it from the state data.

Now I'm ready to start writing the actual code. This is going to be substantial, so I'm prioritizing: first the nav updates, then the localStorage fix for S4BSU, then tackling the portfolio and review prep bugs, and finally the big refactor to consolidate S1 and S5 into a shared kanban module. For that last part, I'll create the new shared file, then simplify both S1 and S5 to use it.

Writing the shared kanban module... Defining the card templates for different categories... Continuing with the brand category badges for the elevator pitch and business casual appearance standards, then wrapping up the data structure. Now I'm setting up the S5 script to initialize the Kanban board with the same column configuration. Now I need to update S1 by adding `data-col` attributes to its four column divs, and for S5 I'll replace all instances of the `instruction-text` class with `brief-text` to match the new naming convention. Both pages also need their old script blocks removed and replaced with the two new script tags. The focus styles need to match too—S1 already has `.brief-text[contenteditable="true"]:focus` while S5 uses `.instruction-text[contenteditable="true"]:focus`, so I'll update S5 to align them. Now I'm ready to execute the plan: first tackle the nav updates for Review Prep and Task Boards, then refactor S4BSU's localStorage handling, and finally fix the Portfolio page's `loadProfile` function. I need to switch to string matching instead of line numbers since the code has shifted with my filter additions. I'm going through S4BSU systematically: adding the constant key, renaming the save function, updating the individual save methods to use the consolidated state function, fixing the restoration logic in window.onload, and consolidating the localStorage reads in the card transform section. I've got a clear list of edits to make across the codebase. Now I need to actually execute these changes rather than keep planning. I'm realizing there's a selector mismatch I need to resolve—S4BSU's applyFilter uses `document.querySelectorAll('.vibe-column')` but I need to verify what selector it uses for the no-match message to ensure consistency.

Looking at S4BSU's existing applyFilter, it's already using `[data-col]` selectors where the attribute gets assigned numerically during initialization. For S1 and S5 after refactoring, I'll use string values like `[data-col="servant"]` and `[data-col="client"]` to match the column names. The shared applyFilter function handles this correctly with `[data-col="${col}"]` where col comes from the cols array.

Time to start writing the actual edit calls methodically.

I should also handle the duplicate dropdown scripts on the Portfolio page when I fix the progress logic, and make sure S1's column divs get the data-col attributes so they work with the refactored applyFilter selector pattern.

Now I'm ready to execute this systematically. I'll work through the changes in order: Task Boards nav, S4BSU localStorage, Portfolio progress, Review Prep S5 key, then create the apex-kanban utility file with all the shared logic.


Now I have the full picture. This is a significant set of changes — let me use a subagent to execute them in parallel while keeping the main context clean.


Agent

Multi-file APEX portfolio cleanup: nav, S4BSU localStorage, progress display, S1/S5 DRY

Prompt:
You're working on an offline APEX portfolio system — standalone HTML/CSS/JS files that use localStorage for persistence. You have 4 concrete tasks to complete. All files are in `/Users/russgoerend/Desktop/Portfolio HTMLs/`.

---

## TASK 1 — Add nav dropdowns to Review Prep and Task Boards

Both pages were missed in a previous nav update. Every other page already has this structure: `Dashboard | Portfolio ▾ | Playbooks ▾` using CSS hover dropdowns and a JS hover timer.

**CSS to add** (after `.nav-links a.active` rule, or `.nav-links a:hover` if no `.active` rule exists):
```css
        /* --- NAV DROPDOWNS --- */
        .nav-dropdown { position: relative; }
        .nav-drop-btn { color: rgba(255,255,255,0.9); background: none; border: none; font-size: 0.8rem; font-weight: 700; padding: 8px 14px; border-radius: 6px; text-transform: uppercase; letter-spacing: 0.03em; transition: all 0.2s; cursor: pointer; font-family: inherit; }
        .nav-dropdown:hover .nav-drop-btn { color: var(--secondary); background: rgba(255,255,255,0.1); }
        .nav-drop-btn.active { background: var(--secondary); color: var(--primary); }
        .nav-dropdown:hover .nav-drop-btn.active { background: var(--secondary); color: var(--primary); opacity: 0.85; }
        .nav-drop-menu { display: none; position: absolute; top: calc(100% + 8px); left: 0; background: white; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); min-width: 210px; z-index: 1001; padding: 6px 0; border: 1px solid #E5E7EB; }
        .nav-dropdown:hover .nav-drop-menu { display: block; }
        .nav-drop-menu a { display: block; padding: 9px 16px; color: #1F2937; text-decoration: none; font-size: 0.82rem; font-weight: 600; transition: background 0.15s; white-space: nowrap; }
        .nav-drop-menu a:hover { background: #F3F4F6; color: var(--primary); }
        .nav-drop-menu a.active { color: var(--primary); font-weight: 800; background: #F5F3FF; }
```

**JS to add** (before `</body>`):
```html
<script>
document.querySelectorAll('.nav-dropdown').forEach(d => {
    let t, m = d.querySelector('.nav-drop-menu');
    d.addEventListener('mouseenter', () => { clearTimeout(t); m.style.display = 'block'; });
    d.addEventListener('mouseleave', () => { t = setTimeout(() => { m.style.display = ''; }, 150); });
});
</script>
```

**`APEX_Review_Prep.html`** — current nav-links HTML is:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <button class="btn-copy" onclick="copyToClipboard()">📋 Copy Text</button>
            <button class="btn-print" onclick="window.print()">🖨 Print / PDF</button>
        </div>
```
The `.nav-links a:hover` rule exists on this page (no `.active` rule). Replace nav-links with:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Portfolio ▾</button>
                <div class="nav-drop-menu">
                    <a href="Standard1.html">S1 · Servant Leadership</a>
                    <a href="Standard2.html">S2</a>
                    <a href="Standard3.html">S3 · Career Exploration</a>
                    <a href="Standard4.html">S4 · Technical Skills</a>
                    <a href="Standard5.html">S5 · Professional Skills</a>
                </div>
            </div>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Playbooks ▾</button>
                <div class="nav-drop-menu">
                    <a href="Video Production Playbook.html">Video Production</a>
                    <a href="Social Media Playbook.html">Social Media</a>
                    <a href="Marketing Analytics Playbook.html">Marketing Analytics</a>
                    <a href="Podcast Production Playbook.html">Podcast Production</a>
                    <a href="Graphic Design Playbook.html">Graphic Design</a>
                    <a href="Photography Playbook.html">Photography</a>
                </div>
            </div>
            <button class="btn-copy" onclick="copyToClipboard()">📋 Copy Text</button>
            <button class="btn-print" onclick="window.print()">🖨 Print / PDF</button>
        </div>
```

**`Project Task Boards.html`** — current nav-links HTML is:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <a href="APEX_Professional_Portfolio.html">Portfolio</a>
            <a href="Video Production Playbook.html">Video Playbook</a>
            <a href="Social Media Playbook.html">Social Media</a>
            <a href="Marketing Analytics Playbook.html">Analytics</a>
            <a href="Project Task Boards.html" class="active">Task Boards</a>
        </div>
```
Has `.nav-links a.active` rule. Replace nav-links with:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Portfolio ▾</button>
                <div class="nav-drop-menu">
                    <a href="Standard1.html">S1 · Servant Leadership</a>
                    <a href="Standard2.html">S2</a>
                    <a href="Standard3.html">S3 · Career Exploration</a>
                    <a href="Standard4.html">S4 · Technical Skills</a>
                    <a href="Standard5.html">S5 · Professional Skills</a>
                </div>
            </div>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Playbooks ▾</button>
                <div class="nav-drop-menu">
                    <a href="Video Production Playbook.html">Video Production</a>
                    <a href="Social Media Playbook.html">Social Media</a>
                    <a href="Marketing Analytics Playbook.html">Marketing Analytics</a>
                    <a href="Podcast Production Playbook.html">Podcast Production</a>
                    <a href="Graphic Design Playbook.html">Graphic Design</a>
                    <a href="Photography Playbook.html">Photography</a>
                </div>
            </div>
            <a href="Project Task Boards.html" class="active">Task Boards</a>
        </div>
```

---

## TASK 2 — Fix S4BSU localStorage pattern

`Standard4BSU.html` currently saves card state as individual localStorage keys (`bmc-sprint_done`, `bmc-sprint_reflection`, etc.), which is fragile. Change it to save a single unified JSON blob like S1 and S5 do.

Read the full `Standard4BSU.html` file first so you know the exact current code. Then make these changes:

**A.** Add `const S4BSU_KEY = 'apex_std4bsu_v1';` before `function saveLink`.

**B.** Replace `function saveLink(id, value) { localStorage.setItem(id, value); }` with:
```javascript
function saveLink(id, value) { saveS4BSUState(); }
```

**C.** Replace the entire `saveStd4State()` function with `saveS4BSUState()`:
```javascript
function saveS4BSUState() {
    const cards = {};
    document.querySelectorAll('.sprint-card').forEach(card => {
        if (!card.id) return;
        const linkInput = card.querySelector('input[type="text"]');
        const reflTA = card.querySelector('.reflection-textarea');
        const h4 = card.querySelector('h4');
        const title = h4 ? [...h4.childNodes]
            .filter(n => n.nodeType === Node.TEXT_NODE)
            .map(n => n.textContent).join('').trim() : '';
        cards[card.id] = {
            done: card.dataset.done === 'true',
            link: linkInput ? linkInput.value.trim() : '',
            reflection: reflTA ? reflTA.value.trim() : '',
            title
        };
    });
    localStorage.setItem(S4BSU_KEY, JSON.stringify(cards));
    // Keep Review Prep summary format
    const allCards = Object.entries(cards).map(([id, d]) => ({
        id, title: d.title, reflection: d.reflection, done: d.done
    }));
    localStorage.setItem('apex_std4_state_v6', JSON.stringify({ sprints: allCards }));
}
```

**D.** Replace `function saveReflection(cardId, value) { localStorage.setItem(cardId + '_reflection', value); }` with:
```javascript
function saveReflection(cardId, value) { saveS4BSUState(); }
```

**E.** In `markDone`, remove the `localStorage.setItem(cardId + '_done', ...)` line and replace the `saveStd4State()` call with `saveS4BSUState()`.

**F.** In `window.onload`, there's a block that restores link inputs by reading individual keys:
```javascript
    // Restore saved link inputs
    document.querySelectorAll('input[type="text"]').forEach(input => {
        const attr = input.getAttribute('onchange');
        if (attr) {
            const match = attr.match(/'([^']+)'/);
            if (match) {
                const saved = localStorage.getItem(match[1]);
                if (saved) input.value = saved;
            }
        }
    });
```
Replace this entire block with a unified state load:
```javascript
    // Load unified card state (with migration from individual keys)
    let s4State = {};
    const s4Raw = localStorage.getItem(S4BSU_KEY);
    if (s4Raw) {
        try { s4State = JSON.parse(s4Raw); } catch(e) {}
    }
```

**G.** In the card transform loop inside `window.onload`, find:
```javascript
            // Restore done state
            const isDone = localStorage.getItem(cardId + '_done') === 'true';
            card.dataset.done = isDone ? 'true' : 'false';

            // Restore reflection
            const savedReflection = localStorage.getItem(cardId + '_reflection') || '';
```
Replace with:
```javascript
            // Restore state (unified key with fallback to legacy individual keys)
            const cs = s4State[cardId];
            const isDone = cs ? cs.done : localStorage.getItem(cardId + '_done') === 'true';
            const savedLink = cs ? cs.link : (localStorage.getItem(cardId) || '');
            const savedReflection = cs ? cs.reflection : (localStorage.getItem(cardId + '_reflection') || '');
            card.dataset.done = isDone ? 'true' : 'false';
```

**H.** In the same card transform loop, find where `linkInput` is created:
```javascript
        const linkInput = document.createElement('input');
        linkInput.type = 'text';
        linkInput.placeholder = 'Paste presentation link here...';
        linkInput.onchange = () => saveLink(newId, linkInput.value);
```
Add `if (savedLink) linkInput.value = savedLink;` right after the `linkInput.onchange` line.

**I.** Near the end of `window.onload`, before `loadQueue()`, add:
```javascript
        // Commit unified state (migrates any legacy individual-key data)
        saveS4BSUState();
```

---

## TASK 3 — Fix portfolio progress display

`APEX_Professional_Portfolio.html` has a `loadProfile` function with this buggy progress code:
```javascript
        for (let i = 1; i <= 5; i++) {
            const stdData = localStorage.getItem(`apex_std${i}_state_v6`);
            if (stdData) {
                const badge = document.getElementById(`status-${i}`);
                if (badge) {
                    badge.innerText = 'Evidence Logged';
                    badge.classList.add('complete');
                }
            }
        }
```

Bugs:
- S3 uses key `apex_v37` not `apex_std3_state_v6` → badge never updates
- S5 uses key `apex_std5_v6` not `apex_std5_state_v6` → badge never updates
- Shows only binary "Evidence Logged" — no count

Replace with this improved version that shows actual counts ("X / Y done") and handles each standard's correct key and data format:

```javascript
        const STD_SOURCES = {
            1: { key: 'apex_std1_state_v6', fmt: 'kanban' },
            3: { key: 'apex_v37',           fmt: 'phase'  },
            4: { key: 'apex_std4bsu_v1',    fmt: 'bsu',   fallback: 'apex_std4_state_v6' },
            5: { key: 'apex_std5_v6',        fmt: 'kanban' }
        };
        [1, 3, 4, 5].forEach(i => {
            const badge = document.getElementById(`status-${i}`);
            if (!badge) return;
            try {
                const src = STD_SOURCES[i];
                let raw = localStorage.getItem(src.key);
                if (!raw && src.fallback) raw = localStorage.getItem(src.fallback);
                if (!raw) return;
                const state = JSON.parse(raw);
                let total = 0, done = 0, unit = 'done';
                if (src.fmt === 'kanban') {
                    Object.values(state).forEach(col => {
                        if (!Array.isArray(col)) return;
                        total += col.length;
                        done += col.filter(c => c.done === true || c.done === 'true').length;
                    });
                } else if (src.fmt === 'phase') {
                    total = 4; done = (state.visited || []).length; unit = 'phases';
                } else if (src.fmt === 'bsu') {
                    const cards = Object.values(state);
                    if (cards[0] && cards[0].sprints) {
                        // old summary format
                        total = cards[0].sprints.length;
                        done = cards[0].sprints.filter(c => c.done).length;
                    } else {
                        total = cards.length;
                        done = cards.filter(c => c.done).length;
                    }
                }
                if (total > 0) {
                    badge.innerText = `${done} / ${total} ${unit}`;
                    badge.classList.add('complete');
                }
            } catch(e) {}
        });
```

Also, the Portfolio page currently has two duplicate dropdown scripts at the bottom — remove the older/duplicate one (keep the one that also adds listeners on the menu element for extra reliability).

---

## TASK 4 — Fix Review Prep S5 key bug

In `APEX_Review_Prep.html`, the `STANDARDS` array has this for S5:
```javascript
        { num: 5, label: 'S5', name: 'Professional Skills', cls: 's5', key: 'apex_std5_state_v6' }
```
The actual S5 localStorage key is `apex_std5_v6` (no "state" in the middle). Change the key.

---

## TASK 5 — Create shared kanban file + DRY up S1 and S5

S1 (`Standard1.html`) and S5 (`Standard5.html`) have ~90% identical JS. Extract the shared logic into a new file `apex-kanban.js`, then simplify both pages to just define their config and call `initKanban(config)`.

**Create `/Users/russgoerend/Desktop/Portfolio HTMLs/apex-kanban.js`** with this content:

```javascript
// apex-kanban.js — Shared kanban logic for APEX Portfolio standards
// Used by: Standard1.html, Standard5.html

function initKanban(config) {
    const { cols, storageKey, sidebarKey, pageUrl, defaultCards } = config;
    let activeFilter = 'all';

    function saveToStorage(btn) {
        if (btn && btn.classList.contains('save-btn')) {
            btn.innerText = '✓ Saved';
            btn.classList.add('saved');
            setTimeout(() => { btn.innerText = 'Done'; btn.classList.remove('saved'); }, 2000);
        }
        const state = {};
        cols.forEach(col => {
            const cards = [];
            document.querySelectorAll(`#cards-${col} .sprint-card`).forEach(card => {
                cards.push({
                    id: card.dataset.id,
                    title: card.querySelector('.title-text').innerText,
                    brief: card.querySelector('.brief-text').innerText,
                    link: card.querySelector('.link-input').value,
                    reflection: card.querySelector('.reflection-textarea').value,
                    isCustom: card.dataset.custom === 'true',
                    done: card.dataset.done === 'true'
                });
            });
            state[col] = cards;
        });
        localStorage.setItem(storageKey, JSON.stringify(state));
    }

    function createCardHTML(col, cardData) {
        const isCustom = cardData.isCustom || String(cardData.id).startsWith('custom-') || String(cardData.id).startsWith('dup-');
        const isDone = cardData.done === true;
        const card = document.createElement('div');
        card.className = 'sprint-card';
        card.dataset.id = cardData.id;
        card.dataset.custom = isCustom;
        card.dataset.done = isDone ? 'true' : 'false';
        card.innerHTML = `
            <div class="card-actions">
                <button class="bookmark-btn" onclick="bookmarkCard(this)">📌 Bookmark</button>
                <button class="duplicate-btn" onclick="duplicateCard(this, '${col}')">Duplicate</button>
                ${isCustom ? `<button class="delete-btn" onclick="if(confirm('Delete this artifact?')){this.closest('.sprint-card').remove(); saveToStorage(); updateProgress('${col}');}">✕ Delete</button>` : ''}
            </div>
            <div class="card-header" onclick="toggleCard(this)">
                <span class="card-chevron">▶</span>
                <div class="editable-title title-text" contenteditable="true" onclick="event.stopPropagation()" onblur="saveToStorage()">${cardData.title}</div>
            </div>
            <div class="card-body">
                <details class="brief-container">
                    <summary>Experience Brief</summary>
                    <div class="brief-text" contenteditable="true" onblur="saveToStorage()">${cardData.brief || 'Describe the experience...'}</div>
                </details>
                <details class="reflection-box" ${cardData.reflection ? 'open' : ''}>
                    <summary>💭 Associate Reflection</summary>
                    <textarea class="reflection-textarea" placeholder="How does this demonstrate growth?" onblur="saveToStorage()">${cardData.reflection || ''}</textarea>
                </details>
                <div class="action-area">
                    <div class="link-row">
                        <input type="text" class="link-input" placeholder="Paste link here..." value="${cardData.link || ''}" onblur="saveToStorage()">
                        <button class="save-btn ${isDone ? 'saved-done' : ''}" onclick="markDone(this, '${col}')">${isDone ? '✓ Done' : 'Done'}</button>
                    </div>
                </div>
            </div>`;
        return card;
    }

    function duplicateCard(btn, col) {
        const orig = btn.closest('.sprint-card');
        const newCard = createCardHTML(col, {
            id: 'dup-' + Date.now(),
            title: orig.querySelector('.title-text').innerText,
            brief: orig.querySelector('.brief-text').innerText,
            link: '', reflection: '', isCustom: true
        });
        orig.after(newCard);
        saveToStorage();
    }

    function addNewCard(col) {
        const container = document.getElementById(`cards-${col}`);
        const newCard = createCardHTML(col, {
            id: 'custom-' + Date.now(),
            title: '✨ Click to name your custom artifact',
            brief: 'Describe the experience here...',
            link: '', reflection: '', isCustom: true
        });
        container.appendChild(newCard);
        saveToStorage();
        activeFilter = 'all';
        document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
        document.querySelector('[data-filter="all"]').classList.add('active');
        document.getElementById('filterSearch').value = '';
        applyFilter();
    }

    function bookmarkCard(btn) {
        const card = btn.closest('.sprint-card');
        const title = card.querySelector('.title-text').innerText;
        const brief = card.querySelector('.brief-text').innerText;
        addToQueue(title, pageUrl, brief);
        btn.innerText = '✓ Bookmarked';
        btn.style.background = '#10B981';
        btn.style.color = 'white';
        setTimeout(() => {
            btn.innerText = '📌 Bookmark';
            btn.style.background = '#FEF3C7';
            btn.style.color = '#92400E';
        }, 2000);
    }

    function addToQueue(title, url, desc) {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        queue.push({ id: Date.now().toString(), title, url, desc, addedAt: new Date().toISOString() });
        localStorage.setItem('apex_work_queue', JSON.stringify(queue));
    }

    function markDone(btn, col) {
        const card = btn.closest('.sprint-card');
        const isNowDone = card.dataset.done !== 'true';
        card.dataset.done = isNowDone ? 'true' : 'false';
        btn.innerText = isNowDone ? '✓ Done' : 'Done';
        btn.classList.toggle('saved-done', isNowDone);
        saveToStorage();
        updateProgress(col);
        applyFilter();
    }

    function updateProgress(col) {
        const cards = document.querySelectorAll(`#cards-${col} .sprint-card`);
        const doneCount = [...cards].filter(c => c.dataset.done === 'true').length;
        const pips = document.querySelectorAll(`#progress-${col} .progress-pip`);
        pips.forEach((pip, i) => pip.classList.toggle('lit', i < doneCount));
    }

    function toggleCard(headerEl) {
        const body = headerEl.closest('.sprint-card').querySelector('.card-body');
        const isOpen = headerEl.classList.contains('open');
        headerEl.classList.toggle('open', !isOpen);
        body.classList.toggle('open', !isOpen);
    }

    function setFilter(filter, btn) {
        activeFilter = filter;
        document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
        btn.classList.add('active');
        applyFilter();
    }

    function applyFilter() {
        const query = document.getElementById('filterSearch').value.toLowerCase().trim();
        cols.forEach(col => {
            let anyVisible = false;
            document.querySelectorAll(`#cards-${col} .sprint-card`).forEach(card => {
                const isDone = card.dataset.done === 'true';
                const link = card.querySelector('.link-input').value.trim();
                const reflection = card.querySelector('.reflection-textarea').value.trim();
                const title = card.querySelector('.title-text').innerText.toLowerCase();
                let statusMatch;
                if (activeFilter === 'all') statusMatch = true;
                else if (activeFilter === 'done') statusMatch = isDone;
                else if (activeFilter === 'in-progress') statusMatch = !isDone && (link !== '' || reflection !== '');
                else statusMatch = !isDone && link === '' && reflection === '';
                const show = statusMatch && (!query || title.includes(query));
                card.style.display = show ? '' : 'none';
                if (show) anyVisible = true;
            });
            const noMsg = document.querySelector(`[data-col="${col}"] .no-match-msg`);
            if (noMsg) noMsg.style.display = anyVisible ? 'none' : 'block';
        });
    }

    function toggleSidebar() {
        const sidebar = document.getElementById('sidebar');
        const isOpen = sidebar.classList.contains('open');
        sidebar.classList.toggle('open', !isOpen);
        document.body.classList.toggle('body-sidebar-open', !isOpen);
        localStorage.setItem(sidebarKey, !isOpen);
    }

    function loadQueue() {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        displayQueue(queue);
    }

    function displayQueue(queue) {
        const list = document.getElementById('queueList');
        if (!list) return;
        if (queue.length === 0) {
            list.innerHTML = '<div class="empty-queue">No items yet. Bookmark an artifact card to add it here.</div>';
            return;
        }
        list.innerHTML = queue.map(item => `
            <div class="queue-item">
                <a href="${item.url}" target="_blank">${item.title}</a>
                <p>${item.desc || ''}</p>
                <button class="remove-btn" onclick="removeFromQueue('${item.id}')">×</button>
            </div>`).join('');
    }

    function removeFromQueue(id) {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        localStorage.setItem('apex_work_queue', JSON.stringify(queue.filter(item => item.id !== id)));
        displayQueue(JSON.parse(localStorage.getItem('apex_work_queue') || '[]'));
    }

    function init() {
        const saved = localStorage.getItem(storageKey);
        const state = saved ? JSON.parse(saved) : defaultCards;
        cols.forEach(col => {
            const container = document.getElementById(`cards-${col}`);
            if (!container) return;
            container.innerHTML = '';
            (state[col] || []).forEach(cardData => {
                if (cardData.brief === undefined) {
                    const def = (defaultCards[col] || []).find(c => c.id === cardData.id);
                    cardData.brief = def ? def.brief : 'Experience details...';
                }
                container.appendChild(createCardHTML(col, cardData));
            });
            updateProgress(col);
        });
        cols.forEach(col => {
            const msg = document.createElement('div');
            msg.className = 'no-match-msg';
            msg.textContent = 'No matching artifacts.';
            document.querySelector(`[data-col="${col}"] .add-btn`).before(msg);
        });
        loadQueue();
        if (localStorage.getItem(sidebarKey) === 'true') {
            document.getElementById('sidebar').classList.add('open');
            document.body.classList.add('body-sidebar-open');
        }
        applyFilter();
    }

    // Expose as globals for HTML onclick attributes
    window.saveToStorage = saveToStorage;
    window.addNewCard = addNewCard;
    window.bookmarkCard = bookmarkCard;
    window.duplicateCard = duplicateCard;
    window.markDone = markDone;
    window.updateProgress = updateProgress;
    window.toggleCard = toggleCard;
    window.toggleSidebar = toggleSidebar;
    window.removeFromQueue = removeFromQueue;
    window.setFilter = setFilter;
    window.applyFilter = applyFilter;

    document.addEventListener('DOMContentLoaded', () => {
        const sb = document.getElementById('sidebarToggle');
        const cl = document.getElementById('closeSidebar');
        if (sb) sb.addEventListener('click', toggleSidebar);
        if (cl) cl.addEventListener('click', toggleSidebar);
        const fs = document.getElementById('filterSearch');
        if (fs) fs.addEventListener('input', applyFilter);
    });

    document.addEventListener('input', e => {
        if (e.target.classList.contains('reflection-textarea') || e.target.hasAttribute('contenteditable')) {
            saveToStorage();
        }
    });

    window.onload = init;
}
```

**Update `Standard1.html`:**

1. Add `data-col` attributes to the 4 vibe-column divs in the HTML. The current divs are:
```html
            <div class="vibe-column col-1" id="col-servant">
            <div class="vibe-column col-2" id="col-client">
            <div class="vibe-column col-3" id="col-team">
            <div class="vibe-column col-4" id="col-brand">
```
Change to add `data-col="servant"`, `data-col="client"`, etc. Keep the existing id.

2. Replace the entire `<script>` block (starting with `const defaultCards = {` and ending with `window.onload = init;`) plus the nav dropdown JS block that follows, with:
```html
<script src="apex-kanban.js"></script>
<script>
    initKanban({
        cols: ['servant', 'client', 'team', 'brand'],
        storageKey: 'apex_std1_state_v6',
        sidebarKey: 's1_sidebar_open',
        pageUrl: 'Standard1.html',
        defaultCards: {
            servant: [
                { id: 'vol-proj', title: '🙋♂🛠 Volunteering for a Project', brief: 'Evidence of stepping up to assist a peer or support a team goal.', link: '', reflection: '', isCustom: false },
                { id: 'peer-rev', title: '📝🤝 Constructive Peer Review', brief: 'Actionable, kind, and specific feedback on a teammate\'s work.', link: '', reflection: '', isCustom: false },
                { id: 'wilc-steward', title: '🧹🏫 WILC Stewardship', brief: 'Maintaining the professional environment (resetting rooms, guiding guests).', link: '', reflection: '', isCustom: false }
            ],
            client: [
                { id: 'c-email', title: '📧✍ Professional Email to Client', brief: 'Email with clear subject, 5 Ws and H, and specific next steps.', link: '', reflection: '', isCustom: false },
                { id: 'c-cal', title: '📅✅ Google Calendar Event', brief: 'Evidence of professional scheduling with all guests invited.', link: '', reflection: '', isCustom: false },
                { id: 'c-up', title: '🔄📈 Weekly Client Update', brief: 'Screenshot of project progress, roadblocks, and next steps.', link: '', reflection: '', isCustom: false }
            ],
            team: [
                { id: 'ind-tour', title: '🏭🗺 Organizing an Industry Tour', brief: 'Coordinating logistics or preparing team questions for a tour.', link: '', reflection: '', isCustom: false },
                { id: 'kan-champ', title: '📊📌 Kanban Board Champion', brief: 'Maintaining a Trello or Slack Canvas task list.', link: '', reflection: '', isCustom: false },
                { id: 'lead-stand', title: '🗣📋 Leading the Stand-Up', brief: 'Evidence of leading a daily scrum or team meeting.', link: '', reflection: '', isCustom: false }
            ],
            brand: [
                { id: 't-fair', title: '🎤🏫 Timberline Career Fair', brief: 'Communicating the APEX value proposition to a public audience.', link: '', reflection: '', isCustom: false },
                { id: 'pitch', title: '🗣👔 The Elevator Pitch', brief: 'Video or script of your 60-second professional introduction.', link: '', reflection: '', isCustom: false },
                { id: 'bus-casual', title: '👔✨ Business Casual Excellence', brief: 'Demonstrating professional appearance standards in client settings.', link: '', reflection: '', isCustom: false }
            ]
        }
    });
</script>
```

**Update `Standard5.html`:**

1. The CSS has `.instruction-text` — rename all occurrences to `.brief-text` in the CSS.

2. Replace the entire `<script>` block (starting with `const COLS = ['digital-comm'...` through `window.onload = init;`) plus any following nav-dropdown JS, with:
```html
<script src="apex-kanban.js"></script>
<script>
    initKanban({
        cols: ['digital-comm', 'prof-presence', 'task-mgmt', 'growth-net'],
        storageKey: 'apex_std5_v6',
        sidebarKey: 's5_sidebar_open',
        pageUrl: 'Standard5.html',
        defaultCards: {
            'digital-comm': [
                { id: 'slack-mastery', title: '✅💬 Slack Mastery', brief: 'Show evidence that you have configured your notifications to "Always" and actively use "Read & React" in team channels.', link: '', reflection: '', isCustom: false },
                { id: 'cc-protocol', title: '📧👥 The "CC" Protocol', brief: 'Capture a screenshot of an email sent to a client where you successfully CC\'d your instructor and partners.', link: '', reflection: '', isCustom: false }
            ],
            'prof-presence': [
                { id: 'handshake', title: '🤝👀 The First Impression', brief: 'Reflect on an interaction involving eye contact, body language, and a professional introduction.', link: '', reflection: '', isCustom: false },
                { id: 'dress-code', title: '👗👔 APEX Dress Code', brief: 'Provide a photo of your Outfit of the Day (OOTD) modeling APEX business casual standards.', link: '', reflection: '', isCustom: false }
            ],
            'task-mgmt': [
                { id: 'agenda', title: '📝⏱ The Pre-Meeting Agenda', brief: 'Provide a link to an agenda shared with your team and client *before* a scheduled meeting.', link: '', reflection: '', isCustom: false },
                { id: 'micro-deliverable', title: '🎯✅ Weekly Micro-Deliverable', brief: 'Evidence of moving a task from "Doing" to "Done" on your team\'s Trello board or Kanban.', link: '', reflection: '', isCustom: false }
            ],
            'growth-net': [
                { id: 'linkedin', title: '🔗🌐 LinkedIn Optimization', brief: 'Update your profile with a headshot, APEX headline, and "About" section featuring your Career Thesis.', link: '', reflection: '', isCustom: false },
                { id: 'feedback', title: '🔄📉 The Feedback Loop', brief: 'Show how you applied constructive criticism from a client or peer to improve a product.', link: '', reflection: '', isCustom: false }
            ]
        }
    });
</script>
```

---

## Important notes

- Read any file before editing it
- The Standard1 and Standard5 scripts are large — make sure you capture the FULL script block when replacing, including the nav-dropdown JS that appears after `window.onload = init;`
- For S1, the `<script>` block starts with `const defaultCards = {` and the entire section (including any following nav dropdown scripts before `</body>`) should be replaced
- For S5, the `<script>` block starts with `const COLS = [`
- When replacing S4BSU individual functions, use exact string matches to avoid touching surrounding code
- The `removeFromQueue` function in the shared file should re-read localStorage after saving instead of using the filtered array directly (to stay consistent)
- Do NOT modify Standard4BSU.html's CSS or HTML structure — only the JS functions and window.onload handler


All tasks are complete. Here is a summary of what was done:

---

**Task 1 — Nav dropdowns added to Review Prep and Task Boards**

- `/Users/russgoerend/Desktop/Portfolio HTMLs/APEX_Review_Prep.html`: Added nav dropdown CSS block (after `.nav-links a:hover` rule), replaced nav-links HTML with Portfolio/Playbooks dropdowns (keeping the Copy/Print buttons), added hover-timer JS before `</body>`.
- `/Users/russgoerend/Desktop/Portfolio HTMLs/Project Task Boards.html`: Same CSS and JS additions; replaced the old flat nav links with the dropdown structure, retaining the `active` class on "Task Boards".

**Task 2 — S4BSU unified localStorage pattern**

- `/Users/russgoerend/Desktop/Portfolio HTMLs/Standard4BSU.html`:
  - Added `const S4BSU_KEY = 'apex_std4bsu_v1'`
  - Replaced `saveStd4State()` with `saveS4BSUState()` (saves full card objects to `apex_std4bsu_v1`, still writes `apex_std4_state_v6` in Review Prep summary format)
  - `saveLink()` and `saveReflection()` now delegate to `saveS4BSUState()`
  - `markDone()` no longer writes individual `_done` keys
  - `window.onload` now loads `s4State` from the unified key with legacy fallback for each field, restores link inputs, and commits unified state at the end

**Task 3 — Portfolio progress display fixed**

- `/Users/russgoerend/Desktop/Portfolio HTMLs/APEX_Professional_Portfolio.html`: Replaced the broken `for i = 1..5` badge loop with `STD_SOURCES` map using correct keys (`apex_v37` for S3, `apex_std5_v6` for S5, `apex_std4bsu_v1` for S4 with fallback). Badges now show `X / Y done` (or `X / 4 phases` for S3). Removed the duplicate simpler dropdown script (kept the IIFE version that also binds menu hover listeners).

**Task 4 — Review Prep S5 key bug fixed**

- `/Users/russgoerend/Desktop/Portfolio HTMLs/APEX_Review_Prep.html`: Changed S5 key from `apex_std5_state_v6` to `apex_std5_v6`.

**Task 5 — Shared kanban JS + DRY up S1 and S5**

- Created `/Users/russgoerend/Desktop/Portfolio HTMLs/apex-kanban.js` (268 lines) with `initKanban(config)` encapsulating all shared kanban logic.
- `/Users/russgoerend/Desktop/Portfolio HTMLs/Standard1.html`: Added `data-col` attributes to all 4 vibe-column divs; replaced entire old script block + duplicate nav dropdown scripts with `<script src="apex-kanban.js">` + `initKanban()` call.
- `/Users/russgoerend/Desktop/Portfolio HTMLs/Standard5.html`: Renamed `.instruction-text` to `.brief-text` in CSS; replaced entire old script block + duplicate nav dropdown scripts with `<script src="apex-kanban.js">` + `initKanban()` call (full file rewrite to cleanly remove all remnants).

agentId: aa9967871ef64be06 (use SendMessage with to: 'aa9967871ef64be06' to continue this agent)
<usage>total_tokens: 148518
tool_uses: 60
duration_ms: 527042</usage>

Prompt:
You're working on an offline APEX portfolio system — standalone HTML/CSS/JS files that use localStorage for persistence. You have 4 concrete tasks to complete. All files are in `/Users/russgoerend/Desktop/Portfolio HTMLs/`.

---

## TASK 1 — Add nav dropdowns to Review Prep and Task Boards

Both pages were missed in a previous nav update. Every other page already has this structure: `Dashboard | Portfolio ▾ | Playbooks ▾` using CSS hover dropdowns and a JS hover timer.

**CSS to add** (after `.nav-links a.active` rule, or `.nav-links a:hover` if no `.active` rule exists):
```css
        /* --- NAV DROPDOWNS --- */
        .nav-dropdown { position: relative; }
        .nav-drop-btn { color: rgba(255,255,255,0.9); background: none; border: none; font-size: 0.8rem; font-weight: 700; padding: 8px 14px; border-radius: 6px; text-transform: uppercase; letter-spacing: 0.03em; transition: all 0.2s; cursor: pointer; font-family: inherit; }
        .nav-dropdown:hover .nav-drop-btn { color: var(--secondary); background: rgba(255,255,255,0.1); }
        .nav-drop-btn.active { background: var(--secondary); color: var(--primary); }
        .nav-dropdown:hover .nav-drop-btn.active { background: var(--secondary); color: var(--primary); opacity: 0.85; }
        .nav-drop-menu { display: none; position: absolute; top: calc(100% + 8px); left: 0; background: white; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); min-width: 210px; z-index: 1001; padding: 6px 0; border: 1px solid #E5E7EB; }
        .nav-dropdown:hover .nav-drop-menu { display: block; }
        .nav-drop-menu a { display: block; padding: 9px 16px; color: #1F2937; text-decoration: none; font-size: 0.82rem; font-weight: 600; transition: background 0.15s; white-space: nowrap; }
        .nav-drop-menu a:hover { background: #F3F4F6; color: var(--primary); }
        .nav-drop-menu a.active { color: var(--primary); font-weight: 800; background: #F5F3FF; }
```

**JS to add** (before `</body>`):
```html
<script>
document.querySelectorAll('.nav-dropdown').forEach(d => {
    let t, m = d.querySelector('.nav-drop-menu');
    d.addEventListener('mouseenter', () => { clearTimeout(t); m.style.display = 'block'; });
    d.addEventListener('mouseleave', () => { t = setTimeout(() => { m.style.display = ''; }, 150); });
});
</script>
```

**`APEX_Review_Prep.html`** — current nav-links HTML is:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <button class="btn-copy" onclick="copyToClipboard()">📋 Copy Text</button>
            <button class="btn-print" onclick="window.print()">🖨 Print / PDF</button>
        </div>
```
The `.nav-links a:hover` rule exists on this page (no `.active` rule). Replace nav-links with:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Portfolio ▾</button>
                <div class="nav-drop-menu">
                    <a href="Standard1.html">S1 · Servant Leadership</a>
                    <a href="Standard2.html">S2</a>
                    <a href="Standard3.html">S3 · Career Exploration</a>
                    <a href="Standard4.html">S4 · Technical Skills</a>
                    <a href="Standard5.html">S5 · Professional Skills</a>
                </div>
            </div>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Playbooks ▾</button>
                <div class="nav-drop-menu">
                    <a href="Video Production Playbook.html">Video Production</a>
                    <a href="Social Media Playbook.html">Social Media</a>
                    <a href="Marketing Analytics Playbook.html">Marketing Analytics</a>
                    <a href="Podcast Production Playbook.html">Podcast Production</a>
                    <a href="Graphic Design Playbook.html">Graphic Design</a>
                    <a href="Photography Playbook.html">Photography</a>
                </div>
            </div>
            <button class="btn-copy" onclick="copyToClipboard()">📋 Copy Text</button>
            <button class="btn-print" onclick="window.print()">🖨 Print / PDF</button>
        </div>
```

**`Project Task Boards.html`** — current nav-links HTML is:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <a href="APEX_Professional_Portfolio.html">Portfolio</a>
            <a href="Video Production Playbook.html">Video Playbook</a>
            <a href="Social Media Playbook.html">Social Media</a>
            <a href="Marketing Analytics Playbook.html">Analytics</a>
            <a href="Project Task Boards.html" class="active">Task Boards</a>
        </div>
```
Has `.nav-links a.active` rule. Replace nav-links with:
```html
        <div class="nav-links">
            <a href="APEX_Professional_Dashboard.html">Dashboard</a>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Portfolio ▾</button>
                <div class="nav-drop-menu">
                    <a href="Standard1.html">S1 · Servant Leadership</a>
                    <a href="Standard2.html">S2</a>
                    <a href="Standard3.html">S3 · Career Exploration</a>
                    <a href="Standard4.html">S4 · Technical Skills</a>
                    <a href="Standard5.html">S5 · Professional Skills</a>
                </div>
            </div>
            <div class="nav-dropdown">
                <button class="nav-drop-btn">Playbooks ▾</button>
                <div class="nav-drop-menu">
                    <a href="Video Production Playbook.html">Video Production</a>
                    <a href="Social Media Playbook.html">Social Media</a>
                    <a href="Marketing Analytics Playbook.html">Marketing Analytics</a>
                    <a href="Podcast Production Playbook.html">Podcast Production</a>
                    <a href="Graphic Design Playbook.html">Graphic Design</a>
                    <a href="Photography Playbook.html">Photography</a>
                </div>
            </div>
            <a href="Project Task Boards.html" class="active">Task Boards</a>
        </div>
```

---

## TASK 2 — Fix S4BSU localStorage pattern

`Standard4BSU.html` currently saves card state as individual localStorage keys (`bmc-sprint_done`, `bmc-sprint_reflection`, etc.), which is fragile. Change it to save a single unified JSON blob like S1 and S5 do.

Read the full `Standard4BSU.html` file first so you know the exact current code. Then make these changes:

**A.** Add `const S4BSU_KEY = 'apex_std4bsu_v1';` before `function saveLink`.

**B.** Replace `function saveLink(id, value) { localStorage.setItem(id, value); }` with:
```javascript
function saveLink(id, value) { saveS4BSUState(); }
```

**C.** Replace the entire `saveStd4State()` function with `saveS4BSUState()`:
```javascript
function saveS4BSUState() {
    const cards = {};
    document.querySelectorAll('.sprint-card').forEach(card => {
        if (!card.id) return;
        const linkInput = card.querySelector('input[type="text"]');
        const reflTA = card.querySelector('.reflection-textarea');
        const h4 = card.querySelector('h4');
        const title = h4 ? [...h4.childNodes]
            .filter(n => n.nodeType === Node.TEXT_NODE)
            .map(n => n.textContent).join('').trim() : '';
        cards[card.id] = {
            done: card.dataset.done === 'true',
            link: linkInput ? linkInput.value.trim() : '',
            reflection: reflTA ? reflTA.value.trim() : '',
            title
        };
    });
    localStorage.setItem(S4BSU_KEY, JSON.stringify(cards));
    // Keep Review Prep summary format
    const allCards = Object.entries(cards).map(([id, d]) => ({
        id, title: d.title, reflection: d.reflection, done: d.done
    }));
    localStorage.setItem('apex_std4_state_v6', JSON.stringify({ sprints: allCards }));
}
```

**D.** Replace `function saveReflection(cardId, value) { localStorage.setItem(cardId + '_reflection', value); }` with:
```javascript
function saveReflection(cardId, value) { saveS4BSUState(); }
```

**E.** In `markDone`, remove the `localStorage.setItem(cardId + '_done', ...)` line and replace the `saveStd4State()` call with `saveS4BSUState()`.

**F.** In `window.onload`, there's a block that restores link inputs by reading individual keys:
```javascript
    // Restore saved link inputs
    document.querySelectorAll('input[type="text"]').forEach(input => {
        const attr = input.getAttribute('onchange');
        if (attr) {
            const match = attr.match(/'([^']+)'/);
            if (match) {
                const saved = localStorage.getItem(match[1]);
                if (saved) input.value = saved;
            }
        }
    });
```
Replace this entire block with a unified state load:
```javascript
    // Load unified card state (with migration from individual keys)
    let s4State = {};
    const s4Raw = localStorage.getItem(S4BSU_KEY);
    if (s4Raw) {
        try { s4State = JSON.parse(s4Raw); } catch(e) {}
    }
```

**G.** In the card transform loop inside `window.onload`, find:
```javascript
            // Restore done state
            const isDone = localStorage.getItem(cardId + '_done') === 'true';
            card.dataset.done = isDone ? 'true' : 'false';

            // Restore reflection
            const savedReflection = localStorage.getItem(cardId + '_reflection') || '';
```
Replace with:
```javascript
            // Restore state (unified key with fallback to legacy individual keys)
            const cs = s4State[cardId];
            const isDone = cs ? cs.done : localStorage.getItem(cardId + '_done') === 'true';
            const savedLink = cs ? cs.link : (localStorage.getItem(cardId) || '');
            const savedReflection = cs ? cs.reflection : (localStorage.getItem(cardId + '_reflection') || '');
            card.dataset.done = isDone ? 'true' : 'false';
```

**H.** In the same card transform loop, find where `linkInput` is created:
```javascript
        const linkInput = document.createElement('input');
        linkInput.type = 'text';
        linkInput.placeholder = 'Paste presentation link here...';
        linkInput.onchange = () => saveLink(newId, linkInput.value);
```
Add `if (savedLink) linkInput.value = savedLink;` right after the `linkInput.onchange` line.

**I.** Near the end of `window.onload`, before `loadQueue()`, add:
```javascript
        // Commit unified state (migrates any legacy individual-key data)
        saveS4BSUState();
```

---

## TASK 3 — Fix portfolio progress display

`APEX_Professional_Portfolio.html` has a `loadProfile` function with this buggy progress code:
```javascript
        for (let i = 1; i <= 5; i++) {
            const stdData = localStorage.getItem(`apex_std${i}_state_v6`);
            if (stdData) {
                const badge = document.getElementById(`status-${i}`);
                if (badge) {
                    badge.innerText = 'Evidence Logged';
                    badge.classList.add('complete');
                }
            }
        }
```

Bugs:
- S3 uses key `apex_v37` not `apex_std3_state_v6` → badge never updates
- S5 uses key `apex_std5_v6` not `apex_std5_state_v6` → badge never updates
- Shows only binary "Evidence Logged" — no count

Replace with this improved version that shows actual counts ("X / Y done") and handles each standard's correct key and data format:

```javascript
        const STD_SOURCES = {
            1: { key: 'apex_std1_state_v6', fmt: 'kanban' },
            3: { key: 'apex_v37',           fmt: 'phase'  },
            4: { key: 'apex_std4bsu_v1',    fmt: 'bsu',   fallback: 'apex_std4_state_v6' },
            5: { key: 'apex_std5_v6',        fmt: 'kanban' }
        };
        [1, 3, 4, 5].forEach(i => {
            const badge = document.getElementById(`status-${i}`);
            if (!badge) return;
            try {
                const src = STD_SOURCES[i];
                let raw = localStorage.getItem(src.key);
                if (!raw && src.fallback) raw = localStorage.getItem(src.fallback);
                if (!raw) return;
                const state = JSON.parse(raw);
                let total = 0, done = 0, unit = 'done';
                if (src.fmt === 'kanban') {
                    Object.values(state).forEach(col => {
                        if (!Array.isArray(col)) return;
                        total += col.length;
                        done += col.filter(c => c.done === true || c.done === 'true').length;
                    });
                } else if (src.fmt === 'phase') {
                    total = 4; done = (state.visited || []).length; unit = 'phases';
                } else if (src.fmt === 'bsu') {
                    const cards = Object.values(state);
                    if (cards[0] && cards[0].sprints) {
                        // old summary format
                        total = cards[0].sprints.length;
                        done = cards[0].sprints.filter(c => c.done).length;
                    } else {
                        total = cards.length;
                        done = cards.filter(c => c.done).length;
                    }
                }
                if (total > 0) {
                    badge.innerText = `${done} / ${total} ${unit}`;
                    badge.classList.add('complete');
                }
            } catch(e) {}
        });
```

Also, the Portfolio page currently has two duplicate dropdown scripts at the bottom — remove the older/duplicate one (keep the one that also adds listeners on the menu element for extra reliability).

---

## TASK 4 — Fix Review Prep S5 key bug

In `APEX_Review_Prep.html`, the `STANDARDS` array has this for S5:
```javascript
        { num: 5, label: 'S5', name: 'Professional Skills', cls: 's5', key: 'apex_std5_state_v6' }
```
The actual S5 localStorage key is `apex_std5_v6` (no "state" in the middle). Change the key.

---

## TASK 5 — Create shared kanban file + DRY up S1 and S5

S1 (`Standard1.html`) and S5 (`Standard5.html`) have ~90% identical JS. Extract the shared logic into a new file `apex-kanban.js`, then simplify both pages to just define their config and call `initKanban(config)`.

**Create `/Users/russgoerend/Desktop/Portfolio HTMLs/apex-kanban.js`** with this content:

```javascript
// apex-kanban.js — Shared kanban logic for APEX Portfolio standards
// Used by: Standard1.html, Standard5.html

function initKanban(config) {
    const { cols, storageKey, sidebarKey, pageUrl, defaultCards } = config;
    let activeFilter = 'all';

    function saveToStorage(btn) {
        if (btn && btn.classList.contains('save-btn')) {
            btn.innerText = '✓ Saved';
            btn.classList.add('saved');
            setTimeout(() => { btn.innerText = 'Done'; btn.classList.remove('saved'); }, 2000);
        }
        const state = {};
        cols.forEach(col => {
            const cards = [];
            document.querySelectorAll(`#cards-${col} .sprint-card`).forEach(card => {
                cards.push({
                    id: card.dataset.id,
                    title: card.querySelector('.title-text').innerText,
                    brief: card.querySelector('.brief-text').innerText,
                    link: card.querySelector('.link-input').value,
                    reflection: card.querySelector('.reflection-textarea').value,
                    isCustom: card.dataset.custom === 'true',
                    done: card.dataset.done === 'true'
                });
            });
            state[col] = cards;
        });
        localStorage.setItem(storageKey, JSON.stringify(state));
    }

    function createCardHTML(col, cardData) {
        const isCustom = cardData.isCustom || String(cardData.id).startsWith('custom-') || String(cardData.id).startsWith('dup-');
        const isDone = cardData.done === true;
        const card = document.createElement('div');
        card.className = 'sprint-card';
        card.dataset.id = cardData.id;
        card.dataset.custom = isCustom;
        card.dataset.done = isDone ? 'true' : 'false';
        card.innerHTML = `
            <div class="card-actions">
                <button class="bookmark-btn" onclick="bookmarkCard(this)">📌 Bookmark</button>
                <button class="duplicate-btn" onclick="duplicateCard(this, '${col}')">Duplicate</button>
                ${isCustom ? `<button class="delete-btn" onclick="if(confirm('Delete this artifact?')){this.closest('.sprint-card').remove(); saveToStorage(); updateProgress('${col}');}">✕ Delete</button>` : ''}
            </div>
            <div class="card-header" onclick="toggleCard(this)">
                <span class="card-chevron">▶</span>
                <div class="editable-title title-text" contenteditable="true" onclick="event.stopPropagation()" onblur="saveToStorage()">${cardData.title}</div>
            </div>
            <div class="card-body">
                <details class="brief-container">
                    <summary>Experience Brief</summary>
                    <div class="brief-text" contenteditable="true" onblur="saveToStorage()">${cardData.brief || 'Describe the experience...'}</div>
                </details>
                <details class="reflection-box" ${cardData.reflection ? 'open' : ''}>
                    <summary>💭 Associate Reflection</summary>
                    <textarea class="reflection-textarea" placeholder="How does this demonstrate growth?" onblur="saveToStorage()">${cardData.reflection || ''}</textarea>
                </details>
                <div class="action-area">
                    <div class="link-row">
                        <input type="text" class="link-input" placeholder="Paste link here..." value="${cardData.link || ''}" onblur="saveToStorage()">
                        <button class="save-btn ${isDone ? 'saved-done' : ''}" onclick="markDone(this, '${col}')">${isDone ? '✓ Done' : 'Done'}</button>
                    </div>
                </div>
            </div>`;
        return card;
    }

    function duplicateCard(btn, col) {
        const orig = btn.closest('.sprint-card');
        const newCard = createCardHTML(col, {
            id: 'dup-' + Date.now(),
            title: orig.querySelector('.title-text').innerText,
            brief: orig.querySelector('.brief-text').innerText,
            link: '', reflection: '', isCustom: true
        });
        orig.after(newCard);
        saveToStorage();
    }

    function addNewCard(col) {
        const container = document.getElementById(`cards-${col}`);
        const newCard = createCardHTML(col, {
            id: 'custom-' + Date.now(),
            title: '✨ Click to name your custom artifact',
            brief: 'Describe the experience here...',
            link: '', reflection: '', isCustom: true
        });
        container.appendChild(newCard);
        saveToStorage();
        activeFilter = 'all';
        document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
        document.querySelector('[data-filter="all"]').classList.add('active');
        document.getElementById('filterSearch').value = '';
        applyFilter();
    }

    function bookmarkCard(btn) {
        const card = btn.closest('.sprint-card');
        const title = card.querySelector('.title-text').innerText;
        const brief = card.querySelector('.brief-text').innerText;
        addToQueue(title, pageUrl, brief);
        btn.innerText = '✓ Bookmarked';
        btn.style.background = '#10B981';
        btn.style.color = 'white';
        setTimeout(() => {
            btn.innerText = '📌 Bookmark';
            btn.style.background = '#FEF3C7';
            btn.style.color = '#92400E';
        }, 2000);
    }

    function addToQueue(title, url, desc) {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        queue.push({ id: Date.now().toString(), title, url, desc, addedAt: new Date().toISOString() });
        localStorage.setItem('apex_work_queue', JSON.stringify(queue));
    }

    function markDone(btn, col) {
        const card = btn.closest('.sprint-card');
        const isNowDone = card.dataset.done !== 'true';
        card.dataset.done = isNowDone ? 'true' : 'false';
        btn.innerText = isNowDone ? '✓ Done' : 'Done';
        btn.classList.toggle('saved-done', isNowDone);
        saveToStorage();
        updateProgress(col);
        applyFilter();
    }

    function updateProgress(col) {
        const cards = document.querySelectorAll(`#cards-${col} .sprint-card`);
        const doneCount = [...cards].filter(c => c.dataset.done === 'true').length;
        const pips = document.querySelectorAll(`#progress-${col} .progress-pip`);
        pips.forEach((pip, i) => pip.classList.toggle('lit', i < doneCount));
    }

    function toggleCard(headerEl) {
        const body = headerEl.closest('.sprint-card').querySelector('.card-body');
        const isOpen = headerEl.classList.contains('open');
        headerEl.classList.toggle('open', !isOpen);
        body.classList.toggle('open', !isOpen);
    }

    function setFilter(filter, btn) {
        activeFilter = filter;
        document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
        btn.classList.add('active');
        applyFilter();
    }

    function applyFilter() {
        const query = document.getElementById('filterSearch').value.toLowerCase().trim();
        cols.forEach(col => {
            let anyVisible = false;
            document.querySelectorAll(`#cards-${col} .sprint-card`).forEach(card => {
                const isDone = card.dataset.done === 'true';
                const link = card.querySelector('.link-input').value.trim();
                const reflection = card.querySelector('.reflection-textarea').value.trim();
                const title = card.querySelector('.title-text').innerText.toLowerCase();
                let statusMatch;
                if (activeFilter === 'all') statusMatch = true;
                else if (activeFilter === 'done') statusMatch = isDone;
                else if (activeFilter === 'in-progress') statusMatch = !isDone && (link !== '' || reflection !== '');
                else statusMatch = !isDone && link === '' && reflection === '';
                const show = statusMatch && (!query || title.includes(query));
                card.style.display = show ? '' : 'none';
                if (show) anyVisible = true;
            });
            const noMsg = document.querySelector(`[data-col="${col}"] .no-match-msg`);
            if (noMsg) noMsg.style.display = anyVisible ? 'none' : 'block';
        });
    }

    function toggleSidebar() {
        const sidebar = document.getElementById('sidebar');
        const isOpen = sidebar.classList.contains('open');
        sidebar.classList.toggle('open', !isOpen);
        document.body.classList.toggle('body-sidebar-open', !isOpen);
        localStorage.setItem(sidebarKey, !isOpen);
    }

    function loadQueue() {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        displayQueue(queue);
    }

    function displayQueue(queue) {
        const list = document.getElementById('queueList');
        if (!list) return;
        if (queue.length === 0) {
            list.innerHTML = '<div class="empty-queue">No items yet. Bookmark an artifact card to add it here.</div>';
            return;
        }
        list.innerHTML = queue.map(item => `
            <div class="queue-item">
                <a href="${item.url}" target="_blank">${item.title}</a>
                <p>${item.desc || ''}</p>
                <button class="remove-btn" onclick="removeFromQueue('${item.id}')">×</button>
            </div>`).join('');
    }

    function removeFromQueue(id) {
        const queue = JSON.parse(localStorage.getItem('apex_work_queue') || '[]');
        localStorage.setItem('apex_work_queue', JSON.stringify(queue.filter(item => item.id !== id)));
        displayQueue(JSON.parse(localStorage.getItem('apex_work_queue') || '[]'));
    }

    function init() {
        const saved = localStorage.getItem(storageKey);
        const state = saved ? JSON.parse(saved) : defaultCards;
        cols.forEach(col => {
            const container = document.getElementById(`cards-${col}`);
            if (!container) return;
            container.innerHTML = '';
            (state[col] || []).forEach(cardData => {
                if (cardData.brief === undefined) {
                    const def = (defaultCards[col] || []).find(c => c.id === cardData.id);
                    cardData.brief = def ? def.brief : 'Experience details...';
                }
                container.appendChild(createCardHTML(col, cardData));
            });
            updateProgress(col);
        });
        cols.forEach(col => {
            const msg = document.createElement('div');
            msg.className = 'no-match-msg';
            msg.textContent = 'No matching artifacts.';
            document.querySelector(`[data-col="${col}"] .add-btn`).before(msg);
        });
        loadQueue();
        if (localStorage.getItem(sidebarKey) === 'true') {
            document.getElementById('sidebar').classList.add('open');
            document.body.classList.add('body-sidebar-open');
        }
        applyFilter();
    }

    // Expose as globals for HTML onclick attributes
    window.saveToStorage = saveToStorage;
    window.addNewCard = addNewCard;
    window.bookmarkCard = bookmarkCard;
    window.duplicateCard = duplicateCard;
    window.markDone = markDone;
    window.updateProgress = updateProgress;
    window.toggleCard = toggleCard;
    window.toggleSidebar = toggleSidebar;
    window.removeFromQueue = removeFromQueue;
    window.setFilter = setFilter;
    window.applyFilter = applyFilter;

    document.addEventListener('DOMContentLoaded', () => {
        const sb = document.getElementById('sidebarToggle');
        const cl = document.getElementById('closeSidebar');
        if (sb) sb.addEventListener('click', toggleSidebar);
        if (cl) cl.addEventListener('click', toggleSidebar);
        const fs = document.getElementById('filterSearch');
        if (fs) fs.addEventListener('input', applyFilter);
    });

    document.addEventListener('input', e => {
        if (e.target.classList.contains('reflection-textarea') || e.target.hasAttribute('contenteditable')) {
            saveToStorage();
        }
    });

    window.onload = init;
}
```

**Update `Standard1.html`:**

1. Add `data-col` attributes to the 4 vibe-column divs in the HTML. The current divs are:
```html
            <div class="vibe-column col-1" id="col-servant">
            <div class="vibe-column col-2" id="col-client">
            <div class="vibe-column col-3" id="col-team">
            <div class="vibe-column col-4" id="col-brand">
```
Change to add `data-col="servant"`, `data-col="client"`, etc. Keep the existing id.

2. Replace the entire `<script>` block (starting with `const defaultCards = {` and ending with `window.onload = init;`) plus the nav dropdown JS block that follows, with:
```html
<script src="apex-kanban.js"></script>
<script>
    initKanban({
        cols: ['servant', 'client', 'team', 'brand'],
        storageKey: 'apex_std1_state_v6',
        sidebarKey: 's1_sidebar_open',
        pageUrl: 'Standard1.html',
        defaultCards: {
            servant: [
                { id: 'vol-proj', title: '🙋♂🛠 Volunteering for a Project', brief: 'Evidence of stepping up to assist a peer or support a team goal.', link: '', reflection: '', isCustom: false },
                { id: 'peer-rev', title: '📝🤝 Constructive Peer Review', brief: 'Actionable, kind, and specific feedback on a teammate\'s work.', link: '', reflection: '', isCustom: false },
                { id: 'wilc-steward', title: '🧹🏫 WILC Stewardship', brief: 'Maintaining the professional environment (resetting rooms, guiding guests).', link: '', reflection: '', isCustom: false }
            ],
            client: [
                { id: 'c-email', title: '📧✍ Professional Email to Client', brief: 'Email with clear subject, 5 Ws and H, and specific next steps.', link: '', reflection: '', isCustom: false },
                { id: 'c-cal', title: '📅✅ Google Calendar Event', brief: 'Evidence of professional scheduling with all guests invited.', link: '', reflection: '', isCustom: false },
                { id: 'c-up', title: '🔄📈 Weekly Client Update', brief: 'Screenshot of project progress, roadblocks, and next steps.', link: '', reflection: '', isCustom: false }
            ],
            team: [
                { id: 'ind-tour', title: '🏭🗺 Organizing an Industry Tour', brief: 'Coordinating logistics or preparing team questions for a tour.', link: '', reflection: '', isCustom: false },
                { id: 'kan-champ', title: '📊📌 Kanban Board Champion', brief: 'Maintaining a Trello or Slack Canvas task list.', link: '', reflection: '', isCustom: false },
                { id: 'lead-stand', title: '🗣📋 Leading the Stand-Up', brief: 'Evidence of leading a daily scrum or team meeting.', link: '', reflection: '', isCustom: false }
            ],
            brand: [
                { id: 't-fair', title: '🎤🏫 Timberline Career Fair', brief: 'Communicating the APEX value proposition to a public audience.', link: '', reflection: '', isCustom: false },
                { id: 'pitch', title: '🗣👔 The Elevator Pitch', brief: 'Video or script of your 60-second professional introduction.', link: '', reflection: '', isCustom: false },
                { id: 'bus-casual', title: '👔✨ Business Casual Excellence', brief: 'Demonstrating professional appearance standards in client settings.', link: '', reflection: '', isCustom: false }
            ]
        }
    });
</script>
```

**Update `Standard5.html`:**

1. The CSS has `.instruction-text` — rename all occurrences to `.brief-text` in the CSS.

2. Replace the entire `<script>` block (starting with `const COLS = ['digital-comm'...` through `window.onload = init;`) plus any following nav-dropdown JS, with:
```html
<script src="apex-kanban.js"></script>
<script>
    initKanban({
        cols: ['digital-comm', 'prof-presence', 'task-mgmt', 'growth-net'],
        storageKey: 'apex_std5_v6',
        sidebarKey: 's5_sidebar_open',
        pageUrl: 'Standard5.html',
        defaultCards: {
            'digital-comm': [
                { id: 'slack-mastery', title: '✅💬 Slack Mastery', brief: 'Show evidence that you have configured your notifications to "Always" and actively use "Read & React" in team channels.', link: '', reflection: '', isCustom: false },
                { id: 'cc-protocol', title: '📧👥 The "CC" Protocol', brief: 'Capture a screenshot of an email sent to a client where you successfully CC\'d your instructor and partners.', link: '', reflection: '', isCustom: false }
            ],
            'prof-presence': [
                { id: 'handshake', title: '🤝👀 The First Impression', brief: 'Reflect on an interaction involving eye contact, body language, and a professional introduction.', link: '', reflection: '', isCustom: false },
                { id: 'dress-code', title: '👗👔 APEX Dress Code', brief: 'Provide a photo of your Outfit of the Day (OOTD) modeling APEX business casual standards.', link: '', reflection: '', isCustom: false }
            ],
            'task-mgmt': [
                { id: 'agenda', title: '📝⏱ The Pre-Meeting Agenda', brief: 'Provide a link to an agenda shared with your team and client *before* a scheduled meeting.', link: '', reflection: '', isCustom: false },
                { id: 'micro-deliverable', title: '🎯✅ Weekly Micro-Deliverable', brief: 'Evidence of moving a task from "Doing" to "Done" on your team\'s Trello board or Kanban.', link: '', reflection: '', isCustom: false }
            ],
            'growth-net': [
                { id: 'linkedin', title: '🔗🌐 LinkedIn Optimization', brief: 'Update your profile with a headshot, APEX headline, and "About" section featuring your Career Thesis.', link: '', reflection: '', isCustom: false },
                { id: 'feedback', title: '🔄📉 The Feedback Loop', brief: 'Show how you applied constructive criticism from a client or peer to improve a product.', link: '', reflection: '', isCustom: false }
            ]
        }
    });
</script>
```

---

## Important notes

- Read any file before editing it
- The Standard1 and Standard5 scripts are large — make sure you capture the FULL script block when replacing, including the nav-dropdown JS that appears after `window.onload = init;`
- For S1, the `<script>` block starts with `const defaultCards = {` and the entire section (including any following nav dropdown scripts before `</body>`) should be replaced
- For S5, the `<script>` block starts with `const COLS = [`
- When replacing S4BSU individual functions, use exact string matches to avoid touching surrounding code
- The `removeFromQueue` function in the shared file should re-read localStorage after saving instead of using the filtered array directly (to stay consistent)
- Do NOT modify Standard4BSU.html's CSS or HTML structure — only the JS functions and window.onload handler


1	<!DOCTYPE html>
2	<html lang="en">
3	<head>
4	    <meta charset="UTF-8">
5	    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6	    <title>APEX Portfolio | Standard 1: Servant Leadership</title>
7	    <style>
8	        :root {
9	            --primary: #500778;    /* APEX Purple */
10	            --secondary: #c4d600;  /* APEX Lime Gold */
11	            --accent: #4b4f54;     /* APEX Dark Gray */
12	            --bg: #F3F4F6;
13	            --card-bg: #FFFFFF;
14	            --text-main: #1F2937;
15	            --text-sub: #6B7280;
16	            --exceeds: #059669;
17	            --secure: #2563EB;
18	            --developing: #D97706;
19	            --beginning: #DC2626;
20	            --success: #10B981;
21	            --info-bg: #E0F2FE;
22	            --info-border: #7DD3FC;
23	            --nav-height: 70px;
24	            --sidebar-width: 300px;
25	        }
26	        body { font-family: 'Inter', 'Segoe UI', sans-serif; background-color: var(--bg); color: var(--text-main); margin: 0; padding-top: var(--nav-height); padding-bottom: 40px; padding-left: 20px; padding-right: 20px; transition: padding-left 0.3s; }
27	        .body-sidebar-open { padding-left: calc(var(--sidebar-width) + 20px); }
28
29	        /* --- GLOBAL NAVIGATION --- */
30	        .main-nav {
31	            background: var(--primary);
32	            height: var(--nav-height);
33	            width: 100%;
34	            position: fixed;
35	            top: 0;
36	            left: 0;
37	            z-index: 1000;
38	            display: flex;
39	            align-items: center;
40	            border-bottom: 4px solid var(--secondary);
41	            box-shadow: 0 2px 12px rgba(0,0,0,0.15);
42	        }
43	        .nav-container {
44	            max-width: 1200px;
45	            margin: 0 auto;
46	            width: 100%;
47	            display: flex;
48	            justify-content: space-between;
49	            align-items: center;
50	            padding: 0 20px;
51	        }
52	        .nav-logo-link {
53	            display: flex;
54	            align-items: center;
55	            text-decoration: none;
56	            height: calc(var(--nav-height) - 15px);
57	        }
58	        .nav-logo-img {
59	            height: 100%;
60	            width: auto;
61	        }
62	        .nav-links {
63	            display: flex;
64	            gap: 10px;
65	        }
66	        .nav-links a {
67	            color: rgba(255,255,255,0.9);
68	            text-decoration: none;
69	            font-size: 0.8rem;
70	            font-weight: 700;
71	            padding: 8px 14px;
72	            border-radius: 6px;
73	            text-transform: uppercase;
74	            letter-spacing: 0.03em;
75	            transition: all 0.2s;
76	        }
77	        .nav-links a:hover {
78	            color: var(--secondary);
79	            background: rgba(255,255,255,0.1);
80	        }
81	        .nav-links a.active {
82	            background: var(--secondary);
83	            color: var(--primary);
84	        }
85	        /* --- NAV DROPDOWNS --- */
86	        .nav-dropdown { position: relative; }
87	        .nav-drop-btn { color: rgba(255,255,255,0.9); background: none; border: none; font-size: 0.8rem; font-weight: 700; padding: 8px 14px; border-radius: 6px; text-transform: uppercase; letter-spacing: 0.03em; transition: all 0.2s; cursor: pointer; font-family: inherit; }
88	        .nav-dropdown:hover .nav-drop-btn { color: var(--secondary); background: rgba(255,255,255,0.1); }
89	        .nav-drop-btn.active { background: var(--secondary); color: var(--primary); }
90	        .nav-dropdown:hover .nav-drop-btn.active { background: var(--secondary); color: var(--primary); opacity: 0.85; }
91	        .nav-drop-menu { display: none; position: absolute; top: calc(100% + 8px); left: 0; background: white; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.15); min-width: 210px; z-index: 1001; padding: 6px 0; border: 1px solid #E5E7EB; }
92	        .nav-dropdown:hover .nav-drop-menu { display: block; }
93	        .nav-dropdown::after { content: ""; position: absolute; top: 100%; left: 0; width: 100%; height: 8px; }
94	        .nav-drop-menu a { display: block; padding: 9px 16px; color: #1F2937; text-decoration: none; font-size: 0.82rem; font-weight: 600; transition: background 0.15s; white-space: nowrap; }
95	        .nav-drop-menu a:hover { background: #F3F4F6; color: var(--primary); }
96	        .nav-drop-menu a.active { color: var(--primary); font-weight: 800; background: #F5F3FF; }
97	        .container { max-width: 1400px; margin: 0 auto; }
98
99	        header { text-align: center; margin-bottom: 30px; border-bottom: 1px solid #D1D5DB; padding-bottom: 20px; }
100	        h1 { font-size: 2.2rem; color: var(--primary); margin-bottom: 5px; font-weight: 800; }
101	        .subtitle { font-size: 1rem; color: var(--text-sub); max-width: 600px; margin: 0 auto; }
102
103	        /* Proficiency Indicators Styling */
104	        .indicators-box { background: white; border: 2px solid var(--secondary); border-radius: 12px; margin-bottom: 25px; overflow: hidden; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); }
105	        .indicators-box summary { padding: 15px 20px; background: #FFFBEB; cursor: pointer; font-weight: 800; color: var(--primary); display: flex; align-items: center; gap: 10px; }
106	        .indicators-content { padding: 20px; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; font-size: 0.85rem; line-height: 1.5; }
107
108	        .indicator-item { padding: 15px; border-radius: 8px; background: #F9FAFB; border-top: 4px solid #DDD; }
109	        .indicator-item strong { display: block; font-size: 1rem; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.05em; }
110
111	        .lvl-exceeds { border-top-color: var(--exceeds); }
112	        .lvl-exceeds strong { color: var(--exceeds); }
113	        .lvl-secure { border-top-color: var(--secure); }
114	        .lvl-secure strong { color: var(--secure); }
115	        .lvl-developing { border-top-color: var(--developing); }
116	        .lvl-developing strong { color: var(--developing); }
117	        .lvl-beginning { border-top-color: var(--beginning); }
118	        .lvl-beginning strong { color: var(--beginning); }
119
120	        /* Strand Info Box */
121	        .strand-info {
122	            background: var(--info-bg);
123	            border: 1px solid var(--info-border);
124	            border-radius: 12px;
125	            padding: 20px;
126	            margin-bottom: 40px;
127	            display: flex;
128	            flex-wrap: wrap;
129	            gap: 30px;
130	            align-items: center;
131	        }
132	        .strand-info-text { flex: 2; min-width: 300px; }
133	        .strand-info-text h2 { margin: 0 0 10px 0; font-size: 1.2rem; color: #0369A1; }
134	        .strand-info-text p { margin: 0; font-size: 0.95rem; line-height: 1.4; color: #0C4A6E; }
135	        .strand-steps { flex: 3; min-width: 300px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
136	        .step { background: rgba(255,255,255,0.5); padding: 12px; border-radius: 8px; font-size: 0.85rem; border: 1px solid rgba(255,255,255,0.8); }
137	        .step strong { color: var(--accent); display: block; margin-bottom: 4px; }
138
139	        /* Grid & Columns */
140	        .vibe-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 25px; align-items: start; }
141	        .vibe-column { background: var(--card-bg); border-radius: 12px; padding: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.05); border-top: 8px solid var(--accent); position: relative; padding-bottom: 80px; min-height: 400px; }
142
143	        .col-1 { border-top-color: #10B981; }
144	        .col-2 { border-top-color: #3B82F6; }
145	        .col-3 { border-top-color: #8B5CF6; }
146	        .col-4 { border-top-color: #F59E0B; }
147
148	        .vibe-title { font-size: 1.3rem; font-weight: 800; margin-bottom: 20px; color: var(--primary); display: flex; align-items: center; gap: 10px; }
149
150	        /* Card Styling */

... [357 lines truncated] ...