/* ============================================================
   REALUMA GLASS — the design system, and it is the SOURCE OF TRUTH.

   THIS FILE IS WRITTEN ONCE AND PORTED TO, NEVER EDITED PER SCREEN.
   Every value here is a decision. A screen that needs a value not in
   this file is a screen that has found a real gap: raise it, do not
   invent a literal in a view. One literal in one view is how a design
   system becomes a suggestion.

   THE LAW THAT GOVERNS EVERY COLOUR:
     champagne  = a human acting, or the brand speaking
     teal       = Realuma AI acting, or Realuma AI speaking
   Nothing else is ever champagne. Nothing else is ever teal. A screen
   that obeys this tells a landlord WHO DID WHAT before they read a word.

   AND COLOUR IS NEVER THE SOLE CARRIER. THIS IS A RULE, NOT A HABIT.
   Measured as luminance, champagne and teal are 1.24:1 apart at night and
   1.55:1 apart in day. In greyscale they are almost the same WEIGHT, so a
   person with deuteranopia or protanopia cannot tell "a human did this"
   from "Realuma AI did this" by colour at all. Every component that pairs
   the two therefore ships A WORD OR A SHAPE as well, in BOTH themes, and
   any new one must too. A badge carries its word, a turn carries the
   speaker's name, an AI panel carries the mark. If a component can only
   say who acted by being one colour or the other, it is not finished.
   ============================================================ */

:root{
  /* ---- ground ---- */
  --rg-canvas:#080F14;

  /* ---- the law ---- */
  --rg-champ:#C9A170;
  --rg-champ-lit:#E0BC8E;
  --rg-champ-wash:rgba(201,161,112,.08);
  --rg-champ-rim:rgba(201,161,112,.26);
  --rg-ink:#080F14;

  --rg-ai:#5FA0AE;
  --rg-ai-lit:#8FC6D2;
  --rg-ai-wash:rgba(95,160,174,.07);
  --rg-ai-rim:rgba(95,160,174,.26);

  /* ---- semantic, and these are NOT the law. status only. ---- */
  --rg-ok:#6FA88C;
  --rg-stop:#E07A6B;

  /* ---- ink ---- */
  --rg-text:#F7F4ED;
  --rg-muted:rgba(247,244,237,.60);
  --rg-faint:rgba(247,244,237,.58);
  --rg-hair:rgba(255,255,255,.07);

  /* ---- GLASS: a material, not a colour. tint + blur + a lit edge. ---- */
  --rg-glass:rgba(28,42,50,.52);
  --rg-glass-hi:rgba(38,55,64,.62);
  --rg-glass-deep:rgba(20,32,39,.62);
  --rg-edge:rgba(255,255,255,.10);
  --rg-edge-top:rgba(255,255,255,.20);
  --rg-blur:26px;
  --rg-blur-bar:24px;
  --rg-blur-dock:30px;

  /* ---- HOW MUCH COLOUR THE GLASS CONCENTRATES, and these are TOKENS because the numbers
     were literals in eight places and one of them was wrong for a reason no reading could
     have found. MEASURED, scripts/glass-probe.js, 2026-08-18, on a flat champagne field
     where blur() is the identity so every difference is the filter and the fill:

       the dock at saturate(200%)   0.78x the chroma it started with, at night
       the sheet at saturate(200%)  0.47x
       the same in daylight         0.56x and 0.45x

     THE BACKDROP WAS COMING OUT LESS COLOURFUL THAN IT WENT IN, on every one of them, with
     saturate() present in the stylesheet the whole time. The fill is composited ON TOP of the
     filtered backdrop, so it dilutes the result by (1 - alpha): at .55 the filter has to beat
     1/0.45 = 2.22 before the surface gains anything at all, and 2.00 does not.

     --rg-sat-dock IS 2.6 AT NIGHT for that reason and not by taste: 2.22 is the break-even and
     2.6 measures 1.04x, which is a real gain that is still nowhere near a stained window.

     AND IN DAY IT RUNS INTO A CEILING IT CANNOT PASS. The day fill is white at .72 on the dock
     and .78 on the sheet, so the backdrop contributes at most 28% and 22% of the result: the
     through-glass chroma cannot exceed 0.28 x 255 = 71 against champagne's 89, whatever
     saturate() is set to, once the channels clip. Daylight glass concentrates colour against
     the SAME GLASS WITHOUT saturate() and not against the bare backdrop, and the thing standing
     in the way is a fill alpha that exists for text contrast rather than for the material. The
     day value sits in the [data-theme="light"] block with the measurement beside it.

     --rg-sat IS AN UNCHANGED VALUE GIVEN A NAME. A card is a thinner surface than the dock and
     it is not this material; it must not drift into it by sharing its token. */
  --rg-sat:190%;        /* .rg-g and .rg-chat, the card. UNCHANGED VALUE, given a name. */
  --rg-sat-dock:260%;   /* the dock, the sheet, the menu: ONE material, one number. */
  /* .rg-bar, .rg-back, .rg-mini and the lens keep their literals and are NOT in this family.
     The sticky capsule is a 24px blur that materialises on a scroll timeline, the lens is a 7px
     blur that must not smear its own chromatic rim, and the two small controls are 18px and
     12px. Naming them here would invite the next person to point them at one token, and four
     surfaces that read as one material would be a claim none of them makes today. */

  /* ---- THE RIM. A lens bends light hardest at its edge, and that edge is where almost all of
     the "this is glass" reading comes from. A band of the outer few pixels carries a heavier
     blur and a little more light than the middle of the same surface.
     5px BECAUSE OF THE RADIUS RATHER THAN THE LOOK. The dock's corner is 27px; a band wider
     than about a fifth of it stops reading as an edge and starts reading as a second, smaller
     panel inside the first. */
  /* HOW BRIGHT THE LIT EDGE IS. A token because it is a different number in daylight, where the
     surface it sits on is already near white and .55 would be an invisible line on an invisible
     line. Measured contrast against the pixel below it is printed by scripts/glass-probe.js. */
  --rg-spec:.55;
  --rg-rim-w:5px;
  /* ON TOP OF THE MATERIAL'S OWN BLUR, NOT INSTEAD OF IT, and two Gaussians add in QUADRATURE
     rather than by addition: the band's effective radius is sqrt(30^2 + r^2). At r = 10 that is
     31.6 against the middle's 30, a 5% difference, and it measured as 0.99x of a hard edge
     surviving — which is to say no difference at all.
     IT WAS 30 AND IT IS 18, AND THE REASON IS THAT 30 BOUGHT NOTHING. Measured smear at the band
     against the middle of the same panel: 102.1px against 75.4 at r = 30, and 101.0 against 75.4
     at r = 18. The same effect. What 30 did buy was dropped frames — a paired scroll at 390 showed
     16 of 157 with the band on against 5 with it off, and at 18 that pair is 8 against 0. The
     material's own 30px dominates the sum, so the band's radius is nearly free to lower and nearly
     useless to raise. */
  --rg-rim-blur:18px;
  --rg-rim-lift:1.06;

  /* ---- THE SPLIT. A thick edge does not pass white through unchanged: it disperses, and the
     two ends of the spectrum come out on opposite sides. Two inset shadows, one hair of the
     edge each, warm on the right and cool on the left.
     WARM ON THE RIGHT BECAUSE THAT IS WHERE THE LIGHT IS. Section 1's world puts its champagne
     radial at 76% 8% and its teal at 12% 30%, so the room is warm from the upper right; a
     dispersion that put the warm end on the left would be a lens disagreeing with its own
     scene. This is the one thing on the surface that is not symmetrical, and it is not
     symmetrical for a reason that is already in the file.
     NEITHER COLOUR IS NEW. rgba(255,150,80) and rgba(96,196,255) are the two ends of the
     conic dispersion .rg-lens::after already carries, verbatim. The law in section 0 is
     untouched: these are refraction, not champagne and not teal, and at this weight they are
     nowhere near either.
     ALMOST INVISIBLE ALONE IS THE SPECIFICATION. Measured contrast against the pixel beside
     it is printed by scripts/glass-probe.js section 3; anything a person could NAME as a
     colour on this edge is decoration and has to come back down. */
  --rg-split-w:inset -1px 0 0 rgba(255,150,80,.18);
  --rg-split-c:inset  1px 0 0 rgba(96,196,255,.18);

  /* ---- CONCENTRIC RADII. Apple's rule: inner = outer minus padding. ----
     A 26px card holding an 18px button holding a 12px icon nests. Mixing
     scales is what makes corners fight each other. */
  --rg-r-out:26px;
  --rg-r-mid:18px;
  --rg-r-in:12px;
  --rg-r-sm:9px;
  --rg-r-pill:999px;

  /* ---- SPACE. six steps, no more. ---- */
  --rg-sp1:6px; --rg-sp2:11px; --rg-sp3:16px; --rg-sp4:22px; --rg-sp5:34px; --rg-sp6:52px;
  /* SEVENTH STEP, AND IT IS THE MARKETING SITE'S RATHER THAN THE APP'S. Six
     steps end at 52px because an app screen is a stack of cards and 52px is
     already a lot of air between two of them. A marketing section is a
     different object: it has to read as a chapter break, and the site has been
     breathing at 100px since it was written. Declared here rather than
     site-local so the site is not carrying a private scale nothing else can
     see. Nothing in the app uses it, and nothing in the app should. */
  --rg-sp7:88px;

  /* ---- MOTION. ONE spring, so the whole app moves as one object. ---- */
  --rg-spring:cubic-bezier(.34,1.4,.5,1);     /* overshoot: presses, chevrons, icons */
  --rg-ease:cubic-bezier(.32,.72,0,1);        /* decelerate: heights, opacity, sweeps */
  --rg-t-fast:.2s; --rg-t-mid:.38s; --rg-t-slow:.62s;

  /* ---- TYPE ---- */
  /* ONE WEIGHT IS LOADED, 400, AND THAT IS WHY THE font-weight:400 RULES BELOW EXIST. Nothing in
     this file ever asked for bold; the USER AGENT did, because h1 to h6 default to font-weight:bold
     and four of these classes land on a heading tag. Marcellus had no 700 to load so the browser
     smeared the 400 outlines into a fake. PLAYFAIR DISPLAY DOES HAVE A REAL 700 and it is
     deliberately NOT loaded: a real bold Playfair is a heavier, more editorial voice than this
     product has ever had, and arriving at it by way of a user-agent default is not choosing it.
     The explicit 400s keep the decision where it was.
     GEORGIA IS OUT OF THE CHAIN. It was the declared second choice and it sets 8 of 30 Vietnamese
     stacked characters, so on the language this product defaults to it was never a fallback at all,
     it was a detour on the way to the generic serif. The generic is what actually carries Vietnamese
     on every current OS, so it is named directly. */
  --rg-display:'Playfair Display',serif;
  --rg-body:'Be Vietnam Pro',system-ui,-apple-system,sans-serif;
  --rg-mono:'IBM Plex Mono',ui-monospace,monospace;

  --rg-fs-hero:44px; --rg-fs-h1:25px; --rg-fs-num:26px; --rg-fs-body:14px;
  --rg-fs-sm:12.5px; --rg-fs-xs:11.5px;
  --rg-fs-lab:9.5px;   /* mono labels, always uppercase, always tracked */
  --rg-tr-lab:.17em;
  /* EIGHTH STEP, ALSO THE SITE'S. --rg-fs-hero is a fixed 44px because a phone
     is 390px wide and a hero there is a fixed object. A marketing hero has to
     hold from 320px to 2560px, so its step is fluid and starts exactly where
     the app's hero sits: the two properties agree at the small end and only the
     site grows. Nothing in the app uses it. */
  --rg-fs-display:clamp(var(--rg-fs-hero), 1.5rem + 4.6vw, 76px);
}

@supports (interpolate-size: allow-keywords){ :root{ interpolate-size: allow-keywords; } }

/* ============================================================
   1. THE WORLD: ATMOSPHERE, NOT A LAMP.
   Fixed behind everything, so every glass surface has something real to
   refract. It drifts, slowly. That drift is the whole reason the material
   is called liquid rather than frosted: a still backdrop reads as plastic.

   IT IS QUIETER THAN IT WAS, AND THE REASON IS CONTRAST RATHER THAN TASTE.
   Glass composites what is behind it, so a panel over the old champagne
   radial rendered a fill 1.77 times lighter than the same panel over the
   deep pool. That made a text contrast a matter of WHERE THE USER HAD
   SCROLLED TO: 16 of 38 text-on-surface pairings fell below AA on the
   bright side and passed on the dark. A panel whose legibility depends on
   its position is not a panel, it is a lottery.
   And at .34 the champagne radial competed with the champagne that MEANS A
   HUMAN ACTED. Dimming it makes the law colour rarer and therefore
   stronger, which is the same argument that made the orb champagne.
   Measured: the composited panel-fill spread falls from 1.766:1 to
   1.559:1, and the world still reads as the world, warm top right, cool
   top left, the pool below.
   ============================================================ */
.rg-world{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(78% 58% at 76% 8%,  rgba(201,161,112,.17), transparent 62%),
    radial-gradient(72% 62% at 12% 30%, rgba(95,160,174,.13),  transparent 64%),
    radial-gradient(90% 70% at 50% 96%, rgba(20,44,54,.85),    transparent 70%),
    linear-gradient(172deg,#10222B 0%,#0A161C 46%,#080F14 100%);
  animation:rg-drift 34s ease-in-out infinite alternate;
}
@keyframes rg-drift{
  0%{transform:scale(1.06) translate3d(0,0,0)}
  100%{transform:scale(1.14) translate3d(-2.5%,-2%,0)}
}

/* ============================================================
   2. GLASS
   .rg-g is the material. Four inset shadows do what a real material does:
   a lit top rim, a hairline all round, thickness at the base, and a cast.
   ::before is the specular sweep, light travelling across a curved face.
   ============================================================ */
/* THE DISPLAY FACE, DECLARED ONCE FOR EVERY CLASS THAT USES IT. Grouped rather than repeated
   per class so a future <h3 class="rg-empty-t"> cannot silently start synthesising: today
   .rg-hero is an h1, .rg-task-h an h2, .rg-sheet-title an h3 and .rg-inset-h an h5 (that one is
   in realuma-forms.css, beside its own declaration). Adding a display class means adding it here.
   FONT-WEIGHT ONLY. Nothing else about these elements is shared and nothing else belongs here. */
.rg-bar-t,.rg-hero,.rg-task-h,.rg-num-val,.rg-empty-t,.rg-sheet-title,.rg-av{font-weight:400}

/* ================================================================================================
   THE ACT, PINNED IN THE THUMB.

   THE TOP OF A PHONE IS FOR READING AND THE BOTTOM IS FOR ACTING. On a 390px screen anything above
   roughly 60% is a stretch, and the unit screen's one act sat mid-card, after six lines of
   explanation, needing a scroll. This is the same act in the place a thumb already is.

   IT IS THE DOCK'S GEOMETRY, NOT A SECOND OPINION ABOUT IT. Same absolute anchor inside .app, same
   width rule, same safe-area handling. What lifts it clear of the dock is --rg-act-lift, which is
   MEASURED at runtime by UI.act from the dock's own box rather than written here as a constant: a
   number copied from another component is a number that drifts the first time that component
   changes. --rg-act-h is this bar's own height, published for .view's bottom padding below.

   EMPTY IS INVISIBLE. A screen with nothing pending has no bar at all, so the dead zone stays what
   it was and the screen scrolls clean. That is why :empty is a rule and not a class somebody has to
   remember to remove.

   ONE ACT. The bar is a single child by construction: UI.act takes one control. Sections 7 and 8
   already allow at most one .rg-btn per screen and this is where that one now lives. */
.rg-act{
  position:absolute;left:50%;transform:translateX(-50%);z-index:55;
  /* THE FALLBACK IS 145 AND NOT 74, and the difference is the bloom. It covers exactly one frame,
     the paint between the markup going in and UI.act reading the boxes, and 74 put that frame's
     bar underneath the bloom. 145 is 62 of dock, 8 of gap, 64 of bloom and 11 of clearance, which
     is what the measurement returns on a phone. It is a fallback and not a source of truth: UI.act
     overwrites it on the same frame, and where the dock is display:none it writes 0 rather than
     letting this stand. */
  bottom:calc(18px + env(safe-area-inset-bottom,0px) + var(--rg-act-lift,145px));
  width:min(400px,calc(100% - 30px));
  display:flex;gap:var(--rg-sp2);
  padding:var(--rg-sp1);
  border-radius:var(--rg-r-out);
  background:var(--rg-glass-deep);
  backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  -webkit-backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  box-shadow:inset 0 1px 0 var(--rg-edge-top),inset 0 0 0 1px var(--rg-edge),
             0 12px 30px -18px rgba(0,0,0,.8);
}
.rg-act:empty{display:none}

/* ⛔ THE PINNED ACT, WITH A KEYBOARD UP.  ST21, 2026-08-21.
   .rg-act is position:absolute inside .app, which is height:100dvh — the LAYOUT viewport, which a
   software keyboard does not resize. So the one control a screen is asking somebody to press sat
   behind the keyboard on every screen that pins an act AND has an inline field, which is all five
   that pin one: calendar, feedback, network, routes and tenancy.

   ⛔ THE OBVIOUS FIX IS THE WRONG ONE AND WAS MEASURED BEFORE IT WAS WRITTEN. Making .app itself
   consume --rg-vvh would fix this bar — and would also move .rg-dock-nav, which is the NAVIGATION
   DOCK and is absolutely positioned against the same box at the same bottom edge. The dock would
   ride up on top of the keyboard on every screen, covering content, which is worse than what it
   replaces. A keyboard is a reason to hide navigation, not to float it.

   SO ONLY THIS BAR MOVES, by exactly the keyboard's height. The keyboard is (100dvh - --rg-vvh):
   the layout viewport minus the visible one. Adding a gap on top of it puts the act immediately
   above the keyboard and touches nothing else in the layout.
   --rg-act-lift IS DROPPED HERE ON PURPOSE. It exists to clear the dock, and with a keyboard up the
   dock is behind the keyboard — reserving 74px for something nobody can see is the same mistake
   .rg-chat's 154px reserve was. */
:root.rg-kb .rg-act{
  bottom:calc(var(--rg-kbh, 0px) + var(--rg-sp2));
}
/* The one control fills the bar, because a bar holding one act that is narrower than it reads as a
   bar with something missing from it. */
.rg-act > .rg-btn{flex:1;margin-top:0}

.rg-g{
  position:relative;
  background:var(--rg-glass);
  backdrop-filter:blur(var(--rg-blur)) saturate(var(--rg-sat));
  -webkit-backdrop-filter:blur(var(--rg-blur)) saturate(var(--rg-sat));
  border-radius:var(--rg-r-out);
  box-shadow:
    inset 0 1px 0 var(--rg-edge-top),
    inset 0 0 0 1px var(--rg-edge),
    inset 0 -18px 34px -22px rgba(0,0,0,.5),
    0 18px 40px -22px rgba(0,0,0,.7);
}
.rg-g::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(146deg, rgba(255,255,255,.11) 0%, transparent 34%);
  mix-blend-mode:screen;
}
/* champagne glass: a human's object. the hero task, a primary surface. */
.rg-g-champ{
  background:linear-gradient(152deg,rgba(201,161,112,.20),rgba(201,161,112,.05) 52%),var(--rg-glass-hi);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.26),
    inset 0 0 0 1px var(--rg-champ-rim),
    inset 0 -22px 40px -26px rgba(0,0,0,.55),
    0 24px 54px -26px rgba(201,161,112,.30);
}
/* teal glass: Realuma AI's object. ONLY when the AI is the actor. */
.rg-g-ai{
  background:linear-gradient(150deg,rgba(95,160,174,.20),rgba(95,160,174,.05) 60%),var(--rg-glass);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.20),
    inset 0 0 0 1px var(--rg-ai-rim),
    0 16px 36px -22px rgba(0,0,0,.6);
}

