/* Makrona design tokens adapted from the read-only firmware dashboard. */

  :root{
    /* Makrona — light (softened). Dark overrides below. Legacy token names kept. */
    --bg:#eef2f1; --surface:#ffffff; --surface-2:#e9efed; --chip:#e9efed; --line:#dce4e2;
    --text:#06292a; --muted:#5b6b6a;
    --brand:#04292a; --brand-ink:#ffffff;
    --accent:#e39a2e; --accent-soft:#f2af4c; --accent-2:#f07b49;
    --on:#2f9e6b; --off:#94a4a2; --warn:#f07b49; --danger:#d64f45;
    --good:var(--on);   /* status green — several call sites use --good; alias
                           re-resolves per theme, so defining it once here is
                           the fix for all of them */
    --logo-ink:#04292a;
    --r-sm:10px; --r:14px; --r-lg:20px; --r-pill:999px;
    --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem; --s6:2rem;
    --shadow:0 1px 2px rgba(4,41,42,.05), 0 6px 20px rgba(4,41,42,.07);
    --font:-apple-system,BlinkMacSystemFont,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    --font-display:ui-rounded,"SF Pro Rounded",-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
    color-scheme:light;
  }
  @media (prefers-color-scheme:dark){
    :root:not([data-theme="light"]){
      --bg:#052a2b; --surface:#0a3739; --surface-2:#0e4143; --chip:#0e4143; --line:#17494b;
      --text:#e9efee; --muted:#9fb3b1;
      --brand:#0a3739; --brand-ink:#e9efee;
      --accent:#f2af4c; --accent-soft:#f2af4c; --accent-2:#f7936a;
      --on:#46c088; --off:#5f7573; --warn:#f7936a; --danger:#e56a60;
      --logo-ink:#e9efee;
      --shadow:0 1px 2px rgba(0,0,0,.25), 0 8px 24px rgba(0,0,0,.28);
      color-scheme:dark;
    }
  }
  :root[data-theme="dark"]{
    --bg:#052a2b; --surface:#0a3739; --surface-2:#0e4143; --chip:#0e4143; --line:#17494b;
    --text:#e9efee; --muted:#9fb3b1;
    --brand:#0a3739; --brand-ink:#e9efee;
    --accent:#f2af4c; --accent-soft:#f2af4c; --accent-2:#f7936a;
    --on:#46c088; --off:#5f7573; --warn:#f7936a; --danger:#e56a60;
    --logo-ink:#e9efee;
    --shadow:0 1px 2px rgba(0,0,0,.25), 0 8px 24px rgba(0,0,0,.28);
    color-scheme:dark;
  }
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:400 15px/1.45 var(--font);min-height:100vh;display:flex;flex-direction:column}header{display:flex;align-items:center;gap:24px;padding:16px 24px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}.brand{display:flex;gap:12px;align-items:center;margin-right:auto;font-family:var(--font-display);font-size:23px;font-weight:700}.brand small{font:12px var(--font);color:var(--muted);display:block;letter-spacing:.15em}.logo{display:flex;align-items:center;gap:3px;height:34px}.logo i{width:5px;border-radius:4px;background:var(--accent-soft);height:17px}.logo i:nth-child(2){height:32px;background:var(--logo-ink)}.logo i:nth-child(3){height:23px}.logo i:nth-child(4){height:29px;background:var(--accent-2)}.logo i:nth-child(5){height:17px;background:var(--logo-ink)}main{max-width:1120px;width:100%;margin:auto;padding:28px 20px 48px;flex:1}h1{font-family:var(--font-display);font-size:30px;margin:0 0 8px}h2{font-size:18px;margin:0 0 16px}h3{font-size:15px;margin:0}p{color:var(--muted)}.tabs{display:flex;padding:3px;background:var(--chip);border:1px solid var(--line);border-radius:10px}.tabs button{border:0;background:transparent;padding:7px 13px;color:var(--muted)}.tabs button.act{background:var(--surface);color:var(--text)}button{font:600 13px var(--font);border:1px solid var(--line);border-radius:9px;background:var(--surface-2);color:var(--text);padding:10px 14px;cursor:pointer}button.primary{background:var(--text);color:var(--bg);border-color:var(--text)}button:disabled{opacity:.5;cursor:not-allowed}button:hover:not(:disabled){border-color:var(--accent)}input,select{width:100%;font:inherit;background:var(--bg);color:var(--text);border:1px solid var(--line);border-radius:var(--r-sm);padding:11px;min-width:0}label{display:grid;gap:5px;font-size:13px;font-weight:600}form{display:grid;gap:14px}.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:22px;margin-bottom:20px}.auth{max-width:440px;margin:35px auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:16px}.card{border:1px solid var(--line);border-radius:12px;padding:16px;background:var(--bg);overflow-wrap:anywhere}.card p{font-size:13px}.pill,.chip{display:inline-flex;align-items:center;gap:7px;background:var(--chip);border:1px solid var(--line);border-radius:999px;padding:4px 10px;font-size:12px;color:var(--muted)}.dot{width:8px;height:8px;border-radius:50%;background:var(--off)}.dot.live{background:var(--on)}.row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.stack{display:grid;gap:12px}.muted,small{color:var(--muted)}.summary{display:flex;gap:12px;flex-wrap:wrap;margin:22px 0}.summary .panel{flex:1;min-width:140px;margin:0}.summary strong{font-size:28px;display:block;font-family:var(--font-display)}.summary span{color:var(--muted);font-size:12px}#message{white-space:pre-wrap;overflow-wrap:anywhere;border-left:3px solid var(--accent);padding:12px 16px;background:var(--surface-2);margin-bottom:20px}#message.error{border-color:var(--danger)}.empty{padding:32px 12px;text-align:center;color:var(--muted)}footer{padding:16px;text-align:center;color:var(--muted);font-size:12px;border-top:1px solid var(--line)}[hidden]{display:none!important}details{margin-top:16px}summary{cursor:pointer;font-weight:600;font-size:13px}details form{margin-top:14px}
/* Zigbee devices behind a gateway. Tile treatment, bubble readings, the self-lit
   power pill and the offline/asleep rules follow the firmware dashboard so a
   reading looks the same on the LAN and here. The cloud now relays commands
   (see the device-control block below): the power pill is a pressable toggle
   that keeps showing the DEVICE-REPORTED state until the gateway confirms. */
.devgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;margin:16px 0}
.dev{background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:12px;display:flex;flex-direction:column;gap:8px;overflow:hidden;overflow-wrap:anywhere}
.dev.off{opacity:.40}
.dev.sleep{opacity:1}
.dev-top{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.dev-top .tname{font-size:13px;font-weight:600;line-height:1.25}
.dev-badges{display:flex;gap:4px;flex-wrap:wrap;flex:none}
.hbadge{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;font-weight:700;letter-spacing:.03em;color:var(--muted);background:var(--chip);border-radius:999px;padding:2px 7px}
.hbadge.warn{color:var(--warn)}
.hbadge.sleep{color:var(--muted);letter-spacing:.12em}
.bubs{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr));gap:7px}
.bub{display:flex;align-items:center;justify-content:space-between;gap:6px;min-width:0;background:var(--chip);border-radius:9px;padding:7px;overflow:hidden}
.bub .bk{font-size:10.5px;color:var(--muted);flex:none}
.bub .bv{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;min-width:0;overflow:hidden;text-overflow:ellipsis}
.bub.warn .bv{color:var(--warn)}
.bub.on .bv{color:var(--on)}
.bub.skel .bv{color:var(--muted);opacity:.55}
.dev-foot{margin-top:auto;display:flex;align-items:center;justify-content:space-between;gap:8px;padding-top:2px}
.pwr{display:inline-flex;align-items:center;justify-content:center;gap:.5em;padding:.5em .9em;border-radius:999px;border:1.5px solid var(--line);background:var(--chip);color:var(--muted);font:700 12px/1 var(--font);letter-spacing:.03em}
.pwr.on{background:var(--on);border-color:var(--on);color:#04241a;box-shadow:0 0 16px -2px color-mix(in srgb,var(--on) 75%,transparent)}
.pwr.unk{border-style:dashed;color:var(--muted);background:transparent}
.pwr.unk .pwr-t{letter-spacing:0;font-size:1.15em;line-height:1}
.seen{flex:none;font-variant-numeric:tabular-nums}
.pill.warn{color:var(--warn);border-color:var(--warn)}
/* Unread notifications carry an accent edge and the tab carries their count. */
.note{display:grid;gap:10px;justify-items:start}
.note>.row{width:100%}
.note p{margin:0}
.note.unread{border-left:3px solid var(--accent)}
.note h2{margin:0}
.badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;margin-left:7px;padding:0 5px;border-radius:999px;background:var(--accent);color:#04241a;font-size:11px;font-weight:700;font-variant-numeric:tabular-nums}a{color:var(--text)}:focus-visible{outline:3px solid var(--accent);outline-offset:3px}code{overflow-wrap:anywhere}#transfer-result{user-select:all;white-space:pre-wrap;overflow-wrap:anywhere}@media(max-width:640px){header{gap:12px;padding:12px}.brand{width:100%;justify-content:center}main{padding:20px 12px}h1{font-size:25px}.panel{padding:16px}.tabs{flex:1}.tabs button{flex:1;padding:7px 8px}.grid{grid-template-columns:1fr}}

/* Device control: commands, drawer, permit-join */
/* Ported from the hub's own page (zigbee_router hs_web): the self-lit .pwr
   toggle with its .sending pulse, the tile slider, the fat range track, the
   warm↔cool colour-temperature track, the slide-over drawer + scrim and the
   permit-join ribbon. New sizes sit on the Fibonacci ladder (5/8/13/21/34). */
:root{--r-drawer:34px;--r-tile:21px;--r-ctl:13px;--g-ctl:8px;--g-ctl-l:13px;--kebab:34px;--line-w:1.5px;--line-w-act:2.5px}
.lic{width:21px;height:21px;flex:none}
button.iconlbl{display:inline-flex;align-items:center;justify-content:center;gap:var(--g-ctl);border-width:var(--line-w);border-radius:var(--r-ctl);padding:8px 13px}
button.iconlbl .lic{width:18px;height:18px}
/* gateway card: status pill + Allow join side by side */
.gw-act{display:flex;align-items:center;gap:var(--g-ctl);flex-wrap:wrap}
.gstat{display:flex;margin-top:var(--g-ctl)}
.gstat:not(:has(.cstat:not([hidden]))){display:none}
/* permit-join countdown ribbon (amber, pulse icon) */
.joinbar{display:flex;align-items:center;justify-content:center;gap:var(--g-ctl);margin:var(--g-ctl-l) 0 0;padding:5px 13px;border-radius:var(--r-pill);background:var(--accent);color:#04241a;font-size:15px;font-weight:600;line-height:1.4}
.joinbar .lic{width:18px;height:18px}
.joinbar-ic{animation:joinpulse 1.6s ease-in-out infinite}
.joinbar-s{font-variant-numeric:tabular-nums;min-width:4ch;text-align:left}
@keyframes joinpulse{50%{opacity:.35}}
/* the "new device joined" moment: accent border that fades over ~2 s */
.dev.new{border-color:var(--accent);border-width:var(--line-w-act);animation:devnew 2s ease-out forwards}
@keyframes devnew{0%{box-shadow:0 0 0 5px color-mix(in srgb,var(--accent) 45%,transparent)}70%{box-shadow:0 0 0 0 transparent}100%{border-color:var(--line);border-width:1px;box-shadow:none}}
/* kebab: 34×34 hit area, outline glyph */
.kebab{width:var(--kebab);height:var(--kebab);padding:0;display:inline-flex;align-items:center;justify-content:center;flex:none;border-width:var(--line-w);border-radius:var(--r-ctl);background:transparent;color:var(--muted);margin:-8px -5px -8px 0}
.kebab:hover:not(:disabled){color:var(--text)}
.kebab .lic{width:21px;height:21px}
/* tile controls by capability */
.ctls{display:flex;flex-direction:column;gap:var(--g-ctl)}
.ctls.disabled{opacity:.45}
.tctl{display:flex;align-items:center;gap:var(--g-ctl)}
.tctl .lic{width:18px;height:18px;color:var(--muted)}
.tctl .bk{font-size:10.5px;color:var(--muted);flex:none}
.tctl input[type=range]{flex:1;min-width:0}
.tlevel-v{font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums;min-width:4.5ch;text-align:right;flex:none}
.cbtns{display:grid;grid-template-columns:1fr 1fr;gap:var(--g-ctl)}
.cbtns .iconlbl{padding:8px 5px;gap:5px;white-space:nowrap;min-width:0}
.stepper{display:flex;align-items:center;gap:var(--g-ctl)}
.stepper .step{width:var(--kebab);height:var(--kebab);padding:0;display:inline-flex;align-items:center;justify-content:center;border-width:var(--line-w);border-radius:var(--r-ctl);flex:none}
.stepper .step .lic{width:18px;height:18px}
.stepper .sv{flex:1;text-align:center;font-size:15px;font-weight:600;font-variant-numeric:tabular-nums}
.ctl-note{margin:0;font-size:13px;color:var(--muted)}
/* standard fat range slider (the global input rule strips native chrome) */
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:34px;padding:0;margin:0;background:transparent;border:0;border-radius:0;cursor:pointer;touch-action:pan-y}
input[type=range]:disabled{cursor:not-allowed}
input[type=range]::-webkit-slider-runnable-track{height:13px;border-radius:var(--r-pill);background:var(--chip);border:var(--line-w) solid var(--line)}
input[type=range]::-moz-range-track{height:13px;border-radius:var(--r-pill);background:var(--chip);border:var(--line-w) solid var(--line)}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:26px;height:26px;margin-top:-8px;border-radius:50%;background:var(--text);border:2px solid var(--surface);box-shadow:0 1px 3px rgba(0,0,0,.45)}
input[type=range]::-moz-range-thumb{width:26px;height:26px;border-radius:50%;background:var(--text);border:2px solid var(--surface);box-shadow:0 1px 3px rgba(0,0,0,.45)}
/* warm↔cool track: cool/blue at 153 mireds (min), warm/orange at 500 (max) */
input[type=range].ct-track::-webkit-slider-runnable-track{background:linear-gradient(90deg,#8fc0ff 0%,#eaf1ff 42%,#ffe7c2 68%,#ff9d43 100%);border-color:var(--line)}
input[type=range].ct-track::-moz-range-track{background:linear-gradient(90deg,#8fc0ff 0%,#eaf1ff 42%,#ffe7c2 68%,#ff9d43 100%);border-color:var(--line)}
/* power toggle is now a button: keep the pill look, add press affordance */
button.pwr{cursor:pointer;padding:.5em .9em;border-radius:var(--r-pill);border-width:var(--line-w);-webkit-tap-highlight-color:transparent;transition:background .16s,color .16s,border-color .16s,box-shadow .16s}
.pwr .lic{width:18px;height:18px}
button.pwr:hover:not(:disabled){border-color:var(--text)}
button.pwr.on:hover:not(:disabled){border-color:var(--on)}
button.pwr:disabled{opacity:.45;cursor:not-allowed}
/* sent, not yet confirmed: the pill keeps the REAL state; only the pulse says "heard" */
.pwr.sending,.sending .pwr{animation:pwrsend 1.1s ease-in-out infinite}
@keyframes pwrsend{0%,100%{opacity:1}50%{opacity:.45}}
/* command outcome pill on a tile, in the drawer and on the gateway card */
.cstat{display:inline-flex;align-items:center;align-self:flex-start;gap:5px;border-radius:var(--r-pill);border:var(--line-w) solid var(--line);background:var(--chip);color:var(--muted);padding:3px 8px;font-size:12px;font-weight:600;overflow-wrap:anywhere}
.cstat.pending{border-style:dashed;animation:pwrsend 1.1s ease-in-out infinite}
.cstat.error{color:var(--danger);border-color:var(--danger);background:color-mix(in srgb,var(--danger) 12%,transparent)}
/* drawer: fixed right slide-over, bottom sheet on phones (φ split) */
.scrim{position:fixed;inset:0;background:rgba(4,41,42,.5);opacity:0;pointer-events:none;transition:opacity .2s;z-index:20}
.scrim.open{opacity:1;pointer-events:auto}
.drawer{position:fixed;top:0;right:0;height:100%;width:400px;max-width:92vw;background:var(--surface);border-left:var(--line-w) solid var(--line);border-radius:var(--r-drawer) 0 0 var(--r-drawer);box-shadow:var(--shadow);transform:translateX(100%);visibility:hidden;transition:transform .22s ease,visibility 0s linear .22s;z-index:21;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:21px}
.drawer.open{transform:none;visibility:visible;transition:transform .22s ease}
.dbody{display:flex;flex-direction:column;gap:21px}
.dhead{display:flex;align-items:center;justify-content:space-between;gap:var(--g-ctl-l)}
.dhead h2{margin:0;font-family:var(--font-display);font-size:21px;overflow-wrap:anywhere;min-width:0}
.dhead .kebab{margin:0}
.dmeta{margin:0;font-size:13px;color:var(--muted);overflow-wrap:anywhere}
.dstat{display:flex}
.dstat .cstat[hidden]{display:none}
.dsec{display:grid;gap:var(--g-ctl-l);padding:13px;border:var(--line-w) solid var(--line);border-radius:var(--r-tile);background:var(--bg)}
.dsec h3{margin:0;font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}
.dsec form{gap:var(--g-ctl)}
.dsec .cbtns{grid-template-columns:auto auto;justify-content:start}
button.danger{color:var(--danger);border-color:var(--danger);background:transparent}
button.danger:hover:not(:disabled){border-color:var(--danger);background:color-mix(in srgb,var(--danger) 12%,transparent)}
button.danger.confirm{background:var(--danger);border-color:var(--danger);color:#fff}
@media(max-width:640px){
  .drawer{top:auto;bottom:0;left:0;right:0;width:100%;max-width:none;height:61.8vh;border-left:0;border-top:var(--line-w) solid var(--line);border-radius:var(--r-drawer) var(--r-drawer) 0 0;transform:translateY(100%)}
  .drawer.open{transform:none}
}
@media(prefers-reduced-motion:reduce){
  .joinbar-ic,.pwr.sending,.sending .pwr,.cstat.pending{animation:none}
  .pwr.sending,.sending .pwr{opacity:.6}
  .dev.new{animation:none;border-color:var(--accent);border-width:var(--line-w-act)}
  .drawer,.scrim{transition:none}
}

/* Notification levels (hub notify: 0 info, 1 warn, 2 critical). Warn uses the
   coral --warn token, critical the --danger red; info keeps the plain card. */
.note-h{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;min-width:0}
.lvl{display:inline-flex;align-items:center;border-radius:var(--r-pill);border:1.5px solid currentColor;padding:2px 8px;font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}
.lvl.lvl-1{color:var(--warn);background:color-mix(in srgb,var(--warn) 12%,transparent)}
.lvl.lvl-2{color:var(--danger);background:color-mix(in srgb,var(--danger) 14%,transparent)}
.note.lvl-1{border-left:3px solid var(--warn)}
.note.lvl-2{border-left:3px solid var(--danger)}
.note.unread.lvl-2{box-shadow:0 0 0 1px color-mix(in srgb,var(--danger) 35%,transparent)}
/* Settings: phone notifications card */
.push-card{max-width:620px}
.push-card form{margin-bottom:var(--s3)}
.push-actions{display:flex;gap:var(--s2);flex-wrap:wrap;margin:var(--s4) 0 var(--s3)}
.push-card .ctl-note{margin:var(--s2) 0}
#ota-current:empty{display:none}
@media(max-width:640px){.push-actions button{flex:1 1 100%}}
