/* ============================================================================
   THE CONTROLS: the house's hand, one implementation for every public page.
   (Movement 1, 2026-08-10. Campbell: "megaphone as primary, plate for everything
   else - go".) Behaviour and drawing live in /controls.js beside this.

   Every colour here reads a token the host page already declares (--orange,
   --blue, --cream, --disp), so a control belongs to the page it stands on and
   nothing is hardcoded twice. A page that links this must declare those tokens.
   ========================================================================== */

/* ---------------- THE MEGAPHONE: the primary action, one per screen ------- */
/* CAMPBELL DREW THESE HIMSELF (2026-08-11) AND THEY ARE THE SPECIFICATION, NOT A
   REFERENCE. Eleven labels, each a navy body pointing right and an orange body pointing
   left. The previous megaphone was recreated by hand from his reference and lost the
   thing that makes it his: HIS CONE TILTS. Measured off his own file, normalised to
   1000px: top edge slope -0.419, underside nearly level at +0.049, axis -10.5deg, label
   baseline about -16deg. The recreation tapered about a LEVEL axis with a horizontal
   word, which is why "can we angle the words?" was the right instinct and the answer it
   got ("it would not help") was wrong: that was measured against the recreation.
   DO NOT RECREATE THEM AGAIN. Rebuild from brand/megaphones/ via scripts/megaphones/.

   THE SPLIT IS HIS, NOT OURS, and it is why this ships at raster weight with a live
   press: in his files the BODY is vector paths and the four RAYS are a separate raster
   layer. So the body is a 480px WebP (~9-10KB, 24-colour palette, lossless: measured
   smaller AND more accurate than lossy on flat art) and the rays are four CSS polygons
   traced back out of his own raster. The rays are identical art in all eleven files, so
   the shapes below are declared ONCE per direction. Fidelity against his own SVG at
   display size: mean 2.68/255, the difference confined to 1px of antialiasing.

   THERE IS NO DRAWING SCRIPT ANY MORE. drawMegaphones(), fitLabel(), advanceEm(), the
   CONE constant and the refit on fonts.ready all existed to fit a word into a cone we
   were drawing; his artwork carries its own word, so all of it came out with this pass,
   and the hardening checks that guarded that geometry came out with it. The control now
   needs no JavaScript at all: markup plus this stylesheet IS the megaphone, so a dead
   script leaves his artwork standing rather than a fallback plate. */
/* THE MEGAPHONE IS SIZED TO ITS BUTTON, NOT TO THE VIEWPORT (2026-08-11, his correction:
   "they need to be the size of the button or sized so that the text inside the megaphone
   is the same size as the button text"). It is a CONTROL standing beside a patch, and a
   patch is a fixed 13px label in a 48px object at every width, so this is a fixed number
   too: a clamp would have held parity on a phone and broken it on a desk.

   MEASURED OFF HIS OWN ARTWORK, not guessed: the drawn label's cap height is 0.0994 of
   the body width (his four MENU glyphs, measured in the shipped WebP and confirmed
   against the letter paths in his SVG). Graduate's caps are 0.75em, so the patch's 13px
   label stands 9.75px, and 9.75 / 0.0994 = 98px of megaphone. 104 is a hair over parity
   so the shout never reads SMALLER than the button beside it. Rendered against 98 / 118 /
   140 / 156 at 390 and 1440 before picking.

   NOTE the number moved once already for a reason worth remembering: while the frame
   carried wedge room the art was only 74.5% of this value, so making the box the art
   itself grew every megaphone by a third without anyone typing a bigger number. When a
   component's box changes meaning, re-pick every size expressed against it. */
.mega{position:relative;display:inline-block;width:var(--mw,104px);
  aspect-ratio:var(--ar);border:0;background:none;padding:0;cursor:pointer;
  text-decoration:none;line-height:0;-webkit-tap-highlight-color:transparent;
  transform-origin:14% 55%;transition:transform .15s cubic-bezier(.3,.9,.4,1.35)}
