/* ============================================================================
   THE TALKING ROVER - the shared stylesheet.  2026-09-09.

   Bryan: the other sites "are currently only spitting back the option to text
   anything, but it's not changing from gold to blue like the Royal Standard."
   This is what makes the ball go gold to blue, the word morph between TALK and
   TEXT, and the transcript open above the bar.

   EVERY RULE HERE IS COPIED, NOT WRITTEN. The source is the page that already
   runs the engine - royalmortgage.net/index.html, itself generated from the
   royalai.app front door. Rules were lifted whole by byte span, including the
   ones nested inside @media, so none of them has been retyped or reworded.
   Load it AFTER the site's own stylesheets: the ask bar becomes the engine's
   bar, and these rules have to win.
   ============================================================================ */
@keyframes rs-think{0%,100%{opacity:.72}50%{opacity:1}}
@keyframes rs-think{0%,100%{opacity:.62}50%{opacity:.88}}
@keyframes rs-listen-glow{0%,100%{filter:brightness(.98)}50%{filter:brightness(1.08)}}
@keyframes rs-spinslam{
    0%   {transform:translateY(-50%) rotate(0deg)   scale(var(--rs-slam-from,2))}
    62%  {transform:translateY(-50%) rotate(560deg) scale(1.16)}
    88%  {transform:translateY(-50%) rotate(720deg) scale(.86)}
    100% {transform:translateY(-50%) rotate(720deg) scale(1)}
  }
@keyframes rs-quake{
    0%   {transform:translate(0,0)}
    18%  {transform:translate(0,3px)}
    38%  {transform:translate(-2px,-2px)}
    58%  {transform:translate(2px,1px)}
    78%  {transform:translate(-1px,0)}
    100% {transform:translate(0,0)}
  }
@keyframes rs-slamonly{
    0%   {transform:translateY(-50%) scale(var(--rs-slam-from,2))}
    70%  {transform:translateY(-50%) scale(.92)}
    100% {transform:translateY(-50%) scale(1)}
  }
@keyframes rai-working{to{transform:rotate(360deg)}}
@keyframes rs-arcin{to{opacity:1}}
@keyframes rv-float{
    0%,100%{transform:translateY(calc(-50% - 4px))}
    50%    {transform:translateY(calc(-50% + 5px))}
  }
@keyframes rf-land{
      0%  {opacity:0;transform:translateY(-10px) scale(.86)}
      70% {opacity:1;transform:translateY(4px) scale(1.02)}
      100%{opacity:1;transform:translateY(0) scale(1)}
    }
@keyframes rf-leave{to{opacity:0;transform:translateY(8px) scale(.94)}}
/* =====================================================================
     THE CONVERSATION  ·  Part 4A, 7/31/2026
     The chat lives BETWEEN the tagline and the ask bar. It is display:none
     until the visitor's own tap — no keyframe, no rise, no arrival on
     arrival — and once open it stays. The transcript scrolls inside its
     own box (in-flow; the page never scrolls sideways) so the visitor can
     read the last few paragraphs and scroll up for history. It is fine
     that the emblem scrolls away once the conversation grows.

     BIG TYPE ON PURPOSE, carried over from the 7/29 ruling: many
     customers are older. 16.5-19px on 1.75 leading, full --ink.
     ===================================================================== */
  .chat{display:none;width:var(--fd-col);max-width:100%;margin:0 0 14px;text-align:left}
.chat.open{display:block}
.chatlog{max-height:min(52vh,460px);overflow-y:auto;overflow-x:hidden;
    border:1px solid var(--line);border-radius:18px;background:var(--card);
    box-shadow:0 10px 30px rgba(21,43,107,.08);padding:16px 18px;
    font-size:clamp(16.5px,2.7vw,19px);line-height:1.75;color:var(--ink)}
.chatlog .msg{margin:0 0 12px;white-space:pre-wrap;overflow-wrap:break-word}
.chatlog .msg:last-child{margin-bottom:0}
.chatlog .msg.u{background:#F2F4FA;border-radius:14px;padding:10px 14px;
    margin-left:14%;color:var(--navy);font-weight:600}
.chatlog .msg.a{color:var(--ink)}
/* =====================================================================
     FOLLOW THE WORDS  ·  2026-08-05, Bryan's functionality ruling
     ---------------------------------------------------------------------
     His words: "THE BIG COMPLAINT IS NOT BEING ABLE TO READ AND FOLLOW THE
     WORDS THAT CLEARLY ON THE SCREEN... EMPHASIZING THE WORDS THAT ARE
     COMING OUT AS ROVER IS SPEAKING. THIS IS OUR HIGHEST IMPORTANCE ON
     FUNCTIONALITY."

     So while he speaks, the word he is ON is lit, and everything he has
     already said stays at full strength behind it. Nobody has to hunt for
     their place in a paragraph, which is the actual complaint — especially
     for the older customers this page is sized for in the first place.

     WHY THE WEIGHT DOES NOT CHANGE. Bolding the live word reflows the line,
     and a line that reflows while you are reading it is worse than no
     emphasis at all. The lit word gets a GOLD UNDERLAY and navy ink instead:
     zero layout movement, and it reads at arm's length.
     THE ROYAL STANDARD: the answer is navy ink (the object); the gold is on
     exactly one word at a time (the point). Gold never carries a letterform
     here, so Rule 3 holds — drop the gold and every word still reads.
     ===================================================================== */
  .chatlog .msg.a .rv-w{border-radius:5px;padding:1px 2px;margin:0 -2px;
    transition:background-color .12s linear,color .12s linear}
/* NO font-weight HERE, ON PURPOSE. Bolding the live word re-measures the
     glyphs and shoves the rest of the paragraph sideways on every single
     word — the one thing worse than no emphasis. Colour and background cost
     nothing in layout, so the line never moves. */
  .chatlog .msg.a .rv-w.rv-now{background:rgba(203,162,96,.42);color:var(--navy)}
.chatlog .msg.a .rv-w.rv-said{color:var(--ink)}
@media (prefers-reduced-motion:reduce){
.chatlog .msg.a .rv-w{transition:none}
}
/* THE READING SURFACE, ENLARGED. Same box, more room and more air, because
     the answer is the product. Kept inside the existing clamp shape so the
     phone layout is unchanged in kind, only in size. */
  .chatlog{font-size:clamp(17.5px,2.9vw,20.5px);line-height:1.8}
.chatlog .msg{margin:0 0 16px}
.chatlog .msg.note{font-size:12.5px;line-height:1.6;color:var(--muted)}
/* =====================================================================
     THE WAIT, MADE HONEST  ·  2026-08-02, RELEASE ORDER G

     THE STATUS LINE gets its own class instead of borrowing .note, because
     for the first several seconds after a send it is the ONLY thing on the
     screen that is moving and it carries the whole promise that the page
     has not frozen. A 12.5px grey aside was not carrying that.

     COLOUR IS MEASURED, NOT EYEBALLED. --muted #5C6178 on --card #FFFFFF is
     6.11:1 by the WCAG relative-luminance formula, so it clears 4.5:1 with
     room to spare and stays --muted; the line earns its weight from size
     and from the gold rule at its left, never from a colour that would
     have to be checked again. The rule is the filed gold and it is a
     4px bar of it, which is the one place gold is allowed to be the
     quiet part: the navy type is still the object.
     ===================================================================== */
  .chatlog .msg.status{font-size:14px;line-height:1.65;color:var(--muted);
    border-left:4px solid var(--gold);padding-left:12px}
/* THE RETENTION CHOICE — customer choice, four options, shown once after
     the first exchange. Plain buttons, no animation.
     THE RETRY ROW (2026-08-02, G-3) rides the identical pill so a visitor
     who has already met one of these rows meets no new vocabulary. */
  .chatlog .keep,
  .chatlog .retry{display:flex;flex-wrap:wrap;gap:8px;margin:2px 0 12px}
/* THE BORDER IS NAVY, NOT GOLD, and that is an accessibility fix rather
     than a design change (2026-08-02). The pill's only visual boundary was a
     1.5px gold hairline on white, measured at 2.37:1 — under WCAG 2.2's 3:1
     floor for the boundary of a UI component (1.4.11). Gold on white is
     emphasis and never structure, which is the Royal Standard's own rule, and
     a control's edge is structure. The gold stays on the surface as the inner
     ring, so the pill still reads Royal and now also reads as a control. */
  .chatlog .keep button,
  .chatlog .retry button{font:600 13px/1.2 inherit;font-family:inherit;cursor:pointer;
    color:var(--navy);background:var(--card);border:1.5px solid var(--navy);
    box-shadow:inset 0 0 0 1.5px var(--gold);
    border-radius:999px;padding:9px 14px;min-height:40px}
.chatlog .keep button:focus-visible,
  .chatlog .retry button:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
.chatrow{display:flex;justify-content:flex-end;margin-top:6px}
.newconvo{display:none;background:none;border:none;cursor:pointer;
    font:600 12.5px/1.2 inherit;font-family:inherit;color:var(--muted);
    text-decoration:underline;padding:6px 2px}
