← back to Sdcc Mockups
generate.js
427 lines
// Generates 10 distinct front-page mockups for SDCC, each grounded in the audit suggestions.
// Run: node generate.js → emits 10 HTML files in html/ + index.html gallery
const fs = require('fs');
const path = require('path');
const SHARED_HEAD = (title, theme) => `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${title} — SDCC Mockup</title>
<meta name="description" content="Mockup design for studentdebtcrisis.org — ${title}">
<meta property="og:title" content="${title} — Student Debt Crisis Center">
<meta property="og:description" content="A people-powered movement of 2 million borrowers fighting for student debt cancellation.">
<meta property="og:type" content="website">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${theme.fonts}" rel="stylesheet">
<style>
${theme.css}
</style>
</head>`;
// Real SDCC content snippets pulled from the crawl
const ARTICLES = [
{ tag: 'SAVE LAWSUIT', title: 'Student Debtors Ask Court to Reconsider Decision to Eliminate the SAVE Plan', date: 'Apr 30, 2026' },
{ tag: 'PRESS', title: 'Department of Education Creates Chaos, Announces Transfer of Loan Portfolio to U.S. Treasury', date: 'Apr 22, 2026' },
{ tag: 'POLICY', title: 'Navigating Student Loan Changes: What Borrowers Need To Know', date: 'Apr 15, 2026' },
{ tag: 'LIVE', title: 'Little Hoover Commission Convenes Historic Hearing on California Institutional Debt', date: 'Mar 28, 2026' },
{ tag: 'EVENT', title: 'Fifth Annual State of Student Debt Summit', date: 'Feb 14, 2026' },
];
const STATS = {
totalDebt: '$1.77T',
borrowers: '45M',
supporters: '2M+',
defaultPct: '23%',
pslfApproved: '$76.2B',
};
const FOOTER_BASE = `
<footer class="ft" role="contentinfo">
<div class="ft-grid">
<div>
<div class="ft-logo">SDCC</div>
<p class="ft-tag">Student Debt Crisis Center is a 501(c)(3) non-profit. EIN 81-2468531. Founded 2012.</p>
<p class="ft-addr">1875 K Street NW, Suite 200 · Washington, DC 20006 · (202) 555-0117</p>
</div>
<div><h4>Issues</h4><a>SAVE Plan</a><a>PSLF</a><a>Income-Driven Repayment</a><a>Loan Forgiveness</a><a>Default & Collection</a></div>
<div><h4>Take Action</h4><a>Sign petition</a><a>Email your rep</a><a>Share your story</a><a>Volunteer</a><a>Donate</a></div>
<div><h4>About</h4><a>Mission & history</a><a>Team</a><a>Press</a><a>Careers</a><a>Privacy</a><a>Accessibility</a></div>
</div>
<div class="ft-bot">© 2026 Student Debt Crisis Center · Charity Navigator 4★ · GuideStar Platinum</div>
</footer>`;
// ---------- 10 MOCKUPS ----------
const MOCKUPS = [];
// ============================================================
// 1. DEBT CLOCK — minimal, urgency-driving, single-CTA
// ============================================================
MOCKUPS.push({
id: '01-debt-clock',
title: 'Debt Clock',
pitch: 'Live national debt counter dominates the hero. One number, one CTA. Journalism-grade restraint. Press citations expected to follow.',
emphasis: ['live FRED debt counter', 'minimal hero', 'single primary CTA', 'borrower stat ribbon'],
theme: {
fonts: 'https://fonts.googleapis.com/css2?family=Instrument+Serif&family=Inter:wght@400;500;600;700&display=swap',
css: `
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#0a0a0a;color:#fff;-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
.skip{position:absolute;top:-40px;left:0;background:#fff;color:#000;padding:8px;z-index:100}
.skip:focus{top:0}
.hd{position:sticky;top:0;background:rgba(10,10,10,.95);backdrop-filter:blur(12px);border-bottom:1px solid rgba(255,255,255,.08);z-index:50}
.hd-inner{max-width:1280px;margin:0 auto;padding:18px 32px;display:flex;align-items:center;justify-content:space-between}
.lg{font-family:'Instrument Serif',serif;font-size:24px;letter-spacing:-.02em}
.nv{display:flex;gap:32px;font-size:14px;color:#bbb}
.nv a:hover{color:#fff}
.search{position:relative;flex:1;max-width:280px;margin:0 32px}
.search input{width:100%;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);color:#fff;padding:8px 12px 8px 36px;border-radius:6px;font-size:13px}
.search svg{position:absolute;left:11px;top:50%;transform:translateY(-50%);opacity:.5}
.donate{background:#f5d142;color:#000;padding:10px 22px;border-radius:999px;font-weight:600;font-size:14px}
.hero{max-width:1280px;margin:0 auto;padding:72px 32px 48px;text-align:center}
.hero-eyebrow{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:#f5d142;margin-bottom:32px}
.clock{font-family:'Instrument Serif',serif;font-size:130px;line-height:1;letter-spacing:-.04em;font-weight:400;background:linear-gradient(180deg,#fff 30%,#f5d142);-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:8px}
.clock-sub{font-size:14px;color:#888;letter-spacing:.04em}
.clock-pulse{display:inline-block;width:8px;height:8px;background:#22c55e;border-radius:50%;margin-right:8px;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}
.h1{font-family:'Instrument Serif',serif;font-size:72px;line-height:1.05;letter-spacing:-.02em;margin:48px 0 20px;font-weight:400;max-width:900px;margin-left:auto;margin-right:auto}
.h1 em{font-style:italic;color:#f5d142}
.lede{font-size:19px;color:#bbb;line-height:1.5;max-width:580px;margin:0 auto 40px}
.cta{display:inline-flex;align-items:center;gap:8px;background:#fff;color:#000;padding:18px 40px;border-radius:4px;font-size:16px;font-weight:600}
.ctas{display:flex;gap:16px;justify-content:center}
.cta.ghost{background:transparent;color:#fff;border:1px solid rgba(255,255,255,.3)}
.stats{max-width:1280px;margin:48px auto;padding:0 32px;display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.08);border-radius:8px;overflow:hidden}
.stat{background:#0a0a0a;padding:32px}
.stat-n{font-family:'Instrument Serif',serif;font-size:42px;letter-spacing:-.02em;margin-bottom:4px}
.stat-l{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:.08em}
.recent{max-width:1280px;margin:96px auto;padding:0 32px}
.recent h2{font-family:'Instrument Serif',serif;font-size:42px;letter-spacing:-.02em;margin-bottom:32px;font-weight:400}
.recent-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
.card{background:#161616;border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:28px;transition:border-color .2s}
.card:hover{border-color:rgba(245,209,66,.5)}
.card-tag{display:inline-block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:#f5d142;margin-bottom:12px}
.card-title{font-family:'Instrument Serif',serif;font-size:20px;line-height:1.3;margin-bottom:14px;font-weight:400}
.card-date{font-size:12px;color:#666}
.ft{background:#000;border-top:1px solid rgba(255,255,255,.08);padding:64px 32px 32px;color:#888;font-size:13px}
.ft-grid{max-width:1280px;margin:0 auto;display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px}
.ft-logo{font-family:'Instrument Serif',serif;font-size:32px;color:#fff;margin-bottom:14px}
.ft-tag{margin-bottom:8px;line-height:1.5}
.ft-addr{font-size:12px}
.ft h4{color:#fff;font-size:13px;margin-bottom:14px;letter-spacing:.04em}
.ft a{display:block;padding:4px 0;color:#888;font-size:13px}
.ft a:hover{color:#fff}
.ft-bot{max-width:1280px;margin:48px auto 0;padding-top:32px;border-top:1px solid rgba(255,255,255,.08);text-align:center;color:#555;font-size:12px}
`,
body: `
<a class="skip" href="#m">Skip to main content</a>
<header class="hd"><div class="hd-inner">
<div class="lg">SDCC</div>
<nav class="nv" aria-label="Primary"><a>Issues</a><a>Stories</a><a>Tools</a><a>Press</a><a>About</a></nav>
<label class="search"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg><input type="search" placeholder="Search policies, articles, tools…" aria-label="Search"></label>
<a class="donate">Donate →</a>
</div></header>
<main id="m" role="main">
<section class="hero">
<div class="hero-eyebrow"><span class="clock-pulse"></span>Live · updated from Federal Reserve G.19</div>
<div class="clock" aria-label="Total US student loan debt">$1,776,432,118,944</div>
<div class="clock-sub">in outstanding US student loan debt — and rising $2,762 every second</div>
<h1 class="h1">A people-powered movement to <em>cancel student debt</em>.</h1>
<p class="lede">Two million supporters. Fifteen years on Capitol Hill. We're the only borrower advocacy org tracking the full debt fight in real time.</p>
<div class="ctas">
<a class="cta">Add your name <span>→</span></a>
<a class="cta ghost">Get monthly briefings</a>
</div>
</section>
<section class="stats" aria-label="Key statistics">
<div class="stat"><div class="stat-n">${STATS.borrowers}</div><div class="stat-l">Federal borrowers</div></div>
<div class="stat"><div class="stat-n">${STATS.supporters}</div><div class="stat-l">SDCC supporters</div></div>
<div class="stat"><div class="stat-n">${STATS.defaultPct}</div><div class="stat-l">In default or collections</div></div>
<div class="stat"><div class="stat-n">${STATS.pslfApproved}</div><div class="stat-l">PSLF discharged since 2021</div></div>
</section>
<section class="recent">
<h2>Recently in the fight</h2>
<div class="recent-grid">
${ARTICLES.slice(0,3).map(a=>`<article class="card"><span class="card-tag">${a.tag}</span><h3 class="card-title">${a.title}</h3><div class="card-date">${a.date}</div></article>`).join('')}
</div>
</section>
</main>
${FOOTER_BASE}`
}
});
// ============================================================
// 2. STORY WALL — borrower faces dominate
// ============================================================
MOCKUPS.push({
id: '02-story-wall',
title: 'Story Wall',
pitch: 'Real borrower portraits are the entire above-the-fold. Each tile is a name, a state, a dollar amount, an outcome. Donations and petitions follow stories — not the other way around.',
emphasis: ['borrower-faces hero grid', 'name + state + balance', 'video-story badges', 'CTA from each tile'],
theme: {
fonts: 'https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600&family=Inter:wght@400;500;600&display=swap',
css: `
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#fffaf2;color:#1a0e08}
a{color:inherit;text-decoration:none}
.skip{position:absolute;top:-40px;left:0;background:#1a0e08;color:#fff;padding:8px;z-index:100}.skip:focus{top:0}
.hd{background:#fffaf2;border-bottom:1px solid rgba(26,14,8,.1);position:sticky;top:0;z-index:50}
.hd-inner{max-width:1320px;margin:0 auto;padding:20px 32px;display:flex;align-items:center;justify-content:space-between}
.lg{font-family:'Fraunces',serif;font-size:28px;font-weight:600;letter-spacing:-.02em}
.nv{display:flex;gap:28px;font-size:14px}
.search{position:relative;width:240px}
.search input{width:100%;background:rgba(26,14,8,.04);border:1px solid rgba(26,14,8,.12);padding:9px 12px 9px 36px;border-radius:24px;font-size:13px;color:#1a0e08}
.search svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);opacity:.5}
.donate{background:#c2410c;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;font-size:14px}
.hero{max-width:1320px;margin:0 auto;padding:48px 32px 32px}
.hero-top{display:flex;justify-content:space-between;align-items:end;margin-bottom:32px;gap:48px}
.hero-h1{font-family:'Fraunces',serif;font-size:88px;line-height:.95;letter-spacing:-.03em;font-weight:400;max-width:780px}
.hero-h1 em{font-style:italic;color:#c2410c}
.hero-meta{text-align:right;font-size:14px;color:#5b4439;max-width:280px}
.hero-meta strong{font-family:'Fraunces',serif;font-size:32px;display:block;color:#1a0e08;margin-bottom:8px;font-weight:600}
.wall{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:280px;gap:14px;max-width:1320px;margin:0 auto;padding:0 32px}
.tile{position:relative;border-radius:14px;overflow:hidden;cursor:pointer;display:flex;flex-direction:column;justify-content:flex-end;padding:24px;color:#fff;background-size:cover;background-position:center;background-color:#3b2419;box-shadow:0 4px 20px rgba(0,0,0,.06)}
.tile::before{content:'';position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 35%,rgba(0,0,0,.7) 100%)}
.tile-badge{position:absolute;top:18px;left:18px;background:rgba(255,255,255,.15);backdrop-filter:blur(8px);padding:5px 10px;border-radius:99px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;z-index:2}
.tile-video{position:absolute;top:18px;right:18px;width:32px;height:32px;background:rgba(255,255,255,.15);backdrop-filter:blur(8px);border-radius:50%;display:flex;align-items:center;justify-content:center;z-index:2}
.tile-content{position:relative;z-index:2}
.tile-name{font-family:'Fraunces',serif;font-size:22px;line-height:1.1;font-weight:600;margin-bottom:6px}
.tile-loc{font-size:12px;opacity:.85;margin-bottom:8px;text-transform:uppercase;letter-spacing:.06em}
.tile-amt{font-family:'Fraunces',serif;font-size:28px;font-weight:600;line-height:1;margin-bottom:4px}
.tile-out{font-size:11px;opacity:.85}
.tile-feat{grid-column:span 2;grid-row:span 2}
.t1{background-image:linear-gradient(135deg,#3b2419,#7c2d12)}
.t2{background-image:linear-gradient(135deg,#451a03,#9a3412)}
.t3{background-image:linear-gradient(135deg,#7c2d12,#c2410c)}
.t4{background-image:linear-gradient(135deg,#1c1917,#44403c)}
.t5{background-image:linear-gradient(135deg,#431407,#9a3412)}
.t6{background-image:linear-gradient(135deg,#451a03,#78350f)}
.t7{background-image:linear-gradient(135deg,#1c1917,#7c2d12)}
.t8{background-image:linear-gradient(135deg,#7c2d12,#451a03)}
.cta-band{max-width:1320px;margin:48px auto;padding:48px 32px;background:#1a0e08;color:#fffaf2;border-radius:18px;display:grid;grid-template-columns:2fr 1fr;gap:48px;align-items:center}
.cta-band h2{font-family:'Fraunces',serif;font-size:42px;line-height:1.05;letter-spacing:-.02em;font-weight:400}
.cta-band p{margin-top:14px;color:#d4c5b9;font-size:16px;line-height:1.5}
.cta-form{display:flex;gap:8px}
.cta-form input{flex:1;padding:14px 18px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);color:#fff;border-radius:8px;font-size:14px}
.cta-form button{background:#c2410c;color:#fff;border:0;padding:14px 24px;border-radius:8px;font-weight:600;cursor:pointer}
${footerCss('#fffaf2','#1a0e08','#5b4439')}
`,
body: `
<a class="skip" href="#m">Skip to main content</a>
<header class="hd"><div class="hd-inner">
<div class="lg">SDCC <span style="font-style:italic;font-weight:400;color:#c2410c">stories</span></div>
<nav class="nv" aria-label="Primary"><a>Stories</a><a>Issues</a><a>Action</a><a>Press</a><a>About</a></nav>
<label class="search"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg><input type="search" placeholder="Search stories"></label>
<a class="donate">Donate</a>
</div></header>
<main id="m">
<section class="hero">
<div class="hero-top">
<h1 class="hero-h1">Two million people. <em>Two million stories.</em> One movement.</h1>
<div class="hero-meta"><strong>${STATS.totalDebt}</strong>owed by ${STATS.borrowers} federal borrowers. Behind every dollar is a person we know by name.</div>
</div>
</section>
<section class="wall" aria-label="Borrower stories">
<article class="tile tile-feat t1"><span class="tile-badge">PSLF VICTORY</span><div class="tile-video">▶</div><div class="tile-content"><div class="tile-name">Marcus Williams</div><div class="tile-loc">Oakland, CA · Public school teacher</div><div class="tile-amt">$87,400 discharged</div><div class="tile-out">After 121 qualifying payments + 9 years of denials</div></div></article>
<article class="tile t2"><span class="tile-badge">SAVE</span><div class="tile-content"><div class="tile-name">Diana Reyes</div><div class="tile-loc">El Paso, TX</div><div class="tile-amt">$54k owed</div></div></article>
<article class="tile t3"><span class="tile-badge">DEFAULT</span><div class="tile-content"><div class="tile-name">James Holloway</div><div class="tile-loc">Erie, PA</div><div class="tile-amt">$31k</div><div class="tile-out">Wages garnished since 2018</div></div></article>
<article class="tile t4"><span class="tile-badge">PARENT PLUS</span><div class="tile-video">▶</div><div class="tile-content"><div class="tile-name">Linda Chen</div><div class="tile-loc">Sacramento, CA</div><div class="tile-amt">$118k</div></div></article>
<article class="tile t5"><span class="tile-badge">IDR</span><div class="tile-content"><div class="tile-name">Tomás Espinoza</div><div class="tile-loc">Chicago, IL</div><div class="tile-amt">$72k</div></div></article>
<article class="tile t6"><span class="tile-badge">CLOSED SCHOOL</span><div class="tile-content"><div class="tile-name">Aisha Brown</div><div class="tile-loc">Tampa, FL</div><div class="tile-amt">$24k discharged</div></div></article>
<article class="tile t7"><span class="tile-badge">FIRST GEN</span><div class="tile-video">▶</div><div class="tile-content"><div class="tile-name">Devontae Smith</div><div class="tile-loc">Memphis, TN</div><div class="tile-amt">$96k</div></div></article>
<article class="tile t8"><span class="tile-badge">RURAL</span><div class="tile-content"><div class="tile-name">Sarah & Mike Torres</div><div class="tile-loc">Bisbee, AZ</div><div class="tile-amt">$203k combined</div></div></article>
</section>
<section class="cta-band">
<div><h2>Tell us your story.</h2><p>Stories drive every petition we file, every press conference we hold, every meeting on the Hill. Photo and consent walk-through takes 5 minutes.</p></div>
<form class="cta-form" aria-label="Story signup"><input type="email" placeholder="email@example.com" required><button>Begin →</button></form>
</section>
</main>
${FOOTER_BASE}`
}
});
// ============================================================
// 3. ACTIVIST — petition counts + Congress scoreboard
// ============================================================
MOCKUPS.push({
id: '03-activist',
title: 'Activist Tracker',
pitch: 'Movement energy, not policy detachment. Live signature counts on the active petition. A scoreboard of how every senator just voted. Urgency is the design language.',
emphasis: ['active-campaign progress bar', 'Congress vote scoreboard', 'sticky action CTA', 'cumulative wins counter'],
theme: {
fonts: 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap',
css: `
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,sans-serif;background:#fff;color:#0a1929}
a{color:inherit;text-decoration:none}
.skip{position:absolute;top:-40px;left:0;background:#0a1929;color:#fff;padding:8px;z-index:100}.skip:focus{top:0}
.alert{background:#dc2626;color:#fff;padding:10px 0;text-align:center;font-size:13px;font-weight:500}
.alert strong{font-weight:700}
.hd{background:#fff;border-bottom:2px solid #0a1929;position:sticky;top:0;z-index:50}
.hd-inner{max-width:1320px;margin:0 auto;padding:18px 32px;display:flex;align-items:center;justify-content:space-between;gap:32px}
.lg{font-family:'Space Grotesk',sans-serif;font-size:24px;font-weight:700;letter-spacing:-.02em}
.lg span{color:#dc2626}
.nv{display:flex;gap:28px;font-size:14px;font-weight:500}
.nv a{padding:8px 0;border-bottom:2px solid transparent}
.nv a.active{border-color:#dc2626}
.search{position:relative;width:220px}
.search input{width:100%;background:#f3f4f6;border:0;padding:9px 12px 9px 36px;border-radius:6px;font-size:13px}
.search svg{position:absolute;left:11px;top:50%;transform:translateY(-50%);opacity:.5}
.donate{background:#dc2626;color:#fff;padding:11px 22px;font-weight:700;font-size:14px;letter-spacing:.02em;text-transform:uppercase}
.campaign{max-width:1320px;margin:0 auto;padding:48px 32px}
.camp-tag{display:inline-block;background:#fef2f2;color:#dc2626;padding:6px 14px;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;margin-bottom:16px}
.camp-h1{font-family:'Space Grotesk',sans-serif;font-size:64px;line-height:1.04;font-weight:700;letter-spacing:-.025em;max-width:920px;margin-bottom:14px}
.camp-h1 mark{background:#fef08a;padding:0 4px}
.camp-sub{font-size:18px;color:#475569;max-width:680px;margin-bottom:32px;line-height:1.5}
.progress-card{background:#0a1929;color:#fff;padding:32px;border-radius:0;display:grid;grid-template-columns:1fr auto;gap:32px;align-items:center}
.progress-card-l{flex:1}
.prog-label{font-size:12px;color:#64748b;letter-spacing:.1em;text-transform:uppercase;margin-bottom:8px}
.prog-count{font-family:'Space Grotesk',sans-serif;font-size:56px;font-weight:700;line-height:1;margin-bottom:6px}
.prog-count em{color:#facc15;font-style:normal}
.prog-target{font-size:14px;color:#94a3b8}
.prog-bar{height:14px;background:rgba(255,255,255,.1);border-radius:0;margin-top:20px;overflow:hidden;position:relative}
.prog-bar::after{content:'';position:absolute;left:0;top:0;height:100%;width:84.2%;background:linear-gradient(90deg,#facc15,#fef08a)}
.prog-cta{display:flex;flex-direction:column;gap:10px}
.prog-cta a{background:#facc15;color:#0a1929;padding:18px 32px;font-weight:700;font-size:15px;text-align:center;letter-spacing:.02em;text-transform:uppercase}
.prog-cta a.share{background:transparent;color:#facc15;border:1px solid #facc15;padding:14px 32px;font-size:13px}
.scoreboard{max-width:1320px;margin:64px auto;padding:0 32px}
.scoreboard h2{font-family:'Space Grotesk',sans-serif;font-size:36px;font-weight:700;margin-bottom:8px;letter-spacing:-.015em}
.scoreboard-sub{color:#475569;margin-bottom:28px;font-size:14px}
.score-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.score-side{background:#fff;border:1px solid #e2e8f0;padding:24px;border-radius:0;border-left:6px solid}
.score-side.yes{border-left-color:#16a34a}
.score-side.no{border-left-color:#dc2626}
.score-h{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}
.score-h h3{font-family:'Space Grotesk',sans-serif;font-size:22px;font-weight:700}
.score-n{font-family:'Space Grotesk',sans-serif;font-size:42px;font-weight:700}
.yes .score-n{color:#16a34a}
.no .score-n{color:#dc2626}
.score-list{font-size:13px;color:#0a1929;line-height:1.7}
.score-list span{display:inline-block;background:#f3f4f6;padding:3px 10px;border-radius:0;margin:3px 4px 3px 0;font-weight:500}
.wins{max-width:1320px;margin:64px auto;padding:32px;background:#0a1929;color:#fff;text-align:center}
.wins h2{font-family:'Space Grotesk',sans-serif;font-size:48px;font-weight:700;margin-bottom:8px}
.wins p{color:#94a3b8;margin-bottom:32px}
.wins-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
.win{padding:24px}
.win-n{font-family:'Space Grotesk',sans-serif;font-size:48px;font-weight:700;color:#facc15;line-height:1}
.win-l{font-size:13px;color:#cbd5e1;margin-top:8px;text-transform:uppercase;letter-spacing:.06em}
${footerCss('#fff','#0a1929','#475569')}
`,
body: `
<a class="skip" href="#m">Skip to main content</a>
<div class="alert"><strong>SENATE VOTES THURSDAY</strong> · S. 1247 — Student Borrower Protection Act · <a style="text-decoration:underline">tell your senators →</a></div>
<header class="hd"><div class="hd-inner">
<div class="lg">SDCC<span>.</span></div>
<nav class="nv" aria-label="Primary"><a class="active">Action</a><a>Issues</a><a>Stories</a><a>Press</a><a>Tools</a></nav>
<label class="search"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg><input type="search" placeholder="Search"></label>
<a class="donate">Donate now</a>
</div></header>
<main id="m">
<section class="campaign">
<div class="camp-tag">● Live Campaign · 4 days left</div>
<h1 class="camp-h1">Tell the Senate to <mark>protect 8 million SAVE borrowers</mark> before Thursday's vote.</h1>
<p class="camp-sub">If S. 1247 fails, the Department of Education plans to transfer the loan portfolio to Treasury. Your signature reaches your senators within 6 minutes via the Senate Daily Constituent Tracker.</p>
<div class="progress-card">
<div class="progress-card-l">
<div class="prog-label">Signatures collected</div>
<div class="prog-count"><em>842,113</em></div>
<div class="prog-target">of 1,000,000 by Thursday 6:00 PM ET</div>
<div class="prog-bar"></div>
</div>
<div class="prog-cta">
<a>Add your name →</a>
<a class="share">Share with 5 friends</a>
</div>
</div>
</section>
<section class="scoreboard">
<h2>Where every senator stands.</h2>
<p class="scoreboard-sub">Updated 6 hours ago · Source: Office records, public statements, prior PROSPER Act vote</p>
<div class="score-grid">
<div class="score-side yes"><div class="score-h"><h3>Committed YES</h3><div class="score-n">47</div></div><div class="score-list"><span>Warren (MA)</span><span>Sanders (VT)</span><span>Booker (NJ)</span><span>Markey (MA)</span><span>Padilla (CA)</span><span>Schiff (CA)</span><span>Murray (WA)</span><span>+40 more</span></div></div>
<div class="score-side no"><div class="score-h"><h3>Committed NO</h3><div class="score-n">38</div></div><div class="score-list"><span>Cruz (TX)</span><span>Cornyn (TX)</span><span>Tuberville (AL)</span><span>Cotton (AR)</span><span>Hawley (MO)</span><span>+33 more</span></div></div>
</div>
<p style="margin-top:20px;font-size:14px;color:#475569"><strong>15 senators undecided.</strong> Find your senator →</p>
</section>
<section class="wins">
<h2>What we've won.</h2>
<p>Since 2012 — every dollar discharged because of borrower advocacy.</p>
<div class="wins-grid">
<div class="win"><div class="win-n">$76.2B</div><div class="win-l">PSLF discharged</div></div>
<div class="win"><div class="win-n">$28B</div><div class="win-l">Closed-school relief</div></div>
<div class="win"><div class="win-n">8M</div><div class="win-l">SAVE plan enrolled</div></div>
<div class="win"><div class="win-n">38</div><div class="win-l">Press victories</div></div>
</div>
</section>
</main>
${FOOTER_BASE}`
}
});
// helper: footer CSS variants
function footerCss(bg, fg, mute) {
return `
.ft{background:${bg==='#fff'?'#f9fafb':bg};border-top:1px solid rgba(0,0,0,.08);padding:64px 32px 32px;color:${mute};font-size:13px}
.ft-grid{max-width:1320px;margin:0 auto;display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px}
.ft-logo{font-family:'Fraunces',serif;font-size:28px;color:${fg};margin-bottom:14px;font-weight:600}
.ft-tag{margin-bottom:8px;line-height:1.5}
.ft-addr{font-size:12px}
.ft h4{color:${fg};font-size:13px;margin-bottom:14px;letter-spacing:.04em}
.ft a{display:block;padding:4px 0;color:${mute};font-size:13px}
.ft a:hover{color:${fg}}
.ft-bot{max-width:1320px;margin:48px auto 0;padding-top:32px;border-top:1px solid rgba(0,0,0,.08);text-align:center;color:${mute};font-size:12px}
`;
}
// Skipping the rest of the file content for brevity in this stage-1 — generator continues with mockups 4-10
// Will be filled in via additional Edit calls
// Write all mockups
const outDir = path.join(__dirname, 'html');
fs.mkdirSync(outDir, { recursive: true });
for (const m of MOCKUPS) {
const html = SHARED_HEAD(m.title, m.theme) + '<body>' + m.theme.body + '</body></html>';
fs.writeFileSync(path.join(outDir, m.id + '.html'), html);
console.log(`✓ ${m.id} (${m.title}) — ${html.length} bytes`);
}
// Write index gallery
const indexHTML = `<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>SDCC Mockup Gallery</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,sans-serif;background:#0a0a0a;color:#fff;padding:48px 32px}
h1{font-family:Georgia,serif;font-size:42px;margin-bottom:8px;font-weight:400}
.sub{color:#888;margin-bottom:48px}
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:24px;max-width:1320px;margin:0 auto}
.card{background:#161616;border:1px solid rgba(255,255,255,.1);border-radius:12px;overflow:hidden;transition:border-color .2s}
.card:hover{border-color:rgba(255,255,255,.3)}
.card-h{padding:20px 24px;border-bottom:1px solid rgba(255,255,255,.08)}
.card-n{font-size:11px;color:#888;letter-spacing:.1em;text-transform:uppercase}
.card-t{font-family:Georgia,serif;font-size:24px;margin:4px 0 8px;font-weight:400}
.card-p{color:#aaa;font-size:14px;line-height:1.5}
.card-tags{margin-top:14px;display:flex;flex-wrap:wrap;gap:6px}
.tag{background:rgba(255,255,255,.08);padding:3px 10px;border-radius:99px;font-size:11px;color:#ccc}
.card-iframe{width:100%;height:500px;border:0;background:#fff}
.card-foot{padding:14px 24px;display:flex;gap:12px}
.btn{padding:7px 14px;border-radius:6px;font-size:13px;text-decoration:none;color:#fff;background:rgba(255,255,255,.1)}
.btn:hover{background:rgba(255,255,255,.2)}
.btn-fig{background:#a855f7}
</style></head><body>
<h1>SDCC Front-Page Mockups</h1>
<div class="sub">10 distinct strategic concepts grounded in the audit findings · admin-only · 2026-05-04</div>
<div class="grid">
${MOCKUPS.map((m,i)=>`<div class="card"><div class="card-h"><div class="card-n">Concept ${String(i+1).padStart(2,'0')}</div><div class="card-t">${m.title}</div><div class="card-p">${m.pitch}</div><div class="card-tags">${m.emphasis.map(e=>`<span class="tag">${e}</span>`).join('')}</div></div><iframe class="card-iframe" src="${m.id}.html" title="${m.title}"></iframe><div class="card-foot"><a class="btn" href="${m.id}.html" target="_blank">Open full</a><a class="btn btn-fig" data-figma-id="${m.id}">Open in Figma →</a></div></div>`).join('')}
</div>
</body></html>`;
fs.writeFileSync(path.join(outDir, 'index.html'), indexHTML);
console.log(`✓ gallery index (${indexHTML.length} bytes)`);
console.log(`\nTotal: ${MOCKUPS.length} mockups + 1 gallery → html/`);