/* Built for a small touch screen bolted to the Pi, read from across a room: dark, few words,
   large hit targets. Everything sizes off clamp() so the same page works on a 5" panel and on
   a phone without media queries to keep in sync. */

:root {
  --bg: #12151a;
  --card: #1b2027;
  --card-hi: #232a33;
  --line: #2e3742;
  --text: #e8edf3;
  --dim: #93a1b1;
  --green: #34c759;
  --amber: #ffb020;
  --red: #ff453a;
  --grey: #5b6673;
  --radius: 18px;
}

* { box-sizing: border-box; }

/* Any rule that sets display would otherwise beat the hidden attribute — which is exactly what
   happened to the details panel: .sheet{display:flex} left it on screen, covering the events. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 clamp(15px, 2.4vw, 19px)/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
}

/* The page is the viewport: header, the cards, and the event list taking whatever is left,
   scrolling inside itself. When the cards alone are taller than the screen (many plugs on a
   phone) the page scrolls as a whole and the list keeps a readable minimum. */
html, body { height: 100%; }
body { padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); display: flex; flex-direction: column; overflow-y: auto; }
main { flex: 1; min-height: 0; }
.events { display: flex; flex-direction: column; min-height: 8rem; }
.events ol { flex: 1; min-height: 0; overflow-y: auto; padding-right: .2rem; scrollbar-width: thin; align-content: start; }   /* rows keep their height when the list is short */

header {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; padding: .8rem clamp(.8rem, 2vw, 1.4rem);
  border-bottom: 1px solid var(--line);
}

/* The traffic light is 32px on every screen and the title is the same 32px, so the text line
   is exactly as tall as the light (capitals at that height read as level with it; 42px, which
   would make the capitals themselves 32px, was too loud). Fixed pixels on purpose: the brand
   should not shrink on a phone. */
h1 { font-size: 32px; margin: 0; letter-spacing: .04em; display: flex; align-items: center; gap: .3rem; line-height: 1; }
h1 .logo { width: 32px; height: 32px; flex: none; }
h2 { font-size: 1em; margin: 0 0 .6rem; color: var(--dim); font-weight: 600; }

.head-right { display: flex; align-items: center; gap: .6rem; flex: none; }
.lang { position: relative; flex: none; }
.flag-btn { padding: .5rem .6rem; min-height: 0; display: flex; align-items: center; }
.flag { width: 30px; height: 20px; border-radius: 4px; display: block; object-fit: cover; }
.lang-list {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 5; margin: 0; padding: .3rem; list-style: none;
  background: var(--card-hi); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,.4);
  min-width: 10rem;
}
.lang-list li { display: flex; align-items: center; gap: .6rem; padding: .6rem .7rem; border-radius: 8px; cursor: pointer; white-space: nowrap; }
.lang-list li:hover { background: var(--card); }
.mode {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .8rem 1rem; border: 1px solid var(--line); border-radius: 16px; background: var(--card);
}
.mode-text { min-width: 0; }
.mode-name { font-weight: 700; font-size: 1.05em; }
.mode-hint { color: var(--dim); font-size: .9em; margin-top: .15rem; }
/* On a phone "Sema4 · EN · ●" needs the padding trimmed */
@media (max-width: 480px) {
  header { gap: .5rem; }
  .head-right { gap: .4rem; }
  .seg button { padding: .55rem .6rem; }
  #lang { padding: .55rem .7rem; }
}

main { padding: clamp(.8rem, 2vw, 1.4rem); display: grid; gap: 1.2rem; grid-template-rows: auto auto auto minmax(0, 1fr); align-content: start; }

/* --- the traffic lights --- */

.tiles {
  display: grid; gap: clamp(.7rem, 1.6vw, 1.1rem);
  grid-template-columns: minmax(0, 1fr);              /* phone: one card under the other */
}
@media (min-width: 560px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* wider: two a row */
}