/* ============================================================
   2b. THE RIM BAND — the outer few pixels of the thick surfaces.

   WHAT IT IS. One absolutely positioned child, masked to a ring, carrying a
   SECOND backdrop-filter. Its backdrop is the material layer that painted
   just below it, so its blur COMPOUNDS with the material's rather than
   replacing it, and the middle of the surface is not touched at all.

   WHY A MASK RATHER THAN AN INNER BOX. Measured before it was written
   (scripts/glass-probe.js): a -webkit-mask CLIPS backdrop-filter output, so
   the ring trick .rg-lens::before already uses for its dispersion works for
   a filter too. The alternative was a second, smaller element inset from
   the first, which is a second box to keep in sync with three radii and it
   would have painted a visible seam where its edge fell.

   WHY AN ELEMENT AND NOT A PSEUDO. .rg-menu-panel has both of its pseudos
   spoken for — the material on ::before and the chromatic dispersion ring
   on ::after — and three surfaces sharing one effect through two different
   mechanisms is how a design system becomes a suggestion. One class, three
   call sites, all real elements.

   IT IS THE FIRST CHILD, ALWAYS, and that is not tidiness. Paint order is
   what decides what a backdrop-filter can see: after ::before so it samples
   the material, before the content so it never blurs a label.

   NOTHING IS ANIMATED HERE. It is a static layer on a surface that is
   dragged, and the frame cost of it is measured in the probe's section 5
   rather than assumed.
   ============================================================ */
.rg-rim{
  position:absolute;inset:0;z-index:0;
  border-radius:inherit;pointer-events:none;
  padding:var(--rg-rim-w);
  backdrop-filter:blur(var(--rg-rim-blur)) brightness(var(--rg-rim-lift));
  -webkit-backdrop-filter:blur(var(--rg-rim-blur)) brightness(var(--rg-rim-lift));
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude;
  /* ---- THE SPECULAR HIGHLIGHT, ON THE BAND'S OWN TOP EDGE ----
     THE CHEAPEST CUE THAT READS AS THICK GLASS RATHER THAN TINTED FILM. A pane has a top face, and
     a top face catches the light in a line: bright where the light is straight on, gone by the
     ends. The existing `inset 0 1px 0` hairline is FLAT — the same value corner to corner — which
     is what a printed border looks like, not what a lit edge looks like.
     A GRADIENT AND NOT A COLOUR. Transparent, white, transparent. No new colour enters the system:
     the ink is the same rgba white every rim in this file is drawn with, and what is new is that
     its ALPHA varies along the edge.
     INSET FROM BOTH ENDS BY 14%, because the corners are 27px arcs and the top edge does not exist
     there; a hairline drawn to the box's full width paints across the curve and reads as a crack.
     IT IS INSIDE THE MASK, so it lands on the band and cannot stray into the middle of the
     surface, and it is 1px tall against tab labels that begin 40px lower. Both measured. */
  /* THE SPLIT RIDES THE BAND, AND IT HAD TO MOVE HERE. It was on the material layer first, and
     measured 0.4 degrees of hue between the two edges on a fringe that should have shown several:
     the band paints ABOVE the material and blurs what it finds by another ten pixels, so a one
     pixel fringe underneath it was being smeared into the fill before anybody saw it. One layer
     up it survives, and it is also where it belongs — a lens splits light AT its rim, which is
     the thing this band is.
     IT IS ALSO NOW IN ONE PLACE. On the material it had to be repeated in seven rules, because
     box-shadow is a single property and every state and every theme that rewrites the list would
     have dropped it. */
  box-shadow:var(--rg-split-w), var(--rg-split-c);
}
.rg-rim::before{
  content:'';position:absolute;left:14%;right:14%;top:0;height:1px;
  border-radius:1px;pointer-events:none;
  background:linear-gradient(90deg,
    transparent, rgba(255,255,255,var(--rg-spec)) 32%,
    rgba(255,255,255,var(--rg-spec)) 68%, transparent);
}

/* ============================================================
   3. ENTRANCE
   Everything rises. Stagger by adding rg-d1 through rg-d4, in the order a
   person reads, never in DOM order for its own sake.
   ============================================================ */
.rg-rise{animation:rg-rise .8s var(--rg-spring) both}
@keyframes rg-rise{from{opacity:0;transform:translateY(22px) scale(.97)}}
.rg-d1{animation-delay:.06s} .rg-d2{animation-delay:.12s}
.rg-d3{animation-delay:.18s} .rg-d4{animation-delay:.24s}

/* ============================================================
   4. THE FLOATING BAR
   Materialises as content passes beneath it. Scroll-driven, no JS.
   Content goes UNDER the glass. That is the point of the layer.
   ============================================================ */
/* IT FLOATS. It spanned wall to wall with square corners and was the one object in the
   product that did not, while the dock hovers, the lens flies and the orb has no edge. Inset
   from both edges now, the dock's radius, the world visible past it, and the lens's own
   chromatic dispersion on ::after, which is what says dock rather than card.

   IT IS ALWAYS PRESENT AT FULL OPACITY, and that is the correction rather than a preference.
   .rg-bar-title used to start at opacity:0 and be faded in by animation-timeline:scroll(), which
   means a screen that never scrolls never gets its title: exactly what the thread screen hit,
   where the bar is the only thing naming the customer. A capsule that fades in from nothing has
   the same failure on every non-scrolling screen.
   SO THE SCROLL DRIVES THE MATERIAL ONLY. Blur 0 to full, fill transparent to the theme's deep
   glass, rim 0 to rest alpha. At the top of a page it is an invisible frame holding a title and a
   control; as content passes beneath it, it BECOMES glass. The consequence, accepted rather than
   discovered: at scroll-top the title sits on the world with no fill behind it, so it has to
   clear AA against the world's brightest point in both themes. It does. */
/* NOT flex:none, AND THAT IS A MEASUREMENT RATHER THAN A PREFERENCE. The obvious
   reading of "the header does not reserve its height" is that the bar, a flex item of
   .rg-chat-screen's column, was being compressed by flex-shrink. Squeezed to 300, 150
   and 90px against a two-line Vietnamese title on 2026-08-13, the bar held 60px in
   every case with and without flex:none: min-height:auto already refuses to shrink an
   item whose overflow is visible, so the declaration would have changed nothing and
   its comment would have claimed a fix it did not make. The height defect on this bar
   is line-height, below. */
.rg-bar{
  /* ---- STUCK FLUSH, AND THE SAFE AREA IS NOT COUNTED TWICE ----
     IT STUCK AT calc(--rg-sp1 + env(safe-area-inset-top)) AND BOTH HALVES WERE WRONG.
     THE 6px LEFT A TRANSPARENT STRIP between the top of the scrollport and the top of the bar, and
     everything scrolling under the bar was visible through it: measured on nine screens, a 6.0px
     strip on every one, with a section icon and a row title actually showing through on four.
     It survived every pass this harness ever made because it does not exist at scroll top: there is
     nothing above the bar until a finger has moved.
     AND THE INSET WAS ALREADY SPENT. .app carries padding-top:env(safe-area-inset-top), and the
     scroller is inside it, so the scrollport already begins below the notch. Adding the inset here
     added it a SECOND time, which is why the photograph shows so much more than 6px poking out: on
     a notched phone the strip was 6px plus the whole notch.
     THE 6px GAP IS KEPT WHERE IT BELONGS, in the margin below, so the pill still floats clear of
     the content while the page is at rest and closes flush the moment it sticks. */
  position:sticky;top:0;z-index:40;
  display:flex;align-items:center;gap:13px;
  /* NO TOP MARGIN, AND THAT IS ABOUT SAFARI RATHER THAN ABOUT SPACING. `position:sticky` with
     `top:0` and a top margin puts two readings of the spec against each other: the sticky rectangle
     constrains the element's MARGIN box, so a 6px top margin can leave the border box 6px down and
     the strip open again. Chrome measures 0.0px here; WebKit is the engine the photograph came from
     and cannot be measured in this harness. Removing the margin removes the disagreement instead of
     betting on it. The air above the bar at rest is .view's own 16px of padding, which is where a
     screen's top gutter belongs anyway. */
  margin:0 var(--rg-sp3);
  padding:11px var(--rg-sp3);
  border-radius:27px;
  background:rgba(8,15,20,0);
  backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);
  animation:rg-bar-in linear both;
  animation-timeline:scroll();animation-range:60px 190px;
}
/* the rim rides the same timeline, from nothing to the dock's rest weight */
.rg-bar::after{
  content:'';position:absolute;inset:-1px;border-radius:inherit;padding:1.4px;
  pointer-events:none;
  background:conic-gradient(from 200deg,
    rgba(255,96,64,.62), rgba(255,214,120,.52), rgba(126,255,190,.48),
    rgba(96,196,255,.62), rgba(196,128,255,.5), rgba(255,96,64,.62));
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude;
  filter:blur(1.2px);opacity:0;
  animation:rg-bar-rim linear both;
  animation-timeline:scroll();animation-range:60px 190px;
}
@keyframes rg-bar-rim{to{opacity:.62}}
@keyframes rg-bar-in{to{
  background:var(--rg-glass-deep);
  box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
  backdrop-filter:blur(var(--rg-blur-bar)) saturate(180%);
  -webkit-backdrop-filter:blur(var(--rg-blur-bar)) saturate(180%);
}}
/* NO opacity ANIMATION. The title is present from the first frame, on every screen, whether or
   not that screen can scroll. */
.rg-bar-title{opacity:1;transform:none}
/* LINE-HEIGHT IS A VIETNAMESE QUESTION HERE, and 1.1 was set for a Latin sample.
   Vietnamese stacks a tone mark over a vowel that may already carry a diacritic (Ế Ộ
   Ữ Ằ), which needs roughly 1.3 of the font size before the mark leaves the line box.
   At 1.1 the marks are clipped and, with the subtitle 2px below, the two lines
   occupy the same pixels. Every title on this bar is an agent-supplied business or
   assistant name, so Vietnamese is the DEFAULT case, not the edge one.
   overflow-wrap:anywhere because these are proper nouns with no break opportunity:
   without it a long unbroken name overflows the capsule instead of wrapping, which
   is the clip mid-sentence. */
.rg-bar-t{font-family:var(--rg-display);font-size:16.5px;line-height:1.32;
  overflow-wrap:anywhere}
.rg-bar-s{font-family:var(--rg-mono);font-size:var(--rg-fs-lab);letter-spacing:.16em;
  line-height:1.45;overflow-wrap:anywhere;
  text-transform:uppercase;color:var(--rg-muted);margin-top:2px}
/* THE SIGNED-IN EMAIL UNDER THE BUSINESS NAME. Deliberately NOT .rg-bar-s: that rule uppercases,
   and an email is a literal that must be read character by character — the two accounts on this
   project differ by one dot, and `MATEOB787.9@GMAIL.COM` is harder to compare than the real thing.
   No letter-spacing and no transform, so what renders is exactly what the session holds. */
.rg-bar-who{font-family:var(--rg-mono);font-size:var(--rg-fs-lab);line-height:1.45;
  overflow-wrap:anywhere;color:var(--rg-muted);opacity:.85;margin-top:3px;text-transform:none}

.rg-back{
  width:44px;height:44px;flex:none;border-radius:50%;cursor:pointer;
  border:1px solid rgba(255,255,255,.14);color:var(--rg-champ);
  background:rgba(28,42,50,.5);
  backdrop-filter:blur(18px) saturate(180%);-webkit-backdrop-filter:blur(18px) saturate(180%);
  display:grid;place-items:center;font-size:18px;line-height:1;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.18);
  transition:transform var(--rg-t-mid) var(--rg-spring);
}
.rg-back:active{transform:scale(.9)}

/* BUSY IS CARRIED BY THE DISABLED STATE, AND THE TURNING IS DECORATION ON TOP.
   Both refresh controls set disabled AND add .rg-spinning together, so the flat state below is
   what actually says "working" and it is not an animation: section 19 kills every animation and
   this survives it untouched. That is the same contract as section 18's four states, told apart
   by shape rather than by movement. A person with reduced motion sees the glyph dim to .45 and
   the cursor refuse, which is the vocabulary realuma-forms.css already uses for a control that
   cannot be touched right now, and gets no rotation at all.
   THE DURATION IS A TOKEN, NOT A NUMBER. --rg-t-slow is .62s, which is the nearest thing this
   system owns to the .6s that app.css has spun #refresh-top at since before the glass existed.
   It is declared as a transition duration and is being used as a loop period, which is a
   stretch worth naming: the system has no loop-duration token, and its five other infinite
   animations each carry a literal chosen per effect. rf-spin was NOT reused. It would be the
   first name this file borrows from app.css, and it is named for an element the ported screens
   now hide, so the spin would die silently the day that rule goes. */
.rg-back:disabled{opacity:.45;cursor:not-allowed}
.rg-spinning{animation:rg-spin var(--rg-t-slow) linear infinite}
@keyframes rg-spin{to{transform:rotate(360deg)}}

/* ============================================================
   5. HEADINGS
   ============================================================ */
.rg-head{padding:var(--rg-sp5) var(--rg-sp4) var(--rg-sp4);position:relative}
.rg-eyebrow{font-family:var(--rg-mono);font-size:10px;letter-spacing:.22em;
  text-transform:uppercase;color:var(--rg-champ);margin-bottom:12px}
