/* ============================================================================
 * !!!!!  CHECK THE APPMENU, AND DETAILS+OPTIONS 3 DOTS ARE STILL WORKING
 *        PERFECTLY  !!!!!
 * ----------------------------------------------------------------------------
 * FOUNDER ORDER, 2026-09-03, word for word:
 *   "APPMENU, DETAILS + 3 DOTS OPTIONS ARE NOT THERE ONLOAD!!!!!!!! I TOLD YOU
 *    TO CONNECT IT FULLY TO THE DOCK & HOW IT LOADS... ITS FUCKING EASY TO CHECK
 *    THE WINDOW STATE (WINDOWED, FILL SCREEN, ETC) AND KEEEP IT LOCKED TO THE
 *    SIDE OF THE APP ALWAYS. INSIDE & OUT FIX THIS... DO NOT EVER LET IT GET
 *    BROKEN AGAIN."  and: "Fix it permanently. Permanently means fix it and dont
 *    fucking touch it again, and ANY / ALL things / files / parts that touch
 *    anything it's connected to gets a hard note on it."
 *
 * THIS FILE TOUCHES THAT CHAIN. BEFORE YOU EDIT IT, AND AGAIN AFTER:
 *   1. Cold-load the app's OWN address in a fresh private window, signed in.
 *   2. The app menu bar .z-bar[data-z-pagebar] must be on screen no later than
 *      the dock [data-z-dock].
 *   3. The options + 3 dots [data-z-chat-tabs] must be on screen no later than
 *      the dock, and pinned to THAT app's own side in EVERY window state
 *      (data-z-page-app-windowed / -minimised / data-z-pagebar-on).
 *   4. Open the picture from that same run and LOOK at it. No picture = UNVERIFIED.
 * One cause, three symptoms - they all read #z-shell data-z-server-auth.
 * He has said this 50+ times. It is one of the most consistent faults in the
 * project. If your change cannot prove all three above, DO NOT SHIP IT.
 * ========================================================================== */
/* Z_FONTS_NEVER_SWAP_IN_LATE_2026-08-31 (Assistant B)
   FOUNDER, 2026-08-31: "The fucking settings font in the app loaded with font and
   then the real one applied... NEVER EVER EEEVER LOAD FONTS. FONTS ARE
   PRELOADADED AT THE BEGINNING OF ENTERING THE WEBSITE AND ALL FONTS ADHERE TO
   THE RIGHT ONES."
   MEASURED, not guessed: both faces below said font-display: swap, which means
   "draw in some other font first, then change it once mine arrives" - that IS
   the change he saw. And the served page carried ZERO font preloads, so the file
   was only discovered after this stylesheet had been read.
   TWO PARTS: the head of the page now preloads both files (face/home/deploy/
   index.html), and swap becomes block, which means never draw the wrong font -
   wait the few milliseconds and draw the right one.
   REVERT THIS PART ALONE: put both back to font-display: swap and take the two
   preload lines out of the page head. */
/* Z World Shell — stylesheet
   Every value comes from tokens.css. No !important anywhere: the host page's
   own CSS can still win where it needs to. All selectors are namespaced .z-*
   or [data-z-*], so nothing leaks into host styles.
*/

/* ---------------------------------------------------------------- fonts */
@font-face {
  font-family: "Z Space Grotesk";
  src: url("fonts/space-grotesk-latin.woff2") format("woff2");
  font-weight: 300 700; font-style: normal; font-display: block;
}
@font-face {
  font-family: "Z JetBrains Mono";
  src: url("fonts/jetbrains-mono-latin.woff2") format("woff2");
  font-weight: 100 800; font-style: normal; font-display: block;
}

/* ------------------------------------------------------------- keyframes */
@keyframes z-halo {
  0%, 100% { box-shadow: var(--z-glow-halo-lo); }
  50%      { box-shadow: var(--z-glow-halo-hi); }
}
@keyframes z-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes z-drop {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes z-genie-out {
  0%   { opacity: 1;    transform: translate(0,0) scale(1) skewY(0deg); filter: blur(0); }
  55%  { opacity: .85;  transform: translate(calc(var(--z-gx) * .55), calc(var(--z-gy) * .55)) scale(.44,.3) skewY(4deg); filter: blur(.6px); }
  100% { opacity: 0;    transform: translate(var(--z-gx), var(--z-gy)) scale(.05,.02) skewY(9deg); filter: blur(2px); }
}
@keyframes z-genie-in {
  0%   { opacity: 0;   transform: translate(var(--z-gx), var(--z-gy)) scale(.05,.02) skewY(9deg); filter: blur(2px); }
  45%  { opacity: .9;  transform: translate(calc(var(--z-gx) * .45), calc(var(--z-gy) * .45)) scale(.5,.36) skewY(-3deg); filter: blur(.5px); }
  100% { opacity: 1;   transform: translate(0,0) scale(1) skewY(0deg); filter: blur(0); }
}

/* ----------------------------------------------------------------- shell */
/* ==========================================================================
   Z_THE_GLOW_MOVES_WITH_THE_PAGE_2026-09-02   (Assistant B)
   ==========================================================================
   FOUNDER, 2026-09-02: "the slide out menu from the left on every app is
   supposed to push the entire page, but it only pushes the element to the page
   because the background, it stays completely still... make sure that when all
   apps slide out, that it pushes the background as well"

   MEASURED, NOT GUESSED. The glow is centred at 48% of the WINDOW. With his
   side menu out on a 1280 wide screen the part he can actually see runs from
   320 to 1280, so 48% of THAT is 780.8 - while the glow sits at 48% of 1280,
   which is 614.4. 166px adrift, and it never moves because nothing told it to.

   HIS 48% IS UNTOUCHED. It is now measured against the area he can see instead
   of the whole window, which is what he asked for. The whole shift is
   0.52 x the menu's own width - that is arithmetic from his own number, not a
   number I picked: 320 + 0.48x(1280-320) minus 0.48x1280 = 0.52x320.

   NOTHING NEW WAS INVENTED FOR THE SELECTOR OR THE WIDTH EITHER. This copies the
   rule the chat runtime already uses to shift the live worker view by the same
   menu - same body:has(.sidebar:not(.z-sidebar-collapsed)), same
   min(320px, 80vw), same @media (min-width: 860px). That media query matters:
   below it the menu FLOATS OVER the page instead of pushing it, so the glow
   must not move there, and measuring the slide-out at phone width once had me
   reporting a defect that was really a different design.

   IT IS SET ON html AND body BOTH, because the guard in chat-runtime.js paints
   this gradient onto both of them inline, and a variable only reaches downwards.

   WHY ONE VARIABLE AND NOT FOUR EDITS: that same centre was written out in FOUR
   separate places - chat-runtime.js, this file, and his index.html twice. They
   now all read one variable, so they cannot drift apart again.

   REVERT THIS PART ALONE: delete this block. The variable falls back to 0px
   everywhere and the glow stops moving, exactly as it was.
   ========================================================================== */
@media (min-width: 860px) {
  html:has(.sidebar:not(.z-sidebar-collapsed)),
  body:has(.sidebar:not(.z-sidebar-collapsed)) {
    --z-glow-shift: calc(min(320px, 80vw) * 0.52);
  }
}

[data-z-shell] {
  position: relative;
  min-height: 100dvh;
  overflow: hidden;
  background: radial-gradient(circle at calc(48% + var(--z-glow-shift, 0px)) 46%, rgba(255,45,85,.19), transparent 44%), var(--z-bg);
  color: var(--z-text-2);
  font-family: var(--z-font);
  font-size: var(--z-fs-body);
  -webkit-font-smoothing: antialiased;
}
[data-z-shell] *, [data-z-shell] *::before, [data-z-shell] *::after { box-sizing: border-box; }



[data-z-shell] button { font-family: inherit; }
[data-z-shell] a { color: var(--z-violet-300); text-decoration: none; }
[data-z-shell] a:hover { color: var(--z-violet-200); }

.z-mono { font-family: var(--z-font-mono); }
.z-cap {
  font-family: var(--z-font-mono);
  font-size: 9px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--z-text-4);
}

