Campus-AI / docs /architecture.html
realruneett's picture
Final Release: CampusGen AI Pipeline & Compositor
a8aea21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Campus-AI β€” Architecture | CounciL</title>
<link
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet">
<style>
:root {
--bg: #06070f;
--card: rgba(14, 17, 38, 0.85);
--border: rgba(120, 140, 255, 0.12);
--blue: #4f8ff7;
--purple: #8b5cf6;
--pink: #ec4899;
--green: #22d3ee;
--orange: #f59e0b;
--text: #c8d6f0;
--muted: #5a6488;
--glow-blue: rgba(79, 143, 247, 0.35);
--glow-purple: rgba(139, 92, 246, 0.35);
--glow-pink: rgba(236, 72, 153, 0.35);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--bg);
font-family: 'Space Grotesk', sans-serif;
color: var(--text);
overflow-x: hidden;
}
/* Animated background grid */
body::before {
content: '';
position: fixed;
inset: 0;
background:
linear-gradient(rgba(79, 143, 247, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(79, 143, 247, 0.03) 1px, transparent 1px);
background-size: 60px 60px;
animation: gridMove 20s linear infinite;
z-index: 0;
}
@keyframes gridMove {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(60px, 60px);
}
}
/* Ambient orbs */
.orb {
position: fixed;
border-radius: 50%;
filter: blur(100px);
opacity: 0.15;
z-index: 0;
animation: float 15s ease-in-out infinite alternate;
}
.orb-1 {
width: 500px;
height: 500px;
background: var(--blue);
top: -100px;
left: -100px;
}
.orb-2 {
width: 400px;
height: 400px;
background: var(--purple);
top: 40%;
right: -100px;
animation-delay: -5s;
}
.orb-3 {
width: 450px;
height: 450px;
background: var(--pink);
bottom: -100px;
left: 30%;
animation-delay: -10s;
}
@keyframes float {
0% {
transform: translate(0, 0) scale(1);
}
100% {
transform: translate(40px, 30px) scale(1.1);
}
}
.container {
max-width: 1300px;
margin: 0 auto;
padding: 50px 30px;
position: relative;
z-index: 1;
}
/* ═══ HEADER ═══ */
.header {
text-align: center;
margin-bottom: 60px;
}
.header .badge {
display: inline-block;
padding: 6px 18px;
border-radius: 50px;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 2px;
text-transform: uppercase;
border: 1px solid rgba(139, 92, 246, 0.3);
color: var(--purple);
background: rgba(139, 92, 246, 0.08);
margin-bottom: 20px;
}
.header h1 {
font-size: 3rem;
font-weight: 700;
line-height: 1.1;
background: linear-gradient(135deg, #fff 0%, #4f8ff7 40%, #8b5cf6 60%, #ec4899 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 12px;
}
.header p {
color: var(--muted);
font-size: 1.05rem;
max-width: 500px;
margin: 0 auto;
}
/* ═══ PHASE SECTIONS ═══ */
.phase {
margin-bottom: 24px;
position: relative;
}
.phase-header {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 24px;
}
.phase-number {
width: 36px;
height: 36px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.85rem;
flex-shrink: 0;
}
.p1 .phase-number {
background: rgba(79, 143, 247, 0.15);
color: var(--blue);
border: 1px solid rgba(79, 143, 247, 0.3);
}
.p2 .phase-number {
background: rgba(139, 92, 246, 0.15);
color: var(--purple);
border: 1px solid rgba(139, 92, 246, 0.3);
}
.p3 .phase-number {
background: rgba(236, 72, 153, 0.15);
color: var(--pink);
border: 1px solid rgba(236, 72, 153, 0.3);
}
.phase-title {
font-size: 1.1rem;
font-weight: 600;
color: #fff;
}
.phase-desc {
font-size: 0.78rem;
color: var(--muted);
}
/* ═══ FLOW (horizontal cards with arrows) ═══ */
.flow {
display: flex;
align-items: center;
gap: 0;
overflow-x: auto;
padding-bottom: 8px;
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 16px;
padding: 24px 22px;
min-width: 200px;
backdrop-filter: blur(20px);
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
border-radius: 16px 16px 0 0;
opacity: 0;
transition: opacity 0.35s;
}
.card:hover {
transform: translateY(-4px);
border-color: rgba(120, 140, 255, 0.3);
}
.card:hover::before {
opacity: 1;
}
.p1 .card::before {
background: linear-gradient(90deg, var(--blue), var(--green));
}
.p1 .card:hover {
box-shadow: 0 8px 40px rgba(79, 143, 247, 0.12);
}
.p2 .card::before {
background: linear-gradient(90deg, var(--purple), var(--blue));
}
.p2 .card:hover {
box-shadow: 0 8px 40px rgba(139, 92, 246, 0.12);
}
.p3 .card::before {
background: linear-gradient(90deg, var(--pink), var(--orange));
}
.p3 .card:hover {
box-shadow: 0 8px 40px rgba(236, 72, 153, 0.12);
}
.card-icon {
font-size: 2rem;
margin-bottom: 12px;
display: block;
}
.card-name {
font-size: 0.92rem;
font-weight: 600;
color: #fff;
margin-bottom: 6px;
}
.card-detail {
font-size: 0.73rem;
color: var(--muted);
line-height: 1.5;
}
.card-tag {
display: inline-block;
margin-top: 10px;
padding: 3px 10px;
border-radius: 6px;
font-size: 0.62rem;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
}
/* Flow arrows */
.flow-arrow {
display: flex;
align-items: center;
justify-content: center;
padding: 0 6px;
flex-shrink: 0;
}
.flow-arrow svg {
width: 40px;
height: 20px;
}
.flow-arrow line,
.flow-arrow polyline {
stroke: var(--muted);
stroke-width: 1.5;
fill: none;
stroke-dasharray: 4 3;
animation: dashFlow 1.5s linear infinite;
}
@keyframes dashFlow {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -14;
}
}
/* Big down arrow between phases */
.phase-connector {
display: flex;
justify-content: center;
padding: 16px 0;
}
.phase-connector svg {
width: 24px;
height: 50px;
}
.phase-connector line {
stroke: rgba(139, 92, 246, 0.3);
stroke-width: 1.5;
stroke-dasharray: 4 3;
animation: dashDown 1.5s linear infinite;
}
.phase-connector polygon {
fill: rgba(139, 92, 246, 0.4);
}
@keyframes dashDown {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -14;
}
}
/* ═══ TRAINING - special 3-col layout ═══ */
.training-layout {
display: grid;
grid-template-columns: 1fr 1.8fr 1fr;
gap: 20px;
align-items: start;
}
.train-core {
background: linear-gradient(145deg, rgba(30, 20, 60, 0.9), rgba(14, 10, 35, 0.95));
border: 1.5px solid rgba(139, 92, 246, 0.25);
border-radius: 20px;
padding: 32px 28px;
text-align: center;
position: relative;
overflow: hidden;
}
.train-core::after {
content: '';
position: absolute;
inset: -1px;
border-radius: 20px;
background: linear-gradient(135deg, rgba(139, 92, 246, 0.15), transparent 50%, rgba(79, 143, 247, 0.1));
z-index: 0;
pointer-events: none;
}
.train-core>* {
position: relative;
z-index: 1;
}
.train-core .card-icon {
font-size: 3rem;
}
.train-core .card-name {
font-size: 1.3rem;
color: var(--purple);
}
.lora-badge {
display: inline-block;
margin-top: 14px;
padding: 8px 20px;
border-radius: 10px;
background: rgba(139, 92, 246, 0.12);
border: 1px solid rgba(139, 92, 246, 0.25);
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: var(--purple);
}
.train-specs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-top: 16px;
}
.spec {
background: rgba(139, 92, 246, 0.06);
border-radius: 8px;
padding: 8px 10px;
text-align: center;
}
.spec-val {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
font-weight: 600;
color: #fff;
}
.spec-label {
font-size: 0.6rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.side-stack {
display: flex;
flex-direction: column;
gap: 12px;
}
.side-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 16px;
backdrop-filter: blur(20px);
transition: all 0.3s;
}
.side-card:hover {
border-color: rgba(139, 92, 246, 0.3);
transform: translateX(4px);
}
.side-card-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.82rem;
font-weight: 600;
color: #fff;
margin-bottom: 4px;
}
.side-card-title .emoji {
font-size: 1.1rem;
}
.side-card-detail {
font-size: 0.68rem;
color: var(--muted);
line-height: 1.5;
padding-left: 28px;
}
/* ═══ INFERENCE - 3 col ═══ */
.inference-layout {
display: grid;
grid-template-columns: 240px 1fr 200px;
gap: 20px;
align-items: center;
}
.inf-input {
display: flex;
flex-direction: column;
gap: 14px;
}
.inf-output {
display: flex;
flex-direction: column;
gap: 14px;
}
.inf-engine {
background: linear-gradient(145deg, rgba(50, 15, 40, 0.85), rgba(20, 8, 25, 0.9));
border: 1.5px solid rgba(236, 72, 153, 0.2);
border-radius: 20px;
padding: 28px 24px;
position: relative;
overflow: hidden;
}
.inf-engine::after {
content: '';
position: absolute;
inset: -1px;
border-radius: 20px;
background: linear-gradient(135deg, rgba(236, 72, 153, 0.1), transparent 50%, rgba(245, 158, 11, 0.08));
z-index: 0;
pointer-events: none;
}
.inf-engine>* {
position: relative;
z-index: 1;
}
.engine-label {
text-align: center;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--pink);
font-weight: 600;
margin-bottom: 18px;
}
.modes {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.mode {
background: rgba(236, 72, 153, 0.06);
border: 1px solid rgba(236, 72, 153, 0.12);
border-radius: 12px;
padding: 16px 12px;
text-align: center;
transition: all 0.3s;
}
.mode:hover {
background: rgba(236, 72, 153, 0.12);
border-color: rgba(236, 72, 153, 0.3);
transform: scale(1.03);
}
.mode-icon {
font-size: 1.4rem;
margin-bottom: 6px;
}
.mode-name {
font-size: 0.78rem;
font-weight: 600;
color: #fff;
}
.mode-sub {
font-size: 0.62rem;
color: var(--muted);
margin-top: 2px;
}
.engine-footer {
text-align: center;
margin-top: 14px;
font-size: 0.65rem;
color: var(--muted);
font-family: 'JetBrains Mono', monospace;
}
/* Result card glow */
.result-card {
border-color: rgba(34, 211, 238, 0.25) !important;
}
.result-card:hover {
box-shadow: 0 8px 40px rgba(34, 211, 238, 0.12) !important;
}
.result-card .card-name {
color: var(--green);
}
/* ═══ DEPLOY BAR ═══ */
.deploy {
display: flex;
gap: 16px;
justify-content: center;
margin-top: 28px;
flex-wrap: wrap;
}
.deploy-chip {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 22px;
border-radius: 50px;
font-size: 0.78rem;
font-weight: 500;
transition: all 0.3s;
}
.deploy-chip:hover {
transform: scale(1.05);
}
.chip-local {
background: rgba(34, 211, 238, 0.08);
border: 1px solid rgba(34, 211, 238, 0.2);
color: var(--green);
}
.chip-cloud {
background: rgba(79, 143, 247, 0.08);
border: 1px solid rgba(79, 143, 247, 0.2);
color: var(--blue);
}
/* ═══ STATS BAR ═══ */
.stats-bar {
display: flex;
justify-content: center;
gap: 40px;
margin-top: 50px;
padding: 30px 0;
border-top: 1px solid var(--border);
flex-wrap: wrap;
}
.stat {
text-align: center;
}
.stat-value {
font-size: 1.6rem;
font-weight: 700;
font-family: 'JetBrains Mono', monospace;
background: linear-gradient(135deg, var(--blue), var(--purple));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
font-size: 0.65rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1.5px;
margin-top: 4px;
}
.footer {
text-align: center;
margin-top: 40px;
font-size: 0.72rem;
color: var(--muted);
}
.footer span {
color: var(--pink);
}
/* Responsive */
@media (max-width: 900px) {
.training-layout,
.inference-layout {
grid-template-columns: 1fr;
}
.flow {
flex-wrap: wrap;
justify-content: center;
}
.flow-arrow {
transform: rotate(90deg);
}
}
</style>
</head>
<body>
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<div class="container">
<!-- HEADER -->
<div class="header">
<div class="badge">System Architecture</div>
<h1>Campus-AI</h1>
<p style="color:var(--purple); font-size:0.85rem; font-weight:600; margin-bottom:8px;">by CounciL</p>
<p>End-to-end AI pipeline that scrapes, curates, trains, and generates campus event posters</p>
</div>
<!-- ═══════════════════════════════════════════ -->
<!-- PHASE 1: DATA PIPELINE -->
<!-- ═══════════════════════════════════════════ -->
<div class="phase p1">
<div class="phase-header">
<div class="phase-number">01</div>
<div>
<div class="phase-title">Data Pipeline</div>
<div class="phase-desc">Collect β†’ Filter β†’ Caption β†’ Split</div>
</div>
</div>
<div class="flow">
<div class="card">
<span class="card-icon">πŸ•·οΈ</span>
<div class="card-name">Pinterest Scraper</div>
<div class="card-detail">Selenium headless browser with automatic scrolling &amp; perceptual hash
deduplication</div>
<div class="card-tag" style="background:rgba(79,143,247,0.1); color:var(--blue);">57 subcategories Γ— 1,900
</div>
</div>
<div class="flow-arrow"><svg>
<line x1="0" y1="10" x2="32" y2="10" />
<polyline points="30,6 36,10 30,14" />
</svg></div>
<div class="card">
<span class="card-icon">πŸ”¬</span>
<div class="card-name">Quality Filter</div>
<div class="card-detail">GPU-accelerated Laplacian sharpness, resolution, aspect ratio &amp; color diversity
checks</div>
<div class="card-tag" style="background:rgba(34,211,238,0.1); color:var(--green);">~68% pass rate</div>
</div>
<div class="flow-arrow"><svg>
<line x1="0" y1="10" x2="32" y2="10" />
<polyline points="30,6 36,10 30,14" />
</svg></div>
<div class="card">
<span class="card-icon">πŸ“</span>
<div class="card-name">Florence-2 Captioner</div>
<div class="card-detail">Microsoft Florence-2-large generates detailed captions in bf16 with torch.compile
</div>
<div class="card-tag" style="background:rgba(139,92,246,0.1); color:var(--purple);">SM120 optimized</div>
</div>
<div class="flow-arrow"><svg>
<line x1="0" y1="10" x2="32" y2="10" />
<polyline points="30,6 36,10 30,14" />
</svg></div>
<div class="card">
<span class="card-icon">βœ‚οΈ</span>
<div class="card-name">Dataset Splitter</div>
<div class="card-detail">Stratified splitting by category into training, validation &amp; test sets</div>
<div class="card-tag" style="background:rgba(245,158,11,0.1); color:var(--orange);">~55K train images</div>
</div>
</div>
</div>
<!-- Connector -->
<div class="phase-connector">
<svg>
<line x1="12" y1="0" x2="12" y2="40" />
<polygon points="6,40 12,50 18,40" />
</svg>
</div>
<!-- ═══════════════════════════════════════════ -->
<!-- PHASE 2: TRAINING -->
<!-- ═══════════════════════════════════════════ -->
<div class="phase p2">
<div class="phase-header">
<div class="phase-number">02</div>
<div>
<div class="phase-title">Training Pipeline</div>
<div class="phase-desc">Fine-tune Flux.1-dev with LoRA adapters</div>
</div>
</div>
<div class="training-layout">
<!-- Left: Optimizer & Loss -->
<div class="side-stack">
<div class="side-card">
<div class="side-card-title"><span class="emoji">⚑</span> Prodigy Optimizer</div>
<div class="side-card-detail">Self-adapting LR = 1.0<br>No manual LR tuning needed</div>
</div>
<div class="side-card">
<div class="side-card-title"><span class="emoji">πŸ“‰</span> Min-SNR-Ξ³ Loss</div>
<div class="side-card-detail">Ξ³ = 5.0 β€” balanced learning<br>across all noise levels</div>
</div>
<div class="side-card">
<div class="side-card-title"><span class="emoji">πŸ”„</span> Cosine Warm Restarts</div>
<div class="side-card-detail">3 cycles over 4 epochs<br>escapes local minima</div>
</div>
</div>
<!-- Center: Core model -->
<div class="train-core">
<span class="card-icon">🧠</span>
<div class="card-name">Flux.1-dev</div>
<div class="card-detail" style="margin-top:8px;">12 billion parameter<br>transformer diffusion model</div>
<div class="lora-badge">+ LoRA Adapter (Rank 16, Ξ±=16)</div>
<div class="train-specs">
<div class="spec">
<div class="spec-val">40M</div>
<div class="spec-label">Trainable Params</div>
</div>
<div class="spec">
<div class="spec-val">bf16</div>
<div class="spec-label">Precision</div>
</div>
<div class="spec">
<div class="spec-val">4</div>
<div class="spec-label">Eff. Batch Size</div>
</div>
<div class="spec">
<div class="spec-val">~55K</div>
<div class="spec-label">Optimizer Steps</div>
</div>
</div>
</div>
<!-- Right: Anti-overfitting & Hardware -->
<div class="side-stack">
<div class="side-card">
<div class="side-card-title"><span class="emoji">πŸ›‘οΈ</span> Anti-Overfitting</div>
<div class="side-card-detail">Caption dropout 10%<br>LoRA dropout 8%<br>L2 weight decay 0.01</div>
</div>
<div class="side-card">
<div class="side-card-title"><span class="emoji">βš™οΈ</span> LoRA+ (ICML '24)</div>
<div class="side-card-detail">B matrix gets 16Γ— higher LR<br>Free +2% accuracy boost</div>
</div>
<div class="side-card">
<div class="side-card-title"><span class="emoji">πŸ–₯️</span> SM120 Blackwell</div>
<div class="side-card-detail">TF32 tensor cores<br>torch.compile max-autotune</div>
</div>
</div>
</div>
</div>
<!-- Connector -->
<div class="phase-connector">
<svg>
<line x1="12" y1="0" x2="12" y2="40" />
<polygon points="6,40 12,50 18,40" />
</svg>
</div>
<!-- ═══════════════════════════════════════════ -->
<!-- PHASE 3: INFERENCE & DEPLOYMENT -->
<!-- ═══════════════════════════════════════════ -->
<div class="phase p3">
<div class="phase-header">
<div class="phase-number">03</div>
<div>
<div class="phase-title">Inference &amp; Deployment</div>
<div class="phase-desc">Prompt β†’ Generate β†’ Upscale β†’ Deliver</div>
</div>
</div>
<div class="inference-layout">
<!-- Left: input -->
<div class="inf-input">
<div class="card">
<span class="card-icon">πŸ‘€</span>
<div class="card-name">User Input</div>
<div class="card-detail">Event description, type, visual style &amp; resolution preset</div>
</div>
<div style="text-align:center;">
<svg width="24" height="30">
<line x1="12" y1="0" x2="12" y2="22" stroke="var(--muted)" stroke-width="1.5" stroke-dasharray="4 3">
<animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.5s" repeatCount="indefinite" />
</line>
<polygon points="6,22 12,30 18,22" fill="var(--muted)" opacity="0.5" />
</svg>
</div>
<div class="card">
<span class="card-icon">πŸ¦™</span>
<div class="card-name">Groq Llama 3.3 70B</div>
<div class="card-detail">Enhances plain text into detailed Flux-optimized prompts</div>
<div class="card-tag" style="background:rgba(245,158,11,0.1); color:var(--orange);">~200ms API</div>
</div>
</div>
<!-- Center: engine -->
<div class="inf-engine">
<div class="engine-label">Flux.1-dev + LoRA Inference Engine</div>
<div class="modes">
<div class="mode">
<div class="mode-icon">✍️</div>
<div class="mode-name">Text β†’ Poster</div>
<div class="mode-sub">From description only</div>
</div>
<div class="mode">
<div class="mode-icon">πŸ–ΌοΈ</div>
<div class="mode-name">Reference Style</div>
<div class="mode-sub">IP-Adapter transfer</div>
</div>
<div class="mode">
<div class="mode-icon">πŸ”„</div>
<div class="mode-name">Image β†’ Image</div>
<div class="mode-sub">Transform existing art</div>
</div>
<div class="mode">
<div class="mode-icon">🎭</div>
<div class="mode-name">Inpainting</div>
<div class="mode-sub">Edit specific regions</div>
</div>
</div>
<div class="engine-footer">CPU offload β€’ ~10GB peak VRAM β€’ bf16 precision</div>
</div>
<!-- Right: output -->
<div class="inf-output">
<div class="card">
<span class="card-icon">πŸ”Ž</span>
<div class="card-name">Real-ESRGAN 2Γ—</div>
<div class="card-detail">AI upscaling for crisp HD output at any size</div>
</div>
<div style="text-align:center;">
<svg width="24" height="30">
<line x1="12" y1="0" x2="12" y2="22" stroke="var(--muted)" stroke-width="1.5" stroke-dasharray="4 3">
<animate attributeName="stroke-dashoffset" from="0" to="-14" dur="1.5s" repeatCount="indefinite" />
</line>
<polygon points="6,22 12,30 18,22" fill="var(--muted)" opacity="0.5" />
</svg>
</div>
<div class="card result-card">
<span class="card-icon">🎨</span>
<div class="card-name">Generated Poster</div>
<div class="card-detail">1024Γ—1024 to 1152Γ—768<br>Multiple variants supported</div>
</div>
</div>
</div>
<!-- Deploy chips -->
<div class="deploy">
<div class="deploy-chip chip-local">πŸ–₯️ Local β€” RTX 5070 Ti (12GB VRAM)</div>
<div class="deploy-chip chip-cloud">☁️ Cloud β€” HF Spaces + ZeroGPU</div>
</div>
</div>
<!-- ═══ STATS ═══ -->
<div class="stats-bar">
<div class="stat">
<div class="stat-value">71K+</div>
<div class="stat-label">Training Images</div>
</div>
<div class="stat">
<div class="stat-value">57</div>
<div class="stat-label">Subcategories</div>
</div>
<div class="stat">
<div class="stat-value">12B</div>
<div class="stat-label">Base Params</div>
</div>
<div class="stat">
<div class="stat-value">40M</div>
<div class="stat-label">LoRA Params</div>
</div>
<div class="stat">
<div class="stat-value">SM120</div>
<div class="stat-label">GPU Arch</div>
</div>
<div class="stat">
<div class="stat-value">~46h</div>
<div class="stat-label">Training Time</div>
</div>
</div>
<div class="footer">Campus-AI · CounciL · Built with <span>❀️</span> for the Indian campus
community</div>
</div>
</body>
</html>