.rg-hero{font-family:var(--rg-display);font-size:var(--rg-fs-hero);line-height:.98;letter-spacing:-.01em;margin:0}
.rg-sub{font-size:var(--rg-fs-body);color:var(--rg-muted);margin-top:9px}

/* ============================================================
   6. BADGES — ONE STATE, ONE BADGE.
   A badge names a state and nothing else. Never concatenate two facts
   into one string: "18.336.000₫ · draft · Sent to 1" is three facts
   crushed together and it contradicts itself. Amount, state and delivery
   are three separate elements.
   Colour is never the only signal: every badge carries a word.
   ============================================================ */
.rg-badge{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--rg-mono);font-size:var(--rg-fs-lab);letter-spacing:.13em;
  text-transform:uppercase;padding:5px 11px;border-radius:var(--rg-r-pill);white-space:nowrap;
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  box-shadow:inset 0 0 0 1px currentColor;
}
.rg-badge::before{content:'';width:5px;height:5px;border-radius:50%;
  background:currentColor;box-shadow:0 0 8px currentColor}
.rg-b-live {color:var(--rg-ok);   background:rgba(111,168,140,.11)}
.rg-b-paid {color:var(--rg-ok);   background:rgba(111,168,140,.11)}
.rg-b-sent {color:var(--rg-ai);   background:var(--rg-ai-wash)}
.rg-b-wait {color:var(--rg-champ);background:var(--rg-champ-wash)}
.rg-b-due  {color:var(--rg-stop); background:rgba(224,122,107,.07)}
/* A BADGE INSIDE THE LIT CARD NEEDS ITS OWN GROUND, and this is the second thing .rg-task::after
   has broken. That bloom is a 230px champagne radial pinned to the card's top right, and the badge
   row sits directly under it, where the glass reads rgb(58,73,76) rather than the rgb(20,32,39) a
   badge is designed against. Every semantic ink then fails at night: teal 3.18:1, stop 3.59:1, ok
   3.66:1, measured on the tenancy standing card. The single delivery badge that card has carried
   since it was built was already below AA; this commit measured it rather than caused it.
   THE GROUND, NOT THE INKS. Three lit variants would be three new tokens for one card, and the
   next semantic colour would need a fourth. The tinted washes above are alpha .07 to .11, which is
   a hint of hue and no protection at all, so on this one surface the badge carries a scrim and the
   hue stays where it belongs, in the ink and in the badge's own dot.
   NIGHT ONLY. In day the card is ivory, the badges are dark ink on a pale wash, and every one of
   them measures 5.53:1 or better already. A scrim there would be a dark chip on paper. */
.rg-task .rg-badge{background:rgba(8,15,20,.46)}
[data-theme="light"] .rg-task .rg-badge{background:none}
[data-theme="light"] .rg-task .rg-b-live,
[data-theme="light"] .rg-task .rg-b-paid{background:rgba(111,168,140,.11)}
[data-theme="light"] .rg-task .rg-b-sent{background:var(--rg-ai-wash)}
[data-theme="light"] .rg-task .rg-b-wait{background:var(--rg-champ-wash)}
[data-theme="light"] .rg-task .rg-b-due {background:rgba(224,122,107,.07)}
.rg-b-draft{color:var(--rg-faint);background:rgba(247,244,237,.03);
  box-shadow:inset 0 0 0 1px rgba(247,244,237,.14)}

/* ---------- A BADGE THAT IS A BUTTON, AND THE ONE PLACE THAT HAPPENS ----------
   A badge NAMES A STATE and is a span in fifteen places. On the broker agenda it is also the
   control that opens the status sheet, which makes it the only interactive badge in the product.
   MEASURED 16 Aug at 360 and 390: 25.0px tall, against the 44px floor the four controls in section
   7 already carry. Its nearest interactive neighbour on that row is NOTHING — it is the only
   control in it — so the area can grow in every direction without taking a tap from anything.
   A TRANSPARENT AREA AND NOT A TALLER BADGE, and that distinction is the whole of this fix. A badge
   is a small pill on purpose: a 44px one would be a button wearing a badge's colours, and the row
   would read as having two actions. Nothing here paints. The pill keeps its exact size, radius,
   dot and ink; what grows is the region a thumb may land in, from 25px to 45.
   SCOPED TO `button`, so the fifteen spans are untouched: a hit area on a label is a region that
   swallows taps and does nothing with them. */
button.rg-badge{position:relative;cursor:pointer}
button.rg-badge::after{content:'';position:absolute;inset:-10px}

/* ============================================================
   7. BUTTONS — SIZE ENCODES IMPORTANCE.
   .rg-btn  the one thing to do on this screen. There is at most ONE.
   .rg-mini a row action. Many.
   .rg-quiet a way out. Never competes.
   Uniform buttons are what made the old screen unreadable: every action
   looked equally urgent, so none of them did.
   ============================================================ */
.rg-btn{
  position:relative;display:block;width:100%;padding:17px var(--rg-sp4);border:none;
  border-radius:var(--rg-r-mid);cursor:pointer;text-align:center;overflow:hidden;
  background:linear-gradient(178deg,var(--rg-champ-lit),var(--rg-champ));
  color:var(--rg-ink);font-family:var(--rg-body);font-size:16px;font-weight:600;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.5),
    inset 0 -2px 6px rgba(0,0,0,.14),
    0 10px 24px -10px rgba(201,161,112,.6);
  transition:transform .42s var(--rg-spring), box-shadow var(--rg-t-fast) var(--rg-ease);
}
.rg-btn::after{content:'';position:absolute;inset:0;transform:translateX(-120%);
  background:linear-gradient(105deg,transparent 32%,rgba(255,255,255,.42) 47%,transparent 62%)}
.rg-btn:active{transform:scale(.965);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4),0 4px 12px -6px rgba(201,161,112,.5)}
.rg-btn:active::after{transform:translateX(120%);transition:transform var(--rg-t-slow) var(--rg-ease)}
.rg-btn small{display:block;font-size:12px;font-weight:400;opacity:.68;margin-top:3px}

/* Realuma AI's primary action, on the rare screen where the AI acts. */
.rg-btn-ai{background:linear-gradient(178deg,var(--rg-ai-lit),var(--rg-ai));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 10px 24px -10px rgba(95,160,174,.6)}

/* ============================================================
   TAP TARGETS, AND FOUR OF THEM WERE TOO SMALL TO HIT.
   MEASURED, 15 Aug, across every ported screen at 360px: .rg-mini rendered 34px tall, .rg-pick 35,
   .rg-quiet 37 and .rg-back 38. The floor a thumb needs is 44px (Apple HIG) or 48dp (Material), and
   this product is used one handed, mid day, walking. A 34px target is not a style opinion, it is a
   miss, and the thing under a missed row action is usually another row's action.
   min-height AND NOT height, so nothing that already exceeds the floor shrinks to meet it and no
   label loses its second line. Every one of the four keeps its exact type, colour, radius, rim and
   transition: this section adds height and nothing else.
   IT COSTS NO ROW HEIGHT WHERE IT MATTERS MOST. A feed row is 63 to 91px tall because of its own
   two lines of text, so a 44px button inside it changes nothing at all.
   ============================================================ */
.rg-mini{
  flex:none;min-height:44px;padding:0 15px;border-radius:var(--rg-r-sm);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.03);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  color:var(--rg-champ);font-family:var(--rg-body);font-size:13px;font-weight:500;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
  transition:transform var(--rg-t-mid) var(--rg-spring), background var(--rg-t-fast);
}
.rg-mini:active{transform:scale(.92);background:var(--rg-champ-wash)}
.rg-mini-ai{color:var(--rg-ai-lit)}
.rg-mini-danger{color:var(--rg-stop)}

.rg-quiet{display:block;width:100%;min-height:44px;margin-top:var(--rg-sp2);padding:12px;
  background:none;border:none;color:var(--rg-muted);font-family:var(--rg-body);
  font-size:13.5px;cursor:pointer;transition:color var(--rg-t-fast)}
.rg-quiet:active{color:var(--rg-text)}

/* ============================================================
   8. THE HERO TASK — the one thing to do, and it is BIG.
   Exactly one per screen. If a screen has two, the screen has no priority
   and the design has failed, not the developer.
   ============================================================ */
.rg-task{margin:var(--rg-sp1) var(--rg-sp3) 0;padding:var(--rg-sp4) var(--rg-sp4) var(--rg-sp4);overflow:hidden}
.rg-task::after{content:'';position:absolute;width:230px;height:230px;right:-70px;top:-110px;
  background:radial-gradient(circle,rgba(201,161,112,.28),transparent 66%);
  pointer-events:none;filter:blur(14px)}
/* THE ONE PLACE CHAMPAGNE SITS INSIDE ITS OWN LIGHT SOURCE, so it takes the lit member rather
   than the text one. .rg-task::after is a 230px champagne bloom pinned to the card's top right,
   and the eyebrow is the line directly under it: --rg-champ on that ground measured 3.81:1 at
   night and failed in eight of the nine hero states across both dashboards. The body text two
   lines lower passes at the same alpha family because it is below the bloom, which is what
   identified the cause as the light rather than the colour.
   --rg-champ-lit CLEARS AT 5.08:1 WORST AND KEEPS THE HUE, which is the whole point: this label
   is inside a champagne object and turning it to ink to win a ratio would have paid for contrast
   with meaning. It takes the day override below for the reason section 22 already states about
   teal: the lit member is the FILL member in day, so anywhere night uses it as TEXT, day takes
   the deep one. Same mechanism, first champagne entry. */
.rg-task-eye{font-family:var(--rg-mono);font-size:var(--rg-fs-lab);letter-spacing:.2em;
  text-transform:uppercase;color:var(--rg-champ-lit);margin-bottom:11px;position:relative}
.rg-task-h{font-family:var(--rg-display);font-size:var(--rg-fs-h1);line-height:1.2;
  margin:0 0 8px;position:relative}
.rg-task-p{font-size:var(--rg-fs-body);color:rgba(247,244,237,.68);
  margin:0 0 var(--rg-sp4);max-width:34ch;position:relative}

/* ============================================================
   9. NUMBERS — they COUNT UP on entry.
   A number that appears is a fact. A number that counts is an event, and
   an event is remembered. Driven by rg-motion.js reading data-rg-count.
   The element ALWAYS ships with its final text in the markup, so a person
   with JS disabled or reduced motion on sees the true figure immediately.
   ============================================================ */
/* minmax(0,1fr) AND NOT 1fr, AND IT IS A DEFECT FIX RATHER THAN A PREFERENCE. A bare 1fr is
   minmax(AUTO,1fr), and auto resolves to min-content: a track whose figure is wider than its share
   does not shrink, it pushes the grid past its own container. The landlord's three across row at
   360px in English was doing exactly that with a nine digit rent, so two tiles sat partly off the
   right edge of the phone. minmax(0) lets the track shrink and the figure wrap inside it, which is
   the wrong number of lines rather than an invisible number. */
.rg-nums{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--rg-sp2);margin:var(--rg-sp2) var(--rg-sp3) 0}
/* THREE ACROSS, for a row whose three figures are one thought. Two is the default because two
   figures read as a pair and a third would orphan; three works only when the set is complete in
   itself, as let / vacant / rent is. A variant rather than a second component: everything else
   about a figure, its label, its scale, its count-up, is unchanged. */
.rg-nums-3{grid-template-columns:repeat(3,minmax(0,1fr))}
/* ---- ONE ACROSS, AND IT IS FOR MONEY ----
   MEASURED, NOT PREFERRED. At 26px Marcellus with tabular figures, "42.000.000" is 163.6px wide and
   has no break opportunity in it: a dot is not a break point, so the span does not wrap, it
   overhangs. A three across tile at 360px gives 62.9px of content and a two across gives 122, so an
   eight digit rent overflowed its tile by ninety three pixels and sat partly off the right edge of
   the phone. A full width tile gives 292px, which holds eleven digits and the symbol with room.
   THE RULE THIS ENCODES: a tile holding a COUNT shares a row, a tile holding MONEY takes the row.
   Shrinking the type was the alternative and it fails on the next landlord: at the size that fits a
   two across tile, a nine digit portfolio overflows again and the figure is 19px besides.
   NOT A MEDIA QUERY, because the failure is not about the phone. It is about how many digits the
   number has, which is a fact about the account rather than about the screen. */
.rg-nums-1{grid-template-columns:minmax(0,1fr)}
.rg-num{padding:18px 18px 16px}
.rg-num-lab{font-family:var(--rg-mono);font-size:9px;letter-spacing:var(--rg-tr-lab);
  text-transform:uppercase;color:var(--rg-faint);margin-bottom:9px}
.rg-num-val{font-family:var(--rg-display);font-size:var(--rg-fs-num);line-height:1;
  letter-spacing:-.005em;font-variant-numeric:tabular-nums}
.rg-num-val i{font-family:var(--rg-body);font-style:normal;font-size:12.5px;
  color:var(--rg-muted);font-weight:300;margin-left:2px}
.rg-num-note{font-size:var(--rg-fs-xs);color:var(--rg-muted);margin-top:9px}
/* A FIGURE AT POSTER SCALE, AND IT IS A THIRD SIZE RATHER THAN A REUSE OF EITHER EXISTING ONE.
   .rg-num-val is a figure at CARD scale, 26px, sized to sit two or three up in a grid. .rg-hero is
   a HEADING at 44px and carries no unit treatment at all. The metrics screen needs the one thing
   neither is: the largest figure in the product, at display scale, with its unit still reading as a
   unit beside it.
   COMBINING THE TWO CLASSES DOES NOT WORK, and that is worth writing down so nobody tries it and
   quietly ships the wrong size: .rg-num-val is declared after .rg-hero in this file, so on an
   element carrying both, the smaller size wins and the poster silently becomes a card figure.
   --rg-fs-display IS THE SITE'S OWN HEADLINE TOKEN and that is the point. This figure is going into
   a screenshot that will sit in somebody's feed next to the marketing site, and the two should look
   like they came from the same place. */
/* THREE THINGS THIS SHIPPED WITHOUT, ALL REPORTED ON A PHONE ON 14 AUGUST AS ONE SYMPTOM: the
   figure and the sentence under it printing on top of each other.
     NO BOTTOM MARGIN. margin was `2px 0 0` and .rg-task-p is `margin:0 0 var(--rg-sp4)`, so the gap
   between them was ZERO. That is survivable for a 25px heading and is not for this: line-height .94
   on a 44px face gives a 41.36px line box, so the half-leading is NEGATIVE and the glyphs overhang
   their own box by about 1.3px top and bottom before the face's descender is counted. Zero gap plus
   an overhang is ink in the next paragraph. 10px, which is the rhythm .rg-task-eye already uses.
     NOT position:relative. .rg-task::after is a 230px champagne bloom pinned to the card's top
   right with no z-index, and .rg-task-eye, .rg-task-h and .rg-task-p ALL carry position:relative to
   sit above it. This one did not, so the largest figure on the screen was the one thing painted
   under the glow. Same omission the eyebrow's own note describes, one element along.
     THE CLAMP IS VIEWPORT-RELATIVE, which is why this was invisible in a desktop browser and plain
   on a phone: 1.5rem + 4.6vw is 40.6 at 360px and 93 at 1500px, so a desktop gets the 76px ceiling
   and every phone gets the 44px floor. Measured at 360, 390 and 430 with the token pinned to 44px,
   which is what all three widths compute. */
.rg-poster-n{font-family:var(--rg-display);font-size:var(--rg-fs-display);line-height:.94;
  letter-spacing:-.015em;font-variant-numeric:tabular-nums;margin:2px 0 10px;position:relative;
  /* FLEX, AND IT IS ABOUT THE WRAPPED LABEL RATHER THAN THE LAYOUT. As inline text the label shares
     the figure's line box, and a line box is at least as tall as the block's STRUT, which here is
     44px of display face. So "1284 messages Realuma AI / answered" put one 14px word alone in a
     41px band, which reads as a rendering fault. As a flex item the label wraps onto its own flex
     line and sets its own height from its own line-height below.
     baseline, so the label still sits on the figure's baseline when both fit on one line, which is
     the whole point of the phrasing. */
  display:flex;flex-wrap:wrap;align-items:baseline;column-gap:6px}
.rg-poster-n i{font-family:var(--rg-body);font-style:normal;font-size:var(--rg-fs-body);
  color:var(--rg-muted);font-weight:300;letter-spacing:0;line-height:1.35}

