/* Vigalert — gate control panel
 *
 * Palette is a machine cabinet: enamel green-grey body, engraved label
 * plates, safety yellow for caution. Red and green are RESERVED — they
 * appear only where they carry signal meaning. Nothing decorative is ever
 * red or green, so when you see one you know what it means.
 *
 * System fonts only. This has to render on a gate VLAN with no internet.
 */

:root {
  --enamel:       #262c2a;
  --enamel-up:    #313937;
  --enamel-edge:  #171c1b;
  --plate:        #d9d6cc;
  --plate-dim:    #8f948f;
  --safety:       #e8b429;
  --signal-red:   #e0392c;
  --signal-green: #23b256;
  --ink:          #0e1211;

  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas,
          "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --r: 3px;
}

* { box-sizing: border-box; }

/* The `hidden` attribute means hidden, whatever else a rule says.
   Any author `display:` beats the UA stylesheet's [hidden] rule, so a
   `display: flex` element with hidden set stays on screen — which is
   how the after-hours acknowledgement ended up showing during business
   hours. This makes the attribute mean what it says. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--enamel);
  color: var(--plate);
  font: 15px/1.55 var(--sans);
  -webkit-text-size-adjust: 100%;
}

main { max-width: 940px; margin: 0 auto; padding: 22px 18px 64px; }

h1 { font-size: 20px; margin: 26px 0 6px; font-weight: 600; }
h2 { font-size: 16px; margin: 30px 0 10px; font-weight: 600; }

a { color: var(--plate); }

/* --- engraved plate lettering ------------------------------------------- */

.wordmark {
  font: 600 22px/1 var(--mono);
  letter-spacing: .34em;
  margin: 0;
  color: var(--plate);
}
.wordmark.small { font-size: 13px; letter-spacing: .28em; }

.mono, .position, .tag, .kind, .meta { font-family: var(--mono); }

/* --- top bar ------------------------------------------------------------- */

.topbar {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 14px 18px;
  background: var(--enamel-edge);
  border-bottom: 1px solid #000;
}
.topbar nav { display: flex; gap: 16px; margin-right: auto; }
.topbar nav a {
  font: 500 12px/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase;
  text-decoration: none; color: var(--plate-dim);
  padding: 6px 0; border-bottom: 2px solid transparent;
}
.topbar nav a:hover { color: var(--plate); }
.topbar nav a.on { color: var(--plate); border-bottom-color: var(--safety); }
.who { font: 12px var(--mono); color: var(--plate-dim); margin-right: 10px; }

/* --- the signal head: the one bold thing --------------------------------- */

.signal {
  flex: none;
  width: 62px; padding: 8px 8px 9px;
  background: var(--enamel-edge);
  border: 1px solid #000;
  border-radius: 7px;
  box-shadow: inset 0 1px 0 #ffffff12, 0 2px 6px #0006;
  display: flex; flex-direction: column; gap: 8px;
}

.lamp {
  display: block;
  width: 44px; height: 44px;
  border-radius: 50%;
  /* unlit means unlit — a dark well, not a grey circle */
  background: #101413;
  box-shadow: inset 0 2px 5px #000, inset 0 -1px 0 #ffffff08;
  transition: background .25s ease, box-shadow .25s ease;
}

.signal.stop .lamp.red {
  background: var(--signal-red);
  box-shadow: inset 0 -3px 8px #0007, 0 0 16px #e0392c88;
}
.signal.go .lamp.green {
  background: var(--signal-green);
  box-shadow: inset 0 -3px 8px #0007, 0 0 16px #23b25688;
}
.signal.moving .lamp.red {
  background: var(--safety);
  box-shadow: inset 0 -3px 8px #0007, 0 0 16px #e8b42988;
  animation: pulse 1.1s steps(2, jump-none) infinite;
}

@keyframes pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .signal.moving .lamp.red { animation: none; }
}

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

.gates { display: flex; flex-direction: column; gap: 16px; }

.gate {
  background: var(--enamel-up);
  border: 1px solid var(--enamel-edge);
  border-radius: var(--r);
  padding: 18px;
}
.gate.offline { opacity: .72; }

.signal-row { display: flex; gap: 18px; align-items: flex-start; }
.readout h2 { margin: 2px 0 4px; font-size: 17px; }

.position {
  margin: 0;
  font-size: 26px; font-weight: 600; letter-spacing: .1em;
  line-height: 1.1;
}

