Add Hot Fuzz graphics across Doris family sites
Some checks failed
secret-guardrails / artifact-secret-scan (push) Has been cancelled
secret-guardrails / gitleaks (push) Has been cancelled

This commit is contained in:
Fizzlepoof
2026-05-25 03:54:44 +00:00
parent a27ed384e7
commit 13919cf4f7
15 changed files with 1411 additions and 96 deletions

View File

@@ -89,6 +89,171 @@ body {
flex: 0 1 320px;
min-width: min(100%, 280px);
}
.art-stack {
display: grid;
gap: 12px;
flex: 0 1 320px;
min-width: min(100%, 280px);
}
.art-stack .hot-fuzz-art {
flex: initial;
min-width: 0;
}
.photo-evidence-card {
position: relative;
margin: 0;
padding: 10px;
border-radius: 22px;
background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
border: 1px solid rgba(242, 239, 221, 0.14);
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.28);
overflow: hidden;
}
.photo-evidence-card::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(242, 193, 78, 0.08), transparent 28%, rgba(145, 15, 63, 0.16));
pointer-events: none;
}
.photo-evidence-card img {
width: 100%;
aspect-ratio: 16 / 10;
object-fit: cover;
display: block;
border-radius: 14px;
filter: saturate(0.82) sepia(0.18) contrast(1.02);
}
.photo-evidence-card figcaption {
position: absolute;
left: 18px;
right: 18px;
bottom: 16px;
z-index: 1;
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: #fff0cb;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.75);
}
.evidence-contact-grid {
display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(140px, 0.85fr);
gap: 12px;
align-items: stretch;
}
.evidence-note-card {
position: relative;
padding: 14px 16px;
border-radius: 22px;
background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.015)), rgba(15,18,25,0.96);
border: 1px solid rgba(242, 239, 221, 0.11);
box-shadow: 0 16px 30px rgba(0, 0, 0, 0.24);
overflow: hidden;
}
.evidence-note-card::before {
content: '';
position: absolute;
inset: 0 auto auto 0;
width: 100%;
height: 4px;
background: linear-gradient(90deg, var(--hf-red), var(--hf-amber), var(--hf-blue));
}
.case-contact-top {
display: flex;
justify-content: space-between;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 10px;
}
.case-contact-kicker,
.case-contact-badge {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 6px 10px;
font-size: 0.74rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.case-contact-kicker {
background: rgba(242, 193, 78, 0.12);
border: 1px solid rgba(242, 193, 78, 0.24);
color: #ffe6aa;
}
.case-contact-badge {
background: rgba(123, 143, 178, 0.16);
border: 1px solid rgba(123, 143, 178, 0.24);
color: #dbe5ff;
}
.contact-sheet-illustration {
width: 100%;
height: auto;
display: block;
margin-bottom: 10px;
filter: drop-shadow(0 14px 22px rgba(0, 0, 0, 0.24));
}
.contact-frame {
fill: rgba(11, 14, 20, 0.92);
stroke: rgba(242, 239, 221, 0.12);
stroke-width: 2;
}
.contact-photo {
fill: rgba(123, 143, 178, 0.22);
stroke: rgba(242, 239, 221, 0.16);
stroke-width: 1.5;
}
.contact-photo.alt {
fill: rgba(213, 54, 0, 0.18);
}
.contact-lines,
.contact-thread,
.beacon-arc {
fill: none;
stroke-linecap: round;
}
.contact-lines {
stroke: rgba(242, 239, 221, 0.86);
stroke-width: 4;
}
.contact-thread,
.map-thread {
stroke: rgba(213, 54, 0, 0.82);
stroke-width: 3;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.contact-marker {
fill: rgba(242, 193, 78, 0.18);
stroke: rgba(242, 193, 78, 0.84);
stroke-width: 2;
}
.contact-label {
fill: rgba(242, 239, 221, 0.9);
font-size: 15px;
font-weight: 800;
letter-spacing: 0.16em;
text-anchor: middle;
}
.case-contact-list {
margin: 0;
padding-left: 18px;
color: #d5dded;
display: grid;
gap: 8px;
line-height: 1.4;
}
.supporting-photo-card { min-height: 100%; }
.evidence-card,
.radio-body { fill: rgba(242, 239, 221, 0.92); }
.evidence-lines { fill: none; stroke: rgba(10, 12, 18, 0.82); stroke-width: 4; stroke-linecap: round; }
.marker-dot { fill: rgba(242, 193, 78, 0.94); }
.marker-dot-alt { fill: rgba(123, 143, 178, 0.9); }
.beacon-arc { stroke: rgba(242, 193, 78, 0.9); stroke-width: 4; }
.radio-antenna { fill: rgba(242, 193, 78, 0.9); }
.poster-illustration {
width: 100%;
height: auto;
@@ -251,7 +416,10 @@ button:focus, input:focus, select:focus, textarea:focus { outline: 2px solid rgb
@media (max-width: 700px) {
.page-header,
.page-shell {
width: min(100% - 1rem, 1100px);
width: min(100% - 1.25rem, 1100px);
}
.evidence-contact-grid {
grid-template-columns: 1fr;
}
.brand-row,
@@ -264,3 +432,94 @@ button:focus, input:focus, select:focus, textarea:focus { outline: 2px solid rgb
min-width: 0;
}
}
.doris-family-shell .case-nav {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.doris-family-shell .case-nav-link,
.doris-family-shell .family-app-card {
text-decoration: none;
}
.doris-family-shell .case-nav-link {
display: inline-flex;
align-items: center;
padding: 0.58rem 0.9rem;
border-radius: 999px;
border: 1px solid rgba(242, 193, 78, 0.18);
background: rgba(13, 19, 33, 0.72);
color: var(--muted);
font-size: 0.88rem;
letter-spacing: 0.02em;
}
.doris-family-shell .case-nav-link:hover,
.doris-family-shell .case-nav-link.active {
color: var(--ink);
border-color: rgba(242, 193, 78, 0.48);
background: linear-gradient(135deg, rgba(213, 54, 0, 0.24), rgba(145, 15, 63, 0.24));
}
.doris-family-shell .nav-link.active {
color: var(--ink);
border-color: rgba(242, 193, 78, 0.58);
background: linear-gradient(135deg, rgba(213, 54, 0, 0.24), rgba(145, 15, 63, 0.24));
}
.family-portal {
display: grid;
gap: 14px;
}
.family-directory-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
}
.family-app-card {
display: grid;
gap: 6px;
padding: 14px;
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: linear-gradient(180deg, rgba(17, 24, 39, 0.92), rgba(8, 12, 22, 0.96));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 14px 28px rgba(0, 0, 0, 0.22);
color: var(--ink);
}
.family-app-card:hover,
.family-app-card.current-desk {
border-color: rgba(242, 193, 78, 0.4);
transform: translateY(-1px);
}
.family-app-kicker {
font-size: 0.72rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--hf-amber);
}
.family-app-card strong {
font-size: 1rem;
}
.family-app-card small {
color: var(--muted);
line-height: 1.45;
}
@media (max-width: 768px) {
.doris-family-shell .case-nav {
overflow-x: auto;
padding-bottom: 4px;
}
.family-directory-grid {
grid-template-columns: 1fr;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

View File

@@ -4,9 +4,9 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title or 'Doris Barbell' }}</title>
<link rel="stylesheet" href="/static/app.css?v=4">
<link rel="stylesheet" href="/static/app.css?v=6">
</head>
<body class="doris-hot-fuzz app-barbell">
<body class="doris-hot-fuzz doris-family-shell app-barbell">
<div class="incident-tape" aria-hidden="true"></div>
<header class="page-header casefile-header app-casefile app-casefile-barbell">
<div class="film-grain" aria-hidden="true"></div>
@@ -16,11 +16,12 @@
<span class="nav-kicker">N.W.A. Case File</span>
<strong>Doris Constabulary</strong>
</div>
<nav class="nav-links" aria-label="Doris Barbell navigation">
<nav class="nav-links doris-family-nav" aria-label="Doris family navigation">
<a class="nav-link {{ 'active' if request.url.path == '/' else '' }}" href="http://10.5.30.6:8093/">Barbell</a>
<a class="nav-link" href="http://10.5.30.7:8787/">Dashboard</a>
<a class="nav-link" href="http://10.5.30.7:8787/services.html">Services</a>
<a class="nav-link" href="http://10.5.30.7:8092/">Kitchen</a>
<a class="nav-link" href="https://schoolhouse.paccoco.com">Schoolhouse</a>
<a class="nav-link" href="/">Barbell</a>
<a class="nav-link" href="https://schoolhouse.paccoco.com/">Schoolhouse</a>
</nav>
</div>
<div class="casefile-title-row">
@@ -35,34 +36,115 @@
<span class="pill">Case type: Training Dossier</span>
</div>
</div>
<div class="hot-fuzz-art" aria-hidden="true">
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
<defs>
<linearGradient id="barbell-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f2c14e"></stop>
<stop offset="50%" stop-color="#d53600"></stop>
<stop offset="100%" stop-color="#910f3f"></stop>
</linearGradient>
</defs>
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
<path d="M112 180 C110 150 116 126 132 110 C140 100 148 94 154 90 C160 94 168 100 176 110 C192 126 198 150 196 180 Z" class="constable-silhouette lead"></path>
<path d="M174 184 C172 154 178 132 194 118 C202 110 210 104 218 100 C226 104 234 110 242 118 C258 132 264 154 262 184 Z" class="constable-silhouette partner"></path>
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
<path d="M76 64 C80 54 91 48 101 53 C93 52 88 57 89 63 C90 69 101 68 103 76 C94 78 83 76 78 69 L71 76 L66 71 L74 64 Z" class="swan-mark"></path>
<rect x="122" y="110" width="86" height="8" rx="4" class="barbell-bar"></rect>
<circle cx="120" cy="114" r="16" class="plate-outer"></circle>
<circle cx="120" cy="114" r="8" class="plate-inner"></circle>
<circle cx="210" cy="114" r="16" class="plate-outer"></circle>
<circle cx="210" cy="114" r="8" class="plate-inner"></circle>
<text x="160" y="204" class="poster-callout">TRAINING DOSSIER</text>
</svg>
<div class="art-stack">
<div class="hot-fuzz-art" aria-hidden="true">
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
<defs>
<linearGradient id="barbell-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f2c14e"></stop>
<stop offset="50%" stop-color="#d53600"></stop>
<stop offset="100%" stop-color="#910f3f"></stop>
</linearGradient>
</defs>
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
<path d="M112 180 C110 150 116 126 132 110 C140 100 148 94 154 90 C160 94 168 100 176 110 C192 126 198 150 196 180 Z" class="constable-silhouette lead"></path>
<path d="M174 184 C172 154 178 132 194 118 C202 110 210 104 218 100 C226 104 234 110 242 118 C258 132 264 154 262 184 Z" class="constable-silhouette partner"></path>
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
<path d="M76 64 C80 54 91 48 101 53 C93 52 88 57 89 63 C90 69 101 68 103 76 C94 78 83 76 78 69 L71 76 L66 71 L74 64 Z" class="swan-mark"></path>
<rect x="120" y="88" width="88" height="38" rx="8" class="evidence-card"></rect>
<path d="M130 98 h58 M130 108 h42 M130 118 h32" class="evidence-lines"></path>
<circle cx="88" cy="88" r="8" class="marker-dot"></circle>
<circle cx="100" cy="104" r="6" class="marker-dot marker-dot-alt"></circle>
<path d="M96 94 L122 110 L152 96" class="map-thread"></path>
<path d="M210 88 C226 80 242 80 254 88" class="beacon-arc"></path>
<rect x="122" y="110" width="86" height="8" rx="4" class="barbell-bar"></rect>
<circle cx="120" cy="114" r="16" class="plate-outer"></circle>
<circle cx="120" cy="114" r="8" class="plate-inner"></circle>
<circle cx="210" cy="114" r="16" class="plate-outer"></circle>
<circle cx="210" cy="114" r="8" class="plate-inner"></circle>
<rect x="216" y="126" width="38" height="16" rx="6" class="radio-body"></rect>
<rect x="232" y="110" width="8" height="16" rx="4" class="radio-antenna"></rect>
<text x="160" y="204" class="poster-callout">TRAINING DOSSIER</text>
</svg>
</div>
<div class="evidence-contact-grid">
<section class="evidence-note-card case-contact-sheet" aria-label="Training caseboard details">
<div class="case-contact-top">
<span class="case-contact-kicker">Station drill board</span>
<span class="case-contact-badge">Cadet fitness</span>
</div>
<svg viewBox="0 0 320 160" class="contact-sheet-illustration" role="presentation">
<rect x="12" y="14" width="116" height="132" rx="16" class="contact-frame"></rect>
<rect x="24" y="30" width="92" height="54" rx="12" class="contact-photo"></rect>
<path d="M30 96 h80 M30 108 h66 M30 120 h48" class="contact-lines"></path>
<rect x="146" y="24" width="78" height="44" rx="10" class="contact-photo alt"></rect>
<rect x="160" y="84" width="112" height="52" rx="12" class="contact-photo alt"></rect>
<circle cx="250" cy="40" r="16" class="contact-marker"></circle>
<path d="M132 94 C152 82 176 80 194 88 C208 94 228 98 250 96" class="contact-thread"></path>
<text x="70" y="60" class="contact-label">RPE</text>
<text x="186" y="52" class="contact-label">PACE</text>
<text x="218" y="114" class="contact-label">PLATE</text>
</svg>
<ul class="case-contact-list">
<li>Clipboard-style slips, radio cues, and drill-board labels make the page read like a police training dossier instead of generic fitness software.</li>
<li>The photo still remains for texture, but the authored graphics now carry the theme.</li>
</ul>
</section>
<figure class="photo-evidence-card hero-photo-card supporting-photo-card">
<img src="/static/images/hotfuzz-training-dossier.jpg" alt="Sepia-toned training photo with dumbbells, weight plates, and a handwritten workout clipboard." loading="eager">
<figcaption>Training still · source logged in docs</figcaption>
</figure>
</div>
</div>
</div>
<nav class="case-nav" aria-label="Doris Barbell sections">
<a class="case-nav-link active" href="/#body-metrics">Body metrics</a>
<a class="case-nav-link" href="/#weight-log">Weight log</a>
<a class="case-nav-link" href="/#routine-board">Routine board</a>
<a class="case-nav-link" href="/#recent-workouts">Workout reports</a>
</nav>
</header>
<main class="page-shell">
<section class="family-portal evidence-board casefile-directory">
<div class="section-heading">
<div>
<p class="section-label">Singular front door</p>
<h2>Doris family directory</h2>
</div>
<span class="pill">Casefile directory</span>
</div>
<p class="muted case-legend">One shell, separate runtimes. Training reports live here, but the rest of the Doris family is one jump away.</p>
<div class="family-directory-grid">
<a class="family-app-card current-desk" href="http://10.5.30.6:8093/">
<span class="family-app-kicker">Current desk</span>
<strong>Doris Barbell</strong>
<small>Body metrics, routines, workouts, and progression dossiers.</small>
</a>
<a class="family-app-card" href="http://10.5.30.7:8787/">
<span class="family-app-kicker">Operator portal</span>
<strong>Doris Dashboard</strong>
<small>Canonical front door for the whole Doris family.</small>
</a>
<a class="family-app-card" href="http://10.5.30.7:8787/services.html">
<span class="family-app-kicker">Switchboard</span>
<strong>Services Directory</strong>
<small>Dense launch wall for the wider homelab stack.</small>
</a>
<a class="family-app-card" href="http://10.5.30.7:8092/">
<span class="family-app-kicker">Pantry desk</span>
<strong>Doris Kitchen</strong>
<small>Recipe leads, repair queue, and planning board.</small>
</a>
<a class="family-app-card" href="https://schoolhouse.paccoco.com/">
<span class="family-app-kicker">Records desk</span>
<strong>Doris Schoolhouse</strong>
<small>Assignments, recordings, and school intake packets.</small>
</a>
</div>
</section>
{% block content %}{% endblock %}
</main>
</body>

View File

@@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<section class="grid cols-2 evidence-board dossier-grid">
<section id="body-metrics" class="grid cols-2 evidence-board dossier-grid">
<article class="card evidence-card">
<div class="case-legend">
<span class="section-label">Training floor board</span>
@@ -44,7 +44,7 @@
</article>
</section>
<section class="grid cols-2 dossier-grid">
<section id="weight-log" class="grid cols-2 dossier-grid">
<article class="card evidence-board">
<div class="case-legend"><span class="section-label">Intake docket</span><span class="pill">Weight log</span></div>
<h2>Log weight</h2>
@@ -147,7 +147,7 @@
</article>
</section>
<section class="grid cols-2 dossier-grid">
<section id="recent-workouts" class="grid cols-2 dossier-grid">
<article class="card evidence-board">
<div class="case-legend"><span class="section-label">Workout report</span><span class="pill">Quick log</span></div>
<h2>Quick log workout</h2>
@@ -170,7 +170,7 @@
</form>
</article>
<article class="card evidence-board">
<article id="routine-board" class="card evidence-board">
<div class="case-legend"><span class="section-label">Current split</span><span class="pill">Routine board</span></div>
<h2>Current routine board</h2>
<p class="muted">John's current five-day split is now loaded as the default routine library with a default prescription of 3 sets of 10 reps. Weights are still TBD.</p>

View File

@@ -0,0 +1,116 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def read(relative_path: str) -> str:
return (ROOT / relative_path).read_text()
def test_family_directory_markers_exist_across_doris_apps() -> None:
expectations = {
'doris-kitchen/app/templates/base.html': [
'doris-family-shell',
'doris-family-nav',
'Singular front door',
'Doris family directory',
'Casefile directory',
'family-directory-grid',
'Doris Dashboard',
'Services Directory',
'Doris Schoolhouse',
'Doris Barbell',
'case-nav',
'Queue board',
],
'doris-schoolhouse/app/templates/base.html': [
'doris-family-shell',
'doris-family-nav',
'Singular front door',
'Doris family directory',
'Casefile directory',
'family-directory-grid',
'Doris Dashboard',
'Services Directory',
'Doris Kitchen',
'Doris Barbell',
'case-nav',
'Submission packet',
],
'doris-barbell/app/templates/base.html': [
'doris-family-shell',
'doris-family-nav',
'Singular front door',
'Doris family directory',
'Casefile directory',
'family-directory-grid',
'Doris Dashboard',
'Services Directory',
'Doris Kitchen',
'Doris Schoolhouse',
'case-nav',
'Workout reports',
],
'doris-dashboard/bin/generate_dashboard.py': [
'render_family_directory',
'Doris family directory',
'Services Directory',
'Doris Kitchen',
'Doris Schoolhouse',
'Doris Barbell',
'family-directory-grid',
],
}
for relative_path, markers in expectations.items():
content = read(relative_path)
for marker in markers:
assert marker in content, f'{marker} missing from {relative_path}'
def test_family_navigation_styles_exist() -> None:
expectations = {
'doris-kitchen/app/static/app.css': ['.doris-family-shell .case-nav', '.family-directory-grid', '.family-app-card', '.family-app-kicker'],
'doris-schoolhouse/app/static/app.css': ['.doris-family-shell .case-nav', '.family-directory-grid', '.family-app-card', '.family-app-kicker'],
'doris-barbell/app/static/app.css': ['.doris-family-shell .case-nav', '.family-directory-grid', '.family-app-card', '.family-app-kicker'],
'doris-dashboard/public/style.css': ['.family-portal', '.family-directory-grid', '.family-app-card', '.family-app-kicker'],
}
for relative_path, markers in expectations.items():
content = read(relative_path)
for marker in markers:
assert marker in content, f'{marker} missing from {relative_path}'
def test_hotfuzz_photo_assets_exist_and_are_wired() -> None:
assets = {
'doris-dashboard/public/assets/hotfuzz-operator-desk.jpg': 'Operator still · source logged in docs',
'doris-barbell/app/static/images/hotfuzz-training-dossier.jpg': 'Training still · source logged in docs',
'doris-kitchen/app/static/images/hotfuzz-pantry-evidence.jpg': 'Pantry still · source logged in docs',
'doris-schoolhouse/app/static/images/hotfuzz-records-desk.jpg': 'Records still · source logged in docs',
}
for relative_path, marker in assets.items():
assert (ROOT / relative_path).exists(), f'missing asset file: {relative_path}'
if relative_path.startswith('doris-dashboard/'):
content = read('doris-dashboard/bin/generate_dashboard.py')
elif 'barbell' in relative_path:
content = read('doris-barbell/app/templates/base.html')
elif 'kitchen' in relative_path:
content = read('doris-kitchen/app/templates/base.html')
else:
content = read('doris-schoolhouse/app/templates/base.html')
assert Path(relative_path).name in content, f'{relative_path} not wired into template/generator'
assert marker in content, f'{marker} missing where {relative_path} is referenced'
provenance_doc = ROOT / 'doris-family-hotfuzz-graphics-sources-2026-05-24.md'
assert provenance_doc.exists(), 'graphics provenance doc missing'
provenance_text = provenance_doc.read_text()
for marker in ['CC BY-SA', 'CC BY-NC-SA', 'CC BY-NC-ND', 'CC BY-NC']:
assert marker in provenance_text, f'{marker} missing from provenance doc'
def test_barbell_interior_anchor_markers_exist() -> None:
content = read('doris-barbell/app/templates/dashboard.html')
for marker in ['id="body-metrics"', 'id="weight-log"', 'id="routine-board"', 'id="recent-workouts"']:
assert marker in content, f'{marker} missing from Doris Barbell dashboard'