/* A PROPORTION, AND NOTHING IN THIS SYSTEM DRAWS ONE. .rg-spark is an SVG line over time, which
   answers a different question; a badge is a state; a chip is a fact. There is no bar anywhere in
   the product, so this is the first, and it is two rules rather than a component because that is
   all a proportion needs.
   CHAMPAGNE, NOT TEAL, and that follows the colour law rather than taste: this measures money a
   PERSON earned against a target a PERSON set. Realuma AI did not do it and has no business being
   the colour of it.
   IT NEVER RENDERS PAST FULL. The fill is capped at 100% by the code that sets it, so a month that
   beat its target shows a full bar and says so in words, rather than a bar overflowing its track,
   which reads as a rendering fault at exactly the moment somebody has done well.
   NO TRANSITION AND NO ANIMATION. A goal bar that slides on every paint turns a quiet fact into an
   event, and this screen must never celebrate at somebody having a bad month. */
/* position:relative for the reason .rg-poster-n above now states: everything inside .rg-task that is
   not positioned paints UNDER the card's champagne bloom, and a progress bar rendered under a glow
   reads as a bar that is already partly full. */
.rg-goal{height:6px;border-radius:3px;background:rgba(247,244,237,.09);overflow:hidden;margin-top:14px;
  position:relative}
/* IT FILLS. The width is the truth and it is written by the screen; the animation only says where
   the width came from, which is the one moment this feature is allowed to have. The target never
   messages anybody, never reminds anybody and appears on no other screen, so the whole of what it
   can make somebody feel is the moment of setting it and this.
   A KEYFRAME WITH ONLY A `from`, NOT A TRANSITION, and that is a failure-mode decision. A
   transition needs the width to CHANGE after paint, which means a second frame, which means a
   requestAnimationFrame — and a tab that is not focused throttles rAF, so the bar would sit at
   zero over a figure that is not zero. Here the width is correct in the very first frame and the
   animation runs backwards from nothing to it. Section 24 kills the animation outright under
   reduced motion and the correct width is what is left. */
@keyframes rg-goal-fill{from{width:0}}
.rg-goal-fill{height:100%;border-radius:3px;background:var(--rg-champ);
  animation:rg-goal-fill var(--rg-t-slow) var(--rg-ease) both}
/* while counting, the figure carries a faint light. it settles when it lands. */
.rg-counting{text-shadow:0 0 18px rgba(201,161,112,.45)}

/* ============================================================
   10. SECTIONS — collapsed by default, native, no JS.
   The summary line must answer the question without opening. "4 người ·
   2 chưa kết nối" is an answer. "People" is a label. Write answers.
   ============================================================ */
.rg-sec{margin:var(--rg-sp2) var(--rg-sp3) 0;overflow:hidden}
/* A SECTION THAT IS ALWAYS OPEN, AND IT EXISTS FOR A MEASURED REASON RATHER THAN A STYLISTIC ONE.
   `::details-content` below animates `block-size` from 0 to auto, and in Chromium the opened content
   DOES take visual space while contributing NOTHING to the scroll container's scrollHeight. On a
   screen with normal-flow content after the sections that is invisible, because the scroller is
   already taller than the viewport for other reasons. On a screen whose LAST element is a section,
   it means `scrollHeight === clientHeight`, `maxScroll` is 0, and the bottom of the final section
   cannot be scrolled to at all. Measured on Settings: the save control sat at y=865 with its bottom
   at 900 in an 871px viewport, 29px past the edge, with the scroller refusing to move.
   So a screen that is ALL sections uses this instead: same box, same vocabulary, no disclosure.
   Nothing here is collapsible, which is also the honest shape for a screen you scan rather than
   browse. `.rg-sec-h` is the summary's box without the summary. */
.rg-sec-h{display:flex;align-items:center;gap:13px;padding:17px 18px}
.rg-sec summary{list-style:none;cursor:pointer;padding:17px 18px;
  display:flex;align-items:center;gap:13px;
  transition:background var(--rg-t-fast);-webkit-tap-highlight-color:transparent}
.rg-sec summary::-webkit-details-marker{display:none}
.rg-sec summary:active{background:rgba(255,255,255,.04)}
.rg-sec-ic{width:36px;height:36px;flex:none;border-radius:var(--rg-r-in);
  display:grid;place-items:center;background:rgba(255,255,255,.06);
  color:var(--rg-champ);font-size:15px;box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
  transition:transform .44s var(--rg-spring)}
.rg-sec[open] .rg-sec-ic{transform:scale(1.08) rotate(-4deg)}
/* SOLE-CARRIER FIX. This marked a section as Realuma AI's by tinting its icon and nothing else,
   so in greyscale it was identical to a human's section. It now also carries a MARK: the same
   diamond the AI note uses, set as a corner pip, so the difference survives without hue. */
.rg-sec-ai .rg-sec-ic{color:var(--rg-ai-lit);position:relative}
.rg-sec-ai .rg-sec-ic::after{content:'◈';position:absolute;right:-3px;bottom:-4px;font-size:8px;
  line-height:1;color:var(--rg-ai-lit)}
.rg-sec-t{flex:1;min-width:0}
.rg-sec-n{font-size:15px;font-weight:400}
.rg-sec-d{font-size:var(--rg-fs-sm);color:var(--rg-muted);margin-top:2px}
.rg-chev{color:var(--rg-faint);font-size:13px;flex:none;transition:transform .44s var(--rg-spring)}
.rg-sec[open] .rg-chev{transform:rotate(90deg)}
.rg-sec-body{padding:2px 18px 18px}
/* OPACITY IS THE ONLY THING TRANSITIONED HERE, AND THE HEIGHT ANIMATION IS GONE ON PURPOSE.
   TRANSITIONING EITHER `block-size` OR `content-visibility` ON THIS PSEUDO MAKES THE OPENED
   CONTENT INVISIBLE TO LAYOUT. The content paints and measures correctly on its own, and the
   `::details-content` box stays at zero for ever, so the `<details>` reports only its summary
   height and the scroll container never learns the content exists. On a screen with anything in
   normal flow after the sections that is invisible. On a screen whose last element IS a section,
   the end of it cannot be scrolled to at all.

   BISECTED IN THE APP against a real section shape, intrinsic content rather than a fixed height,
   which is what made the first attempt at this look fine:

     block-size, content-visibility, opacity   (shipped)   scroller grew 0
     content-visibility, opacity                            scroller grew 0
     block-size, opacity                                    scroller grew 0
     opacity only                                           scroller grew 413   <- correct
     none                                                   scroller grew 464   <- correct

   So it is not one of the two, it is either of them, and only removing both fixes it. Measured on
   the live unit detail with six real sections: closed and open both reported 2921 before, and
   1644 against 3521 after. **1877px of real content the scroller could not see.**

   `interpolate-size:allow-keywords` was tried on `:root` and changed nothing, with
   `CSS.supports` confirming both it and `::details-content` are available. `overflow:visible` on
   `.rg-sec` and `block-size:max-content` on the open state also changed nothing.

   WHAT IT COSTS: the section no longer animates its height open. It still fades. Section 18's law
   already requires every state to be legible without animation, and the foot of app.css kills all
   animation under prefers-reduced-motion, so nothing here was load-bearing for meaning. A height
   that snaps is the price of a container that knows how tall it is. */
@supports selector(::details-content){
  .rg-sec::details-content{block-size:0;overflow:clip;opacity:0;
    transition:opacity var(--rg-t-mid) var(--rg-ease)}
  .rg-sec[open]::details-content{block-size:auto;opacity:1}
}

/* ============================================================
   11. ROWS
   ============================================================ */
/* THE PANEL A LIST OF ROWS SITS IN, and it exists because every other panel-level class in this
   file carries its own side margin and none of them is neutral. .rg-task is the one thing to do,
   .rg-sec is a collapsible section, .rg-nums is a grid of figures, .rg-ai-note is the machine
   speaking. A screen whose whole content IS a list had to borrow one of those and lie about what
   it was rendering, or sit 16px wider than every other panel in the product. Margin only: the
   padding comes from .rg-sec-body inside it and the rows bring their own rhythm, so this adds no
   colour, no radius, no duration and no size. */
.rg-list{margin:var(--rg-sp2) var(--rg-sp3) 0}
.rg-row{display:flex;align-items:center;gap:14px;padding:13px 0}
.rg-row + .rg-row{border-top:1px solid var(--rg-hair)}
.rg-row-main{flex:1;min-width:0}
.rg-row-n{font-size:14.5px}
.rg-row-s{font-family:var(--rg-mono);font-size:var(--rg-fs-xs);color:var(--rg-muted);margin-top:4px}
.rg-delta{font-family:var(--rg-mono);font-size:11px;margin-top:4px}
.rg-up{color:var(--rg-champ)} .rg-dn{color:var(--rg-ok)}

/* ============================================================
   12. SPARKLINES — they DRAW THEMSELVES on entry.
   Pure CSS via dashoffset, triggered by rg-motion.js adding .rg-drawn.
   A shape that draws reads as data arriving. A shape that appears reads
   as decoration.
   ============================================================ */
.rg-spark{flex:none}
.rg-spark polyline{
  stroke-dasharray:var(--rg-len,140);stroke-dashoffset:var(--rg-len,140);
  transition:stroke-dashoffset 1.1s var(--rg-ease);
}
.rg-spark circle{opacity:0;transform:scale(0);transform-origin:center;
  transition:opacity .3s var(--rg-ease) .85s, transform .5s var(--rg-spring) .85s}
.rg-drawn polyline{stroke-dashoffset:0}
.rg-drawn circle{opacity:1;transform:scale(1)}

/* ============================================================
   13. PEOPLE — role and state are TWO signals, never one.
   The ring says connected. The chip says role. A person who cannot be
   reached must be visible as such at a glance, because a bill cannot
   be delivered to them.
   ============================================================ */
.rg-av{width:42px;height:42px;flex:none;border-radius:50%;display:grid;place-items:center;
  font-family:var(--rg-display);font-size:16px;color:var(--rg-champ);
  background:rgba(255,255,255,.05);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 0 0 1.5px var(--rg-champ),
             0 0 16px -4px rgba(201,161,112,.5)}
.rg-av-off{color:var(--rg-faint);border:1.5px dashed rgba(247,244,237,.2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}
.rg-meta{display:flex;align-items:center;gap:7px;margin-top:6px;flex-wrap:wrap}
.rg-chip{font-family:var(--rg-mono);font-size:9px;letter-spacing:.11em;text-transform:uppercase;
  padding:4px 9px;border-radius:var(--rg-r-sm);background:rgba(255,255,255,.035);color:var(--rg-muted)}
.rg-chip-you{background:var(--rg-champ-wash);color:var(--rg-champ)}
/* THE TEAL COUNTERPART, ADDED 2026-08-13 for the calendar. .rg-chip-you says a human did
   this and is champagne; there was no chip that said Realuma AI did this, and the law needs
   both halves or the second one gets painted in the first one's colour. A viewing the worker
   booked wears this. Same geometry as .rg-chip-you, opposite hue, no third variant. */
.rg-chip-ai{background:var(--rg-ai-wash);color:var(--rg-ai-lit)}

/* ============================================================
   14. THE RECORD — who spoke is a colour, per the law.
   ============================================================ */
.rg-turn{padding:14px 0;border-top:1px solid var(--rg-hair)}
.rg-turn:first-child{border-top:none}
.rg-turn-h{display:flex;align-items:center;gap:9px;margin-bottom:7px;
  font-family:var(--rg-mono);font-size:var(--rg-fs-lab);letter-spacing:.08em;color:var(--rg-faint)}
.rg-who{padding:3px 8px;border-radius:var(--rg-r-sm);font-size:9px;
  letter-spacing:.12em;text-transform:uppercase}
.rg-who-ai{background:var(--rg-ai-wash);color:var(--rg-ai-lit)}
.rg-who-you{background:var(--rg-champ-wash);color:var(--rg-champ)}
.rg-who-them{background:rgba(247,244,237,.07);color:var(--rg-muted)}
.rg-turn-b{font-size:13.5px;line-height:1.6}
.rg-turn-note{font-size:var(--rg-fs-xs);color:var(--rg-faint);margin-top:5px;font-style:italic}

/* IN FLIGHT. NOT A SPINNER, and the difference is the point: a spinner says "working"
   and nothing else, while this row also has to say WHAT is being waited on and, past
   five seconds, HOW LONG. It borrows .rg-turn whole, so the pending turn sits in the
   transcript exactly where the answer will land and the answer replaces it in place
   instead of appearing somewhere else. Italic and muted, because it is not a message
   anybody said. rg-breathe is the opacity cycle this file already owns; a second
   animation for one dot would be a second thing to keep in step. */
.rg-working .rg-turn-b{display:flex;align-items:center;gap:9px;color:var(--rg-muted);font-style:italic}
.rg-working-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--rg-ai-lit);
  animation:rg-breathe 1.6s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){.rg-working-dot{animation:none;opacity:.72}}

/* ============================================================
   15. REALUMA AI SPEAKING
   ============================================================ */
.rg-ai-note{margin:var(--rg-sp2) var(--rg-sp3) 0;padding:16px 18px;
  display:flex;gap:13px;align-items:flex-start}
.rg-ai-ic{color:var(--rg-ai-lit);font-size:15px;flex:none;margin-top:1px;
  filter:drop-shadow(0 0 10px rgba(95,160,174,.7));animation:rg-pulse 3.4s ease-in-out infinite}
@keyframes rg-pulse{50%{opacity:.55;transform:scale(.92)}}
.rg-ai-t{font-size:13.5px;color:rgba(247,244,237,.82);line-height:1.55}
.rg-ai-t b{color:var(--rg-ai-lit);font-weight:500}

/* ============================================================
   16. THE QUIET END — edited once a year, so it is not a card.
   Never a dash as a value. A dash is punctuation pretending to be data.
   Write the words: "chưa ghi", "not recorded".
   ============================================================ */
.rg-quiet-block{margin:var(--rg-sp5) var(--rg-sp4) 0}
.rg-quiet-h{font-family:var(--rg-mono);font-size:9px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--rg-faint);margin-bottom:12px}
/* TWO NOTES IN ONE QUIET BLOCK MUST READ AS TWO STATEMENTS. .card-note carries margin:0, which was
   invisible while this block only ever rendered one. The landlord calendar now says two different
   things here — accesses are NOT YET, meter readings are NEVER — and measured on the live screen the
   gap between them was 0 against a 20.15px line-height, so the second sentence was indistinguishable
   from a line wrap of the first. Adjacent-sibling, so the first note still gets nothing: same reason
   .rg-cal-day resets its first child, a doubled gap under the header reads as a mistake. */
.rg-quiet-block .card-note + .card-note{margin-top:var(--rg-sp2)}
/* A DAY HEADER INSIDE A LIST. Same type as .rg-quiet-h, which is already the small mono label in
   this system, plus the space a new day needs above it. First one gets none: the list has its own
   margin and a doubled gap at the top reads as a mistake. */
.rg-cal-day{margin-top:20px}
.rg-list > .rg-cal-day:first-child{margin-top:0}
.rg-kv{display:flex;justify-content:space-between;gap:18px;padding:11px 0;font-size:var(--rg-fs-body)}
.rg-kv + .rg-kv{border-top:1px solid rgba(255,255,255,.06)}
.rg-kv span{color:var(--rg-muted)}
.rg-kv b{font-weight:400;text-align:right}
.rg-kv b.rg-unset{color:var(--rg-faint);font-weight:200;font-style:italic}

/* ============================================================
   17. THE DOCK — DELETED 2026-08-13, and it is worth saying what it was.

   A scroll-driven champagne CTA that was to appear once the dashboard's hero task
   had scrolled away: .rg-dock with .rg-dock-t / -l / -n / -b inside it, revealed by
   @keyframes rg-dock-in on animation-timeline:scroll().

   IT WAS NEVER RENDERED. Nothing in app/js or app/index.html ever wrote the class,
   so the whole block, both [data-theme="light"] overrides, the keyframe, the
   reduced-motion rule and its entry in the no-backdrop-filter fallback list styled
   an element that did not exist.

   AND IT WAS BROKEN. It carried the same position:fixed anchoring bug the navigation
   dock had (see .rg-dock-nav in realuma-nav.css): bottom resolved against the initial
   containing block, which is the large viewport, so on a phone it would have rendered
   behind the browser toolbar. It could NOT take the same fix, because it lived inside
   .app-scroll and its reveal reads animation-timeline:scroll(), so re-anchoring it to
   .app would have removed the scroll container the animation depends on.

   Dead and known-broken is not a thing to leave in the tree for somebody to find and
   use. If a hero-task CTA is wanted later it needs the anchoring question answered
   first, and this block would have been the wrong starting point.
   ============================================================ */

