merge: hot fuzz graphics pass across doris family apps
This commit is contained in:
@@ -89,6 +89,171 @@ body {
|
|||||||
flex: 0 1 320px;
|
flex: 0 1 320px;
|
||||||
min-width: min(100%, 280px);
|
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 {
|
.poster-illustration {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -251,7 +416,10 @@ button:focus, input:focus, select:focus, textarea:focus { outline: 2px solid rgb
|
|||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
.page-header,
|
.page-header,
|
||||||
.page-shell {
|
.page-shell {
|
||||||
width: min(100% - 1rem, 1100px);
|
width: min(100% - 1.25rem, 1100px);
|
||||||
|
}
|
||||||
|
.evidence-contact-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-row,
|
.brand-row,
|
||||||
@@ -264,3 +432,94 @@ button:focus, input:focus, select:focus, textarea:focus { outline: 2px solid rgb
|
|||||||
min-width: 0;
|
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 |
@@ -4,9 +4,9 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{ title or 'Doris Barbell' }}</title>
|
<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>
|
</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>
|
<div class="incident-tape" aria-hidden="true"></div>
|
||||||
<header class="page-header casefile-header app-casefile app-casefile-barbell">
|
<header class="page-header casefile-header app-casefile app-casefile-barbell">
|
||||||
<div class="film-grain" aria-hidden="true"></div>
|
<div class="film-grain" aria-hidden="true"></div>
|
||||||
@@ -16,11 +16,12 @@
|
|||||||
<span class="nav-kicker">N.W.A. Case File</span>
|
<span class="nav-kicker">N.W.A. Case File</span>
|
||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</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/">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="http://10.5.30.7:8092/">Kitchen</a>
|
||||||
<a class="nav-link" href="https://schoolhouse.paccoco.com">Schoolhouse</a>
|
<a class="nav-link" href="https://schoolhouse.paccoco.com/">Schoolhouse</a>
|
||||||
<a class="nav-link" href="/">Barbell</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="casefile-title-row">
|
<div class="casefile-title-row">
|
||||||
@@ -35,34 +36,115 @@
|
|||||||
<span class="pill">Case type: Training Dossier</span>
|
<span class="pill">Case type: Training Dossier</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-fuzz-art" aria-hidden="true">
|
<div class="art-stack">
|
||||||
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
<div class="hot-fuzz-art" aria-hidden="true">
|
||||||
<defs>
|
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
||||||
<linearGradient id="barbell-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
<defs>
|
||||||
<stop offset="0%" stop-color="#f2c14e"></stop>
|
<linearGradient id="barbell-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
<stop offset="50%" stop-color="#d53600"></stop>
|
<stop offset="0%" stop-color="#f2c14e"></stop>
|
||||||
<stop offset="100%" stop-color="#910f3f"></stop>
|
<stop offset="50%" stop-color="#d53600"></stop>
|
||||||
</linearGradient>
|
<stop offset="100%" stop-color="#910f3f"></stop>
|
||||||
</defs>
|
</linearGradient>
|
||||||
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
</defs>
|
||||||
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
||||||
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
|
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
||||||
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
|
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></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="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></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>
|
<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>
|
||||||
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
<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>
|
||||||
<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>
|
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
||||||
<rect x="122" y="110" width="86" height="8" rx="4" class="barbell-bar"></rect>
|
<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>
|
||||||
<circle cx="120" cy="114" r="16" class="plate-outer"></circle>
|
<rect x="120" y="88" width="88" height="38" rx="8" class="evidence-card"></rect>
|
||||||
<circle cx="120" cy="114" r="8" class="plate-inner"></circle>
|
<path d="M130 98 h58 M130 108 h42 M130 118 h32" class="evidence-lines"></path>
|
||||||
<circle cx="210" cy="114" r="16" class="plate-outer"></circle>
|
<circle cx="88" cy="88" r="8" class="marker-dot"></circle>
|
||||||
<circle cx="210" cy="114" r="8" class="plate-inner"></circle>
|
<circle cx="100" cy="104" r="6" class="marker-dot marker-dot-alt"></circle>
|
||||||
<text x="160" y="204" class="poster-callout">TRAINING DOSSIER</text>
|
<path d="M96 94 L122 110 L152 96" class="map-thread"></path>
|
||||||
</svg>
|
<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>
|
||||||
</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>
|
</header>
|
||||||
<main class="page-shell">
|
<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 %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% 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">
|
<article class="card evidence-card">
|
||||||
<div class="case-legend">
|
<div class="case-legend">
|
||||||
<span class="section-label">Training floor board</span>
|
<span class="section-label">Training floor board</span>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="grid cols-2 dossier-grid">
|
<section id="weight-log" class="grid cols-2 dossier-grid">
|
||||||
<article class="card evidence-board">
|
<article class="card evidence-board">
|
||||||
<div class="case-legend"><span class="section-label">Intake docket</span><span class="pill">Weight log</span></div>
|
<div class="case-legend"><span class="section-label">Intake docket</span><span class="pill">Weight log</span></div>
|
||||||
<h2>Log weight</h2>
|
<h2>Log weight</h2>
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="grid cols-2 dossier-grid">
|
<section id="recent-workouts" class="grid cols-2 dossier-grid">
|
||||||
<article class="card evidence-board">
|
<article class="card evidence-board">
|
||||||
<div class="case-legend"><span class="section-label">Workout report</span><span class="pill">Quick log</span></div>
|
<div class="case-legend"><span class="section-label">Workout report</span><span class="pill">Quick log</span></div>
|
||||||
<h2>Quick log workout</h2>
|
<h2>Quick log workout</h2>
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</article>
|
</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>
|
<div class="case-legend"><span class="section-label">Current split</span><span class="pill">Routine board</span></div>
|
||||||
<h2>Current routine board</h2>
|
<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>
|
<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>
|
||||||
|
|||||||
116
home/doris-barbell/tests/test_doris_family_singular_site.py
Normal file
116
home/doris-barbell/tests/test_doris_family_singular_site.py
Normal 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'
|
||||||
@@ -2020,12 +2020,14 @@ def top_nav(active:str)->str:
|
|||||||
items=[
|
items=[
|
||||||
('Home','index.html','home'),
|
('Home','index.html','home'),
|
||||||
('Services','services.html','services'),
|
('Services','services.html','services'),
|
||||||
|
('Kitchen','http://10.5.30.7:8092','kitchen'),
|
||||||
|
('Schoolhouse','https://schoolhouse.paccoco.com','schoolhouse'),
|
||||||
|
('Barbell','http://10.5.30.6:8093','barbell'),
|
||||||
('Donetick','https://donetick.paccoco.com','donetick'),
|
('Donetick','https://donetick.paccoco.com','donetick'),
|
||||||
('Paperless','https://paperless.paccoco.com','paperless'),
|
('Paperless','https://paperless.paccoco.com','paperless'),
|
||||||
('n8n','https://n8n.paccoco.com','n8n'),
|
('n8n','https://n8n.paccoco.com','n8n'),
|
||||||
('Grafana','https://grafana.paccoco.com','grafana'),
|
('Grafana','https://grafana.paccoco.com','grafana'),
|
||||||
('Gitea','https://gitea.paccoco.com','gitea'),
|
('Gitea','https://gitea.paccoco.com','gitea'),
|
||||||
('Schoolhouse','https://schoolhouse.paccoco.com','schoolhouse'),
|
|
||||||
]
|
]
|
||||||
links=[]
|
links=[]
|
||||||
for label,url,key in items:
|
for label,url,key in items:
|
||||||
@@ -2043,7 +2045,30 @@ def top_nav(active:str)->str:
|
|||||||
|
|
||||||
def hot_fuzz_art(label:str)->str:
|
def hot_fuzz_art(label:str)->str:
|
||||||
safe_label=esc(label.upper())
|
safe_label=esc(label.upper())
|
||||||
return f"""<div class='hot-fuzz-art' aria-hidden='true'><div class='film-grain'></div><div class='cinematic-glow'></div><div class='casefile-stamp operator-evidence-tag'>Operator Evidence Board</div><svg viewBox='0 0 320 220' class='poster-illustration' role='presentation'><defs><linearGradient id='dashboard-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><path d='M126 82 h68 v14 h-68z' class='dossier-strip'></path><path d='M126 104 h82 v10 h-82z' class='dossier-strip'></path><path d='M126 122 h54 v10 h-54z' class='dossier-strip'></path><text x='160' y='204' class='poster-callout'>{safe_label}</text></svg></div>"""
|
return f"""<div class='art-stack'><div class='hot-fuzz-art' aria-hidden='true'><div class='film-grain'></div><div class='cinematic-glow'></div><div class='casefile-stamp operator-evidence-tag'>Operator Evidence Board</div><svg viewBox='0 0 320 220' class='poster-illustration' role='presentation'><defs><linearGradient id='dashboard-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='236' cy='64' r='54' 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 182 C109 148 116 124 134 108 C143 100 150 94 155 90 C162 94 170 100 178 108 C196 124 202 148 199 182 Z' class='constable-silhouette lead'></path><path d='M176 184 C174 154 180 132 196 116 C204 108 212 102 220 98 C229 102 236 108 244 116 C260 132 266 154 264 184 Z' class='constable-silhouette partner'></path><circle cx='72' cy='58' r='26' class='swan-stamp'></circle><path d='M64 62 C68 52 79 46 89 51 C81 50 76 55 77 61 C78 67 89 66 91 74 C82 76 71 74 66 67 L59 74 L54 69 L62 62 Z' class='swan-mark'></path><path d='M28 166 L94 124 L98 130 L32 172 Z' class='village-lane'></path><path d='M98 130 L136 150 L132 158 L88 136 Z' class='village-lane'></path><rect x='30' y='112' width='62' height='34' rx='6' class='evidence-card'></rect><path d='M38 120 h46 M38 128 h36 M38 136 h28' class='evidence-lines'></path><circle cx='120' cy='84' r='9' class='marker-dot'></circle><circle cx='138' cy='74' r='7' class='marker-dot marker-dot-alt'></circle><path d='M116 88 L104 116 L86 138' class='map-thread'></path><path d='M136 78 L160 96 L186 92' class='map-thread'></path><rect x='190' y='114' width='54' height='18' rx='6' class='radio-body'></rect><rect x='214' y='98' width='10' height='18' rx='4' class='radio-antenna'></rect><circle cx='238' cy='122' r='4' class='radio-knob'></circle><path d='M198 94 C214 86 232 86 246 94' class='beacon-arc'></path><path d='M202 86 C218 76 238 76 252 86' class='beacon-arc faint'></path><path d='M246 150 L272 130 L292 146 L292 184 L246 184 Z' class='church-block'></path><rect x='262' y='108' width='10' height='32' class='church-spire'></rect><path d='M252 150 H286 M258 160 H282' class='window-line'></path><text x='160' y='204' class='poster-callout'>{safe_label}</text></svg></div><div class='evidence-contact-grid'><section class='evidence-note-card case-contact-sheet' aria-label='Operator caseboard details'><div class='case-contact-top'><span class='case-contact-kicker'>Sandford caseboard</span><span class='case-contact-badge'>Neighbourhood Watch</span></div><svg viewBox='0 0 320 160' class='contact-sheet-illustration' role='presentation'><rect x='10' y='14' width='116' height='132' rx='16' class='contact-frame'></rect><rect x='22' y='30' width='92' height='56' rx='12' class='contact-photo'></rect><path d='M28 96 h80 M28 108 h68 M28 120 h50' class='contact-lines'></path><rect x='146' y='22' width='76' height='48' rx='10' class='contact-photo alt'></rect><rect x='154' y='84' width='108' height='52' rx='12' class='contact-photo alt'></rect><circle cx='254' cy='36' r='16' class='contact-marker'></circle><path d='M136 94 C154 82 174 80 190 88 C206 96 224 100 246 96' class='contact-thread'></path><path d='M126 42 C148 40 164 48 176 62' class='contact-thread faint'></path><text x='68' y='62' class='contact-label'>SWAN</text><text x='184' y='50' class='contact-label'>PUB</text><text x='208' y='114' class='contact-label'>SEA MINE</text></svg><ul class='case-contact-list'><li>Village-map pins and pub callouts keep the portal reading like a British police case wall.</li><li>Emergency arcs, spire silhouette, and evidence slips push the Hot Fuzz vibe ahead of the old stock-desk still.</li></ul></section><figure class='photo-evidence-card hero-photo-card supporting-photo-card'><img src='assets/hotfuzz-operator-desk.jpg' alt='Operator desk photo with corkboard, papers, and desktop gear.' loading='eager'><figcaption>Operator still · source logged in docs</figcaption></figure></div></div>"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_family_directory(active:str)->str:
|
||||||
|
entries=[
|
||||||
|
('Doris Dashboard','index.html','dashboard','Operator portal','Canonical front door for briefings, alerts, and launch control.'),
|
||||||
|
('Services Directory','services.html','services','Switchboard','Dense launch wall for the wider stack once you know where you need to go.'),
|
||||||
|
('Doris Kitchen','http://10.5.30.7:8092','kitchen','Pantry desk','Recipe leads, import repair, and meal-planning evidence review.'),
|
||||||
|
('Doris Schoolhouse','https://schoolhouse.paccoco.com','schoolhouse','Records desk','Assignments, recordings, D2L sync, and archive intake.'),
|
||||||
|
('Doris Barbell','http://10.5.30.6:8093','barbell','Training desk','Body metrics, routines, workouts, and progression dossiers.'),
|
||||||
|
]
|
||||||
|
cards=[]
|
||||||
|
active_key='dashboard' if active=='home' else active
|
||||||
|
for title,url,key,kicker,summary in entries:
|
||||||
|
classes=['family-app-card']
|
||||||
|
if key==active_key:
|
||||||
|
classes.append('current-desk')
|
||||||
|
kicker='Current desk'
|
||||||
|
attrs=[]
|
||||||
|
if url.startswith('http'):
|
||||||
|
attrs.append("target='_blank'")
|
||||||
|
attrs.append("rel='noreferrer'")
|
||||||
|
cards.append(f"<a class='{' '.join(classes)}' href='{esc(url)}' {' '.join(attrs)}><span class='family-app-kicker'>{esc(kicker)}</span><strong>{esc(title)}</strong><small>{esc(summary)}</small></a>")
|
||||||
|
return f"<section class='family-portal evidence-board casefile-directory'><div class='section-heading-inline case-legend'><div><p class='eyebrow'>Singular front door</p><h2>Doris family directory</h2></div><span class='muted'>One shell, separate runtimes.</span></div><div class='family-directory-grid'>{''.join(cards)}</div></section>"
|
||||||
|
|
||||||
|
|
||||||
def build_services_inventory()->tuple[list[dict[str,Any]],list[dict[str,Any]]]:
|
def build_services_inventory()->tuple[list[dict[str,Any]],list[dict[str,Any]]]:
|
||||||
@@ -2119,8 +2144,9 @@ def render_services_page(hero_summary:str,candidate_count:int,source_count:int,h
|
|||||||
groups_data, flat_items=build_services_inventory()
|
groups_data, flat_items=build_services_inventory()
|
||||||
services_html=build_services_directory(groups_data)
|
services_html=build_services_directory(groups_data)
|
||||||
inventory_html=render_services_inventory(flat_items)
|
inventory_html=render_services_inventory(flat_items)
|
||||||
|
family_html=render_family_directory('services')
|
||||||
utility_html=collapsible_section('Portal notes','Keep Doris as the front door, not a monolith.',"<section class='summary-grid'><article class='summary-card'><h3>How to use this</h3><p>Use Home for today, alerts, and the briefing. Use Services when you want the rest of the stack quickly without hunting bookmarks.</p></article><article class='summary-card'><h3>Design rule</h3><p>Shared navigation and status live here; full workflows still stay in their own apps.</p></article></section>",'portal-notes',open_by_default=True)+collapsible_section('Inventory','Coverage, access model, and host distribution.',inventory_html,'services-inventory',open_by_default=True)
|
utility_html=collapsible_section('Portal notes','Keep Doris as the front door, not a monolith.',"<section class='summary-grid'><article class='summary-card'><h3>How to use this</h3><p>Use Home for today, alerts, and the briefing. Use Services when you want the rest of the stack quickly without hunting bookmarks.</p></article><article class='summary-card'><h3>Design rule</h3><p>Shared navigation and status live here; full workflows still stay in their own apps.</p></article></section>",'portal-notes',open_by_default=True)+collapsible_section('Inventory','Coverage, access model, and host distribution.',inventory_html,'services-inventory',open_by_default=True)
|
||||||
return f"<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Doris Services</title><link rel='stylesheet' href='style.css'></head><body><div class='incident-tape' aria-hidden='true'></div><main>{top_nav('services')}<header class='hero casefile-header evidence-board'><div><p class='eyebrow'>Sandford services desk</p><div class='case-legend'><h1>Services Directory</h1><span class='hero-chip'>Operator board</span></div><p class='sub'>Shared navigation across your sites, with Doris still acting as the operational front door. {esc(hero_summary)}</p>{hot_fuzz_art('services')}<div class='brand-badges'><span class='pill badge-hotfuzz for-the-greater-good'>For the Greater Good</span><span class='pill'>Evidence locker: services</span></div></div><div class='status'><div><strong>{esc(now.strftime('%a %b %-d, %-I:%M %p'))}</strong><span>Central time</span></div><div><strong>{candidate_count}</strong><span>News candidates</span></div><div><strong>{source_count}</strong><span>Sources</span></div><div><strong>{esc(hero_tokens)}</strong><span>30-day Hermes tokens</span></div></div></header><div class='dashboard-shell services-shell dossier-grid'><section class='primary-column'>{services_html}</section><aside class='secondary-column'>{utility_html}</aside></div><footer><p>Operator portal links are curated from the homelab docs and current live dashboard context.</p><p>Local-only on NOMAD unless John says otherwise.</p></footer></main></body></html>"
|
return f"<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Doris Services</title><link rel='stylesheet' href='style.css'></head><body><div class='incident-tape' aria-hidden='true'></div><main>{top_nav('services')}<header class='hero casefile-header evidence-board'><div><p class='eyebrow'>Sandford services desk</p><div class='case-legend'><h1>Services Directory</h1><span class='hero-chip'>Operator board</span></div><p class='sub'>Shared navigation across your sites, with Doris still acting as the operational front door. {esc(hero_summary)}</p>{hot_fuzz_art('services')}<div class='brand-badges'><span class='pill badge-hotfuzz for-the-greater-good'>For the Greater Good</span><span class='pill'>Evidence locker: services</span></div></div><div class='status'><div><strong>{esc(now.strftime('%a %b %-d, %-I:%M %p'))}</strong><span>Central time</span></div><div><strong>{candidate_count}</strong><span>News candidates</span></div><div><strong>{source_count}</strong><span>Sources</span></div><div><strong>{esc(hero_tokens)}</strong><span>30-day Hermes tokens</span></div></div></header>{family_html}<div class='dashboard-shell services-shell dossier-grid'><section class='primary-column'>{services_html}</section><aside class='secondary-column'>{utility_html}</aside></div><footer><p>Operator portal links are curated from the homelab docs and current live dashboard context.</p><p>Local-only on NOMAD unless John says otherwise.</p></footer></main></body></html>"
|
||||||
|
|
||||||
def render()->str:
|
def render()->str:
|
||||||
candidates=load_json(DIGEST_ROOT/'data/candidates.json',[]); prefs=load_json(DIGEST_ROOT/'data/preferences.json',{})
|
candidates=load_json(DIGEST_ROOT/'data/candidates.json',[]); prefs=load_json(DIGEST_ROOT/'data/preferences.json',{})
|
||||||
@@ -2154,6 +2180,7 @@ def render()->str:
|
|||||||
system_html=build_system_section(system)
|
system_html=build_system_section(system)
|
||||||
hero_tokens=compact_number(hermes.get('total_tokens',0)) if hermes.get('ok') else '—'
|
hero_tokens=compact_number(hermes.get('total_tokens',0)) if hermes.get('ok') else '—'
|
||||||
snapshot_html=build_operator_snapshot(todos,pulse,paperless,hermes)
|
snapshot_html=build_operator_snapshot(todos,pulse,paperless,hermes)
|
||||||
|
family_html=render_family_directory('home')
|
||||||
hero_caseboard_html=build_hero_caseboard(feature_item,todos,paperless)
|
hero_caseboard_html=build_hero_caseboard(feature_item,todos,paperless)
|
||||||
feature_html=build_feature_story(secondary_feature_item)
|
feature_html=build_feature_story(secondary_feature_item)
|
||||||
session_window=find_usage_window(hermes.get('account_windows',[]),'session') if hermes.get('ok') else None
|
session_window=find_usage_window(hermes.get('account_windows',[]),'session') if hermes.get('ok') else None
|
||||||
@@ -2180,7 +2207,7 @@ def render()->str:
|
|||||||
return f"""<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><meta http-equiv='refresh' content='1800'><title>Doris Dashboard</title><link rel='stylesheet' href='style.css'></head><body data-donetick-focus-signature='{esc(focus_signature)}'><div class='incident-tape' aria-hidden='true'></div><main>{top_nav('home')}
|
return f"""<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><meta http-equiv='refresh' content='1800'><title>Doris Dashboard</title><link rel='stylesheet' href='style.css'></head><body data-donetick-focus-signature='{esc(focus_signature)}'><div class='incident-tape' aria-hidden='true'></div><main>{top_nav('home')}
|
||||||
<header class='hero casefile-header evidence-board'><div class='hero-main'><p class='eyebrow'>Sandford operator desk</p><div class='case-legend'><h1>Doris Dashboard</h1><span class='hero-chip'>Operator board</span></div><div class='hero-title-row'><div class='hero-chips'><span class='hero-chip'>Hermes {esc((hermes.get('models') or [{}])[0].get('model') or hermes.get('default_model') or 'unknown')}</span><span class='hero-chip'>Session {esc(pct_text((session_window or {}).get('remaining_percent')) if hermes.get('ok') else '—')}</span><span class='hero-chip'>24h {esc(compact_number(hermes.get('last_24h_tokens',0)) if hermes.get('ok') else '—')} tokens</span></div></div><p class='sub'>{esc(hero_summary)}</p>{hero_caseboard_html}</div><div class='hero-rail'><div class='status'><div><strong>{esc(now.strftime('%a %b %-d, %-I:%M %p'))}</strong><span>Central time · refreshes every 30m</span></div><div><strong>{len(candidates)}</strong><span>Candidates</span></div><div><strong>{len(set(i.get('source','') for i in candidates))}</strong><span>Sources</span></div><div><strong>{esc(hero_tokens)}</strong><span>30-day Hermes tokens</span></div></div></div></header>
|
<header class='hero casefile-header evidence-board'><div class='hero-main'><p class='eyebrow'>Sandford operator desk</p><div class='case-legend'><h1>Doris Dashboard</h1><span class='hero-chip'>Operator board</span></div><div class='hero-title-row'><div class='hero-chips'><span class='hero-chip'>Hermes {esc((hermes.get('models') or [{}])[0].get('model') or hermes.get('default_model') or 'unknown')}</span><span class='hero-chip'>Session {esc(pct_text((session_window or {}).get('remaining_percent')) if hermes.get('ok') else '—')}</span><span class='hero-chip'>24h {esc(compact_number(hermes.get('last_24h_tokens',0)) if hermes.get('ok') else '—')} tokens</span></div></div><p class='sub'>{esc(hero_summary)}</p>{hero_caseboard_html}</div><div class='hero-rail'><div class='status'><div><strong>{esc(now.strftime('%a %b %-d, %-I:%M %p'))}</strong><span>Central time · refreshes every 30m</span></div><div><strong>{len(candidates)}</strong><span>Candidates</span></div><div><strong>{len(set(i.get('source','') for i in candidates))}</strong><span>Sources</span></div><div><strong>{esc(hero_tokens)}</strong><span>30-day Hermes tokens</span></div></div></div></header>
|
||||||
<section class='snapshot-block evidence-board'><div class='section-heading-inline case-legend'><div><p class='eyebrow'>Today's operator snapshot</p><h2>What matters before the scroll</h2></div><span class='muted'>Glanceable heat across chores, Hermes, and the lab.</span></div>{snapshot_html}</section>
|
<section class='snapshot-block evidence-board'><div class='section-heading-inline case-legend'><div><p class='eyebrow'>Today's operator snapshot</p><h2>What matters before the scroll</h2></div><span class='muted'>Glanceable heat across chores, Hermes, and the lab.</span></div>{snapshot_html}</section>
|
||||||
<div class='dashboard-shell dossier-grid'><section class='primary-column'>{primary_html}</section><aside class='secondary-column'>{secondary_html}</aside></div>
|
{family_html}<div class='dashboard-shell dossier-grid'><section class='primary-column'>{primary_html}</section><aside class='secondary-column'>{secondary_html}</aside></div>
|
||||||
<footer><p>Category mix: {esc(', '.join(f'{k}: {v}' for k,v in counts.most_common(10)))}</p><p>Local-only on NOMAD unless John says otherwise. Weather from Open-Meteo; moon phase calculated locally. Hermes stats come from ~/.hermes/state.db.</p></footer></main>{feedback_bootstrap()}</body></html>"""
|
<footer><p>Category mix: {esc(', '.join(f'{k}: {v}' for k,v in counts.most_common(10)))}</p><p>Local-only on NOMAD unless John says otherwise. Weather from Open-Meteo; moon phase calculated locally. Hermes stats come from ~/.hermes/state.db.</p></footer></main>{feedback_bootstrap()}</body></html>"""
|
||||||
|
|
||||||
def main()->int:
|
def main()->int:
|
||||||
|
|||||||
BIN
home/doris-dashboard/public/assets/hotfuzz-operator-desk.jpg
Normal file
BIN
home/doris-dashboard/public/assets/hotfuzz-operator-desk.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
@@ -41,6 +41,40 @@ main{max-width:1380px;margin:0 auto;padding:18px}
|
|||||||
.incident-tape{height:14px;background:repeating-linear-gradient(135deg,var(--hf-amber) 0 18px,#111 18px 36px);box-shadow:0 3px 14px rgba(0,0,0,.32)}
|
.incident-tape{height:14px;background:repeating-linear-gradient(135deg,var(--hf-amber) 0 18px,#111 18px 36px);box-shadow:0 3px 14px rgba(0,0,0,.32)}
|
||||||
.casefile-header{position:relative}
|
.casefile-header{position:relative}
|
||||||
.hot-fuzz-art{position:relative;max-width:320px;margin:14px 0 0}
|
.hot-fuzz-art{position:relative;max-width:320px;margin:14px 0 0}
|
||||||
|
.art-stack{display:grid;gap:12px}
|
||||||
|
.photo-evidence-card{position:relative;margin:0;padding:10px;border-radius:22px;background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));border:1px solid rgba(242,239,221,.14);box-shadow:0 18px 36px rgba(0,0,0,.28);overflow:hidden}
|
||||||
|
.photo-evidence-card::before{content:'';position:absolute;inset:0;background:linear-gradient(180deg, rgba(242,193,78,.08), transparent 28%, rgba(145,15,63,.16));pointer-events:none}
|
||||||
|
.photo-evidence-card img{width:100%;aspect-ratio:16/10;object-fit:cover;display:block;border-radius:14px;filter:saturate(.82) sepia(.18) contrast(1.02)}
|
||||||
|
.photo-evidence-card figcaption{position:absolute;left:18px;right:18px;bottom:16px;z-index:1;margin:0;font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;color:#fff0cb;text-shadow:0 2px 10px rgba(0,0,0,.75)}
|
||||||
|
.evidence-contact-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(140px,.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,.05),rgba(255,255,255,.015)),rgba(15,18,25,.96);border:1px solid rgba(242,239,221,.11);box-shadow:0 16px 30px rgba(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));opacity:.95}
|
||||||
|
.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:.74rem;letter-spacing:.08em;text-transform:uppercase}
|
||||||
|
.case-contact-kicker{background:rgba(242,193,78,.12);border:1px solid rgba(242,193,78,.24);color:#ffe6aa}
|
||||||
|
.case-contact-badge{background:rgba(123,143,178,.16);border:1px solid rgba(123,143,178,.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,.24))}
|
||||||
|
.contact-frame{fill:rgba(11,14,20,.92);stroke:rgba(242,239,221,.12);stroke-width:2}
|
||||||
|
.contact-photo{fill:rgba(123,143,178,.22);stroke:rgba(242,239,221,.16);stroke-width:1.5}
|
||||||
|
.contact-photo.alt{fill:rgba(213,54,0,.18)}
|
||||||
|
.contact-lines,.contact-thread{fill:none;stroke:rgba(242,239,221,.86);stroke-width:4;stroke-linecap:round}
|
||||||
|
.contact-thread{stroke:rgba(213,54,0,.82);stroke-width:3}
|
||||||
|
.contact-thread.faint{stroke:rgba(123,143,178,.66)}
|
||||||
|
.contact-marker{fill:rgba(242,193,78,.18);stroke:rgba(242,193,78,.84);stroke-width:2}
|
||||||
|
.contact-label{fill:rgba(242,239,221,.9);font-size:15px;font-weight:800;letter-spacing:.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%}
|
||||||
|
.village-lane,.map-thread,.beacon-arc{fill:none;stroke-linecap:round;stroke-linejoin:round}
|
||||||
|
.village-lane{stroke:rgba(242,193,78,.88);stroke-width:7}
|
||||||
|
.map-thread{stroke:rgba(213,54,0,.86);stroke-width:3}
|
||||||
|
.beacon-arc{stroke:rgba(242,193,78,.9);stroke-width:4}
|
||||||
|
.beacon-arc.faint{stroke:rgba(123,143,178,.68)}
|
||||||
|
.evidence-card,.church-block,.radio-body{fill:rgba(242,239,221,.92)}
|
||||||
|
.evidence-lines,.window-line{fill:none;stroke:rgba(10,12,18,.82);stroke-width:4;stroke-linecap:round}
|
||||||
|
.window-line{stroke-width:3}
|
||||||
|
.marker-dot{fill:rgba(242,193,78,.94)}
|
||||||
|
.marker-dot-alt{fill:rgba(123,143,178,.9)}
|
||||||
|
.radio-antenna,.radio-knob,.church-spire{fill:rgba(242,193,78,.9)}
|
||||||
.poster-illustration{width:100%;height:auto;display:block;filter:drop-shadow(0 24px 30px rgba(0,0,0,.35))}
|
.poster-illustration{width:100%;height:auto;display:block;filter:drop-shadow(0 24px 30px rgba(0,0,0,.35))}
|
||||||
.poster-frame{fill:rgba(10,12,18,.88);stroke:rgba(242,239,221,.2);stroke-width:2}
|
.poster-frame{fill:rgba(10,12,18,.88);stroke:rgba(242,239,221,.2);stroke-width:2}
|
||||||
.poster-halo{fill:url(#dashboard-sunset);opacity:.9}
|
.poster-halo{fill:url(#dashboard-sunset);opacity:.9}
|
||||||
@@ -202,6 +236,11 @@ h1{
|
|||||||
margin:0;
|
margin:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-poster-stack .art-stack,
|
||||||
|
.hero-poster-stack .photo-evidence-card{
|
||||||
|
max-width:none;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-badges{justify-content:flex-start}
|
.hero-badges{justify-content:flex-start}
|
||||||
|
|
||||||
.hero-dossier-stack{justify-content:stretch}
|
.hero-dossier-stack{justify-content:stretch}
|
||||||
@@ -856,7 +895,7 @@ footer p{margin:.25rem 0}
|
|||||||
|
|
||||||
@media (max-width:920px){
|
@media (max-width:920px){
|
||||||
.hero{grid-template-columns:1fr}
|
.hero{grid-template-columns:1fr}
|
||||||
.status,.metrics,.metrics.metrics-compact,.focus-grid,.summary-grid,.news-list,.grid,.forecast-grid{grid-template-columns:1fr}
|
.status,.metrics,.metrics.metrics-compact,.focus-grid,.summary-grid,.news-list,.grid,.forecast-grid,.evidence-contact-grid{grid-template-columns:1fr}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width:720px){
|
@media (max-width:720px){
|
||||||
@@ -1097,3 +1136,11 @@ footer p{margin:.25rem 0}
|
|||||||
.hero-chips{justify-content:flex-start}
|
.hero-chips{justify-content:flex-start}
|
||||||
.snapshot-strip,.hero-slip-grid{grid-template-columns:1fr}
|
.snapshot-strip,.hero-slip-grid{grid-template-columns:1fr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.family-portal{margin:18px 0 20px;display:grid;gap:12px}
|
||||||
|
.family-directory-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px}
|
||||||
|
.family-app-card{display:grid;gap:6px;padding:14px;border-radius:16px;border:1px solid var(--line-soft);background:linear-gradient(180deg,rgba(17,24,39,.92),rgba(8,12,22,.96));box-shadow:inset 0 1px 0 rgba(255,255,255,.04),0 14px 28px rgba(0,0,0,.22);color:var(--text);text-decoration:none}
|
||||||
|
.family-app-card:hover,.family-app-card.current-desk{border-color:rgba(242,193,78,.42);transform:translateY(-1px)}
|
||||||
|
.family-app-kicker{font-size:.7rem;letter-spacing:.15em;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}
|
||||||
|
|||||||
49
home/doris-family-hotfuzz-graphics-sources-2026-05-24.md
Normal file
49
home/doris-family-hotfuzz-graphics-sources-2026-05-24.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Doris family Hot Fuzz graphics sources (2026-05-24)
|
||||||
|
|
||||||
|
Purpose: provenance/handoff for the first real-photo graphics pass across the Doris family sites.
|
||||||
|
|
||||||
|
Selection notes:
|
||||||
|
- Images were sourced through Openverse and fetched from the linked Flickr originals.
|
||||||
|
- This is a private household-only stack, so the goal here was practical themed imagery and a clean handoff, not a polished public redistribution package.
|
||||||
|
- The photos are intentionally treated as supporting stills now, not the primary thematic payload.
|
||||||
|
- The corrected pass also adds original in-repo SVG caseboard/contact-sheet graphics authored directly inside the Doris templates and dashboard generator on 2026-05-24. Those authored graphics are the main Hot Fuzz / British police procedural read; the sourced photos remain secondary texture under the same asset hooks.
|
||||||
|
|
||||||
|
## Doris Dashboard
|
||||||
|
- App path: `home/doris-dashboard/public/assets/hotfuzz-operator-desk.jpg`
|
||||||
|
- UI usage: operator-still card beneath the existing poster art in the dashboard hero stack
|
||||||
|
- Openverse query: `corkboard desk`
|
||||||
|
- Source title: `The new workspace`
|
||||||
|
- Creator: `lovelihood`
|
||||||
|
- License: `CC BY-SA`
|
||||||
|
- Source URL: `https://live.staticflickr.com/4148/5063431636_53c8b20835_b.jpg`
|
||||||
|
|
||||||
|
## Doris Barbell
|
||||||
|
- App path: `home/doris-barbell/app/static/images/hotfuzz-training-dossier.jpg`
|
||||||
|
- UI usage: training-still card beneath the existing dossier poster art
|
||||||
|
- Openverse query: `barbell plates gym`
|
||||||
|
- Source title: `Pause`
|
||||||
|
- Creator: `licornenoir`
|
||||||
|
- License: `CC BY-NC-SA`
|
||||||
|
- Source URL: `https://live.staticflickr.com/65535/49477674042_905e3f89b2_b.jpg`
|
||||||
|
|
||||||
|
## Doris Kitchen
|
||||||
|
- App path: `home/doris-kitchen/app/static/images/hotfuzz-pantry-evidence.jpg`
|
||||||
|
- UI usage: pantry-still card beneath the existing poster art
|
||||||
|
- Openverse query: `pantry spices shelf`
|
||||||
|
- Source title: `Larder: The Spice Shelf`
|
||||||
|
- Creator: `velodenz`
|
||||||
|
- License: `CC BY-NC-ND`
|
||||||
|
- Source URL: `https://live.staticflickr.com/2893/33377054132_873b33ee32_b.jpg`
|
||||||
|
|
||||||
|
## Doris Schoolhouse
|
||||||
|
- App path: `home/doris-schoolhouse/app/static/images/hotfuzz-records-desk.jpg`
|
||||||
|
- UI usage: records-still card beneath the existing poster art
|
||||||
|
- Openverse query: `study desk papers headphones`
|
||||||
|
- Source title: `(untitled)`
|
||||||
|
- Creator: `Kevin_Morris`
|
||||||
|
- License: `CC BY-NC`
|
||||||
|
- Source URL: `https://live.staticflickr.com/6168/6171438106_ae3c9e1291.jpg`
|
||||||
|
|
||||||
|
## Follow-up ideas
|
||||||
|
- If the sourcing/review lane wants a stronger exact-Hot-Fuzz vibe later, swap these with tighter police-procedural stills or custom-generated composites while keeping the same asset hooks and CSS card treatment.
|
||||||
|
- Dashboard and Schoolhouse are the weakest thematic matches; those are the first two worth upgrading if better source material lands.
|
||||||
@@ -149,6 +149,223 @@ label,
|
|||||||
min-width: min(100%, 280px);
|
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 {
|
||||||
|
fill: none;
|
||||||
|
stroke: rgba(242, 239, 221, 0.86);
|
||||||
|
stroke-width: 4;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-thread {
|
||||||
|
stroke: rgba(213, 54, 0, 0.82);
|
||||||
|
stroke-width: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.village-lane,
|
||||||
|
.map-thread {
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.village-lane {
|
||||||
|
stroke: rgba(242, 193, 78, 0.88);
|
||||||
|
stroke-width: 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-thread {
|
||||||
|
stroke: rgba(213, 54, 0, 0.86);
|
||||||
|
stroke-width: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-antenna {
|
||||||
|
fill: rgba(242, 193, 78, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
.poster-illustration {
|
.poster-illustration {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -525,6 +742,10 @@ button.danger { color: #ffd7d1; }
|
|||||||
padding: 18px;
|
padding: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.evidence-contact-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.hot-fuzz-art {
|
.hot-fuzz-art {
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -534,3 +755,94 @@ button.danger { color: #ffd7d1; }
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
home/doris-kitchen/app/static/images/hotfuzz-pantry-evidence.jpg
Normal file
BIN
home/doris-kitchen/app/static/images/hotfuzz-pantry-evidence.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@@ -4,9 +4,9 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{ title or 'Doris Kitchen' }}</title>
|
<title>{{ title or 'Doris Kitchen' }}</title>
|
||||||
<link rel="stylesheet" href="/static/app.css?v=9">
|
<link rel="stylesheet" href="/static/app.css?v=11">
|
||||||
</head>
|
</head>
|
||||||
<body class="doris-hot-fuzz app-kitchen">
|
<body class="doris-hot-fuzz doris-family-shell app-kitchen">
|
||||||
<div class="incident-tape" aria-hidden="true"></div>
|
<div class="incident-tape" aria-hidden="true"></div>
|
||||||
<header class="site-header casefile-header app-casefile app-casefile-kitchen">
|
<header class="site-header casefile-header app-casefile app-casefile-kitchen">
|
||||||
<div class="film-grain" aria-hidden="true"></div>
|
<div class="film-grain" aria-hidden="true"></div>
|
||||||
@@ -16,13 +16,12 @@
|
|||||||
<span class="nav-kicker">N.W.A. Case File</span>
|
<span class="nav-kicker">N.W.A. Case File</span>
|
||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-links" aria-label="Doris Kitchen 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.7:8092/">Kitchen</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/">Dashboard</a>
|
||||||
<a class="nav-link" href="/">Queue</a>
|
<a class="nav-link" href="http://10.5.30.7:8787/services.html">Services</a>
|
||||||
<a class="nav-link" href="/search">Search</a>
|
<a class="nav-link" href="https://schoolhouse.paccoco.com/">Schoolhouse</a>
|
||||||
<a class="nav-link" href="/planner">Planner</a>
|
<a class="nav-link" href="http://10.5.30.6:8093/">Barbell</a>
|
||||||
<a class="nav-link" href="/imports">Imports</a>
|
|
||||||
<a class="nav-link" href="/failures">Failures</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="casefile-title-row">
|
<div class="casefile-title-row">
|
||||||
@@ -37,32 +36,114 @@
|
|||||||
<span class="pill">Case type: Pantry Evidence</span>
|
<span class="pill">Case type: Pantry Evidence</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-fuzz-art" aria-hidden="true">
|
<div class="art-stack">
|
||||||
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
<div class="hot-fuzz-art" aria-hidden="true">
|
||||||
<defs>
|
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
||||||
<linearGradient id="kitchen-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
<defs>
|
||||||
<stop offset="0%" stop-color="#f2c14e"></stop>
|
<linearGradient id="kitchen-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
<stop offset="50%" stop-color="#d53600"></stop>
|
<stop offset="0%" stop-color="#f2c14e"></stop>
|
||||||
<stop offset="100%" stop-color="#910f3f"></stop>
|
<stop offset="50%" stop-color="#d53600"></stop>
|
||||||
</linearGradient>
|
<stop offset="100%" stop-color="#910f3f"></stop>
|
||||||
</defs>
|
</linearGradient>
|
||||||
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
</defs>
|
||||||
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
||||||
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
|
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
||||||
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
|
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></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="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></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>
|
<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>
|
||||||
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
<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>
|
||||||
<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>
|
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
||||||
<path d="M126 82 h68 v14 h-68z" class="recipe-card"></path>
|
<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>
|
||||||
<path d="M126 104 h82 v10 h-82z" class="recipe-card"></path>
|
<rect x="120" y="84" width="78" height="40" rx="8" class="evidence-card"></rect>
|
||||||
<path d="M126 122 h54 v10 h-54z" class="recipe-card"></path>
|
<path d="M130 96 h54 M130 106 h40 M130 116 h28" class="evidence-lines"></path>
|
||||||
<text x="160" y="204" class="poster-callout">PANTRY EVIDENCE</text>
|
<circle cx="210" cy="90" r="9" class="marker-dot"></circle>
|
||||||
</svg>
|
<circle cx="226" cy="104" r="6" class="marker-dot marker-dot-alt"></circle>
|
||||||
|
<path d="M186 118 L206 94 L224 104" class="map-thread"></path>
|
||||||
|
<path d="M54 132 C80 116 100 116 126 124" class="village-lane"></path>
|
||||||
|
<path d="M126 82 h68 v14 h-68z" class="recipe-card"></path>
|
||||||
|
<path d="M126 104 h82 v10 h-82z" class="recipe-card"></path>
|
||||||
|
<path d="M126 122 h54 v10 h-54z" class="recipe-card"></path>
|
||||||
|
<rect x="208" y="128" width="44" height="16" rx="6" class="radio-body"></rect>
|
||||||
|
<rect x="226" y="112" width="8" height="16" rx="4" class="radio-antenna"></rect>
|
||||||
|
<text x="160" y="204" class="poster-callout">PANTRY EVIDENCE</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="evidence-contact-grid">
|
||||||
|
<section class="evidence-note-card case-contact-sheet" aria-label="Pantry caseboard details">
|
||||||
|
<div class="case-contact-top">
|
||||||
|
<span class="case-contact-kicker">Village pantry sweep</span>
|
||||||
|
<span class="case-contact-badge">Pub + pantry</span>
|
||||||
|
</div>
|
||||||
|
<svg viewBox="0 0 320 160" class="contact-sheet-illustration" role="presentation">
|
||||||
|
<rect x="12" y="14" width="118" height="132" rx="16" class="contact-frame"></rect>
|
||||||
|
<rect x="24" y="28" width="94" height="48" rx="10" class="contact-photo"></rect>
|
||||||
|
<path d="M30 90 h82 M30 102 h68 M30 114 h52" class="contact-lines"></path>
|
||||||
|
<rect x="150" y="24" width="70" height="42" rx="10" class="contact-photo alt"></rect>
|
||||||
|
<rect x="168" y="82" width="112" height="54" rx="12" class="contact-photo alt"></rect>
|
||||||
|
<circle cx="246" cy="38" r="16" class="contact-marker"></circle>
|
||||||
|
<path d="M134 96 C154 82 174 80 192 88 C208 94 226 100 248 96" class="contact-thread"></path>
|
||||||
|
<text x="72" y="58" class="contact-label">PUB</text>
|
||||||
|
<text x="184" y="50" class="contact-label">MENU</text>
|
||||||
|
<text x="224" y="114" class="contact-label">SPICE</text>
|
||||||
|
</svg>
|
||||||
|
<ul class="case-contact-list">
|
||||||
|
<li>Recipe slips, red thread, and pantry callouts push this away from generic kitchen decor and into procedural evidence-board territory.</li>
|
||||||
|
<li>The old source photo stays as a supporting still, not the only obvious graphic.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<figure class="photo-evidence-card hero-photo-card supporting-photo-card">
|
||||||
|
<img src="/static/images/hotfuzz-pantry-evidence.jpg" alt="Pantry shelf photo packed with labeled spice jars and dry-goods containers." loading="eager">
|
||||||
|
<figcaption>Pantry still · source logged in docs</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<nav class="case-nav" aria-label="Doris Kitchen sections">
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path == '/' else '' }}" href="/">Queue board</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/search') else '' }}" href="/search">Lead intake</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/planner') else '' }}" href="/planner">Planner</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/imports') else '' }}" href="/imports">Import repair</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/failures') else '' }}" href="/failures">Failures</a>
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<main class="page-shell">
|
<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. Jump to the right desk without losing the family look.</p>
|
||||||
|
<div class="family-directory-grid">
|
||||||
|
<a class="family-app-card current-desk" href="http://10.5.30.7:8092/">
|
||||||
|
<span class="family-app-kicker">Current desk</span>
|
||||||
|
<strong>Doris Kitchen</strong>
|
||||||
|
<small>Recipe triage, search warrants, and pantry planning.</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 alerts, briefings, and launch control.</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 portal card wall for the broader stack.</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 archive intake.</small>
|
||||||
|
</a>
|
||||||
|
<a class="family-app-card" href="http://10.5.30.6:8093/">
|
||||||
|
<span class="family-app-kicker">Training desk</span>
|
||||||
|
<strong>Doris Barbell</strong>
|
||||||
|
<small>Weights, routines, workouts, and progression snapshots.</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
<script src="/static/app.js?v=6"></script>
|
<script src="/static/app.js?v=6"></script>
|
||||||
|
|||||||
@@ -60,6 +60,173 @@ body {
|
|||||||
flex: 0 1 320px;
|
flex: 0 1 320px;
|
||||||
min-width: min(100%, 280px);
|
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,
|
||||||
|
.village-lane,
|
||||||
|
.map-thread {
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: 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;
|
||||||
|
}
|
||||||
|
.village-lane {
|
||||||
|
stroke: rgba(242, 193, 78, 0.88);
|
||||||
|
stroke-width: 7;
|
||||||
|
}
|
||||||
|
.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); }
|
||||||
|
.radio-antenna { fill: rgba(242, 193, 78, 0.9); }
|
||||||
.poster-illustration {
|
.poster-illustration {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -223,6 +390,100 @@ button:hover { filter: brightness(1.06); }
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.site-header, .page-shell { padding: 18px; }
|
.site-header, .page-shell { padding: 18px; }
|
||||||
|
.evidence-contact-grid { grid-template-columns: 1fr; }
|
||||||
.stat-grid { grid-template-columns: 1fr; }
|
.stat-grid { grid-template-columns: 1fr; }
|
||||||
.hot-fuzz-art { flex-basis: 100%; min-width: 0; }
|
.hot-fuzz-art { flex-basis: 100%; min-width: 0; }
|
||||||
|
.casefile-title-row,
|
||||||
|
.brand-row { align-items: flex-start; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: 32 KiB |
@@ -4,9 +4,9 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{ title or 'Doris Schoolhouse' }}</title>
|
<title>{{ title or 'Doris Schoolhouse' }}</title>
|
||||||
<link rel="stylesheet" href="/static/app.css?v=4">
|
<link rel="stylesheet" href="/static/app.css?v=6">
|
||||||
</head>
|
</head>
|
||||||
<body class="doris-hot-fuzz app-schoolhouse">
|
<body class="doris-hot-fuzz doris-family-shell app-schoolhouse">
|
||||||
<div class="incident-tape" aria-hidden="true"></div>
|
<div class="incident-tape" aria-hidden="true"></div>
|
||||||
<header class="site-header casefile-header app-casefile app-casefile-schoolhouse">
|
<header class="site-header casefile-header app-casefile app-casefile-schoolhouse">
|
||||||
<div class="film-grain" aria-hidden="true"></div>
|
<div class="film-grain" aria-hidden="true"></div>
|
||||||
@@ -16,11 +16,12 @@
|
|||||||
<span class="nav-kicker">N.W.A. Case File</span>
|
<span class="nav-kicker">N.W.A. Case File</span>
|
||||||
<strong>Doris Constabulary</strong>
|
<strong>Doris Constabulary</strong>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-links" aria-label="Doris Schoolhouse navigation">
|
<nav class="nav-links doris-family-nav" aria-label="Doris family navigation">
|
||||||
|
<a class="nav-link {{ 'active' if request.url.path == '/' or request.url.path.startswith('/assignments') or request.url.path.startswith('/recordings') else '' }}" href="https://schoolhouse.paccoco.com/">Schoolhouse</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/">Dashboard</a>
|
||||||
<a class="nav-link" href="/">Dashboard</a>
|
<a class="nav-link" href="http://10.5.30.7:8787/services.html">Services</a>
|
||||||
<a class="nav-link" href="/assignments/upload">Assignment Upload</a>
|
<a class="nav-link" href="http://10.5.30.7:8092/">Kitchen</a>
|
||||||
<a class="nav-link" href="/recordings/upload">Recording Upload</a>
|
<a class="nav-link" href="http://10.5.30.6:8093/">Barbell</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="casefile-title-row">
|
<div class="casefile-title-row">
|
||||||
@@ -35,31 +36,111 @@
|
|||||||
<span class="pill">Case type: Records Intake</span>
|
<span class="pill">Case type: Records Intake</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hot-fuzz-art" aria-hidden="true">
|
<div class="art-stack">
|
||||||
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
<div class="hot-fuzz-art" aria-hidden="true">
|
||||||
<defs>
|
<svg viewBox="0 0 320 220" class="poster-illustration" role="presentation">
|
||||||
<linearGradient id="schoolhouse-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
<defs>
|
||||||
<stop offset="0%" stop-color="#f2c14e"></stop>
|
<linearGradient id="schoolhouse-sunset" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
<stop offset="50%" stop-color="#d53600"></stop>
|
<stop offset="0%" stop-color="#f2c14e"></stop>
|
||||||
<stop offset="100%" stop-color="#910f3f"></stop>
|
<stop offset="50%" stop-color="#d53600"></stop>
|
||||||
</linearGradient>
|
<stop offset="100%" stop-color="#910f3f"></stop>
|
||||||
</defs>
|
</linearGradient>
|
||||||
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
</defs>
|
||||||
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
<rect x="8" y="8" width="304" height="204" rx="24" class="poster-frame"></rect>
|
||||||
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></path>
|
<circle cx="228" cy="72" r="58" class="poster-halo"></circle>
|
||||||
<path d="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></path>
|
<path d="M18 170 L112 100 L134 124 L40 198 Z" class="poster-tape tape-left"></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="M202 82 L302 32 L314 60 L214 112 Z" class="poster-tape tape-right"></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>
|
<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>
|
||||||
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
<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>
|
||||||
<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>
|
<circle cx="84" cy="60" r="28" class="swan-stamp"></circle>
|
||||||
<rect x="124" y="84" width="78" height="54" rx="6" class="folder-body"></rect>
|
<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="134" y="74" width="36" height="14" rx="4" class="folder-tab"></rect>
|
<rect x="120" y="82" width="86" height="44" rx="8" class="evidence-card"></rect>
|
||||||
<text x="160" y="204" class="poster-callout">RECORDS INTAKE</text>
|
<path d="M130 94 h58 M130 106 h44 M130 118 h34" class="evidence-lines"></path>
|
||||||
</svg>
|
<circle cx="214" cy="88" r="8" class="marker-dot"></circle>
|
||||||
|
<circle cx="228" cy="102" r="6" class="marker-dot marker-dot-alt"></circle>
|
||||||
|
<path d="M176 118 L206 94 L226 102" class="map-thread"></path>
|
||||||
|
<path d="M126 144 C148 126 176 120 206 122" class="village-lane"></path>
|
||||||
|
<rect x="124" y="84" width="78" height="54" rx="6" class="folder-body"></rect>
|
||||||
|
<rect x="134" y="74" width="36" height="14" rx="4" class="folder-tab"></rect>
|
||||||
|
<rect x="210" y="128" width="42" height="16" rx="6" class="radio-body"></rect>
|
||||||
|
<rect x="228" y="112" width="8" height="16" rx="4" class="radio-antenna"></rect>
|
||||||
|
<text x="160" y="204" class="poster-callout">RECORDS INTAKE</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="evidence-contact-grid">
|
||||||
|
<section class="evidence-note-card case-contact-sheet" aria-label="Records caseboard details">
|
||||||
|
<div class="case-contact-top">
|
||||||
|
<span class="case-contact-kicker">Archive incident wall</span>
|
||||||
|
<span class="case-contact-badge">Chain of custody</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="28" width="92" height="50" rx="10" class="contact-photo"></rect>
|
||||||
|
<path d="M30 92 h80 M30 104 h68 M30 116 h50" class="contact-lines"></path>
|
||||||
|
<rect x="150" y="22" width="72" height="44" rx="10" class="contact-photo alt"></rect>
|
||||||
|
<rect x="164" y="84" width="108" height="52" rx="12" class="contact-photo alt"></rect>
|
||||||
|
<circle cx="248" cy="38" r="16" class="contact-marker"></circle>
|
||||||
|
<path d="M132 94 C154 80 176 80 196 88 C214 96 232 98 248 94" class="contact-thread"></path>
|
||||||
|
<text x="70" y="58" class="contact-label">D2L</text>
|
||||||
|
<text x="186" y="50" class="contact-label">AUDIO</text>
|
||||||
|
<text x="218" y="114" class="contact-label">FILE</text>
|
||||||
|
</svg>
|
||||||
|
<ul class="case-contact-list">
|
||||||
|
<li>Archive labels, custody language, and red-thread routing make Schoolhouse feel like a records room instead of a plain upload utility.</li>
|
||||||
|
<li>The old desk still remains as supporting texture, while the authored graphics now do the thematic heavy lifting.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<figure class="photo-evidence-card hero-photo-card supporting-photo-card">
|
||||||
|
<img src="/static/images/hotfuzz-records-desk.jpg" alt="Study desk photo with reading glasses, printed paperwork, and over-ear headphones." loading="eager">
|
||||||
|
<figcaption>Records still · source logged in docs</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<nav class="case-nav" aria-label="Doris Schoolhouse sections">
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path == '/' else '' }}" href="/">Course board</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/assignments/upload') else '' }}" href="/assignments/upload">Submission packet</a>
|
||||||
|
<a class="case-nav-link {{ 'active' if request.url.path.startswith('/recordings/upload') else '' }}" href="/recordings/upload">Audio evidence</a>
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<main class="page-shell">
|
<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. Jump between coursework, household ops, and training without losing the same evidence-room language.</p>
|
||||||
|
<div class="family-directory-grid">
|
||||||
|
<a class="family-app-card current-desk" href="https://schoolhouse.paccoco.com/">
|
||||||
|
<span class="family-app-kicker">Current desk</span>
|
||||||
|
<strong>Doris Schoolhouse</strong>
|
||||||
|
<small>Assignments, recordings, D2L sync, and archive handoffs.</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 rest of the 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>Meal discovery, import repair, and planner triage.</small>
|
||||||
|
</a>
|
||||||
|
<a class="family-app-card" href="http://10.5.30.6:8093/">
|
||||||
|
<span class="family-app-kicker">Training desk</span>
|
||||||
|
<strong>Doris Barbell</strong>
|
||||||
|
<small>Workout logs, body metrics, and progression dossiers.</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user