/* --------------------------------------------------------------- desktop */
.z-desktop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100dvh;
  padding: 56px var(--z-space-7) var(--z-space-9);
}
.z-desktop__inner {
  width: 60vw; max-width: 60vw;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.z-orb {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: clamp(84px, min(27vw, 30vh), 300px);
  height: clamp(84px, min(27vw, 30vh), 300px);
  margin: 0 auto var(--z-space-1);
  border-radius: var(--z-radius-pill);
  background: radial-gradient(circle at 50% 50%, rgba(40,25,70,.52), rgba(10,6,22,.38) 66%, transparent);
  animation: z-halo var(--z-halo-cycle) cubic-bezier(.45,0,.55,1) infinite;
  pointer-events: none;
}
.z-orb img { display: block; width: 100%; height: 100%; object-fit: contain; }
.z-lede {
  font-family: var(--z-font-mono);
  font-size: clamp(12px, 4vw, 17px); font-weight: 700;
  color: var(--z-text); white-space: nowrap;
}
.z-sub {
  margin-top: var(--z-space-1);
  font-family: var(--z-font-mono);
  font-size: clamp(6px, 2.15vw, 11px); line-height: 1.3;
  color: rgba(224,230,244,.62); white-space: nowrap;
}
.z-grid {
  display: grid; grid-template-columns: repeat(10, minmax(0, 1fr));
  row-gap: clamp(8px, 2.25vh, 20px); column-gap: clamp(10px, 1.2vw, 20px);
  justify-items: center; width: 100%; margin-top: var(--z-space-2);
}
.z-tile {
  grid-column: auto / span 2;
  display: flex; flex-direction: column; align-items: center;
  min-width: 0; color: #eef0f6; background: none; border: 0; padding: 0;
  cursor: pointer;
}
.z-tile[data-z-ready="0"] { opacity: .28; }
.z-tile__art {
  display: block;
  width: min(1.7em, calc(12vw - 8px)); height: min(1.7em, calc(12vw - 8px));
  font-size: clamp(40px, 7.5vh, 66px);
  background-repeat: no-repeat; background-position: center; background-size: contain;
  animation: z-fade-in var(--z-dur) ease-out both;
}
.z-tile[data-z-ready="0"] .z-tile__art { filter: grayscale(.7); }
.z-tile__label {
  font-family: var(--z-font-mono);
  font-size: clamp(12px, 1.9vh, 15px); font-weight: 600; line-height: 1;
  white-space: nowrap;
}

/* ------------------------------------------------------------- app icons *
 * The artwork is a square raster. Rounding belongs on the ART, never on the
 * surrounding tile / button / cell.                                       */
/* Z_ONE_ICON_SET_2026-08-30 (Assistant C, founder-ordered).
   FOUNDER: "homepage is the icons. Dock is the icons ON the purple bg. The icons
   should be identical, and anywehre the icon appears onthe purple thing should all
   be identical ... Docks and everywhere PULL from the same set of icons"
   The picture is now the SAME one the homepage shows and the served page already
   preloads. The purple is drawn HERE, behind it, instead of being baked into a
   second shrunken copy. That is what makes the dock icon identical to the homepage
   icon and full size - his "the app icons look really fucking small" was the baked
   copies having the artwork shrunk inside their own padding.
   The treatment is the design's own --z-tile-art / --z-tile-art-inner from
   tokens.css, which were defined and had never been used anywhere.
   REVERT: delete this block and set Z_ONE_ICON_SET = false in shell.js. */
.z-appicon {
  display: block;
  object-fit: contain;
  border-radius: var(--z-radius-icon-lg);
  background: var(--z-tile-art);
  box-shadow: var(--z-tile-art-inner);
  filter: var(--z-shadow-icon);
}
.z-appicon--md { border-radius: var(--z-radius-icon-md); }
.z-appicon--md { width: var(--z-dock-icon); height: var(--z-dock-icon); border-radius: var(--z-radius-icon-md); }
.z-appicon--sm { width: 19px; height: 19px; border-radius: var(--z-radius-icon-sm); }
.z-appicon--lg { width: 38px; height: 38px; }

/* ==========================================================================
 * Z_ICON_SIZE_IS_ONE_NUMBER_2026-08-31  (Assistant C, founder-ordered)
 *
 * FOUNDER: "YOU STILL NEVER DID THE FUCKING ICON SIZES!!!!!! I TOLD U TO MFIX
 * THEM AT THE CORE LEVEL SO ITS EQUAL/CONSISTENT ACROSS THE BOARD"
 *
 * The drawing inside each square is a different size, so without a correction
 * Mail (a wide flat envelope, 210 x 122 inside a 256 square) reads much
 * smaller than Gallery (216 x 200). His own correction existed but only ever
 * reached the home grid - the dock, window title bars, the launcher, the menus
 * and Add to Dock all drew the raw picture. That is what "not consistent
 * across the board" was.
 *
 * NOW: mark any app picture with the app's name and it is corrected, wherever
 * it is, forever. The numbers live once, in tokens.css. Nothing is applied by
 * script any more.
 *
 * THE PURPLE TILE MOVED OFF THE PICTURE. It used to be painted on the picture
 * itself, so making the drawing bigger would have made the purple square
 * bigger with it and Mail's tile would have stuck out past its neighbours.
 * The tile is now its own holder, the same shape .z-appshell already uses for
 * the vector apps, so the drawing can be corrected without touching it.
 *
 * REVERT, THIS PART ALONE: delete this block and set Z_ICON_ONE_NUMBER = false
 * in shell.js. The pictures then draw uncorrected again, as they do today
 * everywhere except the home grid.
 * ======================================================================== */

.z-apptile {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  border-radius: var(--z-radius-icon-lg);
  background: var(--z-tile-art);
  box-shadow: var(--z-tile-art-inner);
  filter: var(--z-shadow-icon);
  overflow: visible;   /* a corrected drawing may sit a hair proud - never clip */
}
.z-apptile--lg { width: 38px; height: 38px; }
.z-apptile--md {
  width: var(--z-dock-icon); height: var(--z-dock-icon);
  border-radius: var(--z-radius-icon-md);
}
.z-apptile--sm { width: 19px; height: 19px; border-radius: var(--z-radius-icon-sm); }
.z-apptile > img.z-appicon {
  width: 100%; height: 100%;
  background: none; box-shadow: none; filter: none;
  border-radius: inherit;
}
/* Z_THE_SIZE_IS_IN_THE_PICTURES_NOW_2026-08-31: the scale that used to sit here,
   and the one on the home grid picture, are BOTH GONE. The drawings themselves
   were made the same size, so nothing in code resizes a picture any longer -
   his own instruction. data-z-app stays on the pictures because it is how any
   surface says which app it is drawing, and the purple holder stays because the
   purple is a tile and the drawing is a drawing; they were never the same
   thing. REVERT: put back
     .z-apptile > img.z-appicon { transform: scale(var(--z-art-fix,1)); }
     img.z-home-surface-img[data-z-app] { transform: scale(var(--z-art-fix,1)); }
   and the fifteen [data-z-app="..."] lines that set --z-art-fix. */

/* Vector Z apps sit in the same shell as the raster artwork, so both rows of
   the picker read identically. app-shell.png is the real artwork's purple
   field, reconstructed from the icon set. */
.z-appshell {
  display: inline-flex; align-items: center; justify-content: center;
  background: url("icons/app-shell.png") center / cover no-repeat;
  border-radius: var(--z-radius-icon-lg);
}
.z-appshell--lg { width: 38px; height: 38px; }
.z-appshell--lg > svg, .z-appshell--lg > img { display: block; width: 23px; height: 23px; }
.z-appshell--md {
  width: var(--z-dock-icon); height: var(--z-dock-icon);
  border-radius: var(--z-radius-icon-md);
  filter: var(--z-shadow-icon);
}
.z-appshell--md > svg, .z-appshell--md > img { display: block; width: 20px; height: 20px; }

/* ------------------------------------------------------------------ dock */
.z-dockwrap {
  position: absolute; inset: 0 0 0 auto; z-index: var(--z-layer-dock);
  display: flex; align-items: center; justify-content: flex-end;
  padding: 72px 0;
  pointer-events: none;
}
.z-dockwrap[data-z-align="start"] { align-items: flex-start; }
.z-dock-edge { position: absolute; inset: 0 0 0 auto; width: var(--z-space-7); pointer-events: auto; }
.z-dock {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: var(--z-dock-gap);
  margin-right: var(--z-dock-inset);
  padding: 11px 30px 13px 11px;
  transition: margin-right var(--z-dur-slow) var(--z-ease), opacity var(--z-dur) ease;
  pointer-events: auto;
}
.z-dock[data-z-hidden="1"] { margin-right: var(--z-dock-hidden); opacity: 0; }
.z-dock__slab {
  position: absolute; inset: -4px 0; z-index: -1;
  border-radius: 26px 0 0 26px;
  background: var(--z-grad-dock);
  box-shadow: var(--z-shadow-dock);
  backdrop-filter: blur(20px);
}
.z-dock__orb {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: var(--z-radius-pill);
  background: radial-gradient(circle at 50% 50%, rgba(58,34,104,.6), rgba(10,6,22,.2) 70%, transparent);
  animation: z-halo var(--z-halo-cycle) cubic-bezier(.45,0,.55,1) infinite;
}
.z-dock__orb img { display: block; width: 34px; height: 34px; object-fit: contain; }
.z-dock__sep { width: 24px; height: 1px; background: rgba(196,181,253,.22); }
.z-dock__slot { position: relative; display: flex; align-items: center; justify-content: center; width: var(--z-dock-slot); height: var(--z-dock-slot); }
.z-dock__btn {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0; border: 0;
  background: transparent; cursor: pointer;
  transform-origin: right center;
  transition: transform var(--z-dur) var(--z-ease);
  touch-action: none; -webkit-tap-highlight-color: transparent;
}
.z-dock__btn:hover { transform: scale(1.08); }
.z-dock__btn:active { transform: scale(.94); }
.z-dock__btn[data-z-holding="1"] { transform: scale(.86); transition-duration: 420ms; }
/* =========================================================================
   Z_NON_OPEN_DOCK_APPS_ARE_HALF_DIM_2026-09-03   (Agent E)
   =========================================================================
   HIS SPEC, word for word, in #1 HOME & ORB, APPS, DOCK, DASHBOARD-DOCK-MENU.MD:
     "Non-open Dock apps are half-dim."
   and in the very same paragraph, the rule that decides HOW it may be done:
     "No fake count, badge, chip, device, controller, or slot attribute is allowed."

   SO IT READS REAL STATE AND NOTHING ELSE. shell.js renderDock adds .z-dock__run
   to a slot ONLY when there is a genuinely open window for that app
   (if (d.running || win)). A slot without that dot is genuinely not open. No new
   attribute is invented, no script writes anything, and nothing is faked - this is
   a stylesheet rule reading a mark his own renderer already puts there from truth.

   IT DIMS THE ICON, NOT THE SLOT, so the open dot and the minimised flag keep their
   full strength when they do appear, and it cannot fight the drag rule further down
   which dims the SLOT at 0.45.
   REVERT THIS PART ALONE: delete this block.
   ========================================================================= */
.z-dock__slot:not(:has(.z-dock__run)) .z-dock__btn { opacity: .5; }
.z-dock__slot:not(:has(.z-dock__run)) .z-dock__btn:hover { opacity: 1; }
/* Z_THE_OPEN_DOT_IS_UNDER_THE_APP_AND_NOTHING_CLIPS_IT_2026-09-01  (Assistant A)
   FOUNDER, 2026-09-01: "you still HAVE NOT FIXED THE PURPLE GLOW. I HAVE NEVER
   SEEN MY OPEN APPS SHOW IT. FIX IT PROPERLY FFS! UNDER THE FUKCING APP O N THE
   DOCK! IT SHOWS CLEARLY THAT 2 APPS ARE OPEN CUZ DRIVE IS DIMMED, BUT OPENED
   ONES DONT SHOW THE DOT GLOW."

   HE IS RIGHT, AND HE COULD NEVER HAVE SEEN IT. The dot used to sit at
   right:-6px, six pixels OUTSIDE the slot on the right. The rail was later made
   scrollable, and that block's own note says why this kills it:
       "A box that scrolls one way clips the other way too - the browser gives
        you no choice."
   Room was added on the LEFT (padding-left:14px, margin-left:-14px) for the
   hover zoom, and none on the right. So overflow-x:hidden sliced the dot off
   every single time. MEASURED LIVE 2026-09-01: the dot's own centre point
   reported the dock underneath it, not the dot -- nothing was painted there.

   AND THE DIMMING STILL WORKED, which is exactly the half he could see: the
   dim rule only asks whether the dot EXISTS in the page, and it did.

   NOW IT IS UNDER THE APP, WHERE HE ASKED FOR IT. bottom:-4px drops it into the
   9px gap between slots, which is on the scroll axis and already has 6px of
   padding, so no clipping can ever reach it again.
   REVERT THIS PART ALONE: put back right:-6px / top:50% / margin-top:-2.5px. */
/* Z_THE_OPEN_DOT_TOUCHES_THE_WALL_2026-09-01 (Assistant A)
   Founder, 2026-09-01: "the purple open button is supposed to be on the right
   side of the app icon, not underneath. Make that so the purple button for an
   open icon is touching the wall, not in between the apps."
   It sat underneath because the rail used to slice anything past the tile's
   right edge. The rail now makes room on the right, so the dot goes back where
   he wants it: beside the icon, hard against the edge of the screen.
   REVERT THIS ALONE: put back left:50%; bottom:-4px; transform:translateX(-50%). */
.z-dock__run {
  position: absolute; right: -12px; top: 50%; left: auto; bottom: auto;
  width: 5px; height: 5px; margin: 0; transform: translateY(-50%);
  border-radius: var(--z-radius-pill);
  background: var(--z-running); box-shadow: 0 0 8px 1px rgba(168,85,247,.95);
  pointer-events: none;
}
.z-dock__flag {
  position: absolute; right: -3px; bottom: -3px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: var(--z-radius-chip);
  background: rgba(9,5,18,.94); box-shadow: 0 0 0 1px rgba(196,181,253,.32);
  color: var(--z-violet-300); pointer-events: none;
}
.z-dock__badge {
  position: absolute; top: -4px; left: -5px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 var(--z-space-2);
  border-radius: var(--z-radius-pill);
  background: var(--z-badge); box-shadow: 0 0 0 1.5px rgba(9,5,18,.9);
  font-size: 10px; font-weight: 800; line-height: 1; color: #fff;
  pointer-events: none;
}
.z-tip {
  position: absolute; right: 48px; top: 50%; transform: translateY(-50%);
  padding: 5px var(--z-space-4);
  border: 1px solid rgba(196,181,253,.24); border-radius: var(--z-radius-ctl);
  background: rgba(12,8,24,.96); box-shadow: var(--z-shadow-pop);
  font-family: var(--z-font-mono); font-size: var(--z-fs-mono); font-weight: 700;
  color: var(--z-text-2); white-space: nowrap; pointer-events: none;
  animation: z-fade-in var(--z-dur-fast) ease-out both;
}
.z-dock__add {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0;
  border-radius: var(--z-radius-pill);
  background: transparent; color: rgba(196,181,253,.72); cursor: pointer;
}
.z-dock__add:hover { color: var(--z-violet-200); background: rgba(196,181,253,.12); }
/* =========================================================================
   Z_HE_IS_TOLD_WHEN_THE_CONNECTION_GOES_2026-09-03   (Agent D)
   HIS LAW: "if it cannot be reached, the screen says so and shows nothing."
   HIS TEST MATRIX, item 8 of 18: "offline/reconnect".
   HIS 532 DEVICE RECORDS: every one carries an offlinePolicy.

   ⛔ THIS IS NOT AN OFFLINE COPY OF THE SITE. ⛔
   Founder, 2026-08-26: "I NEVER ASKED YOU TO KEEP AN OFFLINE COPY OF ITSELF.
   REMOVE THE OFFLINE COPY." That order stands and nothing here stores one byte.
   This only TELLS HIM the connection is gone, so a message he types does not
   vanish without him knowing. A cold load with no internet still shows the
   browser's own page, and that is correct.

   THE POSITION IS NOT INVENTED. It is the exact place his own stylesheet
   already puts a site message - the .z-toast rule directly below this one:
   right 82px, bottom var(--z-space-9), beside the dock. Same offsets, same
   tokens, same look. `fixed` instead of `absolute` only so it does not have to
   live inside the dock's own box and so it can touch NOTHING that is already
   on the page - it takes no space in the layout and cannot clip a neighbour.

   IT SHIPS HIDDEN AND ONLY A MARKER TURNS IT ON. No script ever creates it,
   sizes it or places it - his hard rule. The script flips one attribute, the
   same way the boot shell already uses data-stage.

   REVERT THIS PART ALONE: delete this block and the matching block in
   shell.js. Nothing else refers to it.
   ========================================================================= */
.z-offline-notice {
  position: fixed; right: 82px;
  /* ⛔ LIFTED CLEAR OF HIS MESSAGE BOX, 2026-09-03. ⛔
     The first version sat at the toast's own bottom offset and the picture from
     the live run showed it sitting ON TOP OF his message box. His rule: nothing
     may overlap, and nothing sits over the page's own words. When two of his
     rules collide the stricter one wins, so it moves.
     THE LIFT IS NOT INVENTED EITHER. It is the exact expression his own dock
     already uses to stay clear of the message box, a few hundred lines below
     this: env(safe-area-inset-bottom) + var(--composer-height, 118px) + 18px
     + var(--z-kb-tray-space, 0px). Same tokens, same fallbacks. On a page with
     no message box --composer-height is unset and the fallback keeps it in the
     same corner, clear of the dock. */
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--composer-height, 118px) + 18px + var(--z-kb-tray-space, 0px) + var(--z-space-3, 10px));
  display: none;
  z-index: 70;
  max-width: min(360px, calc(100vw - 120px));
  padding: 9px 13px;
  border: 1px solid rgba(196,181,253,.26); border-radius: var(--z-radius-ctl);
  background: rgba(12,8,24,.94); box-shadow: var(--z-shadow-pop);
  font-family: var(--z-font-mono); font-size: var(--z-fs-mono); font-weight: 700;
  color: var(--z-violet-200);
  pointer-events: none;
}
html[data-z-offline] .z-offline-notice { display: block; }

