/* ==========================================================================
   LISRAYA MOA illustration -- GENERATED from moa-test/index.html.
   Do not hand-edit: regenerate with tools/build-port.py after prototype changes.

   Scoped under .moa-module. The prototype's `*` reset, `body` rule and demo
   chrome (.controls / #status) are dropped. All SVG ids are namespaced `moa-*`
   because url(#...) resolves against the DOCUMENT, not the SVG.

   Must ALSO be registered for the block editor (generate_editor_styles):
   .glow/.ring/.towers/.lines carry no fill/stroke attributes, so without it the
   illustration previews as a black blob.
   ========================================================================== */
:root{
    --blue-dark:#00529b;
    --blue-mid:#358ccb;
    --orange:#f26a38;
    --yellow:#ffc60a;
    --cream-outer:#faefc8;
    --cream-inner:#fdfaee;
    /* the pale conduit a stuck vein shows BETWEEN pulses (sampled from
       "02- MOA Initial State.mp4"; the bright pulse is --yellow over this) */
    --cream-pipe:#fbe6ae;
  }
  /* border-box on the module and its descendants. The prototype gets this from a
     global `*` reset, but the port strips that -- and WITHOUT it the 1:1 square
     silently breaks: aspect-ratio sizes the CONTENT box, so the module's 26px+30px
     vertical padding is ADDED on top and a 440-wide module measures 440x496.
     Scoped rather than global so the stylesheet stays safe to drop into a theme
     that has its own reset. */
  .moa-module, .moa-module *{box-sizing:border-box}
