/* theme.css — shared look: card panel, server panel, Flutter mirror.
   Light default, dark via prefers-color-scheme. System fonts only, no
   framework (ships on ESP8266 LittleFS). Token names are the contract. */
:root {
 --bg:#EEF2F8; --surface:#FFFFFF; --surface-2:#F4F6FA; --surface-3:#E6EBF3;
 --border:rgba(16,36,70,.09); --border-strong:rgba(16,36,70,.18);
 --text:#0F1A2E; --text-2:#5B6B85; --text-3:#5A6983;
 --accent:#1461C2; --accent-600:#0F4FA3; --accent-soft:rgba(20,97,194,.12); --on-accent:#FFFFFF;
 --success:#127044; --success-soft:rgba(34,166,106,.14);
 --danger:#BE2A30; --danger-soft:rgba(229,72,77,.12);
 --warn:#8F5D0F; --warn-soft:rgba(224,154,43,.14); --knob:#FFFFFF;
 /* light text/accent tokens are ≥4.5:1 (WCAG AA body text) on --surface, --bg,
    --surface-2, --surface-3 and on their own *-soft tints; keep that when editing */
 --sh-1:0 1px 2px rgba(16,36,70,.05),0 2px 8px rgba(16,36,70,.05); --sh-2:0 6px 24px rgba(16,36,70,.10);
 --focus:0 0 0 3px var(--accent-soft);
 --r-sm:10px; --r:14px; --r-lg:20px; --r-pill:999px;
 --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px;
 --fs-xs:12px; --fs-sm:14px; --fs:16px; --fs-md:18px; --fs-lg:22px; --fs-xl:28px; --fs-num:36px;
 --tap:44px; --ease:cubic-bezier(.4,0,.2,1); --dur:.18s;
 --safe-t:env(safe-area-inset-top,0px); --safe-b:env(safe-area-inset-bottom,0px);
 --safe-l:env(safe-area-inset-left,0px); --safe-r:env(safe-area-inset-right,0px);
 color-scheme:light dark;
}
@media (prefers-color-scheme:dark) { :root {
 --bg:#0E131C; --surface:#171E2A; --surface-2:#1E2632; --surface-3:#27313F;
 --border:rgba(255,255,255,.08); --border-strong:rgba(255,255,255,.16);
 --text:#EEF2F8; --text-2:#A6B1C4; --text-3:#8E99AD;
 --accent:#4D9BFF; --accent-600:#2F85F5; --accent-soft:rgba(77,155,255,.16); --on-accent:#07213F;
 --success:#34C27E; --danger:#FF6B70; --warn:#F0B04A; --knob:#F6F8FB;
 --sh-1:0 1px 2px rgba(0,0,0,.25); --sh-2:0 8px 28px rgba(0,0,0,.45);
} }