/* THE BOX IS THE ART, AND NOTHING ELSE (2026-08-11, his catch: "the megaphones aren't
   properly centered over the button"). He was right and it was structural. The first cut
   made the box the union of the body AND the wedges, so at rest a quarter of it was empty
   air on the ray side and the drawn cone sat 29px off centre at 390 and 42px at 1440,
   navy leaning left and orange leaning right. A margin was pulled back to hide it, which
   then ran a left-pointing megaphone clean off the screen edge at 430px, so the pull was
   gated to desktop and the phone was left with the full offset. Two wrongs, one cause.

   The box is now the BODY EXACTLY: full width, and the height of the taller of his two
   drawings so a flip cannot move the layout. The four wedges are absolutely positioned
   OUTSIDE it (negative left when it points left, past 100% when it points right), which
   is why they need no room in the box at all. So the object is centred on itself by
   construction, in a row, in a column, alone, at any width, and it lines up with whatever
   it stands under.

   THE WEDGES OVERFLOW ON PURPOSE, AND THE OVERFLOW IS CONTAINED HERE. A painted box past
   a container edge widens the layout viewport on a phone and never shrinks back (the
   2026-08-10 SIDEWAYS SCROLL lesson), so any page carrying a megaphone clips its own
   horizontal overflow. `clip` and not `hidden`: hidden makes a scroll container and would
   break the gallery's position:sticky. */
body:has(.mega){overflow-x:clip}

/* A PATCH BESIDE A MEGAPHONE MAY NOT STRETCH TO ITS HEIGHT. Caught on the render, not by
   a check: his artwork is about 165px tall where the drawn cone was 113, and every CTA
   row in this build is a flex row that never declared align-items, so the default
   `stretch` pulled FULL CATERING PAGE and CALL up into two blue SQUARES beside the
   ORDER megaphone. Six rows across five files had it, so the rule belongs to the control
   rather than to any one page: a control is a fixed object and a row is not allowed to
   resize it. The sibling combinator carries every real case (the megaphone leads, always,
   being the primary); :has() covers a future row that puts it second. */
.mega,.mega ~ .patch,.patch:has(~ .mega){align-self:center}
.mega>img{position:absolute;left:var(--bx);top:var(--by);width:var(--bw);height:var(--bh);
  display:block;max-width:none}
/* the words live in the img's alt, so a failed image reads and taps as its own label */
.mega>img[alt]{color:var(--cream);font-family:var(--disp);font-size:13px;letter-spacing:.07em}

/* HIS FOUR WEDGES. Each is one element clipped to the quadrilateral he drew, in the
   colour his own raster measures (#f67e27, which is NOT the site --orange and is not the
   #f68500 of his trim: it is what the ray layer actually is). */
.mega .ry{position:absolute;background:#f67e27;opacity:0;
  transform:translate(calc(var(--ox) * -.34),calc(var(--oy) * -.34)) scale(.72);
  transform-origin:var(--fx) var(--fy);
  transition:opacity .16s ease,transform .34s cubic-bezier(.2,.9,.3,1.35)}
.mega .r1{left:var(--r1x);top:var(--r1y);width:var(--r1w);height:var(--r1h)}
.mega .r2{left:var(--r2x);top:var(--r2y);width:var(--r2w);height:var(--r2h)}
.mega .r3{left:var(--r3x);top:var(--r3y);width:var(--r3w);height:var(--r3h)}
.mega .r4{left:var(--r4x);top:var(--r4y);width:var(--r4w);height:var(--r4h)}
[data-mega][data-dir=r] .r1{clip-path:polygon(0% 92.02%,73.82% 0%,100% 21.10%,12.50% 100%)}
[data-mega][data-dir=r] .r2{clip-path:polygon(1.78% 64.10%,99.82% 0%,100% 80.51%,0% 100%)}
[data-mega][data-dir=r] .r3{clip-path:polygon(3.06% 0%,100% 39.92%,90.32% 100%,0% 25.93%)}
[data-mega][data-dir=r] .r4{clip-path:polygon(8.04% 0%,100% 74.05%,78.69% 100%,0% 11.90%)}
[data-mega][data-dir=l] .r1{clip-path:polygon(0% 18.06%,27.55% 0%,100% 90.32%,90% 100%)}
[data-mega][data-dir=l] .r2{clip-path:polygon(13.94% 0%,100% 81.07%,95.01% 100%,0% 42.59%)}
[data-mega][data-dir=l] .r3{clip-path:polygon(3.93% 0%,100% 29.68%,99.49% 72.26%,0% 100%)}
[data-mega][data-dir=l] .r4{clip-path:polygon(0% 72.46%,90% 0%,100% 9.32%,18.09% 100%)}
/* each wedge leaves the bell on its own bearing, so the cheer opens as a fan */
[data-mega][data-dir=r] .r1{--ox:34%;--oy:-58%;--fx:0%;--fy:100%}
[data-mega][data-dir=r] .r2{--ox:64%;--oy:-14%;--fx:0%;--fy:70%}
[data-mega][data-dir=r] .r3{--ox:64%;--oy:20%;--fx:0%;--fy:20%}
[data-mega][data-dir=r] .r4{--ox:38%;--oy:56%;--fx:0%;--fy:0%}
[data-mega][data-dir=l] .r1{--ox:-34%;--oy:-58%;--fx:100%;--fy:100%}
[data-mega][data-dir=l] .r2{--ox:-64%;--oy:-14%;--fx:100%;--fy:60%}
[data-mega][data-dir=l] .r3{--ox:-64%;--oy:20%;--fx:100%;--fy:40%}
[data-mega][data-dir=l] .r4{--ox:-38%;--oy:56%;--fx:100%;--fy:0%}
.mega .r2,.mega .r3{transition-delay:.02s}
.mega .r1,.mega .r4{transition-delay:.05s}

