/* Neon tubes: glass, paint, electrodes, gas, and the physics of striking and dying.
   Shared by the sign (web/) and the control room (admin/tubes.css is a copy made by scripts/build_page.py).
   Every size derives from the tube's own unit: --u is px per path unit, set by the page that places it. */

:root {
  --paint: #1f1c1a;          /* blockout paint and electrode boots */
  --cast: rgba(43, 33, 27, .40);
  --glass: rgba(238, 233, 226, .20);
  --glass-hi: rgba(255, 255, 255, .50);
  --red: #ff3d1c;            /* neon gas, the ring */
  --red-core: #ffe2d4;
  --red-halo: #ff2a08;
  --white: #ffedd9;          /* warm-white phosphor, the words */
  --white-core: #fffcf7;
  --white-halo: #ffbf86;
}

.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.tube {
  position: relative;
  display: block;
  width: calc(var(--u) * (var(--w) - 2 * var(--pad)));
  height: calc(var(--u) * (var(--h) - 2 * var(--pad)));
}

.tube > svg,
.tube > .wash {
  position: absolute;
  left: calc(var(--u) * var(--pad) * -1);
  top: calc(var(--u) * var(--pad) * -1);
  width: calc(var(--u) * var(--w));
  height: calc(var(--u) * var(--h));
  overflow: visible;
  pointer-events: none;
}

.tube use, .tube path { fill: none; stroke-linecap: round; stroke-linejoin: round; }

.t-shadow {
  stroke: var(--cast);
  stroke-width: calc(var(--t) * 1.05px);
  transform: translate(calc(var(--t) * .25px), calc(var(--t) * 1.1px));
  transition: transform .12s ease-out;
}
.ring .t-shadow { filter: url(#ring-shadow); }
.word .t-shadow { filter: url(#word-shadow); }

.t-body { stroke: var(--glass); stroke-width: calc(var(--t) * 1px); }
.t-blockout { fill: none; stroke: var(--paint); stroke-width: calc(var(--t) * .98px); stroke-linecap: round; opacity: .85; }
.t-hi {
  stroke: var(--glass-hi);
  stroke-width: calc(var(--t) * .2px);
  transform: translate(calc(var(--t) * -.14px), calc(var(--t) * -.24px));
}
.t-boot { fill: var(--paint); stroke: rgba(255, 255, 255, .14); stroke-width: 1; }
.t-standoff { fill: rgba(255, 255, 255, .08); stroke: rgba(255, 255, 255, .26); stroke-width: 1; }

/* the words hang as faint glass until they strike: findable, not legible at a glance */
.word .t-body { stroke: rgba(238, 233, 226, .1); }
.word .t-hi { stroke: rgba(255, 255, 255, .24); }
.word .t-shadow { stroke: rgba(43, 33, 27, .2); }
.word .t-boot { fill: rgba(31, 28, 26, .55); stroke: none; }
.word .t-blockout { opacity: .4; }
.word .g-halo2 { opacity: .32; }
.word .g-halo1 { opacity: .7; }

/* the gas: drawn additively over the wall */
.gas, .wash {
  mix-blend-mode: screen;
  opacity: 0;
  will-change: opacity;
}
@supports (mix-blend-mode: plus-lighter) {
  .gas, .wash { mix-blend-mode: plus-lighter; }
}

.ring { --gas: var(--red); --core: var(--red-core); --halo: var(--red-halo); }
.word { --gas: var(--white); --core: var(--white-core); --halo: var(--white-halo); }
.word.red { --gas: var(--red); --core: var(--red-core); --halo: var(--red-halo); }

.g-halo2 { stroke: var(--halo); stroke-width: calc(var(--t) * 2.8px); opacity: .5; }
.g-halo1 { stroke: var(--halo); stroke-width: calc(var(--t) * 1.5px); opacity: .85; }
.g-tube { stroke: var(--gas); stroke-width: calc(var(--t) * 1px); }
.g-core { stroke: var(--core); stroke-width: calc(var(--t) * .36px); }
.ring .g-halo2 { filter: url(#ring-halo-l); }
.ring .g-halo1 { filter: url(#ring-halo-s); }
.word .g-halo2 { filter: url(#word-halo-l); }
.word .g-halo1 { filter: url(#word-halo-s); }

/* light thrown on the plaster: the gas owns the wall, not a halo sticker */
.wash {
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--halo) 30%, transparent) 0%,
    color-mix(in srgb, var(--halo) 22%, transparent) 22%,
    color-mix(in srgb, var(--halo) 12%, transparent) 45%,
    color-mix(in srgb, var(--halo) 5%, transparent) 68%,
    color-mix(in srgb, var(--halo) 1%, transparent) 88%,
    transparent 100%);
}
.ring > .wash { inset: auto; left: -95%; top: -95%; width: 290%; height: 290%; }
/* struck words throw warm light on the plaster around them, not just their own halo */
.word > .wash {
  left: -45%; top: -170%; width: 190%; height: 440%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--halo) var(--wash-0, 44%), transparent) 0%,
    color-mix(in srgb, var(--halo) var(--wash-1, 30%), transparent) 25%,
    color-mix(in srgb, var(--halo) var(--wash-2, 15%), transparent) 50%,
    color-mix(in srgb, var(--halo) var(--wash-3, 5%), transparent) 75%,
    transparent 100%);
}

/* ---------- neon physics ---------- */

.tube[data-state="lit"] :is(.gas, .wash) {
  opacity: 1;
  animation: strike 1.15s linear both, hum 6.7s 1.15s linear infinite;
}
.tube[data-state="lit"][data-current] :is(.gas, .wash) {
  animation: strike 1.15s linear both, warm 3.4s 1.15s ease-in-out infinite;
}
.tube[data-state="cut"] :is(.gas, .wash) { animation: cut .22s ease-out both; }
.tube[data-state="down"] :is(.gas, .wash) { animation: powerdown 1.3s ease-out both; }
.tube[data-state="sputter"] :is(.gas, .wash) { animation: sputter .5s linear both; }

/* a cold tube striking: two weak catches, then it holds (never more than 3 flashes a second) */
@keyframes strike {
  0%   { opacity: 0; }
  6%   { opacity: .5; }
  12%  { opacity: .04; }
  38%  { opacity: .66; }
  45%  { opacity: .12; }
  72%  { opacity: .9; }
  100% { opacity: 1; }
}
@keyframes hum {
  0%, 100% { opacity: 1; }
  31% { opacity: .965; }
  33% { opacity: 1; }
  58% { opacity: .985; }
  79% { opacity: .95; }
  81% { opacity: 1; }
}
/* the stage in progress breathes unevenly, like a tube still warming */
@keyframes warm {
  0%, 100% { opacity: 1; }
  40% { opacity: .74; }
  58% { opacity: .9; }
  70% { opacity: .78; }
}
@keyframes cut {
  0% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes powerdown {
  0%   { opacity: 1; }
  7%   { opacity: .3; }
  13%  { opacity: .72; }
  100% { opacity: 0; }
}
@keyframes sputter {
  0%   { opacity: 0; }
  16%  { opacity: .2; }
  28%  { opacity: 0; }
  52%  { opacity: .1; }
  64%, 100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .tube[data-state="lit"] :is(.gas, .wash),
  .tube[data-state="lit"][data-current] :is(.gas, .wash) { animation: cut .3s ease-out reverse both; }
  .tube[data-state="down"] :is(.gas, .wash) { animation: cut .5s ease-out both; }
  .tube[data-state="sputter"] :is(.gas, .wash) { animation: none; }
}