/* ============================================================
   18. THE FOUR STATES, in glass.
   Told apart by SHAPE, not by animation, because the rule below kills
   every animation and all four must still be distinguishable with
   nothing moving. Bars, centred line, red-edged row, dashed panel.
   ============================================================ */
.rg-load{padding:20px 18px;display:flex;flex-direction:column;gap:10px}
.rg-load i{display:block;height:11px;border-radius:var(--rg-r-sm);
  background:rgba(255,255,255,.07);box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
  animation:rg-breathe 1.6s ease-in-out infinite}
.rg-load i:nth-child(1){width:100%} .rg-load i:nth-child(2){width:72%;animation-delay:.18s}
.rg-load i:nth-child(3){width:45%;animation-delay:.36s}
@keyframes rg-breathe{0%,100%{opacity:.45}50%{opacity:1}}

.rg-empty{padding:32px 20px;text-align:center;color:var(--rg-muted);font-size:var(--rg-fs-body)}
.rg-empty-ic{font-size:30px;color:var(--rg-champ);opacity:.4;margin-bottom:12px}
.rg-empty-t{font-family:var(--rg-display);font-size:19px;color:var(--rg-text);margin-bottom:7px}
/* AN EMPTY STATE IS USUALLY A PLACE WITH NOTHING IN IT YET, which is the product speaking, so the
   mark is champagne. .rg-empty-ai is the one case where the empty state IS Realuma AI speaking: the
   copilot's opening, where the mark names the actor and the law makes that teal. It never carries
   the meaning alone. The name sits under it in .rg-empty-t, and the icon is aria-hidden. */
.rg-empty-ai .rg-empty-ic{color:var(--rg-ai-lit)}
.rg-empty-note{margin-top:var(--rg-sp3);font-size:var(--rg-fs-xs);color:var(--rg-muted);line-height:1.55}

.rg-fail{padding:16px 18px;display:flex;align-items:center;gap:13px;flex-wrap:wrap;
  border-left:3px solid var(--rg-stop)}
.rg-fail p{flex:1;min-width:170px;color:var(--rg-stop);font-size:13.5px;margin:0}

/* refused is not failure. flat, dashed, recessed. NEVER a retry. */
.rg-refused{padding:30px 20px;text-align:center;color:var(--rg-muted);font-size:var(--rg-fs-body);
  background:rgba(255,255,255,.03);border:1px dashed rgba(255,255,255,.14);
  box-shadow:none;backdrop-filter:none;-webkit-backdrop-filter:none}

/* ============================================================
   19. THE SHEET: AN OBJECT OVER THE WORLD, AND IT IS ONE COMPONENT.

   A sheet and a modal are the same object at two heights. The app has been
   carrying two of them, which is two sets of chrome, two z-indexes, two
   dismiss behaviours and one of them with no grip and no drag. There is
   one here. .rg-sheet-short is the SAME panel anchored to its content
   instead of to the top of the screen, and that is the only difference.

   THE STRUCTURE IS FORCED BY THE DRAG, and this is the part that must not
   be simplified later by someone who reads it as redundant:

     the panel carries the TRANSFORM and NO MATERIAL
     the panel's ::before carries the MATERIAL and never moves

   Exactly the split realuma-nav.css:33-59 makes for the dock, for the same
   reason and a harder one. A finger drags this panel to dismiss it, so its
   transform changes every frame. A fill, a blur and a rim on the same box
   as a per-frame transform is a picture of glass being slid around; on its
   own layer the material re-samples the scrim underneath it as the panel
   travels, which is what glass does.

   THE ONE PLACE IT DIFFERS FROM THE DOCK, and it is deliberate: the dock
   puts its cast shadow on ::before and sets overflow:visible to let it
   escape. A sheet cannot do that, because a scrolling body must be clipped
   to the panel's top radius. So the panel keeps overflow:hidden and carries
   the cast shadow itself. A cast is not material. It is the object's
   relationship to the world, it is drawn entirely OUTSIDE the box, and it
   must travel with the transform rather than with the fill.

   THE SCRIM IS ITS OWN ELEMENT AND IT IS NOT DECORATION. It is the depth
   cue, and it is also the thing the panel's blur samples: no scrim, and the
   glass has nothing behind it but the drifting world, which reads as a
   panel printed on the wallpaper. It carries no blur of its own. Two blurs
   stacked is one blur's cost paid twice for a result nobody can tell apart.
   ============================================================ */

/* THE OVERLAY LAYER. Above the nav dock at z-index 60, below the toast at
   90 and the lightbox at 100, so a confirmation fired from inside a sheet
   is still readable and a photo still opens over it. */
/* inset:0 SIZED THIS TO THE ICB, which is the large viewport, so the panel's bottom
   edge sat behind the browser's bottom toolbar and took .rg-sheet-foot with it: the
   save button on every sheet in the product, uncoverable by scrolling because the
   panel does not scroll. Anchored to the dynamic viewport instead, which is what .app
   already uses. Same correction as .rg-dock-nav, same reason. */
/* --rg-vvh, NOT 100dvh, AND THE SAVE BUTTON IS WHY.  ST21, 2026-08-21.
   .rg-sheet-foot is PINNED to the bottom of this box. 100dvh tracks the LAYOUT viewport, which a
   software keyboard does not resize, so with a keyboard up the foot sat at the bottom of the screen
   BEHIND it — and a pinned foot cannot be scrolled to. Measured with npm run render at 360/390/430:
   every form, every width, save unreachable. The body scrolls, so on a long form the FIELDS could be
   brought up; the one control that commits the work could not.
   NO safe-area SUBTRACTION HERE, unlike .rg-chat-screen: that one sits inside .app-scroll, which
   already starts below the top inset. A sheet is top:0 over the whole screen, and --rg-vvh is
   visualViewport.height RAW, which under viewport-fit=cover measures from the top of the screen
   too. The two agree without an adjustment, and adding one would make the sheet an inset too short.
   ui.js keeps --rg-vvh accurate for the whole app; the fallback is what every desktop gets. */
.rg-sheet{position:fixed;top:0;left:0;right:0;height:var(--rg-vvh, 100dvh);z-index:70}

/* THE SCRIM. Nothing but the canvas colour at weight. */
.rg-sheet-scrim{
  position:absolute;inset:0;touch-action:none;
  background:rgba(8,15,20,.62);
  transition:opacity var(--rg-t-mid) var(--rg-ease);
  will-change:opacity;
}

/* THE PANEL. Transform, radius, clip, cast. No fill, no blur, no rim. */
.rg-sheet-panel{
  position:absolute;left:0;right:0;bottom:0;
  top:calc(env(safe-area-inset-top,0px) + var(--rg-sp1));
  display:flex;flex-direction:column;overflow:hidden;
  background:none;
  border-radius:var(--rg-r-out) var(--rg-r-out) 0 0;
  box-shadow:0 -20px 56px -26px rgba(0,0,0,.9);
  transition:transform var(--rg-t-mid) var(--rg-ease);
  will-change:transform;
}

/* THE MATERIAL, ON ITS OWN LAYER. Insets only: an outer shadow here would
   be clipped away by the panel above, which is why the cast lives there. */
.rg-sheet-panel::before{
  content:'';position:absolute;inset:0;z-index:0;
  border-radius:inherit;pointer-events:none;
  background:var(--rg-glass-deep);
  backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  -webkit-backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  box-shadow:
    inset 0 1px 0 var(--rg-edge-top),
    inset 0 0 0 1px var(--rg-edge);
}

/* Every part sits ABOVE the material layer. A positioned ::before paints
   over normal-flow content, so this is not belt and braces: without it the
   glass is drawn on top of the title. */
.rg-sheet-head,.rg-sheet-body,.rg-sheet-foot{position:relative;z-index:1}

/* THE HEAD IS THE DRAG SURFACE, which is why touch-action is none on it and
   pan-y on the body: the header dismisses, the body scrolls, and the two
   gestures never have to be told apart at runtime. */
.rg-sheet-head{
  flex:none;
  padding:var(--rg-sp1) var(--rg-sp3) var(--rg-sp2);
  border-bottom:1px solid var(--rg-hair);
  touch-action:none;cursor:grab;
  -webkit-user-select:none;user-select:none;
  -webkit-tap-highlight-color:transparent;
}
.rg-sheet-dragging .rg-sheet-head{cursor:grabbing}

/* THE GRIP. 40x5 is a literal because it is hardware, not spacing: it is
   the same affordance every OS draws at roughly this size, and a person
   reads it as "pull me" before they read anything else on the panel. */
.rg-sheet-grip{
  width:40px;height:5px;margin:var(--rg-sp1) auto var(--rg-sp2);
  border-radius:var(--rg-r-pill);background:var(--rg-faint);
}

.rg-sheet-head-row{display:flex;align-items:center;justify-content:space-between;gap:var(--rg-sp2)}
.rg-sheet-title{
  font-family:var(--rg-display);font-size:var(--rg-fs-h1);
  line-height:1.15;letter-spacing:-.005em;margin:0;min-width:0;
}

/* A FIGURE TILE INSIDE A PANEL KEEPS ITS PADDING AND LOSES ITS SIDE MARGIN. .rg-nums carries
   `margin: sp2 sp3 0`, which is the SCREEN's gutter: it exists so a tile lines up with the cards
   above and below it on a page that has no padding of its own. .rg-sheet-body already pads by sp3,
   so a tile dropped into one is inset twice and reads as a smaller card floating in a panel.
   The rule is the containment, not the number: no new value, and nothing changes on a screen. */
.rg-sheet-body > .rg-nums{margin-left:0;margin-right:0;margin-bottom:var(--rg-sp3)}
/* AND THE FIGURE MAY WRAP RATHER THAN OVERHANG. A month target has no upper bound — the store
   refuses zero and nothing else — and a grouped number has no break opportunity in it, so at
   display scale a long one walks straight out of the tile. Same fix and same reason as .rg-bar-t,
   which carries an agent-supplied business name at 16.5px for exactly this. */
.rg-sheet-body .rg-poster-n{overflow-wrap:anywhere;min-width:0}

/* THE BODY. min-height:0 is what stops a long form from pushing the foot
   off the bottom of the panel instead of scrolling. */
.rg-sheet-body{
  flex:1;min-height:0;
  overflow-y:auto;overscroll-behavior:contain;touch-action:pan-y;
  -webkit-overflow-scrolling:touch;
  padding:var(--rg-sp3) var(--rg-sp3) var(--rg-sp4);
}

/* THE FOOT. A raised lip on the panel's own material, NOT a second pane of
   glass: a backdrop-filter here would blur an already-blurred surface for a
   difference nobody can see, and it would pay for it on every dragged frame.
   It holds one champagne .rg-btn, because a save is a human acting.
   :empty because the renderer always emits the element, and a bordered
   strip with nothing in it is a rule the eye has to explain to itself. */
.rg-sheet-foot{
  flex:none;
  padding:var(--rg-sp2) var(--rg-sp3) calc(var(--rg-sp2) + env(safe-area-inset-bottom,0px));
  border-top:1px solid var(--rg-hair);
  background:linear-gradient(to top,rgba(255,255,255,.055),rgba(255,255,255,.02));
  box-shadow:0 -16px 30px -24px rgba(0,0,0,.85);
}
.rg-sheet-foot:empty{display:none}

/* WHILE A FINGER HOLDS IT. A thing stuck to a finger may never ease, and
   the scrim must fade in step with it or the panel appears to slide over a
   still photograph. Same rule the dock's lens follows. */
.rg-sheet-dragging .rg-sheet-panel,
.rg-sheet-dragging .rg-sheet-scrim{transition:none}

/* A MODAL IS A SHORT SHEET. Anchored to its own content rather than to the
   top of the screen; everything else is the panel above, unchanged. It
   loses the head's rule because a hairline under a bare grip is a divider
   between nothing and something. */
.rg-sheet-short .rg-sheet-panel{top:auto;max-height:calc(100% - var(--rg-sp6))}
.rg-sheet-short .rg-sheet-head{border-bottom:none;padding-bottom:0}

/* A LAPTOP IS NOT A PHONE HELD SIDEWAYS. The sheet stops being a drawer and
   becomes a centred object, which is the one place the bottom radius shows. */
@media (min-width:900px){
  .rg-sheet{display:grid;place-items:center}
  .rg-sheet-panel{position:relative;left:auto;right:auto;top:auto;bottom:auto;
    width:min(620px,92vw);max-height:88vh;border-radius:var(--rg-r-out);
    box-shadow:0 30px 70px -30px rgba(0,0,0,.9)}
  .rg-sheet-short .rg-sheet-panel{max-height:88vh}
  .rg-sheet-foot{padding-bottom:var(--rg-sp2)}
}

/* ============================================================
   20. PICKED: THE SYSTEM SAYS WHICH ONE IS CURRENT.

   Until now nothing in this file could say it. There is a badge for a
   state, a chip for a label and a button for an act, and not one of them
   answers "this is the one you chose". Two screens needed it and both
   borrowed app.css, which paints it TEAL.

   IT IS CHAMPAGNE, AND THE REASON IS THE LAW AT THE TOP OF THIS FILE.
   A tapped filter is A HUMAN ACTING. Teal is reserved for words Realuma
   AI actually produced, and the moment a control wears teal, teal stops
   meaning that: a landlord who has seen a teal chip they pressed
   themselves can no longer read teal as "the machine wrote this", and the
   signal is spent on furniture. app.css:740 paints a selected facet chip
   with var(--ai) and app.css:760 paints an active filter button with
   var(--ai-strong). Both are wrong under the law, and both are wrong on
   every screen that still reads them.

   ONE IDEA, TWO CONTAINERS, and the containers differ for a reason rather
   than for variety. The ITEM is the same object in both: .rg-pick, and
   .rg-picked is the state. What differs is what it sits in:

     .rg-picks      a wrapping set. Facets. Zero or one may be current,
                    because a facet can be cleared, and the set is as wide
                    as its labels.
     .rg-picks-seg  a track. Sort. EXACTLY one is always current, the
                    options are equal width, and it never wraps.

   The track is RECESSED, which is the grammar realuma-forms.css already
   uses: a hole in the surface, light falling in from above. The picked
   segment is then a raised object sitting in that hole, so the two read
   as figure and ground rather than as two chips of different colours.
   A wrapping facet chip has no track because it has no set width to draw
   one around, and drawing one per chip would say each chip is its own
   control.

   IT SURVIVES REDUCED MOTION, which section 21 proves by killing every
   transition below. Picked and unpicked are told apart with NOTHING
   MOVING, by three static differences at once:
     FILL    picked is a solid champagne gradient, unpicked is a barely
             tinted translucency. Solid against hollow is a weight
             difference readable at a glance and at any zoom.
     INK     picked is --rg-ink on light, unpicked is --rg-muted on dark.
             The polarity inverts, so it survives greyscale.
     RIM     picked carries a lit top edge and a cast; unpicked carries a
             flat hairline and no cast.
   A person with reduced motion sees exactly what everyone else sees one
   frame after the tap, and loses only the .2s cross-fade between them.
   Colour is never the only signal, which is the same contract section 6
   holds badges to.
   ============================================================ */

/* THE SET. Facets: wraps, and its label is .rg-label from realuma-forms.css,
   which is already the system's way of naming a group of controls. */
.rg-picks{display:flex;flex-wrap:wrap;gap:var(--rg-sp1);align-items:center}
/* A PICK THAT CARRIES TWO FACTS IS A ROW, NOT A PILL. A chip reading "5 lettings · 43.500.000 ₫"
   measures 152px, so three of them wrap 2 and 1 at 360 and the odd one out reads as a mistake.
   Stacked, each is a full-width line with the count on the left and the money after it, which is
   what a list of three choices wants to be anyway.
   TWO PROPERTIES AND NO NEW VALUES: the direction and the stretch. The gap, the height, the fill,
   the rim and the type are .rg-pick's, unchanged, so this is a layout of the existing component
   rather than a second one. */
.rg-picks-goal{flex-direction:column;align-items:stretch}

/* THE TRACK. A hole, per the forms doctrine: shadow at the top, no cast,
   fill darker than what surrounds it. Equal columns, so the options cannot
   argue about width, and one row always. */
.rg-picks-seg{
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
  gap:0;padding:3px;border-radius:var(--rg-r-in);
  background:rgba(0,0,0,.24);
  box-shadow:
    inset 0 2px 5px rgba(0,0,0,.4),
    inset 0 0 0 1px rgba(255,255,255,.06);
}

