/* ============================================================
   enigma.css — visual systems for the Enigma deck
   (deck.css provides the chrome and shared tokens)
   ============================================================ */

/* ============================================================
   1 · COVER
   ============================================================ */
.ecover{ position:absolute; inset:0; overflow:hidden;
  display:grid; place-items:center; padding:clamp(14px,3vh,30px) clamp(16px,5vw,44px); }

/* text and machine sit side by side, so the photo is never cropped by the
   stage edge nor laid over the paragraph */
.ecover-grid{
  position:relative; z-index:1; width:100%; max-width:1220px;
  display:grid; grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:clamp(18px,3.4vw,56px); align-items:center;
}

/* ---------- blending the cover into the page ----------

   The cover is `position:absolute; inset:0` - the same full-bleed box as the frame
   itself - so every layer it paints runs to the frame's border. Anything still
   painting AT that border is a rectangle laid on the page, which is the one thing
   the rest of this file works to avoid: the deck's own backdrop went through the
   same problem and is fixed by feathering (`--bg-fade-h`/`--bg-fade-v`, declared in
   deck.css and intersected with `mask-composite:intersect`).

   So the cover's three full-bleed layers get that identical treatment. Alpha is
   then zero on all four sides by construction rather than by each gradient happening
   to fade in time, which means a later edit to any one of them cannot quietly
   reintroduce a visible edge. The gradients still have to be softened on their own
   terms too - the mask is a guarantee, not a substitute for the values being right. */

.ecover-art{
  position:absolute; inset:0; z-index:0; pointer-events:none;
  -webkit-mask-image:var(--bg-fade-h), var(--bg-fade-v);
  mask-image:var(--bg-fade-h), var(--bg-fade-v);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}
.ecover-art svg{ display:block; width:100%; height:100%; }

/* The two accent blooms. Their centres were at `2% 68%` and `99% 34%` - hard against
   the left and right borders, which is what made the cover read as a lit rectangle
   rather than as light in a field. Pulled in to roughly a sixth and five sixths of
   the width, they now sit clearly inside the frame as accents in their own right,
   and the mask guarantees neither can reach an edge. Radii tightened a little for
   the same reason: at 58-78% they were wide enough to span the frame.

   Every end stop is the wash's own colour at zero alpha - `rgba(255,179,71,0)`, never
   the `transparent` keyword - and each wash carries three intermediate stops. Both
   are what separate a gradient that looks like light from one that looks like a
   gradient: fading toward the keyword `transparent` resolves to `rgba(0,0,0,0)`, so
   the interpolation runs the hue down through black on the way to zero alpha and the
   outer third of every wash turns muddy grey, which is the single most common reason
   a coloured glow reads as cheap. Naming the colour keeps the hue all the way out.
   The intermediate stops do the same job as the mask ramps in deck.css: a straight
   two-stop ramp changes slope abruptly where it meets the flat middle, and that
   corner shows as a faint ring. */
.ecover-glow{ position:absolute; inset:0; }
.ecover-glow-a{ background:
  radial-gradient(42% 58% at 17% 66%,
    rgba(255,179,71,.20) 0%,
    rgba(255,179,71,.15) 28%,
    rgba(255,179,71,.07) 48%,
    rgba(255,179,71,0)   70%),
  radial-gradient(30% 42% at 27% 24%,
    rgba(255,206,120,.10) 0%,
    rgba(255,206,120,.07) 32%,
    rgba(255,206,120,.03) 54%,
    rgba(255,206,120,0)   74%); }
.ecover-glow-b{ background:
  radial-gradient(40% 58% at 83% 32%,
    rgba(63,224,208,.19) 0%,
    rgba(63,224,208,.14) 28%,
    rgba(63,224,208,.07) 48%,
    rgba(63,224,208,0)   70%),
  radial-gradient(32% 44% at 73% 76%,
    rgba(90,169,255,.14) 0%,
    rgba(90,169,255,.10) 30%,
    rgba(90,169,255,.05) 52%,
    rgba(90,169,255,0)   74%); }

/* --- the machine itself ---
   A cut-out photograph beside the title. The stacked layers give the
   accent colours the eye needs around a photo: a warm halo bleeding
   outward, a thin glowing border that follows the photo's alpha
   silhouette, and corner ticks in the era colours. */
.emachine{
  position:relative; margin:0; justify-self:center; width:100%;
  max-width:min(100%,560px);
  animation:emachineFloat 24s ease-in-out infinite;
}
@keyframes emachineFloat{
  0%,100%{ transform:translate3d(0,0,0) rotate(0deg) }
  50%    { transform:translate3d(0,-14px,0) rotate(.35deg) }
}
.emachine-img{
  position:relative; z-index:2; display:block; width:100%; height:auto;
  filter:drop-shadow(0 30px 60px rgba(0,0,0,.85))
         drop-shadow(0 0 44px rgba(255,179,71,.22));
}