.tile {
  position: relative; text-align: left;
  background: var(--card); color: var(--text);
  border: 1px solid var(--line); border-left: 12px solid var(--grey);
  border-radius: var(--radius); padding: clamp(.7rem, 2.6vw, 1rem) clamp(.75rem, 2.8vw, 1.1rem);
  display: flex; flex-direction: column; gap: .3rem;
  cursor: pointer;
}
/* Rectangles, as tall as the text needs and no taller: a square left a large empty middle on
   any screen wider than a phone. */

/* The star sits in the corner; the name keeps the whole width except that corner. A 24 px icon
   there is safe where a text badge and the switch were not. */
.tile .head { position: relative; padding-right: 1.9rem; }
/* Watts and switch share a row; the switch never collides with a word this way */
.tile .foot { display: flex; justify-content: space-between; align-items: center; gap: .6rem; margin-top: auto; }
.tile .state { white-space: nowrap; }

.tile:active { filter: brightness(1.25); }

/* Three colours, one meaning each: green works, orange is queued, grey does not work — whether
   the customer switched it off or it is simply resting. The switch says which. Tinting the whole
   card carries across a room where a coloured edge would not. */
/* The colour follows the switch: on is green, whatever the meter says — a switch on and a grey
   bar read as a contradiction (the air conditioner in standby, 5/9/2026). The word under the
   watts says whether it is working, resting or has nothing plugged in. Grey is off, orange is
   queued. */