.chat.haslog .newconvo{display:inline-block}
.newconvo:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
/* =====================================================================
     THE STATE BALL  ·  THE BLUE SONNY ORDER, 8/3/2026, ruled by Bryan
     (rule -1000, AMENDED — not repealed)

     BRYAN, VERBATIM: "I don't think we should have an R on Sonny. It's
     overkill. My final answer for Sonny is that his ROVER state is the Blue
     Sphere. When he is awake, he will turn from gold to blue... Sonny will
     never be able to function unless he is blue... When Sonny starts
     talking, he will again transform from Blue Sonny back to original
     Sonny. This time, Sonny will be bigger... He will also breathe when he
     speaks."

     ---------------------------------------------------------------------
     WHAT THIS BLOCK SUPERSEDES, KEPT VERBATIM PER THE NEVER-DELETE LAW.
     Full copies at _superseded/2026-08-03-blue-sonny/.
     ---------------------------------------------------------------------
     (1) THE AWAKE/ASLEEP LAW as written 7/31/2026:

         "THE STATE BALL · THE AWAKE/ASLEEP LAW (rule -1000), 7/31/2026
          The ball IS the state indicator, and it is the ask bar's own send
          button — one Sonny on the page, never two (the old fixed
          bottom-right orb is retired below; a fixed element was banned from
          this page anyway). ASLEEP: the plain gold sphere — the bare orb
          material front-door.css already paints, untouched. AWAKE: the
          chrome R forms on its face (assets/coin_chromeR.png, the filed app
          mark — never redrawn). The face appears ONLY when the engine has
          actually proven it can answer — headers/first token or a live
          /health 200 — never before, so the awake state can never lie. No
          keyframe on the morph: the R is simply there once he is ready,
          deliberately a beat late while the engine assesses."

           .searchbox .rface{display:none;width:72%;height:72%;object-fit:contain}
.searchbox button.awake .rface{display:block}
AMENDED, NOT REPEALED: the ball is still the one state indicator and
         there is still exactly one Sonny on the page. What changed is what
         carries AWAKE. It is no longer the chrome R — the R is off Sonny
         entirely — it is THE BLUE MATERIAL.

     (2) The 8/2/2026 ruling that BLUE = SPEAKING is INVERTED as of 8/3.
         Blue is AWAKE AND WORKING. SPEAKING is GOLD AND LARGE. Its own
         block, kept verbatim, is a few rules further down this stylesheet.

     ---------------------------------------------------------------------
     TRAP 6 · THE AWAKE STATE CAN NOW LIE, AND HERE IS WHERE THE HONESTY WENT
     ---------------------------------------------------------------------
     The old law could never lie because the R waited for a /health 200 or a
     first streamed token. Blue now arrives on the visitor's own touch,
     BEFORE the engine has proven anything, because Bryan ruled that it does.
     THE R IS NOT QUIETLY KEPT TO PRESERVE THAT CLAUSE. Honesty moved into
     the words instead, and those words must keep firing unchanged:
       · OFFLINE  — "The live assistant is not answering yet, so nothing you
         typed left this device."
       · the six staged status lines, each true of the moment it is spoken.
     BLUE MEANS "SONNY IS ENGAGED". IT HAS NEVER MEANT "THE ENGINE IS
     HEALTHY". Do not re-derive this; it was decided here.

     ---------------------------------------------------------------------
     THE FIVE STATES. One element. It never becomes a second element, it
     never leaves the bar, and there is never a second sphere (TWO-SONNYS).
     ---------------------------------------------------------------------
       0 AT REST   gold gradient + navy magnifier   44px       still
       1 AWAKE     blue sphere, bare                44px       still
       2 THINKING  blue sphere, DIMMED              44px       breathes .62<->.88
       3 LISTENING blue sphere, BARE                48.4->54.6 PULSATES with YOUR voice
       4 SPEAKING  gold gradient + NAVY rim         88px       breathes with HIS voice
     Then back to 1. HE NEVER RETURNS TO STATE 0 IN THE SAME VISIT (B-1).
     Small gold means one thing only: he has not been touched yet.

     ---------------------------------------------------------------------
     STATES 2 AND 3 AMENDED 2026-08-03 BY BRYAN'S ROVER-BRAIN RULING.
     Verbatim: "HE GOES FROM GOLD TO BLUE WHEN CALLD UPON, TURNS LIGHT BLUE
     WHEN THINKING ABOUT SENDING TEXT AND READING, BUT HE ONCE AGAIN TURNS
     GOLD BUT GETS BIGGER WHEN HE BEGINS TO SPEAK... THINKING ROVER TURNS
     DIM AFTER HE IS BLUE. TALKING SONNY WILL GROW IN SIZE AND MORPH ONCE
     AGAIN FROM BLUE TO GOLD AND SPEAKING AND LISTENING SONNY WILL PULSATE.
     IF YOU THINK WE CAN PULSATE IN THIS BUILD, GO FOR IT."

     TWO CHANGES, AND ONLY TWO. States 0, 1 and 4 were already exactly what
     he ruled and are untouched.
       · STATE 2 was `.72<->1`, which returned to FULL awake brightness at
         every peak — so at any given instant thinking could be pixel-
         identical to awake. He said thinking TURNS DIM. The breath is now
         capped below awake, so it is dimmer at every instant of the cycle
         and never merely dimmer on average.
       · STATE 3 was carried by a SIBLING RING and the ball itself was
         explicitly pinned still. He said LISTENING SONNY PULSATES. The
         pulse moved onto SONNY. The ring is RETIRED, not deleted — see the
         L-13 block further down this stylesheet and
         _superseded/2026-08-03-rover-brain-listening-pulse/.
     ---------------------------------------------------------------------

     TRAP 3 · TWO GOLDS, TWO MEANINGS. Small gold is asleep, big gold is
     speaking, and the only differences are size and sound. That is why the
     speaking scale is 2.0x and not a polite 1.3x: at 1.3x a glancing
     visitor cannot tell the two golds apart, and under reduced motion there
     is no breathing left to disambiguate them. Do not shrink this number to
     be tasteful.

     THE SIZE, MEASURED. The order specifies 40px -> 80px from
     front-door.css:168. That rule is overridden ONE LINE BELOW and has been
     since the awake/asleep law shipped: this button is 44px, because 44px is
     the WCAG 2.5.8 target floor and PART 8 gate 10 requires it in every
     state. So the real numbers are 44px -> 88px at the ruled 2.0x, against a
     56px-tall bar. The ball breaks the bar's silhouette by even more than
     the order predicted, which is the entire point of the ruling.
     ===================================================================== */
  .searchbox button[type="submit"]{width:44px;height:44px;min-height:44px}
/* ---------------------------------------------------------------------
     THE TRANSFORM TRAP. front-door.css:164 centres this button with
     `transform: translateY(-50%)`. A bare `transform: scale(2)` written
     here would DELETE that centring and throw the ball out of the search
     bar. Every transform on #sonnyball is therefore COMPOSED, and the scale
     is carried by custom properties so that one rAF loop writing a single
     number can drive it.

     THE SPEED CONTRACT: transform, opacity, and custom properties that feed
     those two. NOTHING ELSE. No width, no height, no box-shadow, no filter,
     no background-position — every one of those forces layout or paint on
     every frame and would stutter an 88px ball on the exact phone this gets
     demonstrated on.

     --lvl is 0..1 and is written by js/voice.js onto #ask (the shared
     parent) rather than onto the ball, because the listening ring and the
     speaking rim are SIBLINGS of the ball and a custom property set on the
     ball could never reach them. One write per frame, three readers.
     --------------------------------------------------------------------- */
  #sonnyball{
    --rs-base:1;                    /* 1 at rest/awake/thinking; 2 for audio */
    /* LITERAL 1 AT REST, AWAKE AND THINKING. --lvl reaches the ball's scale
       in exactly TWO states as of 2026-08-03 — .listening and .speaking —
       and each writes its own formula below. Everywhere else this stays a
       literal 1 so a stale --lvl can never twitch a ball that is meant to
       be still.
       RETIRED 2026-08-03: the clause that pinned LISTENING still -> replaced
       by the listening pulse below. Verbatim copy at
       _superseded/2026-08-03-rover-brain-listening-pulse/. Reason: Bryan's
       ROVER-BRAIN ruling, "SPEAKING AND LISTENING SONNY WILL PULSATE."
         was: "DELIBERATELY LITERAL 1, NOT WIRED TO --lvl. State 3 says the
          ball does not change size while the mic is open — only the ring
          reacts to your voice. --lvl is live on this element the whole time
          the mic is open (it is inherited from #ask so the sibling ring can
          read it), so if these two were wired up here the ball would twitch
          along with the listening ring. --lvl reaches the ball's scale in
          exactly one state, and .speaking below is where that happens." */
    --rs-x:1;
    --rs-y:1;
    transform:translateY(-50%) scale(var(--rs-x),var(--rs-y));
    transition:transform .26s ease-out,opacity .22s cubic-bezier(.4,0,.2,1)}
/* THE RESTING GLYPH fades out on the 0 -> 1 curve and never returns —
     B-4 chose the navy breathing rim over a counter-scaled glyph for the
     speaking state, so nothing brings it back inside a visit. */
  #sonnyball svg{position:relative;z-index:1;
    transition:opacity .22s cubic-bezier(.4,0,.2,1)}