/* base */
*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
html { -webkit-text-size-adjust:100%; }
body {
 font-family:-apple-system,"SF Pro Text","Segoe UI",Roboto,"Helvetica Neue",sans-serif;
 font-size:var(--fs); line-height:1.4; color:var(--text); background:var(--bg);
 min-height:100vh; -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
button,input,select,textarea { font:inherit; color:inherit; touch-action:manipulation; }
button { cursor:pointer; border:none; background:none; }
svg { display:block; }
.hidden { display:none !important; }
.muted { color:var(--text-2); }
.tiny { font-size:var(--fs-xs); color:var(--text-3); }
.num { font-variant-numeric:tabular-nums; }
.stack > * + * { margin-top:var(--sp-3); }
.row { display:flex; align-items:center; gap:var(--sp-2); }
.row-end { margin-left:auto; }
.divider { height:1px; background:var(--border); margin:var(--sp-4) 0; }

/* layout */
.app { max-width:1100px; margin:0 auto; padding:var(--sp-4) calc(var(--sp-4) + var(--safe-r)) calc(var(--sp-6) + var(--safe-b)) calc(var(--sp-4) + var(--safe-l)); }
.topbar {
 position:sticky; top:0; z-index:50; display:flex; align-items:center; gap:var(--sp-3);
 margin:calc(-1 * var(--sp-4)) calc(-1 * var(--sp-4)) var(--sp-4);
 padding:calc(var(--sp-3) + var(--safe-t)) var(--sp-4) var(--sp-3); background:var(--bg);
}
.topbar-title { font-size:var(--fs-lg); font-weight:700; letter-spacing:-.2px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.topbar-sub { font-size:var(--fs-xs); color:var(--text-2); }
.topbar-actions { margin-left:auto; display:flex; gap:var(--sp-2); flex:0 0 auto; }
.section { margin-bottom:var(--sp-6); }
.section-head { display:flex; align-items:center; gap:var(--sp-2); margin:0 var(--sp-1) var(--sp-3); }
.section-title { font-size:var(--fs-md); font-weight:650; }
.grid { display:grid; grid-template-columns:1fr; gap:var(--sp-3); }
@media (min-width:560px) { .grid { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (min-width:900px) { .grid { grid-template-columns:repeat(3,minmax(0,1fr)); } }

/* cards */
.card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); padding:var(--sp-4) var(--sp-5); box-shadow:var(--sh-1); min-width:0; }
.card-head { display:flex; align-items:flex-start; gap:var(--sp-2); margin-bottom:var(--sp-3); }
.card-title { font-size:var(--fs-md); font-weight:650; }
.card-sub { font-size:var(--fs-sm); color:var(--text-2); margin-top:2px; }
.card-foot { margin-top:var(--sp-4); display:flex; gap:var(--sp-2); }

/* widget cards (ui-render.js); .w-off = value unreadable */
.w { display:flex; flex-direction:column; gap:var(--sp-2); min-height:96px; justify-content:space-between; }
.w-row { flex-direction:row; align-items:center; min-height:72px; }
.w-row .w-label { flex:1 1 auto; }
.w-label { font-size:var(--fs-sm); font-weight:600; color:var(--text-2); min-width:0; overflow:hidden; text-overflow:ellipsis; }
.w-value { display:flex; align-items:baseline; gap:var(--sp-1); min-width:0; }
.w-num { font-size:var(--fs-num); font-weight:700; line-height:1.05; letter-spacing:-.5px; font-variant-numeric:tabular-nums; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.w-unit { font-size:var(--fs-sm); font-weight:600; color:var(--text-2); }
.w-hint { font-size:var(--fs-xs); color:var(--text-3); min-height:1.2em; }
.w-err { font-size:var(--fs-xs); color:var(--danger); font-weight:600; }
.w-off .w-num { color:var(--text-3); }
.w-foot { display:flex; align-items:center; gap:var(--sp-2); }
.w-edit { display:flex; flex-direction:column; gap:var(--sp-2); }
.w-edit .row .btn { flex:1 1 0; }

/* chips / segmented */
.chips { display:flex; gap:var(--sp-2); overflow-x:auto; padding-bottom:2px; scrollbar-width:none; }
.chips::-webkit-scrollbar { display:none; }
.chip {
 flex:0 0 auto; display:inline-flex; align-items:center; gap:6px; min-height:36px; padding:0 var(--sp-4);
 border-radius:var(--r-pill); background:var(--surface); border:1px solid var(--border);
 color:var(--text-2); font-size:var(--fs-sm); font-weight:600; transition:background var(--dur) var(--ease),color var(--dur) var(--ease);
}
.chip.active { background:var(--accent); color:var(--on-accent); border-color:var(--accent); }
.chip.soft { background:var(--accent-soft); color:var(--accent); border-color:transparent; }
.seg { display:grid; grid-auto-flow:column; grid-auto-columns:1fr; gap:3px; padding:3px; background:var(--surface-3); border-radius:var(--r-sm); }
.seg-btn { min-height:38px; padding:0 var(--sp-2); border-radius:8px; font-size:var(--fs-sm); font-weight:600; color:var(--text-2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; transition:background var(--dur) var(--ease),color var(--dur) var(--ease); }
.seg-btn.active { background:var(--surface); color:var(--accent); box-shadow:var(--sh-1); }
.seg-btn:disabled { opacity:.5; cursor:default; }

/* buttons */
.btn {
 display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2); position:relative;
 min-height:var(--tap); padding:0 var(--sp-5); border-radius:var(--r); font-size:var(--fs); font-weight:600; white-space:nowrap;
 background:var(--surface-3); color:var(--text); transition:background var(--dur) var(--ease),opacity var(--dur) var(--ease),transform .1s;
}
.btn:active { transform:scale(.98); }
.btn-primary { background:var(--accent); color:var(--on-accent); }
.btn-primary:hover { background:var(--accent-600); }
.btn-ghost { background:transparent; border:1px solid var(--border-strong); color:var(--accent); }
.btn-ghost:hover { background:var(--accent-soft); }
.btn-danger { background:var(--danger-soft); color:var(--danger); }
.btn-sm { min-height:36px; padding:0 var(--sp-3); font-size:var(--fs-sm); border-radius:var(--r-sm); }
.btn-block { width:100%; }
.btn:disabled { opacity:.45; cursor:default; transform:none; }
.btn.is-loading { color:transparent !important; pointer-events:none; }
.btn.is-loading::after { content:""; position:absolute; width:18px; height:18px; border-radius:50%; border:2px solid var(--text-3); border-top-color:transparent; animation:spin .7s linear infinite; }
.btn-primary.is-loading::after { border-color:var(--on-accent); border-top-color:transparent; }
.icon-btn { width:var(--tap); height:var(--tap); border-radius:50%; display:inline-flex; align-items:center; justify-content:center; background:var(--surface); border:1px solid var(--border); color:var(--text-2); position:relative; transition:color var(--dur) var(--ease); }
.icon-btn:hover { color:var(--accent); }
.icon-btn svg { width:20px; height:20px; }
.spinner { width:18px; height:18px; border-radius:50%; border:2px solid var(--border-strong); border-top-color:var(--accent); animation:spin .7s linear infinite; flex:0 0 auto; }
@keyframes spin { to { transform:rotate(360deg); } }

/* iOS toggle */
.toggle { position:relative; flex:0 0 auto; width:51px; height:31px; border-radius:var(--r-pill); background:var(--surface-3); border:1px solid var(--border); padding:0; transition:background var(--dur) var(--ease); }
.toggle::before { content:""; position:absolute; top:2px; left:2px; width:25px; height:25px; border-radius:50%; background:var(--knob); box-shadow:0 2px 5px rgba(0,0,0,.2); transition:transform var(--dur) var(--ease); }
.toggle.on { background:var(--accent); border-color:var(--accent); }
.toggle.on::before { transform:translateX(20px); }
.toggle:disabled { opacity:.5; cursor:default; }
.toggle .spinner { display:none; }
.toggle.busy .spinner { display:block; position:absolute; top:6px; left:6px; width:17px; height:17px; border-color:rgba(120,130,150,.4); border-top-color:var(--accent); }
.toggle.busy.on .spinner { left:26px; border-color:rgba(255,255,255,.5); border-top-color:var(--on-accent); }
.toggle.busy::before { opacity:.35; }
.toggle-state { font-size:var(--fs-xs); font-weight:700; color:var(--text-3); min-width:44px; text-align:right; }
.toggle-state.on { color:var(--accent); }

/* lamp */
.lamp-dot { width:14px; height:14px; border-radius:50%; background:var(--text-3); flex:0 0 auto; opacity:.45; transition:background var(--dur) var(--ease),box-shadow var(--dur) var(--ease); }
.lamp.on .lamp-dot { opacity:1; background:var(--danger); box-shadow:0 0 0 4px var(--danger-soft); }
.lamp.on.tone-state .lamp-dot { background:var(--success); box-shadow:0 0 0 4px var(--success-soft); }
.lamp.w-off .lamp-dot { opacity:.25; box-shadow:none; }
.lamp-text { font-size:var(--fs-xs); font-weight:700; color:var(--text-3); }

/* forms (all controls ≥16px so iPhone does not zoom) */
.field { display:flex; flex-direction:column; gap:6px; }
.field > label,.field-label { font-size:var(--fs-xs); font-weight:600; color:var(--text-2); text-transform:uppercase; letter-spacing:.4px; }
.input,.field input,.field select {
 width:100%; min-height:var(--tap); padding:0 var(--sp-4); font-size:var(--fs); background:var(--surface-2);
 border:1px solid var(--border); border-radius:var(--r); outline:none; -webkit-appearance:none; appearance:none;
 transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
select.input,.field select { background-image:linear-gradient(45deg,transparent 50%,var(--text-2) 50%),linear-gradient(135deg,var(--text-2) 50%,transparent 50%); background-position:calc(100% - 20px) 50%,calc(100% - 15px) 50%; background-size:5px 5px; background-repeat:no-repeat; padding-right:40px; }
.input:focus,.field input:focus,.field select:focus { border-color:var(--accent); box-shadow:var(--focus); }
.input:disabled { opacity:.5; }
.field-hint { font-size:var(--fs-xs); color:var(--text-3); }
.field-err { font-size:var(--fs-xs); color:var(--danger); font-weight:600; }
.form-row { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-3); }
.check { display:flex; align-items:center; gap:var(--sp-2); min-height:var(--tap); font-size:var(--fs-sm); }
.check input { width:20px; height:20px; accent-color:var(--accent); }
.range { -webkit-appearance:none; appearance:none; width:100%; height:var(--tap); background:transparent; }
.range::-webkit-slider-runnable-track { height:6px; border-radius:3px; background:var(--surface-3); }
.range::-moz-range-track { height:6px; border-radius:3px; background:var(--surface-3); }
.range::-webkit-slider-thumb { -webkit-appearance:none; width:28px; height:28px; margin-top:-11px; border-radius:50%; background:var(--knob); border:1px solid var(--border-strong); box-shadow:0 2px 6px rgba(0,0,0,.2); }
.range::-moz-range-thumb { width:28px; height:28px; border-radius:50%; background:var(--knob); border:1px solid var(--border-strong); box-shadow:0 2px 6px rgba(0,0,0,.2); }
.range:disabled { opacity:.5; }
.range-scale { display:flex; justify-content:space-between; font-size:var(--fs-xs); color:var(--text-3); }

/* messages, status pill, empty, skeleton */
.msg { padding:var(--sp-3) var(--sp-4); border-radius:var(--r); font-size:var(--fs-sm); background:var(--accent-soft); color:var(--accent); }
.msg.err { background:var(--danger-soft); color:var(--danger); }
.msg.warn { background:var(--warn-soft); color:var(--warn); }
.msg.ok { background:var(--success-soft); color:var(--success); }
.status { display:inline-flex; align-items:center; gap:6px; min-height:28px; padding:0 var(--sp-3); border-radius:var(--r-pill); font-size:var(--fs-xs); font-weight:700; background:var(--surface-3); color:var(--text-2); }
.status::before { content:""; width:8px; height:8px; border-radius:50%; background:var(--text-3); }
.status.online { background:var(--success-soft); color:var(--success); }
.status.online::before { background:var(--success); }
.status.offline { background:var(--danger-soft); color:var(--danger); }
.status.offline::before { background:var(--danger); }
.status.booting { background:var(--warn-soft); color:var(--warn); }
.status.booting::before { background:var(--warn); animation:pulse 1.2s ease-in-out infinite; }
.boot-banner { display:flex; align-items:center; gap:var(--sp-3); margin-bottom:var(--sp-4); }
.empty { text-align:center; padding:var(--sp-6) var(--sp-4); color:var(--text-2); }
.empty-title { font-size:var(--fs-md); font-weight:650; color:var(--text); margin-bottom:var(--sp-1); }
.skel { background:var(--surface-3); border-radius:8px; animation:pulse 1.4s ease-in-out infinite; }
.skel-text { height:14px; width:55%; }
.skel-num { height:36px; width:40%; }
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:.45; } }

/* overlay (login), modal (bottom sheet on phones), toast */
.overlay,.modal-backdrop { position:fixed; inset:0; z-index:200; display:none; align-items:center; justify-content:center; padding:var(--sp-4); background:rgba(10,20,40,.45); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); }
.overlay { background:var(--bg); -webkit-backdrop-filter:none; backdrop-filter:none; }
.overlay.active,.modal-backdrop.active { display:flex; }
.modal { width:100%; max-width:440px; max-height:calc(100vh - 32px); overflow-y:auto; background:var(--surface); border-radius:var(--r-lg); padding:var(--sp-5); box-shadow:var(--sh-2); animation:rise .2s var(--ease); }
.modal-head { display:flex; align-items:center; gap:var(--sp-2); margin-bottom:var(--sp-3); }
.modal-title { font-size:var(--fs-lg); font-weight:700; flex:1 1 auto; }
.modal-close { width:36px; height:36px; border-radius:50%; background:var(--surface-3); color:var(--text-2); font-size:22px; line-height:1; flex:0 0 auto; }
.modal-actions { display:flex; gap:var(--sp-2); margin-top:var(--sp-5); }
.modal-actions .btn { flex:1 1 0; }
@media (max-width:559px) {
 .modal-backdrop { align-items:flex-end; padding:0; }
 .modal { max-width:none; border-radius:var(--r-lg) var(--r-lg) 0 0; padding-bottom:calc(var(--sp-5) + var(--safe-b)); animation:slideUp .22s var(--ease); }
}
@keyframes rise { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
@keyframes slideUp { from { transform:translateY(100%); } to { transform:none; } }
.toast-zone { position:fixed; left:var(--sp-4); right:var(--sp-4); bottom:calc(var(--sp-4) + var(--safe-b)); z-index:300; display:flex; flex-direction:column; align-items:center; gap:var(--sp-2); pointer-events:none; }
.toast { max-width:440px; width:100%; padding:var(--sp-3) var(--sp-4); border-radius:var(--r); background:var(--text); color:var(--bg); font-size:var(--fs-sm); font-weight:600; box-shadow:var(--sh-2); border-left:4px solid var(--accent); animation:rise .2s var(--ease); }
.toast.ok { border-left-color:var(--success); }
.toast.err { border-left-color:var(--danger); }
@media (prefers-reduced-motion:reduce) { *,*::before,*::after { animation-duration:.01ms !important; transition-duration:.01ms !important; } }