/* warm halo: amber behind the wood, cyan cooling off toward the base

   Three things used to carry this glow out to the frame's border on the right, above
   and below, which is where the cover stopped reading as light in a field and started
   reading as a lit panel with a photo in it:

   * The box bled `-14%` left and right of a ~550px photo, which put its right edge
     about 10px from the frame border at this width. It also inherited the cover's
     full-bleed habit without inheriting the cover's mask: `.emachine` sits inside
     `.ecover-grid`, which is not one of the three feathered layers.
   * `filter: blur(30px)` spread what little paint reached that edge another 30px out.
   * `haloPulse` animated `transform: scale(1.05)`, growing the box 5% *every 11
     seconds* and pushing it past the border on the peak of every cycle. A static
     screenshot would have caught this only half the time.

   The bleed is pulled in to -8%, the pulse animates opacity alone, and the box is
   masked on both axes so it reaches zero at its own edges whatever happens above.
   Dropping the scale costs nothing visually - the halo is a soft radial wash, and
   breathing opacity reads the same as breathing size - and it removes the only thing
   that could push paint outside a box whose position is now fixed.

   The mask's plateau is deliberately wide (15%-85%) rather than the cover's 26%-74%:
   this box is only a little larger than the photo, so a tight plateau would eat the
   glow from behind the machine's own edges and leave it looking pasted on.

   The *bottom* is weighted differently from the other three sides, and deliberately
   so. The photo is 552px tall in a 682px frame, so it finishes only 65px above the
   frame's bottom edge: the halo's box bottom was landing ~10px above that edge, and
   with `blur(30px)` on top the glow terminated in a band that read as a line across
   the slide rather than as light running out. A glow that ends needs to end *over
   distance*, so the bottom ramp now starts at 58% of the box instead of 85% - a
   ~250px dissolve - and the bleed is pulled in to -2% so the box stops at the
   photo's own base rather than reaching for the frame edge. The other three sides
   keep the tight 15%-85% ramp and are unchanged. */
.emachine-halo{
  position:absolute; z-index:0; left:-8%; right:-8%; top:-8%; bottom:-2%;
  /* Every wash fades to its own colour at zero alpha rather than to the `transparent`
     keyword, and every wash is sampled at four points instead of two. See the note by
     `.ecover-glow` above: fading toward `transparent` drags the hue through black and
     greys out the outer third, which is most of what made this halo look like a
     smudge rather than light. The three washes also overlap the way real light does -
     amber high and warm, cyan low and cooling off toward the base, violet filling
     between them - so the middle of the machine never sits on a flat single colour. */
  background:
    radial-gradient(58% 48% at 46% 22%,
      rgba(255,179,71,.34) 0%,
      rgba(255,179,71,.25) 24%,
      rgba(255,179,71,.12) 48%,
      rgba(255,179,71,0)   70%),
    radial-gradient(54% 44% at 40% 74%,
      rgba(63,224,208,.26) 0%,
      rgba(63,224,208,.19) 26%,
      rgba(63,224,208,.09) 52%,
      rgba(63,224,208,0)   72%),
    radial-gradient(70% 60% at 50% 50%,
      rgba(167,139,250,.16) 0%,
      rgba(167,139,250,.12) 26%,
      rgba(167,139,250,.06) 52%,
      rgba(167,139,250,0)   74%);
  filter:blur(30px);
  /* Sides use the shared smooth ramp; the bottom uses the bottom-weighted one, so the
     glow keeps dissolving over ~250px instead of ending in a band. */
  -webkit-mask-image:var(--bg-fade-h), var(--halo-fade-v);
  mask-image:var(--bg-fade-h), var(--halo-fade-v);
  -webkit-mask-composite:source-in; mask-composite:intersect;
  animation:haloPulse 11s var(--ease) infinite;
}
@keyframes haloPulse{
  0%,100%{ opacity:.72 }
  50%    { opacity:1 }
}

/* the accent border: follows the photo's alpha silhouette using a
   drop-shadow chain, so it hugs the machine instead of drawing a box */
.emachine-frame{
  position:absolute; z-index:3; inset:0; pointer-events:none;
  filter:
    drop-shadow(0 0 1px rgba(255,208,138,.95))
    drop-shadow(0 0 7px rgba(255,179,71,.55))
    drop-shadow(0 0 18px rgba(63,224,208,.30));
}

/* corner ticks, in the era colours */
.emachine::before, .emachine::after{
  content:""; position:absolute; z-index:4; width:18px; height:18px;
  border:1.5px solid var(--amber); opacity:.7; pointer-events:none;
}
.emachine::before{ left:-10px; top:-10px;    border-right:0; border-bottom:0; }
.emachine::after{  right:-10px; bottom:-10px; border-left:0;  border-top:0;
  border-color:var(--cyan); }

.emachine-cap{
  position:absolute; z-index:5; left:4%; right:4%; bottom:-18px;
  display:flex; flex-direction:column; gap:3px; text-align:center;
  font-family:var(--mono); line-height:1.45;
}
.emachine-cap b{
  font-size:clamp(9px,.86vw,11.5px); letter-spacing:.24em; text-transform:uppercase;
  color:var(--amber);
}
.emachine-cap span{
  font-size:clamp(8px,.72vw,10px); letter-spacing:.06em; color:var(--txt-3);
}