.tile.running, .tile.ready { border-left-color: var(--green); background: linear-gradient(105deg, #17351f, var(--card) 65%); }
.tile.waiting { border-left-color: var(--amber); background: linear-gradient(105deg, #3a2c0c, var(--card) 65%); }
.tile.off { border-left-color: var(--grey); }
.tile.unknown { border-left-color: var(--line); opacity: .55; }

/* State first, in words, because colour alone excludes anyone colour-blind */
.tile .state { display: flex; align-items: center; gap: .45rem; font-weight: 650; font-size: 1.15em; }
/* the queued state is a button, dressed as the plain line: same place, same look, a hand cursor */
.tile button.state { padding: 0; min-height: 0; border: 0; background: transparent; color: inherit; font: inherit; font-weight: 650; font-size: 1.15em; cursor: pointer; align-self: flex-start; }
.tile button.state:hover span:last-child { text-decoration: underline; }
.tile button.state:active { transform: scale(.97); }
.running .state, .ready .state { color: #6ee98a; }
.waiting .state, .tile.waiting button.state { color: #ffc554; }

/* Shrinks on a narrow card so the name and the star fit on one line before it has to break */
.tile .name { display: block; font-size: 1.35em; font-weight: 650; line-height: 1.15; overflow-wrap: anywhere; }
.tile .watt { font-variant-numeric: tabular-nums; font-size: 1.9em; font-weight: 300; white-space: nowrap; }
.tile .watt small { font-size: .45em; color: var(--dim); font-weight: 400; }
/* No reserved height: the grid already gives the cards of a row the same height, so a row of
   cards without a hint (all off) is just shorter instead of carrying two empty lines each. */
.tile .why { color: var(--dim); font-size: .85em; }
.tile .why:empty { display: none; }
/* A plug not named yet: the hint is the thing to read on that card */
.tile.fresh .why { color: var(--amber); }
.tile.fresh .name { font-size: 1em; font-weight: 500; color: var(--dim); overflow-wrap: anywhere; }
.sheet .hint { color: var(--dim); font-size: .85em; margin: -.3rem 0 .4rem; overflow-wrap: anywhere; }

.dot { width: .85rem; height: .85rem; border-radius: 50%; background: var(--grey); flex: none; }
.running .dot, .ready .dot { background: var(--green); }
.waiting .dot { background: var(--amber); animation: pulse 1.6s ease-in-out infinite; }

@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .waiting .dot { animation: none; } }

/* The star: outline when this plug is normal, lit amber when it has priority */
button.star {
  position: absolute; top: -.55rem; right: -.6rem;
  width: 2.4rem; height: 2.4rem; min-height: 0; padding: 0;
  border: 0; background: transparent; color: var(--dim); font-size: 1.5rem; line-height: 1;
}
button.star.lit { color: var(--amber); text-shadow: 0 0 10px rgba(255, 159, 10, .55); }
button.star:active { background: transparent; transform: scale(1.2); }

/* --- the switch on the card: big enough for a thumb, position = relay --- */
.sw { flex: none; display: inline-block; cursor: pointer; }
.sw-input { position: absolute; opacity: 0; width: 0; height: 0; }
.sw-track {
  display: block; width: 3.4rem; height: 1.9rem; border-radius: 999px;
  background: #3a4150; border: 1px solid var(--line); position: relative;
  transition: background .15s;
}
.sw-track::after {
  content: ""; position: absolute; top: .15rem; left: .15rem;
  width: 1.5rem; height: 1.5rem; border-radius: 50%; background: #e8ecf1;
  transition: transform .15s;
}
.sw-input:checked + .sw-track { background: var(--green); border-color: var(--green); }
.sw-input:checked + .sw-track::after { transform: translateX(1.5rem); }
.sw-input:disabled + .sw-track { opacity: .35; cursor: default; }
.sw-input:focus-visible + .sw-track { outline: 2px solid var(--text); outline-offset: 2px; }

/* --- controls --- */

button {
  font: inherit; color: var(--text); background: var(--card-hi);
  border: 1px solid var(--line); border-radius: 12px;
  padding: .7rem 1rem; min-height: 2.9rem; cursor: pointer;
}

button:active { background: var(--line); }
button.ghost { background: transparent; }
button.wide { flex: 1; }
button.on { background: #1e4d2b; border-color: var(--green); }
button.offb { background: #4d1f1c; border-color: var(--red); }
button[disabled] { opacity: .45; cursor: default; }

.row { display: flex; gap: .7rem; flex-wrap: wrap; }
.actions { margin-top: 1.2rem; justify-content: flex-start; }
.sheet-box .foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 1.2rem; }
.sheet-box .foot .hint { margin: 0; color: var(--dim); font-size: .8em; }
button:disabled { opacity: .45; cursor: default; }
.actions button { flex: 1; }
button.danger { color: var(--red); border-color: var(--line); }
button.danger:hover { border-color: var(--red); }

.seg { display: flex; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.seg button { flex: 1; border: 0; border-radius: 0; background: transparent; padding: .55rem .9rem; min-height: 2.4rem; }
.seg button[aria-pressed="true"] { background: var(--card-hi); font-weight: 650; }

/* The link-lost notice: full width, under the header, only when the daemon stops answering.
   It replaced a green dot that was there all the time and that the owner had to ask about. */
.offline { background: var(--red); color: #fff; font-weight: 650; text-align: center; padding: .6rem 1rem; }

/* --- events --- */

.events ol { list-style: none; margin: 0; padding: 0; display: grid; gap: .3rem; }
.events li {
  display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: baseline;
  padding: .4rem .6rem; background: var(--card); border-radius: 10px;
  font-size: .85em; border-left: 3px solid var(--line);
}
.events li.warn { border-left-color: var(--amber); }
.events li.err { border-left-color: var(--red); }
.events time { color: var(--dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.events .plug { color: var(--dim); }

/* --- the sheet --- */

/* The panel opens where the customer tapped: its top at the card's top, a margin all round, on
   every screen size. A sheet sliding in from the bottom, or a box in the middle, both put it
   somewhere else than where the eyes are. The offset is set by the script (--sheet-top). */
.sheet {
  position: fixed; inset: 0; background: #000a; z-index: 10;
  display: flex; align-items: flex-start; justify-content: center; padding: 1rem;
}
.sheet-box {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  width: min(40rem, 100%); max-height: calc(100vh - 2rem); overflow-y: auto;
  padding: 1rem clamp(.9rem, 2vw, 1.4rem) 1.4rem;
  margin-top: var(--sheet-top, 0px);
  box-shadow: 0 12px 40px rgba(0,0,0,.5);
}
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.modal { z-index: 20; }                                  /* above the plug/settings panel */
/* A fixed top margin, not a share of the viewport: on a phone the viewport shrinks and grows with
   the keyboard, and a dialog that moves with it slides its buttons from under the finger. */
.modal .sheet-box { width: min(26rem, 100%); margin-top: 3rem; }
.modal p { color: var(--dim); margin: .2rem 0 1rem; line-height: 1.4; }
.modal .field { grid-template-columns: minmax(6.5rem, 9rem) minmax(0, 1fr); }
.modal .field input { width: 100%; }
/* eight digits at most: a box sized for them, not for the row (letter-spaced like a PIN pad) */
.modal input.pin { width: 9.5rem; letter-spacing: .25em; font-size: 1.15em; text-align: center;
                   background: var(--bg); border-color: var(--dim); }   /* visible before it has focus: a phone gives it none */
.modal .err { color: var(--red); font-size: .9em; min-height: 1.2em; margin: .4rem 0 0; }
/* dialog buttons at their natural size, at the right — not stretched across the box */
.modal .actions { justify-content: flex-end; margin-top: .8rem; }
.modal .actions button { flex: 0 0 auto; min-width: 7rem; }
.sheet-head h2 { margin: 0; color: var(--text); font-size: 1.2em; }
#sheet-close { font-size: 1.4em; line-height: 1; padding: .3rem .7rem; }

/* One line per setting: the label on the left, the control and its button on the right, the
   hint (if any) under the control. On a phone the label goes above. */
.field { display: grid; grid-template-columns: minmax(5.5rem, 8rem) minmax(0, 1fr); gap: .3rem .8rem; align-items: center; margin: 1rem 0; }
.field > label { color: var(--dim); font-size: .9em; }
.field .hint { grid-column: 2; color: var(--dim); font-size: .8em; }
@media (max-width: 480px) { .field { grid-template-columns: 5.5rem minmax(0, 1fr); gap: .3rem .6rem; } .field > label { font-size: .85em; } }

input, select {
  font: inherit; color: var(--text); background: var(--card);
  border: 1px solid var(--line); border-radius: 12px;
  padding: .7rem .8rem; min-height: 2.9rem; width: 100%;
}

/* One focus look everywhere, instead of each browser's own (an orange ring on desktop Chrome,
   next to nothing on a phone): the box being typed in has the amber edge of the traffic light. */
input:focus-visible, select:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; border-color: var(--amber); }

.pair { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.pair input[type=number] { width: 4.6rem; padding-left: .6rem; padding-right: .4rem; }
.pair input.port { width: 6.2rem; }                      /* five digits, plus the spinner */
.pair input[type=text] { flex: 1; min-width: 10rem; }   /* a name has to be readable while typed */

.wattbox { display: flex; align-items: center; gap: .35rem; }
.wattbox > span:first-child { color: var(--dim); font-size: .85em; white-space: nowrap; }
.pair input.watt { width: 3.4rem; padding: .55rem .4rem; text-align: right; -moz-appearance: textfield; appearance: textfield; }
.pair input.watt::-webkit-inner-spin-button, .pair input.watt::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.nudges { display: inline-flex; flex-direction: column; gap: 2px; }
.nudges button.nudge {
  padding: 0; width: 1.7rem; height: 1.3rem; min-height: 0; line-height: 1; font-size: .6em;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--dim);
}
@media (max-width: 480px) {
  .pair { gap: .4rem; }
  .wattbox { gap: .25rem; }
  .pair input.watt { width: 3rem; padding: .5rem .3rem; }
  .nudges button.nudge { width: 1.4rem; }
}
.nudges button.nudge:active { color: var(--fg); border-color: var(--fg); }

.toast {
  position: fixed; left: 50%; bottom: 1.2rem; transform: translateX(-50%);
  background: var(--card-hi); border: 1px solid var(--line); border-radius: 12px;
  padding: .7rem 1.1rem; z-index: 20; max-width: 90vw;
}
.toast.err { border-color: var(--red); }
