/* The gate: sign-in page. An event display: black space, the detector drawn on
   #det, the sign-in plate on the left, the proton mark top left. Loaded after
   pfd.css. mk9. */
body.gate { height: 100%; overflow: hidden; background: var(--void);
  /* success colour for the gate's own UI. The detector keeps cyan for LLP hits:
     that is the event display's physics palette, not a status colour. */
  --win: #43E08C; --win-glow: rgba(67, 224, 140, .65);
  /* Plate state. ONE token drives the left rule, the password field, the state
     diagram and the lock, so those four can never disagree about what the gate
     is doing. Set per state on .gate-plate below, nowhere else. */
  --st: #2A5CC7; --st-soft: rgba(42, 92, 199, .26); --st-glow: rgba(42, 92, 199, .80); }
#det { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
body.gate.probing { cursor: crosshair; }
#wipe { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 40;
  display: none; }
#wipe.run { display: block; }

/* One screen, per the house rule in pfd.css: a fixed height rather than a
   min-height, and min-height:0 on the row holding the plate so it shrinks
   instead of pushing the frame taller. The run block is absolutely positioned,
   so the bottom padding is only reserving room for it. */
.gate-frame { position: relative; z-index: 1; margin: 14px;
  height: calc(100vh - 28px); height: calc(100dvh - 28px); overflow: hidden;
  border: 1px solid rgba(31, 85, 132, .55); display: grid; grid-template-rows: auto minmax(0, 1fr);
  padding: 0 0 clamp(64px, 11vh, 92px); }
.gate-frame::before, .gate-frame::after { content: ""; position: absolute; width: 34px; height: 34px;
  border: 2px solid var(--blue); pointer-events: none; z-index: 2; }
.gate-frame::before { left: -2px; top: -2px; border-right: 0; border-bottom: 0; }
.gate-frame::after { right: -2px; bottom: -2px; border-left: 0; border-top: 0; }

/* ------------------------------------------------------------ proton mark -- */
/* assets/images/proton_2_256.png, trimmed to its content. The mark is IN the
   header's flow, not floated over it: the band grows to whatever the mark needs,
   so it can never reach down into the sign-in plate no matter how the title
   wraps or how short the window is. The image is black-backed, so screen
   blending drops the field and the radial mask feathers the square's corners. */
.gate-head { display: flex; align-items: center; gap: clamp(16px, 2vw, 34px);
  padding: clamp(14px, 3.4vh, 34px) clamp(20px, 3vw, 54px) clamp(10px, 1.6vh, 16px);
  background: linear-gradient(180deg, rgba(1, 3, 6, .78) 0%, rgba(1, 3, 6, .45) 70%, rgba(1, 3, 6, 0) 100%);
  border-bottom: 1px solid rgba(31, 85, 132, .35); }
.head-text { flex: 1 1 auto; min-width: 0; text-align: center; }

.mark { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; }
/* No mask. A radial mask on a circle with no size keyword measures to the
   farthest CORNER, so 62%/82% fell at 0.44w/0.58w -- inside the image's own
   half-width -- and ate the outer ring and the quarks. Screen blending already
   drops the black field completely, which is all the mask was there to do. */
.mark img { width: clamp(96px, min(12.6vw, 19vh), 208px); height: auto; display: block;
  mix-blend-mode: screen;
  filter: drop-shadow(0 0 18px rgba(30, 136, 216, .35)); }
.mark .mark-tag { font-size: 12px; letter-spacing: .22em; color: var(--ink-2); text-transform: uppercase;
  border-left: 1px solid var(--rule-hi); padding-left: 12px; line-height: 1.7; white-space: nowrap; }
.mark .mark-tag b { display: block; color: var(--accent); font-weight: normal; letter-spacing: .18em; }

