second commit
@@ -19,6 +19,9 @@
|
||||
--radius-sm: 14px;
|
||||
--panel-blur: 28px;
|
||||
--font-ui: "SF Pro Display", "Avenir Next", "Segoe UI Variable", "Helvetica Neue", system-ui, sans-serif;
|
||||
--track-accent: rgba(139, 228, 255, 0.34);
|
||||
--track-surface: rgba(255, 255, 255, 0.08);
|
||||
--track-glow: rgba(139, 228, 255, 0.18);
|
||||
}
|
||||
|
||||
*,
|
||||
@@ -110,7 +113,7 @@ button {
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
padding: 1.35rem;
|
||||
border-radius: var(--radius-xl);
|
||||
min-height: calc(100vh - 2.5rem);
|
||||
@@ -153,6 +156,24 @@ button {
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.meta-pill--button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.topbar-date-form {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.topbar-date-input {
|
||||
width: auto;
|
||||
min-height: 2.2rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 999px;
|
||||
padding: 0.45rem 0.9rem;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.chart-chip--warm {
|
||||
background: rgba(255, 173, 124, 0.12);
|
||||
}
|
||||
@@ -209,6 +230,9 @@ button {
|
||||
}
|
||||
|
||||
.main-nav a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8rem;
|
||||
padding: 0.9rem 1rem;
|
||||
border-radius: 18px;
|
||||
color: var(--muted);
|
||||
@@ -225,6 +249,14 @@ button {
|
||||
.sidebar-footer {
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
margin-top: auto;
|
||||
padding-top: 1.2rem;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.user-chip {
|
||||
@@ -497,6 +529,13 @@ button {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.section-head__actions {
|
||||
display: flex;
|
||||
gap: 0.7rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.field-grid--single {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -560,8 +599,9 @@ input[type="range"] {
|
||||
.range-card {
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: var(--track-surface);
|
||||
border: 1px solid var(--track-accent);
|
||||
transition: border-color 220ms ease, background 220ms ease, transform 220ms ease, box-shadow 220ms ease;
|
||||
}
|
||||
|
||||
.range-card output {
|
||||
@@ -570,6 +610,66 @@ input[type="range"] {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.preview-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-color: var(--track-accent);
|
||||
box-shadow: 0 24px 70px rgba(4, 18, 31, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.preview-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: auto -12% -25% auto;
|
||||
width: 14rem;
|
||||
height: 14rem;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, var(--track-glow), transparent 70%);
|
||||
pointer-events: none;
|
||||
transition: background 220ms ease, transform 220ms ease;
|
||||
}
|
||||
|
||||
.preview-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.preview-status__icon {
|
||||
width: 4.5rem;
|
||||
height: 4.5rem;
|
||||
border-radius: 20px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.preview-status__icon img {
|
||||
width: 2.3rem;
|
||||
height: 2.3rem;
|
||||
}
|
||||
|
||||
.preview-status__label {
|
||||
margin: 0;
|
||||
font-size: 1.55rem;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.preview-scoreline {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.preview-scoreline span {
|
||||
color: var(--text);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.component-list,
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
@@ -684,6 +784,17 @@ input[type="range"] {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.archive-item__actions {
|
||||
display: flex;
|
||||
gap: 0.55rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.archive-action {
|
||||
min-height: 2.4rem;
|
||||
padding-inline: 0.85rem;
|
||||
}
|
||||
|
||||
.note-box {
|
||||
padding: 1rem;
|
||||
border-radius: 18px;
|
||||
@@ -740,6 +851,68 @@ input[type="range"] {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="storm"] {
|
||||
--track-accent: rgba(255, 143, 143, 0.38);
|
||||
--track-surface: rgba(255, 143, 143, 0.08);
|
||||
--track-glow: rgba(255, 126, 126, 0.22);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="heavy"] {
|
||||
--track-accent: rgba(255, 191, 141, 0.34);
|
||||
--track-surface: rgba(255, 191, 141, 0.08);
|
||||
--track-glow: rgba(255, 191, 141, 0.18);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="steady"] {
|
||||
--track-accent: rgba(139, 228, 255, 0.34);
|
||||
--track-surface: rgba(255, 255, 255, 0.08);
|
||||
--track-glow: rgba(139, 228, 255, 0.18);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="bright"] {
|
||||
--track-accent: rgba(143, 243, 198, 0.34);
|
||||
--track-surface: rgba(143, 243, 198, 0.08);
|
||||
--track-glow: rgba(143, 243, 198, 0.2);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="radiant"] {
|
||||
--track-accent: rgba(255, 233, 140, 0.35);
|
||||
--track-surface: rgba(255, 233, 140, 0.1);
|
||||
--track-glow: rgba(255, 233, 140, 0.22);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood] .aurora-one,
|
||||
.page-track[data-track-mood] .aurora-two,
|
||||
.page-track[data-track-mood] .range-card,
|
||||
.page-track[data-track-mood] .preview-card,
|
||||
.page-track[data-track-mood] .preview-card::before {
|
||||
transition: background 220ms ease, opacity 220ms ease, transform 220ms ease, border-color 220ms ease, box-shadow 220ms ease;
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="storm"] .aurora-one,
|
||||
.page-track[data-track-mood="storm"] .aurora-two {
|
||||
opacity: 0.5;
|
||||
background: radial-gradient(circle, rgba(255, 128, 128, 0.32), transparent 70%);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="heavy"] .aurora-one,
|
||||
.page-track[data-track-mood="heavy"] .aurora-two {
|
||||
opacity: 0.55;
|
||||
background: radial-gradient(circle, rgba(255, 192, 128, 0.3), transparent 70%);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="bright"] .aurora-one,
|
||||
.page-track[data-track-mood="bright"] .aurora-two {
|
||||
opacity: 0.72;
|
||||
background: radial-gradient(circle, rgba(127, 243, 187, 0.3), transparent 70%);
|
||||
}
|
||||
|
||||
.page-track[data-track-mood="radiant"] .aurora-one,
|
||||
.page-track[data-track-mood="radiant"] .aurora-two {
|
||||
opacity: 0.78;
|
||||
background: radial-gradient(circle, rgba(255, 228, 122, 0.32), transparent 70%);
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.shell {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -778,6 +951,12 @@ input[type="range"] {
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.archive-item,
|
||||
.preview-status {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M4 7.5A3.5 3.5 0 0 1 7.5 4H10l1.2 1.6a2 2 0 0 0 1.6.8H16.5A3.5 3.5 0 0 1 20 9.9v6.6A3.5 3.5 0 0 1 16.5 20h-9A3.5 3.5 0 0 1 4 16.5v-9Z" fill="#DFF7FF" opacity=".18"/>
|
||||
<path d="M8 12h8" stroke="#DFF7FF" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M8 16h5" stroke="#8CFFD1" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 416 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<rect x="3" y="3" width="7" height="7" rx="2" fill="#DFF7FF"/>
|
||||
<rect x="14" y="3" width="7" height="11" rx="2" fill="#90E3FF"/>
|
||||
<rect x="3" y="14" width="7" height="7" rx="2" fill="#8CFFD1"/>
|
||||
<rect x="14" y="18" width="7" height="3" rx="1.5" fill="#DFF7FF"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 348 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<circle cx="24" cy="24" r="15" fill="#8CFFD1"/>
|
||||
<circle cx="19" cy="20.5" r="2" fill="#114F3C"/>
|
||||
<circle cx="29" cy="20.5" r="2" fill="#114F3C"/>
|
||||
<path d="M18 27.5c1.7 2.3 3.8 3.5 6 3.5s4.3-1.2 6-3.5" stroke="#114F3C" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 349 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<path d="M15 30c-4.4 0-8-3.4-8-7.6S10.6 15 15 15c1 0 1.9.2 2.8.5C20 11.6 24 9 28.6 9 35.4 9 41 14.3 41 21c0 .5 0 1-.1 1.5 3 1.4 5.1 4.4 5.1 7.9 0 4.9-4.1 8.6-9.3 8.6H15Z" fill="#FFC98F"/>
|
||||
<path d="M18 34c1.6 1.3 3.6 2 6 2s4.4-.7 6-2" stroke="#8E4C1F" stroke-width="3" stroke-linecap="round"/>
|
||||
<circle cx="19" cy="24" r="2" fill="#8E4C1F"/>
|
||||
<circle cx="29" cy="24" r="2" fill="#8E4C1F"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 476 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<circle cx="24" cy="24" r="12" fill="#FFE28A"/>
|
||||
<path d="M24 4v6" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M24 38v6" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M4 24h6" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M38 24h6" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M10 10l4 4" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M34 34l4 4" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M10 38l4-4" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M34 14l4-4" stroke="#FFE28A" stroke-width="3" stroke-linecap="round"/>
|
||||
<circle cx="19" cy="22" r="1.8" fill="#7A5A08"/>
|
||||
<circle cx="29" cy="22" r="1.8" fill="#7A5A08"/>
|
||||
<path d="M18 27.5c1.7 2.3 3.8 3.5 6 3.5s4.3-1.2 6-3.5" stroke="#7A5A08" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 994 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<circle cx="24" cy="24" r="15" fill="#A6E8FF"/>
|
||||
<circle cx="19" cy="21" r="2" fill="#174861"/>
|
||||
<circle cx="29" cy="21" r="2" fill="#174861"/>
|
||||
<path d="M18 29h12" stroke="#174861" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none">
|
||||
<path d="M15 29c-4.4 0-8-3.4-8-7.6S10.6 14 15 14c1 0 1.9.2 2.8.5C20 10.6 24 8 28.6 8 35.4 8 41 13.3 41 20c0 .5 0 1-.1 1.5 3 1.4 5.1 4.4 5.1 7.9 0 4.9-4.1 8.6-9.3 8.6H15Z" fill="#FFB2B2"/>
|
||||
<path d="M20 34l-2.4 5.4" stroke="#FFE6A6" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M28 34l-2.4 5.4" stroke="#FFE6A6" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M24 22c-2.2 0-4 1.8-4 4" stroke="#7A2437" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 540 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M5 7h14" stroke="#DFF7FF" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M5 17h14" stroke="#DFF7FF" stroke-width="2" stroke-linecap="round"/>
|
||||
<circle cx="9" cy="7" r="3" fill="#90E3FF"/>
|
||||
<circle cx="15" cy="17" r="3" fill="#8CFFD1"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<rect x="3" y="5" width="18" height="16" rx="4" fill="#DFF7FF" opacity=".18"/>
|
||||
<rect x="6" y="3" width="2" height="4" rx="1" fill="#DFF7FF"/>
|
||||
<rect x="16" y="3" width="2" height="4" rx="1" fill="#DFF7FF"/>
|
||||
<path d="M7 11.5h10" stroke="#DFF7FF" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M7 16.5h6" stroke="#8CFFD1" stroke-width="2" stroke-linecap="round"/>
|
||||
<circle cx="16.5" cy="16.5" r="2.5" fill="#90E3FF"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 511 B |
@@ -34,6 +34,10 @@
|
||||
return value || fallback;
|
||||
}
|
||||
|
||||
function moodIconPath(sentiment) {
|
||||
return `/assets/icons/mood-${sentiment}.svg`;
|
||||
}
|
||||
|
||||
function updateRangeOutputs() {
|
||||
document.querySelectorAll("[data-output-for]").forEach(output => {
|
||||
const input = document.querySelector(`[name="${output.dataset.outputFor}"]`);
|
||||
@@ -50,6 +54,17 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initHeaderDatePicker() {
|
||||
document.querySelectorAll(".topbar-date-input").forEach(input => {
|
||||
input.addEventListener("change", () => {
|
||||
const form = input.closest("form");
|
||||
if (form) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function sleepDurationPoints(hours, points) {
|
||||
if (hours < 4) {
|
||||
return Number(points.lt4 || 0);
|
||||
@@ -123,6 +138,35 @@
|
||||
return currentIndex > capIndex ? cap : current;
|
||||
}
|
||||
|
||||
function sentimentForLabel(label, ratings) {
|
||||
const order = ratings.map(item => item.label);
|
||||
const index = order.indexOf(label);
|
||||
|
||||
if (index === -1 || order.length <= 1) {
|
||||
return "steady";
|
||||
}
|
||||
|
||||
const ratio = index / Math.max(order.length - 1, 1);
|
||||
|
||||
if (ratio <= 0.1) {
|
||||
return "storm";
|
||||
}
|
||||
|
||||
if (ratio <= 0.35) {
|
||||
return "heavy";
|
||||
}
|
||||
|
||||
if (ratio <= 0.65) {
|
||||
return "steady";
|
||||
}
|
||||
|
||||
if (ratio <= 0.9) {
|
||||
return "bright";
|
||||
}
|
||||
|
||||
return "radiant";
|
||||
}
|
||||
|
||||
function evaluateEntry(entry, settings) {
|
||||
const ratings = sortedRatings(settings.ratings || []);
|
||||
const scoring = settings.scoring || {};
|
||||
@@ -151,7 +195,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
return { total, label, components };
|
||||
return { total, label, components, sentiment: sentimentForLabel(label, ratings) };
|
||||
}
|
||||
|
||||
function initTrackPreview() {
|
||||
@@ -169,6 +213,7 @@
|
||||
|
||||
const totalNode = card.querySelector("[data-preview-total]");
|
||||
const labelNode = card.querySelector("[data-preview-label]");
|
||||
const iconNode = card.querySelector("[data-preview-icon]");
|
||||
const componentsNode = card.querySelector("[data-preview-components]");
|
||||
const componentLabels = {
|
||||
mood: "Stimmung",
|
||||
@@ -196,6 +241,9 @@
|
||||
const result = evaluateEntry(collect(), payload.settings);
|
||||
totalNode.textContent = formatNumber(result.total);
|
||||
labelNode.textContent = result.label;
|
||||
iconNode.src = moodIconPath(result.sentiment);
|
||||
card.dataset.sentiment = result.sentiment;
|
||||
document.body.dataset.trackMood = result.sentiment;
|
||||
componentsNode.innerHTML = Object.entries(result.components).map(([key, value]) => {
|
||||
return `<div><dt>${componentLabels[key] || key}</dt><dd>${formatNumber(Number(value))}</dd></div>`;
|
||||
}).join("");
|
||||
@@ -419,6 +467,7 @@
|
||||
}
|
||||
|
||||
updateRangeOutputs();
|
||||
initHeaderDatePicker();
|
||||
initTrackPreview();
|
||||
initDashboardCharts();
|
||||
})();
|
||||
|
||||