/* ONE OPTION, NOT CURRENT. Hollow, quiet, and obviously pressable. */
/* min-width JOINS min-height 16 Aug, AND IT IS THE SAME RULE ONE AXIS OVER. Measured rendering the
   whole product: every .rg-pick clears 44px tall everywhere, and ONE is 43px WIDE — "all", the
   first and default chip on the Leads filter row, in English. Three characters at 13px plus 30px of
   padding is 43. In a .rg-picks-seg the columns are 1fr and always wider, so this reaches only the
   scroller, and only a chip whose label is shorter than 14px of type. The one visible consequence
   is that chip growing by a single pixel. */
.rg-pick{
  appearance:none;-webkit-appearance:none;
  border:none;cursor:pointer;
  min-height:44px;min-width:44px;padding:0 15px;border-radius:var(--rg-r-sm);
  display:inline-flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.05);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.10);
  color:var(--rg-muted);
  font-family:var(--rg-body);font-size:var(--rg-fs-sm);font-weight:500;
  line-height:1.25;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
  transition:background var(--rg-t-fast) var(--rg-ease),
             color var(--rg-t-fast) var(--rg-ease),
             box-shadow var(--rg-t-fast) var(--rg-ease),
             transform var(--rg-t-mid) var(--rg-spring);
}
.rg-pick:active{transform:scale(.94)}
/* Inside a track the option carries no shell of its own: the track is the
   shell, and a rim on every segment would draw four boxes inside one box. */
.rg-picks-seg .rg-pick{background:none;box-shadow:none}

/* ---------- A ROW OF PICKS THAT DOES NOT FIT, ADDED 2026-08-13 ----------
   .rg-picks-seg is a grid of equal columns and it is right for two or three choices. Leads
   has seven statuses and the calendar strip has fourteen days, and at 360px an equal-column
   grid gives each of those about 46px, which cannot hold "qualifying" in either language.
   Nothing else in the system scrolls sideways, so this is the one new layout: the same
   .rg-pick children, laid out in a scroller with the scrollbar hidden and snap points so a
   flick lands on a chip rather than between two. ONE class serving both screens rather than
   one each, because it is the same problem both times. */
.rg-picks-scroll{
  display:flex;gap:var(--rg-sp1);
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x proximity;
  -webkit-overflow-scrolling:touch;touch-action:pan-x;
  scrollbar-width:none;
  /* BLEED TO THE EDGES AND PAD BACK IN, so a chip scrolled to the end sits under the screen's
     own margin rather than being clipped by it, and the first chip still lines up with the
     text above. margin/padding pair must stay equal to --rg-sp3, the view's side padding. */
  margin-inline:calc(-1 * var(--rg-sp3));padding-inline:var(--rg-sp3);
  padding-block:2px;
}
.rg-picks-scroll::-webkit-scrollbar{display:none}
.rg-picks-scroll > *{flex:none;scroll-snap-align:start}

/* A DAY IN THE STRIP. Two lines and a dot, so it cannot be a plain .rg-pick, which is one
   line and nowrap. Everything else about it IS .rg-pick: it takes that class too and only
   overrides the box. The dot is the whole point of the strip, so it is drawn even when
   empty, at zero opacity, and the row of dots stays on one baseline. */
.rg-pick-day{
  display:grid;justify-items:center;gap:3px;
  padding:7px 11px;min-width:52px;line-height:1.15;
}
.rg-pick-day .rg-pd-w{font-family:var(--rg-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase}
.rg-pick-day .rg-pd-n{font-size:15px;font-weight:600}
.rg-pick-day .rg-pd-dot{width:5px;height:5px;border-radius:50%;background:currentColor;opacity:0}
.rg-pick-day.rg-pd-has .rg-pd-dot{opacity:1;box-shadow:0 0 7px currentColor}
.rg-pick-day:disabled{cursor:default;opacity:.45}

/* CURRENT. The same fill realuma-forms.css gives a ticked checkbox, because
   it is the same sentence: a human chose this one. Deliberately NOT the
   .rg-btn treatment, which is reserved for the single most important act on
   a screen; this is smaller, quieter and repeatable. */
.rg-pick.rg-picked,
.rg-picks-seg .rg-pick.rg-picked{
  background:linear-gradient(178deg,var(--rg-champ-lit),var(--rg-champ));
  color:var(--rg-ink);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.5),
    0 3px 10px -4px rgba(201,161,112,.75);
}
.rg-pick:focus-visible{
  outline:none;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.10),0 0 0 4px rgba(201,161,112,.28);
}
.rg-pick.rg-picked:focus-visible{
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 0 0 4px rgba(201,161,112,.34);
}
.rg-pick:disabled{opacity:.45;cursor:not-allowed}

/* ============================================================
   21. THE THREAD: A PLACE, NOT AN OVERLAY.

   THIS IS NOT .rg-sheet AND IT MUST NEVER BORROW ITS CHROME. The two
   rhyme geometrically, a head over a scrolling body over a pinned foot,
   and they are different objects:

     A SHEET IS SOMETHING YOU DISMISS. It arrives over the screen you were
     on, it takes z-index 70 so it covers the dock, it has a grip and a
     drag because getting rid of it is a gesture you own, and the thing
     underneath it is still where you were.
     A THREAD IS A PLACE YOU ARE IN. You navigated to it, the back button
     is how you leave, the dock stays visible because the rest of the app
     is still reachable, and there is nothing underneath it to return to.

   Borrowing the sheet's chrome would give a route view a grab cursor and
   touch-action:none on a header that drags nowhere, and would put a place
   above the navigation that is supposed to lead out of it.

   THE MATERIAL SITS ON THE BOX HERE, not on a ::before as the sheet and
   the dock do. That split exists to stop a per-frame transform dragging a
   blur around with it, and nothing here transforms: the panel is planted.
   One layer is cheaper and the reason for two does not apply.

   THE HEIGHT IS SELF-CONTAINED. .app is height:100dvh with
   padding-top:env(safe-area-inset-top) and .app-scroll takes the rest, so
   a screen that has claimed data-bar (its own .rg-bar, topbar hidden) has
   exactly 100dvh minus the top inset to work in. Said as arithmetic
   rather than as a percentage, because a percentage would need every
   ancestor between here and .app-scroll to carry a definite height, and
   #view does not.

   THE 156px IS THE DOCK'S FOOTPRINT PLUS THE BLOOM'S, measured rather
   than guessed. It was 84 while the bloom sat inside the bar: the dock is
   at bottom:calc(18px + safe) and renders 62px tall, so 80px of the bottom
   edge belonged to it and 84 left four of clearance.
   THE BLOOM LEFT THE BAR ON 2026-08-18 and floats 8px above the dock's top
   at 64px across, so the dead zone is 18 + 62 + 8 + 64 = 152, and 154 ships
   because 152 exactly measured -0.4px on the tightest screen. Measured with
   npm run render, which mounts the real dock: at 84 the copilot's own
   suggestion chips sat 49px under the light.
   96 OF IT IS THE DOCK AND 56 IS THE BLOOM, and app.css carries the whole
   argument about what that costs the glass at the end of a page.
   It is a literal because neither height is a token and inventing one
   would imply either could be resized from here. Neither can.
   ============================================================ */

/* THE SCREEN. A column exactly as tall as the space a bar-claiming view
   is given, so nothing below it can scroll and the composer cannot leave.

   --rg-vvh IS THE iOS KEYBOARD, and the fallback is the honest default.
   100dvh tracks the LAYOUT viewport. On iOS Safari the software keyboard
   does not resize the layout viewport, it only shrinks the VISUAL one, so
   a shell sized in dvh keeps its full height and puts the composer behind
   the keyboard. The view sets --rg-vvh from visualViewport.height while a
   thread is open and drops it on teardown; every browser that has no
   visualViewport, and every screen that is not a thread, gets the calc. */
/* THE TOP INSET IS SUBTRACTED OUTSIDE THE var(), corrected 2026-08-13, and that
   placement is the whole fix. It used to sit inside the FALLBACK only, so the two
   branches did not agree: the fallback was 100dvh minus the inset, which is exactly
   the height .app-scroll has, while --rg-vvh is visualViewport.height RAW and under
   viewport-fit=cover that measures from the top of the screen, inset included. The
   var branch was therefore one top inset TALLER than the box it sits in, .app-scroll
   gained that much scroll on a screen that must not scroll at all, and the composer
   went back under the dock: the same failure app.css's #view padding reset was
   written to stop. Subtracting once, outside, makes both branches mean the same
   thing. Zero on a phone in a browser, 47 to 59px in a home-screen PWA. */
.rg-chat-screen{
  display:flex;flex-direction:column;
  height:calc(var(--rg-vvh, 100dvh) - env(safe-area-inset-top,0px));
}

/* THE PANEL. Ordinary glass, planted in the page at z-index 1, which is
   below .rg-dock-nav's 60 and therefore below the navigation. */
.rg-chat{
  flex:1;min-height:0;
  display:flex;flex-direction:column;
  position:relative;z-index:1;
  margin:var(--rg-sp2) var(--rg-sp3) calc(154px + env(safe-area-inset-bottom,0px));
  border-radius:var(--rg-r-out);
  overflow:hidden;
  background:var(--rg-glass);
  backdrop-filter:blur(var(--rg-blur)) saturate(var(--rg-sat));
  -webkit-backdrop-filter:blur(var(--rg-blur)) saturate(var(--rg-sat));
  box-shadow:
    inset 0 1px 0 var(--rg-edge-top),
    inset 0 0 0 1px var(--rg-edge),
    0 18px 40px -22px rgba(0,0,0,.7);
}

/* THE TRANSCRIPT. The only thing on this screen that scrolls.
   overscroll-behavior:contain stops a flick at the top of the record from
   handing the gesture to .app-scroll and taking the whole screen with it. */
/* ⛔ THE DOCK'S RESERVE GOES WHEN THE DOCK DOES.  ST21, 2026-08-21.

   PHOTOGRAPHED ON A PHONE: take over a thread, tap the composer, and the conversation collapses to
   one clipped line — the message being replied to cut mid-sentence at the moment somebody needs to
   read it.

   THE SHELL ALREADY SHRANK CORRECTLY. .rg-chat-screen is calc(var(--rg-vvh) - safe-top) and the
   views drive --rg-vvh from visualViewport.height, so the box does lose the keyboard's height. What
   nothing did was give back the 154px of bottom margin .rg-chat holds for the DOCK and the BLOOM —
   both position:fixed against the LAYOUT viewport, which a software keyboard does not resize. With
   the keyboard up they are BEHIND it. The conversation was paying twice: once for the keyboard, and
   again for furniture nobody can see.

   MEASURED with npm run render, .rg-chat-log, --rg-vvh at the phone height then at height minus a
   336px iOS keyboard:

                        closed    keyboard, no fix    keyboard, fixed
       thread   360x800     379px          43px               186px   49% left
       thread   390x844     423px          87px               230px   54%
       thread   430x932     531px         195px               338px   64%
       copilot  360x800     500px         164px               307px   61%
       copilot  390x844     544px         208px               351px   65%
       copilot  430x932     632px         296px               439px   69%

   43px, AND ONE MESSAGE ROW IS 44. That column is the photograph, and it is why the middle state is
   measured on every run rather than dropped once the fix landed: a regression that silently gives
   the reserve back shows up as the middle and right columns converging. What is lost now is the
   keyboard's own height and nothing else.

   THE COPILOT IS HERE BECAUSE IT IS THE SAME SHELL. views/assistant.js renders the same
   .rg-chat-screen / .rg-chat / .rg-chat-log and carried the same defect; fixing only the screen in
   the photograph would have shipped it still broken.

   THE CLASS IS SET BY THE VIEW, NOT GUESSED HERE. There is no CSS way to ask whether a software
   keyboard is up; the two views that own a .rg-chat already listen to visualViewport, and both
   toggle .rg-kb on <html> from the one number that answers it. */
:root.rg-kb .rg-chat{ margin-bottom:var(--rg-sp2); }

.rg-chat-log{
  flex:1;min-height:0;
  overflow-y:auto;overscroll-behavior:contain;touch-action:pan-y;
  -webkit-overflow-scrolling:touch;
  padding:var(--rg-sp3) var(--rg-sp3) var(--rg-sp2);
}

/* THE COMPOSER. flex:none, so it is pinned by the layout rather than by
   position:sticky: there is no scroll for it to stick to. A raised lip on
   the panel's own material, the same reading .rg-sheet-foot takes, and no
   backdrop-filter of its own for the same reason. */
.rg-chat-foot{
  flex:none;
  display:flex;align-items:center;gap:var(--rg-sp1);
  padding:var(--rg-sp2);
  border-top:1px solid var(--rg-hair);
  background:linear-gradient(to top,rgba(255,255,255,.055),rgba(255,255,255,.02));
}
.rg-chat-foot .rg-input{flex:1;min-width:0}

/* THE SEPARATOR. A day marker, and the line saying where she stopped
   reading. Centred rules either side, because a transcript has no left
   edge to hang a heading on and a bare word floats.
   THE NEW MARKER IS CHAMPAGNE because it is about HER: it marks the place
   a human left off, which is the only thing on this screen that is not a
   record of what somebody said. */
/* =================================================================================================
   THE INBOX THREAD.  ST23 FREEZE, 2026-08-22. EVERY RULE HERE IS SCOPED TO .rg-thread.

   PHOTOGRAPHED ON AN iPHONE at fcb581c: the conversation rendered in a short boxed area floating
   mid-screen with a fifth of the screen dead below it, and with the keyboard up it collapsed to
   about a line and a half with the composer not visible at all.

   MEASURED BY npm run render, .rg-chat-log with the keyboard CLOSED, before -> after:

       thread  360    379px -> 522px
       thread  390    423px -> 566px
       thread  430    531px -> 674px

   ⛔ AND THE HARNESS'S OWN REGRESSION DETECTOR IS NOW BLIND FOR THIS SCREEN, which has to be said
   here because the table further up this file is what it was built to protect. Its middle column
   ("kb NO FIX") simulates the 154px dock reserve being handed back; .rg-thread no longer HAS that
   margin in either state, so the middle and right columns now read identically — 186/186, 230/230,
   338/338. That convergence was defined up there as the SIGNATURE OF A REGRESSION. For .rg-thread
   it is now the correct result, and only the copilot's rows still carry the original meaning.

   .rg-chat-screen IS SHARED WITH THE COPILOT (views/assistant.js renders the same shell), so not one
   rule below may hang off it. .rg-thread is set by views/inbox.js on the thread and nowhere else.
   ================================================================================================= */

/* (a) + (c) — THE RESERVE GOES BECAUSE THE DOCK GOES.
   .rg-chat holds 154px of bottom margin for the dock and its bloom. With the dock stood down for
   the duration of a thread that margin is reserving space for furniture that is not there, so the
   transcript gets it back. The keyboard-up rule above (:root.rg-kb) already collapsed it; this does
   the same for the keyboard-DOWN case, which is the state the photograph was taken in.
   Left and right go too: the bubbles below are the shapes now, so the panel stops being one. */
.rg-thread .rg-chat{
  margin:var(--rg-sp2) 0 calc(var(--rg-sp2) + env(safe-area-inset-bottom,0px));
  border-radius:0;
  background:none;
  backdrop-filter:none;-webkit-backdrop-filter:none;
  box-shadow:none;
}
/* THE DOCK, MOVED RATHER THAN REMOVED — see the comment at views/inbox.js where the class is set.
   pointer-events:none so a tap in the composer's neighbourhood cannot reach a tab that is off
   screen but still hit-testable. */
:root.rg-thread-open .rg-dock-nav{
  transform:translateY(calc(100% + 24px));
  pointer-events:none;
}

/* (f) — THREE STACKED CARDS BECOME ONE LINE.  ST24 FREEZE, 2026-08-23.
   The title bar, a full .rg-g takeover card, a teal language banner and — on a lead that had been
   handed over — a FOURTH teal escalation banner took roughly the top third of the screen before one
   message was visible. That fourth is the one that mattered: it renders on exactly the lead
   somebody opens in a hurry.

   IT IS NOT A CARD ANY MORE, AND THAT IS THE WHOLE SAVING. .rg-g's 26px radius, blur, inset rim and
   40px drop shadow are what made three of these read as three separate objects, each paying its own
   margin and its own 18px of padding. A status line is not an object, it is a line: a hairline under
   it and the actor's own tint carry the same meaning in the height of its own button.

   THE 44px CONTROL IS NOT TOUCHED. .rg-mini keeps its floor — scripts/check-tap-targets.js arm 1 and
   its poison p1 both hang off that exact declaration — so this row can never be shorter than a
   thumb, and that is the right floor for it. The row is as tall as its button and no taller.

   ⛔ THE COLOUR LAW IS THE ONE THE BUBBLES BELOW OBEY. Teal is Realuma AI, champagne is a human
   acting, the customer is neither. Existing tokens only; no hex appears in this block. */