.meta { margin: 8px 0 0; font-size: 11.5px; color: var(--plate-dim);
        display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

.tag {
  font-size: 10.5px; letter-spacing: .1em;
  border: 1px solid #ffffff26; border-radius: 2px;
  padding: 3px 7px; color: var(--plate-dim);
}
.tag.live { color: var(--plate); border-color: #ffffff55; }
.tag.caution { color: var(--safety); border-color: #e8b42966; }
.warn {
  font-size: 10.5px; letter-spacing: .1em;
  color: var(--ink); background: var(--safety);
  padding: 3px 7px; border-radius: 2px; font-weight: 700;
}

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

.controls {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid #ffffff14;
}

.inline { display: inline-flex; gap: 8px; align-items: center; margin: 0; }

.btn {
  font: 600 12px/1 var(--mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--plate);
  background: var(--enamel-up);
  border: 1px solid #ffffff2e;
  border-bottom-color: #0009;
  border-radius: var(--r);
  padding: 11px 16px;
  cursor: pointer;
  box-shadow: inset 0 1px 0 #ffffff12;
}
.btn:hover:not(:disabled) { background: #3b4441; }
.btn:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }
.btn:disabled { opacity: .38; cursor: not-allowed; }

.btn.primary { background: var(--plate); color: var(--ink); border-color: #fff6; }
.btn.primary:hover:not(:disabled) { background: #fff; }

.btn.caution { border-color: #e8b42977; color: var(--safety); }

.linkbtn {
  background: none; border: 0; padding: 0;
  color: var(--plate-dim); cursor: pointer;
  font: 11.5px var(--mono); text-decoration: underline;
  text-underline-offset: 3px;
}
.linkbtn:hover { color: var(--plate); }
.linkbtn.danger:hover { color: var(--signal-red); }

select, input[type=text], input[type=email], input[type=password], input[type=time] {
  font: 13px var(--mono);
  background: var(--enamel-edge);
  color: var(--plate);
  border: 1px solid #ffffff26;
  border-radius: var(--r);
  padding: 9px 10px;
}
:focus-visible { outline: 2px solid var(--safety); outline-offset: 2px; }

/* --- forms --------------------------------------------------------------- */

.stack { display: flex; flex-direction: column; gap: 14px; max-width: 360px; }
.stack.wide { max-width: 520px; }
.stack label { display: flex; flex-direction: column; gap: 6px;
               font: 11px var(--mono); letter-spacing: .1em;
               text-transform: uppercase; color: var(--plate-dim); }
.stack label input, .stack label select { text-transform: none; letter-spacing: 0; }
.row { display: flex; gap: 14px; }
.check { flex-direction: row !important; align-items: center; gap: 8px !important; }

.days { border: 1px solid #ffffff1f; border-radius: var(--r); padding: 10px 12px; }
.days legend { font: 11px var(--mono); letter-spacing: .1em;
               text-transform: uppercase; color: var(--plate-dim); padding: 0 5px; }
.day { display: inline-flex; align-items: center; gap: 5px; margin-right: 11px;
       font: 12px var(--mono); }

/* --- tables -------------------------------------------------------------- */

.grid { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: 13px; }
.grid th {
  text-align: left; font: 10.5px var(--mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--plate-dim);
  padding: 8px 10px; border-bottom: 1px solid #ffffff26;
}
.grid td { padding: 9px 10px; border-bottom: 1px solid #ffffff12; }
.grid tr.off { opacity: .5; }
.grid .empty { color: var(--plate-dim); padding: 22px 10px; }
.dim { color: var(--plate-dim); }

.kind { font-size: 11px; letter-spacing: .06em; }
.k-guard_blocked, .k-divergence, .k-auth_failure,
.k-device_offline, .k-command_failed { color: var(--safety); }

.filter { margin-top: 14px; }
.filter label { font: 11px var(--mono); letter-spacing: .1em;
                text-transform: uppercase; color: var(--plate-dim);
                display: inline-flex; gap: 8px; align-items: center; }

/* --- misc ---------------------------------------------------------------- */

.flash {
  max-width: 940px; margin: 14px auto 0; padding: 11px 14px;
  font: 12px var(--mono);
  background: var(--enamel-up); border-left: 3px solid var(--plate-dim);
  border-radius: 2px;
}
.flash.bad { border-left-color: var(--safety); color: var(--safety); }

.note { color: var(--plate-dim); font-size: 12.5px; margin-top: 20px;
        max-width: 620px; }

.gate-login { max-width: 340px; margin: 14vh auto 0; padding: 0 18px; }
.plate { margin-bottom: 28px; }
.plate .sub { font: 11px var(--mono); letter-spacing: .2em;
              text-transform: uppercase; color: var(--plate-dim);
              margin: 8px 0 0; }

@media (max-width: 560px) {
  .signal { width: 52px; }
  .lamp { width: 36px; height: 36px; }
  .position { font-size: 21px; }
  .controls .btn { flex: 1 1 auto; }
  .topbar { gap: 12px; }
}

/* --- one-time secret reveal ---------------------------------------------- */
/* Shown once, never again. Framed as a thing to act on now, not a notice. */

.reveal {
  margin: 18px 0;
  padding: 14px 16px;
  background: var(--enamel-edge);
  border: 1px solid var(--safety);
  border-radius: var(--r);
}
.reveal .label {
  margin: 0 0 9px;
  font: 700 10.5px/1 var(--mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--safety);
}
.secret {
  display: block; word-break: break-all;
  font: 13px var(--mono); color: var(--plate);
  background: #0c0f0e; border: 1px solid #ffffff1f;
  border-radius: 2px; padding: 11px 12px; user-select: all;
}
.reveal .note { margin: 9px 0 0; font-size: 11.5px; }

/* ===========================================================================
   Hold-open dialog
   ---------------------------------------------------------------------------
   The duration is asked for after the decision, not before it. A dropdown
   sitting beside the button is a question nobody has asked yet.
   =========================================================================== */

.hold-dialog {
  background: var(--enamel-up);
  color: var(--plate);
  border: 1px solid var(--enamel-edge);
  border-radius: var(--r);
  padding: 22px;
  max-width: 380px;
  width: calc(100% - 32px);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .55);
}

.hold-dialog::backdrop {
  background: rgba(10, 13, 12, .72);
}

.hold-dialog h3 {
  margin: 0 0 6px;
  font-size: 17px;
  letter-spacing: .01em;
}

.hold-dialog .note { margin: 0 0 16px; }

/* ===========================================================================
   Recent activity
   ---------------------------------------------------------------------------
   Answers "is anyone here" at a glance. Deliberately terse — the Log page
   is where the full picture lives.
   =========================================================================== */

.activity {
  border-top: 1px solid var(--enamel-edge);
  margin-top: 14px;
  padding-top: 12px;
}

.activity ul {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
}

.activity li {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 3px 0;
  font-size: 13.5px;
}

.activity .when {
  color: var(--plate-dim);
  flex: none;
  min-width: 5.5em;
  font-size: 12.5px;
}

.activity .what { color: var(--plate); }

/* Duress is the one thing here that must not blend in. */
.activity li.warn .what {
  color: var(--signal-red);
  font-weight: 600;
}

.activity .note { margin: 0; }

@media (max-width: 520px) {
  .activity .when { min-width: 4.5em; }
}

/* ===========================================================================
   Primary action
   ---------------------------------------------------------------------------
   Sits directly under the status, alone. A held gate needs closing, not
   opening, so only one of Open/Close ever exists at a time.
   =========================================================================== */

.primary-action {
  margin: 12px 0 0;
}

.primary-action .btn.primary {
  min-width: 128px;
  font-size: 15px;
  padding: 9px 20px;
}

/* Close is a caution action, not a go action. Not red: red on this panel
   means the gate is closed, and a red button next to a red lamp reads as
   a state rather than a control. */
.primary-action .btn.close {
  background: var(--safety);
  border-color: var(--safety);
  color: var(--ink);
}

.primary-action .btn.close:hover:not(:disabled) {
  filter: brightness(1.08);
}


/* ===========================================================================
   Status rows
   ---------------------------------------------------------------------------
   One fact per row. Wrapped together on a single line they read as a blob;
   stacked, each is something you can check at a glance.
   =========================================================================== */

.status-rows {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  margin-top: 22px;          /* the gap between the action and the state */
}

.status-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11.5px;
  color: var(--plate-dim);
  font-family: var(--mono);
}

/* Auto-open live: the one tag that earns a colour, because it is the one
   that changes what the gate does when nobody is watching it. */
.tag.armed {
  color: var(--signal-green);
  border-color: rgba(35, 178, 86, .55);
  background: rgba(35, 178, 86, .12);
}

/* ===========================================================================
   Auto-open slider
   ---------------------------------------------------------------------------
   The whole control is one button. The highlight sits over the live state
   and slides to the other when pressed, so what you read and what you
   press are the same object.
   =========================================================================== */

.autoopen {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.ao-label {
  font-size: 14px;
  color: var(--plate);
}

.ao-slider {
  position: relative;
  display: inline-grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  width: 92px;
  padding: 3px;
  background: var(--enamel);
  border: 1px solid var(--enamel-edge);
  border-radius: 999px;
  cursor: pointer;
  font-family: var(--mono);
  overflow: hidden;
}

/* The moving part. Sits behind the words and carries the colour. */
.ao-thumb {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc(50% - 3px);
  border-radius: 999px;
  transition: transform .16s ease, background .16s ease;
}

.ao-slider.is-on  .ao-thumb { transform: translateX(0);     background: var(--signal-green); }
.ao-slider.is-off .ao-thumb { transform: translateX(100%);  background: var(--signal-red); }

.ao-word {
  position: relative;               /* above the thumb */
  z-index: 1;
  text-align: center;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .06em;
  padding: 4px 0;
  color: var(--plate-dim);
  transition: color .16s ease;
}

/* The live word sits on the colour, so it goes dark for contrast. The other
   stays muted — it is a destination, not a state. */
.ao-slider.is-on  .ao-on  { color: var(--ink); }
.ao-slider.is-off .ao-off { color: var(--ink); }

.ao-slider:hover:not(:disabled) { border-color: #ffffff33; }

.ao-slider:disabled { opacity: .45; cursor: not-allowed; }

.ao-slider:focus-visible {
  outline: 2px solid var(--safety);
  outline-offset: 2px;
}

/* Respect a stated preference for less motion — the colour still carries
   the state, so nothing is lost by dropping the slide. */
@media (prefers-reduced-motion: reduce) {
  .ao-thumb, .ao-word { transition: none; }
}


/* ===========================================================================
   Hold dialog — durations, overrun choice, acknowledgement
   =========================================================================== */

.durations {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 14px;
}

.durations .duration {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r);
  cursor: pointer;
  border: 1px solid transparent;
}

/* Hover moves the border, never the text. Recolouring the label is what
   made the hovered row the hardest one to read. */
.durations .duration:hover {
  background: var(--enamel);
  border-color: var(--safety);
}

.durations .duration:has(input:checked) {
  background: var(--enamel);
  border-color: #ffffff2e;
}

.durations input[type="radio"] { accent-color: var(--safety); flex: none; }

.d-label { flex: 1; font-size: 14px; }

/* The clock time each option lands on. The point of showing it is that
   "4 hours" means nothing at a glance but "until 8:00 PM" does. */
.d-until {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--plate-dim);
}

.d-time {
  background: var(--enamel-edge);
  border: 1px solid #ffffff26;
  color: var(--plate);
  border-radius: var(--r);
  padding: 4px 7px;
  font-family: var(--mono);
  font-size: 12.5px;
  color-scheme: dark;
}

.overrun-choice {
  border: 1px solid #e8b42955;
  background: #e8b4290f;
  border-radius: var(--r);
  padding: 12px 14px;
  margin-bottom: 14px;
}

.overrun-choice .note { margin: 0 0 8px; color: var(--safety); }

.radio-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 3px 0;
  font-size: 13.5px;
  cursor: pointer;
}

.radio-row input { accent-color: var(--safety); }

/* The acknowledgement. Deliberately the loudest thing in the dialog —
   an open gate at night is the failure this whole flow exists to prevent. */
.ack {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1px solid var(--signal-red);
  background: rgba(224, 57, 44, .10);
  border-radius: var(--r);
  padding: 12px 14px;
  margin-bottom: 16px;
  font-size: 13.5px;
  line-height: 1.45;
  cursor: pointer;
}

.ack input { accent-color: var(--signal-red); margin-top: 2px; flex: none; }

.after-hours-note {
  color: var(--safety);
  border-left: 2px solid var(--safety);
  padding-left: 10px;
}

.dialog-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* The log's Details column. Narrow on purpose — it carries a hold length,
   not prose, and the columns either side matter more. */
.detail-cell {
  max-width: 22ch;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 720px) {
  .detail-cell { max-width: 12ch; }
}
