/* ============================================================
   nx-rivo.css
   ASK RIVO, the sheet. Lifted VERBATIM out of companion.html so the
   conversation looks and behaves identically wherever it is opened
   from.

   WHY IT IS A FILE. The Ask Rivo chat existed once, inline in home,
   which meant every other page could offer a button that opened a
   placeholder instead. That is the same defect nx-topbar.css and
   nx-tabbar.css were pulled out to fix, and the same fix.

   HOME IS NOT EDITED. Per the standing rule, home keeps its own inline
   copy and is never changed to make another page work. This is a
   faithful extract that home will adopt when somebody is deliberately
   touching home, not a change made underneath it.
   ============================================================ */

/* THE PILL'S RULES WERE STRIPPED BADLY WHEN IT WAS RETIRED: the selector
   lines went and the declaration bodies stayed, leaving eight orphaned
   blocks at the top of this file. A stylesheet recovers from that by
   discarding until it finds a brace it understands, so it was not only
   dead weight, it was capable of eating the rule after it. */
.rv-stage{display:block}
.rv-sheet.chat .rv-intro,#sheet-rivo.chat .rv-intro{display:none}
.rv-chat{display:none}
/* THE ID IS HOME'S AND HOME IS ONE PAGE. Every one of these was carried
   over as #sheet-rivo, so on every other page the chat stayed display:none
   and the panel opened as a header over nothing. */