#sonnyball.awake svg{opacity:0}
/* ---------------------------------------------------------------------
     BLUE SONNY · THE MATERIAL

     HE IS NOT A NEW BLUE. The centre pixel of the filed cut
     assets/sonny_blue_128.png measures #283366; brand navy is #253069. They
     are the same colour. Blue Sonny is THE ROYAL NAVY RENDERED AS GLASS,
     and that is exactly why he is easy on the eyes — he is already the
     colour the whole brand is built on.

     WHY CSS AND NOT THE PNG. The PNG is 128x128. At 44px on a 3x phone that
     is 132 needed against 128 available: zero headroom, and it cannot be
     "the best Blue Sphere we have ever created" at 88px. A gradient is
     resolution-free, so it is pixel-perfect at 44px, 88px and 176px for
     nothing. sonny_blue_128.png STAYS ON DISK as the reference master and
     is still precached by sw.js; it is simply no longer painted here.

     TRAP 4 · `display:grid; place-items:center` (front-door.css:169) would
     make a static ::before a GRID ITEM and centre it as a zero-size box
     instead of filling the ball. It is absolutely positioned, inset:0,
     against the button — which front-door.css already positions.
     TRAP 5 · ::after is taken by the working arc, so the skin is ::before.
     No DOM node is added to the button for any of this.
     --------------------------------------------------------------------- */
  /* MEASURED, NOT INVENTED, AND MEASURED TWICE. Every number below was
     solved against assets/sonny_blue_128.png by least squares and then
     re-rendered and diffed. The order's own one-layer first cut
       radial-gradient(circle at 32% 26%,#C6CAD8 0%,#6E78A6 9%,#394476 22%,
                       #1C254F 45%,#222337 70%,#0E1024 100%)
     measures 9.927/255 mean per-channel error and FAILS its own gate 1.
     It fails for a structural reason, not a tuning one: the filed sphere is
     NOT radially symmetric about any single point. The best possible
     single radial gradient about 32%/26% floors at 8.848/255, because the
     sphere carries a RIM LIGHT that follows its OWN edge rather than the
     light axis. Two layers is what the order actually calls for — "build
     him as layered CSS, matching how gold is built" — and two layers
     measure 5.013/255. PASS.

     LAYER 1 (bottom) is THE LIGHT: circle at 32% 26%, the gold orb's own
     light source, so the two spheres are lit from the same window.
     LAYER 2 (top) is THE RIM, centred on the sphere itself.

     WHY `ellipse 62.26% 62.26%` AND NOT `circle 79.69px`. CSS will not take
     a percentage radius on `circle`, and a px radius would be frozen at one
     size — wrong the moment the ball scales to 88px. Two equal percentages
     of a square box IS a circle, and it scales. That is the whole reason.

     Verified: no pixel inside the ball ever reaches past 85.6% of the top
     layer's radius, so stops beyond 86% were dropped after proving they
     changed 0 pixels. Re-rasterise before touching any of it. */
  #sonnyball::before{content:"";position:absolute;inset:0;border-radius:50%;
    pointer-events:none;opacity:0;
    background:url('/assets/sonny_blue_128.png') center/100% 100% no-repeat;
    transition:opacity .26s ease-out}
/* 0 -> 1 · THE ORIGINAL TRANSFORMATION. 220ms crossfade, and .awake is
     never removed once set. */
  #sonnyball.awake{background:#111936}
#sonnyball.awake::before{opacity:1;transition:opacity .22s cubic-bezier(.4,0,.2,1)}
/* 2 -> 4 · blue leaves over 180ms while the ball grows; 4 -> 1 · blue
     returns over the 260ms ease-out on the base rule above. A transition
     always comes from the state being moved TO, so the two directions get
     their two different curves for free. */
  #sonnyball.listening::before,
  #sonnyball.speaking::before{opacity:0;transition:opacity .18s cubic-bezier(.4,0,.2,1)}
#sonnyball.awake[aria-busy="true"]{animation:rs-think 1.6s ease-in-out infinite}
/* ---------------------------------------------------------------------
     STATE 3 · LISTENING. SONNY HIMSELF PULSATES.  2026-08-03, ROVER BRAIN.

     BRYAN, VERBATIM: "SPEAKING AND LISTENING SONNY WILL PULSATE. IF YOU
     THINK WE CAN PULSATE IN THIS BUILD, GO FOR IT. I WILL NEVER STOP
     PROGRESS."

     WHAT THIS REPLACES. Until now the listening signal was L-13's SIBLING
     RING and the ball was explicitly held still. The ring is retired (see
     the L-13 block below — retired, never deleted, and it comes back with
     one line in js/voice.js). Bryan's ruling describes colour, size and
     pulse. So the pulse is on SONNY.

     THE NUMBERS, AND WHY THEY ARE NOT THE RING'S NUMBERS. The ring ran
     1.12 + .28*lvl. Moving that amplitude onto the ball would reach 1.40
     = 61.6px inside a 56px bar, which makes LISTENING break the bar's
     silhouette — and breaking the silhouette is state 4's entire job
     (TRAP 3). So listening is deliberately quieter: it rests at 1.10 and
     peaks at 1.24, i.e. 48.4px -> 54.6px, visibly alive and still INSIDE
     the 56px bar. Nobody can confuse it with the 88px speaking ball.

     UNIFORM, NOT SQUASH-AND-STRETCH. State 4's x/y move in OPPOSITE
     directions because that reads as a mouth. Listening is a uniform
     breath because he is TAKING IN, not putting out. Two different
     motions for two different jobs, from the same one --lvl bus.

     .07s LINEAR FROM THE FIRST FRAME. There is no .rs-live two-step here
     and there does not need to be: the entry step is only 1.00 -> 1.10, so
     unlike state 4's 320ms overshoot there is no landing for a per-frame
     --lvl write to retarget. The ball answers the finger immediately.
     Coming OUT, .listening comes off and the base rule's 260ms ease-out
     carries him back to a still blue sphere.

     A LISTENING BALL IS NOT DIMMED. He can be aria-busy while the mic is
     open (a held mic over a still-streaming answer), and a thinking breath
     fighting a listening pulse on the same 44px object is noise. Listening
     is the state the visitor is CAUSING, so it wins the ball outright —
     the same call state 4 already makes, one rule further down.
     --------------------------------------------------------------------- */
  /* FINAL 1.1 OWNER LAW, 2026-08-03: LISTENING and SPEAKING share one
     canonical LARGE-GOLD material. Listening is a circular intake pulse;
     speaking is the stronger speech-shaped response below. No ring is a
     state signal. Earlier small-blue listening measurements remain above
     as the never-delete record and are superseded by this rule. */
  #sonnyball.listening,
  #sonnyball.listening[aria-busy="true"]{--rs-base:2;animation:none;opacity:1;
    --rs-x:calc(var(--rs-base) * (1 + (.030 * var(--lvl,0))));
    --rs-y:calc(var(--rs-base) * (1 + (.030 * var(--lvl,0))));
    transition:transform .07s linear}
/* ---------------------------------------------------------------------
     STATE 4 · SPEAKING. Gold again — and twice the size. Because it is a
     gradient and not a bitmap it is pixel-perfect at 88px for free, so the
     big ball costs nothing.

     ~~~ THE SUN LIFT · 2026-08-03, BRYAN'S SECOND RULING ON THIS STATE ~~~
     "Make large Sonny a touch brighter than sleeping Sonny, while also
      keeping him a derivative of the same beautiful gold that home base
      Sonny is on... It must be a FULL COLORED SPHERE and bright like a
      golden sun, but it should not leave home base of sleeping Sonny very
      much, only a touch brighter but of course much bigger."

     THE PIGMENT IS NOT RE-MIXED. front-door.css:161 carries a standing order
     on this material — "the coin's own material, the same light source, at
     orb scale. Never re-mix it." That order is obeyed. Each stop was moved
     in CIELCh by one reproducible operation:

         hue    HELD EXACTLY  ..... 0.00° drift on every stop, by construction
         L*     +2.5 ............... a touch brighter
         C*     x1.15 .............. FULL COLOURED, never washed out

     Mean CIEDE2000 from home base is 2.21 — a just-noticeable step. Side by
     side you can see it; alone nobody would call it a different gold. That
     is exactly "does not leave home base very much".

     THE SPECULAR IS PINNED AND IS NOT LIFTED. The 0% stop stays #fdf6e3,
     byte-identical to sleeping Sonny. Two reasons, and the first is the
     ruling itself: a plain "turn the light up" gain was built first and
     REJECTED, because in linear light that near-white highlight blows toward
     #FFFFFF and its chroma collapses 10.0 -> 2.8. The sphere came out PALER,
     which is the opposite of a full coloured sphere. The second reason is
     physics: a specular highlight is the light source's own reflection, so
     it does not brighten when you light the object more — the diffuse body
     does. Lifting only the body is both the correct look and the correct
     model. DO NOT "FINISH THE JOB" BY LIFTING STOP 0.

     SLEEPING SONNY IS UNTOUCHED. This override lives on .speaking and
     nowhere else, so state 0 still paints the exact front-door.css material.
     Two golds, and now three things separate them — size, sound, and a
     measured 2.21 of lift. TRAP 3 gets stronger, never weaker.
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     WAS, and it is one line to revert:
       (no background override — .speaking inherited front-door.css:170
        radial-gradient(circle at 32% 26%,#fdf6e3 0%,#f0d9a6 22%,#d5b271 45%,
                        #ab854a 70%,#7d6238 100%))

     The 2.0x lands with a slight overshoot (cubic-bezier(.34,1.4,.64,1)) so
     it reads like a breath in rather than a zoom. `.rs-live` is set 330ms
     later, once that landing is finished, and swaps the transition to a
     short linear one so the per-frame breathing is not smeared by a 320ms
     ease. On the way down there is no overshoot: both classes come off and
     the base rule's 260ms ease-out carries him back to blue.

     A SPEAKING BALL IS NEVER DIMMED. He may still be aria-busy while he
     talks, and dimming the state that carries the meaning would read as a
     fault, so the thinking breath is switched off here. The second selector
     exists purely to out-specify #sonnyball.awake[aria-busy="true"] above.
     --------------------------------------------------------------------- */
  #sonnyball.listening,
  #sonnyball.listening[aria-busy="true"],
  #sonnyball.speaking,
  #sonnyball.speaking[aria-busy="true"]{--rs-base:2;animation:none;opacity:1;
    /* THE SUN LIFT. Same hue, +2.5 L*, x1.15 chroma. Stop 0 is home base,
       byte-identical. Derived, never hand-picked — see the block above. */
    background:radial-gradient(circle at 32% 26%,
      #FDF6E3 0%,#FAE0A5 22%,#E0B86C 45%,#B58B45 70%,#866736 100%);
    /* THE SQUASH AND STRETCH. Bryan asked for "contract and change and
       retract and change... similar to Gemini", and a uniform pulse reads
       as a heartbeat rather than as speech, so x and y move in opposite
       directions. It is kept under 8% on purpose — past that it stops
       looking like a voice and starts looking like a water balloon. */
    --rs-x:calc(var(--rs-base) * (1 + (.060 * var(--lvl,0))));
    --rs-y:calc(var(--rs-base) * (1 - (.040 * var(--lvl,0))));
    transition:transform .32s cubic-bezier(.34,1.4,.64,1),
               opacity .18s cubic-bezier(.4,0,.2,1)}