.rg-thread .rg-th-state{
  display:flex;align-items:center;gap:var(--rg-sp2);
  padding:var(--rg-sp2) var(--rg-sp3);
  border-bottom:1px solid var(--rg-hair);
}
.rg-thread .rg-th-ai  {background:var(--rg-ai-wash);   box-shadow:inset 2px 0 0 var(--rg-ai)}
.rg-thread .rg-th-you {background:var(--rg-champ-wash);box-shadow:inset 2px 0 0 var(--rg-champ)}
.rg-thread .rg-th-main{flex:1;min-width:0}
.rg-thread .rg-th-l1{display:flex;align-items:center;gap:var(--rg-sp1);flex-wrap:wrap}
.rg-thread .rg-th-who{font-size:13.5px}
/* WHICH LANGUAGE LEAVES THE BUILDING, and it is teal because the translation is Realuma AI's act
   and not the agent's: the agent types, Realuma sends. The full sentence stays on the title. */
.rg-thread .rg-th-lang{
  font-family:var(--rg-mono);font-size:var(--rg-fs-lab);
  letter-spacing:var(--rg-tr-lab);text-transform:uppercase;
  color:var(--rg-ai-lit);
}
/* TWO LINES AND NO MORE. The escalation reason is a whole sentence and it may not be allowed to
   push the composer down a third time. */