.rv-sheet.chat .rv-chat,#sheet-rivo.chat .rv-chat{display:flex;flex-direction:column;min-height:0}
.rv-sheet.chat .sheet-body,#sheet-rivo.chat .sheet-body{display:flex;flex-direction:column;min-height:0;padding-bottom:0}
.rv-mark{display:flex;align-items:center;gap:12px;margin-bottom:18px}
.rv-orb{width:46px;height:46px;border-radius:50%;flex-shrink:0;position:relative;
  background:conic-gradient(from 200deg,#B52555,#0077C8 45%,#00A499 78%,#B52555);
  display:grid;place-items:center}
.rv-orb::after{content:'';position:absolute;inset:5px;border-radius:50%;background:var(--canvas)}
html[data-theme="light"] .rv-orb::after{background:#fff}
.rv-orb svg{position:relative;z-index:1;color:var(--ink)}
.rv-mark b{display:block;font-family:var(--display);font-size:17px;font-weight:700;letter-spacing:-.02em}
.rv-mark span{display:block;font-size:12.5px;color:var(--ink-3)}
.rv-say{font-size:15.5px;line-height:1.55;color:var(--ink-2);margin-bottom:18px}
.rv-points{display:flex;flex-direction:column;gap:10px;margin-bottom:20px}
.rv-pt{display:flex;align-items:flex-start;gap:12px;padding:14px;border-radius:var(--r-md);
  background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.11)}
html[data-theme="light"] .rv-pt{background:#EEF1F7;border-color:#DDE3EC}
.rv-pt .ic{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex-shrink:0;color:#fff}
.rv-pt b{display:block;font-size:14px;font-weight:600;margin-bottom:2px}
.rv-pt span{display:block;font-size:13px;color:var(--ink-2)}
.rv-stream{flex:1;min-height:180px;overflow-y:auto;display:flex;flex-direction:column;gap:12px;
  padding:4px 0 14px}
.rv-msg{max-width:82%;padding:13px 16px;border-radius:18px;font-size:14.5px;line-height:1.5}
.rv-msg.bot{align-self:flex-start;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.11);
  border-bottom-left-radius:6px}
html[data-theme="light"] .rv-msg.bot{background:#EEF1F7;border-color:#DDE3EC}
.rv-msg.me{align-self:flex-end;background:var(--secondary);color:#fff;border-bottom-right-radius:6px}
.rv-typing{align-self:flex-start;display:flex;gap:5px;padding:15px 18px;border-radius:18px;
  background:rgba(255,255,255,.07);border-bottom-left-radius:6px}
.rv-typing i{width:7px;height:7px;border-radius:50%;background:var(--ink-3);display:block;
  animation:rvdot 1.2s infinite}
.rv-typing i:nth-child(2){animation-delay:.15s}
.rv-typing i:nth-child(3){animation-delay:.3s}
.rv-sugg{display:flex;gap:8px;flex-wrap:wrap;padding-bottom:12px}
.rv-sugg[hidden]{display:none}
.rv-sugg button{padding:9px 14px;border-radius:999px;background:rgba(255,255,255,.06);
  border:1px solid rgba(255,255,255,.13);font-size:13px;font-weight:500;color:var(--ink-2);
  transition:background .2s var(--ease),transform .18s var(--bounce)}
html[data-theme="light"] .rv-sugg button{background:#EEF1F7;border-color:#DDE3EC}
.rv-sugg button:hover{background:rgba(255,255,255,.13)}
.rv-sugg button:active{transform:scale(.95)}
.rv-input{display:flex;align-items:flex-end;gap:10px;padding:12px 0 10px;
  border-top:1px solid var(--hair)}
.rv-input textarea{flex:1;resize:none;min-height:46px;max-height:120px;padding:13px 15px;
  border-radius:var(--r-md);background:var(--raise-2);border:1px solid var(--hair);color:var(--ink);
  font:inherit;font-size:15px;line-height:1.4}
.rv-input textarea:focus{outline:none;border-color:var(--secondary)}
.rv-input textarea::placeholder{color:var(--ink-3)}
.rv-send{width:46px;height:46px;border-radius:50%;flex-shrink:0;display:grid;place-items:center;
  color:#fff;background:linear-gradient(135deg,#B52555,#0077C8 55%,#00A499);
  box-shadow:0 6px 18px rgba(181,37,85,.34);
  transition:transform .18s var(--bounce),filter .2s var(--ease)}
.rv-send:hover{filter:brightness(1.12)}
.rv-send:active{transform:scale(.92)}
.rv-foot{font-size:12px;color:var(--ink-3);text-align:center;
  padding:0 0 calc(14px + env(safe-area-inset-bottom))}
.rv-foot a{color:var(--secondary-soft);font-weight:600}
html[data-theme="light"] .rv-foot a{color:var(--secondary)}
/* The docked geometry lives further down, written once against .rv-sheet.
   What was here was the same thing scoped to home's id, and its @media
   wrapper had been lost in the extract, so the phone override was applying at
   every width. */
body.rivo-open .shell{transform:none;border-radius:0;filter:none}
/* THE PAGE STAYS READABLE BEHIND IT. This is a panel in the corner, not a
   modal, so the backdrop stops being a 3px blur over everything and becomes a
   dim. Home has done this since it shipped. */
body.rivo-open .backdrop{background:rgba(4,7,14,.28);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.rv-mark .rv-orb{width:46px;height:46px;background:linear-gradient(135deg,#B52555,#0077C8 52%,#00A499);
  display:flex;align-items:center;justify-content:center;line-height:0;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16),0 4px 14px rgba(0,119,200,.28)}
.rv-mark .rv-orb::after{display:none}
.rv-mark .rv-orb svg{color:#fff;display:block;width:23px;height:23px;
  transform:translate(.5px,-.5px);stroke-width:2}
.rv-flag{align-self:flex-start;max-width:82%;padding:11px 14px;border-radius:14px;font-size:13px;
  background:rgba(180,35,24,.16);border:1px solid rgba(180,35,24,.34);color:var(--ink)}
.rv-flag a{color:#FF8B80;font-weight:600}

/* ---- the three points, one at a time, above the composer ---------------
   SOLID, NOT GLASS. The first version used the translucent card fill, which
   over a photograph or a busy page left the one thing somebody actually has to
   read as the least readable thing on the screen. Consent wording does not get
   a transparency treatment. */
.rv-bubbles{margin:0 0 12px}
.rv-bub-stack{position:relative;transition:height .4s var(--spring)}
/* NO COLOURED DOT. It was a leftover from the three-icon consent screen, where
   each point had its own colour, and shrunk to a 9px disc beside a heading it
   read as a bullet that meant something. It did not mean anything. */
.rv-bub{display:block;padding:12px 16px;border-radius:14px;
  background:var(--canvas);border:1px solid var(--hair-2);
  box-shadow:0 10px 26px rgba(0,0,0,.34)}
html[data-theme="light"] .rv-bub{background:#fff;border-color:rgba(31,42,68,.16);
  box-shadow:0 6px 18px rgba(31,42,68,.12)}

/* THE CROSSFADE. Every point sits in the same place, absolutely, and the one
   showing is the only one at full opacity. The outgoing one keeps its layer
   until it has finished leaving (.was), so the two overlap for half a second
   and neither the box nor the text ever jumps. The incoming one arrives from
   slightly below and the outgoing one leaves slightly above, which is what
   makes it read as one thing being replaced rather than two things blinking. */
/* OPACITY ONLY. THESE MUST NOT SET visibility OR pointer-events.
   They did, and a child setting visibility:visible or pointer-events:auto
   OVERRIDES a hidden ancestor rather than being contained by it. The closed
   widget hides its consent block with visibility:hidden and pointer-events:
   none, and the showing bubble inside it undid both, so a 430px by 90px
   invisible panel sat over the middle of the screen swallowing clicks on
   everything under it. That is what stopped the tab bar's More control from
   working at 1024px: elementFromPoint at that button returned SPAN.rv-bub.

   These bubbles are read-only text and never need to be clicked, so opacity is
   the whole of what they need, and it cannot leak out of a hidden parent. */
.rv-bubbles.rot .rv-bub{position:absolute;inset:0 0 auto 0;opacity:0;
  transform:translateY(7px) scale(.985);
  transition:opacity .42s var(--ease),transform .52s var(--spring)}
.rv-bubbles.rot .rv-bub.was{opacity:0;
  transform:translateY(-7px) scale(.985);
  transition:opacity .34s var(--ease),transform .52s var(--spring)}
.rv-bubbles.rot .rv-bub.on{opacity:1;transform:none;
  transition:opacity .46s var(--ease) .06s,transform .56s var(--spring) .06s}
.rv-bubbles.all{display:flex;flex-direction:column;gap:7px}
.rv-bubbles.all .rv-bub-stack{height:auto!important;display:flex;flex-direction:column;gap:7px}

.rv-bub b{display:block;font-size:13px;font-weight:650;color:var(--ink);line-height:1.3;
  margin-bottom:2px}
.rv-bub span{display:block;font-size:12.2px;line-height:1.45;color:var(--ink-2)}
.rv-bub-foot{display:block;margin-top:9px;padding-left:4px;
  font-size:11.5px;font-weight:600;color:var(--ink-3)}
.rv-bubbles.all .rv-bub-foot{margin-top:4px}
.rv-mark-top{margin-bottom:14px}
@media (prefers-reduced-motion: reduce){
  .rv-bub-stack,.rv-bubbles.rot .rv-bub{transition:none}
}

/* ==========================================================================
   THE CONTROL ITSELF: an orb that becomes a bar.

   It replaced the "Ask Rivo / Any question, any time" pill that used to sit in
   this corner on every page. The pill was a label for a thing somewhere else;
   this is the thing. Tapping it does not open a screen, it opens a line you
   can type into, and the widget only becomes a conversation once there is
   something to say.

   THE WIDTH ANIMATES rather than the element being swapped, which is what
   makes it read as one object changing shape instead of two objects trading
   places. Everything inside is faded and non-interactive until the width has
   arrived, or the caret lands in a field that is still 56px wide.

   OPENING SPRINGS, CLOSING DOES NOT. A spring on the way out overshoots into
   a bounce, which on a control somebody has just dismissed reads as the thing
   arguing back. Closing gets a plain decelerating curve and the contents fade
   first, so what is left to watch is a shape settling.
   ========================================================================== */
.rv7{position:absolute;right:16px;bottom:16px;display:flex;align-items:center;
  height:56px;width:56px;padding:0;border-radius:999px;overflow:hidden;
  background:var(--glass);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid transparent;box-shadow:var(--shadow-lg);
  transition:width .4s cubic-bezier(.22,.61,.36,1),border-color .34s var(--ease),
    background .34s var(--ease),box-shadow .34s var(--ease)}
.rv7.open{width:calc(100% - 32px);max-width:460px;border-color:var(--hair-2);
  transition:width .46s var(--spring),border-color .3s var(--ease),
    background .3s var(--ease),box-shadow .3s var(--ease)}
/* 54px, NOT 56. The pill is 56px with a 1px border and box-sizing:border-box,
   so its content box is 54px wide, and a 56px knob inside it overflowed by a
   pixel and was clipped flat on the right by overflow:hidden. A circle with a
   straight edge down one side is the kind of defect that looks like a
   rendering fault rather than a mistake, and it was arithmetic. Measured on
   the deployed page: knob right edge 1415, pill right edge 1414.

   The pill also stops clipping altogether while it is closed, so no transform,
   hover or half-finished transition can ever shave the disc again. The field
   is not merely transparent when closed, it is hidden, so nothing can appear
   outside the circle. */
.rv7:not(.open){overflow:visible}
.rv7:not(.open) .field{visibility:hidden}
.rv7 .knob{width:54px;height:54px;flex:none;border-radius:50%;display:grid;
  place-items:center;color:#fff;border:0;padding:0;cursor:pointer;
  transition:transform .34s cubic-bezier(.22,.61,.36,1)}
/* THE HOVER GROWS THE WHOLE CONTROL, NOT THE KNOB INSIDE IT.
   .rv7 clips its contents so the field can slide out of a 56px circle, and a
   knob scaled to 1.06 is 59px inside a 56px clip: the disc came back with its
   top, bottom and right shaved off, which read as a broken crescent rather
   than a button. Scaling the container moves the clip with it. */
.rv7:not(.open){transition:width .4s cubic-bezier(.22,.61,.36,1),
  transform .3s cubic-bezier(.22,.61,.36,1),border-color .34s var(--ease),
  background .34s var(--ease),box-shadow .34s var(--ease)}
.rv7:not(.open):hover{transform:scale(1.06)}
.rv7:not(.open):active{transform:scale(.96)}
.rv7.open .knob{transform:scale(.78);transition:transform .34s var(--spring)}
.rv7 .field{flex:1;min-width:0;display:flex;align-items:center;gap:8px;
  padding-left:4px;opacity:0;pointer-events:none;
  transition:opacity .14s var(--ease)}
.rv7.open .field{opacity:1;pointer-events:auto;transition:opacity .22s var(--ease) .18s}
.rv7 input{flex:1;min-width:0;background:none;border:0;color:var(--ink);font:inherit;
  font-size:14.5px;padding:8px 0}
.rv7 input::placeholder{color:var(--ink-3)}
.rv7 input:focus{outline:none}
.rv7 .talk{width:38px;height:38px;flex:none;border-radius:50%;display:grid;place-items:center;
  background:transparent;border:0;color:var(--ink-3);cursor:pointer;padding:0;
  transition:color .2s var(--ease),background .2s var(--ease)}
.rv7 .talk:hover{color:var(--ink);background:var(--raise)}
.rv7 .send{width:42px;height:42px;flex:none;margin-right:7px;border-radius:50%;
  display:grid;place-items:center;background:var(--secondary);color:#fff;border:0;cursor:pointer;
  transition:filter .18s var(--ease),opacity .18s var(--ease)}
.rv7 .send:hover{filter:brightness(1.1)}
.rv7 .send[disabled]{opacity:.35;pointer-events:none}
@media (prefers-reduced-motion: reduce){
  .rv7,.rv7:not(.open),.rv7 .knob,.rv7 .field{transition:none}
}

/* THE WRAPPER HAS TO HAVE A WIDTH, and so does the one inside it. `width:100%`
   on the open state resolves against the PARENT, and both a fixed element with
   no width and an inline <span> shrink to fit the collapsed 56px child, so the
   bar opened to two pixels the first time and to 330px the second. Measured
   both times, reasoned about neither. */
/* --rv-shift IS WRITTEN BY THE HELP DOCK AND READ ONLY HERE.
   The help orb takes the bottom-right corner when it is docked, so Rivo has
   to give the corner up and stand to its left. The dock is the only thing
   that knows whether the orb is docked, so it publishes the distance and
   this consumes it. THE 0px FALLBACK IS LOAD BEARING: a page without the
   dock never sets the property and lands on exactly the old geometry, so
   this is not a dependency in the other direction.

   The WIDTH has to subtract it too. Without that the ask bar keeps its full
   460px, is pushed left by the shift, and runs off the left of a narrow
   window instead of shrinking. */
/* THIS WIDTH ONLY EVER GOVERNS BEFORE nx-rivo.js's fit() RUNS, and on any
   page that mounts the bar without a .tabbar for fit() to measure against:
   once open, wireBar()'s fit() sets an INLINE width on .rv-real, and an
   inline style always wins over this rule regardless of what it says. It is
   corrected to match anyway, on the same reasoning fit() now uses: the
   460px CAP has to shrink by the shift too, not just the viewport term, or
   the bar's left edge only MOVES with the orb instead of the bar shrinking
   away from it, travelling exactly --rv-shift further over whatever sits to
   its left. */
.rv-real{position:fixed;right:calc(26px + var(--rv-shift, 0px));bottom:20px;z-index:57;
  width:min(calc(460px - var(--rv-shift, 0px)),calc(100vw - 52px - var(--rv-shift, 0px)));
  display:flex;justify-content:flex-end;
  pointer-events:none}
.rv-real .rv7-wrap{flex:1 1 auto;min-width:0;display:block;position:relative}
.rv-real:not(.open-real) .rv7-wrap{flex:0 0 auto}
.rv-real .rv7{position:relative;right:auto;bottom:auto;pointer-events:auto}
.rv-real .rv7.open{width:100%;max-width:none}
/* NO ROOM BESIDE THE BAR: on a phone the tab bar is the full width of the
   screen, so there is no line to share. It goes above it and takes the width
   instead, which is the only honest answer to a 390px viewport. */
.rv-real.stacked{left:14px;right:14px;bottom:84px;width:auto}
/* ON A PHONE IT CANNOT SHARE THE LINE, because there is no line to share:
   below 860px the tab bar is the full width of the screen, so "level with the
   tab bar" means "on top of the tab bar". It was, and it was covering the More
   control: elementFromPoint at that button's centre returned the orb, so the
   first press of More hit Ask Rivo instead. It sits above the bar here, which
   is the same corner and the only place it fits. */
@media (max-width:900px){.rv-real{right:calc(14px + var(--rv-shift, 0px));bottom:84px;
  width:min(calc(460px - var(--rv-shift, 0px)),calc(100vw - 28px - var(--rv-shift, 0px)))}}
@media (max-width:900px){.rv-real.stacked{bottom:84px}}
body.sheet-open .rv-real,body.cf-open .rv-real,body.rail-open .rv-real,
body.rivo-open .rv-real{opacity:0;pointer-events:none;transform:translateY(24px)}
/* `right` joins the list so Rivo SLIDES aside as the orb arrives rather than
   jumping. Same duration and easing the component already uses, so there is
   no new timing to tune. */
.rv-real{transition:opacity .3s var(--ease),transform .34s var(--ease),right .34s var(--ease)}
body.tabbar-wide .rv-real{opacity:0;pointer-events:none;transform:translateY(24px)}

/* AIMED AT. The help dock sets body.hd-aiming while its tab is being dragged
   towards this corner, so Rivo can show that it is the thing being aimed at.
   The rule lives HERE, with the rest of Rivo's appearance, and reacts to a
   body class the way the sheet-open and rail-open rules above already do.
   Putting it in hn-dock.css would give one element two owners, which is how
   a collision arrives later. */
body.hd-aiming .rv7{box-shadow:0 0 0 3px var(--cyan-soft),0 18px 44px rgba(0,0,0,.5)}
@media (prefers-reduced-motion: reduce){body.hd-aiming .rv7{transition:none}}

/* ==========================================================================
   THE CONVERSATION IS A DOCKED PANEL, NOT A BOTTOM SHEET.

   This is home's behaviour, and home was right: the widget you tapped grows
   into the thing you use, anchored to the same corner, with the page still
   behind it. Every other page opened the house bottom sheet instead, which
   slides a modal up over the whole screen from somewhere else entirely, so the
   same control did two different things depending on which page you were on.

   transform-origin is bottom right on purpose: the panel expands OUT of the
   orb rather than appearing at full size and fading in. Below 860px there is
   no corner worth anchoring to, so it takes the width and sits above the tab
   bar.

   SPECIFICITY, deliberately: .sheet.rv-sheet is (0,2,0) against the house
   .sheet at (0,1,0), and .sheet.rv-sheet.on is (0,3,0) against .sheet.on at
   (0,2,0). Both have to win or the panel keeps the bottom sheet's
   translateX(-50%) and sits half off the screen.
   ========================================================================== */
.sheet.rv-sheet{left:auto;right:26px;bottom:20px;width:min(420px,calc(100vw - 36px));
  max-height:min(640px,calc(100vh - 120px));
  border-radius:var(--r-xl);border:1px solid var(--hair-2);
  transform:translateY(14px) scale(.94);transform-origin:bottom right;opacity:0;
  box-shadow:0 18px 60px rgba(0,0,0,.5);
  transition:transform .46s var(--spring),opacity .3s var(--ease),
    max-height .46s var(--spring)}
.sheet.rv-sheet.on{transform:none;opacity:1}
.sheet.rv-sheet.chat{max-height:min(720px,calc(100vh - 110px))}
.sheet.rv-sheet .grabber{display:none}
/* the widget stays where it is and dims rather than sliding away, so the
   expansion visibly comes out of it */
body.rivo-open .rv-real{opacity:0;pointer-events:none;transform:scale(.9)}
@media (max-width:860px){
  .sheet.rv-sheet{right:10px;left:10px;width:auto;bottom:76px;
    max-height:min(560px,calc(100vh - 150px))}
}
@media (prefers-reduced-motion: reduce){
  .sheet.rv-sheet{transition:opacity .2s linear}
}

/* the consent points, floating above the expanded bar. They leave as smoothly
   as they arrive: display:none took them away in one frame while the bar was
   still collapsing underneath them. */
.rv7-wrap{position:relative}
.rv7-bubbles{position:absolute;right:0;bottom:calc(100% + 12px);width:min(430px,88vw);
  opacity:0;transform:translateY(10px) scale(.985);pointer-events:none;visibility:hidden;
  transition:opacity .26s var(--ease),transform .34s cubic-bezier(.22,.61,.36,1),
    visibility 0s .34s}
.rv7-bubbles[hidden]{display:none}
.rv7-wrap.open .rv7-bubbles{opacity:1;transform:none;pointer-events:auto;visibility:visible;
  transition:opacity .34s var(--ease) .1s,transform .46s var(--spring) .1s,visibility 0s}
@media (prefers-reduced-motion: reduce){.rv7-bubbles{transition:none}}

/* ==========================================================================
   THE ORB, six treatments.
   The three brand colours are not negotiable: the Forward gradient marks
   genuine AI and nothing else on this platform. HOW they are rendered is
   entirely open, and the flat ninety six degree band is only one answer, and
   the harshest of them. Every one below uses the same three colours.
   ========================================================================== */
.orb-band   { background:linear-gradient(96deg,#B52555,#0077C8,#00A499); }
/* a sphere rather than a swatch: the colours turn through the form */
.orb-bloom  { background:radial-gradient(circle at 32% 26%,#E0568A 0%,#B52555 26%,#0077C8 62%,#00A499 100%); }
/* swept round, so there is no start and no end to catch the eye */
.orb-sweep  { background:conic-gradient(from 210deg,#B52555,#0077C8,#00A499,#B52555); }
/* two colours instead of three, deeper, with a glass highlight over it */
.orb-duo    { background:
                radial-gradient(circle at 34% 24%,rgba(255,255,255,.55),rgba(255,255,255,0) 46%),
                linear-gradient(150deg,#B52555,#0077C8); }
/* mostly navy, the spectrum only as a rim light. The most restrained */
.orb-ink    { background:
                radial-gradient(circle at 50% 120%,rgba(0,164,153,.9),rgba(0,164,153,0) 58%),
                radial-gradient(circle at 12% 8%,rgba(181,37,85,.85),rgba(181,37,85,0) 52%),
                #1F2A44;
              box-shadow:inset 0 0 0 1px rgba(255,255,255,.16); }
/* three soft blobs that overlap, so it reads as depth rather than as a print */
.orb-mesh   { background:
                radial-gradient(circle at 22% 26%,#B52555 0%,rgba(181,37,85,0) 55%),
                radial-gradient(circle at 78% 32%,#0077C8 0%,rgba(0,119,200,0) 58%),
                radial-gradient(circle at 50% 88%,#00A499 0%,rgba(0,164,153,0) 60%),
                #16213A; }

/* INK IS THE DEFAULT, picked 2026-08-10. Mostly Forward navy with the spectrum
   only as a rim light: the quietest rendering that still reads as AI, which is
   the right register for something sitting in the corner of every screen all
   day. The five above stay so the choice remains legible and reversible.

   THE :not() IS LOAD BEARING. Without it this rule is `.rv7 .knob` at (0,1,1)
   and beats every .orb-* class at (0,1,0), which is exactly the collision that
   made the picker look broken in the first place. With it, the default applies
   only when nothing has been picked. */
.rv-orb:not([class*="orb-"]), .rv7 .knob:not([class*="orb-"]) {
  background:
    radial-gradient(circle at 50% 120%,rgba(0,164,153,.9),rgba(0,164,153,0) 58%),
    radial-gradient(circle at 12% 8%,rgba(181,37,85,.85),rgba(181,37,85,0) 52%),
    #1F2A44;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16); }
/* .rv-orb has a punched centre for the chat header mark; the widget's knob is
   a solid disc, so it must not inherit the hole. */
.rv7 .knob::after { content:none; }

/* THE i OF THRIVE, on the knob. The sprite is the real artwork, so it is
   filtered to a white silhouette rather than recoloured: brightness(0) makes
   every pixel black and invert(1) makes it white, which keeps the letterform's
   own antialiased edges exactly. width is fixed and height follows the glyph,
   which is much taller than it is wide. */
.rv7 .knob .rv-ifig{width:auto;height:26px;display:block;
  filter:brightness(0) invert(1)}
.rv7.open .knob .rv-ifig{height:24px}


/* ==========================================================================
   VOICE MODE. The same conversation, out loud.

   THE WORDS ARE HIDDEN, DELIBERATELY. Reading a reply while it is being read
   to you is neither, and watching text appear is the thing that makes a voice
   assistant feel like a form being filled in. The transcript is not gone, it
   is behind "Back to typing", one press away.
   ========================================================================== */
.rv-mic{width:38px;height:38px;flex:none;border-radius:50%;display:grid;place-items:center;
  background:var(--raise);border:1px solid var(--hair);color:var(--ink-2);cursor:pointer;
  transition:background .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease)}
.rv-mic:hover{background:var(--raise-2);color:var(--ink);border-color:var(--hair-2)}
html[data-theme="light"] .rv-mic{background:#EEF1F7;border-color:#DDE3EC}

.rv-voice{display:none}
.sheet.voice .rv-stream,.sheet.voice .rv-sugg,.sheet.voice .rv-input,
.sheet.voice .rv-bubbles,.sheet.voice .rv-foot{display:none}
.sheet.voice .rv-voice{display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;gap:6px;padding:22px 8px 26px;flex:1;min-height:300px}

/* THE ORB MOVES WITH THE SOUND, not near it. --lvl is set from the same
   analyser reading that decides when somebody has stopped talking, so what is
   on the screen is the thing being measured. A decorative pulse beside an
   automatic cut-off would be a lie about what the app is doing. */
.rv-vorb{--lvl:0;position:relative;width:132px;height:132px;display:grid;place-items:center;
  margin-bottom:8px}
.rv-vorb i{position:absolute;inset:0;border-radius:50%;border:1px solid var(--hair-2);
  opacity:calc(.2 + var(--lvl) * .55);
  transform:scale(calc(1 + var(--lvl) * .22));
  transition:transform .12s linear,opacity .12s linear}
.rv-vorb i:nth-child(2){inset:14px;border-color:var(--secondary-soft);
  transform:scale(calc(1 + var(--lvl) * .34))}
.rv-vorb .core{width:76px;height:76px;border-radius:50%;display:grid;place-items:center;color:#fff;
  background:
    radial-gradient(circle at 50% 120%,rgba(0,164,153,.9),rgba(0,164,153,0) 58%),
    radial-gradient(circle at 12% 8%,rgba(181,37,85,.85),rgba(181,37,85,0) 52%),
    #1F2A44;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16);
  transform:scale(calc(1 + var(--lvl) * .1));transition:transform .12s linear}

/* Each state reads differently at a glance, because in voice mode the state
   IS the interface: there is nothing else on the screen to work it out from. */
.rv-voice[data-state="listening"] .rv-vorb i:nth-child(2){border-color:var(--teal-soft)}
.rv-voice[data-state="thinking"] .rv-vorb i{animation:rvThink 1.6s var(--ease) infinite}
.rv-voice[data-state="thinking"] .rv-vorb i:nth-child(2){animation-delay:.2s}
@keyframes rvThink{0%,100%{transform:scale(1);opacity:.25}50%{transform:scale(1.12);opacity:.6}}
.rv-voice[data-state="hearing"] .rv-vorb i{animation:rvThink 1.1s var(--ease) infinite}

.rv-vstate{font-family:var(--display);font-size:20px;font-weight:650;letter-spacing:-.02em;
  color:var(--ink)}
.rv-vhint{font-size:13.5px;line-height:1.5;color:var(--ink-3);max-width:34ch;min-height:2.6em}
.rv-vacts{display:flex;gap:9px;margin-top:12px;flex-wrap:wrap;justify-content:center}
.rv-vbtn{padding:11px 20px;border-radius:999px;border:0;cursor:pointer;font:inherit;
  font-size:14px;font-weight:650;background:var(--secondary);color:#fff;
  transition:filter .18s var(--ease)}
.rv-vbtn:hover{filter:brightness(1.08)}
.rv-vbtn.quiet{background:var(--raise);color:var(--ink-2);border:1px solid var(--hair)}
.rv-vbtn.quiet:hover{background:var(--raise-2);color:var(--ink);filter:none}
html[data-theme="light"] .rv-vbtn.quiet{background:#EEF1F7;border-color:#DDE3EC}
.rv-vfoot{margin-top:14px;font-size:11.5px;line-height:1.5;color:var(--ink-3);max-width:36ch}

@media (prefers-reduced-motion: reduce){
  .rv-vorb i,.rv-vorb .core{transition:none;animation:none}
}
