/* The operator page: the whole control panel, on a phone, in a dark room.

   It does not borrow the visitor page's stylesheet. That file is written for one
   round pad and nothing else, and half of what it sets — a flex column that
   fills the viewport, a body that never scrolls — is exactly wrong for a page
   that is mostly a long list of sliders.

   Four things drive how this looks. It is a light source in a room whose whole
   point is that it is dim, so nothing here is white and nothing brightens on
   press. It is used by touch in that dark, so every target is finger-sized and
   the sliders are fatter than a desktop panel's. What the ceiling is doing is
   pinned to the top, because it is true continuously rather than on request.
   And the dim controls are pinned to the bottom, out of the scroll, because the
   moment somebody wants them is a moment when scrolling to find them is the
   thing that goes wrong. Everything between those two edges scrolls. */

* { box-sizing: border-box; }

/* Kept rather than removed, because somebody navigating this by keyboard has no
   other way to know where they are — but recoloured, because the browser's
   default ring is a bright blue rectangle and this page is a light source in a
   room whose whole point is that it is dim. */
:focus-visible { outline: 2px solid #3a6f63; outline-offset: 2px; }

html, body {
  margin: 0;
  background: #05060a;
  color: #6a7280;
  font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif;
  /* Pull-to-refresh on a control panel means losing your place mid-set. */
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* The foot of the scroll clears the dim bar, which is 54px of button plus its
   padding and whatever the phone reserves under it. Without this the last
   setting on the page can never be dragged. */
body { padding: 0 12px calc(96px + env(safe-area-inset-bottom)); }

main {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 560px;
  margin: 0 auto;
}

/* --------------------------------------------------------- the ceiling now */

/* Sticky rather than fixed, so it takes part in the scroll's own layout and the
   first card sits under it rather than behind it. The negative margin pulls it
   back out to the full width the body padding took in, so the rule under it
   reaches both edges of the screen. */
#now {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 -12px;
  padding: max(10px, env(safe-area-inset-top)) 12px 10px;
  background: #05060a;
  border-bottom: 1px solid #12161e;
}

#now .row1,
#now .row2 { max-width: 560px; margin: 0 auto; }

#now .row1 { display: flex; align-items: center; gap: 8px; }
#now .row2 { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }

/* The row above sets a display, and a display set in a stylesheet beats the
   `hidden` attribute every time — the browser's own rule for `hidden` is only
   `display: none` at the weakest origin there is, so the line above quietly
   cancelled it. The row went on showing whatever it last held whenever the page
   hid it, which nobody caught because the only time it was hidden used to be
   before the first reply arrived, when it was empty anyway. It matters now: the
   page hides this row the moment the ceiling stops answering, and a strip of
   chips still claiming what is crossing overhead, sitting under a header that
   has just said it cannot tell, is the page contradicting itself about the one
   thing somebody looked up to check. */
#now .row2[hidden] { display: none; }

#dot { width: 9px; height: 9px; border-radius: 50%; background: #2f3542; flex: 0 0 auto; }
#dot[data-state="live"] { background: #46705f; }
#dot[data-state="waiting"] { background: #6c5a3a; }

/* `min-width: 0` because a flex item will not shrink below its own content
   otherwise, and without it a long look name pushes the dim state off the edge
   of the screen instead of clipping. */
#now .look {
  flex: 0 1 auto;
  min-width: 0;
  color: #8a94a4;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#now .state { margin-left: auto; flex: 0 0 auto; font-size: 13px; color: #5d6472; }

/* A body's label runs to seventy characters and four of them can be up at once,
   so an unbounded chip row stacks four full lines into the header and eats a
   quarter of the screen — which quietly breaks the promise the dim bar is
   making at the other edge. The label is clipped instead: what it is for is
   recognising a line you sent a minute ago, and the first dozen characters do
   that. */
.chip {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  max-width: 100%;
  padding: 5px 11px;
  border: 1px solid #223129;
  border-radius: 999px;
  background: #0a0f0d;
  font-size: 12px;
  color: #7fb8a8;
}

.chip .label {
  max-width: 15ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chip time { flex: 0 0 auto; color: #47635a; font-variant-numeric: tabular-nums; }

#now .none { font-size: 12px; color: #3a414f; }

#now .clear {
  margin-left: auto;
  min-height: 0;
  padding: 6px 2px;
  border: 0;
  border-radius: 0;
  background: none;
  color: #5d6472;
  font-size: 12px;
}

/* ------------------------------------------------------------------- cards */

.card { border: 1px solid #161b26; border-radius: 18px; overflow: hidden; }

.card > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px;
  color: #8a94a4;
  font-size: 16px;
  cursor: pointer;
  list-style: none;
}

.card > summary::-webkit-details-marker { display: none; }
.card > summary .sub { margin-left: auto; font-size: 12px; color: #3a414f; }

.card > .body {
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ---------------------------------------------------------------- controls */

button {
  /* A finger in the dark is not accurate, and the cost of missing is a tap that
     did nothing at the moment it mattered. */
  min-height: 60px;
  padding: 10px;
  border: 1px solid #202634;
  border-radius: 14px;
  background: #090b11;
  color: #78818f;
  font: inherit;
  font-size: 16px;
  /* Only ever down. The press state darkens rather than lighting up. */
  transition: background 120ms linear, color 120ms linear;
}

button:active { background: #05060a; color: #5d6472; }
button:disabled { opacity: 0.35; }
button[aria-pressed="true"] { border-color: #2c4a44; color: #7fd4c1; }

/* Full width, shorter, and with no fill behind it: the one destructive control
   on the page reads as a different kind of thing from the buttons above it
   without being hidden or confirmed, because in a dark room a confirmation
   dialog is one more thing to find. */
.ghost {
  min-height: 46px;
  border-color: #161b26;
  background: none;
  color: #5d6472;
  font-size: 14px;
}

.minor { min-height: 52px; font-size: 14px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

select,
input[type="text"] {
  width: 100%;
  min-height: 52px;
  padding: 10px 12px;
  border: 1px solid #202634;
  border-radius: 12px;
  background: #090b11;
  color: #97a2b1;
  font: inherit;
  font-size: 16px;   /* under 16px iOS zooms the page on focus */
}

input[type="text"]::placeholder { color: #3d4653; }

.note { margin: 0; font-size: 12px; line-height: 1.45; color: #3a414f; }

/* -------------------------------------------------------------- the sender */

/* Two halves of one control rather than two buttons that happen to be next to
   each other, so it reads as a switch with a position rather than as a pair of
   things either of which might be on. */
.seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border: 1px solid #202634;
  border-radius: 12px;
  overflow: hidden;
}

.seg button {
  min-height: 48px;
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: 15px;
}

.seg button[aria-pressed="true"] { background: #10151f; color: #9aa5b4; }

/* What will actually cross, shown before it does. The dashed edge says the
   thing inside is not a control — nothing in here is pressable. */
.preview {
  min-height: 84px;
  padding: 10px;
  border: 1px dashed #1c2230;
  border-radius: 12px;
  display: grid;
  place-content: center;
  text-align: center;
}

.preview .line {
  font-size: 22px;
  line-height: 1.25;
  color: #7fd4c1;
  letter-spacing: 0.02em;
  overflow-wrap: anywhere;
}

.preview .empty { font-size: 13px; color: #3a414f; }
.preview img { max-width: 140px; max-height: 64px; border-radius: 8px; }
.preview canvas { width: 64px; height: 64px; opacity: 0.85; }

.how > summary {
  padding: 4px 0;
  font-size: 13px;
  color: #5d6472;
  cursor: pointer;
  list-style: none;
}

.how > summary::-webkit-details-marker { display: none; }
.how > summary::before { content: "\25B8\00a0"; color: #3a414f; }
.how[open] > summary::before { content: "\25BE\00a0"; }

/* Three across, always. Two-and-one wrapping made the third option look like it
   belonged to something else, and at this width three equal columns fit if the
   menus keep their padding tight. */
.how .opts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding-top: 8px; }
.how .opts span { display: block; margin-bottom: 6px; font-size: 12px; color: #6a7482; }
.how .opts select { padding: 10px 6px; }

/* ------------------------------------------------------------ the settings */

.adv {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0 8px;
  font-size: 14px;
  color: #6a7280;
}

.adv input[type="checkbox"] { width: 22px; height: 22px; accent-color: #46525f; }

.scope + .scope { margin-top: 10px; }

.scopehead { display: flex; align-items: baseline; gap: 10px; padding: 4px 0; }

.scopehead .tag {
  flex: 0 0 auto;
  padding: 3px 8px;
  border-radius: 6px;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.scopehead .tag.look { color: #7fd4c1; background: #0a1512; }
.scopehead .tag.room { color: #7fa8d4; background: #0d1420; }
.scopehead .what { font-size: 12px; color: #3a414f; }

.scope details { border-top: 1px solid #12161e; }

.scope > div > details > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 4px;
  font-size: 15px;
  color: #8a94a4;
  cursor: pointer;
  list-style: none;
}

.scope details > summary::-webkit-details-marker { display: none; }
.scope details > summary .name::before { content: "\25B8\00a0"; color: #3a414f; }
.scope details[open] > summary .name::before { content: "\25BE\00a0"; }

/* The count is what makes the advanced toggle visible from outside a closed
   section. Without it, ticking the box changed nothing anybody could see until
   they opened an accordion, which made it read as a switch that did nothing. */
.scope details > summary .count { margin-left: auto; font-size: 12px; color: #3a414f; }

/* One control per row rather than the desktop panel's label-and-slider pair: at
   phone width a slider sharing a line with its label is too short to place a
   value accurately, and accuracy is the whole job of a slider. */
.ctl {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4px 10px;
  padding: 10px 4px 14px;
}

.ctl label { font-size: 14px; color: #6a7280; }

/* The input level, under the sensitivity slider it describes. Sits tight to the
   slider rather than a row apart, because at arm's length in the dark the two
   have to read as one thing: the number moves while the thumb does. */
.ctl.level {
  padding: 0 4px 14px;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: #6a7280;
}

.ctl .val {
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: #8a94a4;
  text-align: right;
}

.ctl select { grid-column: 1 / -1; }

.ctl input[type="range"] {
  grid-column: 1 / -1;
  width: 100%;
  height: 34px;
  margin: 0;
  appearance: none;
  background: transparent;
}

.ctl input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: #1b212c;
}

.ctl input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  /* Deliberately large. This is dragged with a thumb, in the dark. */
  width: 26px;
  height: 26px;
  margin-top: -11px;
  border-radius: 50%;
  background: #46525f;
  border: none;
}

.ctl input[type="range"]:active::-webkit-slider-thumb { background: #5d6c7a; }

.ctl input[type="range"]::-moz-range-track {
  height: 4px; border-radius: 2px; background: #1b212c;
}
.ctl input[type="range"]::-moz-range-thumb {
  width: 26px; height: 26px; border: none; border-radius: 50%; background: #46525f;
}

.ctl input[type="checkbox"] {
  width: 26px;
  height: 26px;
  accent-color: #46525f;
}

/* ------------------------------------------------------------- the dim bar */

#dimbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  /* Solid rather than translucent: a blur over a dark page costs a surprising
     amount of GPU on an old phone, and this sits over nothing worth seeing. */
  background: #05060a;
  border-top: 1px solid #12161e;
}

#dimbar .inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  max-width: 560px;
  margin: 0 auto;
}

#dimbar button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 54px;
  padding: 6px 4px;
  border-radius: 12px;
  font-size: 14px;
}

/* How much of the hold is left, under the level that is holding it. Empty on
   every button but the active one, and empty on that one too when the ceiling
   is not reporting a remaining time — a countdown invented here would be a
   guess about the ceiling's own clock. */
#dimbar button time {
  font-size: 11px;
  color: #47635a;
  font-variant-numeric: tabular-nums;
}

#dimbar button time:empty { display: none; }

/* ---------------------------------------------------------------- the say */

#toast {
  position: fixed;
  left: 50%;
  bottom: calc(96px + env(safe-area-inset-bottom));
  z-index: 4;
  transform: translateX(-50%) translateY(8px);
  /* `max-content` because a fixed box pinned at `left: 50%` otherwise shrinks to
     fit the space between that edge and the right of the screen — half the
     viewport — so a sentence that fits on one line wrapped onto three. The
     max-width is what stops the longest of them running off both sides instead.
     Between the two, a toast is one line whenever one line will do. */
  width: max-content;
  max-width: calc(100vw - 32px);
  padding: 10px 18px;
  border: 1px solid #223129;
  border-radius: 999px;
  background: #0a0f0d;
  color: #7fb8a8;
  font-size: 14px;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  /* Decelerating, because the rise is an answer to a tap and should arrive
     fast then settle, not glide in at one speed. */
  transition: opacity 200ms ease-out, transform 200ms ease-out;
}

#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Under reduced motion the toast appears in place instead of rising. The
   colour fades on the buttons stay: they move nothing, and they are the "only
   ever down" press feedback this page is built around. */
@media (prefers-reduced-motion: reduce) {
  #toast { transform: translateX(-50%); transition: none; }
}