.z-toast {
  position: absolute; right: 82px; bottom: var(--z-space-9);
  padding: 7px 11px;
  border: 1px solid rgba(196,181,253,.26); border-radius: var(--z-radius-ctl);
  background: rgba(12,8,24,.94); box-shadow: var(--z-shadow-pop);
  font-family: var(--z-font-mono); font-size: var(--z-fs-mono); font-weight: 700;
  color: var(--z-violet-200); white-space: nowrap;
  animation: z-fade-in var(--z-dur-fast) ease-out both;
}

/* ------------------------------------------------------------ popovers */
.z-pop {
  display: flex; flex-direction: column;
  padding: var(--z-space-3);
  border: 1px solid rgba(196,181,253,.2); border-radius: var(--z-radius);
  background: var(--z-grad-panel); box-shadow: var(--z-shadow-panel);
  backdrop-filter: blur(20px);
  animation: z-drop var(--z-dur-fast) ease-out both;
}
.z-pop--dock {
  position: absolute; right: 50px; top: 50%; transform: translateY(-50%);
  z-index: 6; min-width: 190px;
}
.z-row {
  display: flex; align-items: center; gap: var(--z-space-5);
  width: 100%; padding: 7px var(--z-space-5);
  border: 0; border-radius: var(--z-radius-ctl);
  background: transparent; color: var(--z-text-2);
  font-size: var(--z-fs-ui); font-weight: 600; text-align: left;
  white-space: nowrap; cursor: pointer;
}
.z-row:hover { background: var(--z-grad-hover); box-shadow: inset 0 0 14px rgba(45,225,255,.18); }
.z-row__ico { display: inline-flex; justify-content: center; width: 16px; color: var(--z-violet-300); }
.z-row__end { margin-left: auto; padding-left: var(--z-space-7); display: inline-flex; align-items: center; gap: var(--z-space-4); }
.z-row__tick { display: inline-flex; color: var(--z-suggest); }
.z-row__more { color: rgba(232,232,234,.5); font-size: 14px; line-height: 1; }
.z-sep { height: 1px; margin: 5px var(--z-space-4); background: var(--z-line); }

/* -------------------------------------------------------------- windows */
.z-win {
  position: absolute; z-index: var(--z-layer-window);
  /* Z_WINDOW_BUTTONS_CAN_BE_PRESSED_2026-08-31 (Assistant C).
     FOUNDER: "Make sure minimize, expand and close work".
     MEASURED LIVE, not guessed: the layer windows sit in is set to ignore
     clicks, so clicks fall through it to the page underneath. Every other
     layer in this file that does that turns clicks back on for its own
     children - the dock edge does it, the menu bar does it. The window never
     did, so its Minimize, Expand and Close were impossible to press. Pressing
     Close did nothing at all, which is exactly what a frozen window looks
     like. This is the same one-line pattern the rest of the file already
     uses.
     REVERT: delete this one line. */
  pointer-events: auto;
  display: flex; flex-direction: column;
  border: 1px solid rgba(255,255,255,.08); border-radius: var(--z-radius);
  background: var(--z-bg-window); box-shadow: var(--z-shadow-window);
  backdrop-filter: blur(16px);
  transform-origin: 100% 50%;
  animation: z-drop var(--z-dur) ease-out both;
  will-change: transform, opacity;
}
.z-win[data-z-mode="full"] { border: 0; border-radius: 0; }
.z-win[data-z-anim="out"] { animation: z-genie-out var(--z-dur-genie-out) var(--z-ease-in) both; }
.z-win[data-z-anim="in"]  { animation: z-genie-in  var(--z-dur-genie-in)  var(--z-ease-out) both; }
.z-win[data-z-min="1"] { display: none; }

.z-grip { position: absolute; z-index: 4; }
.z-grip--n  { left: var(--z-space-6); right: var(--z-space-6); top: -4px; height: 9px; cursor: ns-resize; }
.z-grip--s  { left: var(--z-space-6); right: var(--z-space-6); bottom: -4px; height: 9px; cursor: ns-resize; }
.z-grip--w  { top: var(--z-space-6); bottom: var(--z-space-6); left: -4px; width: 9px; cursor: ew-resize; }
.z-grip--e  { top: var(--z-space-6); bottom: var(--z-space-6); right: -4px; width: 9px; cursor: ew-resize; }
.z-grip--nw { left: -4px;  top: -4px;    width: var(--z-space-7); height: var(--z-space-7); cursor: nwse-resize; }
.z-grip--ne { right: -4px; top: -4px;    width: var(--z-space-7); height: var(--z-space-7); cursor: nesw-resize; }
.z-grip--sw { left: -4px;  bottom: -4px; width: var(--z-space-7); height: var(--z-space-7); cursor: nesw-resize; }
.z-grip--se { right: -4px; bottom: -4px; width: var(--z-space-7); height: var(--z-space-7); cursor: nwse-resize; }
.z-win[data-z-mode="full"] .z-grip,
.z-win[data-z-mode="fill"] .z-grip { display: none; }

.z-bar {
  position: relative; z-index: 5;
  display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr);
  align-items: center; gap: var(--z-space-3);
  min-height: var(--z-menubar-h); padding: 7px 11px;
  border-bottom: 1px solid var(--z-line-hairline);
  border-radius: 23px 23px 0 0;
  background: var(--z-grad-titlebar);
  cursor: grab;
}
.z-bar:active { cursor: grabbing; }
.z-win[data-z-mode="full"] > .z-bar { border-radius: 0; }
.z-bar__menus { grid-column: 1; justify-self: start; display: flex; align-items: center; gap: 1px; min-width: 0; }
.z-win[data-z-mode="split"] .z-bar__menus { grid-column: 2; justify-self: center; }
.z-bar__lights { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: var(--z-space-4); }

/* Z_THE_APP_MENU_ROW_SLIDES_IT_NEVER_RUNS_UNDER_THE_LIGHTS_2026-09-02 (Agent D)
   MEASURED, not guessed: at tablet width the app menu's words ran straight under
   the three window lights — his picture showed "Inse" with the yellow light on
   top of it and "Help" with the red one over it. His law is explicit on both
   halves: a row that does not fit SLIDES sideways, and nothing may sit over the
   page's own words.
   The row is a grid: words in their column, lights in the last one. The words
   column could shrink but its buttons could not, so they spilled across.
   NOW the words slide inside their own column, with no scrollbar drawn, and the
   lights hold their size and their place. Nothing overlaps at any width.
   REVERT THIS PART ALONE: delete this block. */
/* THE ACTUAL CAUSE, found by reading the grid rather than guessing twice:
   .z-bar is  grid-template-columns: minmax(0,1fr) auto minmax(0,1fr)  and in
   page-bar mode the words sit in that MIDDLE, AUTO track. An auto track grows to
   fit its content and refuses to shrink, so the words' track overran the lights'
   track — and the overflow below could never fire, because the element was never
   narrower than its own content. Making that one track shrinkable is the fix.
   REVERT THIS PART ALONE: delete the grid-template-columns line below. */