#sonnyball.speaking.rs-live,
  #sonnyball.speaking.rs-live[aria-busy="true"]{transition:transform .07s linear}
#sonnyball.listening,
  #sonnyball.listening[aria-busy="true"]{
    --rs-x:calc(var(--rs-base) * (1.02 + (.030 * var(--lvl,0))));
    --rs-y:calc(var(--rs-base) * (1.02 + (.030 * var(--lvl,0))));
    animation:rs-listen-glow 1.25s ease-in-out infinite}
/* ===================================================================
     8/8/2026 · THE FOUR STATES MUST BE UNMISTAKABLE — Bryan's ruling.
     ===================================================================
     "The Golden Son must always be frozen when it is not in listening
     mode." And separately: thinking "should also be a different look
     than the look that we see when he is in listening mode," and
     talking "should be a completely different appearance."

     THE DEFECT THIS CLOSES. Voice mode holds `.speaking` between turns
     so the sun stays up (8/5 ruling). That made WAITING and TALKING the
     same class, wearing the same face — the user could not tell whether
     Rover was hearing them, thinking, or answering. Three different
     things, one appearance.

     THE VOCABULARY, and every one of the four is now visually distinct:
       LISTENING  .listening      alive — glow + live amplitude.
                                  THE ONLY STATE WITH MOTION.
       THINKING   .rs-frozen     FROZEN and dimmed. Time stops.
       TALKING    .speaking.rs-live  2x gold, navy rim, lit words.
       ASLEEP     .awake / rest   blue.

     WHY FROZEN IS THE HONEST CHOICE, and it is the whole point: if the
     ball is moving, it is hearing you. That is a promise the page makes
     with motion instead of words. A ball that moves while deaf is a lie
     — the same reasoning that flattened the Android wobble in v94.

     `animation:none` and a fixed transform, not merely a slower one:
     a paused-looking animation still resumes mid-cycle and reads as a
     stutter. Frozen means frozen. */
  #sonnyball.rs-frozen,
  #sonnyball.rs-frozen[aria-busy="true"],
  #sonnyball.speaking.rs-frozen,
  #sonnyball.speaking.rs-frozen[aria-busy="true"]{
    animation:none!important;
    --lvl:0;
    filter:brightness(.90) saturate(.94);
    transition:filter .22s ease-out,transform .26s ease-out}
#sonnyball.rs-spinslam{
    animation:rs-spinslam .62s cubic-bezier(.22,.9,.24,1) 1 both!important;
    transition:none!important}
.searchbox.rs-quake{animation:rs-quake .34s cubic-bezier(.36,.07,.19,.97) 1 both}
@media (prefers-reduced-motion:reduce){
#sonnyball.rs-spinslam{
      animation:rs-slamonly .26s cubic-bezier(.22,.9,.24,1) 1 both!important}
.searchbox.rs-quake{animation:none}
}
/* B-4 · WHAT CARRIES NAVY ON THE BIG GOLD BALL. A navy rim that expands
     and contracts with his voice: it is the Gemini vocabulary Bryan named,
     it IS the shape change he described ("contract and change and retract
     and change"), and it satisfies THE ROYAL STANDARD by itself — navy
     object on a gold field. It is a SIBLING node, never a repaint of the
     ball, and it exists only while he is audibly speaking.

     It is deliberately NOT the listening ring and must never be mistaken
     for the brand ring device: plain stroke, no bevel, no glass, no
     keystone, no gradient. Its box is measured off the ball's own rect
     BEFORE the ball scales, so it can never drift if a future pass
     re-lays-out the bar, and it then rides the same two custom properties.
     Its amplitude is larger than the ball's so the rim visibly breathes
     AGAINST him instead of moving with him as one rigid object. */
  .speakrim{--rr-base:1}
#sonnyball.speaking ~ .speakrim{--rr-base:2;opacity:1;
    transition:transform .32s cubic-bezier(.34,1.4,.64,1),opacity .18s ease-out}
#sonnyball.speaking.rs-live ~ .speakrim{transition:transform .07s linear}
/* ---------------------------------------------------------------------
     REDUCED MOTION · THE MEANING MUST SURVIVE, AND THIS IS NOT OPTIONAL.
     No breathing and no crossfade — states switch instantly. BUT THE
     SPEAKING BALL IS STILL 2.0x AND STILL GOLD, and the navy rim is still
     there, held steady. Size and colour carry the meaning; motion only
     decorates it. A visitor with vestibular sensitivity must still be able
     to tell that Rover is talking.

     2026-08-03, ROVER BRAIN — THE SAME PROMISE, NOW OWED TO TWO MORE
     STATES, and both are held by SIZE AND COLOUR, never by motion:
      · LISTENING holds at a steady 1.10 (48.4px). Bryan's pulse is a
        decoration on top of a size step that is there whether it pulses or
        not, so a reduced-motion visitor still sees a ball that grew when
        the mic opened. A-3 is satisfied three times over even if this
        stylesheet failed entirely: .rovermic.on is GOLD while the mic is
        live, and "Listening." lands in the aria-live log. There is no
        state on this page where audio is open and nothing says so.
      · THINKING holds at .75, not the .88 it used to. .88 is the CEILING
        of the live breath; a reduced-motion visitor gets no breath at all,
        so the one static number they do get has to carry the whole "he is
        dim" meaning on its own. .75 against an awake 1.0 is unmistakable
        and is still far above anything that would read as disabled.
     --------------------------------------------------------------------- */
  @media (prefers-reduced-motion:reduce){
    /* 🐛 DEFECT FOUND AND FIXED 2026-08-03, ROVER BRAIN AUDIT — THIS LIST
       WAS BEING OUT-SPECIFIED AND SILENTLY LOSING.
       `#sonnyball` is (1,0,0). `#sonnyball.speaking` is (1,1,0),
       `#sonnyball.speaking[aria-busy="true"]` is (1,2,0),
       `#sonnyball.speaking ~ .speakrim` is (1,2,0) — and every one of those
       declares its OWN `transition` further up this stylesheet. A more
       specific rule wins regardless of which media query it sits in, so
       `transition:none` here never reached the speaking ball, the rs-live
       ball or the rim: a visitor with prefers-reduced-motion has been
       getting the full 320ms overshoot on an 88px sphere and a 70ms
       tracking transition on the rim this whole time. The scale and the
       colour were correctly pinned; only the MOTION between states leaked,
       which is exactly the part reduced motion exists to remove.
       Every state selector that declares a transition is now named here at
       its own specificity. Nothing else changed: the 2.0x, the gold, the
       navy rim and the 1.10 listening step are all still held. */
    #sonnyball,
    #sonnyball svg,
    #sonnyball::before,
    #sonnyball.awake::before,
    #sonnyball.listening::before,
    #sonnyball.speaking::before,
    #sonnyball.listening,
    #sonnyball.listening[aria-busy="true"],
    #sonnyball.speaking,
    #sonnyball.speaking[aria-busy="true"],
    #sonnyball.speaking.rs-live,
    #sonnyball.speaking.rs-live[aria-busy="true"],
    #sonnyball.speaking ~ .speakrim,
    #sonnyball.speaking.rs-live ~ .speakrim,
    .speakrim{transition:none}
    /* RETIRED 2026-08-03: opacity:.88 -> replaced by .75. Verbatim copy at
       _superseded/2026-08-03-rover-brain-listening-pulse/index-BEFORE.html.
       Reason: the live breath's ceiling moved to .88, so .88 stopped being
       a dim and became the brightest point of thinking.
         was: #sonnyball.awake[aria-busy="true"]{animation:none;opacity:.88} */
    #sonnyball.awake[aria-busy="true"]{animation:none;opacity:.75}
    /* THE LISTENING PULSE IS PINNED OFF IN CSS, not merely left unwritten
       by JS — the same rule state 4 already gets, for the same reason. The
       amplitude bus refuses to start under reduced motion, but a stale
       --lvl from a session that began before the visitor turned it on must
       not be able to leak a pulse into a state meant to be still. THE SIZE
       STEP SURVIVES: he is still 1.10, because size carries the meaning. */
    #sonnyball.listening,
    #sonnyball.listening[aria-busy="true"]{
      --rs-x:var(--rs-base);--rs-y:var(--rs-base)}
    /* THE BREATH IS PINNED OFF IN CSS, not merely left unwritten by JS.
       The amplitude bus already refuses to start under reduced motion, so
       --lvl stays 0 — but "safe because nobody writes it" is a promise, and
       this makes it a rule. A stale --lvl from a session that began before
       the visitor turned reduced motion on can no longer leak a squash into
       a state that is supposed to be perfectly still. The 2.0x and the gold
       are untouched: size and colour carry the meaning. */
    #sonnyball.speaking,
    #sonnyball.speaking[aria-busy="true"]{
      --rs-x:calc(var(--rs-base) * 1.035);--rs-y:calc(var(--rs-base) * .975)}
    .speakrim{transform:scale(calc(var(--rr-base) * 1.10))}
  }