/* Darken the middle so the title always has contrast, whatever is behind it.
   Sampled at five points and easing out to nothing rather than stopping at `.32` and
   then jumping to the keyword, which is what turned this from a soft vignette into a
   visible dark disc with an edge. The centre is also a shade lighter than the `.80`
   it was: the title is white on near-black and does not need that much ink behind it,
   and a heavy centre is the least refined thing on the slide. */
.ecover-scrim{ position:absolute; inset:0;
  background:radial-gradient(60% 54% at 50% 50%,
    rgba(2,4,8,.74) 0%,
    rgba(2,4,8,.64) 26%,
    rgba(2,4,8,.46) 48%,
    rgba(2,4,8,.24) 68%,
    rgba(2,4,8,.07) 82%,
    rgba(2,4,8,0)   94%);
  -webkit-mask-image:var(--bg-fade-h), var(--bg-fade-v);
  mask-image:var(--bg-fade-h), var(--bg-fade-v);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* The grounding band under the machine.

   This was the one genuinely hard edge on the cover: `linear-gradient(to top,
   rgba(2,4,8,.92), transparent 64%)` put its *darkest* stop at the bottom of a box
   whose bottom is the bottom of the frame, so 28vh of near-black met the page at
   full strength and stopped dead. The band carries a wave drawn into it by the cover
   slide's own `init()`, so this gradient is the shadow *under* that drawing rather
   than the whole of it - but it was the part with the edge.

   Now the gradient starts transparent at the frame's bottom edge, peaks a little way
   up where the machine's base needs something to sit on, and falls away again at the
   top, so it is a shadow in the field rather than a band painted over the page. The
   mask then guarantees it regardless.

   Both ends are transparent on purpose. The top used to stop at `.42`, so the wash
   went from 42% opaque to nothing the instant it crossed the band's upper edge - a
   crisp horizontal line straight across the slide, and precisely the "line below the
   image where the glow ends" that was reported. A gradient has to reach zero at both
   of its ends, or it has two edges instead of none. */

.ecover-wave{
  position:absolute; left:0; right:0; bottom:0; height:min(28vh,240px);
  -webkit-mask-image:var(--bg-fade-h), var(--bg-fade-v);
  mask-image:var(--bg-fade-h), var(--bg-fade-v);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

.ecover-wave{
  position:absolute; left:0; right:0; bottom:0; height:min(28vh,240px);
  -webkit-mask-image:var(--bg-fade-h), var(--bg-fade-v);
  mask-image:var(--bg-fade-h), var(--bg-fade-v);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}
.ecover-wave::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(to top,
    rgba(2,4,8,0)   0%,
    rgba(2,4,8,.07) 14%,
    rgba(2,4,8,.18) 28%,
    rgba(2,4,8,.32) 44%,
    rgba(2,4,8,.46) 60%,
    rgba(2,4,8,.55) 74%,
    rgba(2,4,8,.50) 86%,
    rgba(2,4,8,.32) 94%,
    rgba(2,4,8,0)   100%); }

.ecover-body{ position:relative; z-index:2; display:flex; flex-direction:column; align-items:flex-start; }

.ecover-badge{
  display:inline-flex; align-items:center; gap:9px;
  padding:7px 16px 7px 13px; border-radius:999px;
  border:1px solid rgba(120,255,190,.24); background:rgba(9,20,18,.55);
  font-family:var(--mono); font-size:clamp(8px,.72vw,10px);
  letter-spacing:.26em; text-transform:uppercase; color:#8ef0c0; backdrop-filter:blur(6px);
}
.ecover-badge i{ width:7px; height:7px; border-radius:50%; background:#4ade80;
  box-shadow:0 0 10px 2px rgba(74,222,128,.7); }

.ecover h1{
  margin:clamp(14px,2.4vh,26px) 0 0; font-weight:800;
  letter-spacing:-.045em; line-height:.92; white-space:nowrap;
  font-size:clamp(38px,8.6vw,116px);
}
.ecover h1 .w{ color:#f4f7fb; }
.ecover h1 .ar{ display:inline-block; margin:0 .08em; color:var(--amber); font-weight:400; font-size:.66em; translate:0 -.08em; }
.ecover h1 .g{ background:linear-gradient(96deg,#ffb347 0%,#ffd28a 26%,#3fe0d0 66%,#5aa9ff 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent; }

/* An accent rule under the title, on the `h1` rather than as another element, so it
   needs no markup and sits at exactly the column's left edge with the wordmark and
   the paragraphs below it. It fades out rather than stopping square: a hard-ended
   2px bar under a display-weight wordmark reads as a divider, and a rule that
   dissolves into the dark is a highlight. Width is a clamp because the title scales
   from 38px to 116px and a fixed rule would look pinned at one end and lost at the
   other.

   The centred tier below sets `margin-inline:auto`, because the cover stops being
   left-aligned on one column and a left-hanging rule would break that centre line. */
.ecover h1::after{
  content:""; display:block;
  width:clamp(96px,58%,260px); height:2px; margin:clamp(12px,2vh,20px) 0 0;
  border-radius:2px;
  background:linear-gradient(90deg, var(--amber) 0%, var(--cyan) 58%, rgba(63,224,208,0) 100%);
}

/* three paragraphs read as one block: the first carries the gap from the
   title, the rest share an even gap between them.
   The horizontal margin is 0, not auto — auto would centre each paragraph
   inside the column and break the shared left edge with the title. The
   centred tier below puts the auto margins back. */
.ecover-sub, .ecover-sub2{
  font-size:clamp(12.5px,1.24vw,16px);
  max-width:60ch; margin:clamp(12px,1.9vh,20px) 0 0; line-height:1.7;
}
.ecover-sub{ color:var(--txt-2); }
.ecover-sub2{ color:var(--txt-3); }
.ecover-sub em{ font-style:normal; font-weight:600; color:#7dd3fc; }
.ecover-body > .ecover-sub:first-of-type{ margin-top:clamp(18px,2.8vh,30px); }

/* The cover's calls to action: "Learn How It Works" and "Try the Enigma Machine".

   These replace a line of instructions ("↓ press arrow keys to begin ↓") and a row of
   keyboard hints (`<kbd>←</kbd> <kbd>→</kbd> navigate  O all slides  ? help`), so the
   `.ecover-cta`, `.ecover-keys` and `.ecover-keybar` rules and the `pulseCta`
   keyframes they needed are gone rather than left behind.

   The two are styled by `.btn` (filled) and `.btn.ghost` from deck.css, which is why
   there is no bespoke button styling here: the deck already had a button and a
   ghost-button, and these are the same two controls the rest of the deck uses. Only
   the row they sit in is new.

   `flex-wrap` so the pair reflows to one-per-line rather than overflowing on a
   narrow frame, and the buttons are centred in the stacked tiers below - the cover is
   centre-aligned from 1024px down (see `.ecover-body` there), and a left-hanging pair
   of buttons would be the one thing refusing to line up with everything else. */
.ecover-actions{
  display:flex; flex-wrap:wrap; gap:10px;
  margin-top:clamp(18px,2.8vh,30px);
}

/* ============================================================
   2 · SHARED PIECES
   ============================================================ */

/* a numbered "why this matters" tile */
.why{
  position:relative; padding:16px 16px 18px 54px; border-radius:var(--r);
  border:1px solid var(--line); background:linear-gradient(168deg,var(--panel-2),var(--panel));
}
.why::before{
  content:attr(data-n); position:absolute; left:15px; top:15px;
  width:25px; height:25px; border-radius:50%; display:grid; place-items:center;
  font-family:var(--mono); font-size:11px; font-weight:600; color:#070b13;
  background:var(--accent);
}
.why h4{ margin:0 0 6px; font-size:13.5px; font-weight:650; color:var(--txt); line-height:1.3; }
.why p{ margin:0; font-size:12px; color:var(--txt-3); line-height:1.55; }
.why p b{ color:var(--txt-2); font-weight:600; }

/* plain-language definition block */
.plain{
  border-left:3px solid var(--accent); padding:4px 0 4px 16px;
  font-size:14.5px; color:var(--txt-2); line-height:1.6;
}
.plain b{ color:var(--txt); font-weight:650; }

/* a simple two-column "from / to" diagram */
.pair{ display:grid; grid-template-columns:1fr auto 1fr; gap:10px; align-items:center; }
.pair .side{ text-align:center; padding:10px 6px; border-radius:var(--r-s); background:rgba(255,255,255,.03); border:1px solid var(--line); }
.pair .side .k{ font-family:var(--mono); font-size:9.5px; letter-spacing:.18em; text-transform:uppercase; color:var(--txt-3); }
.pair .side .v{ font-family:var(--mono); font-size:clamp(20px,3vw,32px); font-weight:700; margin-top:5px; line-height:1; }
.pair .side.in .v{ color:var(--amber); } .pair .side.out .v{ color:var(--cyan); }
.pair .arw{ font-family:var(--mono); font-size:20px; color:var(--txt-3); }

/* alphabet strip showing a substitution */
.astrip{ display:flex; gap:2px; flex-wrap:wrap; justify-content:center; }
.astrip i{
  width:clamp(19px,2.3vw,26px); text-align:center; font-style:normal;
  font-family:var(--mono); font-size:clamp(9px,1.02vw,12px);
  padding:6px 0; border-radius:5px; background:rgba(255,255,255,.035);
  border:1px solid var(--line); color:var(--txt-2); transition:.3s var(--ease);
}
.astrip i.in{ background:rgba(255,179,71,.16); border-color:rgba(255,179,71,.5); color:var(--amber); }
.astrip i.out{ background:rgba(63,224,208,.16); border-color:rgba(63,224,208,.5); color:var(--cyan); }
.astrip i.dead{ opacity:.22; text-decoration:line-through; }

/* ============================================================
   3 · MACHINE ANATOMY DIAGRAM
   ============================================================ */
/* The full machine, drawn in CSS so it stays crisp. Parts are laid
   out in the same vertical order as the electrical path, which is
   the whole point: top to bottom reads like the current's journey. */
.mach{
  position:relative; width:100%; max-width:560px; margin:0 auto;
  border-radius:20px; padding:clamp(10px,1.4vw,16px);
  background:linear-gradient(168deg,#1c1608,#141a24 42%,#0d121b);
  border:1px solid #33405a; box-shadow:0 30px 70px -34px rgba(0,0,0,.95), inset 0 1px 0 rgba(255,255,255,.05);
}
.mach-part{
  position:relative; border-radius:12px; padding:9px 10px; margin-bottom:8px;
  border:1px solid rgba(255,255,255,.09); background:rgba(255,255,255,.028);
  transition:.3s var(--ease);
}
.mach-part:last-child{ margin-bottom:0; }
.mach-part .pl{
  font-family:var(--mono); font-size:8.5px; letter-spacing:.2em;
  text-transform:uppercase; color:#8a7a58; display:block; margin-bottom:6px;
}
.mach-part .pn{ font-family:var(--mono); font-size:10.5px; color:var(--txt-3); letter-spacing:.04em; }
.mach-part.lit{ border-color:var(--accent); background:color-mix(in srgb,var(--accent) 13%,transparent);
  box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 35%,transparent), 0 0 34px -8px var(--accent); }
.mach-part.lit .pl{ color:var(--accent); }

/* the wire between two parts, lit when the current is there */
.mach-wire{ height:16px; position:relative; }
.mach-wire::before{
  content:""; position:absolute; left:50%; top:0; bottom:0; width:2px;
  translate:-50% 0; background:rgba(255,255,255,.12); border-radius:2px;
}
.mach-wire::after{
  content:""; position:absolute; left:50%; top:50%; width:8px; height:8px;
  translate:-50% -50%; border-radius:50%; background:rgba(255,255,255,.12);
  transition:.2s var(--ease);
}
.mach-wire.lit::before{ background:var(--cyan); box-shadow:0 0 12px var(--cyan); }
.mach-wire.lit::after{ background:var(--cyan); box-shadow:0 0 16px 3px rgba(63,224,208,.7); }

/* three rotors drawn side by side inside the machine */
.mach-rotors{ display:flex; gap:7px; }
.mach-rotor{
  flex:1; text-align:center; padding:8px 3px; border-radius:9px;
  background:linear-gradient(180deg,rgba(255,179,71,.11),rgba(255,255,255,.02));
  border:1px solid rgba(255,179,71,.22); min-width:0;
}
.mach-rotor .rl{ font-family:var(--mono); font-size:8px; letter-spacing:.14em; color:#8a7a58; text-transform:uppercase; }
.mach-rotor .rv{ font-family:var(--mono); font-size:clamp(15px,2.1vw,22px); font-weight:700; color:var(--amber); line-height:1.15; }
.mach-rotor.on{ border-color:var(--amber); box-shadow:0 0 20px -6px var(--amber); }

/* keyboard / lampfield grids inside the machine */
.mach-kb{ display:grid; gap:3px; }
.mach-kb .mrow{ display:flex; gap:3px; justify-content:center; }
.mach-kb .k{
  flex:1; text-align:center; border-radius:4px; padding:4px 0;
  font-family:var(--mono); font-size:8.5px; color:var(--txt-3);
  background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.07);
  transition:.16s var(--ease);
}
.mach-kb .k.hot{ background:var(--amber); color:#070b13; border-color:var(--amber); box-shadow:0 0 14px rgba(255,179,71,.7); }
.mach-kb .k.lit{ background:var(--cyan); color:#070b13; border-color:var(--cyan); box-shadow:0 0 14px rgba(63,224,208,.7); }
.mach-kb .k.plugd{ border-color:var(--amber); color:var(--amber); }

/* plugboard columns, connected by cables */
.plugboard{ position:relative; }
.pbcab{ position:absolute; inset:0; pointer-events:none; overflow:visible; }
.pbcab path{ fill:none; stroke:var(--amber); stroke-width:1.4; opacity:.85; }
.pbcab path.dim{ stroke:var(--txt-3); opacity:.3; }

/* ============================================================
   4 · ROTOR / WIRING DIAGRAM
   ============================================================ */
/* A rotor as a ring of contacts: contact i is the point where the
   letter in that position touches the alphabet. */
.rotor-ring{ display:grid; place-items:center; }
.rotor-ring svg{ overflow:visible; }
.rotor-ring .contact{ transition:.25s var(--ease); }
.rotor-ring .contact.in{ fill:var(--amber); }
.rotor-ring .contact.out{ fill:var(--cyan); }
.rotor-ring .contact.mid{ fill:var(--amber); }
.rotor-ring .chord{ stroke:var(--amber); stroke-width:1.5; fill:none; opacity:0; transition:.35s var(--ease); }
.rotor-ring .chord.on{ opacity:.9; }

/* three big position wheels, the odometer */
.wheels{ display:flex; gap:clamp(8px,1.4vw,18px); justify-content:center; }
.wheel{ flex:1; max-width:130px; text-align:center; min-width:0; }
.wheel .wl{ font-family:var(--mono); font-size:8.5px; letter-spacing:.18em; text-transform:uppercase; color:#8a7a58; margin-bottom:5px; }
.wheel .wbox{
  border-radius:11px; padding:11px 6px 9px; position:relative; cursor:pointer;
  background:linear-gradient(180deg,#241a0a,#150f06);
  border:1px solid #3a2f18; box-shadow:inset 0 4px 12px rgba(0,0,0,.8);
}
.wheel .wbox .cur{ font-family:var(--mono); font-size:clamp(26px,4vw,42px); font-weight:700;
  color:var(--amber); line-height:1; text-shadow:0 0 20px rgba(255,179,71,.55); }
.wheel .wbox .ticks{ position:absolute; inset:0; display:grid; grid-template-columns:repeat(13,1fr); pointer-events:none; }
.wheel .wbox .ticks i{ border-right:1px solid rgba(255,255,255,.05); }
.wheel .wbox .notch{
  position:absolute; right:-1px; top:5px; bottom:5px; width:4px; border-radius:3px;
  background:var(--red); opacity:.14; transition:.25s var(--ease);
}
.wheel .wbox .notch.at{ opacity:1; box-shadow:0 0 14px var(--red); }
.wheel .wbox.moved{ animation:wheelMove .34s var(--ease); }
@keyframes wheelMove{
  0%{ transform:translateY(-7px); filter:brightness(1.9) }
  100%{ transform:none; filter:none }
}
.wheel .wname{ font-family:var(--mono); font-size:10px; color:var(--txt-3); margin-top:6px; }
.wheel .wname b{ color:var(--txt-2); }

/* the plugboard, standalone */
.stecker{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:clamp(6px,.9vw,12px); }
.stecker .scol{ display:grid; gap:3px; }
.stecker .sk{
  display:flex; align-items:center; gap:7px; padding:3px 7px; border-radius:5px; cursor:pointer;
  font-family:var(--mono); font-size:clamp(9px,1vw,11.5px);
  background:rgba(255,255,255,.035); border:1px solid var(--line);
  color:var(--txt-3); transition:.16s var(--ease); text-align:left;
}
.stecker .sk:hover{ border-color:var(--amber); color:var(--amber); }
.stecker .sk.linked{ background:linear-gradient(90deg,rgba(255,179,71,.24),rgba(255,179,71,.10)); border-color:var(--amber); color:var(--amber); font-weight:600; }
.stecker .sk.pend{ border-color:var(--cyan); color:var(--cyan); border-style:dashed; }
.stecker .sk .dot{ width:5px; height:5px; border-radius:50%; background:currentColor; opacity:.55; flex:none; }
.stecker .sk.linked .dot{ opacity:1; box-shadow:0 0 7px currentColor; }

/* signal path strip */
.sigpath{ display:flex; align-items:center; gap:2px; flex-wrap:wrap; justify-content:center; font-family:var(--mono); font-size:9px; }
.sigpath .node{
  min-width:34px; text-align:center; padding:5px 3px; border-radius:6px;
  background:rgba(255,255,255,.04); border:1px solid var(--line); color:var(--txt-3);
  transition:.2s var(--ease);
}
.sigpath .node span{ display:block; font-size:7.5px; letter-spacing:.1em; opacity:.7; }
.sigpath .node b{ font-size:12px; color:var(--txt-2); }
.sigpath .node.act{ background:var(--cyan); border-color:var(--cyan); color:#070b13; box-shadow:0 0 16px rgba(63,224,208,.65); }
.sigpath .node.act b{ color:#070b13; }
.sigpath .arw{ color:var(--txt-3); opacity:.45; font-size:9.5px; }

/* the layered alphabet diagram: each layer shown as a row of letters */
.layer{ margin-bottom:7px; }
.layer .ll{ font-family:var(--mono); font-size:8.5px; letter-spacing:.18em; text-transform:uppercase; color:var(--txt-3); margin-bottom:4px; }
.layer .lr{ display:flex; gap:2px; flex-wrap:nowrap; overflow:hidden; }
.layer .lr i{
  flex:1 1 0; min-width:0; text-align:center; font-style:normal;
  font-family:var(--mono); font-size:clamp(7px,.82vw,10px);
  padding:4px 0; border-radius:3px; background:rgba(255,255,255,.03);
  border:1px solid var(--line); color:var(--txt-3); transition:.22s var(--ease);
}
.layer .lr i.hot{ background:var(--amber); color:#070b13; border-color:var(--amber); font-weight:700; }
.layer .lr i.hot2{ background:var(--cyan); color:#070b13; border-color:var(--cyan); font-weight:700; }

/* menu / result lines */
.resline{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:7px 11px; border-radius:8px; font-size:12.5px;
  background:rgba(255,255,255,.03); border:1px solid var(--line); color:var(--txt-2);
}
.resline b{ font-family:var(--mono); color:var(--amber); }
.resline.good{ border-color:rgba(87,224,138,.45); background:rgba(87,224,138,.08); }
.resline.good b{ color:var(--green); }
.resline.bad{ border-color:rgba(255,107,107,.4); background:rgba(255,107,107,.07); }
.resline.bad b{ color:var(--red); }

/* big keyspace number */
.kcount{ font-family:var(--mono); font-size:clamp(24px,3.6vw,40px); font-weight:700;
  color:var(--amber); text-align:center; line-height:1.05; }
.kbars{ display:grid; gap:8px; }
.kbar{ display:grid; grid-template-columns:minmax(96px,132px) 1fr auto; gap:11px; align-items:center; }
.kbar .n{ font-family:var(--mono); font-size:10.5px; color:var(--txt-2); }
.kbar .t{ height:18px; border-radius:5px; background:rgba(255,255,255,.05); overflow:hidden; }
.kbar .t i{ display:block; height:100%; width:0; border-radius:5px; transition:width 1.3s var(--ease); }
.kbar .v{ font-family:var(--mono); font-size:10.5px; color:var(--txt-3); white-space:nowrap; }

/* timeline of the war years */
.tline{ position:relative; padding-left:0; }
.tlrow{
  display:grid; grid-template-columns:82px 20px 1fr; gap:12px; align-items:start;
  padding-bottom:14px; position:relative;
}
.tlrow:last-child{ padding-bottom:0; }
.tlrow .yr{ font-family:var(--mono); font-size:11px; color:var(--accent); padding-top:1px; text-align:right; }
.tlrow .mk{ position:relative; display:grid; place-items:center; }
.tlrow .mk::before{ content:""; position:absolute; top:14px; bottom:-14px; width:2px; background:var(--line); }
.tlrow:last-child .mk::before{ display:none; }
.tlrow .mk i{ width:9px; height:9px; border-radius:50%; background:var(--line-2); border:2px solid var(--bg);
  position:relative; z-index:1; transition:.3s var(--ease); }
.tlrow:hover .mk i{ background:var(--accent); box-shadow:0 0 12px var(--accent); }
.tlrow .tx .h{ font-size:13px; font-weight:650; color:var(--txt); }
.tlrow .tx .d{ font-size:11.5px; color:var(--txt-3); line-height:1.5; margin-top:2px; }

/* self-test output list */
.stlist{ display:grid; gap:3px; max-height:none; }
.strow{
  display:flex; align-items:center; gap:9px; padding:4px 9px; border-radius:6px;
  font-family:var(--mono); font-size:10.5px; color:var(--txt-3);
  background:rgba(255,255,255,.025); border:1px solid transparent;
}
.strow .mk{ color:var(--green); flex:none; }
.strow.fail .mk{ color:var(--red); }
.strow.fail{ color:var(--red); background:rgba(255,107,107,.08); border-color:rgba(255,107,107,.3); }

/* ============================================================
   5 · RESPONSIVE
   ============================================================ */

@media (max-width:1024px){
  /* stack: the machine above the title, both centred — so the paragraphs
     need their auto margins back to stay centred in this tier */
  .ecover-grid{ grid-template-columns:minmax(0,1fr); gap:clamp(14px,2.4vh,26px); }
  .ecover-body{ align-items:center; text-align:center; }
  .ecover-sub, .ecover-sub2{ margin-left:auto; margin-right:auto; }
  /* The accent rule follows the text to the centre: the cover is centre-aligned from
     here down, and a rule still hanging off the left edge would be the one thing on
     the slide refusing to line up with everything else. */
  .ecover h1::after{ margin-left:auto; margin-right:auto; }
  .ecover-actions{ justify-content:center; }
  .emachine{ max-width:min(58%,340px); order:-1; }
  .emachine-cap b{ font-size:9px; letter-spacing:.18em; }
  .emachine-cap span{ font-size:8px; }
  .emachine-cap{ bottom:-14px; }
}

@media (max-width:820px){
  .mach{ max-width:100%; }
  .mach-part .pn{ font-size:9.5px; }
  .pair{ grid-template-columns:1fr; }
  .pair .arw{ text-align:center; transform:rotate(90deg); justify-self:center; }
  .wheels{ gap:6px; }
  .kbar{ grid-template-columns:1fr; gap:3px; }
  .kbar .t{ height:12px; }
  .kbar .v{ text-align:left; }
  .tlrow{ grid-template-columns:64px 16px 1fr; gap:9px; }
  .why{ padding:14px 13px 15px 46px; }
  .why::before{ left:11px; top:13px; width:23px; height:23px; }
}

@media (max-width:560px){
  .ecover{ padding:12px 14px; }
  .ecover h1{ font-size:clamp(32px,11vw,54px); }
  .ecover-sub{ max-width:none; }
  .ecover-sub2{ max-width:none; }
  .ecover-wave{ height:20vh; }
  /* On a phone the two accent blooms would sit almost on top of each other in a
     narrow frame, so they move to the upper corners instead of the lower ones - far
     enough apart to read as two, and clear of the title block in the middle. */
  .ecover-glow-a{ background:
    radial-gradient(58% 26% at 22% 16%,
      rgba(255,179,71,.20) 0%, rgba(255,179,71,.14) 30%,
      rgba(255,179,71,.06) 52%, rgba(255,179,71,0) 74%),
    radial-gradient(46% 20% at 74% 12%,
      rgba(255,206,120,.09) 0%, rgba(255,206,120,.06) 32%,
      rgba(255,206,120,.03) 56%, rgba(255,206,120,0) 76%); }
  .ecover-glow-b{ background:
    radial-gradient(56% 26% at 78% 86%,
      rgba(63,224,208,.19) 0%, rgba(63,224,208,.13) 30%,
      rgba(63,224,208,.06) 52%, rgba(63,224,208,0) 74%),
    radial-gradient(44% 20% at 24% 90%,
      rgba(90,169,255,.13) 0%, rgba(90,169,255,.09) 32%,
      rgba(90,169,255,.04) 56%, rgba(90,169,255,0) 76%); }
  /* on a phone there is no room for a second column: the photo becomes
     atmosphere behind the title rather than a separate block */
  .emachine{
    position:absolute; right:-14%; top:50%; translate:0 -50%;
    width:86%; max-width:none; z-index:0; opacity:.3; order:0;
    animation:none;
  }
  .emachine-cap, .emachine::before, .emachine::after{ display:none; }
  .emachine-halo{ filter:blur(24px); opacity:.65; }
  .mach{ padding:9px; border-radius:16px; }
  .mach-kb .k{ font-size:7.5px; padding:3px 0; }
  .stecker .sk{ font-size:9.5px; padding:2px 5px; gap:5px; }
  .plain{ font-size:13.5px; padding-left:13px; }
  .astrip i{ width:18px; font-size:8.5px; padding:5px 0; }
}

/* ============================ Tab: the cover's type, and only the cover's
   833px-952px is exactly the site's Tab band (880-999px) as it arrives in here. The
   iframe is 47px narrower than the page at every width this covers - measured, not
   assumed; the offset only changes below site 560px, where it becomes 32px - so
   site 880 lands on frame 833, site 999 on frame 952, and site 1000 on frame 953.
   Those are the same two numbers already gating the fullscreen button and the slide
   scrollbar in deck.css, which is worth stating once instead of re-deriving.

   Written *after* the max-width:1024px tier on purpose: that tier already applies
   across this whole band (it is what stacks the cover into one centred column), and
   a media query adds no specificity, only a condition - so source order is what lets
   these win.

   Only the cover is touched. Every other slide is laid out against the stage and has
   no business changing size because the window happens to be a tablet's width.

   The title is deliberately NOT enlarged. At 8.6vw it is already 72-82px across this
   band, so it is not what reads as too small, and it is the single most likely thing
   to push the slide past its height - which matters more than usual here, because the
   scrollbar is hidden at these widths (deck.css), so anything that overflows becomes
   content nobody can scroll to.

   What was genuinely too small is the supporting type. The badge and the paragraphs
   both use `clamp()` floors that their `vw` terms never reach at tablet widths: the
   badge sits pinned at 8px and the paragraphs at 12.5px for the whole band, because
   0.72vw and 1.24vw of a 900px frame are 6px and 11px. Those floors are what this
   raises. Line-height comes down slightly with the larger size so the paragraphs gain
   legibility without gaining much height. */
@media (min-width:833px) and (max-width:952px){
  .ecover-badge{ font-size:10.5px; padding:8px 18px 8px 15px; }
  .ecover-badge i{ width:8px; height:8px; }
  .ecover-sub, .ecover-sub2{ font-size:15px; line-height:1.6; }
  .ecover-cap b{ font-size:10.5px; }
  .ecover-cap span{ font-size:9.5px; }

  /* And the photograph gives up the height to pay for it.

     This is not incidental. Stacked into one column at this width the cover needs
     741-750px of content while the slide has 684, so it already overflowed by 35-40px
     before any of the type above was enlarged - the overflow is pre-existing, and
     raising the text would have taken it to 57-66px. The photo is the largest single
     block in the stacked layout and it is decoration, so it is the right thing to
     spend: 340px -> 270px frees 70px, which covers the largest overflow in the band
     with a few pixels to spare. Two-column layouts are untouched, so the Desktop
     cover keeps its 560px photograph.

     Deliberately not fixed here: the same overflow exists just *above* this band -
     153-161px across frames 953-1024 - where the cover also stacks, the photo is
     still 340px and the slide is shorter still. That is the stacking breakpoint being
     set too high, it predates this change, and fixing it means choosing between
     shrinking the photo a great deal or keeping two columns further down. That is a
     design call rather than a tweak, so it is flagged rather than taken. */
  .emachine{ max-width:270px; }
}

/* ---------- Mobile: smaller cover buttons, and only there ----------
   `max-width:832px` is the same boundary as the block above, read from the other
   side: site 880 arrives as frame 833 and site 879 as frame 832, so everything at or
   below 832 is the site's Mobile layout. It has to be its own block rather than an
   extension of the deck's existing `max-width:560px` phone tier, because that tier
   only covers site 607 and down - it misses most of what a phone visitor actually
   sees, and a 700px-wide window would have kept full-size buttons.

   Written after the Tab block so the two, which share these declarations and do not
   overlap at any width, cannot be separated by anything. */
@media (max-width:832px){
  .ecover-actions{ gap:8px; }
  .ecover-actions .btn{
    font-size:9.5px;
    padding:7px 12px;
    border-radius:8px;
  }
}