/* Z_THE_LIGHTS_TAKE_ONLY_WHAT_THEY_NEED_2026-09-03  (Agent A)
   FOUNDER, 2026-09-03: "APPMENU, DETAILS + 3 DOTS OPTIONS ARE NOT THERE ONLOAD...
   INSIDE & OUT FIX THIS." Measured live, signed in as Dare, on BOTH /chat and
   /office, and the picture agrees with the numbers:
     the bar          600-1160, 560 wide
     resolved tracks  261px / 0px / 261px
     the words        column 1, 261 wide, holding 389 on Notes and 344 on Chat
     the lights       column 3, 261 wide, holding 65
   So the LIGHTS' track was taking an equal 1fr share and wasting 196 pixels,
   while the words were cut off - the bar read "Office/Notes File Edit Format Vi"
   and stopped. Window and Help could not be reached at all, on either page. That
   is why a test could not even open the Window menu.
   Agent D's fix above is UNTOUCHED and still does its job: the words' track is
   still shrinkable and still slides, so nothing can ever run under the lights.
   The ONLY change is the third track, from an equal share to what the lights
   actually need. The lights do not move - they are justify-self:end, and 1148
   minus 65 puts them at the same 1082 they are at today. The words' track gains
   the 196 that was being thrown away.
   REVERT THIS PART ALONE: put the last track back to minmax(0,1fr). */
.z-bar[data-z-pagebar] { grid-template-columns: minmax(0,1fr) minmax(0,auto) minmax(0,auto); }
.z-bar[data-z-pagebar] .z-bar__menus {
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  flex-wrap: nowrap;
}
.z-bar[data-z-pagebar] .z-bar__menus::-webkit-scrollbar { width: 0; height: 0; display: none; }
.z-bar[data-z-pagebar] .z-bar__lights {
  flex: 0 0 auto;
  position: relative;
  z-index: 1;
  padding-left: 10px;
}

.z-menu {
  min-height: 25px; padding: 0 var(--z-space-4);
  border: 0; border-radius: 9px;
  background: transparent; color: var(--z-text-2);
  font-size: 12px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.z-menu:hover, .z-menu[data-z-open="1"] { background: rgba(196,181,253,.12); }
.z-menu[data-z-open="1"] { font-weight: 750; }
.z-menu__host { position: relative; }
.z-menu__host[hidden] { display: none; }
.z-pop--menu {
  position: absolute; top: 30px; left: 0; z-index: var(--z-layer-menu);
  min-width: 258px;
}
.z-pop--sub {
  position: absolute; top: -6px; left: 100%; z-index: var(--z-layer-submenu);
  min-width: 216px; margin-left: var(--z-space-3);
  border-radius: var(--z-radius-inner);
  animation: z-fade-in 110ms ease-out both;
}

.z-light {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--z-tl); height: var(--z-tl); padding: 0; border: 0;
  border-radius: var(--z-radius-pill);
  box-shadow: inset 0 0 0 .5px rgba(0,0,0,.28), 0 1px 2px rgba(0,0,0,.35);
  color: rgba(0,0,0,.62); cursor: pointer;
}
.z-light:hover { filter: brightness(1.18); }
.z-light:active { filter: brightness(.9); }
.z-light--close { background: var(--z-tl-close); }
.z-light--min   { background: var(--z-tl-min); }
.z-light--max   { background: var(--z-tl-max); }
.z-light svg { width: 9px; height: 9px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

.z-win__body {
  display: grid; grid-template-columns: 158px minmax(0, 1fr);
  flex: 1; min-height: 0;
  border-radius: 0 0 23px 23px; overflow: hidden;
}
.z-win[data-z-mode="full"] .z-win__body { border-radius: 0; }
.z-win__side {
  display: flex; flex-direction: column; gap: var(--z-space-1);
  padding: var(--z-space-6) var(--z-space-5);
  border-right: 1px solid var(--z-line-hairline);
  overflow-y: auto;
}
.z-win__side > * {
  padding: 7px var(--z-space-5); border-radius: var(--z-radius-ctl);
  font-size: 12px; font-weight: 600; color: rgba(226,232,240,.78);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.z-win__side > *:hover { background: var(--z-grad-hover); }
.z-win__main { display: flex; flex-direction: column; min-height: 0; padding: var(--z-space-6) var(--z-space-7); overflow-y: auto; }
.z-win__row {
  display: grid; grid-template-columns: minmax(0,1fr) auto; gap: var(--z-space-5);
  padding: 11px var(--z-space-4);
  border-bottom: 1px solid rgba(255,255,255,.05); border-radius: var(--z-radius-ctl);
}
.z-win__row:hover { background: linear-gradient(90deg, rgba(45,225,255,.1), rgba(255,65,221,.07)); }
.z-win__row b { font-size: var(--z-fs-ui); font-weight: 700; color: #f1f5f9; }
.z-win__row span { font-size: var(--z-fs-small); line-height: 1.35; color: var(--z-text-3); }
.z-win__row i { font-family: var(--z-font-mono); font-size: var(--z-fs-mono); font-style: normal; color: var(--z-text-4); white-space: nowrap; }

/* ==========================================================================
   Z_HIS_MAIN_MENU_SITS_ON_ITS_OWN_RUNG_2026-09-02   (Assistant B)
   ==========================================================================
   FOUNDER, 2026-09-02: "when I hover my mouse towards the app menu, the app
   menu goes over the z menu and the fucking top menu z index. It should never
   go over them. It's sliding underneath... for a split second it goes over on
   top the top menu and the z. Fix that and don't ever let that fucking happen
   again."

   MEASURED, NOT GUESSED. His main menu row is .top-bar in the chat runtime and
   it is declared:  .top-bar { position: relative; ... }  with NO z-index at all,
   so it sits at z-index:auto. The app menu bar is position:fixed with z-index:5
   in all three of its states - resting, peeking, and menu-open. A positioned
   element with a real z-index always paints above a positioned element with
   auto, no matter which comes first in the page. So the app menu bar was ALWAYS
   in front of his main menu; it only LOOKED right at rest because the bar is
   clipped in half there. The moment it slides down and the clip comes off, it
   crosses over the main menu in full view. That is the split second he sees.

   Agent D had already moved the bar to sit before the main menu in the page so
   ordinary paint order would put it underneath. That was the right idea and it
   could not work on its own, because a z-index of 5 beats DOM order outright.

   NO INVENTED NUMBER: tokens.css:137 already names this exact rung -
   --z-layer-header: 40 "a page's own top bar". His main menu IS that. It now
   reads its own rung, so 40 beats the bar's 5 and the bar genuinely slides
   underneath in every state.

   WHY body .top-bar AND NOT .top-bar: the chat runtime injects its own style
   block at runtime, which lands after this stylesheet, so an equal-strength
   rule here would lose. One extra step of strength wins without !important.

   SAFE BY MEASUREMENT: this only changes how the main menu compares with things
   between auto and 40, and the only such thing is the app menu bar at 5.
   Everything meant to sit above it is far higher - open menus 84, the whole
   dock and its menus above 2147481500 - and none of them move.

   REVERT THIS PART ALONE: delete this block. The bar goes back over the row.
   ========================================================================== */
body .top-bar { z-index: var(--z-layer-header, 40); }

/* Page-level menu bar for mode="full" */
.z-menubar {
  position: absolute; left: 0; right: 0; top: var(--z-space-3);
  z-index: var(--z-layer-menubar);
  display: flex; align-items: center; justify-content: center; gap: var(--z-space-4);
  pointer-events: none;
}
.z-menubar > * { pointer-events: auto; }
.z-menubar__inner {
  display: flex; align-items: center; gap: var(--z-space-4);
  padding: 5px var(--z-space-5);
  border: 1px solid var(--z-line); border-radius: var(--z-radius);
  background: rgba(12,8,24,.86); backdrop-filter: blur(20px);
}

/* ---------------------------------------------------------------- modals */
.z-scrim {
  position: absolute; inset: 0; z-index: var(--z-layer-scrim);
  background: var(--z-bg-scrim);
  animation: z-fade-in var(--z-dur-fast) ease-out both;
}
.z-modal-layer {
  position: absolute; inset: 0; z-index: var(--z-layer-modal);
  display: flex; align-items: center; justify-content: center;
  padding: var(--z-space-7);
  pointer-events: none;
}
.z-modal {
  pointer-events: auto;
  display: flex; flex-direction: column;
  border: 1px solid var(--z-line); border-radius: var(--z-radius);
  background: linear-gradient(180deg, rgba(18,12,32,.98), rgba(9,6,18,.98));
  box-shadow: var(--z-shadow-window); backdrop-filter: blur(16px);
  animation: z-drop var(--z-dur) ease-out both;
}
.z-modal--picker { width: 60%; min-width: min(320px, 100%); max-width: 940px; max-height: min(74dvh, 620px); }
.z-modal__head {
  display: flex; align-items: center; gap: var(--z-space-5);
  padding: 15px var(--z-space-7) var(--z-space-6);
  border-bottom: 1px solid var(--z-line);
}
.z-modal__title { font-size: 14px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--z-text); }
.z-modal__sub { margin-top: 3px; font-size: 11.5px; line-height: 1.35; color: var(--z-text-3); }
.z-modal__x {
  margin-left: auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border: 0; border-radius: var(--z-radius-pill);
  background: transparent; color: rgba(196,181,253,.72); cursor: pointer;
}
.z-modal__x:hover { background: rgba(196,181,253,.12); color: var(--z-violet-200); }
.z-modal__body { display: flex; flex-direction: column; gap: var(--z-space-7); min-height: 0; padding: var(--z-space-7); overflow-y: auto; }
.z-modal__foot {
  display: flex; align-items: center; gap: var(--z-space-5);
  padding: var(--z-space-6) var(--z-space-7);
  border-top: 1px solid var(--z-line); background: rgba(10,7,18,.6);
}
.z-modal__hint { font-family: var(--z-font-mono); font-size: var(--z-fs-mono); color: var(--z-text-4); }
.z-modal__actions { margin-left: auto; display: flex; align-items: center; gap: var(--z-space-4); }

.z-btn {
  min-height: 38px; padding: 0 var(--z-space-7);
  border: 1px solid rgba(196,181,253,.28); border-radius: var(--z-radius-ctl);
  background: rgba(124,58,237,.16); color: var(--z-violet-200);
  font-size: 13px; font-weight: 700; white-space: nowrap; cursor: pointer;
}
.z-btn:hover { border-color: rgba(232,215,255,.6); background: rgba(124,58,237,.28); }
.z-btn:active { transform: translateY(1px); }
.z-btn[disabled] { opacity: .45; cursor: default; }
.z-btn--primary {
  padding: 0 18px;
  border-color: var(--z-line-strong);
  background: var(--z-grad-cta); color: var(--z-text); font-weight: 750;
}
.z-btn--primary:hover { border-color: rgba(232,215,255,.7); }

/* ------------------------------------------------------------ app picker */
.z-picker__cat { display: flex; flex-direction: column; gap: var(--z-space-4); }
.z-picker__strip { display: flex; gap: var(--z-space-5); padding: 2px 2px var(--z-space-3); overflow-x: auto; }
.z-picker__tile {
  position: relative; flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 106px; padding: 11px 3px 9px;
  border: 1px solid var(--z-line); border-radius: var(--z-radius-inner);
  background: rgba(22,17,36,.5); cursor: pointer;
}
.z-picker__tile:hover { border-color: rgba(196,181,253,.6); }
.z-picker__tile[data-z-picked="1"] { border-color: var(--z-line-strong); background: rgba(124,58,237,.24); }
.z-picker__label {
  width: 100%; padding: 0 1px;
  font-size: var(--z-fs-mono); font-weight: 650; line-height: 1.22;
  text-align: center; color: var(--z-text-2); white-space: pre-line;
}
.z-picker__tag {
  padding: 2px 7px; border-radius: var(--z-radius-pill);
  background: rgba(168,85,247,.18); box-shadow: inset 0 0 0 1px rgba(196,181,253,.28);
  font-family: var(--z-font-mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: #d8b4fe; white-space: nowrap;
}
.z-picker__keep {
  position: absolute; top: var(--z-space-3); right: var(--z-space-3);
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; padding: 0;
  border: 1px solid rgba(196,181,253,.3); border-radius: 5px;
  background: transparent; color: #0d0617; cursor: pointer;
}
.z-picker__keep[data-z-on="1"] { border-color: var(--z-line-strong); background: var(--z-violet-300); }
.z-picker__keep svg { width: 11px; height: 11px; opacity: 0; }
.z-picker__keep[data-z-on="1"] svg { opacity: 1; }

/* ----------------------------------------------------------- signed out */
.z-signedout {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100dvh; padding: 56px var(--z-space-7);
  text-align: center;
}
.z-signedout__grid {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 22px);
  margin-top: clamp(18px, 3vh, 34px);
  width: min(560px, 78vw);
  opacity: .3; filter: grayscale(.55); pointer-events: none;
}
.z-signedout__grid img { display: block; width: 100%; border-radius: var(--z-radius-inner); }

/* The login affordance occupies the dock's position: right edge, centred. */
.z-login {
  position: absolute; inset: 0 0 0 auto; z-index: var(--z-layer-dock);
  display: flex; align-items: center; justify-content: flex-end;
  padding-right: var(--z-space-8);
  pointer-events: none;
}
.z-login__btn {
  pointer-events: auto;
  position: relative;
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--z-space-2);
  width: 76px; height: 76px; padding: 0;
  border: 1px solid var(--z-line); border-radius: var(--z-radius);
  background: var(--z-grad-dock); box-shadow: var(--z-shadow-dock);
  backdrop-filter: blur(20px);
  color: var(--z-violet-200); cursor: pointer;
  transition: transform var(--z-dur) var(--z-ease),
              border-color var(--z-dur) ease,
              box-shadow var(--z-dur) ease;
}
.z-login__btn::after {
  content: ""; position: absolute; inset: -1px;
  border-radius: inherit; pointer-events: none;
  box-shadow: var(--z-glow-halo-lo); opacity: 0;
  transition: opacity var(--z-dur) ease;
}
.z-login__btn:hover { transform: translateX(-4px); border-color: var(--z-line-strong); }
.z-login__btn:hover::after { opacity: 1; }
.z-login__btn:focus-visible { outline: 2px solid var(--z-cyan-400); outline-offset: 3px; }
.z-login__btn:active { transform: translateX(-1px) scale(.96); box-shadow: inset 0 2px 10px rgba(0,0,0,.5); }
.z-login__art {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: var(--z-radius-pill);
  background: radial-gradient(circle at 50% 50%, rgba(58,34,104,.75), rgba(10,6,22,.25) 70%, transparent);
}
.z-login__art svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.z-login__btn:hover .z-login__art svg { stroke: var(--z-cyan-300); }
.z-login__label {
  font-family: var(--z-font-mono); font-size: 9px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: rgba(196,181,253,.8);
}
.z-login__btn:hover .z-login__label { color: var(--z-violet-200); }

/* ========================================================================
   NATIVE STATIC DOCK — Z_DOCK_STATIC_FIRST_PAINT_2026-08-30
   The permanent host/furniture is in the served index.html. These rules are
   loaded from the head with the document and do not wait for JavaScript,
   reparenting, a timer, an observer, or a runtime-created Dock. The server
   fills data-z-signed-in from its trusted access result before the response
   is sent; the signed-out rule keeps the locked Dock from being exposed. */
body > #z-shell[data-z-static-shell="1"] {
  position: fixed;
  inset: 0;
  min-height: 0;
  overflow: visible;
  background: transparent;
  pointer-events: none;
  z-index: 2147481500;
}
body > #z-shell[data-z-static-shell="1"] > [data-z-windows] {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* ==========================================================================
 * Z_A_SIGNED_OUT_PERSON_HAS_NO_APPS_ON_THE_RAIL_2026-08-31 (Assistant C)
 *
 * FOUNDER: "Let's show it even to logged-out users. When logged out:
 *   * No apps should be on the dock"
 *
 * MY OWN MISS, CAUGHT BY LOOKING AT A PICTURE. When I made the rail show to
 * everyone I deleted the rule that used to hide the whole rail and never put
 * back a rule for the APPS. A live picture of the sign-in screen showed Mail,
 * Calendar and Drive sitting on the rail with nobody signed in.
 *
 * The orb, Dashboard, Tools and the plus stay - they are the rail itself, and
 * he asked for all four to open the log-in sheet. Only the apps go.
 *
 * WHY IT KEYS OFF "false" AND NOT "not true": the page the server sends does
 * not say who you are, so the mark is absent until the page works it out. If
 * this hid on "not true" it would blank a signed-in person's apps for the first
 * moment of every load. Keying off a definite "false" means a signed-in person
 * never flickers. A signed-out person sees the three for a moment and then they
 * go - honest once settled, and the moment is only there because the served
 * page cannot yet say who is looking at it.
 * THE REAL FIX, WHEN IT EXISTS: the server stamps who you are AND your own
 * saved apps into the page it sends. That is written up for Assistant A at the
 * top of FILE 2 as part of saving the dock. Then this rule can go.
 *
 * REVERT, THIS PART ALONE: delete this block. A signed-out visitor then sees
 * Mail, Calendar and Drive on the rail again.
 * ======================================================================== */