/* =====================================================================
     ROVER'S VOICE  ·  WAVE 4  ·  2026-08-02
     PART TWO, PART A (A-4 the mic, A-5 the teaching line, A-6 consent)
     and L-13 (THE NAVY LISTENING RING, ruled 8/1 by Bryan, option (a)).

     🚨 NOTHING BELOW RENDERS TODAY. js/voice.js ships with ENABLED=false
     and returns before it creates a single node, so none of these
     selectors ever match. This is the styling that lands the day L-1 is
     answered — it is here so the flip stays ONE LINE, per A-7.
     8/2/2026 LATE: L-1 IS ANSWERED — Bryan's talking-rover order. ENABLED
     is now true in js/voice.js and everything below RENDERS. The three
     lines above are the dormant-era record, kept per the never-delete law.
     ===================================================================== */

  /* A-4 · THE MIC. The order offers press-and-hold on the ball, a mic
     glyph inside it, or — "if it is genuinely ambiguous" — a SEPARATE,
     CLEARLY-LABELLED mic target beside the ball, because "a discoverable
     second button beats a hidden gesture every time." It IS ambiguous (no
     usability check with a 68-year-old has been run, and L-13 already took
     the ball out of the indicator job), so this is the order's own stated
     fallback. It is a labelled control, NOT a second Sonny ball, so THE
     TWO-SONNYS LAW is untouched.
     44px hard minimum, reachable one-handed on a 375px screen (A-4/4). */
  .rovermic{display:inline-flex;align-items:center;gap:6px;
    min-width:44px;min-height:44px;padding:0 12px;margin-left:8px;
    border:2px solid var(--navy);border-radius:22px;background:#fff;
    color:var(--navy);font:600 13px/1 inherit;font-family:inherit;
    cursor:pointer;flex:0 0 auto}
/* 8/2/2026 LATE — THE STACKED-MIC FIX, found the night the flip went
     true and never before, because dormant CSS cannot be seen. TWO real
     defects, both from front-door.css's generic `.searchbox button` rule
     (specificity 0,1,1 beats bare `.rovermic` 0,1,0 on every line):
       1. It positions EVERY .searchbox button absolute at right:8px, so
          the mic and the send ball landed on the same spot (both at
          left 890 in the flip-night measurement).
       2. It paints EVERY .searchbox button as the GOLD ORB — radial gold
          gradient, 40px circle — so the mic rendered as a SECOND SONNY
          BALL, which THE TWO-SONNYS LAW bans outright. The .rovermic
          base block above lost every single declaration.
     So the whole mic treatment is restated here at 0,2,0, where it wins.
     The base block above is kept as the record. The input's right padding
     grows so typed words never run under the re-seated mic. */
  .searchbox .rovermic{position:absolute;right:60px;top:50%;
    transform:translateY(-50%);margin-left:0;
    display:inline-flex;align-items:center;gap:6px;
    width:auto;height:44px;min-height:44px;min-width:44px;padding:0 12px;
    border:2px solid var(--navy);border-radius:22px;background:#fff;
    box-shadow:none;color:var(--navy)}