/* ============ outer container ============
     Designer feedback #1: the pale-yellow outer shape and its drop shadow are
     GONE -- the module sits directly on the page background.
     Designer feedback #2: the full background container is 1:1. aspect-ratio
     does the work; the toggle row and illustration are centred inside it, so
     the square holds regardless of the illustration's own height. */
  /* aspect-ratio ALONE IS NOT ENOUGH. It sets a preferred box, but a flex item
     whose content is taller simply pushes the box past it, and the SVG's
     intrinsic height (from its viewBox ratio) does exactly that -- this module
     measured 440x541 with aspect-ratio:1/1 declared and apparently "working".
     Declaring the property is not the same as it taking effect; measure the box,
     don't trust the computed style.
     The square holds only because BOTH of these are true:
       - min-height:0 on the flex children, so they may shrink below content;
       - the SVG is height-driven (see .moa-card svg), so it scales down instead
         of dictating the height. */
  .moa-module{
    width:440px;max-width:96vw;aspect-ratio:1/1;
    padding:26px 20px 30px;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:22px;
    overflow:hidden;
  }

  /* ============ top switcher (stand-in for GreenShift switcher block) ============ */
  .moa-toggle-row{display:flex;align-items:center;justify-content:center;gap:12px;
    font-weight:800;font-size:13px;line-height:1.3;color:var(--blue-dark)}
  .moa-toggle-row .side{transition:opacity .3s;text-align:right}
  .moa-toggle-row .side:last-child{text-align:left}
  .moa-toggle-row .side.dim{opacity:.45}
  .moa-switch{
    position:relative;width:56px;height:28px;border-radius:14px;border:none;cursor:pointer;
    background:#63666a;transition:background .4s;flex:0 0 auto;padding:0;
  }
  .moa-switch .thumb{
    position:absolute;top:3px;left:3px;width:22px;height:22px;border-radius:50%;
    background:#fff;transition:transform .4s cubic-bezier(.4,0,.2,1);
    display:flex;align-items:center;justify-content:center;
  }
  .moa-switch .thumb::after{
    content:"";width:11px;height:11px;border-radius:50%;
    background:var(--blue-dark);
  }
  .moa-switch[aria-checked="true"]{background:var(--blue-dark)}
  .moa-switch[aria-checked="true"] .thumb{transform:translateX(28px)}

  /* ============ inner illustration panel ============
     Designer feedback #4: no white/cream background behind the illustration --
     the SVG sits straight on whatever is behind the module. Padding stays so the
     glow's blur bleed is not clipped by the container edge. */
  /* min-height:0 + the flex sizing below is what lets the 1:1 module above
     constrain this box instead of being stretched by it. */
  .moa-card{
    width:380px;max-width:100%;padding:18px 10px 8px;
    min-height:0;flex:1 1 auto;display:flex;
  }
  /* The SVG must be allowed to size DOWN to whatever height the square leaves,
     not just up. width:100% alone gives it an intrinsic height from the viewBox
     ratio, which is what pushed the module past 1:1. height:100% + min-height:0
     lets flex shrink it; preserveAspectRatio (default xMidYMid meet) then keeps
     the artwork undistorted and centred inside the shorter box. */
  .moa-card svg{
    display:block;width:100%;height:100%;
    max-height:100%;min-height:0;flex:1 1 auto;
  }

  /* ---- wall switches ----
     The lever pivots at the BOTTOM of its stem and throws ~90deg between OFF
     (knob far left) and ON (knob far right) -- this wide swing is the whole
     read of the animation.
     The knob is ORANGE when ON and BLUE when OFF, per the source artboard
     (Normal Immune System Activity: switches 0 & 4 read ON with orange bulbs,
     the other four are blue). An earlier comment here claimed the art had no
     knob color change -- it does; that claim was wrong. */
  /* Pivot and throw are DERIVED FROM THE SOURCE ART, not eyeballed. Decoding the
     baked Illustrator transforms in On-/Off-Switch.svg puts the knob centre at
     (14.13,3.60) when ON and (5.41,3.62) when OFF, about a base pivot of
     (9.78, 11.4). That is a radius of 8.93 and a symmetric +/-29.1deg throw.
     (Measuring the stem RECT's diagonal instead gives ~41deg and overshoots --
     the angle must come from the pivot->knob line.)
     Do NOT use transform-box:fill-box here -- the group bbox includes the knob,
     which drags the pivot off and makes the lever lie flat. */
  .moa-module .sw .lever{
    transform:rotate(-29.1deg);
    transform-origin:9.78px 11.4px;
    transition:transform .34s cubic-bezier(.5,1.6,.5,1);
  }
  .moa-module .sw.on .lever{transform:rotate(29.1deg)}
  /* bulb reads the state: blue at rest, orange once the switch is thrown */
  .moa-module .sw .knob{fill:var(--blue-mid);transition:fill .34s}
  .moa-module .sw.on .knob{fill:var(--orange)}
  .moa-module .sw-label{
    font-family:'Nunito Sans',sans-serif;font-size:11px;font-weight:800;
    fill:var(--blue-dark);text-anchor:middle;letter-spacing:.3px;
  }

  /* ---- switch panel outline ----
     Colours live here, not on the rect, so the dark variant can override them.
     fill:none per designer feedback #4 -- the switch panel is an OUTLINE, not a
     white card; the module background shows through it. */
  .moa-module #moa-panel{fill:none;stroke:var(--blue-mid);transition:stroke .6s,fill .6s}
  .moa-module .moa.lines-hot #moa-panel{stroke:var(--yellow)}

  /* ---- connecting lines ----
     The blue paths are the resting structure. JS clones each one into a yellow
     overlay and draws that overlay on via stroke-dashoffset as its switch flips. */
  /* stroke-width is in the group's LOCAL units, and .lines is placed at
     scale 0.38432, so the rendered weight is stroke-width * 0.38432.
     The 2026-08 round asks for "1-2px more" AND for the line to match the
     width of the trunk at the top of the two-circle shape:
        4.5 / 0.38432 = 11.71 local units.
     Those two halves of the note agree -- the artwork's own stroke is 12 local
     = 4.61px, so 4.5px lands essentially back on the source weight. This
     supersedes the earlier feedback #3 thinning to 3px (7.806 local).
     Change the wrapper scale and this number must change with it.
     NB: a CSS rule beats a stroke-width presentation attribute on the <g>, so
     the weight must live here, not in the markup. */
  .moa-module .lines path{
    fill:none;stroke:var(--blue-mid);stroke-width:11.71;
    stroke-linecap:round;stroke-linejoin:round;
    transition:stroke .45s;
  }
  .moa-module .lines path.line-hot{stroke:var(--yellow)}
  /* The cover that re-draws over a settled vein when a line changes colour.
     Same weight and caps as the base line (inherited from `.lines path`) so it
     hides the old colour exactly; it must be OPAQUE, since it works by covering
     rather than by erasing. Blue by default (the reverse cascade), retargeted
     by the sweep-* modifiers: the reference videos show the jammed veins 0 & 4
     re-drawing PALE CREAM on the way back and GOLD on the way to DM, at their
     own slots in the left-to-right cascade -- that recolor is this same cover
     mechanism, just in another colour. */
  .moa-module .lines path.line-cool{stroke:var(--blue-mid)}
  .moa-module .lines path.line-cool.sweep-cream{stroke:var(--cream-pipe)}
  .moa-module .lines path.line-cool.sweep-gold{stroke:var(--yellow)}

  /* ---- idle electricity flow ----
     In the reference the two STUCK veins are not a solid yellow: they are a
     PALE cream conduit with a bright saturated pulse sliding along toward the
     blob, looping while the page waits for the user. That constant motion is
     what sells "stuck in the on position" -- the system is still running, it
     just cannot be switched off.

     Two layers make that work: .line-hot is dimmed to the pale conduit while
     .flowing, and a third clone (.line-flow) carries the bright pulse. The
     pulse is a dash whose length is a FRACTION of each path (set from
     getTotalLength() in JS), so it stays proportional on every vein. */
  .moa-module .lines path.line-hot.flow-dim{stroke:var(--cream-pipe)}
  .moa-module .lines path.line-flow{
    stroke:var(--yellow);
    stroke-linecap:round;
    pointer-events:none;
    opacity:0;
  }
  .moa-module .lines path.line-flow.on{
    opacity:1;
    animation:moaFlow var(--flow-dur,1.8s) linear infinite;
  }
  @keyframes moaFlow{
    from{stroke-dashoffset:var(--flow-from,0)}
    to  {stroke-dashoffset:var(--flow-to,0)}
  }

  /* ---- blob outline + glow ----
     Two glows: a soft blue halo in the "stuck" state and an orange one when hot
     (the Figma prototype shows a halo in BOTH states, not just the orange one). */
  .moa-module .ring{fill:none;stroke:var(--blue-mid);stroke-width:1.4;transition:stroke .7s}
  .moa-module .moa.hot .ring{stroke:#f7a600}
  .moa-module .glow{opacity:0;transition:opacity .9s}
  .moa-module .glow-blue{fill:#7fb4e0}
  .moa-module .glow-hot{fill:#f7a600}
  .moa-module .moa.cool .glow-blue{opacity:.38}
  .moa-module .moa.hot  .glow-blue{opacity:0}
  .moa-module .moa.hot  .glow-hot{opacity:.45}

  /* NB the blob fill has no CSS rule: it is a mask sweep driven by one rAF loop
     in JS (see fillCircles). A CSS transition cannot animate <stop offset>
     reliably, and the old sliding-rect approach could not follow the waist. */

  /* ---- towers ----
     driven by .towers-hot, NOT .hot: in the real prototype the towers turn
     orange a beat BEFORE the blob fill washes over. */
  .moa-module .towers *{
    fill:none;stroke:var(--blue-dark);stroke-width:1.02;
    stroke-linecap:round;stroke-linejoin:round;
    transition:stroke .7s;
  }
  .moa-module .moa.towers-hot .towers *{stroke:var(--orange)}

  /* ---- houses (crossfade) ---- */
  .moa-module .house{transition:opacity .7s}
  .moa-module .house-orange{opacity:0}
  .moa-module .moa.hot .house-orange{opacity:1}
  .moa-module .moa.hot .house-blue{opacity:0}

  /* ---- window glow ----
     Orange, looping 0% <-> 80% opacity, per the 2026-08 round. Gated on .hot so
     it cannot leak into the healthy state -- the same trap that showBolts /
     showFlames document (their "off" path must clear .idle, not just .show).
     Here the gate is the selector itself, so toggling .hot off stops the loop
     with no JS bookkeeping. */
  .moa-module .window-glow{fill:var(--orange);opacity:0}
  .moa-module .moa.hot .window-glow{
    animation:moaWindowGlow 2.4s ease-in-out infinite alternate;
  }
  @keyframes moaWindowGlow{
    from{opacity:0}
    to  {opacity:.8}
  }

  /* ---- bolts & flames ----
     animated element is an inner <use> with NO transform attribute
     (positioning lives on a wrapper <g>), so CSS transform is safe.
     Entrance = transition on .show; idle = infinite keyframe loop that must
     keep running until the toggle flips (per design notes). The idle animation
     is applied on a delay equal to the entrance so the two don't fight. */
  .moa-module .bolt,.moa-module .flame{
    opacity:0;
    transform:scale(.2);
    transform-box:fill-box;
    transition:opacity .3s,transform .35s cubic-bezier(.4,1.8,.5,1);
  }
  .moa-module .bolt{transform-origin:center}
  .moa-module .flame{transform-origin:50% 100%}   /* flames grow from their base */
  .moa-module .bolt.show,.moa-module .flame.show{opacity:1;transform:scale(1)}

  /* idle loops. --dur/--delay are randomized per element in JS so the bolts
     pulse at different speeds rather than in lockstep. */
  .moa-module .bolt.idle{
    animation:moaBoltPulse var(--dur,1.4s) ease-in-out var(--delay,0s) infinite alternate;
  }
  @keyframes moaBoltPulse{
    from{transform:scale(.82);opacity:.55}
    to  {transform:scale(1.15);opacity:1}
  }
  .moa-module .flame.idle{
    animation:moaFlameSway var(--dur,1.6s) ease-in-out var(--delay,0s) infinite alternate;
  }
  @keyframes moaFlameSway{
    from{transform:scale(.94,1) rotate(-3.5deg);opacity:.62}
    to  {transform:scale(1.06,.95) rotate(3.5deg);opacity:1}
  }
  @media (prefers-reduced-motion:reduce){
    .moa-module .bolt.idle,.moa-module .flame.idle{animation:none}
    /* the pulse is gone; the pale conduit is a static colour, so .flow-dim
       stays cream here too -- an earlier override to saturated yellow predates
       the 2026-08 round, whose stuck frame IS the pale-cream vein */
    .moa-module .lines path.line-flow.on{animation:none;opacity:0}
    /* hold the window glow at its lit end rather than pulsing */
    .moa-module .moa.hot .window-glow{animation:none;opacity:.8}
  }

  /* ============ dark variant (What is LISRAYA? page) ============
     The illustration itself carries NO background: the CMS section supplies
     cream on the Living with DM page and dark blue here, so one asset serves
     both. What the variant does change is everything that was tuned to read
     against cream and would disappear against #003d74.

     THIS SECTION MUST STAY BELOW THE BASE ILLUSTRATION RULES. Several of the
     overrides tie their base rule on specificity now that the base is scoped
     under .moa-module (.sw-label, .sw .knob, #panel are all exact ties), so
     source order is what makes the dark values win. An earlier revision had
     this block ABOVE the switch rules and the dark page shipped with
     --blue-dark ON/OFF labels invisible on the navy card (2026-09-01 QA).

     Values sampled from the [D] What is LISRAYA? prototype, not invented:
       card ground   #003d74
       panel outline #2e79b3  (lighter than --blue-mid, which vanishes here)
       ON/OFF labels white    (they are --blue-dark on the cream page)
       blob fill     #e6f0f9  (unchanged -- already the gradBlue top stop)
       stuck veins   #fef1c9  (unchanged -- already --cream-pipe)
     The blob, towers, house and switch art are otherwise identical, which is
     why this is a palette override and not a second illustration.

     The navy is declared HERE, on .moa-card, rather than inherited from the
     page. The GreenShift container behind it is a 20%-opacity gradient
     (linear-gradient(135deg, rgba(0,61,116,.2), rgba(1,30,69,.2))) sitting on a
     #00529b section, so what actually shows through is a lighter, non-uniform
     blue -- NOT #003d74, and the illustration washes out against it. Owning the
     backdrop also keeps the art readable if the section is ever re-themed. */
  .moa-module--dark{
    background:transparent;
    box-shadow:none;
    padding:0;
  }
  .moa-module--dark .moa-card{
    background:#003d74;
    padding:18px 10px 8px;
    border-radius:12px;
  }
  .moa-module--dark .moa-toggle-row{color:#fff}
  /* the panel is an outline only -- the card's navy shows through it */
  .moa-module--dark #moa-panel{fill:none;stroke:#2e79b3}
  .moa-module--dark .sw-label{fill:#ffffff}
  /* OFF reads as pale ice against the navy, where --blue-mid would disappear
     into it. The ON rule is restated for clarity, not necessity: the base
     .moa-module .sw.on .knob is (0,4,0) and already outranks the (0,3,0) OFF
     rule below -- but leaving ON implicit here would be a trap for the next
     editor, who could raise the OFF rule's specificity and silently kill it. */
  .moa-module--dark .sw .knob{fill:#e6f0f9}
  .moa-module--dark .sw.on .knob{fill:var(--orange)}

  /* ============ surviving the switcher's display:none ============
     The GreenShift editor only allows blocks INSIDE the two locked panes, so in
     practice the illustration lives in .switcher-panel-1. The block hides the
     inactive pane with `display:none !important`, which would take the
     illustration with it -- and a display:none subtree also stops
     IntersectionObserver firing and makes getBBox() return zeros, which the
     line-draw animation depends on.

     So: force the pane that CONTAINS the illustration to stay laid out, and
     hide only its sibling content instead. `!important` is required to beat the
     block's own !important. Scoped with :has(#moa-mount) so the OTHER pane
     still hides normally and exactly one text card shows at a time.

     NB these selectors are deliberately NOT scoped under .moa-module: they
     target the switcher's own containers, which sit outside it. Inert on this
     demo page (no .gspb elements here). */
  .gspb__switcher-content .switcher-panel:has(#moa-mount){
    display:block !important;
  }
  .gspb-switchwrap:has(.gspb-switchbox-checkbox:checked)
    .switcher-panel-1:has(#moa-mount) > *:not(#moa-mount){
    display:none !important;
  }
  .gspb-switchwrap:has(.gspb-switchbox-checkbox:not(:checked))
    .switcher-panel-2:has(#moa-mount) > *:not(#moa-mount){
    display:none !important;
  }

  /* ============ small screens (2026-09 round) ============
     Designer: "increase size of the animated illustration; roughly 20-40px
     padding across all sides". The SVG is HEIGHT-constrained inside the 1:1
     square (toggle row + gap + paddings are subtracted before it gets any
     height), so the real size lever is the vertical padding and gap; the
     horizontal trim lands the SVG box's side inset at 12+8 = 20px.
     box-sizing:border-box (scoped rule above) and the min-height:0 chain are
     untouched -- only widths/paddings/gap change, so the square still holds.
     NB the art's own aspect (viewBox 332x372) always leaves the horizontal
     breathing ~5% of module width wider than the vertical -- inherent to the
     1:1 square, not a padding bug. */
  @media (max-width:560px){
    .moa-module{width:100%;max-width:100%;padding:0px;gap:12px}
    .moa-card{width:100%;padding:10px 0px}
    /* The CMS wrappers around the switcher already inset the module ~25px per
       side on phones (GreenShift row 15px + column 10px); the innermost
       container adds another 20px that starves the illustration. Reclaim it
       here (scoped to the switcher that holds the mount, like the
       display:none-survival rules below) so the module gets the remaining
       width; the vertical rhythm stays the CMS's. Inert on this demo page. */
    /* .gspb_container:has(> .gspb-switchwrap #moa-mount){padding-left:0;padding-right:0} */
    /* dark page: the navy card IS the visual frame -- the module stays flush
       (restated because the generic rules above would otherwise win the
       cascade over the earlier equal-specificity --dark rules). */
    .moa-module--dark{padding:0}
    .moa-module--dark .moa-card{padding:12px 0px}
  }