body > #z-shell[data-z-static-shell="1"][data-z-signed-in="false"] .z-dock__slot[data-z-dock-slot]:not([data-z-dock-slot="dashboard"]):not([data-z-dock-slot="tools"]) {
  display: none;
}

body > #z-shell[data-z-static-shell="1"] .z-dockwrap {
  position: absolute;
  inset: 0 0 0 auto;
  z-index: var(--z-layer-dock);
  margin: 0;
  padding: 72px 0;
  pointer-events: none;
  transform: none;
}
body > #z-shell[data-z-static-shell="1"] .z-dock {
  margin-right: 0;
  padding-right: 11px;
}
body > #z-shell[data-z-static-shell="1"] [data-z-dock-slots] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--z-dock-gap);
}

/* ==========================================================================
 * Z_THE_RAIL_SCROLLS_WHEN_IT_RUNS_OUT_OF_ROOM_2026-08-31 (Assistant C)
 *
 * FOUNDER, answering his own question "what happens when the rail runs out of
 * room for all your apps": "Check how many can be opened at once per screen
 * (browser and mobile). We can invent a type of scroller so they can tap scroll
 * or scroll with a mouse".
 *
 * THE ARITHMETIC, so the number is not a guess: each app on the rail takes 40
 * plus a 9 gap, so 49. The rail keeps 72 clear top and bottom, and the orb, the
 * two permanent tiles, the plus and the two dividers sit above and below the
 * apps. On a 900-tall screen that leaves room for about eleven apps; on a
 * 1400-tall screen about twenty-one; on a phone at 700 tall about seven. Past
 * that they used to run off the screen with no way to reach them.
 *
 * NOW: the apps take whatever room is left and no more, and scroll inside it -
 * a finger drag on a touch screen and the wheel with a mouse, which is exactly
 * the two he named. Nothing scrolls while everything fits. The scrollbar itself
 * is hidden because the rail is a floating slab, and a scroll never leaks out
 * to the page behind it. The orb, the two permanent tiles and the plus never
 * scroll away - only the apps move.
 *
 * REVERT, THIS PART ALONE: delete this block. The rail then runs off the screen
 * again once there are too many apps.
 * ======================================================================== */
body > #z-shell[data-z-static-shell="1"] .z-dock {
  max-height: 100%;
  min-height: 0;
}
body > #z-shell[data-z-static-shell="1"] [data-z-dock-slots] {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* THE HOVER ZOOM WOULD HAVE BEEN CUT IN HALF. A box that scrolls one way
     clips the other way too - the browser gives you no choice. A tile grows
     34 per cent on hover from its right edge, so it reaches about 14 to the
     LEFT, and that is exactly what would have been sliced off. The room is
     added inside and taken back outside, so nothing moves and the zoom is
     whole. Same trick top and bottom for the first and last tile. */
  /* Z_THE_OPEN_DOT_TOUCHES_THE_WALL_2026-09-01 (Assistant A)
     Founder, 2026-09-01: "the purple open button is supposed to be on the right
     side of the app icon, not underneath. Make that so the purple button for an
     open icon is touching the wall, not in between the apps."
     The dot has to sit past the tile's right edge, and a box that scrolls one
     way clips the other way. So the SAME trick already used on the left is used
     on the right: room added inside, taken back outside. Nothing moves, and the
     dot is no longer sliced off.
     REVERT THIS ALONE: delete the two right-hand values below. */
  padding: 6px 14px 6px 14px;
  margin-left: -14px;
  margin-right: -14px;
  scroll-padding: 6px 0;
}
body > #z-shell[data-z-static-shell="1"] [data-z-dock-slots]::-webkit-scrollbar {
  width: 0;
  height: 0;
}
/* Z_DOCK_SHOWS_SIGNED_OUT_2026-08-30 (Assistant C, founder-ordered).
   FOUNDER: "Let's show it even to logged-out users. When logged out:
     * No apps should be on the dock
     * When dashbaord, tools or the plus sign is clicked, a popup ... with the
       title 'Log In to your Account' ... should appear"
   The whole rail used to be hidden until the server said signed in. Now the rail
   is there for everyone from the first painted frame - it is permanent furniture,
   like the top menu - and only the APPS are absent while nobody is signed in.
   Absent, not hidden-but-present: a signed-out person genuinely has no apps, so
   the truthful rail carries the orb, Dashboard, Tools and the plus, and nothing
   else. All of this is static CSS in the served head; no script reveals anything.
   REVERT: restore the two rules this block replaced -
     :not([data-z-signed-in="true"]) .z-dockwrap { visibility:hidden; pointer-events:none; }
     [data-z-signed-in="true"] .z-dockwrap { visibility:visible; }
*/
body > #z-shell[data-z-static-shell="1"] .z-dockwrap {
  visibility: visible;
}
/* WHY THIS KEYS ON data-z-server-auth AND NOT data-z-signed-in:
   measured live 2026-08-30 in a fresh private window with nobody signed in -
   the server correctly sends data-z-signed-in="false", it still reads false at
   688ms, and then something on the page flips it to "true" at about 26 seconds.
   So a signed-out visitor ends up marked signed in and sees the apps, which is
   exactly what the Founder reported: "IM LOGGED OUT AND I SEE THE APPS".
   data-z-server-auth carries the same server answer and is written by nobody
   else, so the rail's app slots follow the truth instead of a value that gets
   overwritten. The flip itself is a separate fault, recorded in FILE 1.
   REVERT: change these two selectors back to data-z-signed-in. */
body > #z-shell[data-z-static-shell="1"]:not([data-z-server-auth="true"]) [data-z-dock-slots] {
  display: none;
}
body > #z-shell[data-z-static-shell="1"]:not([data-z-server-auth="true"]) [data-z-dock-sep] {
  display: none;
}