.searchbox .rovermic svg{width:18px;height:18px;fill:none;stroke:currentColor;
    stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.searchbox .rovermic.on{background:var(--gold);border-color:var(--navy)}
/* 8/5/2026 — 148 -> 164. The Talk pill grew when the Bubble R took the
     mark slot (20 -> 30 wide) and the pill went to 44px, so typed words were
     about to run under it. The reserve tracks the button, always. */
  /* 8/5/2026 — ROVER FLOATS OUT OF THE BAR. Bryan: "WE WILL TAKE ROVER AND
     THE WORDS 'TALK' OUTSIDE OF THE SEARCH BAR ENTIRELY - ROVER WILL FLOAT
     IMMEDIATELY TO THE RIGHT OF SONNY/ROVER EGG... AND THEY WILL BOTH FLOAT."
     He is right, and it is not only taste: with the bubble R and the bubble
     word in the bar, the control ran ~126px wide and the input had to reserve
     over 200px for it. On a 375px phone that leaves almost nothing to type
     in. Lifting the control out gives the reserve back to the words, which is
     the same ruling as the bigger reading surface — the words come first.
     So the input reserves the EGG ONLY, and the control is seated just past
     the bar's right edge, which is exactly where the egg is. */
  /* 8/6 — AND BACK IN THE BAR, BY BRYAN'S SAME-NIGHT FINAL: "put that exact
     floating thing in the search bar again. Done. go!" The reserve returns
     with it: egg (60) + the bare R-and-word control (~125) + air. The typed
     words yield the room; the words on the CONTROL are the feature tonight.
       was: .searchbox #q{padding-right:60px}
(the float-out era)
       was: .searchbox #q{padding-right:164px}
(the navy-pill era)  */
  /* 8/8 — THE RESERVE TRACKS THE BUTTON, ALWAYS, and the button just grew.
     Measured at four widths after the art went 34 -> 47/52: the placeholder
     ran 14px UNDER the canvas on a phone and 30px under it on desktop. Text
     under the art is a defect, never a trade. Desktop reserves
     60 (egg) + 166 (canvas) + 8 (air) = 234; the phone canvas is smaller,
     so it reserves 218 and keeps the extra 16px for typing.
       was: .searchbox #q{padding-right:196px}
(the 34/40px-art era) */
  .searchbox #q{padding-right:234px}
@media (max-width:700px){
.searchbox #q{padding-right:218px}
}
`
     you have restored a repealed law.

     Kept verbatim per THE NEVER-DELETE LAW. Full copy at
     _superseded/2026-08-03-blue-sonny/index-BEFORE.html:

       "BLUE SONNY IS SPEAKING ROVER — Bryan, 8/2/2026 late: 'THIS IS THE
        FINAL ANSWER.' While Rover speaks aloud, the send ball wears the
        filed BLUE SONNY sphere (assets/sonny_blue_128.png, the home-base
        cut), bare — NO chrome R in the speaking state, his order. The gold
        material returns the instant he stops. Supersedes the frozen-form
        note and blue-only-on-X for this ONE state; shape never changes.
        Full opacity even while aria-busy: a speaking ball is visibly
        working, and dimming the state that carries the meaning would read
        as a fault."

         .searchbox button.speaking{background:url('/assets/sonny_blue_128.png') center/cover no-repeat}
.searchbox button.speaking .rface{display:none}
.searchbox button.speaking[aria-busy="true"]{opacity:1}
TWO CLAUSES OF IT SURVIVED INTO THE NEW LAW, and they are worth saying
     out loud so nobody re-argues them: the ball wears the sphere BARE with
     no chrome R on it, and a speaking ball is never dimmed. Both are
     carried by #sonnyball.speaking in the state-ball block above.
     ===================================================================== */
  .rovermic svg{width:18px;height:18px;fill:none;stroke:currentColor;
    stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.rovermic:focus-visible{outline:3px solid var(--gold);outline-offset:3px}
/* The pressed state is NAVY-ON-GOLD, which is the Royal Standard's own
     shape: navy is the object, gold is the point — the gold says "this is
     the live one". Never gold-on-gold. */
  .rovermic.on{background:var(--gold);border-color:var(--navy)}
/* A-5 · the one teaching line, in the log the page already has. */
  .chatlog .voicenote{margin:0 0 12px;font-size:13.5px;line-height:1.6;
    color:#5C6178}
/* =====================================================================
     🚨 RETIRED 2026-08-03: THE L-13 LISTENING RING -> replaced by THE
     LISTENING PULSE ON SONNY HIMSELF (see STATE 3 in the state-ball block
     near the top of this stylesheet). Verbatim copy of the whole page at
     _superseded/2026-08-03-rover-brain-listening-pulse/index-BEFORE.html.
     Reason: Bryan's ROVER-BRAIN ruling of 2026-08-03 — "SPEAKING AND
     LISTENING SONNY WILL PULSATE" — describes COLOUR, SIZE and PULSE on
     Sonny. It does not describe a ring. His new ruling wins.

     BOTH ORDERS ARE ON THIS PAGE ON PURPOSE, BECAUSE THEY DISAGREE AND
     BRYAN SHOULD SEE BOTH:
       L-13, 8/1: "Use the navy listening ring around the ball only while
         ROVER is actively listening... a temporary sibling status element,
         not a repaint of the ball."
       ROVER BRAIN, 8/3: "SPEAKING AND LISTENING SONNY WILL PULSATE."
     A pulse IS a repaint of the ball, so the 8/3 ruling supersedes L-13's
     "not a repaint" clause for the listening state. Nothing else about
     L-13 is disturbed, and the ring is not the brand ring either way.

     THE CSS BELOW IS DELIBERATELY LEFT LIVE AND UNTOUCHED. It is inert
     because js/voice.js no longer creates a .listenring node — no selector
     here can ever match, so it renders nothing and costs nothing. It stays
     so that bringing the ring back is genuinely the ONE LINE the never-
     delete law promises: set RING_RETIRED to false in js/voice.js and the
     ring returns fully styled. Deleting these rules would have made that a
     rebuild. If the ring ever does come back ALONGSIDE the pulse, re-read
     the gold-on-blue note in clause 4 first — it is still correct.
     ===================================================================== */
  /* ---------------------------------------------------------------------
     L-13 · THE LISTENING RING. Bryan, 8/1, verbatim:
     "a temporary sibling status element, not a repaint of the ball and not
     a return of the old public-facing ring. It must disappear when ROVER
     is not listening."

     · SIBLING, NEVER A REPAINT — its own node, absolutely positioned
       around the ball. It never touches #sonnyball's art and never adds a
       class that restyles the sphere. ROVER's form is FROZEN (A-3).
     · NOT THE BRAND RING — royal-ai-social.md rules THE RING IS DEAD in
       public, and that governs the BRAND RING DEVICE. This is a transient
       audio-status indicator alive only while a mic is open. They must
       never look alike: a plain stroke, and that is all. No bevel, no
       glass, no keystone, no gradient.
     · IT IS GOLD AS OF 2026-08-03, AND THAT INVERTS L-13 BY ITS OWN STATED
       REASON. The clause it replaces, kept verbatim per the never-delete
       law, read:
           "NAVY ONLY. The ball underneath is gold, so the field already
            satisfies the Royal Standard. Adding gold here would be Trap 1 —
            gold on gold vanishes. NEVER ADD GOLD TO THIS RING."
       Every word of that is still true OF A GOLD BALL. Under the Blue Sonny
       order the ball underneath is BLUE — which is the Royal navy — so a
       navy ring on it does not read as restraint, it disappears completely.
       That was a defect the new law created, and this is the same-pass fix.
       On a navy ball, gold is maximum contrast and Trap 1 is not in play.
       THE RULE THAT ACTUALLY GOVERNS BOTH VERSIONS: the accent is always
       the INVERSE of the ball. Gold ring on the blue ball, navy rim on the
       gold ball, and never the reverse.
       It stays a plain 2px stroke. Nothing else about L-13 changed.
       js/voice.js wakes the ball before it ever opens the mic, so this ring
       can never find itself sitting on gold.
     · pointer-events:none so it can never steal the 44px tap (L-13/5).
     · --lvl is written by voice.js from the live input level, so the ring
       reacts to THEIR voice in real time — the market convention A-3 asks
       us to match without copying anyone's orb.
     --------------------------------------------------------------------- */
  /* left/top/width/height are written by voice.js from the BALL'S OWN measured
     rect when the mic opens — never hard-coded here, so the ring cannot drift
     off-centre if a future pass re-lays-out the ask bar. */
  .listenring{position:absolute;pointer-events:none;
    border-radius:50%;box-sizing:border-box;
    /* 2026-08-03, B-3. WAS: border:2px solid var(--navy) */
    border:2px solid var(--gold);
    transform:scale(calc(1.12 + (var(--lvl,0) * .28)));
    opacity:calc(.55 + (var(--lvl,0) * .45));
    transition:transform .08s linear,opacity .08s linear}
/* =====================================================================
     THE WORKING STATE  ·  2026-08-02, RELEASE ORDER G-2

     WHAT IT WAS. front-door.css:176 is the whole of the old busy state:
     `.searchbox button[aria-busy="true"]{opacity:.55;cursor:default}
`.
     Its own comment says "no spinner, nothing new on screen". A control
     that greys to 55% and grows a default cursor is the exact visual
     vocabulary of DISABLED, and for eight to eleven seconds that is the
     only thing the orb said. Nobody read it as "working". They read it as
     "this button is dead", which is how the page got its reputation.

     WHAT IT IS NOW. The dim is lifted back to 88% — enough to read as
     changed, not enough to read as switched off — and one gold arc turns
     around the orb. That is the difference between a state and an event:
     a dim is a state and could be permanent, a turning arc is an event
     and is self-evidently mid-something.

     IT IS NOT A PROGRESS BAR. The arc turns at a fixed rate and encodes
     NOTHING about how far along the answer is, because nothing on this
     page knows that. It says "still going" and it says nothing else. No
     percentage, no fill, no estimate.

     REDUCED MOTION gets the SAME RING, STANDING STILL — not nothing. A
     visitor who has asked for no animation still has to be able to tell
     that Rover is working, so the ring goes fully gold and holds. The
     staged status line in the log carries the rest for both of them.

     THE SPEED LAW IS UNTOUCHED: this is a ::after on a button that was
     already there, it is not interactive, it is not fixed, and it stands
     in front of nothing.
     ===================================================================== */
  .searchbox button[aria-busy="true"]{opacity:.88;cursor:progress}
.searchbox button[aria-busy="true"]::after{content:"";position:absolute;
    inset:-6px;border-radius:50%;pointer-events:none;
    border:2.5px solid rgba(37,48,105,.18);border-top-color:var(--gold);
    animation:rai-working .95s linear infinite}
@media (prefers-reduced-motion:reduce){
.searchbox button[aria-busy="true"]::after{animation:none;border-color:var(--gold)}
}
#sonnyball[aria-busy="true"]::after{opacity:0;
    animation:rai-working .95s linear infinite,
              rs-arcin .3s ease-out 2.5s forwards}
/* FINAL 1.1 OWNER LAW: no circular ring is an approved ROVER state
     signal. Thinking is the small-blue dim/breath cue plus the live status
     text, so the generic busy arc is suppressed on this one state ball. */
  #sonnyball[aria-busy="true"]::after{content:none;animation:none;opacity:0}
@media (prefers-reduced-motion:reduce){
#sonnyball[aria-busy="true"]::after{
      animation:rs-arcin 1ms linear 2.5s forwards;border-color:var(--gold)}
}
/* A SPEAKING BALL IS NOT A WORKING BALL. He can still be aria-busy while
     he talks; a spinner around a talking 88px sphere is noise. */
  #sonnyball.speaking[aria-busy="true"]::after{animation:none;opacity:0}
/* FINAL OWNER PUNCH LIST · 2026-08-03.
     One physical sphere per state: no translucent blue layer over a second
     ball, no glyph at rest, and no ring. Awake/working/thinking use the filed
     blue sphere directly on the button. Listening and speaking use the same
     large gold body; their motion distinguishes them. */
  #sonnyball::before{content:none!important;display:none!important}
#sonnyball.awake{background:#111936 url('/assets/sonny_blue_128.png') center/100% 100% no-repeat}
#sonnyball.listening,
  #sonnyball.listening[aria-busy="true"],
  #sonnyball.speaking,
  #sonnyball.speaking[aria-busy="true"]{
    background:radial-gradient(circle at 32% 26%,#FDF6E3 0%,#FAE0A5 22%,#E0B86C 45%,#B58B45 70%,#866736 100%)}
/* The voice control belongs to the Royal family without impersonating a
     second Sonny. It is a compact navy wordmark control with the filed R,
     not an off-the-shelf microphone pictogram. */
  /* 8/5/2026 — THE BUBBLE R TAKES THE BUTTON, and the pill turns navy to
     carry it. Bryan's ruling the same day: the Bubble R "is now ROVER's HOME
     BASE FOR ALL ROVERS MOVING FORWARD," and this is where it ships first.
     Two findings out of the sizing study (`_coin-cutter\talk-button\`, which
     rasterises the master onto both grounds at five sizes and LOOKS at it,
     per the Standard's measure-never-eyeball rule) decided this block:
       1. THE GROUND MUST BE NAVY. On the old white pill the bowl's COUNTER —
          the hole that makes an R an R — dissolves into the page, and the
          gold cloud sits near 2.4:1. That is TRAP 1 and Rule 3 at once. On
          navy the counter reads, both crown teeth read, and the gold is
          doing exactly Rule 2's job: the one element carrying the meaning.
       2. 20px WAS TOO SMALL. The old 20x22 slot loses both crown teeth and
          turns the window Sunny into a smudge. 32 is the floor; 34 ships.
     AND GOLD NOW MEANS LISTENING. The resting pill is navy on a dark navy
     edge; the gold ring arrives only with .on, so gold marks the live state
     instead of decorating the resting one. */
  /* 8/6 — THE PILL IS GONE, BY BRYAN'S OWN ORDER, launch eve: "get rid of
     the blue background behind ROVER... the look is supposed to be the same
     as the buttons, as if he is floating in space." So the bubble R and the
     bubble word sit BARE on the page, exactly like the Royal coins — no
     plate, no border, no shadow. This composes with the filed ALIVE ROVER
     law (white/transparent ground, non-negotiable, never re-raise
     gold-on-white). The 44px hit target survives: the control's box is
     unchanged, only its paint is gone. Prior pill, per the never-delete law:
       was: gap:8px;border:1.5px solid #161E45;border-radius:22px;
            background:linear-gradient(180deg,#2C3878 0%,#1E2857 100%);
            box-shadow:0 4px 12px rgba(21,27,60,.18);font-size:12px;
            font-weight:800;letter-spacing:.025em;color:#F7F3E8 */
  /* 8/8 — ROVER GETS BIGGER IN THE BAR (Bryan, this build): "ROVER needs to
     be bigger on the search bar. It's okay if he is close to the edges."
     The 8/5 sizing study set 32px as the floor and shipped 34; that study
     answered "how small can he go before the crown teeth and the counter
     dissolve," which is a different question from how big he SHOULD be.
     Mark 34 -> 47, word 22 -> 28. The control's own box grows 44 -> 52 so
     the art is not clipped by its own container, and 52 still clears the
     56px bar — he crowds the edges on purpose, which is the ruling. The
     44px WCAG 2.5.8 floor is exceeded, not approached.
     Prior numbers per the never-delete law:
       was: .searchbox .rovermic{height:44px;min-height:44px}
.rovermic-mark{width:30px;height:34px}
.rovermic-label{height:22px}
*/
  .searchbox .rovermic{height:56px;min-height:56px;padding:0 14px 0 10px;
    gap:9px;border:none;border-radius:28px;background:transparent;
    box-shadow:none}
.rovermic-mark{width:41px;height:47px;display:grid;place-items:center;flex:none}
/* 8/5 — the label is a bubble WORD, not type. Height is locked so the
     pill never resizes when Talk swaps to TEXT; width follows the art. */
  .rovermic-label{display:grid;place-items:center;flex:none;height:28px}
.searchbox .rovermic .rovermic-label img{height:28px;width:auto;max-width:none;
    object-fit:contain;display:block}
.searchbox .rovermic .rovermic-mark img{width:100%;height:100%;max-width:none;
    max-height:none;object-fit:contain;display:block}
/* THE LIVE-MIC SIGN, without the pill: a soft gold halo on the art itself.
     Prior ring, per the never-delete law:
       was: .searchbox .rovermic.on{border-color:var(--gold);
            box-shadow:0 0 0 3px rgba(203,162,96,.30),0 4px 14px rgba(21,27,60,.22)}
*/
  .searchbox .rovermic.on img{
    filter:drop-shadow(0 0 6px rgba(203,162,96,.55))}
/* 8/6 — THE ESCAPE HATCH SHRINKS (Bryan, with the mockup): in conversation
     mode the label reads TEXT and renders VERY SMALL — "they should need to
     try hard to press that button because we actually prefer that they stay
     in conversing mode." The 44px hit box is untouched; only the art gets
     quiet. TALK, the invitation, keeps the full 22px cut. */
  .rovermic-label img{transition:height .3s ease-out}
/* 8/8 — rides the bigger art at the SAME RATIO (was 11 of 22, now 14 of
     28). The escape hatch must stay proportionally quiet; scaling only the
     invitation would have made TEXT read as the louder of the two. */
  .searchbox .rovermic.voicemode .rovermic-label img{height:14px}
/* 8/7 — THE SEARCH-BAR MORPH IN THE SEAT (FINAL-BUILD items 1 + 2, and
     Bryan's FINAL LAW for the morph). One canvas replaces the mark image and
     the label image together; the panes ride inside it in the ruled order,
     word · figure · egg, immediately left of golden Sonny.

     THE PADDING GOES TO ZERO, and that is why the bar fits. The 14/10px
     horizontal padding belonged to the navy pill, and Bryan retired the pill
     on 8/6 ("as if he is floating in space"). Keeping it would have pushed
     the canvas past the 136px the input's padding-right:196px reserves and
     eaten into the question box on a phone. The 44px hit box is untouched —
     it is the control's own height, not the art's.

     The two rules above still apply to the FALLBACK control (an old cache, a
     blocked script) and are deliberately left standing: .rovermic-label does
     not exist when the morph mounts, so the 11px small-TEXT cut simply has
     nothing to size. Per the core build order the final TEXT size rides its
     own confirm; 11px is not restored here as a regression fix. */
  .searchbox .rovermic.morphbar{padding:0;gap:0}
.rovermic-bar{display:block;line-height:0;font-size:0}
.rovermic-morph{display:block}
/* THE LIVE-MIC SIGN on the canvas, same gold halo the images wore. */
  .searchbox .rovermic.on .rovermic-morph{
    filter:drop-shadow(0 0 6px rgba(203,162,96,.55))}
/* 8/7 — NO PLATE, IN ANY STATE. Bryan, on sight of the live bar: "GET RID
     OF THAT GOLD BACKGROUND THAT SHOULD NEVER BE THERE!" — and his earlier
     blue was the same defect one state over. The 8/6 pill removal cleared
     the RESTING background, but the pill era's STATE rules survived above:
     `.searchbox .rovermic.on{background:var(--gold)}
` painted a gold plate
     the moment the mic went live, and the original navy-control rules could
     paint the blue one. Higher specificity + later in the cascade, so every
     state is transparent whenever the morph carries the control. The mic's
     live sign is the gold halo on the ART (the rule just above), never a
     plate — the morphs sit bare on the page, floating in space, exactly the
     8/6 ruling. The old state rules stay untouched above: they are the
     fallback control's record, per the never-delete law. */
  .searchbox .rovermic.morphbar,
  .searchbox .rovermic.morphbar.on,
  .searchbox .rovermic.morphbar.voicemode,
  .searchbox .rovermic.morphbar.voicemode.on{
    background:transparent !important;border:none !important;box-shadow:none !important}
/* THE SEAT — 8/6, BRYAN'S FINAL WORD ON IT: "just go ahead and put that
     exact floating thing in the search bar again. Done. go!" So the bare
     R + word art (no pill, no bob — his same-day orders) sits back INSIDE
     the bar, in the pre-float seat left of the egg. The outside-the-bar
     seat is kept below per the never-delete law:
       was: .searchbox .rovermic{left:calc(100% + 14px + var(--voice-shift,0px));
            right:auto;transition:left .3s ease-out,top .3s ease-out}
AND THE SLIDE, INVERTED FOR THE IN-BAR SEAT. When TALK grows Sonny to
     the Royal bubbles' own coin size, js/voice.js writes --voice-shift on
     the root: exactly the extra radius the grown ball needs, (coin−ball)/2.
     In the bar the control sits LEFT of the ball, so it slides LEFT that
     far on the same 0.3s the growth takes — the R and the word keep the
     same clear distance off the sun's edge, large or small. Symmetry by
     construction. Inert at rest: property removed on exit, fallback 0px. */
  .searchbox .rovermic{right:calc(60px + var(--voice-shift,0px));left:auto;
    transition:right .3s ease-out}
/* 8/8 — ROVER 2 RULING: THE TEXT DOOR FLOATS AWAY. Once talk begins the
     control glides out of the bar to the bottom-left corner of the SCREEN
     — "located in a spot in which their finger would have to move
     locations and go away from the sun to actually go and press that
     button" (Bryan, 8/8, verbatim). position:fixed, so however far the
     conversation scrolls the one way back to typing is never off screen:
     the user must always know exactly what to press to fix the situation.
     This rule is only the DESTINATION seat; the graceful ride there and
     back is a JS FLIP in voice.js, and the locked morph module simply
     rides inside the travelling control — no clause of THE MORPH LAW
     touches where the control's box sits on the page. The 44px hit box
     and the 8/6 small-TEXT art both ride along unchanged: small AND far
     compose — "we actually prefer that they stay in conversing mode."
     transform:none on purpose: the bar seat's translateY(-50%) centering
     has no meaning against a fixed bottom edge. */
  .searchbox .rovermic.textaway{
    position:fixed;left:16px;right:auto;top:auto;
    bottom:calc(16px + env(safe-area-inset-bottom,0px));
    transform:none;z-index:80;margin:0}
/* 8/6 — THE BOB IS RETIRED, BY BRYAN'S OWN ORDER: "I did not mean to move
     him around to float him. I mean that the look is supposed to be the same
     as the buttons, as if he is floating in space." Floating is the LOOK —
     bare art on the open page, like every Royal coin — not a motion. The
     control now sits still, exactly like the buttons it must match. The
     keyframes stay on file per the never-delete law; nothing points at them.
       was: .searchbox .rovermic{animation:rv-float 4.2s ease-in-out infinite}
.searchbox .rovermic:hover{animation-play-state:paused}
.searchbox .rovermic{transform:translateY(-50%)}
/* ===================================================================
     8/8/2026 · THE NARROW-PHONE SEAT — the control takes the room BELOW
     the bar instead of taking it from the person typing.
     ===================================================================
     Bryan's two rulings collide at 375px and both of them are right:
     8/6, the control goes back IN the bar ("put that exact floating thing
     in the search bar again"); 8/8, ROVER gets BIGGER ("it's okay if he
     is close to the edges").

     MEASURED, not guessed, at four widths after the art went to 47px:
     in-bar on a 375px phone the reserve has to grow to 218px, which
     leaves 90px of typing space — about nine characters. That is the
     exact failure recorded on 8/5 ("on a 375px phone that leaves almost
     nothing to type in"), arriving from the other direction. A 90px
     question box is a broken input, and the words come first.

     So on narrow phones ONLY, the control keeps its full new size and
     sits centred just under the bar, where there is room for it, and the
     input gets its whole width back (reserve returns to the egg alone).
     Wide screens are unaffected — there the big control and a 360px type
     box both fit, which is why this is a media query and not a retreat.

     🔑 THE TEXT DOOR STILL WINS. `.searchbox .rovermic.textaway` is
     (0,3,0) against this rule's (0,2,0), so the moment TALK is pressed
     the control still leaves for the bottom-left corner of the screen
     from here exactly as it does from the in-bar seat. Ruling 3 intact.

     The door bubbles are pushed down by the control's height so it never
     lands on top of them — the same defect the 8/5 float hit, and the
     same `!important` is required because `.bubbles.g8` is (0,2,0) AND
     sits later in this file. */
  /* 2026-09-07 — 430 -> 480. The phone seat (TALK under the bar, the short
     padding-right) stopped at 430px and the iPhone Pro Max is 440 wide, so
     those phones kept a 175px typing column beside two controls. Found in
     review. The three seats (this one, and the two in THE SIMPLE FRONT DOOR
     block) move together. */
  @media (max-width:480px){
    .searchbox .rovermic{left:50%;right:auto;top:calc(100% + 10px);
      transform:translateX(-50%)}
    .searchbox #q{padding-right:60px}
    .bubbles,.bubbles.g8{margin-top:78px!important}
  }
:root{--row-half:28px}
@media (max-width:390px){
:root{--row-half:27px}
}
/* THE BOX. One row at rest; it grows with the words to about six lines
       and then scrolls inside itself. padding-right is NOT set here on
       purpose: the .searchbox #q rules above still own it (room for the
       TALK control and the ball at every width). */
    .searchbox #q{display:block;width:100%;box-sizing:border-box;margin:0;
      border:1.5px solid var(--line);border-radius:28px;background:var(--card);
      padding-top:15px;padding-bottom:15px;padding-left:24px;
      font-size:16.5px;line-height:1.4;font-family:inherit;font-weight:500;
      color:var(--ink);box-shadow:0 10px 30px rgba(21,43,107,.10);outline:none;
      resize:none;overflow-y:hidden;min-height:56px;max-height:196px;
      -webkit-appearance:none;appearance:none;
      transition:box-shadow .2s,border-color .2s}
.searchbox #q:focus{border-color:var(--gold);box-shadow:0 12px 34px rgba(202,162,95,.28)}
.searchbox #q::placeholder{color:var(--muted);opacity:1}
.searchbox #q:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
/* at its tallest the box scrolls by wheel, keys and finger; a visible desktop
       scrollbar would sit under the ball's seat (review finding) */
    .searchbox #q{scrollbar-width:none}
.searchbox #q::-webkit-scrollbar{display:none}
@media (max-width:390px){
.searchbox #q{padding-top:14px;padding-bottom:14px;padding-left:18px;
        font-size:16px;min-height:52px}
}
/* THE BALL AND THE TALK CONTROL RIDE THE LAST LINE of the box, not its
       middle, so a tall box keeps them where the thumb expects them. Every
       transform above still says translateY(-50%), so only `top` moves. */
    .searchbox #sonnyball{top:calc(100% - var(--row-half))}
@media (min-width:481px){
.searchbox .rovermic{top:calc(100% - var(--row-half))}
}
/* THE TRANSCRIPT: the width of the stage, tall, hugging the bar. */
    .chat{width:100%;max-width:none;margin:0 0 10px}
.chatlog{max-height:min(60vh,600px)}
.chat.open ~ .searchbox{margin-top:0}
/* THE SEAT UNDER THE MARK once the tagline is gone. Desktop keeps the
       continuity law's 128px constant (stage 22 + 106 = the old 22 + 26 +
       80); phones, where the two layers never matched to the pixel, sit
       closer so the page does not open on a void. */
    .searchbox{margin-top:106px}
@media (max-width:700px){
.stage{padding-left:10px;padding-right:10px}
.searchbox{margin-top:clamp(36px,7vh,64px)}
.chatlog{border-radius:16px;padding:14px 14px}
}
/* ROVER AT YOUR FEET — the seat above the bar (see the markup note). The
       landing: he drops in from a little above, settles with one bounce.
       The transcript gives up some height while he is on the page so the
       dog and the bar stay on screen together on a phone. */
    .roverfetch{width:100%;display:flex;justify-content:center;align-items:flex-end;
      margin:0;line-height:0;pointer-events:none;
      max-height:0;overflow:visible;transition:max-height .32s ease-out,margin .32s ease-out}
.roverfetch.on{max-height:80px;margin:0 0 4px}
.roverfetch[hidden]{display:none}
/* the same height as the little Rover in the bar (its canvas is 47px on a
       phone, 52px on a desktop) — Bryan: "the same size as the one we have
       right now. We don't need to make them gigantic." */
    .roverfetch img{height:54px;width:auto;display:block;
      filter:drop-shadow(0 4px 6px rgba(21,27,60,.18))}
@media (max-width:700px){
.roverfetch img{height:48px}
}
.roverfetch.landing img{animation:rf-land .62s cubic-bezier(.22,.9,.24,1) 1 both}
.roverfetch.leaving img{animation:rf-leave .28s ease-in 1 both}
/* SUNNY IS BRIGHT WHEN HE IS CONVERSING — 2026-09-08, Bryan: "when it's large
       and Sunny, it should be conversing... really big, really bright, and really
       Sunny when it's conversing. When it's not listening, or when it's dead,
       null, or void, it should be closed in gold." So the big sun never dims
       while the conversation is open (the 8/8 frozen beat kept the size and
       lost the shine) and he wears a halo. Closed = the small gold ball. */
    #sonnyball.listening,#sonnyball.speaking,
    #sonnyball.listening[aria-busy="true"],#sonnyball.speaking[aria-busy="true"],
    #sonnyball.speaking.rs-frozen,#sonnyball.speaking.rs-frozen[aria-busy="true"]{
      filter:none;
      /* the shadow scales with the sun (about 2.4x here), so it is authored small */
      box-shadow:0 0 0 3px rgba(203,162,96,.16),0 0 22px 7px rgba(253,230,165,.58)}
@media (prefers-reduced-motion:reduce){
#sonnyball.listening,#sonnyball.speaking,
      #sonnyball.listening[aria-busy="true"],#sonnyball.speaking[aria-busy="true"],
      #sonnyball.speaking.rs-frozen,#sonnyball.speaking.rs-frozen[aria-busy="true"]{
        box-shadow:0 0 0 3px rgba(203,162,96,.16)}
}

/* ---------------------------------------------------------------------------
   THE GRID LAW OVERRIDE - 2026-09-09, found by testing on the live site.

   Some sites carry grid-law.css, which pins the ask bar with
       .searchbox{margin:var(--rg-bar-gap) auto 0 !important}   (80px)
   The reference collapses that gap when the transcript opens, with
       .chat.open ~ .searchbox{margin-top:0}
   but that rule is not !important and the reference page never linked
   grid-law.css, so the clash could not happen there. Measured live on
   royalagent.ai: 90px of dead air between the answer and the bar, and the bar
   pushed under the fold. Restated at the same weight so the bar sits under the
   transcript the way it does on the front door. The CLOSED state is left alone -
   that gap is each site's own spacing under its lockup.
   --------------------------------------------------------------------------- */
.chat.open ~ .searchbox{margin-top:0!important}
.chat.open ~ .roverfetch{margin-top:0!important}

/* ---------------------------------------------------------------------------
   ROOM FOR THE TALK CONTROL - 2026-09-09, found by testing on the live site.

   On a phone the TALK control drops below the bar, and it is absolutely
   positioned, so it takes no space in the flow: whatever follows the bar has to
   leave it. The reference reserves that room with
       @media (max-width:480px){ .bubbles,.bubbles.g8{margin-top:78px!important} }
   which names .bubbles only. royalmortgage.ai's row of coins is nav.coins, a
   class that rule never mentioned, so the TALK beads landed on top of the coins -
   measured live at 375 wide, 50px of overlap. The same reservation is extended to
   the coins row here. Desktop is untouched: above 480 the control sits inside the
   bar, not under it.
   --------------------------------------------------------------------------- */
@media (max-width:480px){
  .searchbox ~ nav.coins,.searchbox ~ .coins{margin-top:78px!important}
}