.gate-title { font-family: var(--display); font-weight: 900; margin: 0; color: #fff; white-space: nowrap;
  font-size: clamp(28px, 3.3vw, 110px); line-height: 1.05; letter-spacing: .035em;
  text-shadow: 0 0 2px #fff, 0 0 26px rgba(30, 136, 216, .6), 0 0 64px rgba(30, 136, 216, .25); }
.gate-title span { display: inline-block; }
.gate-title i { font-style: normal; color: var(--accent); text-shadow: 0 0 18px var(--accent-glow); }
.gate-kicker { color: var(--ink-2); font-size: clamp(11px, 1.5vh, 13px); letter-spacing: .04em;
  margin: clamp(5px, 1vh, 10px) 0 0; }

/* Fit, do not scroll. The row is min-height:0 so it can be smaller than its
   content, the plate is capped at the row's height, and the console is the
   flexible part -- it gives first, because losing two lines of log matters
   less than losing the password field. Every fixed size below is now in vh as
   well as px, so the plate shrinks on a short window instead of handing you a
   scrollbar. */
.gate-main { display: flex; align-items: flex-start; padding: clamp(6px, 1vh, 12px) 7vw 0 2.4vw;
  min-height: 0; overflow: hidden; }
.gate-plate { max-height: 100%; display: flex; flex-direction: column; min-height: 0; }
.plate-body { min-height: 0; }
.crt { min-height: 0; display: flex; flex-direction: column; }
.crt-screen { min-height: 0; }

.gate-plate { width: min(700px, 38vw); background: rgba(6, 16, 27, .9); border: 1px solid var(--rule-hi);
  border-left: 3px solid var(--blue); backdrop-filter: blur(2px);
  box-shadow: 0 0 0 1px rgba(30, 136, 216, .15), 0 24px 60px -24px rgba(30, 136, 216, .55); }
.gate-plate.denied { animation: reject .7s ease-out both; border-left-color: var(--drop); }
.gate-plate.open { border-left-color: var(--st); }
@keyframes reject {
  0% { box-shadow: 0 0 0 2px var(--drop), 0 0 48px -4px rgba(227, 38, 46, .75); transform: translateX(0); }
  15% { transform: translateX(-6px); } 30% { transform: translateX(5px); } 45% { transform: translateX(-3px); }
  60% { transform: translateX(0); }
  100% { box-shadow: 0 0 0 1px rgba(227, 38, 46, .3), 0 24px 60px -24px rgba(227, 38, 46, .45); }
}
/* Two rows: the title, with the state line beneath it. The diagram and the lock
   sit right. Both state words stay on screen -- the live one lit in --st, the
   other barely visible -- so it reads as a switch position, not a label that
   swaps text. */
.plate-head { display: flex; align-items: center; gap: 16px; flex: 0 0 auto;
  padding: clamp(9px, 1.5vh, 14px) 22px; border-bottom: 1px solid var(--rule); }
.ph-text { display: grid; gap: 5px; min-width: 0; }
.plate-name { font-family: var(--display); font-weight: 700; font-size: clamp(12px, 1.15vw, 17px);
  letter-spacing: .14em; color: #fff; text-transform: uppercase; }
.ph-state { display: flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: clamp(10px, .92vw, 13px); letter-spacing: .14em;
  text-transform: uppercase; white-space: nowrap; }
.ph-key { color: var(--ink-2); }
.ph-dash, .ph-pipe { color: var(--rule-hi); }
.ph-state em { font-style: normal; transition: color .3s ease, opacity .3s ease, text-shadow .3s ease; }
.st-lock { color: var(--st); text-shadow: 0 0 10px var(--st-glow); }
.st-open { color: var(--ink-2); opacity: .16; }
.gate-plate.open .st-lock { color: var(--ink-2); opacity: .16; text-shadow: none; }
.gate-plate.open .st-open { color: var(--st); opacity: 1; text-shadow: 0 0 10px var(--st-glow); }

/* ------------------------------------------------------- state diagram -- */
.ph-rig { margin-left: auto; display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.diag { width: clamp(118px, 12vw, 165px); height: auto; overflow: visible; }
.diag .wire { stroke: var(--rule-hi); stroke-width: 1.5; }
.diag .node circle { fill: rgba(3, 9, 16, .9); stroke: var(--rule-hi); stroke-width: 1.5;
  transition: stroke .25s ease; }
.diag .node .pip { fill: var(--rule-hi); stroke: none; transition: fill .25s ease, opacity .25s ease;
  opacity: .5; }
.diag .lab { font-family: var(--mono); font-size: 7.4px; letter-spacing: .14em;
  fill: var(--meta); text-anchor: middle; transition: fill .25s ease; }
/* the live node, whichever it is, wears --st */
.diag .node.on circle { stroke: var(--st); }
.diag .node.on .pip { fill: var(--st); opacity: 1; filter: drop-shadow(0 0 5px var(--st)); }
.diag .lab.on { fill: var(--st); }
.diag .wire.lit { stroke: var(--st); opacity: .75; }

/* ------------------------------------------------------------------ lock -- */
/* Shackle lifts and swings when the gate opens; the body flashes on a reject. */
.lock { width: 36px; height: 41px; flex: 0 0 auto; display: block; overflow: visible; }
.lock .body { fill: var(--st-soft); stroke: var(--st); stroke-width: 1.6;
  transition: stroke .25s ease, fill .25s ease; }
.lock .shackle { fill: none; stroke: var(--st); stroke-width: 2.2; stroke-linecap: round;
  transform-box: fill-box; transform-origin: 82% 100%;
  transition: transform .5s cubic-bezier(.2, .9, .25, 1.2); }
.lock .key { fill: var(--st); transition: fill .25s ease; }
.lock .glow { animation: lock-lamp 2.6s ease-in-out infinite; }
@keyframes lock-lamp { 50% { opacity: .35; } }

.gate-plate.open .lock .shackle { transform: translateY(-5px) rotate(24deg); }


.gate-plate.denied .lock .shackle { animation: shackle-rattle .5s ease-out 2; }

@keyframes shackle-rattle { 25% { transform: rotate(-5deg); } 60% { transform: rotate(4deg); } }

/* ------------------------------------------------------------ cathode -- */
/* A green-phosphor terminal. Bright ink over curved glass, with the things a
   real tube does: scanlines, a shadow mask, a bright band rolling down the
   screen, a slow brightness flicker and an occasional vertical hold slip. All
   of it is decoration over the text, none of it moves the text itself. */
.crt { border: 1px solid var(--rule-hi); background: #01090B;
  box-shadow: inset 0 0 34px -6px rgba(77, 255, 122, .30), 0 0 0 1px rgba(0, 0, 0, .6),
              0 0 26px -12px rgba(77, 255, 122, .45); }
.crt-screen { position: relative; overflow: hidden; padding: 12px 14px 10px;
  background: radial-gradient(120% 140% at 50% 45%, #04180F 0%, #01090B 70%, #000 100%); }

.crt-lines { position: relative; z-index: 1; height: clamp(5.4em, 13vh, 9.2em);
  min-height: 0; overflow: hidden;
  font-family: var(--mono); font-size: clamp(12px, 1.05vw, 16px); line-height: 1.55;
  color: var(--crt-ink);
  text-shadow: 0 0 3px var(--crt-ink), 0 0 10px var(--crt-glow), 0 0 22px var(--crt-glow);
  animation: crt-flicker 3.7s steps(1, end) infinite;
  scrollbar-width: none; }
.crt-lines::-webkit-scrollbar { display: none; }
.crt-line { white-space: pre-wrap; word-break: break-word; }
.crt-line.dim { opacity: .62; }
.crt-line.ok { color: #7CFFA8; text-shadow: 0 0 4px #7CFFA8, 0 0 14px rgba(77, 255, 122, .8); }
.crt-line.bad { color: #FF7A72; text-shadow: 0 0 4px #FF7A72, 0 0 14px rgba(227, 38, 46, .8); }
.crt-line.hot { color: #EAFFF0; letter-spacing: .06em;
  text-shadow: 0 0 5px #fff, 0 0 14px var(--crt-ink), 0 0 30px var(--crt-glow); }
.crt-cursor { display: inline-block; margin-left: 1px;
  animation: crt-blink 1.05s steps(2, end) infinite; }
@keyframes crt-blink { 50% { opacity: 0; } }

/* the glass: scanlines + shadow mask, drifting slowly, under a vignette */
.crt-glass { position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    repeating-linear-gradient(0deg, rgba(0, 0, 0, .40) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .030) 0 1px, transparent 1px 3px),
    radial-gradient(130% 130% at 50% 50%, transparent 50%, rgba(0, 0, 0, .58) 100%);
  background-size: 100% 3px, 3px 100%, 100% 100%;
  animation: crt-drift 9s linear infinite; }
@keyframes crt-drift { to { background-position: 0 3px, 0 0, 0 0; } }

/* the band that rolls down a tube that is not quite in sync */
.crt-screen::after { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 26%;
  z-index: 3; pointer-events: none;
  background: linear-gradient(180deg, transparent 0%, rgba(77, 255, 122, .07) 38%,
              rgba(255, 255, 255, .055) 52%, rgba(77, 255, 122, .05) 64%, transparent 100%);
  animation: crt-band 6.5s linear infinite; }
@keyframes crt-band { 0% { transform: translateY(-130%); } 100% { transform: translateY(520%); } }

@keyframes crt-flicker {
  0%, 93% { opacity: 1; } 94% { opacity: .84; } 95% { opacity: 1; }
  97% { opacity: .91; } 98%, 100% { opacity: 1; }
}

/* tone follows the gate state; green is the resting phosphor */
.crt { --crt-ink: #4DFF7A; --crt-glow: rgba(77, 255, 122, .55); }
.crt.tone-busy { --crt-ink: #9FFFC4; --crt-glow: rgba(120, 255, 180, .6); }
.crt.tone-ok { --crt-ink: #6BFF9B; --crt-glow: rgba(77, 255, 122, .7); }
.crt.tone-bad { --crt-ink: #FF8A84; --crt-glow: rgba(227, 38, 46, .55); }

/* the mark again, in cathode format: a 64x64 grid of one phosphor green that
   scrambles between the proton mark and "13.6 TeV" (crt_mark.js). The canvas is
   64 actual pixels scaled up nearest-neighbour, so the blocks stay square. Sits
   under the glass and beside the text, washed back so it reads as burn-in. */
.crt-mark { position: absolute; right: clamp(40px, 5vw, 96px); top: 50%;
  transform: translateY(-50%); z-index: 1;
  width: clamp(96px, 9.6vw, 160px); height: auto; aspect-ratio: 1 / 1; opacity: .5;
  image-rendering: pixelated; image-rendering: -moz-crisp-edges;
  filter: drop-shadow(0 0 8px var(--crt-glow)); pointer-events: none; }
.crt-lines { padding-right: clamp(150px, 15vw, 270px); }

.crt-foot { display: flex; justify-content: space-between; gap: 10px;
  border-top: 1px solid var(--rule); padding: 7px 14px; font-size: 11px;
  letter-spacing: .18em; color: var(--meta); text-transform: uppercase; }
.rd-fallback { color: var(--accent); font-family: var(--display); letter-spacing: .12em; }

.plate-body { padding: clamp(13px, 2.2vh, 24px) 26px clamp(15px, 2.6vh, 28px);
  display: grid; gap: clamp(10px, 1.8vh, 20px); align-content: start; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.plate-body input { width: 100%; font-size: clamp(17px, 2.6vh, 24px);
  padding: clamp(11px, 1.8vh, 17px) 16px; background: #000;
  letter-spacing: .08em; border: 1px solid var(--st);
  box-shadow: inset 0 0 22px -8px var(--st-glow), 0 0 0 1px var(--st-soft);
  transition: border-color .3s ease, box-shadow .3s ease; }
.plate-body input:focus { border-color: var(--st); outline: none;
  box-shadow: inset 0 0 26px -6px var(--st-glow), 0 0 0 2px var(--st-soft); }
.plate-body input::placeholder { font-family: var(--mono); font-size: 16px; letter-spacing: .26em;
  text-transform: uppercase; color: rgba(127, 152, 174, .55); }
.plate-body input:focus::placeholder { color: rgba(127, 152, 174, .32); }
/* Hovering the button pulses the prompt: the button is telling the field what
   it wants. Driven by a class from gate.js rather than :has(), which is only
   in engines from 2023 and would fail silently on anything older. */
.gate-plate.btn-hot input::placeholder { animation: ph-pulse 1.15s ease-in-out infinite; }
@keyframes ph-pulse {
  0%, 100% { color: rgba(127, 152, 174, .45); }
  50%      { color: var(--st); }
}
.plate-body button { font-family: var(--display); font-weight: 800; letter-spacing: .20em;
  font-size: clamp(16px, 2.3vh, 22px); padding: clamp(12px, 2.1vh, 20px) 16px; margin-top: 2px; }
/* The sign-in button, on the recipe from the splash page's PRESS TO ENTER:
   a translucent fill rather than a solid one (.16 to .28 alpha), a 1px border,
   a label that is a light tint of the colour, and ONE wide un-offset halo. The
   site paints those four per frame from its power level; here they come from
   --st, so the button is whatever the gate currently is.

   Hover moves the halo and the border alpha and NOTHING else. The colour is
   state, and hover is not a state -- a button that changes colour under the
   pointer is reporting something that did not happen. */
.plate-body button.primary {
  /* rgba(var(--st-rgb), a) rather than color-mix(): color-mix only reached
     browsers in 2023, and when it is unsupported the WHOLE declaration is
     dropped -- so the background fell back to pfd.css's orange .primary and
     the label to the plain hex before it, which is a 1.5:1 pale blue on
     orange. That is the unreadable button. rgba-with-a-variable has worked
     since custom properties shipped. */
  background: linear-gradient(180deg, rgba(var(--st-rgb), .20), rgba(var(--st-rgb), .34));
  background-color: rgba(var(--st-rgb), .26);
  color: var(--st-ink);
  border: 1px solid var(--st);
  border-radius: 2px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .85), 0 0 14px var(--st-glow);
  box-shadow: 0 0 22px -2px var(--st-soft), inset 0 0 26px -14px var(--st);
  position: relative; z-index: 1;          /* over the diagram, see below */
  transition: box-shadow .25s ease, border-color .25s ease, text-shadow .25s ease; }
/* Hover keeps the button's own blue. What changes is that the two LLP
   diagrams fade in either side of the label -- the label stays centred, the
   diagrams take the outer thirds, so nothing ever sits on top of the word.
   The halo lifts a little; the colour does not move. */
/* Hover must restate the background, not just the shadows. pfd.css has
     button.primary:hover:not(:disabled) { background: #FFB04D }
   and :not(:disabled) scores as a class, so that is (0,3,1) against the
   resting rule's (0,2,1) -- on hover it wins, and a hover rule that only sets
   shadows lets the orange straight through. Matching the same pseudo-classes
   here makes this (0,4,1): it wins on specificity, not on load order. */
.plate-body button.primary:hover,
.plate-body button.primary:hover:not(:disabled) {
  background: linear-gradient(180deg, rgba(var(--st-rgb), .20), rgba(var(--st-rgb), .34));
  background-color: rgba(var(--st-rgb), .26);
  border-color: var(--st);
  color: var(--st-ink);
  box-shadow: 0 0 40px 0 var(--st-glow), inset 0 0 30px -10px var(--st);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .85), 0 0 18px var(--st-glow); }

/* Disabled means "already sent" here, not "unavailable": the button is
   disabled for exactly the checking window in which the diagrams flash and
   hold. pfd.css dims every disabled button to .4, which would fade the button
   out underneath them. Still unclickable -- disabled is what stops a double
   submit -- it just stays lit, with a progress cursor to say why. */
.plate-body button.primary:disabled {
  opacity: 1; cursor: progress;
  background: linear-gradient(180deg, rgba(var(--st-rgb), .20), rgba(var(--st-rgb), .34));
  background-color: rgba(var(--st-rgb), .26);
  border-color: var(--st);
  color: var(--st-ink); }
.plate-body button.primary:active { box-shadow: 0 0 16px -4px var(--st-soft), inset 0 3px 10px rgba(0, 0, 0, .5); }
/* the side caps, straight off the splash button */
.btn-wrap { --cap: 9px; }
.plate-body button.primary::before, .plate-body button.primary::after {
  content: ""; position: absolute; top: -1px; bottom: -1px; width: var(--cap);
  border: 1px solid var(--st); pointer-events: none; }
.plate-body button.primary::before { left: -1px; border-right: 0; }
.plate-body button.primary::after { right: -1px; border-left: 0; }
.plate-body button.primary { position: relative; }
.plate-body button.primary:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.plate-body button.primary { transition: transform .22s cubic-bezier(.2, .9, .25, 1.2),
  box-shadow .22s ease, background .22s ease; will-change: transform; }

/* the wrapper sits between the button and .plate-body's grid, so the button
   would otherwise shrink to its text width -- it was a grid child before */
.btn-wrap { position: relative; display: block; width: 100%; }
.btn-wrap button.primary { display: block; width: 100%; }

/* fired: the blocks burn across the face (canvas) while a gradient ring opens
   out around it */
.btn-px { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;
  opacity: 0; z-index: 2; transition: opacity .09s linear; }
.btn-px.run { opacity: 1; }
.btn-wrap::after { content: ""; position: absolute; inset: -2px; pointer-events: none; opacity: 0;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(214, 255, 228, .55),
              rgba(52, 217, 106, .35), rgba(255, 255, 255, 0));
  filter: blur(9px); }
.btn-wrap.firing::after { animation: btn-ring .45s ease-out both; }
@keyframes btn-ring {
  0% { opacity: 0; transform: scale(.94); }
  18% { opacity: 1; transform: scale(1.02); }
  100% { opacity: 0; transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) { .btn-wrap.firing::after { animation: none; } }
/* quiescent blue -> typing orange -> verdict green or red. The left rule is
   3px of --st; so is the password field's border; so is the diagram. */
/* Quiescent was #3FC3FF, which sits on top of the plate's own ambient blue and
   so read as "no state" rather than as a state. #8BE4FF is light enough to
   separate from the furniture. The left rule also carries a real glow now --
   the state is supposed to be visible from the far side of the room. */
/* Quiescent is a deep blue in the University's family rather than a pale cyan.
   #0C234B itself is 1.2:1 against the plate -- it cannot read as a light
   source, it just reads as a slightly different dark. #2A5CC7 is 3.1:1, deep
   enough to be navy and bright enough to glow. The button's label is a 62%
   white tint of whatever --st is, which lands at 10.6:1 here, so the deeper
   colour costs nothing in legibility. */
.gate-plate { --st: #2A5CC7; --st-rgb: 42, 92, 199; --st-ink: #AEC1E9;
  --st-soft: rgba(42, 92, 199, .26); --st-glow: rgba(42, 92, 199, .80);
  border-left-color: var(--st);
  box-shadow: 0 0 0 1px rgba(30, 136, 216, .15), 0 24px 60px -24px rgba(30, 136, 216, .55); }

/* ---- (1) the state haze, from the LEFT edge only ------------------------
   A box-shadow cannot do this: even offset, its blur bleeds around all four
   corners. A strip sitting outside the left edge with a horizontal gradient
   goes one way and one way only. */
.gate-plate { position: relative; }
.gate-plate::before {
  content: ""; position: absolute; top: 0; bottom: 0; right: 100%;
  width: clamp(26px, 3.4vw, 64px); pointer-events: none;
  background: linear-gradient(270deg, var(--st-glow), transparent 78%);
  filter: blur(7px);
  transition: background .3s ease; }
.gate-plate.typing, .gate-plate.busy {
  --st: #FF9A1F; --st-rgb: 255, 154, 31; --st-ink: #FFD8A9;
  --st-soft: rgba(255, 154, 31, .26); --st-glow: rgba(255, 154, 31, .80); }
.gate-plate.open {
  --st: #2FD98A; --st-rgb: 47, 217, 138; --st-ink: #AFF0D2;
  --st-soft: rgba(47, 217, 138, .26); --st-glow: rgba(47, 217, 138, .80); }
.gate-plate.denied {
  --st: #E0323C; --st-rgb: 224, 50, 60; --st-ink: #F3B1B4;
  --st-soft: rgba(224, 50, 60, .26); --st-glow: rgba(224, 50, 60, .80); }
.gate-plate { transition: border-color .3s ease, box-shadow .3s ease; }
.plate-foot { border-top: 1px solid var(--rule); flex: 0 0 auto;
  padding: clamp(8px, 1.3vh, 14px) 26px; font-size: clamp(11px, 1.5vh, 13px); color: var(--ink-2); }
.gate-msg { border: 1px solid var(--rule-hi); padding: 10px 12px; font-size: 15px; }
.gate-msg.bad { border-color: var(--drop); background: rgba(227, 38, 46, .14); color: #FFD9D7; }
.gate-msg.warn { border-color: var(--ember); background: rgba(242, 201, 30, .10); }

/* the run/event block of an event display */
.gate-meta { position: absolute; left: 2.4vw; bottom: 20px; z-index: 1; font-size: 12px; line-height: 1.55; color: #cfd9e3; }
.gate-meta b { color: var(--accent); font-weight: normal; }
.gate-meta .note { color: var(--ink-2); font-size: 11px; margin-top: 4px; }

@media (max-width: 1279px) and (min-width: 900px) { .mark .mark-tag { display: none; } }
@media (max-width: 899px) {
  .gate-title { white-space: normal; font-size: clamp(24px, 7vw, 40px); }
  .gate-frame { height: auto; min-height: calc(100dvh - 28px); overflow: visible; }
  .gate-main { overflow: visible; }
  .gate-head { flex-direction: column; gap: 12px; padding: 16px 16px 14px; }
  .mark img { width: clamp(96px, 30vw, 150px); }
  .mark .mark-tag { display: none; }
  .gate-main { padding: 88vw 16px 0; justify-content: center; }   /* detector sits between title and plate */
  .gate-plate { width: 100%; }
  .gate-meta { position: static; padding: 18px 20px 0; }
  .gate-frame { padding-bottom: 24px; }
}
@media (prefers-reduced-motion: reduce) {
  .gate-plate.denied, .lock .glow, .lock .shackle, .crt-cursor, .crt-glass,
  .crt-lines, .crt-screen::after { animation: none; }
  .lock .shackle { transition: none; }
}

/* trigger outcome, placed at the CENTRE of the wheel by gate.js: the verdict
   belongs at the interaction point, not below the detector. */
.gate-alert { position: fixed; z-index: 2; transform: translate(-50%, -50%) scale(.94); opacity: 0;
  pointer-events: none; text-align: center; padding: 14px 26px 0; border: 1px solid transparent;
  background: rgba(1, 3, 6, .88); backdrop-filter: blur(3px); min-width: min(360px, 26vw);
  transition: opacity .25s ease, transform .25s ease; }
.gate-alert.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.gate-alert b { display: block; font-family: var(--display); font-weight: 900; font-size: clamp(18px, 2.2vw, 30px);
  letter-spacing: .16em; text-transform: uppercase; }
.gate-alert span { display: block; font-size: 12px; color: var(--ink-2); margin: 4px 0 12px; }
.gate-alert i { display: block; font-style: normal; font-family: var(--mono); font-size: 13px;
  letter-spacing: .22em; text-transform: uppercase; padding: 9px 0 10px;
  border-top: 1px solid currentColor; }
.gate-alert.ok { border-color: var(--win); box-shadow: 0 0 46px -6px var(--win-glow); }
.gate-alert.ok b { color: var(--win); text-shadow: 0 0 18px var(--win-glow); }
.gate-alert.ok i { color: #8BF3BC; background: rgba(67, 224, 140, .15); }
.gate-alert.bad { border-color: var(--drop); box-shadow: 0 0 44px -6px rgba(227, 38, 46, .7); }
.gate-alert.bad b { color: #FF5A55; text-shadow: 0 0 18px rgba(227, 38, 46, .7); }
.gate-alert.bad i { color: #FF8A84; background: rgba(227, 38, 46, .14); }

/* detector rumble on a rejected trigger */
#det.rumble { animation: rumble .6s cubic-bezier(.36, .07, .19, .97) both; }
@keyframes rumble {
  10%, 90% { transform: translate(-2px, 1px); } 20%, 80% { transform: translate(4px, -2px); }
  30%, 50%, 70% { transform: translate(-7px, 3px) rotate(-.3deg); } 40%, 60% { transform: translate(7px, -3px) rotate(.3deg); }
}
@media (prefers-reduced-motion: reduce) { .gate-alert { transition: none; } #det.rumble { animation: none; } }