/* Existing Claude Design Dock appearance, now available before paint. */
body > #z-shell[data-z-static-shell="1"] .zd-hubs {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 2px 0 0;
}
body > #z-shell[data-z-static-shell="1"] .zd-orb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: radial-gradient(circle at 50% 50%, rgba(58,34,104,.6), rgba(10,6,22,.2) 70%, transparent);
  cursor: pointer;
  overflow: hidden;
  animation: z-halo var(--z-halo-cycle, 3.2s) cubic-bezier(.45,0,.55,1) infinite;
}
body > #z-shell[data-z-static-shell="1"] .zd-orb > z-orb-mark,
body > #z-shell[data-z-static-shell="1"] .zd-orb .zd-orb-clone,
body > #z-shell[data-z-static-shell="1"] .zd-orb .zd-orb-img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 999px;
}
/* ONE ZOOM ON THE WHOLE DOCK. The hubs and the app slots both grow to 1.34 on
   hover; the orb was the only item that did not move at all, so the rail zoomed
   inconsistently. Founder: "MAKE SURE THE ZOOM FACTOR ON THE DOCK IS THE SAME TOO."
   REVERT: delete the transform and transition lines. */
body > #z-shell[data-z-static-shell="1"] .zd-orb {
  transition: filter 140ms ease, transform 140ms ease;
  transform-origin: right center;
}
body > #z-shell[data-z-static-shell="1"] .zd-orb:hover {
  filter: brightness(1.18) drop-shadow(0 0 10px rgba(45,225,255,.4));
  transform: scale(1.34);
}
body > #z-shell[data-z-static-shell="1"] .zd-hub {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  cursor: pointer;
  transition: background 140ms ease, box-shadow 140ms ease, transform 140ms ease;
  transform-origin: right center;
}
body > #z-shell[data-z-static-shell="1"] .zd-hub:hover {
  background: linear-gradient(90deg, rgba(45,225,255,.13), rgba(255,65,221,.09));
  box-shadow: 0 0 16px rgba(45,225,255,.18) inset;
  transform: scale(1.34);
}
body > #z-shell[data-z-static-shell="1"] .zd-sep {
  width: 26px;
  height: 1px;
  margin: 7px auto;
  background: rgba(196,181,253,.28);
}
body > #z-shell[data-z-static-shell="1"] .z-dock__slot {
  position: relative;
  transition: transform 140ms ease;
  transform-origin: right center;
}
body > #z-shell[data-z-static-shell="1"] .z-dock__btn {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  padding: 0;
  transition: transform 140ms ease, background 140ms ease, opacity 140ms ease;
}
body > #z-shell[data-z-static-shell="1"] .z-dock__btn:hover {
  transform: scale(1.34);
}
/* Z_DOCK_ICON_SIZE_IS_THE_TOKEN_2026-08-30 (founder: "I dont know at what
   point you reduced my dock orb sizes into tiny smaller icons, REVERT THAT NOW.
   PUT THEM THE SIZES THEY WERE, AND MAKE SURE THE ZOOM FACTOR ON THE DOCK IS THE
   SAME TOO"). This block hard-coded 24px and beat --z-dock-icon (33px), which is
   the design's own number and what .z-appicon--md uses everywhere else. Hard-coding
   a number here means the token stops being the single source of the icon size, so
   it reads the token instead of restating it. REVERT: put 24px back. */
body > #z-shell[data-z-static-shell="1"] .z-dock__btn img.z-appicon {
  width: var(--z-dock-icon);
  height: var(--z-dock-icon);
  object-fit: contain;
  opacity: 1;
}
body > #z-shell[data-z-static-shell="1"] .z-dock__btn .z-appshell,
body > #z-shell[data-z-static-shell="1"] .z-dock__btn .z-appshell img {
  width: var(--z-dock-icon);
  height: var(--z-dock-icon);
}
body > #z-shell[data-z-static-shell="1"] .z-dock__slot:not(:has(.z-dock__run)) .z-dock__btn {
  opacity: .5;
}
body > #z-shell[data-z-static-shell="1"] .z-dock__slot:has(.z-dock__run) .z-dock__btn,
body > #z-shell[data-z-static-shell="1"] .z-dock__slot:hover .z-dock__btn {
  opacity: 1;
}
body > #z-shell[data-z-static-shell="1"] .zd-hub-img {
  width: 24px;
  height: 24px;
  display: block;
}

/* ==========================================================================
 * Z_LOGIN_POPUP_WHOLE_FLOW_2026-08-30  (Assistant C, founder-ordered)
 *
 * FOUNDER: "i clicked my name and passcode didnt appear in the popup, but
 * behind on the page. make sure it apepars in the popup and the page stays
 * unchanged"
 *
 * The number pad and the code dots are borrowed into the log-in sheet. Outside
 * the sign-in panel, the site's own "hide the pad until somebody is picked"
 * rule can no longer reach them, so the sheet does that job itself - driven by
 * the sign-in's REAL state, mirrored onto the sheet as data-z-login-step.
 *
 * REVERT: delete this block. The sheet then shows the pad at both steps, which
 * is the state it was in before this change.
 * ======================================================================== */
[data-z-login-picker]:not([data-z-login-step="code"]) .access-lock-display,
[data-z-login-picker]:not([data-z-login-step="code"]) .access-lock-keypad,
[data-z-login-picker]:not([data-z-login-step="code"]) .z-home-back {
  display: none !important;
}
/* at the code step the pad is the whole point - give it room and centre it */
[data-z-login-picker][data-z-login-step="code"] .access-lock-display,
[data-z-login-picker][data-z-login-step="code"] .access-lock-keypad {
  margin-inline: auto;
}
[data-z-login-picker][data-z-login-step="code"] .z-home-back {
  display: block !important;
  margin: var(--z-space-4) auto 0;
  text-align: center;
}
/* the borrowed pieces stack down the sheet, never side by side */
[data-z-login-picker] .z-modal__body {
  display: flex;
  flex-direction: column;
  gap: var(--z-space-4);
  align-items: stretch;
}

/* ==========================================================================
 * Z_FILL_STOPS_AT_THE_DOCK_2026-08-31 (Assistant C, founder-direct).
 *
 * FOUNDER: "Currently on desktop: fills the screen and goes over the dock. Make
 * it so the right side of the screen STOPs where the dock is, and doeesnt
 * extend past it."
 * AND: "On mobile, never hide the dock. Apps stretch to full width, but dock
 * stays ontop"
 *
 * MEASURED LIVE 2026-08-31 before writing this: a filled window was 1440 wide on
 * a 1440 screen, and the rail sits from 1376 to 1440. So it really did run
 * underneath, by exactly the rail's width.
 *
 * ONE NUMBER, NOT TWO. The rail is given that same width here, so the space a
 * filled window leaves and the space the rail actually takes are the SAME token
 * and can never drift apart. 64px is what the rail already measured, so the rail
 * does not move because of this.
 *
 * MOBILE KEEPS THE FULL WIDTH, exactly as he asked. The reserve is only turned
 * on where there is a real mouse - which is his own test, in his words: "If no
 * mouse is detected, never hide the dock." A tablet without a mouse therefore
 * behaves like a phone here, with no extra guessing.
 *
 * WINDOWS CAN STILL BE DRAGGED OVER THE DOCK. This only changes what FILL does.
 * His words: "ALLOW users to extend it themselves over the dock if they want".
 *
 * REVERT: delete this block. Fill goes back to the whole screen width.
 * ======================================================================== */
/* ==========================================================================
   Z_MOBILE_GETS_THE_PUSH_OVER_TOO_2026-09-03   (Agent E)
   CHECK THE APPMENU, AND DETAILS+OPTIONS 3 DOTS ARE STILL WORKING PERFECTLY

   FOUNDER, 2026-09-03, word for word:
     "earlier i explained how the dock can push chat elements, buttons, etc over
      BUT not background... All that was explained for desktop. Mobile gets it too."

   MEASURED BEFORE THIS WAS WRITTEN, on his own phone size, signed in as him, three
   of his pages, picture opened:
     --z-fill-reserve read 0px while --z-dock-reserve read 64px, so his message bar
     ran from 0 to 390 while the dock sat at 326 to 390. HIS MESSAGE BAR WAS 64px
     UNDER THE DOCK. Gate: agentE-phone-dock-covers-his-stuff-gate.js, 6 of 7, RED.

   WHY IT HAPPENED: the line below only handed the reserve a real value inside
   "(hover: hover) and (pointer: fine)" - a real mouse. A phone has touch and no
   hover, so it stayed 0 and every rule that reads it collapsed: the app-shell
   padding, a filled window's width, and the message bar rule at the foot of this
   file. Nothing was pushed, so the dock drew on top of his content.

   THE CHANGE, and it invents NOTHING: the reserve is now HIS OWN --z-dock-reserve
   everywhere, instead of only where there is a mouse. No new number, no new
   breakpoint, no width chosen by me. The mouse-only line is left in place directly
   below, unchanged, because it now agrees with the base rather than overriding it -
   deleting someone else's line to make mine tidier is not a fix.

   HIS HARD RULE, KEPT AND PROVEN: THE BACKGROUND NEVER MOVES. The background is
   position:fixed with left:0 and right:0, so padding on .app-shell cannot move it -
   and the same gate asserts it, and it PASSED on all three pages before this change
   and must keep passing after.

   REVERT THIS PART ALONE: put --z-fill-reserve back to 0px on the :root line.
   ========================================================================== */
:root { --z-dock-reserve: 64px; --z-fill-reserve: var(--z-dock-reserve); }
@media (hover: hover) and (pointer: fine) {
  :root { --z-fill-reserve: var(--z-dock-reserve); }
}
.z-dockwrap { width: var(--z-dock-reserve); }

/* ===========================================================================
   Z_NOTHING_RUNS_UNDER_THE_DOCK_2026-09-02 (Agent A)
   FOUNDER, 2026-09-02, with a screenshot of his desktop chat where the dock
   icons sat on top of his message text: "No things like that should ever
   overlap, period."
   MEASURED FIRST, live, signed in as Dare at 1440x900, before changing a thing
   (I broke his chat menu once by adding to a layout I had not measured):
     dock            1376 -> 1440
     .app-shell         0 -> 1440   pokes 64px under the dock
     .app-content-shell 320 -> 1440  same
     .main-panel        320 -> 1440  same
     .chat-body-root    320 -> 1440  same
     .conversation-shell 320 -> 1440 same
     .message-scroll    320 -> 1440  same   <- his messages
   Nine things in all. --z-dock-reserve is already 64px and --z-fill-reserve is
   already that same 64px on a desktop and 0px everywhere else, but the ONLY
   place it was ever spent is a window in fill mode (shell.js:1126). The page
   app never spent it, so every app page ran straight under the dock.
   THIS IS ONE LINE AT THE OUTERMOST CONTAINER. Everything inside stops with it,
   so no per-app rule and no new number. z-windows.js already sets
   html[data-z-pagebar-on="1"] .app-shell{box-sizing:border-box}, so padding
   shrinks the content instead of widening the box. On a phone the variable is
   0px, so nothing changes there at all.
   REVERT THIS PART ALONE: delete the one rule below.
   =========================================================================== */
html[data-z-pagebar-on="1"] .app-shell { padding-right: var(--z-fill-reserve, 0px); }

