Dashboard Studio 1.12.0: new cards, kiosk doorbell, night mode, editor tools, version history

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 15:12:30 +01:00
parent f9822a9c49
commit 7abf7379d8
11 changed files with 1728 additions and 182 deletions
+283 -47
View File
@@ -190,12 +190,20 @@ textarea.input { resize: vertical; min-height: 76px; font-family: ui-monospace,
.color-row { display: flex; gap: 6px; }
.color-row input[type=color] { width: 34px; height: 34px; padding: 0; border: 1px solid var(--separator); border-radius: 9px; background: none; flex: none; cursor: pointer; }
.icon-row { display: flex; gap: 6px; align-items: center; }
.ip-search { margin: 12px 0 10px; }
.ip-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); gap: 6px; max-height: 50vh; overflow-y: auto; padding: 2px; }
.ip-icon { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 4px 8px; border: 0; border-radius: 10px; background: var(--fill); cursor: pointer; min-width: 0; }
.ip-icon .i { font-size: 22px; }
.ip-icon span { font-size: 11px; color: var(--text-2); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ip-icon:hover { background: var(--fill-strong); }
.ip-icon.on { box-shadow: inset 0 0 0 2px var(--accent); }
.icon-prev { border: 0; cursor: pointer; color: inherit; }
.icon-row .icon-prev { width: 34px; height: 34px; border-radius: 9px; background: var(--fill); display: grid; place-items: center; flex: none; font-size: 18px; }
.ent-item { display: flex; gap: 6px; margin-bottom: 6px; }
.ent-item .btn { flex: none; }
.ent-hint { font-size: 13px; color: var(--text-2); margin: 4px 2px 0; }
.insp-actions { display: flex; gap: 8px; margin-top: 18px; }
.insp-actions .btn { flex: 1; }
.insp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.insp-actions .btn { flex: 1 1 auto; white-space: nowrap; }
.presets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.preset { height: 52px; border-radius: 10px; border: 2px solid transparent; padding: 0; position: relative; overflow: hidden; cursor: pointer; }
.preset span { position: absolute; left: 7px; bottom: 5px; font-size: 12px; font-weight: 600; }
@@ -305,6 +313,22 @@ body.editing .w { cursor: grab; }
body.editing .w-body { pointer-events: none; }
body.editing .surface-plain .w, body.editing .w.transparent { outline: 1px dashed var(--d-separator); outline-offset: -1px; }
body.editing .w.selected { outline: 2px solid var(--accent); outline-offset: 2px; z-index: 5; }
body.editing .w.drop-into { outline: 3px solid var(--accent); outline-offset: 4px; }
body.editing .w.drop-into::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: color-mix(in srgb, var(--accent) 18%, transparent); pointer-events: none; }
.align-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.align-grid .btn { justify-content: center; }
#palette-search { width: 100%; margin-bottom: 10px; }
.ent-pop { position: fixed; z-index: 120; overflow-y: auto; background: var(--surface, var(--bg)); border: 1px solid var(--separator); border-radius: 12px; box-shadow: 0 12px 32px rgba(0,0,0,.18); padding: 4px; }
.ent-pop[hidden] { display: none; }
.ep-item { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: 8px; cursor: pointer; min-height: 40px; }
.ep-item:hover { background: var(--fill); }
.ep-item.on { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.ep-item > svg { width: 20px; height: 20px; flex: none; color: var(--text-2, inherit); }
.ep-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.ep-name { font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ep-id { font-size: 11px; color: var(--text-2, #8e8e93); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ep-state { font-size: 11px; color: var(--text-2, #8e8e93); flex: none; max-width: 30%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ep-none { padding: 10px; font-size: 13px; color: var(--text-2, #8e8e93); }
.w.dragging { transition: none; z-index: 20; cursor: grabbing; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3); }
.w-resize { position: absolute; right: 2px; bottom: 2px; width: 24px; height: 24px; cursor: nwse-resize; z-index: 3; opacity: 0; transition: opacity .1s; color: var(--accent); }
.w-resize::after { content: ""; position: absolute; right: 5px; bottom: 5px; width: 10px; height: 10px; border-right: 3px solid currentColor; border-bottom: 3px solid currentColor; border-bottom-right-radius: 4px; }
@@ -411,7 +435,7 @@ body.editing .w:hover .w-resize, body.editing .w.selected .w-resize { opacity: 1
.row-ico { font-size: 20px; display: flex; color: var(--d-text-2); }
.list-row .grow { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 15px; }
.list-row .st { color: var(--d-text-2); font-size: 14px; white-space: nowrap; }
.switch { width: 46px; height: 28px; border-radius: 14px; background: var(--d-fill); position: relative; flex: none; transition: background-color .2s; }
.switch { display: inline-block; vertical-align: middle; width: 46px; height: 28px; border-radius: 14px; background: var(--d-fill); position: relative; flex: none; transition: background-color .2s; }
.switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 24px; height: 24px; border-radius: 50%; background: #fff; transition: transform .2s; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25); }
[aria-checked="true"] > .switch { background: var(--ok); }
[aria-checked="true"] > .switch::after { transform: translateX(18px); }
@@ -653,6 +677,45 @@ body.kiosk #tabnav { top: 6px; max-height: calc(100vh - 20px); }
.pos9 button[aria-pressed="true"] { background: var(--surface); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); }
.pos9 button[aria-pressed="true"] i { width: 14px; height: 10px; border-radius: 3px; background: var(--accent); opacity: 1; }
/* light card */
.light-head { display: flex; align-items: center; gap: 12px; min-width: 0; }
.light-badge { cursor: pointer; }
.light-toggle { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.light-switch { flex: none; display: flex; }
.lm-switch { display: flex; }
.swatches { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; flex: none; }
.swatch { width: 26px; height: 26px; border-radius: 50%; background: var(--sw); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); flex: none; display: grid; place-items: center; font-size: 15px; }
.swatches.compact .swatch { width: 22px; height: 22px; }
.swatch.more { background: var(--d-fill); }
.swatch:hover { transform: scale(1.1); }
.light.is-group { justify-content: flex-start; gap: 10px; }
.light-members2 { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; margin: 0 -4px; padding: 0 4px; }
.lm-row { display: grid; grid-template-columns: 36px minmax(0, 1fr) auto auto; align-items: center; gap: 10px; padding: 6px 8px; border-radius: 12px; background: var(--d-fill); }
.lm-dot { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; font-size: 18px; background: color-mix(in srgb, var(--d-text) 8%, transparent); }
.lm-row.on .lm-dot { background: var(--tint, var(--d-lit)); color: #2a1d05; }
.lm-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.lm-name { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lm-state { font-size: 12px; color: var(--d-text-2); }
.lm-slider { height: 8px; border-radius: 4px; }
.lm-slider::-webkit-slider-thumb { height: 16px; width: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.lm-slider::-moz-range-thumb { height: 16px; width: 16px; border-radius: 50%; background: #fff; }
.lm-pct { font-size: 12px; color: var(--d-text-2); min-width: 32px; text-align: right; }
.w.lit-lamp .lm-row { background: rgba(255, 255, 255, 0.45); }
.w.lit-lamp .swatch.more { background: rgba(255, 255, 255, 0.55); }
/* light popup */
.lp-head { display: flex; align-items: center; gap: 12px; }
.lp-head p { margin: 2px 0 0; }
.lp-dot { width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center; font-size: 24px; background: var(--tint); color: #2a1d05; flex: none; }
.lp-slider { -webkit-appearance: none; appearance: none; width: 100%; height: 48px; border-radius: 14px; margin: 0; cursor: pointer;
background: linear-gradient(to right, var(--fill-c, #ffd58a) var(--pct, 0%), var(--fill) var(--pct, 0%)); }
.lp-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 6px; height: 30px; border-radius: 3px; background: rgba(255, 255, 255, .9); box-shadow: 0 1px 3px rgba(0,0,0,.3); }
.lp-slider::-moz-range-thumb { width: 6px; height: 30px; border: 0; border-radius: 3px; background: #fff; }
.lp-temp { height: 36px; background: linear-gradient(to right, #ffb35c, #fff4e5 55%, #cfe2ff); }
.lp-swatches { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.swatch.big { width: 38px; height: 38px; border: 0; cursor: pointer; }
.swatch.custom { background: conic-gradient(red, yellow, lime, cyan, blue, magenta, red); color: #fff; position: relative; overflow: hidden; }
.swatch.custom input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
/* light groups */
.light-members { flex: 1; min-height: 0; margin: 0 -6px; }
.light:has(.light-members) { justify-content: flex-start; }
@@ -677,6 +740,145 @@ body.kiosk #tabnav { top: 6px; max-height: calc(100vh - 20px); }
.ef-totals { flex: none; display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; color: var(--d-text-2); justify-content: center; }
.ef-total b { color: var(--d-text); font-size: 13px; }
/* to-do */
.todo { display: flex; flex-direction: column; height: 100%; gap: 10px; }
.todo-head { display: flex; align-items: center; gap: 8px; }
.todo-head .name { flex: 1; min-width: 0; font-size: 17px; }
.todo-count { font-size: 13px; font-weight: 700; min-width: 24px; height: 24px; padding: 0 7px; border-radius: 12px; display: grid; place-items: center; background: var(--d-fill); }
.todo-add { display: flex; gap: 6px; flex: none; margin: 0; }
.todo-input { flex: 1; min-width: 0; min-height: 40px; padding: 0 12px; border: 0; border-radius: 12px; background: var(--d-fill); color: inherit; font: inherit; outline: none; }
.todo-input:focus { box-shadow: var(--focus); }
.todo-plus { width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center; background: var(--d-text); color: var(--d-tile); font-size: 20px; flex: none; }
.todo-list { flex: 1; min-height: 0; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.todo-row { display: flex; align-items: center; gap: 10px; min-height: 44px; border-bottom: 1px solid var(--d-separator); transition: transform .15s, opacity .15s; touch-action: pan-y; }
.todo-check { width: 24px; height: 24px; border-radius: 50%; border: 2px solid var(--d-text-2); display: grid; place-items: center; font-size: 14px; color: transparent; flex: none; }
.todo-row.done .todo-check { background: var(--ok); border-color: var(--ok); color: #fff; }
.todo-text { flex: 1; min-width: 0; font-size: 15px; display: flex; flex-direction: column; }
.todo-text small { font-size: 12px; color: var(--d-text-2); }
.todo-row.done .todo-text { color: var(--d-text-2); text-decoration: line-through; }
.todo-del { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; color: var(--d-text-2); opacity: 0; font-size: 15px; }
.todo-row:hover .todo-del, .todo-del:focus-visible { opacity: 1; }
@media (hover: none) { .todo-del { opacity: .5; } }
.todo-more { display: block; margin: 8px 0; font-size: 13px; font-weight: 600; color: var(--d-text-2); }
.todo-empty { color: var(--d-text-2); font-size: 14px; padding: 12px 0; }
/* who's home */
.people { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 10px; align-content: start; height: 100%; overflow-y: auto; }
.pp { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; min-width: 0; padding: 4px 0; }
.pp-av { position: relative; width: clamp(48px, 30cqh, 72px); aspect-ratio: 1; border-radius: 50%; background: var(--d-fill); display: grid; place-items: center; box-shadow: 0 0 0 3px var(--d-fill); }
.pp.home .pp-av { box-shadow: 0 0 0 3px var(--ok); }
.pp-av img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.pp:not(.home) .pp-av img { filter: grayscale(.7); opacity: .8; }
.pp-init { font-size: clamp(16px, 9cqh, 24px); font-weight: 700; }
.pp-dot { position: absolute; right: 2%; bottom: 2%; width: 26%; aspect-ratio: 1; border-radius: 50%; background: var(--d-text-2); box-shadow: 0 0 0 2px var(--w-bg, var(--d-tile)); }
.pp.home .pp-dot { background: var(--ok); }
.pp-text { display: flex; flex-direction: column; min-width: 0; max-width: 100%; }
.pp-name { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-where { font-size: 12px; color: var(--d-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.people.as-list { grid-template-columns: 1fr; gap: 4px; }
.people.as-list .pp { flex-direction: row; text-align: left; gap: 12px; }
.people.as-list .pp-av { width: 40px; }
/* forecast */
.fc { display: flex; flex-direction: column; height: 100%; gap: 12px; min-height: 0; }
.fc-now { display: flex; align-items: center; gap: 12px; flex: none; }
.fc-ico { font-size: 40px; display: flex; color: var(--d-text-2); }
.fc-temp { font-size: 40px; font-weight: 700; line-height: 1; letter-spacing: -0.02em; }
.fc-cond { display: flex; flex-direction: column; min-width: 0; font-size: 13px; color: var(--d-text-2); }
.fc-cond b { font-size: 15px; color: var(--d-text); }
.fc-sun { margin-left: auto; display: flex; flex-direction: column; gap: 2px; font-size: 13px; color: var(--d-text-2); }
.fc-sun span { display: inline-flex; align-items: center; gap: 6px; }
.fc-hours { display: flex; gap: 6px; overflow-x: auto; flex: none; scrollbar-width: none; padding-bottom: 2px; }
.fc-hours::-webkit-scrollbar { display: none; }
.fc-hour { flex: 0 0 52px; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 0; border-radius: 12px; background: var(--d-fill); font-size: 18px; }
.fc-hour b { font-size: 14px; }
.fc-t { font-size: 12px; color: var(--d-text-2); }
.fc-rain { font-size: 11px; color: var(--d-accent); font-weight: 600; min-height: 13px; }
.fc-days { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto; }
.fc-day { display: grid; grid-template-columns: 52px 22px 36px 34px 1fr 34px; align-items: center; gap: 8px; min-height: 36px; font-size: 14px; border-top: 1px solid var(--d-separator); }
.fc-day .i { font-size: 18px; }
.fc-dn { font-weight: 600; text-transform: capitalize; }
.fc-lo { color: var(--d-text-2); text-align: right; }
.fc-bar { position: relative; height: 6px; border-radius: 3px; background: var(--d-fill); }
.fc-bar i { position: absolute; top: 0; bottom: 0; border-radius: 3px; background: linear-gradient(to right, #5ac8fa, #ffcc00, #ff9500); }
@container (max-height: 300px) { .fc-days { display: none; } }
@container (max-width: 360px) { .fc-sun { display: none; } }
/* timer */
.tm { display: flex; flex-direction: column; align-items: center; height: 100%; gap: 8px; }
.tm-name { align-self: flex-start; font-size: 15px; font-weight: 600; }
.tm-ring { position: relative; flex: 1; min-height: 0; aspect-ratio: 1; display: grid; place-items: center; }
.tm-ring > svg { position: absolute; inset: 0; transform: rotate(-90deg); }
.tm-ring circle { fill: none; stroke-width: 7; }
.tm-ring .track { stroke: var(--d-fill); }
.tm-ring .fill { stroke: var(--d-accent); stroke-linecap: round; transition: stroke-dasharray 1s linear; }
.tm.idle .tm-ring .fill { stroke: var(--d-text-2); opacity: .4; }
.tm-time { font-size: clamp(22px, 14cqh, 40px); font-weight: 700; }
.tm.done .tm-time { color: var(--ok); animation: tm-blink 1s ease-in-out infinite; }
.tm.done .tm-ring .fill { stroke: var(--ok); }
@keyframes tm-blink { 50% { opacity: .35; } }
.tm-ctrls { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; flex: none; }
/* blinds */
.cv { display: flex; flex-direction: column; height: 100%; gap: 10px; min-height: 0; }
.cv-head { display: flex; align-items: center; gap: 6px; }
.cv-head .name { flex: 1; min-width: 0; }
.cv-win { position: relative; width: 44px; aspect-ratio: 3 / 4; border-radius: 6px; border: 2px solid var(--d-text-2); background: linear-gradient(#bfe0ff, #e8f4ff); overflow: hidden; flex: none; }
.cv-blind { position: absolute; left: 0; right: 0; top: 0; background: repeating-linear-gradient(to bottom, var(--d-text-2) 0 3px, color-mix(in srgb, var(--d-text-2) 60%, transparent) 3px 5px); transition: height .4s; }
.cv.one .cv-main { flex: 1; min-height: 0; display: flex; align-items: center; gap: 16px; }
.cv.one .cv-win { width: auto; height: min(100%, 150px); }
.cv-side { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cv-pct { font-size: 28px; font-weight: 700; }
.cv-btns { display: flex; gap: 6px; flex: none; }
.cv-btns .round { width: 40px; height: 40px; }
.cv-btns .round:disabled { opacity: .35; cursor: default; }
.cv-tilt { display: flex; align-items: center; gap: 10px; }
.cv-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
.cv-row { display: flex; align-items: center; gap: 12px; }
.cv-row .cv-win { width: 30px; }
.cv-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cv-name { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-name b { color: var(--d-text-2); font-weight: 600; }
.cv-slider { height: 10px; }
/* alarm */
.al { display: flex; flex-direction: column; height: 100%; gap: 10px; }
.al-head { display: flex; align-items: center; gap: 12px; }
.al-shield { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; font-size: 22px; background: var(--d-fill); flex: none; }
.al[class*="state-armed"] .al-shield { background: var(--d-accent); color: var(--d-on-accent, #fff); }
.al.state-triggered .al-shield { background: var(--crit); color: #fff; animation: tm-blink 1s infinite; }
.al.state-disarmed .al-shield { color: var(--d-text-2); }
.al-state { font-size: 14px; color: var(--d-text-2); font-weight: 600; }
.al.state-triggered .al-state { color: var(--crit); }
.al-open { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 12px; color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent); font-size: 13px; font-weight: 600; }
.al-open span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-code { text-align: center; font-size: 26px; letter-spacing: .3em; min-height: 32px; }
.al-code .wt { letter-spacing: 0; font-size: 14px; }
.al-keys { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; flex: 1; min-height: 0; }
.al-key { display: grid; place-items: center; min-height: 40px; border-radius: 12px; background: var(--d-fill); font-size: 20px; font-weight: 600; }
.al-key:active { background: color-mix(in srgb, var(--d-text) 20%, transparent); }
.al-modes { display: flex; gap: 6px; flex: none; }
.al-mode { flex: 1; min-height: 44px; border-radius: 12px; display: grid; place-items: center; background: var(--d-fill); font-weight: 600; font-size: 14px; }
.al-mode.on { background: var(--d-accent); color: var(--d-on-accent, #fff); }
.al-mode.off { background: var(--d-text); color: var(--d-tile); }
/* notifications */
.nt { display: flex; flex-direction: column; height: 100%; gap: 10px; }
.nt-head { display: flex; align-items: center; gap: 8px; }
.nt-head .name { flex: 1; font-size: 17px; }
.nt-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.nt-item { display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 12px; background: var(--d-fill); }
.nt-item.update .nt-ico { color: var(--d-accent); }
.nt-ico { font-size: 18px; display: flex; padding-top: 1px; color: var(--d-text-2); }
.nt-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: 13px; }
.nt-body b { font-size: 14px; }
.nt-msg { color: var(--d-text-2); overflow-wrap: anywhere; }
.nt-msg a, .nt-link { color: var(--d-accent); }
.nt-time { font-size: 11px; color: var(--d-text-2); }
.nt-x { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; color: var(--d-text-2); }
.nt-empty { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 6px; color: var(--d-text-2); font-size: 14px; }
.nt-empty .i { font-size: 28px; color: var(--ok); }
/* room */
.room { position: relative; display: flex; flex-direction: column; gap: 12px; height: 100%; min-height: 0; }
.room.on-image { margin: -16px; padding: 16px; border-radius: inherit; color: #fff; --d-text-2: rgba(255, 255, 255, 0.86); --d-fill: rgba(255, 255, 255, 0.18);
@@ -772,51 +974,63 @@ body.kiosk #tabnav { top: 6px; max-height: calc(100vh - 20px); }
/* pill button with icon and label (media "Turn on") */
.car-act { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border-radius: 12px; background: var(--d-fill); font-size: 14px; font-weight: 500; }
.car-act .i { font-size: 18px; }
/* car: built around 3×4; compact when short, side by side when wide */
.car2 { display: grid; grid-template-areas: "top" "visual" "info"; grid-template-rows: auto 1fr auto; gap: 10px; height: 100%; min-height: 0; }
.car-top { grid-area: top; display: flex; align-items: center; gap: 8px; min-width: 0; }
.car-top .name { font-size: 17px; flex: 1; min-width: 0; }
.car-status { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 99px; background: var(--d-fill); color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60%; }
.car-status.charging { color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent); }
.car-visual { grid-area: visual; position: relative; min-height: 0; display: grid; place-items: center; }
.car-photo { width: 100%; height: 100%; object-fit: contain; }
.car-art { width: 100%; max-height: 100%; }
.car-body { fill: var(--d-fill); stroke: var(--d-text-2); stroke-width: 1.5; }
.car-glass { fill: color-mix(in srgb, var(--d-text) 12%, transparent); }
.car-wheel { fill: var(--d-text); opacity: .85; }
.car-hub { fill: var(--d-tile); }
.car-visual.is-charging::before { content: ""; position: absolute; inset: 15% 10%; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in srgb, var(--ok) 35%, transparent), transparent); animation: glow 2.4s ease-in-out infinite; }
@keyframes glow { 50% { opacity: .45; } }
.car-bolt { position: absolute; right: 8%; top: 4%; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--ok); color: #04210f; font-size: 17px; }
.car-info { grid-area: info; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.car-nums { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.car-pct { font-size: clamp(32px, 14cqh, 52px); font-weight: 700; line-height: 1; letter-spacing: -0.02em; }
.car-range { font-size: 17px; font-weight: 600; color: var(--d-text-2); }
.car2 .level { color: var(--crit); align-self: center; }
.car-bar { position: relative; height: 12px; border-radius: 6px; background: var(--d-fill); overflow: visible; flex: none; }
.car-fill { position: absolute; inset: 0 auto 0 0; border-radius: 6px; background: var(--d-text); transition: width .4s; }
.car-bar.charging .car-fill { background: var(--ok); background-image: linear-gradient(110deg, transparent 40%, rgba(255, 255, 255, 0.45) 50%, transparent 60%); background-size: 200% 100%; animation: charge 2.4s linear infinite; }
.car-bar.low .car-fill { background: var(--crit); }
.car-limit { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; border-radius: 1px; background: var(--d-text-2); }
@keyframes charge { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.car-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 8px; }
.car-stat { display: flex; flex-direction: column; padding: 8px 10px; border-radius: 12px; background: var(--d-fill); min-width: 0; }
.car-stat-v { font-size: 15px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.car-stat-l { font-size: 12px; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.car-actions2 { display: flex; gap: 6px; }
.car-btn { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 4px; border-radius: 14px; background: var(--d-fill); }
.car-btn-ico { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; font-size: 18px; }
.car-btn[aria-pressed="true"] .car-btn-ico { background: var(--d-text); color: var(--d-tile); }
.car-btn-label { font-size: 12px; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* sizes */
@container (max-height: 380px) { .car-stats { display: none; } }
@container (max-height: 250px) and (max-width: 479px) { .car2 { grid-template-areas: "top" "info"; grid-template-rows: auto 1fr; } .car-visual { display: none; } .car-info { justify-content: flex-end; } }
@container (max-height: 160px) { .car-actions2 { display: none; } }
@container (min-width: 480px) and (min-height: 150px) {
.car2 { grid-template-areas: "top top" "visual info"; grid-template-columns: 1.1fr 1fr; grid-template-rows: auto 1fr; column-gap: 18px; }
.car-info { justify-content: center; }
/* car: a round gauge with the range in the middle and the car in front */
.car3 { --car-top: color-mix(in srgb, var(--d-accent) 55%, var(--d-text)); --car-bottom: color-mix(in srgb, var(--d-accent) 70%, #000);
display: grid; grid-template-areas: "top" "gauge" "info"; grid-template-rows: auto minmax(0, 1fr) auto; gap: 8px; height: 100%; min-height: 0; }
.car3-top { grid-area: top; display: flex; align-items: center; gap: 8px; min-width: 0; }
.car3-top .name { font-size: 17px; flex: 1; min-width: 0; }
.car3-status { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 99px; background: var(--d-fill); color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 62%; }
.car3-status.charging { color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent); }
.cg { grid-area: gauge; position: relative; min-height: 0; height: 100%; max-height: 260px; max-width: 100%; justify-self: center; align-self: center; aspect-ratio: 1 / .92; }
.cg > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.cg > svg path { fill: none; stroke-width: 12; stroke-linecap: round; }
.cg-track { stroke: var(--d-fill); }
.cg-fill { stroke: var(--d-text); }
.cg.charging .cg-fill { stroke: var(--ok); animation: cg-pulse 2.4s ease-in-out infinite; }
.cg.low .cg-fill { stroke: var(--crit); }
@keyframes cg-pulse { 50% { opacity: .65; } }
.cg-limit { stroke: var(--d-text-2); stroke-width: 3; stroke-linecap: round; }
.cg-center { position: absolute; left: 0; right: 0; top: 22%; display: flex; flex-direction: column; align-items: center; }
.cg-range { font-size: clamp(26px, 11cqh, 44px); font-weight: 700; letter-spacing: -0.02em; line-height: 1; }
.cg-range small { font-size: .42em; font-weight: 600; color: var(--d-text-2); margin-left: 3px; letter-spacing: 0; }
.cg-pct { margin-top: 4px; font-size: 14px; font-weight: 600; color: var(--d-text-2); }
.cg.charging .cg-pct { color: var(--ok); }
.cg-car { position: absolute; left: 6%; right: 6%; bottom: 2%; display: flex; justify-content: center; }
.cg-car img { max-width: 100%; max-height: 72px; object-fit: contain; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .3)); }
.car-art2 { width: 100%; max-height: 74px; }
.car-shadow { fill: rgba(0, 0, 0, .28); }
.car-body2 { fill: url(#carBody); }
.car-glass2 { fill: url(#carGlass); }
.car-line { stroke: rgba(255, 255, 255, .25); stroke-width: 1; }
.car-light { fill: #fff6d6; }
.car-tire { fill: #17181b; } .car-rim { fill: #8e9097; }
.car3.is-charging .cg-car::after { content: ""; position: absolute; left: 20%; right: 20%; bottom: 0; height: 40%; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in srgb, var(--ok) 45%, transparent), transparent); z-index: -1; }
.car3-info { grid-area: info; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.cchips { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.cchip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 99px; background: var(--d-fill); white-space: nowrap; }
.cchip .i { font-size: 14px; color: var(--d-text-2); }
.cbs { display: flex; gap: 6px; }
.cb { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 4px; border-radius: 14px; background: var(--d-fill); }
.cb-ico { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; font-size: 18px; transition: background-color .2s; }
.cb[aria-pressed="true"] .cb-ico { background: var(--d-text); color: var(--d-tile); }
.cb-label { font-size: 12px; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-sub { font-size: 11px; color: var(--d-text-2); white-space: nowrap; }
/* short: gauge small on the left, numbers and buttons beside it */
@container (max-height: 250px) {
.car3 { grid-template-areas: "top top" "gauge info"; grid-template-columns: auto 1fr; grid-template-rows: auto 1fr; column-gap: 14px; }
.cg { width: auto; height: 100%; max-width: none; aspect-ratio: 1; }
.cg-car { display: none; }
.cg-center { top: 30%; }
.car3-info { justify-content: center; }
.cchips { justify-content: flex-start; }
}
@container (max-width: 230px) { .car-btn-label { display: none; } .car-range { font-size: 15px; } }
@container (max-height: 160px) { .cbs { display: none; } }
/* wide: gauge on the left, the rest on the right */
@container (min-width: 480px) and (min-height: 251px) {
.car3 { grid-template-areas: "top top" "gauge info"; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: auto 1fr; column-gap: 20px; }
.car3-info { justify-content: center; }
}
@container (max-width: 300px) { .cb-label, .cb-sub { display: none; } .cb { padding: 6px 2px; } }
/* ============================================================ phone dashboards */
body.phone-ui:not(.peek) #topbar { display: none; }
@@ -833,6 +1047,23 @@ body.kiosk.idle, body.kiosk.idle * { cursor: none !important; }
#saver[hidden] { display: none; }
#saver .time { font-size: min(22vw, 220px); font-weight: 600; letter-spacing: -0.03em; line-height: 1; }
#saver .date { font-size: 22px; margin-top: 12px; color: #636366; }
#saver .photos { display: none; }
#saver.photo .photos { display: block; }
#saver.photo .saver-clock { position: absolute; left: 32px; bottom: 28px; text-align: left; color: #fff; text-shadow: 0 1px 12px rgba(0,0,0,.55); }
#saver.photo .time { font-size: clamp(40px, 9vw, 96px); }
#saver.photo .date { color: rgba(255,255,255,.85); font-size: 18px; margin-top: 4px; }
#saver .ph-msg { position: absolute; inset: 0; display: grid; place-items: center; }
#doorbell { position: fixed; inset: 0; z-index: 90; background: #000; display: flex; flex-direction: column; }
#doorbell[hidden] { display: none; }
#doorbell .db-cam { flex: 1; min-height: 0; display: grid; place-items: center; }
#doorbell .db-cam > img { width: 100%; height: 100%; object-fit: contain; }
#doorbell .db-icon > svg { width: 120px; height: 120px; color: #fff; animation: db-ring 1s ease-in-out infinite; }
@keyframes db-ring { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(12deg); } 75% { transform: rotate(-12deg); } }
#doorbell .db-bar { display: flex; align-items: center; gap: 16px; padding: 18px 24px calc(18px + env(safe-area-inset-bottom)); background: rgba(28,28,30,.9); color: #fff; }
#doorbell .db-title { flex: 1; font-size: 22px; font-weight: 600; }
#doorbell .db-close { min-height: 52px; padding: 0 28px; font-size: 18px; }
#night-dim { position: fixed; inset: 0; z-index: 70; pointer-events: none; background: #000; opacity: var(--night-dim, 0); transition: opacity 2s ease; }
@media (prefers-reduced-motion: reduce) { #doorbell .db-icon > svg { animation: none; } #night-dim { transition: none; } }
/* ============================================================ motion + sizes */
@media (prefers-reduced-motion: reduce) {
@@ -874,3 +1105,8 @@ body.kiosk.idle, body.kiosk.idle * { cursor: none !important; }
body.editing #inspector { order: 3; width: auto; max-height: 40vh; padding: 14px; border: 0; border-top: 1px solid var(--separator); }
.btn.label-wide span { display: none; }
}
.hist-list { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow-y: auto; margin-top: 12px; }
.hist-row { display: flex; align-items: center; gap: 12px; padding: 8px 4px; border-bottom: 1px solid var(--separator); }
.hist-row:last-child { border-bottom: 0; }
.hist-when { font-weight: 500; }