/* THE WEDGES NEVER DIM, THEY TRAVEL. A partly transparent orange over this ink reads
   BROWN, which is a colour nowhere in his artwork; caught on the render at 62% opacity.
   So the state is carried by how far the fan has opened, never by its strength, and the
   rest position is TUCKED IN rather than out: the box only ever has to hold the wedges
   where he drew them, so a fired megaphone can never contribute overflow. */
.mega:hover .ry,.mega.is-hov .ry{opacity:1;
  transform:translate(calc(var(--ox) * -.16),calc(var(--oy) * -.16)) scale(.88)}
.mega:active .ry,.mega.is-press .ry{opacity:1;transform:none;transition-duration:.1s,.26s}
.mega:hover,.mega.is-hov{transform:rotate(-1.6deg) scale(1.015)}
.mega[data-dir=l]:hover,.mega[data-dir=l].is-hov{transform:rotate(1.6deg) scale(1.015)}
.mega:active,.mega.is-press{transform:rotate(-4.6deg) scale(.992)}
.mega[data-dir=l]:active,.mega[data-dir=l].is-press{transform:rotate(4.6deg) scale(.992)}
.mega:focus-visible{outline:2px solid var(--orange-lt,#ffab4d);outline-offset:6px;border-radius:4px}
.mega[hidden]{display:none}
@media(prefers-reduced-motion:reduce){
  .mega,.mega .ry{transition:none}
  .mega:hover,.mega.is-hov,.mega:active,.mega.is-press,
  .mega[data-dir=l]:hover,.mega[data-dir=l]:active{transform:none}
}

/* THE ROOM'S OWN MEGAPHONE. Not a control: it is a STATEMENT, so it never leans, never
   takes a pointer, and its wedges are not driven by a finger. They are driven by the
   room: the cheer is out while the doors are open and on an Auburn day, and a closed
   room stands with nothing coming out of it, which is the same call Campbell made for
   the little signal ("no cheer when the room is shut"). */
.mega[data-mega-state]{cursor:default;pointer-events:none}
.mega[data-mega-state]:hover,.mega[data-mega-state]:active{transform:none}
.mega[data-mega-state][data-mega=open] .ry,
.mega[data-mega-state][data-mega=gameday] .ry{opacity:1;transform:none}

/* THE GEOMETRY, measured off his files and declared once. Ten of the eleven share one
   frame; gameday carries pom-poms and is its own. Frame = the union of his body and his
   wedges, so the control's box is exactly the art and never a padded rectangle. */
[data-mega][data-dir=r]{--ar:1.5722;--bx:0%;--by:0.00%;--bw:100%;--bh:100.00%;
  --r1x:98.11%;--r1y:-3.60%;--r1w:14.07%;--r1h:27.44%;
  --r2x:102.42%;--r2y:26.45%;--r2w:18.68%;--r2h:10.17%;
  --r3x:103.75%;--r3y:48.25%;--r3w:19.54%;--r3h:12.68%;
  --r4x:101.29%;--r4y:63.43%;--r4w:17.75%;--r4h:21.91%}
[data-mega][data-dir=l]{--ar:1.5722;--bx:0%;--by:4.10%;--bw:100%;--bh:91.80%;
  --r1x:-14.82%;--r1y:2.77%;--r1w:14.79%;--r1h:22.06%;
  --r2x:-21.00%;--r2y:23.08%;--r2w:17.53%;--r2h:15.04%;
  --r3x:-21.21%;--r3y:46.80%;--r3w:17.65%;--r3h:7.35%;
  --r4x:-15.06%;--r4y:60.94%;--r4w:14.18%;--r4h:22.91%}
[data-mega=gameday][data-dir=r]{--ar:1.0667;--bx:0%;--by:0.00%;--bw:100%;--bh:100.00%;
  --r1x:98.11%;--r1y:16.31%;--r1w:14.03%;--r1h:18.57%;
  --r2x:102.42%;--r2y:36.64%;--r2w:18.63%;--r2h:6.88%;
  --r3x:103.74%;--r3y:51.39%;--r3w:19.49%;--r3h:8.58%;
  --r4x:101.29%;--r4y:61.67%;--r4w:17.70%;--r4h:14.83%}
[data-mega=gameday][data-dir=l]{--ar:1.0667;--bx:0%;--by:7.84%;--bw:100%;--bh:84.33%;
  --r1x:-7.88%;--r1y:14.06%;--r1w:13.89%;--r1h:14.06%;
  --r2x:-13.69%;--r2y:27.01%;--r2w:16.47%;--r2h:9.58%;
  --r3x:-13.89%;--r3y:42.12%;--r3w:16.58%;--r3h:4.69%;
  --r4x:-8.11%;--r4y:51.13%;--r4w:13.32%;--r4h:14.60%}

/* ---------------- THE PATCH: every secondary action ---------------------- */
/* THE CHENILLE PATCH OFF THE LETTERMAN JACKET (2026-08-10, Campbell on the first
   answer: "The plates seem way too boring for such an exciting site what other options
   do we have"). He was right, and the defect had a name: a bordered rectangle standing
   next to a drawn megaphone reads as the placeholder somebody forgot to finish.

   FIVE OBJECTS DIED AT THE CONCEPT LEVEL, each for a reason and not a preference: the
   TICKET STUB (it says "ticketed" on a bar whose owner's first law is walk-in only),
   the COASTER (round; it cannot hold SEE EVERY SHOW), the SCOREBOARD SLAT (it steals
   the split-flap board Movement 3 is built around), the PENNANT (it breaks the moment
   six stand in a row), the MARQUEE SLAT (its bulb ends put back the very dots 1c just
   purged). THREE WERE DRAWN and two more died on the render: a punched STENCIL PLATE
   (its rivets read as status dots, and a warehouse tag belongs to no part of this bar)
   and a TAP BADGE (a tap handle names a beer, so GET DIRECTIONS on one is the ticket
   stub's lie wearing a different shape).

   The patch wins because it is the jacket the whole face is named for, because it is
   the same family as the primary control (both are things a cheer squad owns), and
   because its press does what the real object does: it seats into the wool. Felt body,
   the corners notched the way a real patch is cut, an orange chain stitch inside them.

   IT NEVER OUT-SHOUTS THE MEGAPHONE. That is the job of a secondary, and it is why the
   heavier take (a solid merrowed orange edge) was rejected after rendering a row of
   six: it was handsome alone and a wall of orange in the rail where it actually lives.

   text-decoration is declared here because a control is never underlined and the page
   it stands on may not have said so: /menu/ carries no anchor reset, so every
   anchor-shaped patch there printed with a rule through its own label until this line.
   Caught on the render, not by a check.

   NO COMMENT GOES INSIDE THIS DECLARATION BLOCK. A closing brace in comment prose ends
   a gate's [^}]* capture early, and the hardening check for the cut and the felt went
   red reading a truncated rule while the CSS was perfect (2026-08-10; the 2026-07-19
   comment-stripping lesson arriving from the other direction). */
.patch{position:relative;font-family:var(--disp);font-weight:400;letter-spacing:.07em;
  text-transform:uppercase;font-size:13px;color:var(--cream);border:0;
  padding:15px 28px;min-height:48px;
  display:inline-flex;align-items:center;justify-content:center;text-align:center;
  cursor:pointer;-webkit-tap-highlight-color:transparent;text-decoration:none;
  background:radial-gradient(rgba(255,255,255,.055) .5px,transparent .6px) 0 0/3px 3px,
             linear-gradient(168deg,#1d3767,#152a52);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.09),inset 0 -2px 4px rgba(0,0,0,.4);
  clip-path:polygon(9px 0,calc(100% - 9px) 0,100% 9px,100% calc(100% - 9px),calc(100% - 9px) 100%,9px 100%,0 calc(100% - 9px),0 9px);
  transition:transform .13s cubic-bezier(.3,.9,.4,1.2),box-shadow .13s}
/* the clip-path carries the pseudo-elements with it, so the beam below needs no
   overflow:hidden and the stitch can never escape the notch */
.patch>*{position:relative;z-index:3}

/* THE CHAIN STITCH, drawn as four repeating gradients rather than a dashed border:
   a CSS dash reads as a dash, and a chain stitch reads as thread: short links, close
   together, carrying their own shadow so they sit ON the felt instead of in it. The
   stitch wears the SAME notched clip as the body, so the thread turns every corner the
   way it does on a real patch instead of cutting across it. */
.patch::before{content:"";position:absolute;inset:6px;z-index:2;pointer-events:none;
  background:
    repeating-linear-gradient(90deg,var(--th,#f79a34) 0 3.5px,transparent 3.5px 6px) top left/100% 2px no-repeat,
    repeating-linear-gradient(90deg,var(--th,#f79a34) 0 3.5px,transparent 3.5px 6px) bottom left/100% 2px no-repeat,
    repeating-linear-gradient(180deg,var(--th,#f79a34) 0 3.5px,transparent 3.5px 6px) top left/2px 100% no-repeat,
    repeating-linear-gradient(180deg,var(--th,#f79a34) 0 3.5px,transparent 3.5px 6px) top right/2px 100% no-repeat;
  filter:drop-shadow(0 1px 0 rgba(0,0,0,.55));transition:filter .2s}
.patch:hover{transform:translateY(-1px)}
.patch:hover::before{--th:#ffc46a;filter:drop-shadow(0 0 4px rgba(255,180,90,.6)) drop-shadow(0 1px 0 rgba(0,0,0,.5))}
/* PRESS SEATS IT INTO THE WOOL: down two, and the light that was sitting on top of it
   falls inside. Nothing scales, because a sewn patch cannot change size. */
.patch:active,.patch.is-press{transform:translateY(2px);
  box-shadow:inset 0 3px 7px rgba(0,0,0,.55)}
.patch:focus-visible{outline:2px solid var(--orange-lt,#ffab4d);outline-offset:4px}
.patch[hidden]{display:none}
/* the game-day patch: orange chenille, navy thread. At most one on a screen. */
.patch.o{background:radial-gradient(rgba(255,255,255,.07) .5px,transparent .6px) 0 0/3px 3px,
                    linear-gradient(168deg,#f0912b,#d2701a);color:#0b1428}
.patch.o::before{--th:rgba(11,20,40,.66)}
.patch.o:hover::before{--th:#0b1428;filter:drop-shadow(0 1px 0 rgba(255,255,255,.28))}
@media(prefers-reduced-motion:reduce){
  .patch{transition:none}.patch:hover,.patch:active,.patch.is-press{transform:none}}

/* THE RIG FINDS IT: RE-PACED (2026-08-10, Campbell: "the light of the plates seems too
   frequent and not random enough directionally. Needs to be slowed, paced and from
   different directions").

   WHAT WAS WRONG, measured: the beam crossed every 3.8s to 6.4s and was lit for 42% of
   that, so a screen holding nine of them was never once at rest: and only TWO
   directions existed, left-to-right and back. Continuous motion in two directions is a
   shimmer, and a shimmer is on the median's shelf however it is motivated.

   WHAT IT IS NOW: the house rig makes ONE slow pass and then leaves. The beam is lit
   for 13% of an 11s to 18s cycle, so a patch stands dark for nine to sixteen seconds
   between passes and the light becomes something you catch rather than a texture you
   stop seeing. The travel ANGLE is rolled per element (--ang) and the travel SIGN with
   it (--sg): eight angles times two signs is sixteen apparent directions: across,
   down, up, and every diagonal: where there used to be two. Rolled per element per
   load by armSweep(), so no two patches on a screen are ever in step or in line. */
/* THE BEAM DECLARES ITS RESTING TRANSFORM, NOT ONLY ITS KEYFRAMES, and that is a
   correctness line rather than a tidiness one. Without it the pseudo-element exists
   for at least one frame at left:50%/top:50% with NO transform, sticking 58px past the
   patch's centre and 340% below it. On a phone viewport that momentary box widens the
   LAYOUT viewport, and a widened layout viewport does not shrink back: the wall
   reported SIDEWAYS SCROLL with realOverflow 0 and window.innerWidth 18px to 73px over
   what was asked for, on a different set of pages every run. A keyframed transform
   always carries a matching static declaration. `overflow:hidden` rides beside it as
   the structural guarantee: clip-path already handles the visual, so this costs
   nothing and makes the beam incapable of contributing scrollable overflow at all. */
.patch{overflow:hidden}
.patch.sweep::after{content:"";position:absolute;left:50%;top:50%;
  width:58px;height:340%;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(255,196,120,.32),rgba(255,196,120,.05),transparent);
  transform:translate(-50%,-50%) rotate(var(--ang,24deg)) translateX(calc(-210px * var(--sg,1)));
  animation:g28pan var(--dur,13s) linear var(--delay,0s) infinite}
@keyframes g28pan{
  0%  {transform:translate(-50%,-50%) rotate(var(--ang,24deg)) translateX(calc(-210px * var(--sg,1)))}
  13% {transform:translate(-50%,-50%) rotate(var(--ang,24deg)) translateX(calc( 210px * var(--sg,1)))}
  100%{transform:translate(-50%,-50%) rotate(var(--ang,24deg)) translateX(calc( 210px * var(--sg,1)))}}
/* the thread catches only while the beam is actually crossing it, never on its own */
.patch.sweep::before{animation:g28edge var(--dur,13s) linear var(--delay,0s) infinite}
@keyframes g28edge{
  0%,13%,100%{filter:drop-shadow(0 1px 0 rgba(0,0,0,.55))}
  6.5%{filter:drop-shadow(0 0 5px rgba(255,190,110,.72)) drop-shadow(0 1px 0 rgba(0,0,0,.55))}}
@media(prefers-reduced-motion:reduce){
  .patch.sweep::after{display:none}.patch.sweep::before{animation:none}}

/* ---------------- THE SIGNAL: HIS megaphone, at pill size ---------------- */
/* THE SIGNAL WAS ALWAYS MEANT TO BE HIS ARTWORK (2026-08-11, his correction: "the open
   closed and gameday megaphones were supposed to be the replacement for the ones that
   are in the pill. small. the drawn ones"). The drawn one was a stand-in built before
   he had drawn OPEN, CLOSED and GAMEDAY; now that they exist, a hand-drawn cone sitting
   where his own belongs is the recreation law breaking in miniature.

   It is bigger than the dot it replaced because it has to be: his artwork CARRIES THE
   WORD, and at 34px the word is unreadable. 74px puts the label at about 7.4px, which
   reads at pill scale, and it is what lets the pill's own text drop to just the hour.
   `.lm` keeps its name and its four states so every caller and every gate still finds
   what it looks for; only what it draws has changed. */
/* it is built exactly like the big one, at a fixed small width, so ONE set of geometry
   below serves both objects and they can never drift apart */
.lm{width:var(--lmw,74px);aspect-ratio:var(--ar);flex:0 0 auto;display:inline-block;
  line-height:0;position:relative}
/* THE CHEER NEEDS SOMEWHERE TO GO, and inside a pill the only thing beside it is the
   words. The wedges sit OUTSIDE the box by design (that is what keeps the object centred
   on itself), so the signal asks its row for exactly the space they occupy: 23.3% of the
   body width on the side it points. Caught on the render, where OPEN fired straight
   through "TIL 9PM". */
.lm[data-dir=r]{margin-right:calc(var(--lmw,74px) * .25)}
.lm[data-dir=l]{margin-left:calc(var(--lmw,74px) * .25)}
.lm>img{position:absolute;left:var(--bx);top:var(--by);width:var(--bw);height:var(--bh);
  display:block;max-width:none}
.lm .ry{position:absolute;background:#f67e27;opacity:0;
  transition:opacity .2s ease,transform .34s cubic-bezier(.2,.9,.3,1.35)}
.lm .r1{left:var(--r1x);top:var(--r1y);width:var(--r1w);height:var(--r1h)}
.lm .r2{left:var(--r2x);top:var(--r2y);width:var(--r2w);height:var(--r2h)}
.lm .r3{left:var(--r3x);top:var(--r3y);width:var(--r3w);height:var(--r3h)}
.lm .r4{left:var(--r4x);top:var(--r4y);width:var(--r4w);height:var(--r4h)}
/* OPEN AND GAMEDAY CHEER; A CLOSED ROOM STANDS WITH NOTHING COMING OUT OF IT (his call
   on the drawn signal, kept). The open room also catches the house light once and then
   holds it, and an Auburn day SHAKES: the pom-poms are baked into his artwork, so the
   whole object shakes and the wedges fan with it, which is the pom-poms moving. */
.lm.on .ry,.lm.hot .ry{opacity:1;transform:none}
/* PRE IS A SHUT ROOM AND CHEERS FOR NOTHING (2026-08-12). It used to fire its wedges at
   82%, which was the object cheering at six in the morning beside the words OPENS TODAY
   11AM. His own call on the drawn signal was that a closed room stands with nothing
   coming out of it, and a morning is a closed room. It wears the CLOSED artwork now. */
.lm.pre .ry,.lm.off .ry{opacity:0}
.lm.on>img{filter:drop-shadow(0 0 6px rgba(240,136,32,.55));animation:g28lmglow 4.6s ease-in-out .4s 2}
.lm.hot{animation:g28shake 1.4s cubic-bezier(.36,.07,.19,.97) .3s 3}
.lm.hot>img{filter:drop-shadow(0 0 8px rgba(255,190,110,.9))}
/* ...but the morning is not the small hours: PRE keeps its colour (the lights are on
   inside, the doors simply are not open yet) while OFF goes cold. Two shut states, one
   of them warming, and neither of them claims to be open. */
.lm.pre>img{filter:grayscale(.22) brightness(.86)}
.lm.off>img{filter:grayscale(.66) brightness(.62)}
@keyframes g28lmglow{
  0%,100%{filter:drop-shadow(0 0 6px rgba(240,136,32,.55))}
  50%{filter:drop-shadow(0 0 13px rgba(255,190,110,.95)) drop-shadow(0 0 26px rgba(240,136,32,.4))}}
@keyframes g28shake{
  0%,100%{transform:rotate(0) translateX(0)}
  8%{transform:rotate(-3.2deg) translateX(-1.5px)}  20%{transform:rotate(2.8deg) translateX(1.5px)}
  32%{transform:rotate(-2.2deg) translateX(-1px)}   44%{transform:rotate(1.6deg) translateX(1px)}
  56%{transform:rotate(-.8deg)}                     68%{transform:rotate(.4deg)}}
@media(prefers-reduced-motion:reduce){
  .lm.on>img,.lm.hot{animation:none}
  .lm .ry{transition:none}}
/* RETIRED 2026-08-11 and named rather than left standing: the drawn cone, bell, core and
   three wedges the signal used to be built from, and the g28fire keyframe that lit them.
   They were a stand-in made before Campbell had drawn OPEN, CLOSED and GAMEDAY. */

/* ---------------- THE SIGN: the client's own mark, lit as neon ----------- */
/* Nothing is redrawn: the brand mark's own orange outline layer is the gas and its navy
   core is the glass, so the site's largest signal is the client's real logo doing what
   it was always shaped to do. The sign may never lie: its state is the same stamp the
   signal reads. Strike is one irregular flicker on arrival, then steady, never a loop. */
.sign{display:inline-block;line-height:0}
.sign img,.sign svg{display:block;width:100%;height:auto}
.sign.is-open{filter:drop-shadow(0 0 6px rgba(240,136,32,.85)) drop-shadow(0 0 22px rgba(240,136,32,.5)) drop-shadow(0 0 52px rgba(240,136,32,.26));
  animation:g28strike .62s steps(1,end) 1 both}
.sign.is-pre{filter:drop-shadow(0 0 4px rgba(240,136,32,.34)) drop-shadow(0 0 14px rgba(240,136,32,.16))}
.sign.is-off{filter:grayscale(.72) brightness(.5) contrast(.92)}
.sign.is-hot{filter:drop-shadow(0 0 8px rgba(255,190,110,.95)) drop-shadow(0 0 28px rgba(240,136,32,.6)) drop-shadow(0 0 64px rgba(240,136,32,.34));
  animation:g28strike .62s steps(1,end) 1 both}
@keyframes g28strike{0%{opacity:.15}8%{opacity:1}14%{opacity:.2}24%{opacity:1}32%{opacity:.45}42%{opacity:1}100%{opacity:1}}
@media(prefers-reduced-motion:reduce){.sign.is-open,.sign.is-hot{animation:none}}