/* ===========================================================================
   Z_NOTHING_GETS_SELECTED_WHILE_YOU_MOVE_A_WINDOW_2026-08-31  (Assistant B)
   ===========================================================================
   FOUNDER, 2026-08-31: "Whenever I resize/move windows around, it always selects
   text in the app or the top appName File .. Help text. Make it not do that.
   Make sure selecting text on menus and stuff actually is disabled. Make it only
   select on the right things".

   MEASURED: not one user-select rule existed anywhere in the window chrome, so
   dragging a title bar dragged a text selection with it.

   TWO PARTS, and only two:
   1. Chrome is never selectable. A title bar, its menu names, the traffic
      lights, the resize edges, the dock, a menu and a menu row are things you
      press, not things you read. Real content - what is inside a window, a
      message, a note - is untouched and still selects normally.
   2. While a window is actually being moved or resized, nothing at all selects,
      anywhere. The moment it stops, everything goes back to normal.
   REVERT THIS PART ALONE: delete this block and the two lines in shell.js that
   set and clear data-z-moving on the root element.
   =========================================================================== */
.z-bar,
.z-bar__menus,
.z-bar__lights,
.z-bar__appname,
.z-grip,
.z-menu__host,
.z-menu,
.z-menu__row,
.z-dock,
.z-dockwrap,
[data-z-dock],
[data-z-dock-slot],
[data-z-titlebar],
.z-picker__tile,
.z-picker__label,
.z-menu-row {
  -webkit-user-select: none;
  user-select: none;
}

/* while a window is being moved or resized, nothing anywhere selects */
[data-z-moving="1"],
[data-z-moving="1"] * {
  -webkit-user-select: none !important;
  user-select: none !important;
  cursor: inherit;
}

/* ===========================================================================
   Z_ZDESIGN_IS_ONE_PRESS_AWAY_ON_EVERY_PAGE_2026-09-01  (Assistant B)
   Founder row: "Right-click anywhere on any Z page and zDesign is one press
   away." Source: A6 .../05_ZDESIGN_COMPLETE/ZDESIGN_COMPLETE_CURRENT_CANON.md
   - "On current authorized Z World aliases ... the context entry can show a
   moving Z mark with: Open zDesign; Open zDesign in New Tab; Edit here with Z."

   THESE RULES MOVED HERE FROM shared/zdesign/zdesign.css ON 2026-09-01, WHOLE,
   NOT COPIED. zdesign.css is linked on the /zdesign route only, so the menus
   would have shown UNSTYLED - meaning fully visible - on every other page the
   moment they shipped in the served bytes. shell.css is already linked on every
   page, so moving them costs the visitor NOTHING: no extra request, no extra
   parse, and the menus are hidden on the very first frame like all other shell
   furniture. Never a second copy of these rules anywhere.
   =========================================================================== */
[data-z-design-menu] {
  position: fixed;
  /* Z_ONE_LAYER_LEDGER_2026-08-31 - its rung, never a raw number.
     The one ledger lives in shared/z-shell/tokens.css. */
  z-index: var(--z-shell-zdesign, 2147482000);
  min-width: 236px;
  padding: 6px;
  border: 1px solid var(--line, rgba(255,255,255,.10));
  border-radius: var(--radius-md, 16px);
  background-color: rgb(17, 10, 28);
  box-shadow: var(--shadow, 0 18px 48px rgba(0,0,0,.55));
  font-family: var(--z-font-temp, ui-monospace, SFMono-Regular, Menlo, monospace);
  color: var(--text, #f8fafc);
}
/* HIDDEN BY A STATIC RULE, not by a script removing it from the page. */
[data-z-design-menu][data-open="0"] { display: none; }
[data-z-design-menu][data-open="1"] { display: block; }

[data-z-design-menu] .zdm-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 11px 10px;
  border-bottom: 1px solid var(--line, rgba(255,255,255,.10));
  margin-bottom: 5px;
}
/* the moving Z mark the canon asks for. An animation is the one other thing
   script-free CSS is allowed to do, and this one runs in the stylesheet. */
[data-z-design-menu] .zdm-mark {
  width: 20px; height: 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  font-weight: 760; font-size: 13px;
  color: var(--accent, #d0c3ad);
  background: var(--accent-soft, rgba(208,195,173,.13));
  animation: zdm-breathe 3.2s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes zdm-breathe {
  0%, 100% { box-shadow: 0 0 10px rgba(208,195,173,.30); }
  50%      { box-shadow: 0 0 20px rgba(208,195,173,.55); }
}
[data-z-design-menu] .zdm-title {
  font-size: 11px; letter-spacing: .4px; text-transform: uppercase;
  color: var(--muted, #9aa7b4);
}
[data-z-design-menu] .zdm-row {
  display: block; width: 100%;
  appearance: none; border: 0; background: transparent;
  color: var(--text, #f8fafc);
  font: inherit; font-size: 13px; text-align: left;
  padding: 9px 11px;
  border-radius: var(--radius-md, 16px);
  cursor: pointer;
}
[data-z-design-menu] .zdm-row:hover { background: var(--surface-soft, rgba(255,255,255,.06)); }
[data-z-design-menu] .zdm-row[aria-disabled="true"] { opacity: .45; cursor: default; }
[data-z-design-menu] .zdm-sep {
  height: 1px; margin: 5px 8px;
  background: var(--line, rgba(255,255,255,.10));
}

/* ===========================================================================
   Z_THE_DOCK_SITS_AT_HIS_THUMB_ON_A_PHONE_2026-09-01   (Assistant A)
   ===========================================================================
   Founder, 2026-09-01, with a picture of his phone home screen:
     "lets make the dock stick out a LITTTLE lessa, and maybe resize dovk to
      like 90% the current size, so its not covering the 'exocortex' text and
      is a lil less intrusive. ALSO , its centered vertically on mobile, make
      it bottom middle default start, so users have it closer to their thumb"

   MEASURED, not guessed: this whole stylesheet had exactly ONE screen-size
   rule in it, and none of it was about a phone. The dock sat in the middle
   of the right edge at full size, which is why it landed straight across his
   "an obsidian mind, fueled by a neuromorphic mesh intelligence" line.

   On a phone the rail now starts at the BOTTOM of the right edge, at 90% of
   its size, sitting closer in. Nothing about how it works changes - it still
   drags, still opens, still hides the same way.

   REVERT THIS ALONE: delete this block. Nothing else refers to it.
   =========================================================================== */
@media (max-width: 640px) {
  /* Z_HIS_MOBILE_DOCK_2026-09-01 (Agent D) — HIS WORDS: "the dock is currently
     90% size on mobile. change it to 85%, and move it upwards more so the
     STARTING position is ABOVE where the messge Z bar is".
     The lift uses the Message Z bar's OWN height (--composer-height, 118px in
     the chat runtime) instead of a guessed number.
     REVERT THIS PART ALONE: scale(0.9) in both rules and drop the composer
     height back out of the padding. */
  .z-dockwrap,
  .z-dockwrap[data-z-align="start"] {
    align-items: flex-end;
    padding: 20px 0 calc(env(safe-area-inset-bottom, 0px) + 18px) 0;
  }

  .z-dock {
    transform: scale(0.85);
    transform-origin: bottom right;
    padding: 9px 8px 11px 9px;
  }

  /* hiding still slides it out to the right, just from its new size */
  .z-dock[data-z-hidden="1"] {
    transform: scale(0.85) translateX(26px);
  }
}

/* Z_DOCK_LIFTS_ONLY_WHERE_A_MESSAGE_BAR_EXISTS_2026-09-01 (Agent D)
   HIS CORRECTION: "ON MOBILE ACTUALLY, KEEP IT WHERE IT IS. THE UPDATE I TOLD U
   ABOUT THE MESSAGE BAR, OOOONLY DO THAT WHEN THERS A MESSAGE BAR ON A PAGE. AS
   U SHOULD BE AWARE, LOTS OF APPS WILL HAVE MESSAGE BARS. THE DOCK SHOULD
   AUTO-MOVE UPWARDS WHEN ONE EXISTS."
   I had lifted it on every phone page. Wrong: the dock keeps its normal place,
   and only lifts on a page that actually HAS a message bar. This asks the page
   itself, in CSS, so nothing measures anything and nothing moves after load —
   which is his hard rule. It uses the bar's own height, --composer-height.
   Works on any size, not just a phone, because he said lots of apps will have
   message bars.
   REVERT THIS PART ALONE: delete this block. */
/* EVERY APP'S BOTTOM GUI LIFTS THE DOCK, not just a message bar. HIS WORDS:
   "the dck should also be moved upwards in mail app so its above those buttons
   too. bascially, when you build the apps for all apps, make sure the dock
   auto-moves up to center center when there's GUIs on the screen. mark this in
   all the places u need to for every app so u do NOT forget".
   THE CONTRACT FOR EVERY APP, now and future: put data-z-bottom-gui="1" on any
   bar that sits at the bottom of your app, and the dock lifts above it by
   itself. Nothing to wire, nothing to remember beyond that one attribute. The
   two known ones today are listed by name so they work with no change. */
/* ===========================================================================
   Z_THE_LIFT_IS_A_PHONE_RULE_ONLY_2026-09-02 (Agent A) — FOUNDER, 2026-09-02,
   with a screenshot of his desktop chat:
     "the doc. Do you see how somebody... one of the agents I don't know who
      moved the keyboard up on desktop? That's not supposed to be like that. No
      things like that should ever overlap, period. When you look at the MOBILE
      version of the apps, I had the rule where if there's a message bar or
      you're in the mail app and something like a button is at the bottom of the
      page, the sliding out keyboard doc icon and the full doc itself ON MOBILE,
      they get moved up the page."
   The lift below was written to apply at EVERY width. His newest words say it
   is a MOBILE rule, so it is wrapped in the phone query. On a desktop the dock
   goes back to its normal place on the right edge and stops riding up over his
   messages. His older words that read "works on any size" are SUPERSEDED by
   this message; newest wins.
   REVERT THIS PART ALONE: delete the @media line and its closing brace.
   =========================================================================== */
@media (max-width: 640px) {
body:has([data-z-bottom-gui="1"]) .z-dockwrap,
body:has([data-z-bottom-gui="1"]) .z-dockwrap[data-z-align="start"],
body:has(.mail-message-actions) .z-dockwrap,
body:has(.mail-message-actions) .z-dockwrap[data-z-align="start"],
body:has(.composer-shell) .z-dockwrap,
body:has(.composer-shell) .z-dockwrap[data-z-align="start"],
body:has(.composer) .z-dockwrap,
body:has(.composer) .z-dockwrap[data-z-align="start"] {
  /* Z_THE_DOCK_LEAVES_ROOM_FOR_WHAT_HANGS_UNDER_IT_2026-09-02 (Agent D)
     FOUNDER, 2026-09-02: "I TOLD YOU TO MOVE THE FUCKING KEYBOARD SLIDEOUT AAAND
     THE FUCKING DOCK. WHY THE FUCK WOULD I WANT THAT UGLY MASHOP OF THEM
     OVERLAPPING!?"
     The keyboard tray hangs a fixed gap UNDER the dock and always has. Lifting the
     dock by the message bar alone left the tray still sitting on the message bar,
     and forcing the tray up on its own jammed it into the dock. Both were wrong.
     The dock now also leaves room for whatever hangs below it. z-keyboard.js
     measures its own tray and publishes --z-kb-tray-space; it is 0 when there is
     no tray, so nothing changes on pages without one. The two move as one stack
     and the gap between them never changes.
     REVERT THIS PART ALONE: delete "+ var(--z-kb-tray-space, 0px)". */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--composer-height, 118px) + 18px + var(--z-kb-tray-space, 0px));
}
}


/* Z_THE_LIFT_HAS_TO_BEAT_THE_STATIC_SHELL_RULE_2026-09-02 (Agent D)
   MEASURED, NOT GUESSED, live on a phone: --z-kb-tray-space read back as "76px"
   exactly as intended, and the dock wrapper's padding-bottom was 72px — not the
   sum above. The rule that actually wins is
   `body > #z-shell[data-z-static-shell="1"] .z-dockwrap { padding: 72px 0; }`
   near the top of this file. It carries an id, so it beats the plain
   body:has(...) .z-dockwrap lift, and it is a SHORTHAND, so it resets the bottom
   too. The lift above has therefore never applied on this page at all.
   THIS IS THE SAME LIFT, written so it wins: identical value, same four things
   counted as a bottom bar, just scoped through the same shell the winning rule
   uses. No new number and no second idea.
   REVERT THIS PART ALONE: delete this whole block. */
/* Z_THE_LIFT_IS_A_PHONE_RULE_ONLY_2026-09-02b (Agent A) — this is the copy that
   actually WINS (it carries the #z-shell id). I wrapped the weaker copy above in
   the phone query first and the desktop dock was STILL lifted - measured live at
   178px of padding on a 1440 window. This one was the reason. Same founder words:
   the move-up is a MOBILE rule, so it is now phone-only too. On a desktop the
   dock keeps its normal place on the right edge.
   REVERT THIS PART ALONE: delete the @media line and its closing brace. */
@media (max-width: 640px) {
body:has([data-z-bottom-gui="1"]) > #z-shell[data-z-static-shell="1"] .z-dockwrap,
body:has(.mail-message-actions) > #z-shell[data-z-static-shell="1"] .z-dockwrap,
body:has(.composer-shell) > #z-shell[data-z-static-shell="1"] .z-dockwrap,
body:has(.composer) > #z-shell[data-z-static-shell="1"] .z-dockwrap {
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--composer-height, 118px) + 18px + var(--z-kb-tray-space, 0px));
}
}