.rg-thread .rg-th-sub{
  font-size:var(--rg-fs-xs);color:var(--rg-muted);margin-top:2px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.rg-thread .rg-th-state .rg-ai-ic{margin-top:0}

/* (e) — IT READS AS A CONVERSATION.
   .rg-turn was a full-width block with a hairline above it, which is a TRANSCRIPT: correct for the
   copilot, wrong for two people talking. Here each turn is a bubble that takes only the width it
   needs, on the side of whoever said it. The hairline goes: a bubble is its own edge. */
.rg-thread .rg-turn{
  border-top:none;
  padding:0;
  margin:0 0 var(--rg-sp2);
  max-width:82%;
  display:flex;flex-direction:column;
}
.rg-thread .rg-turn:first-child{margin-top:0}
/* THE CUSTOMER ON THE LEFT, US ON THE RIGHT. Both of ours sit right because the reader of this
   screen is the agent: their side of the conversation is one side, whether the words were the AI's
   or their own. WHICH of the two it was is the colour, below, and never the side. */
.rg-thread .rg-t-them{margin-right:auto;align-items:flex-start}
.rg-thread .rg-t-ai,
.rg-thread .rg-t-you{margin-left:auto;align-items:flex-end}

.rg-thread .rg-turn-h{margin-bottom:4px}
.rg-thread .rg-turn-b{
  padding:var(--rg-sp2) var(--rg-sp3);
  border-radius:var(--rg-r-out);
  background:var(--rg-glass);
  box-shadow:inset 0 0 0 1px var(--rg-edge);
}
/* The tail. One corner square on the speaker's side, which is what makes a rounded box read as
   coming FROM somewhere rather than floating. */
.rg-thread .rg-t-them .rg-turn-b{border-bottom-left-radius:var(--rg-sp1)}
.rg-thread .rg-t-ai .rg-turn-b,
.rg-thread .rg-t-you .rg-turn-b{border-bottom-right-radius:var(--rg-sp1)}

/* ⛔ THE COLOUR LAW, AND IT MAY NOT BE SWAPPED. Teal is Realuma AI. Champagne is a human acting.
   The customer is NEITHER — not the assistant, not the operator — so they keep the neutral glass
   .rg-who-them has always carried. Existing tokens only; no hex appears in this block. */
.rg-thread .rg-t-ai .rg-turn-b{
  background:var(--rg-ai-wash);
  box-shadow:inset 0 0 0 1px var(--rg-ai);
}
.rg-thread .rg-t-you .rg-turn-b{
  background:var(--rg-champ-wash);
  box-shadow:inset 0 0 0 1px var(--rg-champ);
}

/* THE CLOCK, UNDER THE BUBBLE. It used to sit in the header next to the speaker's name, which put
   the least important thing on the line the eye reads first. Delivery status rides with it, because
   "sent at 14:02" is one fact and belongs on one line. */
.rg-thread .rg-turn-f{
  display:flex;align-items:center;gap:var(--rg-sp1);
  margin-top:4px;
  font-size:var(--rg-fs-xs);
  color:var(--rg-faint);
}

.rg-chat-sep{
  display:flex;align-items:center;gap:var(--rg-sp2);
  margin:var(--rg-sp3) 0 var(--rg-sp2);
  font-family:var(--rg-mono);font-size:var(--rg-fs-lab);
  letter-spacing:var(--rg-tr-lab);text-transform:uppercase;
  color:var(--rg-faint);
}
.rg-chat-sep::before,.rg-chat-sep::after{content:'';flex:1;height:1px;background:var(--rg-hair)}
.rg-chat-sep-new{color:var(--rg-champ)}
.rg-chat-sep-new::before,.rg-chat-sep-new::after{background:var(--rg-champ-rim)}

/* ============================================================
   22. REALUMA IN DAYLIGHT.

   NIGHT IS THE DEFAULT AND ALWAYS WILL BE. Day is a real choice a person
   makes, so it is a real theme rather than a lightened one: ivory ground,
   champagne, deep ink, sunlit, the way the marketing site already is.

   THE LAW IS THE HUE, NOT THE HEX. Champagne means a human acted and teal
   means Realuma AI acted in BOTH themes. What changes is which member of
   each hue family carries on its own ground: on obsidian the light member
   (#C9A170, #5FA0AE), on ivory the deep one (#8F6B36, #2F5B63). Measured,
   champagne as text goes 6.28:1 at night and 4.54:1 in day; teal goes
   5.08:1 and 7.02:1. Same meaning, opposite lightness.

   GLASS IN DAYLIGHT IS FROSTED, NOT TINTED. The blur is identical and the
   tint inverts. On dark, glass is a window you see darkly through; on
   light it is a scrim you see brightly through. Same material, opposite
   polarity, and the world behind it is still worth refracting.

   NO CAST SHADOWS IN DAY, and that is a design decision rather than an
   omission. A shadow that reads as depth on obsidian is a grey smear on
   ivory. Daylight objects on paper are defined by their EDGES, so day
   carries depth on rim and fill alone. Every inset shadow stays, because
   an inset is the light falling INTO something and that is a different
   statement. See realuma-forms.css for the hole.

   TWO INK WEIGHTS, NOT THREE. On ivory, muted and faint both need the same
   alpha to clear AA, so a third weight would be a distinction nobody can
   see. Day has ink and quiet. Night keeps its three.
   ============================================================ */
[data-theme="light"]{
  --rg-canvas:#FBFAF6;

  /* the law, in its day members. FILLS keep the night hex, because a
     champagne fill is champagne on any ground; only the TEXT member moves. */
  --rg-champ:#7A5A2C;
  --rg-champ-lit:#E0BC8E;
  --rg-champ-wash:rgba(143,107,54,.10);
  --rg-champ-rim:rgba(143,107,54,.30);
  --rg-ink:#16333A;                    /* a champagne fill is light in BOTH themes, so its ink is dark in both */

  --rg-ai:#2F5B63;
  --rg-ai-lit:#3F707D;                 /* the day teal FILL, dark enough to carry ivory at 5.26:1 */
  --rg-ai-wash:rgba(47,91,99,.08);
  --rg-ai-rim:rgba(47,91,99,.28);

  --rg-ok:#35684F;
  --rg-stop:#A8362A;

  /* TWO WEIGHTS. muted and faint are the same value on purpose: see above. */
  --rg-text:#16333A;
  --rg-muted:rgba(22,51,58,.80);
  --rg-faint:rgba(22,51,58,.80);
  --rg-hair:rgba(22,51,58,.10);

  /* frosted: the tint inverts, the blur does not */
  --rg-glass:rgba(255,255,255,.62);
  --rg-glass-hi:rgba(255,255,255,.74);
  --rg-glass-deep:rgba(255,255,255,.78);
  --rg-edge:rgba(22,51,58,.10);
  --rg-edge-top:rgba(255,255,255,.9);

  /* ---- THE MATERIAL IN DAYLIGHT, and this is where the ceiling is ----
     A 78% white fill leaves the backdrop 22% of the result, so the through-glass chroma cannot
     pass 0.22 x 255 = 56 against champagne's 89 however high saturate goes. 3.4 reaches that
     ceiling; 5 measures the same number and only clips harder. The value is 3.4 because it is
     the smallest one that gets there.
     THE SPLIT GOES QUIETER, exactly as the lens's dispersion does eleven rules below and for the
     same reason: at night's alpha these two colours read as a printing error on ivory. .09
     measured 1.03:1 against the pixel beside it, which is the intent. */
  --rg-sat-dock:340%;
  --rg-split-w:inset -1px 0 0 rgba(255,150,80,.13);
  --rg-split-c:inset  1px 0 0 rgba(96,196,255,.13);
  /* THE RIM LIFTS LESS IN DAY. brightness() on a near-white surface has almost nowhere to go
     before it is simply white, and a band of white on white is a band that is not there. */
  --rg-rim-lift:1.04;
  /* BRIGHTER IN DAY, NOT DIMMER. The band it sits on is 78% white, so a white line at night's alpha
     is a line nobody can see; the specular is the one part of this material that has to work
     HARDER in daylight rather than less. */
  --rg-spec:.85;
}

/* THE DAY WORLD IS ATMOSPHERE TOO, and it is designed to a measured spread
   from the start rather than discovered afterwards. It is not the night
   world lightened: two coloured glows on ivory are two stains. The model
   inverts. The ground is the light source, so the gradients are WARMTH and
   SHADE rather than glows, and their alphas are a third of night's because
   on ivory a gradient does not have to glow to be seen. */
[data-theme="light"] .rg-world{
  background:
    radial-gradient(78% 58% at 76% 8%,  rgba(201,161,112,.13), transparent 64%),
    radial-gradient(72% 62% at 12% 30%, rgba(95,160,174,.09),  transparent 66%),
    radial-gradient(90% 70% at 50% 96%, rgba(196,178,150,.16), transparent 72%),
    linear-gradient(172deg,#FDFBF7 0%,#F7F1E6 46%,#F2EADC 100%);
}

/* ---------- NO CASTS. Rim and fill carry depth. ---------- */
[data-theme="light"] .rg-g{
  box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
}
[data-theme="light"] .rg-g-champ{
  background:linear-gradient(152deg,rgba(201,161,112,.16),rgba(201,161,112,.04) 52%),var(--rg-glass-hi);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px var(--rg-champ-rim);
}
[data-theme="light"] .rg-g-ai{
  background:linear-gradient(150deg,rgba(95,160,174,.16),rgba(95,160,174,.04) 60%),var(--rg-glass);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px var(--rg-ai-rim);
}
[data-theme="light"] .rg-btn{
  background:linear-gradient(178deg,var(--rg-champ-lit),#C9A170);
  color:var(--rg-ink);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5), inset 0 0 0 1px rgba(122,90,44,.42);
}
[data-theme="light"] .rg-btn-ai{
  background:linear-gradient(178deg,var(--rg-ai-lit),var(--rg-ai));color:#FBFAF6;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.28), inset 0 0 0 1px rgba(47,91,99,.5);
}
[data-theme="light"] .rg-back,
[data-theme="light"] .rg-mini{
  background:rgba(255,255,255,.66);
  border-color:var(--rg-edge);
  box-shadow:inset 0 0 0 1px var(--rg-edge);
}
[data-theme="light"] .rg-av{
  background:rgba(255,255,255,.7);
  box-shadow:inset 0 0 0 1px var(--rg-edge), 0 0 0 1.5px var(--rg-champ);
}
[data-theme="light"] .rg-sheet-panel{box-shadow:none}
[data-theme="light"] .rg-sheet-panel::before,
[data-theme="light"] .rg-chat{
  background:var(--rg-glass-deep);
  box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
}
[data-theme="light"] .rg-sheet-scrim{background:rgba(22,51,58,.34)}
[data-theme="light"] .rg-sheet-foot,
[data-theme="light"] .rg-chat-foot{
  background:linear-gradient(to top,rgba(22,51,58,.05),rgba(22,51,58,.02));
  box-shadow:none;
}
[data-theme="light"] .rg-pick{
  background:rgba(22,51,58,.05);
  box-shadow:inset 0 0 0 1px var(--rg-edge);
  color:var(--rg-muted);
}
[data-theme="light"] .rg-pick.rg-picked,
[data-theme="light"] .rg-picks-seg .rg-pick.rg-picked{
  background:linear-gradient(178deg,var(--rg-champ-lit),#C9A170);
  color:var(--rg-ink);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5), inset 0 0 0 1px rgba(122,90,44,.42);
}
[data-theme="light"] .rg-picks-seg{
  background:rgba(22,51,58,.06);
  box-shadow:inset 0 2px 4px rgba(22,51,58,.10), inset 0 0 0 1px var(--rg-edge);
}
[data-theme="light"] .rg-tab-dot{background:var(--rg-champ);color:#FBFAF6;box-shadow:none}
/* THESE CARRY HARDCODED NEAR-WHITE rgba LITERALS rather than tokens, so they do not follow
   --rg-text and had to be named. Two of them measured 1.05:1 in day before this. */
[data-theme="light"] .rg-task-p{color:rgba(22,51,58,.82)}
[data-theme="light"] .rg-ai-t{color:rgba(22,51,58,.88)}
[data-theme="light"] .rg-turn-b,[data-theme="light"] .rg-row-n,[data-theme="light"] .rg-empty-t{color:var(--rg-text)}
[data-theme="light"] .rg-empty{color:var(--rg-muted)}
/* --rg-ai-lit is the FILL member in day, so every place night uses it as TEXT takes --rg-ai. */
[data-theme="light"] .rg-who-ai,[data-theme="light"] .rg-mini-ai,[data-theme="light"] .rg-ai-ic,
[data-theme="light"] .rg-sec-ai .rg-sec-ic,[data-theme="light"] .rg-ai-t b,
[data-theme="light"] .rg-empty-ai .rg-empty-ic{color:var(--rg-ai)}
[data-theme="light"] .rg-ai-ic{filter:none}
/* the same sentence for champagne, and .rg-task-eye is its first entry: --rg-champ-lit is the
   FILL member in day, so a label that takes it as TEXT at night takes --rg-champ here. Measured
   on ivory: 5.61:1 worst across all nine hero states. */
[data-theme="light"] .rg-task-eye{color:var(--rg-champ)}
[data-theme="light"] .rg-b-sent{color:var(--rg-ai)}
/* the chevron is a thin antialiased glyph, so its measured ink is the edge rather than the
   core. It clears the 3:1 that WCAG asks of a graphical indicator either way; it takes full ink
   in day so it clears the 4.5 this product holds everything to. */
[data-theme="light"] .rg-chev{color:var(--rg-text)}
[data-theme="light"] .rg-counting{text-shadow:none}
[data-theme="light"] .rg-badge{backdrop-filter:none;-webkit-backdrop-filter:none}
[data-theme="light"] .rg-load i{background:rgba(22,51,58,.08);box-shadow:none}

/* REFUSED, INVERTED. It is flat, dashed and RECESSED in both themes; on
   ivory that means a warm grey sink and an ink-toned dash rather than a
   white wash and a white dash, which would be nothing at all. */
[data-theme="light"] .rg-refused{
  background:rgba(22,51,58,.05);
  border-color:rgba(22,51,58,.22);
}

/* ---------- THE BAR, THE SHEET AND THE HAIRLINES ---------- */
[data-theme="light"] .rg-bar{background:rgba(251,250,246,0)}
[data-theme="light"] .rg-bar::after{opacity:0}
[data-theme="light"] .rg-bar::after{animation-name:rg-bar-rim-light}
@keyframes rg-bar-rim-light{to{opacity:.34}}
@keyframes rg-bar-in-light{to{
  background:var(--rg-glass-deep);
  box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
  backdrop-filter:blur(var(--rg-blur-bar)) saturate(150%);
  -webkit-backdrop-filter:blur(var(--rg-blur-bar)) saturate(150%);
}}
[data-theme="light"] .rg-bar{animation-name:rg-bar-in-light}

/* ---------- THE NAV IN DAYLIGHT ---------- */
[data-theme="light"] .rg-dock-nav::before{
  background:rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px var(--rg-edge);
}
[data-theme="light"] .rg-lens{
  background:linear-gradient(163deg, rgba(255,255,255,.9) 0%, rgba(255,255,255,.5) 38%, transparent 62%),
             rgba(22,51,58,.05);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.8), inset 0 0 0 1px var(--rg-edge);
}
/* THE CHROMATIC RIM SURVIVES, and it survives BEST. Dispersion needs a
   bright source and daylight supplies more of one, not less. What changes
   is weight: at night's alpha the colours read as a printing error on
   ivory, so it is quieter at rest, stronger in flight, and blurred further
   so it reads as refraction rather than as a drawn outline. */
[data-theme="light"] .rg-lens::before{opacity:.34;filter:blur(1.8px)}
[data-theme="light"] .rg-lens.rg-flying::before{opacity:.70;filter:blur(1.5px)}
[data-theme="light"] .rg-tab{color:var(--rg-muted)}
[data-theme="light"] .rg-on{color:var(--rg-text)}
[data-theme="light"] .rg-on .rg-tab-ic{filter:none}
[data-theme="light"] .rg-lit{color:var(--rg-text)}
[data-theme="light"] .rg-lit .rg-tab-ic{filter:none}

/* THE ORB AT NOON IS A LENS, NOT A SOURCE, and it keeps its own law: if
   you can see where it ends it stopped being light, so it still has no
   border and still fades to nothing. What inverts is the DIRECTION of the
   difference. At night it emits champagne into darkness. In day it
   concentrates and warms the ivory behind it, brighter than the ground at
   the core and a soft warm shade at the perimeter, both approaching zero
   difference at the boundary. It reads as a magnifier resting on the page.
   THE PARAGRAPH THAT WAS HERE NAMED A LOSS THAT NO LONGER EXISTS: it said
   .rg-ai-glow's spill through the dock's glass was a smudge on ivory and
   therefore quieter in day. The bloom left the bar on 2026-08-18 and the
   spill went with it, so day and night now differ in this one object by
   the direction of its own light and nothing else. */
[data-theme="light"] .rg-orb::before{
  background:radial-gradient(circle,
    rgba(255,252,246,.95) 0%,
    rgba(245,232,210,.72) 18%,
    rgba(214,190,152,.34) 38%,
    rgba(190,168,132,.14) 58%,
    transparent 76%);
}
[data-theme="light"] .rg-orb::after{
  background:radial-gradient(circle,
    rgba(160,132,88,.16) 0%, rgba(160,132,88,.07) 36%, transparent 68%);
}
[data-theme="light"] .rg-orb-ic{color:#3A2A12}
[data-theme="light"] .rg-orb-locked::before{
  background:radial-gradient(circle,
    rgba(255,252,246,.6) 0%, rgba(235,220,196,.34) 22%,
    rgba(200,180,148,.14) 44%, transparent 72%);
}
[data-theme="light"] .rg-orb-lock{background:var(--rg-champ);color:#FBFAF6}

/* ============================================================
   23. THE MENU: A SLAB THAT UNFOLDS FROM THE DOCK.

   IT WAS A LID. A wall-to-wall panel with two square top corners sliding
   up from the bottom edge, and it was the last piece of chrome that did
   not live in the world. Everything else floats: the dock hovers, the lens
   flies, the orb is light with no edge. This belongs to that family.

   SO IT IS THE DOCK'S SIBLING, not a sheet. Same width rule, same 27px
   radius, inset from both edges with the world visible past it, and the
   chromatic rim at rest alpha, which is the detail that says dock rather
   than card. The material is on ::before for the same reason the dock's
   is: the panel SCALES on open, and a fill and a blur on the same box as a
   transform is a picture of glass being stretched.

   IT UNFOLDS RATHER THAN APPEARS. transform-origin sits below the panel at
   the dock's centre, so the scale reads as the navigation opening upward
   rather than as a card fading in. The dock's own ::before deforms for the
   46ms it already spends on a press, so the two objects acknowledge each
   other instead of ignoring each other.

   ITS HEIGHT IS ITS CONTENT. A landlord hides all four agent entries, so
   their menu is visibly a small floating object rather than a half-screen
   sheet with an empty half.
   ============================================================ */
.rg-menu{position:fixed;top:0;left:0;right:0;height:100dvh;z-index:70}   /* dvh, not the ICB: see .rg-sheet */
.rg-menu-scrim{
  position:absolute;inset:0;
  background:rgba(8,15,20,.62);
  transition:opacity var(--rg-t-mid) var(--rg-ease);
}
.rg-menu-panel{
  position:absolute;left:50%;
  bottom:calc(90px + env(safe-area-inset-bottom,0px));
  width:min(400px,calc(100% - 30px));
  padding:var(--rg-sp3);
  border-radius:27px;
  background:none;                       /* NO MATERIAL: it scales. see ::before */
  transform:translateX(-50%);
  transform-origin:50% calc(100% + 62px);   /* the dock's centre, below the panel */
  transition:transform var(--rg-t-mid) var(--rg-spring), opacity var(--rg-t-fast) var(--rg-ease);
  touch-action:none;
}
.rg-menu-panel::before{
  content:'';position:absolute;inset:0;z-index:0;
  border-radius:inherit;pointer-events:none;
  background:var(--rg-glass-deep);
  backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  -webkit-backdrop-filter:blur(var(--rg-blur-dock)) saturate(var(--rg-sat-dock));
  box-shadow:
    inset 0 1px 0 var(--rg-edge-top),
    inset 0 0 0 1px var(--rg-edge),
    0 22px 50px -22px rgba(0,0,0,.85);
}
/* THE CHROMATIC RIM, the same dispersion the lens carries, at rest weight.
   This is what makes it read as the dock's sibling rather than as a card. */
.rg-menu-panel::after{
  content:'';position:absolute;inset:-1px;border-radius:inherit;padding:1.4px;
  pointer-events:none;z-index:0;
  background:conic-gradient(from 200deg,
    rgba(255,96,64,.62), rgba(255,214,120,.52), rgba(126,255,190,.48),
    rgba(96,196,255,.62), rgba(196,128,255,.5), rgba(255,96,64,.62));
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude;
  filter:blur(1.2px);opacity:.62;
}
/* every child sits above the material, exactly as the sheet's parts do.
   :not(.rg-rim) BECAUSE THE RIM BAND IS NOT CONTENT. It is a layer of the material, it is
   absolutely positioned, and this rule would have made it position:relative at z-index 1 —
   which is to say it would have blurred the outer five pixels of the menu's own rows. Section
   2b explains why the band has to paint after the material and before everything else. */
.rg-menu-panel > *:not(.rg-rim){position:relative;z-index:1}

/* ---------- WHAT MAKES IT A DIRECTORY RATHER THAN A DRAWER ----------
   Two elements, no new colour and no new font. The heading is the existing muted ink at the
   existing small step; the rule is the existing hairline. Everything the grouping does visually is
   made of parts the system already had, which is why this section adds five lines rather than a
   component.
   NOT UPPERCASED, and that is a Vietnamese decision rather than a taste one. The eyebrow idiom in
   section 8 sets text-transform:uppercase with .22em tracking, and capital Vietnamese carries its
   tone marks ABOVE the cap height: "VIỆC CỦA BẠN" needs more leading than an 11px eyebrow gives it
   before the marks start touching the row above. Sentence case at 1.5 line-height clears them with
   room to spare, in both languages, at every width.
   NO CHAMPAGNE ON EITHER. Champagne is a human acting and a heading is not an action; it names the
   things below it, which are the actions and which already carry it on their icons. */
/* ONE LABEL TYPE, TWO PLACES, AND THE ONLY DIFFERENCE IS THE MARGIN. .rg-feed-eye names a group on
   the home feed and .rg-menu-grid-h names one in the menu; they are the same object doing the same
   job, so they share the declaration rather than drifting into two sizes of the same idea. Only the
   side margin differs, because one sits inside a padded panel and the other beside full width
   cards that carry the page's own gutter. */
.rg-menu-grid-h,.rg-feed-eye{
  font-family:var(--rg-body);font-size:11px;font-weight:500;letter-spacing:.08em;
  color:var(--rg-muted);line-height:1.5;
}
.rg-menu-grid-h{margin:var(--rg-sp2) 2px 7px}
.rg-feed-eye{margin:var(--rg-sp4) var(--rg-sp3) 8px}
/* ---- A NOTE THAT REPORTS AN OUTCOME, in the two tokens that already mean those two things ----
   NO NEW COLOUR: --rg-stop and --rg-ok are the product's existing failure and success inks, already
   carried as TEXT by .rg-mini-danger and .rg-b-live. What is new is that an explanatory note can
   now be an outcome, which it could not before: every .card-note in the product is muted, and a
   failure sentence rendered in the same grey as the help text above it is a failure somebody reads
   past. It exists because a toast was the alternative and a toast is gone in four seconds, which is
   the wrong lifetime for the only evidence somebody has that they were not heard. */
.rg-note-stop{color:var(--rg-stop)}
.rg-note-ok{color:var(--rg-ok)}
/* ---- ONE ROW, ONE LINE, AND THAT IS WHAT MAKES SIX CARDS FIT A PHONE ----
   .rg-row-s wraps everywhere else in the product and must keep wrapping: on the calendar and in the
   inbox a second line is the content. Here it is the difference between a 63px row and a 142px one,
   and six of the second kind is a screen nobody can see the bottom of. Scoped to the feed panel by
   its own class rather than by .rg-list, because the inbox and both calendars use .rg-list too.
   min-width:0 ON THE FLEX CHILD is what actually lets it clip. A flex item's default min-width is
   auto, so .rg-row-main refuses to shrink below its longest word and the ellipsis never appears;
   .rg-row-main already sets it, and this states the dependency rather than leaving it to luck. */
.rg-feed .rg-row-main{min-width:0}
.rg-feed .rg-row-s{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rg-menu-panel > .rg-menu-grid-h:first-child{margin-top:0}

/* ---- WHICH ACCOUNT AM I IN, in the More sheet, directly under "This account" ----
   It is a READING, not a control: no tap target, no 44px floor, nothing to press. The email is set
   in the monospace face at its literal case for the same reason as .rg-bar-who — the two accounts
   on this project are one dot apart and an uppercased address is harder to tell apart, not easier.
   The label above it is the only uppercase part, because that is a label and this is evidence. */
.rg-menu-who{margin:0 2px var(--rg-sp2);padding:10px 12px;border-radius:var(--rg-r-sm,10px);
  background:var(--rg-glass-2,rgba(255,255,255,.04));border:1px solid var(--rg-hair,rgba(255,255,255,.08))}
.rg-menu-who-l{font-family:var(--rg-mono);font-size:var(--rg-fs-lab);letter-spacing:.16em;
  text-transform:uppercase;color:var(--rg-muted);line-height:1.4}
.rg-menu-who-b{font-family:var(--rg-body);font-size:15px;line-height:1.35;margin-top:4px;
  overflow-wrap:anywhere}
.rg-menu-who-e{font-family:var(--rg-mono);font-size:var(--rg-fs-lab);line-height:1.45;margin-top:2px;
  color:var(--rg-muted);overflow-wrap:anywhere;text-transform:none}
.rg-menu-grid-h.hidden{display:none}
/* THE RULE BETWEEN THE WORK AND THE ACCOUNT. One hairline, the same one every other section break
   in the system uses, with more air above it than below so it reads as belonging to what follows. */
.rg-menu-sep{height:1px;background:var(--rg-hair);margin:var(--rg-sp3) 0 var(--rg-sp2)}

.rg-menu-row{display:flex;gap:var(--rg-sp2);margin-bottom:var(--rg-sp3)}
.rg-menu-cell{flex:1;min-width:0}
/* auto-fit RATHER THAN 1fr 1fr, AND THE REASON IS THE HIDE LISTS. This grid holds seven entries
   and every one of them can be hidden: a broker is left with one, and one item in a fixed two
   column grid is a half-width tile with a hole beside it, which reads as a row that failed to
   render rather than as a panel with one thing in it. auto-fit COLLAPSES the empty track, so a
   lone survivor spans the row and a pair still sits two up.
   128px, not 140: the panel's inner width at 360 is 294px, and two 140s plus the gap is 292, which
   is inside the margin of error of a font that has not loaded yet. Two 128s and the gap is 266. */
.rg-menu-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));
  gap:var(--rg-sp1);margin-bottom:var(--rg-sp1)}
.rg-menu-item{
  display:flex;align-items:center;gap:var(--rg-sp2);
  width:100%;min-height:46px;padding:0 var(--rg-sp2);
  border:none;border-radius:var(--rg-r-in);cursor:pointer;
  background:rgba(255,255,255,.05);
  box-shadow:inset 0 0 0 1px var(--rg-edge);
  color:var(--rg-text);text-decoration:none;
  font-family:var(--rg-body);font-size:var(--rg-fs-body);
  transition:background var(--rg-t-fast) var(--rg-ease), transform var(--rg-t-mid) var(--rg-spring);
  -webkit-tap-highlight-color:transparent;
}
.rg-menu-item:active{transform:scale(.96)}
.rg-menu-item .more-ic{flex:none;width:20px;text-align:center;color:var(--rg-champ);font-size:15px}
.rg-menu-item.hidden{display:none}
.rg-menu-danger{margin-top:var(--rg-sp1)}
.rg-menu-danger .more-ic{color:var(--rg-stop)}
.rg-menu-up .more-ic{color:var(--rg-champ)}
.rg-menu-up{box-shadow:inset 0 0 0 1px var(--rg-champ-rim);margin-bottom:var(--rg-sp1)}

/* ---------- DAYLIGHT ---------- */
[data-theme=light] .rg-menu-scrim{background:rgba(22,51,58,.34)}
[data-theme=light] .rg-menu-panel::before{
  background:var(--rg-glass-deep);
  box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
}
[data-theme=light] .rg-menu-panel::after{opacity:.34;filter:blur(1.8px)}
[data-theme=light] .rg-menu-item{
  background:rgba(22,51,58,.05);
  box-shadow:inset 0 0 0 1px var(--rg-edge);
}

/* ---------- REDUCED MOTION ----------
   The panel is simply there at full scale, the scrim at full alpha, and
   both segmented controls still show which one is picked by fill and by
   ink polarity, which is section 20's contract and carries no motion. */
@media (prefers-reduced-motion:reduce){
  .rg-menu-panel{transform:translateX(-50%)}
  .rg-menu-scrim{opacity:1}
}

/* ============================================================
   24. REDUCED MOTION
   Everything above is decoration on top of a screen that must already
   work. This rule proves it: with all motion dead, every state is still
   distinguishable and every number is already correct in the markup.
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
  .rg-world{transform:scale(1.08)}
  /* THE BAR KEEPS ITS MATERIAL WITH MOTION DEAD. Its fill, blur and rim are scroll-driven
     animations, and section 24 kills every animation, so without this the bar would be a
     permanently transparent frame. It lands ON rather than off, which is the readable end. */
  .rg-bar{background:var(--rg-glass-deep);
    box-shadow:inset 0 1px 0 var(--rg-edge-top), inset 0 0 0 1px var(--rg-edge);
    backdrop-filter:blur(var(--rg-blur-bar)) saturate(180%);
    -webkit-backdrop-filter:blur(var(--rg-blur-bar)) saturate(180%)}
  .rg-bar::after{opacity:.62}
  .rg-rise{opacity:1;transform:none}
  .rg-spark polyline{stroke-dashoffset:0}
  .rg-spark circle{opacity:1;transform:scale(1)}
  /* THE SHEET IS SIMPLY THERE, AND IT STILL DRAGS. Both of those are the
     point. The slide and the fade are gone, so it arrives at rest and
     leaves at once; the drag is not motion the app decided to perform, it
     is a thing following a finger, and a thing that stops following a
     finger is broken rather than calm. Same reading realuma-nav.css:349
     makes about the lens. */
  .rg-sheet-panel{transform:none}
  .rg-sheet-scrim{opacity:1}
  /* PICKED NEEDS NOTHING HERE, and that is the point rather than an
     omission. Fill, ink polarity and rim are all static, so the blanket
     transition:none above removes the cross-fade and leaves every one of
     the three differences standing. Written down so nobody adds a rule to
     "fix" a state that was never broken. */
}

/* ============================================================
   25. NO BACKDROP-FILTER
   Older Android WebView. The glass becomes solid rather than absent:
   an unreadable translucent panel is worse than an opaque one.
   ============================================================ */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .rg-g,.rg-bar,.rg-back,.rg-mini,.rg-badge{background:#16242C}
  .rg-sheet-panel::before{background:#16242C}
  .rg-g-champ{background:linear-gradient(152deg,rgba(201,161,112,.22),#16242C 52%)}
  .rg-g-ai{background:linear-gradient(150deg,rgba(95,160,174,.22),#16242C 60%)}
}
