Family Home Lab: portal, dsh (chat+plugins), transcriber, music/media tools, home dash

This commit is contained in:
2026-08-26 11:30:34 +10:00
commit 1110dbc978
62 changed files with 4237 additions and 0 deletions

330
portal/static/app.css Normal file
View File

@@ -0,0 +1,330 @@
/* ============================================================
Family Home Lab — Components & Layout
Builds strictly from tokens.css variables per DESIGN.md.
============================================================ */
* { box-sizing: border-box; }
body {
margin: 0;
font-family: var(--font-family);
background: var(--canvas-soft);
color: var(--ink-secondary);
font: var(--type-body-md);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }
/* -------------------------------------------------- Login */
.login-screen {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--secondary);
color: var(--surface);
position: relative;
overflow: hidden;
padding: var(--space-lg);
}
/* Subtle starfield/sticker dots */
.login-screen::before {
content: "";
position: absolute; inset: 0;
background-image:
radial-gradient(rgba(255,255,255,.16) 1px, transparent 1.5px),
radial-gradient(rgba(255,255,255,.1) 1px, transparent 1.5px);
background-size: 44px 44px, 71px 71px;
background-position: 0 0, 17px 21px;
pointer-events: none;
}
.login-wrap { position: relative; z-index: 1; width: 100%; max-width: 400px; }
.login-greeting {
margin: 0 0 var(--space-xxl);
font: var(--type-display-1);
color: var(--surface);
letter-spacing: -1.5px;
}
.auth-card {
background: var(--surface);
border-radius: var(--rounded-lg);
box-shadow: var(--shadow-level-2);
padding: var(--space-xl) var(--space-xl);
color: var(--ink-secondary);
}
.auth-title { font: var(--type-title); margin: 0 0 var(--space-md); color: var(--ink); }
/* -------------------------------------------------- Form bits */
.form-field { margin-bottom: var(--space-md); }
.form-field label {
display: block;
font: var(--type-eyebrow);
letter-spacing: 0.125px;
color: var(--ink-muted);
margin-bottom: var(--space-xs);
}
.text-input {
width: 100%;
padding: 10px 12px;
font: var(--type-body-sm);
color: var(--ink);
border: 1px solid rgb(221, 221, 221);
border-radius: var(--rounded-xs);
background: var(--surface);
}
.text-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: var(--shadow-level-1), 0 0 0 3px rgba(0, 117, 222, 0.18);
}
.form-error {
background: #fdecec;
color: #b3261e;
border: 1px solid #f5c6c4;
border-radius: var(--rounded-sm);
padding: var(--space-sm);
font: var(--type-body-sm);
margin-bottom: var(--space-md);
}
/* -------------------------------------------------- Buttons */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
border: none;
cursor: pointer;
font: var(--type-button);
text-decoration: none;
transition: transform .08s ease, background .15s ease, box-shadow .15s ease;
}
.button:active { transform: scale(0.97); }
button.button, input[type="submit"].button { font-family: inherit; }
.button-primary {
background: var(--primary);
color: var(--on-primary);
border-radius: var(--rounded-full);
padding: 12px 28px;
}
.button-primary:hover { background: var(--primary-active); color: var(--on-primary); text-decoration: none; }
.button-secondary {
background: var(--surface);
color: var(--ink);
border-radius: var(--rounded-full);
padding: 11px 26px;
box-shadow: var(--shadow-level-1);
border: 1px solid var(--hairline);
}
.button-secondary:hover { box-shadow: var(--shadow-level-1); }
.button-utility {
background: var(--surface);
color: var(--ink);
border-radius: var(--rounded-md);
padding: 4px 14px;
border: 1px solid var(--hairline);
font: var(--type-body-sm);
}
.button-utility:hover { background: var(--canvas-soft); }
.width-full { width: 100%; }
/* -------------------------------------------------- Nav bar */
.nav-bar {
position: sticky;
top: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-lg);
background: var(--surface);
border-bottom: 1px solid var(--hairline);
padding: var(--space-sm) var(--space-xxl);
}
.nav-wordmark {
font: var(--type-heading-2);
color: var(--ink);
letter-spacing: -0.25px;
text-decoration: none;
}
.nav-wordmark:hover { text-decoration: none; }
.nav-right { display: flex; align-items: center; gap: var(--space-md); }
.nav-user { display: flex; align-items: center; gap: var(--space-xs); font: var(--type-body-sm); color: var(--ink-secondary); }
.nav-avatar {
width: 28px; height: 28px;
border-radius: var(--rounded-full);
background: var(--primary);
color: var(--on-primary);
display: flex; align-items: center; justify-content: center;
font: var(--type-button);
}
/* -------------------------------------------------- Layout */
.page { max-width: var(--container-max); margin: 0 auto; padding: var(--space-xxl) var(--space-lg); }
.greeting { font: var(--type-heading-1); color: var(--ink); margin: 0 0 var(--space-xxl); }
.section { margin-bottom: var(--space-xl); }
.section-head { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-md); }
.category-pill {
display: inline-flex;
align-items: center;
gap: 6px;
font: var(--type-eyebrow);
letter-spacing: 0.125px;
background: var(--surface);
border-radius: var(--rounded-full);
padding: 4px 8px;
border: 1px solid var(--hairline);
}
.pill-dot { width: 8px; height: 8px; border-radius: var(--rounded-full); }
.section-title { font: var(--type-heading-2); color: var(--ink); margin: 0; }
/* -------------------------------------------------- Tool grid */
.tool-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-lg);
}
@media (max-width: 840px) { .tool-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .tool-grid { grid-template-columns: 1fr; } }
.tool-card {
display: flex;
flex-direction: column;
gap: var(--space-sm);
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: var(--rounded-lg);
padding: var(--space-lg);
text-decoration: none;
color: var(--ink-secondary);
transition: box-shadow .18s ease, transform .18s ease;
}
.tool-card:hover {
box-shadow: var(--shadow-level-1);
transform: translateY(-1px);
text-decoration: none;
}
.tool-card-top { display: flex; align-items: flex-start; justify-content: space-between; }
.tool-icon {
width: 40px; height: 40px;
border-radius: var(--rounded-full);
display: flex; align-items: center; justify-content: center;
color: var(--surface);
font: var(--type-title);
flex: none;
}
.tool-card .tool-name { font: var(--type-title); color: var(--ink); margin: 0; }
.tool-card .tool-desc { font: var(--type-body-sm); color: var(--ink-muted); margin: 0; flex: 1; }
.tool-card-bottom { display: flex; align-items: center; justify-content: space-between; }
.launch-chip { font: var(--type-caption); color: var(--ink-faint); }
.tag-chip {
font: var(--type-caption);
color: var(--ink-muted);
border: 1px solid var(--hairline);
border-radius: var(--rounded-full);
padding: 1px 8px;
background: var(--surface-2);
}
.tag-chip-login { color: var(--sticker-orange); border-color: color-mix(in srgb, var(--sticker-orange) 40%, transparent); }
.tag-chip-admin { color: var(--ink-muted); border-style: dashed; }
/* catalogue search */
.dash-search {
max-width: 480px;
margin-bottom: var(--space-lg);
}
.tool-grid .tool-card.hidden { display: none; }
.section.hidden-section { display: none; }
/* status dot levels */
.status-dot {
width: 8px; height: 8px; border-radius: var(--rounded-full); display: inline-block;
}
.status-online { background: var(--sticker-green); }
.status-starting { background: var(--sticker-orange); }
.status-offline { background: var(--ink-faint); }
/* loading pulse for polling */
.tool-grid.htmx-request .tool-card { opacity: .6; transition: opacity .2s; }
/* -------------------------------------------------- Tool embed */
.tool-bar {
position: sticky; top: 52px; z-index: 15;
display: flex; align-items: center; justify-content: space-between;
gap: var(--space-md);
background: var(--surface);
border-bottom: 1px solid var(--hairline);
padding: var(--space-sm) var(--space-xxl);
}
.embed-well {
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: var(--rounded-lg);
overflow: hidden;
height: calc(100vh - 170px);
box-shadow: var(--shadow-level-1);
}
.embed-well iframe { width: 100%; height: 100%; border: 0; display: block; }
/* -------------------------------------------------- Link-out fallback */
.linkout-card {
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: var(--rounded-lg);
padding: var(--space-xl);
max-width: 520px;
box-shadow: var(--shadow-level-1);
}
/* -------------------------------------------------- Partial rows (status) */
.status-refresh {
display: contents; /* keeps the HTMX swap invisible-pressure-free */
}
/* -------------------------------------------------- Admin */
.user-table { width: 100%; border-collapse: collapse; background: var(--surface); border-radius: var(--rounded-lg); overflow: hidden; border: 1px solid var(--hairline); }
.user-table th, .user-table td { text-align: left; padding: var(--space-sm) var(--space-md); border-bottom: 1px solid var(--hairline); font: var(--type-body-sm); }
.user-table th { background: var(--canvas-soft); color: var(--ink-muted); font: var(--type-eyebrow); letter-spacing: .125px; }
.user-table tr:last-child td { border-bottom: none; }
.badge { display: inline-block; font: var(--type-eyebrow); border-radius: var(--rounded-full); padding: 2px 8px; background: var(--canvas-soft); color: var(--ink-muted); }
.badge-admin { background: var(--secondary); color: var(--surface); }
.admin-panel { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-xxl); align-items: start; }
@media (max-width: 840px) { .admin-panel { grid-template-columns: 1fr; } }
/* -------------------------------------------------- Workspace / file list */
.tool-detail { display: flex; flex-direction: column; gap: var(--space-lg); }
.workspace {
background: var(--surface);
border: 1px solid var(--hairline);
border-radius: var(--rounded-lg);
padding: var(--space-lg) var(--space-xl);
box-shadow: var(--shadow-level-1);
}
.workspace-head {
display: flex; align-items: baseline; justify-content: space-between;
gap: var(--space-md); margin-bottom: var(--space-sm);
}
.file-list { list-style: none; margin: 0; padding: 0; }
.file-list li {
display: flex; align-items: center; justify-content: space-between;
gap: var(--space-md); padding: 10px 2px;
border-bottom: 1px solid var(--hairline); font: var(--type-body-sm);
}
.file-list li:last-child { border-bottom: none; }
.file-name { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-meta { color: var(--ink-faint); font: var(--type-caption); white-space: nowrap; }
/* -------------------------------------------------- Minimal, generic */
.muted { color: var(--ink-muted); }
.faint { color: var(--ink-faint); }
.margin-top { margin-top: var(--space-lg); }
.stack > * + * { margin-top: var(--space-lg); }