/* Z_THE_DOCK_SLIDES_WHEN_IT_MOVES_UP_2026-09-01 (Agent D)
   HIS WORDS: "make sure the dock moving to the upwards position uses a slide
   animation. look at all animations on the website and make sure it fits the
   family speed-wise, etc".
   THE FAMILY, read from tokens.css rather than picked: --z-dur is 180ms, fast is
   120ms, slow is 240ms, and the shared easing is --z-ease. The dock's own buttons
   already use var(--z-dur) var(--z-ease). So the slide uses the SAME pair — it
   belongs to the family instead of being a new speed.
   It is a slide because only the padding changes, which moves the dock up and
   down its own edge. Anyone who has asked for less motion gets none.
   REVERT THIS PART ALONE: delete this block. */
.z-dockwrap,
.z-dockwrap[data-z-align="start"] {
  transition: padding-bottom var(--z-dur, 180ms) var(--z-ease, cubic-bezier(.45,0,.55,1));
}
@media (prefers-reduced-motion: reduce) {
  .z-dockwrap,
  .z-dockwrap[data-z-align="start"] { transition: none; }
}

/* Z_MOBILE_APP_MENU_ALWAYS_FITS_2026-09-01 (Agent D) — HIS WORDS: "THE MOBILE
   APP MENU DOESNT SHOW WHEN THE 3 DOTS ARE CLICKED. MAKE IT SHOW THE MENYU, AND
   MAKE IT FIT IN THAT RED BOX- 90% THE WIDTH OF THE MENU, SO IT ALWAYS FITS".
   It carried min-width:258px and opened from the button's left edge, so on a
   phone it ran off the screen and the rows were unreachable. On a phone it is
   now fixed at 90% of the width, centred, sitting just under the app menu.
   REVERT THIS PART ALONE: delete this block. */
@media (max-width: 640px) {
  .z-pop--menu,
  .z-pop--sub {
    position: fixed;
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    top: calc(56px + 46px);
    min-width: 0;
    width: 90vw;
    max-width: 90vw;
    max-height: 60vh;
    overflow-y: auto;
  }
}

/* ===========================================================================
   Z_MOVING_A_WINDOW_FEELS_RIGHT_UNDER_A_FINGER_2026-09-01   (Assistant A)
   ===========================================================================
   Founder, 2026-09-01: "floating, dragging , resizing on mobile needs to be
   smoother and work better with touch".

   MEASURED, not guessed: the title bar and the eight edge grips had no rule
   telling the browser that a finger on them is a DRAG. So a finger scrolled
   the page instead, the phone took the gesture back mid-drag, and the window
   either did not move or stuck to the finger. That one missing rule is the
   whole "does not work with touch".

   The edges were also 9px wide and the corners 18px - fine for a mouse
   pointer, far too small for a fingertip. On a touch screen they are now
   nearly twice that, without changing how they look.

   REVERT THIS ALONE: delete this block.
   =========================================================================== */
.z-bar,
.z-grip {
  touch-action: none;
}

@media (pointer: coarse) {
  .z-grip--n, .z-grip--s { height: 16px; }
  .z-grip--w, .z-grip--e { width: 16px; }
  .z-grip--nw, .z-grip--ne, .z-grip--sw, .z-grip--se { width: 30px; height: 30px; }
  .z-bar { min-height: 42px; }
}

/* ==========================================================================
   Z_THE_OPTIONS_AND_3_DOTS_ARE_DRESSED_BEFORE_ANY_SCRIPT_RUNS_2026-09-03  (Agent A)
   ==========================================================================
   FOUNDER 2026-09-03: "APPMENU, DETAILS + 3 DOTS OPTIONS ARE NOT THERE ONLOAD...
   CONNECT IT FULLY TO THE DOCK & HOW IT LOADS."
   MEASURED at 250ms on a signed-in cold load: his dock is on screen at ~1.5s
   because its markup is in the SERVED BYTES. These two buttons are at ~2.1s
   because they have ZERO references there and are built by script.
   His law line 194: a control that script CREATES after load is a defect - it
   ships in the page, in place, first frame; script only fills and opens it.
   THE BLOCKER THIS REMOVES: the tray's own dress came from a <style> that
   z-design.js CREATES at runtime, so shipping the markup alone would have shown
   an undressed tray until that script ran. These four rules are copied WORD FOR
   WORD out of z-design.js zChatTabsCss so the look cannot drift, and they live
   here because this stylesheet is already linked on every page - it costs a
   visitor no extra request and it is in force on the very first frame, exactly
   like the dock's own rules above.
   z-design.js still injects its identical copy; identical rules applied twice
   change nothing on screen, and its own id guard means it never stacks up.
   REVERT THIS PART ALONE: delete this block. The script's copy still dresses
   the tray exactly as it does today.
   ========================================================================== */
.z-win .zd-chat-tabs,.z-window .zd-chat-tabs,[data-z-win] .zd-chat-tabs{position:absolute;}
.zd-chat-tabs{position:fixed;right:0;top:69px;z-index:2147482700;display:flex;flex-direction:column;gap:6px;padding:8px 7px;border-radius:14px 0 0 14px;border:0;background:linear-gradient(180deg,rgba(20,14,36,0.96),rgba(10,7,20,0.96));box-shadow:0 12px 34px rgba(0,0,0,0.42);}
.zd-chat-tab{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:0;border-radius:10px;background:transparent;color:#c4b5fd;cursor:pointer;}
.zd-chat-tab:hover,.zd-chat-tab:focus-visible{background:rgba(168,85,247,0.22);box-shadow:inset 3px 0 0 rgba(168,85,247,0.9);color:#fff;}

/* ===========================================================================
   Z_HIS_MESSAGE_BAR_REACHES_THE_END_2026-09-03   (Agent E)   ledger row 2209
   CHECK THE APPMENU, AND DETAILS+OPTIONS 3 DOTS ARE STILL WORKING PERFECTLY

   FOUNDER, 2026-09-03, with two screenshots of his own chat page:
     "SEE HOW THE BAGE CHAT GOT PUSHED OVER FOR THE DOCK AND THE OPTIONS Items?
      ... SO THE MESSAGES BAR, BACGKROUND, AND OPTIONS ON THE PPAGE -- WILL
      ALAWSSSYS EXTEND TO THE END.
      ONLY ITMES LIKE MESSAGES, AN D WHATEEVER ELSE IS ON THE PAGE GETS MOVERD
      1. move the page as it is now on chat for places that itd bloc kstuff
      2. stretch the backgorund AND the z message bar/menu items back so its
         JUST the content thats pusshed over"

   MEASURED FIRST, LIVE, SIGNED IN AS DARE, BEFORE A LINE WAS WRITTEN
   (core/tests/proofs/agentE-REACHEND-*, 2000x1600 and 1440x900, 36 of his real
   message rows on screen):
     .app-shell::before  position:fixed  left:0  right:0  width:2000px
        -> HIS BACKGROUND ALREADY REACHES THE END and never stopped short. A
           fixed pseudo element is positioned against the VIEWPORT, so the
           padding above cannot move it. That half of his order was already true.
     .composer-shell     position:absolute  left:0  right:0  inside .chat-body-root
        -> HIS MESSAGE BAR IS THE ONE THAT STOPS SHORT. chat-body-root sits inside
           .app-shell, the rule directly above pads .app-shell by the dock's width,
           so the bar inherits a box 64px narrower and ends at the padded edge
           instead of the real one. That is exactly what he photographed.
     .message-scroll     computed padding right = 10px, from
           max(10px, env(safe-area-inset-right))

   WHAT THIS RULE DOES, AND ONLY THIS:
     · pulls the message bar back out to the true screen edge, by giving it a
       negative right offset of exactly the dock's own reserve - one token, so
       the space the dock takes and the space the bar reclaims can never drift
     · adds that same reserve to the bar's own right padding, so his plus, his
       microphone and his orb keep the SAME clear space they have today and the
       dock never covers them. That is his "ONLY ITMES LIKE MESSAGES ... GETS
       MOVERD" - the bar reaches the end, the things inside it do not move under
       the dock. The existing max(10px, env(safe-area-inset-right)) is carried
       through, never replaced by a bare number, so his safe-area handling on a
       phone survives.

   WHAT IT DELIBERATELY DOES NOT DO, and why:
     · IT DOES NOT TOUCH THE .app-shell PADDING ABOVE. That padding is what keeps
       EVERY app's content clear of the dock, not just chat's - his clause 1 says
       "move the page as it is now ... for places that itd bloc kstuff", so the
       inset STAYS. I planned to move it onto the content and stopped: only chat
       has a .message-scroll, so removing it would have pushed mail, calendar,
       drive and ten more straight under the dock. That is the neighbour trap and
       it would have been a fix for one app that broke thirteen.
     · IT DOES NOT TOUCH THE BACKGROUND. Measured already full width; changing
       something that is already right is how things get broken.
     · It is scoped to the same html[data-z-pagebar-on="1"] state as the padding
       it corrects, so when there is no inset there is nothing to undo.

   --z-fill-reserve is 64px only where there is a real mouse and 0px everywhere
   else, so on a phone both values collapse to zero and nothing changes at all.

   REVERT: delete this whole block. The bar goes back to stopping short by the
   width of the dock.
   =========================================================================== */
html[data-z-pagebar-on="1"] .composer-shell {
  right: calc(-1 * var(--z-fill-reserve, 0px));
  padding-right: calc(max(10px, env(safe-area-inset-right)) + var(--z-fill-reserve, 0px));
}
