/* ---------------------------------------------------------------------------
   The app, on the web.

   These rules mirror the React Native styles in app/ and components/ so the
   two products are one interface: same top bar, same command bar, same rows,
   same spacing. Numbers here are the numbers there — app/messages/index.tsx
   and components/CommandBar.tsx are the source of truth, and this file should
   be changed alongside them.
   --------------------------------------------------------------------------- */

.app-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  /* The page colour is the body's; the shell is clear so the theme's living
     backdrop (theme.js, .backdrop below) shows through the column. */
  background: transparent;
  /* On a wide screen the same interface sits in a phone-shaped column rather
     than stretching into a different-looking product. */
  max-width: 520px;
  margin: 0 auto;
  border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
}
@media (max-width: 560px) {
  .app-shell { max-width: none; border: none; }
}
/* Installed as an app — a desktop window, or a section added to a home screen
   — the phone-shaped column is wrong: the window is already exactly as wide as
   the person wanted it, and letting 520px of it sit in the middle of an 800px
   pane wastes most of the pane. Still capped, so a full-screen monitor does not
   stretch a task list to arm's length. */
@media (display-mode: standalone), (display-mode: window-controls-overlay), (display-mode: minimal-ui) {
  .app-shell { max-width: 1100px; border: none; }
}

/* ---- floating profile circle + drop-down menu (app/index.tsx) ----
   The old top bar of icons is gone. The only chrome is one small circle at
   the top centre — the one quiet spot, since page titles sit left and their
   buttons right. Its menu drops down OVER the page, so navigation borrows no
   strip of any screen. */

/* The rail the circle sits in. It spans the top so a page can hang controls
   off either side of the circle, but only its children take clicks — the gaps
   stay the page's. The two slots are equal and flexible, so the circle keeps
   the centre whether they are full or empty. */
.float-nav {
  position: absolute;
  top: calc(9px + env(safe-area-inset-top));
  left: 0; right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  pointer-events: none;
}
.float-nav > * { pointer-events: auto; }
.float-nav-slot {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  /* More projects than fit across is normal; the row scrolls rather than
     wrapping down into the page underneath it. */
  overflow-x: auto;
  scrollbar-width: none;
}
.float-nav-slot::-webkit-scrollbar { display: none; }
/* A slot with nothing in it costs no gap either: two empty slots were ten
   pixels, and on a 320px phone that is the difference between the rail
   fitting and the gear hanging off the edge. */
/* Not in a solo window: there the left slot is what takes the rail's width
   (below), and without it Settings slid in beside the drawer button. */
.float-nav:not(.solo) .float-nav-slot:empty { display: none; }
/* Both slots are sized by what is in them and the group is centred, so an
   empty rail is just the circle in the middle as it has always been. Only the
   right slot gives ground: the left holds fixed navigation that must never
   clip, while the projects are a scrolling row that can take whatever width is
   left rather than leaving dead space opposite. */
.float-nav-slot.float-nav-left { flex: 0 0 auto; justify-content: flex-end; }
.float-nav-slot.float-nav-right { flex: 0 1 auto; justify-content: flex-start; }
/* Solo (?solo=1): the rail is the drawer, the section's own controls, and
   Settings. There is no centre to hold any more — the profile circle and the
   shortcuts either side of it are gone — so the left slot takes the width
   instead of sizing to its contents, and the section fills it. */
.float-nav.solo .float-nav-slot.float-nav-left { flex: 1 1 auto; justify-content: flex-start; gap: 8px; }
.float-nav.solo .float-spacer { flex: 0 0 0; }

.float-profile {
  flex: none;
  width: 36px; height: 36px; border-radius: 18px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-muted);
  font-size: 15px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
.float-profile img { width: 100%; height: 100%; object-fit: cover; }
.float-profile svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
/* The one control: three bars, top-left. A sidebar has a side, and the corner
   is where a hand goes looking for one. */
.float-burger {
  flex: none;
  width: 36px; height: 36px; border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
.burger-bars { display: flex; flex-direction: column; gap: 4px; width: 17px; }
.burger-bars i { display: block; height: 2px; border-radius: 1px; background: var(--ink); }

/* Inside the phone app's WebView (app/hq.tsx) the page is the whole screen:
   no rail, no composer, no install bar. */
body.embedded .cmd-wrap,
body.embedded .install-bar, body.embedded .sidebar, body.embedded .menu-backdrop { display: none !important; }
/* The rail stays for a page that hangs its own controls off it (HQ's views
   and projects); only the shell's own buttons go. */
body.embedded .float-nav > .float-burger, body.embedded .float-nav > .float-guide,
body.embedded .float-nav > .float-fav { display: none !important; }
body.embedded .app-shell { max-width: none; border: none; border-radius: 0; box-shadow: none; height: 100dvh; }
body.embedded #root:has(> .app-shell) { padding: 0; }
/* The app around the WebView already paints the backdrop; two would fight. */
body.embedded .backdrop { display: none !important; }

/* ---- the sidebar ---- */
.sidebar {
  position: absolute;
  /* Stops above the composer (and the keyboard under it): --bar-clear is
     measured after every render. Never over the box you type into. */
  top: 0; bottom: var(--bar-clear, 0px); left: 0;
  width: min(300px, 84vw);
  z-index: 25;
  background: var(--bg);
  border-right: 1px solid var(--border);
  box-shadow: 6px 0 24px rgba(0, 0, 0, 0.18);
  /* The head row lines up with the floating burger: same top as the rail. */
  padding: calc(9px + env(safe-area-inset-top)) 10px 12px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 10px;
  /* Rows run the full width — the narrow-screen top-bar rule in styles.css
     centres them otherwise. */
  align-items: stretch;
  /* Slides in and out (app.js toggleMenu); `hidden` returns after the slide. */
  transform: translateX(-100%);
  transition: transform 240ms cubic-bezier(0.2, 0.9, 0.3, 1);
  will-change: transform;
}
.sidebar.open { transform: none; }
.sidebar[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }

/* Apps, in a section of its own at the top of the drawer. */
.apps-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px; border-radius: 14px; border: none; text-align: left;
  background: var(--accent-soft); color: var(--accent);
}
.apps-row:active { opacity: 0.75; }
.apps-row .apps-ico {
  flex: none; width: 30px; height: 30px; border-radius: 10px; background: var(--accent);
  display: flex; align-items: center; justify-content: center;
}
.apps-row .apps-ico svg { width: 16px; height: 16px; stroke: var(--bg); fill: none; stroke-width: 40; }
.apps-row > svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 40; flex: none; }
.apps-row .grow { min-width: 0; }
.apps-title { font-size: 15px; font-weight: 800; }
.apps-sub { font-size: 12px; color: var(--ink-muted); white-space: normal; overflow-wrap: break-word; }
.side-row:active { opacity: 0.75; }

/* Identity first: which of your worlds this is governs everything under it. */
.ws-block { display: flex; flex-direction: column; }
/* The burger floats above the drawer, so this row leaves its width free and the
   name starts just past it — one control in the corner, not a button and a
   decorative circle fighting for the same spot. */
.ws-head {
  display: flex; align-items: center; gap: 6px;
  width: 100%; min-height: 36px; padding: 8px 8px 8px 44px; border-radius: 12px;
  background: none; border: none; text-align: left;
}
.ws-head:hover { background: var(--accent-soft); }
.ws-name {
  flex: 1; min-width: 0; font-size: 15px; font-weight: 700; color: var(--ink); white-space: normal; overflow-wrap: break-word;
}
.ws-chev { display: flex; color: var(--ink-faint); transition: transform 0.15s ease; }
.ws-chev.open { transform: rotate(180deg); }
.ws-chev svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 42; }
/* The drop-down, when there is one, sits OVER what is under it rather than
   shoving the pages down the drawer. */
.ws-block { position: relative; }
.ws-list {
  position: absolute; top: 100%; left: 6px; right: 6px; z-index: 5;
  display: flex; flex-direction: column; padding: 4px 0 4px 6px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
}
/* The drawer's head: burger's spot, the account's face, the profiles, the
   pencil, and the account at the far right. */
.drawer-head { display: flex; align-items: center; gap: 6px; padding: 0 2px 0 44px; height: 36px; flex: none; }
.head-face {
  flex: none; width: 28px; height: 28px; border-radius: 14px; overflow: hidden;
  background: var(--accent-soft); color: var(--accent); font-size: 12px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.head-face img { width: 100%; height: 100%; object-fit: cover; }
/* Only the icon drawn straight in it: a stock face (face.js) is an svg too, and draws its own fills. */
.head-face > svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.head-icon {
  flex: none; width: 26px; height: 28px; border-radius: 8px; border: none; background: none;
  display: flex; align-items: center; justify-content: center; color: var(--ink-muted);
}
.head-icon:not(:disabled):hover { background: var(--accent-soft); }
.head-icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 36; }
.seg { flex: 1; min-width: 0; display: flex; gap: 2px; padding: 2px; border: 1px solid var(--border); border-radius: 14px; }
.seg-item {
  flex: 1 1 auto; min-width: 0; padding: 5px 6px; border-radius: 11px; border: none; background: none;
  font-size: 12.5px; font-weight: 700; color: var(--ink-muted); white-space: normal; overflow-wrap: break-word;
}
.seg-item.on { background: var(--ink); color: var(--bg); }
.seg-drop {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 4px;
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 14px; background: none; text-align: left;
}
.seg-drop .ws-name { font-size: 13px; }

/* ---- the guide button and the gear on the rail ---- */
.float-spacer { flex: 1; pointer-events: none !important; }
/* The favourites and the Apps button: a touch smaller than the burger, so
   three of them and the guide's pill share the rail with the corner. */
.float-fav {
  flex: none; width: 30px; height: 30px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  transition: transform 120ms ease, background 120ms ease;
}
/* A key on its lip, like the phone's strip (13 Oct 2026), and a press that
   reaches past its edge so it is never aimed for; the rail keeps its height,
   so every page's room under it stays as it is. */
.float-fav { position: relative; box-shadow: 0 3px 0 var(--border), 0 2px 6px rgba(0, 0, 0, 0.08); }
.float-fav::after { content: ''; position: absolute; inset: -7px -2px; }
.float-fav:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--border); }
.float-fav.on { box-shadow: 0 3px 0 color-mix(in srgb, var(--place, var(--ink)) 68%, #000), 0 2px 6px rgba(0, 0, 0, 0.08); }
.float-fav.on { background: var(--place, var(--ink)); color: var(--bg); }
.float-fav svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 34; }
/* Each place in its own colour (app.js paintPlaces sets --place from the
   registry, made to read on this theme), the same as the phone's strip. Lit,
   the button fills with it and the glyph takes whichever ink reads on it. */
.float-fav[data-place] svg { stroke: var(--place, currentColor); }
.float-fav.on[data-place] svg { stroke: var(--on-place, var(--bg)); }
/* The guide's word goes before its icon does on a phone: with three
   favourites, Apps, Study and the gear, a 390px rail has no room for it. The
   button is then a round icon, like the gear, and is labelled "Guide". */
@media (max-width: 430px) { .float-guide .guide-words { display: none; } .float-guide { padding: 0; width: 32px; justify-content: center; } }
/* More than three favourites: the guide keeps its icon and gives up its word on a phone. */
@media (max-width: 560px) { .float-nav.tight .float-guide .guide-words { display: none; } .float-nav.tight .float-guide { padding: 0; width: 32px; justify-content: center; } }
.float-burger { transition: transform 120ms ease; }
.float-burger:active { transform: scale(0.92); }
.float-guide {
  flex: none; height: 32px; padding: 0 9px; border-radius: 16px; white-space: nowrap;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font-size: 13px; font-weight: 700; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  display: inline-flex; align-items: center; gap: 5px;
}
.float-guide svg { width: 18px; height: 18px; flex: none; stroke: currentColor; fill: none; stroke-width: 34; }
.float-guide.on { background: var(--ink); color: var(--bg); }
.float-nav { gap: 5px; padding-left: 8px; padding-right: 8px; }
/* Today's sun between the two sides of the rail: what stands in every
   profile to its left, the profile's own to its right (app.js railPlaces),
   the phone's strip in the web's shape. Its own yellow, like the strip's. */
.float-fav.float-today:not(.on)[data-place] svg { stroke: var(--place, #F5B53C); }
/* Narrow phones: the rail's buttons give up a little width rather than run off the edge. */
@media (max-width: 380px) { .float-nav { gap: 3px; padding-left: 6px; padding-right: 6px; } .float-fav { width: 28px; height: 28px; } }
/* A rail with more places than the window is wide scrolls sideways instead of
   running its last buttons off the edge (app.js fitRail sets the class only
   when it does not fit, so every other rail is untouched). The room above and
   below is for the buttons' shadows, which a scrolling box would cut. */
.float-nav.scrolls {
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain;
  padding-top: 6px; padding-bottom: 10px; margin-top: -6px;
}
.float-nav.scrolls::-webkit-scrollbar { display: none; }
.float-nav.scrolls .float-spacer { display: none; }
.float-nav.scrolls::after { content: ''; flex: none; width: 1px; }

/* ---- holding a place on the rail: Unpin, Add to Home Screen (app.js placeMenu) ---- */
.place-menu {
  position: fixed; z-index: var(--z-modal, 44); min-width: 190px; padding: 6px;
  border-radius: 14px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface));
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
}
.place-menu-title { font-size: 12px; font-weight: 700; color: var(--ink-faint, var(--ink-muted)); padding: 6px 10px 4px; letter-spacing: 0.3px; }
.place-menu-row {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; padding: 0 10px;
  border: 0; border-radius: 10px; background: transparent; color: var(--ink); font-size: 15px; font-weight: 600; text-align: left; cursor: pointer;
}
.place-menu-row:hover, .place-menu-row:focus-visible { background: var(--accent-soft, rgba(127, 127, 127, 0.12)); outline: none; }
.place-menu-row svg { width: 18px; height: 18px; flex: none; stroke: var(--ink-muted, currentColor); fill: none; stroke-width: 34; }

/* ---- the guide panel: over the page, above the composer ---- */
.guide-panel {
  position: absolute; left: 10px; right: 10px;
  top: calc(50px + env(safe-area-inset-top)); bottom: var(--bar-clear, 0px);
  z-index: 26; border-radius: 18px; border: 1px solid var(--border); background: var(--bg);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18); display: flex; flex-direction: column; overflow: hidden;
}
/* The settings gear floats over this panel's top-right corner, so the close
   button sits in from the edge rather than under it. */
.guide-head, .settings-head { display: flex; align-items: center; gap: 10px; padding: 12px 52px 6px 14px; }
.guide-h { flex: 1; font-size: 16px; font-weight: 800; color: var(--ink); }
.guide-scroll { flex: 1; overflow-y: auto; padding: 4px 10px 10px; display: flex; flex-direction: column; gap: 8px; }
.guide-card { border-radius: 14px; padding: 12px; background: var(--surface); border: 1px solid var(--border); }
.guide-card.lead { background: var(--accent-soft); border-color: transparent; }
.guide-card.lead .guide-title { color: var(--accent); }
.guide-title { display: flex; align-items: center; gap: 8px; font-size: 14.5px; font-weight: 700; color: var(--ink); }
.guide-title svg { width: 17px; height: 17px; stroke: var(--accent); fill: none; stroke-width: 34; }
.guide-line { font-size: 13px; line-height: 18px; color: var(--ink-muted); margin-top: 5px; }
.guide-line.faint { color: var(--ink-faint); }
.guide-title.big { font-size: 18px; color: var(--accent); }
.guide-examples { margin-top: 8px; display: flex; flex-direction: column; gap: 3px; color: var(--accent); font-size: 14px; font-weight: 600; }
.guide-label { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; color: var(--ink-faint); margin: 6px 0 0 4px; }
.guide-row { display: flex; align-items: flex-start; gap: 12px; padding: 12px; border-radius: 14px; background: var(--surface); border: 1px solid var(--border); }
.guide-row svg { width: 19px; height: 19px; stroke: var(--accent); fill: none; stroke-width: 34; flex: none; margin-top: 1px; }
.guide-row-title { font-size: 14.5px; font-weight: 700; color: var(--ink); }
.guide-row .guide-line { margin-top: 2px; }
.guide-more { display: flex; align-items: center; gap: 10px; padding: 13px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); color: var(--accent); font-size: 14px; font-weight: 700; text-align: left; margin-top: 4px; }
.guide-more svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
.guide-searchrow { display: flex; align-items: center; gap: 8px; padding: 0 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); }
.guide-searchrow svg { width: 17px; height: 17px; stroke: var(--ink-faint); fill: none; stroke-width: 34; flex: none; }
.guide-search { flex: 1; border: none; background: none; font-size: 14px; color: var(--ink); padding: 9px 0; outline: none; }
.guide-basics { border: none; background: none; color: var(--accent); font-size: 13px; font-weight: 700; }
.guide-row.side-row .guide-emoji, .side-row .guide-emoji { font-size: 17px; display: inline-flex; }
.side-row .guide-emoji svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 34; }
.settings-back { position: absolute; top: calc(9px + env(safe-area-inset-top)); left: 10px; z-index: 2; }
.settings-back svg { width: 20px; height: 20px; stroke: var(--ink); fill: none; stroke-width: 34; }
/* The back button floats top-left; the first label starts below it. */
.settings-body { padding-top: 0; }
.settings-body .set-body { padding-top: calc(50px + env(safe-area-inset-top)); }
.guide-card.lead .guide-line { color: var(--ink); font-size: 13.5px; line-height: 19px; }

/* ---- settings as a window over everything ---- */
.settings-window {
  position: fixed; inset: 0; z-index: 60; background: var(--bg);
  display: flex; flex-direction: column;
  padding-top: env(safe-area-inset-top);
  /* Its own backdrop (app.js attaches one) stays inside the rounded frame. */
  overflow: hidden;
}
.settings-body { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.settings-body .screen-scroll { flex: 1; overflow-y: auto; }
@media (min-width: 700px) {
  .settings-window {
    inset: 24px auto auto 50%; transform: translateX(-50%);
    width: min(560px, 92vw); height: calc(100vh - 48px);
    border-radius: 18px; border: 1px solid var(--border); box-shadow: 0 18px 44px rgba(0, 0, 0, 0.28);
  }
}
.ws-item, .ws-act {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 10px; background: none; border: none;
  text-align: left; font-size: 14px; color: var(--ink-muted); width: 100%;
}
.ws-item.active { color: var(--ink); font-weight: 600; }
.ws-item:hover, .ws-act:hover { background: var(--accent-soft); }
.ws-tick { display: flex; width: 14px; flex: none; color: var(--accent); }
.ws-tick svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 50; }
.ws-act { color: var(--accent); font-weight: 600; padding-left: 32px; }
.ws-sep { height: 1px; background: var(--border); margin: 5px 10px; }
.ws-note {
  margin: 2px 0 6px; padding: 10px 12px; border-radius: 12px;
  background: var(--accent-soft); color: var(--ink-muted);
  font-size: 12.5px; line-height: 17px;
}
.ws-tag {
  flex: none; font-size: 11px; font-weight: 600; padding: 3px 8px;
  border-radius: 9px; background: var(--accent-soft); color: var(--accent);
}

/* Pages, as a list rather than the old four-across grid: a sidebar is tall. */
.side-rows { display: flex; flex-direction: column; }
.side-row {
  display: flex; align-items: center; gap: 11px; position: relative;
  padding: 9px 10px; border-radius: 11px; background: none; border: none;
  text-align: left; width: 100%;
  color: var(--ink-muted); font-size: 14.5px; font-weight: 600;
}
.side-row.active { background: var(--accent-soft); color: var(--accent); }
.side-row:hover { background: var(--accent-soft); }
.side-ico { display: flex; flex: none; }
.side-ico svg { width: 19px; height: 19px; stroke: var(--place, currentColor); fill: none; stroke-width: 34; }
.side-label { flex: 1; min-width: 0; white-space: normal; overflow-wrap: break-word; }
.side-row .dot {
  position: static; min-width: 15px; height: 15px; border-radius: 8px;
  background: var(--accent); color: #fff; font-size: 10px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; padding: 0 4px;
}
.side-section { display: flex; flex-direction: column; gap: 6px; padding: 0 4px; }
.side-head { font-size: 11px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-faint); padding-left: 6px; }
.side-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.menu-backdrop {
  position: absolute; inset: 0; z-index: 20;
  background: rgba(0, 0, 0, 0.3);
  opacity: 0; transition: opacity 240ms ease;
}
.menu-backdrop.open { opacity: 1; }
.menu-backdrop[hidden] { display: none; }
.profile-menu {
  position: absolute;
  top: calc(48px + env(safe-area-inset-top));
  left: 10px; right: 10px; z-index: 25;
  border-radius: 18px;
  border: 1px solid var(--border);
  background: var(--bg);
  padding: 12px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}
.menu-grid { display: flex; flex-wrap: wrap; }
.menu-tile {
  /* Four to a row: every layout fits without the menu needing to scroll. */
  width: 25%;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 0; border-radius: 14px;
  color: var(--ink-muted); font-size: 11.5px; font-weight: 600;
  position: relative; background: none;
}
.menu-tile.active { background: var(--accent-soft); color: var(--accent); }
.menu-tile svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 34; }
.menu-tile .dot {
  position: absolute; top: 4px; right: 16%; min-width: 15px; height: 15px;
  border-radius: 8px; background: var(--accent); color: #fff;
  font-size: 10px; font-weight: 700; display: flex; align-items: center;
  justify-content: center; padding: 0 4px;
}

/* The identity control: the circle and, beside it, who you are and which
   workspace you are in. Both halves open the same menu. */
.float-id { flex: none; display: flex; align-items: center; gap: 8px; min-width: 0; }
.float-name {
  flex: none; max-width: 190px;
  display: flex; align-items: center; gap: 4px;
  height: 28px; padding: 0 10px;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  font-size: 13px; font-weight: 600;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
.float-name span { white-space: normal; overflow-wrap: break-word; }
.float-name svg { width: 13px; height: 13px; flex: none; stroke: var(--ink-faint); fill: none; stroke-width: 42; }

/* Rows under the page tiles: model, then the account. */
.menu-divider { height: 1px; background: var(--border); margin: 10px 0; }
.profile-line { display: flex; align-items: center; gap: 8px; }
.profile-label { flex: none; width: 58px; font-size: 12px; font-weight: 600; color: var(--ink-faint); }
.sidebar .profile-line { flex-wrap: wrap; padding: 0 4px; }
.sidebar .profile-label { display: none; }
.profile-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.profile-chip {
  display: flex; align-items: center; gap: 6px;
  border-radius: 16px; border: 1px solid var(--border);
  padding: 6px 12px; background: none;
  color: var(--ink-muted); font-size: 13px; font-weight: 600;
}
.profile-chip.active { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.profile-chip.disabled { opacity: 0.45; cursor: not-allowed; }
.profile-chip svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 34; }
.menu-note { font-size: 11.5px; color: var(--ink-faint); margin: 2px 0 0 6px; }
.account-name {
  flex: 1; min-width: 0; white-space: normal;
  font-size: 13px; font-weight: 600; color: var(--ink); overflow-wrap: break-word;
}

/* ---- screen: what sits between the bars ---- */
.screen { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.screen-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.screen-pad { padding: 14px 16px 8px; }

/* ---- stack header (the back-chevron screens) ---- */
.stack-head {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px 10px; flex: none;
}
.stack-title { flex: 1; text-align: center; font-size: 17px; font-weight: 700; }
.round-btn {
  width: 40px; height: 40px; border-radius: 20px; background: var(--surface);
  display: flex; align-items: center; justify-content: center; color: var(--ink);
  flex: none;
}
.pill-btns {
  display: flex; align-items: center; gap: 18px; background: var(--surface);
  border-radius: 20px; padding: 9px 14px;
}
.pill-btns svg, .round-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }

/* ---- thread rows (app/messages/index.tsx styles.row) ---- */
.thread-row {
  display: flex; align-items: center; width: 100%; text-align: left;
  padding: 11px 16px; border-bottom: 1px solid var(--border);
}
.thread-row .avatar {
  width: 48px; height: 48px; border-radius: 24px; margin-right: 12px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 700; background: var(--accent-soft); color: var(--accent);
  object-fit: cover;
}
.thread-row .body { flex: 1; min-width: 0; }
.thread-row .top { display: flex; align-items: center; gap: 6px; }
.thread-row .name { flex: 1; font-size: 16.5px; font-weight: 600; white-space: normal; overflow-wrap: break-word; }
.thread-row .time { font-size: 13px; color: var(--ink-faint); flex: none; }
.thread-row .preview {
  font-size: 14px; line-height: 19px; color: var(--ink-muted); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */
}

/* ---- command bar (components/CommandBar.tsx styles.bar) ---- */
.cmd-bar {
  flex: none;
  margin: 8px 16px calc(10px + env(safe-area-inset-bottom));
  border-radius: 24px;
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 6px 8px;
  display: flex;
  align-items: center;
  gap: 2px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
/* The themed ring round a text box (the phone's ComposerPill): the theme's
   accent sweep as a 1.5px border, lit by its glow, a little brighter while
   you type. Any composer takes it by adding the class: the assistant's bar,
   a conversation's box, a Claude chat's box. */
.composer-ring {
  border: 1.5px solid transparent;
  background:
    linear-gradient(var(--surface-solid, var(--surface)), var(--surface-solid, var(--surface))) padding-box,
    linear-gradient(135deg, var(--gradient-a), var(--gradient-b)) border-box;
  box-shadow: 0 0 10px color-mix(in srgb, var(--glow) 28%, transparent);
  transition: box-shadow 160ms ease;
}
.composer-ring:focus-within { box-shadow: 0 0 14px color-mix(in srgb, var(--glow) 45%, transparent); }
.cmd-bar input {
  flex: 1; min-width: 0; border: none; outline: none; background: none;
  font-size: 16px; padding: 8px 4px; color: var(--ink);
}
.cmd-bar input::placeholder { color: var(--ink-faint); }
/* "@AI" in a box that is a page's own field: who the words are for now (app.js aiChip). */
.cmd-ai { max-width: calc(100% - 82px); box-sizing: border-box; height: 28px; margin: 0 16px 0 66px; padding: 0 10px 0 8px; border-radius: 14px; border: 1px solid transparent; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); font: inherit; font-size: 12.5px; font-weight: 800; display: inline-flex; align-items: center; gap: 5px; vertical-align: bottom; }
.cmd-ai > span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cmd-ai[aria-disabled="true"] { cursor: default; }
.cmd-ai[hidden] { display: none; }
.cmd-ai svg { width: 13px; height: 13px; fill: currentColor; }
.cmd-ai[data-state="offer"] { background: transparent; border-color: color-mix(in srgb, var(--accent) 55%, transparent); border-style: dashed; cursor: pointer; }

.cmd-ai:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cmd-icon {
  width: 30px; height: 30px; border-radius: 15px; flex: none;
  display: flex; align-items: center; justify-content: center; color: var(--ink-muted);
}
.cmd-icon svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 34; }
.cmd-send { background: var(--border); color: var(--bg); transition: box-shadow 0.2s ease; }
/* Something to send: the theme's accent sweep, lit by its glow — the same
   button as the phone's ComposerPill when it is active. */
.cmd-send.ready {
  background: linear-gradient(135deg, var(--gradient-a), var(--gradient-b));
  color: var(--on-accent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--glow) 45%, transparent);
}
.send-col { display: flex; flex-direction: column; align-items: center; gap: 2px; flex: none; }
.send-col .cmd-send { width: 30px; height: 30px; }
/* 30 to see, 44 to hit (21 Oct 2026 review), as the Claude box's send is. */
.cmd-send { position: relative; }
.cmd-send::after { content: ''; position: absolute; inset: -7px; }

/* ---- the bar's two ends (barSides.js; the phone's components/BarSides.tsx) ----
   Since 13 Oct 2026 every box at the bottom of a screen stands in a line:
   Settings, the box, the microphone. Two coins of one size on their lips, so
   the box stays in the middle; a missing one keeps its room (.bar-side-space).
   A press sinks a coin onto its lip. Since 19 Oct 2026 the line is the
   profile, the box (its microphone inside, where send is while there is
   nothing to send) and Apps, both coins filled with their colours. */
.cmd-line { display: flex; align-items: flex-end; gap: 6px; margin: 8px 10px calc(10px + env(safe-area-inset-bottom)); }
.cmd-line > .cmd-bar { flex: 1; min-width: 0; margin: 0; }
/* The assistant's box is the coins' height (47 with their lips), as the phone's pill is. */
.cmd-wrap > .cmd-line > .cmd-bar { min-height: 47px; box-sizing: border-box; padding-top: 3px; padding-bottom: 3px; }
.cmd-bar input { text-overflow: ellipsis; }
.bar-side, .bar-side-space { flex: none; width: 44px; height: 44px; margin-bottom: 3px; box-sizing: border-box; }
.bar-side {
  position: relative; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1.5px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink-muted);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 0 var(--border);
  transition: transform 70ms ease-out, box-shadow 70ms ease-out, background 180ms ease, color 180ms ease, border-color 180ms ease;
  -webkit-tap-highlight-color: transparent;
}
/* The press reaches a little past the coin, so it is never aimed for. */
.bar-side::after { content: ''; position: absolute; inset: -8px -5px; }
.bar-side:active { transform: translateY(3px) scale(0.96); box-shadow: 0 0 0 var(--border); }
.bar-side:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.bar-side svg { position: relative; width: 23px; height: 23px; stroke: currentColor; fill: none; stroke-width: 32; }
/* The microphone in the box (barSides.js boxMic, below): rings leave it while it listens, and a press finishes. Its
   coin right of the box went on 19 Oct 2026, for Apps, on every box. */
.bar-mic-ring { position: absolute; inset: -1.5px; border-radius: 50%; background: color-mix(in srgb, var(--glow) 40%, transparent); opacity: 0; pointer-events: none; }
@keyframes bar-mic-ring { from { transform: scale(1); opacity: 0.9; } to { transform: scale(1.7); opacity: 0; } }
@keyframes bar-mic-pop { from { transform: scale(0.82); } to { transform: scale(1); } }
@media (max-width: 359px) { .cmd-line { margin-left: 8px; margin-right: 8px; } }
/* The wheel sits above the composer, right-aligned, on its own line. Stacked
   over the send button it was a 44x18 sliver — too small to read and in the
   way of the button under it. */
.cmd-wrap { flex: none; }
.wheel-row { display: flex; justify-content: space-between; align-items: center; margin: 0 10px 2px; }
/* The corner above the box, on every screen, where the app's strip has it:
   the browser, then ShmangNav, then Apps — in every profile. The gear went
   into the box on 10 Oct 2026, and out of it to a coin of its own, left of
   the box (.bar-gear), on 13 Oct. The three profiles stand at the other end
   (.wheel-right). */
.wheel-corner { display: flex; align-items: center; gap: 2px; flex: none; }
/* Each corner button is a key of its own on its lip (13 Oct 2026), the
   phone's strip keys' twin (components/StripKey.tsx): the user, "the
   stationary buttons and the space setting buttons are still hard to click
   u need to do the separation thing for those to". The press reaches past
   the key, and a thin line stands between each corner and the middle of the
   row ("add lines between the stationary buttons and the moving carousel"). */
.wheel-community, .wheel-persona {
  position: relative; width: 36px; height: 38px; box-sizing: border-box; padding: 0;
  border: 1px solid var(--border); border-bottom-width: 4px; border-radius: 12px;
  background: var(--surface-solid, var(--surface)); color: var(--ink-muted);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: transform 70ms ease-out; -webkit-tap-highlight-color: transparent;
}
.wheel-community::after, .wheel-persona::after { content: ''; position: absolute; inset: -7px -1px; }
.wheel-community:active, .wheel-persona:active { transform: translateY(2px); }
.wheel-community:focus-visible, .wheel-persona:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wheel-corner::after, .wheel-right::before { content: ''; flex: none; width: 1px; height: 26px; margin: 0 5px; border-radius: 0.5px; background: var(--ink-faint); opacity: 0.55; }
.wheel-community svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 32; }
/* Lit while its page or popup is up: its colour round its edge and through its face. */
.wheel-community.on { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--surface-solid, var(--surface))); }
/* ShmangNav: the same arrow whether it opens the map or leaves it — lit
   while the map is up, so the one button reads as a toggle. */
.wheel-nav svg { width: 20px; height: 20px; }
/* Health: the heart, the same toggle. Both toggles fill while their page is
   up, as the phone's do (navigate / heart), so "on" reads the same twice. */
.wheel-health svg { width: 20px; height: 20px; }
.wheel-nav.on svg { fill: currentColor; }
/* Apps: nine dots, drawn as its two neighbours are and filled while its popup is up. */
.wheel-apps svg { width: 19px; height: 19px; stroke-width: 30; }
.wheel-apps.on svg { fill: currentColor; }
/* The browser: the globe, the same toggle as the map's arrow, where All
   apps stood until 9 Oct 2026 — lit while the browser page is up. */
.wheel-browser svg { width: 20px; height: 20px; }

/* ---- the profiles: Personal, Learn, Work (persona.js) ----
   Three round buttons at the right end of the row above the box, where Apps,
   Study and the gear stood: a person, the school cap, a briefcase. Each
   wears its profile's colour (app.js paintPlaces sets --place), and the one
   you are in is filled with it. */
.wheel-right { display: flex; align-items: center; gap: 2px; flex: none; margin-left: auto; }
.wheel-persona svg { width: 19px; height: 19px; stroke: var(--place, var(--ink-muted)); fill: none; stroke-width: 32; }
/* The profile you are in: the whole key in its colour. */
.wheel-persona.on { background: var(--place, var(--ink)); border-color: var(--place, var(--ink)); border-bottom-color: color-mix(in srgb, var(--place, var(--ink)) 68%, #000); }
.wheel-persona.on svg { stroke: var(--on-place, var(--bg)); }
/* The splash line sits between the two ends, never after the profiles. */
.wheel-row > .cmd-splash:not(.under) { margin-right: 8px; }

/* ---- 14 Oct 2026: two round buttons a side, one profile button, its fan ----
   The fixed buttons are coins (the phone's StripKey `round`), so they never
   read as places: the browser and ShmangNav on the left, Apps and the
   profile on the right. The profile button is the profile you are in, filled
   with its colour; a click or a slide up opens the fan (persona.js). */
.wheel-community, .wheel-persona { border-radius: 18px; }
/* 14 Oct 2026, evening: three coins a side — the browser, ShmangNav and
   Messages; the pin, Apps and the profile (app.js). A little smaller, and the
   lines between the coins and the middle shorter and closer, for the room
   ("make the swipe area and the border lines … smaller bc less room"). */
/* Late that day the coins grew — the phone's COIN 38 in 41pt cells (lib/stripGeometry.ts):
   "make the stationary buttons bigger … i want the buttons to be easier to press". */
.wheel-community, .wheel-persona { width: 38px; height: 42px; border-radius: 20px; }
.wheel-corner { gap: 3px; }
.wheel-right { gap: 3px; }
.wheel-corner::after, .wheel-right::before { height: 18px; margin: 0 3px; opacity: 0.5; }
.wheel-community svg { width: 22px; height: 22px; }
.wheel-persona svg { width: 22px; height: 22px; }
/* Messages and the pinned page wear their place's colour (paintPlaces sets --place), filled while they are the page. */
.wheel-community[data-place] { color: var(--place, var(--ink-muted)); }
.wheel-community[data-place].on { color: var(--place, var(--accent)); border-color: var(--place, var(--accent)); background: color-mix(in srgb, var(--place, var(--accent)) 18%, var(--surface-solid, var(--surface))); }
.wheel-messages svg { stroke-width: 34; }
/* The "+": quiet until a page is pinned there. */
.wheel-plus { color: var(--ink-muted); }
.wheel-plus svg { width: 21px; height: 21px; stroke-width: 40; }
/* The pin's menu: the page pinned now is ticked. */
.pin-menu { max-height: min(70vh, 520px); overflow-y: auto; }
.pin-menu .place-menu-title { color: var(--ink-muted); }
.pin-menu .place-menu-row.on { font-weight: 700; }
.pin-menu .place-menu-row.on svg { stroke: var(--accent); }
/* The profile coin's fan (.persona-fan) went on 21 Oct 2026: the coin is a toggle between Personal and Work, and
   Settings, Claude and Admin are coins of the AI coin's fan (ai-page.css .ai-fan-coins). */
.wheel-persona, .bar-profile { touch-action: none; }

/* The place you are on, on the rail, says its name beside its icon (the
   phone's strip draws it under the middle key). Only the lit one. */
.float-fav .fav-name { display: none; }
.float-fav.on { width: auto; padding: 0 10px 0 8px; gap: 6px; }
.float-fav.on .fav-name { display: inline; font-size: 12.5px; font-weight: 700; white-space: nowrap; color: var(--on-place, var(--bg)); }
/* A rail with no room for the name (app.js fitRail): the lit place is an icon again, before anything scrolls. */
.float-nav.no-name .float-fav.on { width: 30px; padding: 0; }
.float-nav.no-name .float-fav.on .fav-name { display: none; }
@media (max-width: 380px) { .float-nav.no-name .float-fav.on { width: 28px; } }

/* The three notches at the top of Today: one slim pill, the profile you are in filled. */
.persona-tabs {
  display: flex; gap: 2px; padding: 2px; width: 100%; max-width: 340px; margin: 4px auto 8px;
  border: 1px solid var(--border); border-radius: 16px; background: var(--surface); box-sizing: border-box;
}
.chat-home > .persona-tabs { margin: 0 auto; }
.persona-tab {
  flex: 1 1 0; min-width: 0; height: 30px; border: 0; border-radius: 13px; background: none; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 600; color: var(--ink-muted); white-space: nowrap;
}
.persona-tab span { overflow: hidden; text-overflow: ellipsis; }
.persona-tab svg { width: 14px; height: 14px; flex: none; stroke: currentColor; fill: none; stroke-width: 36; }
.persona-tab.on { background: var(--ink); color: var(--bg); }
/* A 320-wide phone: tighter, so "Personal" is never cut to "Perso…". */
@media (max-width: 360px) { .persona-tab { gap: 4px; padding: 0 3px; font-size: 12.5px; } .persona-tab svg { width: 13px; height: 13px; } }

/* Settings › Profiles & pages: each address with the profile it is filed under. */
.persona-mail-row { padding: 8px 0; border-bottom: 1px solid var(--border); }
.persona-mail-row:last-of-type { border-bottom: 0; }
.persona-mail-addr { font-weight: 600; font-size: 14px; margin-bottom: 6px; overflow-wrap: anywhere; }
.persona-seg { max-width: 320px; }
.set-link.apps-in-settings { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; border: 0; background: none; font: inherit; color: var(--ink); cursor: pointer; padding: 6px 0; }
.set-link.apps-in-settings .fav-slot + .grow { flex: 1 1 0; min-width: 0; white-space: normal; }
.set-link.apps-in-settings > svg { flex: none; width: 16px; height: 16px; stroke: var(--ink-faint); fill: none; stroke-width: 40; }
/* A profile's row: its name and what it holds, then its theme under them. */
.persona-row { flex-wrap: wrap; }
.persona-row .fav-slot + .grow { flex: 1 1 0; min-width: 0; white-space: normal; }
.persona-row select.input { flex: 1 1 100%; margin-left: 40px; }

/* Pages still render a .inner block; give it the scrolling, padded frame the
   old .content used to provide so every screen behaves the same way. */
.screen > .inner,
.screen-scroll > .inner { padding: 14px 16px 10px; }
.screen > .inner { overflow-y: auto; -webkit-overflow-scrolling: touch; flex: 1; min-height: 0; }

/* ---- chat hero (components/pages/ChatPage.tsx styles.hero + wordmark) ---- */
.chat-hero {
  display: flex;
  align-items: center;
  justify-content: center;
  /* The app centres this in the whole screen; here the scroll container has no
     height of its own, so it borrows the space between the two bars. */
  min-height: calc(var(--app-height, 100dvh) - 150px);
}
.chat-wordmark {
  font-size: 46px;
  font-weight: 700;
  letter-spacing: 0.5px;
  /* The theme's typographic personality — serif, mono, rounded — shows here
     and nowhere in the body text. */
  font-family: var(--font-display, Georgia, serif);
  color: var(--ink);
}

/* A conversation has its own composer; the assistant bar does not belong on
   top of it. The app's thread screen deliberately has no command bar, and
   stacking two inputs is exactly the confusion that removed it there. */
/* …except while a community's settings are open in front of the person who
   runs it (body.cm-ask, pages/community.js): there the bar is the way to
   say what to change, and the settings have no composer of their own. */
body.in-thread:not(.cm-ask) .shell-bar { display: none; }

/* ---- the keyboard ----
   iOS does not shrink the page when the keyboard opens: it scrolls the whole
   view, which pushes the composer up the screen and leaves a dead band. The
   visual viewport reports the space actually visible, so the shell is sized
   to that and nothing has to move. */
.app-shell { height: var(--app-height, 100dvh); }

/* ---- where the keyboard would be ----
   There used to be a 300px band under the composer on pointer devices — the
   space a phone gives its keyboard, held open so the layout kept the same
   proportions instead of reading as a different-shaped product. It is gone.

   The reasoning was about how the app looks; the complaint was about what it
   costs. A machine with a keyboard attached never opens a software one, so on
   every laptop and desktop the band was a third of the window doing nothing
   but holding a wordmark, and the list above it scrolled for want of exactly
   that height. Window heights over 820px were the only ones that showed it,
   which is to say: every full-screen laptop, and the board down the left half
   of the Omarchy screen.

   Proportion is worth something, but not a third of the screen on the one
   device the band was never needed on. The composer now sits at the foot of
   the shell and the page keeps the rest. */

/* ---- chat: one conversation, as the app shows it ----
   The old web layout kept a chat-history sidebar beside the transcript. The
   app has no such pane — it shows the current conversation and nothing else —
   so it is hidden here and history is reached the same way, through the bar. */
.chat-layout { display: flex; flex: 1; min-height: 0; }
.chat-list { display: none; }
.chat-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* ---- panel (components/Panel.tsx) ----
   The app opens editors as a sheet inside the page, not as a native modal,
   because a modal takes the keyboard away with it. Same shape here: it is
   absolutely positioned inside the shell column, so on a wide screen it rises
   out of the phone-shaped frame rather than the whole browser window. */
.app-shell { position: relative; overflow: hidden; }
.panel-back {
  position: absolute; inset: 0; background: rgba(0, 0, 0, 0.45);
  z-index: var(--z-panel-back, 40); animation: panel-fade 0.22s ease-out;
}
.panel-card {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: var(--z-panel, 41);
  max-height: 94%;
  display: flex; flex-direction: column;
  background: var(--bg);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 20px 20px 0 0;
  overflow: hidden;
  animation: panel-rise 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes panel-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes panel-rise { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.panel-grab {
  width: 38px; height: 4px; border-radius: 2px; background: var(--border);
  margin: 8px auto 2px; flex: none;
}
.panel-body { overflow-y: auto; padding: 18px 18px 28px; }
/* A sheet header sits flush to the card edge, so it cancels the body padding. */
.sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px; margin: -18px -18px 0;
  border-bottom: 1px solid var(--border);
}
.sheet-title { font-size: 17px; font-weight: 700; min-width: 0; overflow-wrap: anywhere; }
/* Never pushed off the edge by a long title (an address with no spaces). */
.sheet-act { font-size: 16px; color: var(--ink-muted); flex: none; }
.sheet-act.danger { color: var(--danger); }

/* ---- mail (components/pages/MailPage.tsx) ---- */
.mail-head {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 11px 16px; border-bottom: 1px solid var(--border); flex: none;
}
.mail-head .addr { font-size: 15.5px; font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.mail-head .sub { font-size: 12px; margin-top: 2px; color: var(--ink-faint); }
.mail-head svg { width: 20px; height: 20px; flex: none; stroke: currentColor; fill: none; stroke-width: 34; color: var(--ink-muted); }
.mail-item {
  display: flex; align-items: flex-start; gap: 11px; width: 100%; text-align: left;
  padding: 12px 15px; border-bottom: 1px solid var(--border);
}
.mail-item .avatar {
  width: 36px; height: 36px; border-radius: 18px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 700;
}
.mail-item .body { flex: 1; min-width: 0; }
.mail-item .top { display: flex; align-items: center; gap: 8px; }
.mail-item .from { flex: 1; min-width: 0; font-size: 15px; font-weight: 500; color: var(--ink); overflow-wrap: break-word; }
.mail-item.unseen .from { font-weight: 800; }
.mail-item .when { font-size: 12px; color: var(--ink-faint); flex: none; }
.mail-item .subject { font-size: 14.5px; font-weight: 400; color: var(--ink); margin-top: 2px; overflow-wrap: break-word; }
.mail-item.unseen .subject { font-weight: 700; }
.mail-item .snippet { /* preview: a mail's first words; the click opens it whole. */ font-size: 13px; line-height: 18px; color: var(--ink-muted); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-item .tag { display: flex; align-items: center; gap: 3px; margin-top: 3px;
  font-size: 11px; font-weight: 600; color: var(--ink-faint); }
.read-subject { font-size: 20px; font-weight: 700; line-height: 27px; color: var(--ink); margin-top: 18px; }
.read-from { font-size: 14px; color: var(--ink-muted); margin-top: 8px; }
.read-meta { font-size: 12px; color: var(--ink-faint); margin-top: 3px; }
.read-body { font-size: 15px; line-height: 22px; color: var(--ink); margin-top: 20px; white-space: pre-wrap; word-break: break-word; }
.attach-chip { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--border);
  border-radius: 12px; padding: 6px 10px; font-size: 12.5px; color: var(--ink-muted); margin: 12px 6px 0 0; }
.addr-row {
  display: flex; align-items: center; gap: 8px; border: 1px solid var(--border);
  border-radius: 14px; padding: 11px 14px; margin-bottom: 8px; background: var(--surface);
}
.addr-row .text { font-size: 14.5px; font-weight: 600; color: var(--ink); white-space: normal; overflow-wrap: break-word; }
.addr-row.off .text { color: var(--ink-faint); text-decoration: line-through; }
.addr-row .label { font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
.addr-foot { font-size: 12.5px; line-height: 18px; color: var(--ink-faint); margin-top: 14px; }

/* ---- the setup state a page shows before it has anything (MailPage.setup) ---- */
.setup { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 50px 18px 18px; text-align: center; }
.setup .badge {
  width: 66px; height: 66px; border-radius: 20px; background: var(--accent-soft);
  display: flex; align-items: center; justify-content: center; color: var(--accent);
}
.setup .badge svg { width: 30px; height: 30px; stroke: currentColor; fill: none; stroke-width: 34; }
.setup h2 { font-size: 21px; font-weight: 700; color: var(--ink); margin: 0; }
.setup p { font-size: 14.5px; line-height: 21px; color: var(--ink-muted); margin: 0; }
.ink-btn {
  border-radius: 14px; padding: 13px 22px; background: var(--ink); color: var(--bg);
  font-weight: 600; font-size: 15px; margin-top: 14px;
}
.ink-btn[disabled] { opacity: 0.6; }

/* The pill toggle the app draws with Ionicons "toggle" / "toggle-outline". */
.switch {
  width: 44px; height: 26px; border-radius: 13px; flex: none;
  background: var(--border); position: relative; transition: background 0.15s;
}
.switch::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 10px; background: var(--bg);
  transition: transform 0.15s;
}
.switch.on { background: var(--success, #2E9E6B); }
.switch.on::after { transform: translateX(18px); }
.switch[disabled] { opacity: 0.5; }

/* A bare icon sitting in a row — no chrome, just the glyph and a tap target. */
.icon-btn {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 30px; height: 30px; color: var(--ink-muted);
}
.icon-btn.danger { color: var(--danger); }
.icon-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.sheet-act svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.mail-item .tag svg { width: 10px; height: 10px; stroke: currentColor; fill: currentColor; stroke-width: 0; }
.attach-chip svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
.setup > div > svg { width: 30px; height: 30px; stroke: currentColor; fill: none; stroke-width: 34; }

/* The offer to install one section as its own app. It floats over the shell
   rather than sitting in it: it belongs to the browser tab, not to the page,
   and it is gone the moment the app is installed. */
/* Solo hands the top line to the section, so the offer to install drops below
   it rather than landing on top of the strip — and it is still there, because
   that bar is how this window gets installed as its own app in the first
   place. */
.solo .install-bar { top: 56px; }
.install-bar {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 45; display: flex; align-items: center; gap: 10px;
  max-width: min(92vw, 460px); padding: 8px 8px 8px 14px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);
  font-size: 13px;
}
.install-bar > span { min-width: 0; }
.install-bar .btn { flex: none; }
.install-x {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  background: transparent; color: var(--ink-faint);
  font-size: 17px; line-height: 1;
}

/* ---- Apps grid (components/pages/AppsPage.tsx) ---- */
.apps-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 18px 8px;
}
.apps-title { font-size: 20px; font-weight: 700; letter-spacing: 0.2px; color: var(--ink); }
.apps-body { padding: 0 14px 16px; }
.section-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; color: var(--ink-faint); margin: 0 0 8px 2px; }
/* Four per row and short cards, as in the app: with the keyboard band the
   page only has the top half of the frame, and the grid should fit in it. */
.app-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.app-card {
  border-radius: 14px; border: 1px solid var(--border); background: var(--surface);
  padding: 8px; text-align: center;
  display: flex; flex-direction: column; align-items: center;
}
.app-card.soon { opacity: 0.62; }
.card-icon {
  width: 34px; height: 34px; border-radius: 10px; margin-bottom: 6px;
  display: flex; align-items: center; justify-content: center;
}
.card-icon svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.card-name { font-size: 12px; font-weight: 600; color: var(--ink); }
.card-meta { font-size: 10.5px; color: var(--ink-faint); margin-top: 1px; min-height: 13px; }
.apps-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 20px 30px 0; text-align: center; }
.apps-empty svg { width: 36px; height: 36px; stroke: currentColor; fill: none; stroke-width: 34; }
.empty-title { font-size: 17px; font-weight: 700; color: var(--ink); margin-top: 2px; }
.empty-text { font-size: 14px; line-height: 20px; color: var(--ink-muted); }
.apps-hint { font-size: 12.5px; line-height: 18px; text-align: center; color: var(--ink-faint); margin-top: 20px; }

/* ---- spending (components/pages/SpendingPage.tsx) ---- */
.sp-body { padding: 14px; padding-bottom: 16px; display: flex; flex-direction: column; gap: 10px; }
.sp-head { display: flex; align-items: center; justify-content: space-between; padding: 0 4px; }
.sp-upload {
  display: flex; align-items: center; gap: 6px; border-radius: 16px;
  padding: 8px 13px; background: var(--ink); color: var(--bg);
  font-size: 13.5px; font-weight: 600;
}
.sp-upload svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 44; }
.sp-card {
  border-radius: 16px; border: 1px solid var(--border); padding: 14px;
  background: var(--surface); width: 100%; text-align: left; display: block;
}
.sp-card.tappable { cursor: pointer; }
.sp-card.warn { border-color: #F59E3C; }
.sp-card.dashed { border-style: dashed; }
.sp-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; color: var(--ink-muted); margin-bottom: 8px; }
.sp-label.warn { color: #F59E3C; }
.sp-sub { display: flex; align-items: center; justify-content: space-between; }
.sp-action { font-size: 13px; font-weight: 600; color: var(--accent); margin-bottom: 8px; }
.sp-meta { font-size: 12px; color: var(--ink-faint); margin-bottom: 8px; }
.sp-total { font-size: 32px; font-weight: 700; letter-spacing: 0.3px; color: var(--ink); }
.sp-compare { font-size: 13px; margin-top: 4px; color: var(--ink-muted); }
.sp-compare.faint { color: var(--ink-faint); }
.sp-text { font-size: 13.5px; line-height: 19px; color: var(--ink-muted); }
.sp-text.faint { color: var(--ink-faint); }
.sp-empty-title { font-size: 16px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.cat-row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; width: 100%; text-align: left; }
.cat-name { width: 100px; flex: none; font-size: 13px; font-weight: 600; color: var(--ink); white-space: normal; overflow-wrap: break-word; }
.bar-track { flex: 1; height: 8px; border-radius: 4px; background: var(--accent-soft); overflow: hidden; }
.bar-fill { height: 8px; border-radius: 4px; }
/* As wide as its figures: "$131.80/$500.00" is wider than the 74px this used
   to be, and on a phone-width window it ran off the edge of the card. The bar
   beside it is the part that gives way. */
.cat-amount { min-width: 74px; flex: none; white-space: nowrap; font-size: 12.5px; text-align: right; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.bar-track { min-width: 40px; }
.cat-amount.over { color: var(--danger); }
.tx-row { display: flex; align-items: center; margin-bottom: 8px; width: 100%; text-align: left; }
.tx-icon {
  width: 30px; height: 30px; border-radius: 9px; margin-right: 10px; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.tx-icon svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.tx-body { flex: 1; min-width: 0; margin-right: 10px; }
.tx-merchant { font-size: 14px; font-weight: 600; color: var(--ink); white-space: normal; overflow-wrap: break-word; }
.tx-merchant.faint { color: var(--ink-faint); }
.tx-meta { font-size: 12px; margin-top: 1px; color: var(--ink-faint); white-space: normal; overflow-wrap: break-word; }
.tx-amount { font-size: 14px; font-weight: 600; color: var(--ink); flex: none; }
.tx-amount.credit { color: var(--success); }
.tx-amount.reimbursed { color: var(--ink-faint); text-decoration: line-through; }
.tx-chev { display: flex; color: var(--ink-faint); margin-left: 6px; }
.tx-chev svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 44; }
.teaser-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; color: var(--ink-muted); }
.teaser-row svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 34; }
.teaser-title { font-size: 14.5px; font-weight: 700; color: var(--ink); }

/* ---- a page's own title row (StuffPage.pageHeader, AppsPage.header) ---- */
.page-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 18px 8px; flex: none;
}
.sub-tabs { display: flex; gap: 8px; }
.sub-tab {
  border-radius: 16px; border: 1px solid var(--border); padding: 6px 14px;
  font-size: 13.5px; font-weight: 600; color: var(--ink-muted); background: none;
}
.sub-tab.active { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.add-btn {
  width: 32px; height: 32px; border-radius: 16px; flex: none;
  background: var(--accent-soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
}
.add-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 44; }

/* ---- stuff rows (StuffPage.row) ---- */
.stuff-list { display: flex; flex-direction: column; gap: 8px; padding: 0 14px 12px; }
.stuff-row {
  display: flex; align-items: center; width: 100%; text-align: left;
  border-radius: 16px; border: 1px solid var(--border); background: var(--surface);
  padding: 12px 14px;
}
.row-icon { display: flex; margin-right: 12px; color: var(--ink-muted); flex: none; }
.row-icon svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.row-body { flex: 1; min-width: 0; margin-right: 8px; }
.row-title { font-size: 16px; font-weight: 600; color: var(--ink); white-space: normal; overflow-wrap: break-word; }
.row-sub { font-size: 13px; margin-top: 3px; color: var(--ink-muted); white-space: normal; overflow-wrap: break-word; }
.row-chev { display: flex; color: var(--ink-faint); flex: none; }
.row-chev svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 44; }
.stuff-empty { padding: 60px 36px 0; text-align: center; font-size: 14px; line-height: 20px; color: var(--ink-faint); }
/* The sentence a page shows when it is offline with nothing saved (errorWords.mjs): the one thing on the page, so it is read — muted ink, not faint. */
.offline-empty-line { color: var(--ink-muted); }

/* ---- contact rows (FriendsPage.row) ---- */
/* The row is not one button here: the switch lives inside it and must not be
   swallowed by a tap that opens the editor. */
.contact-row { padding: 10px 14px; }
.contact-row .row-body { text-align: left; background: none; }
.row-initials {
  flex: none; width: 38px; height: 38px; border-radius: 19px; margin-right: 12px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 14px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.row-perm { font-size: 11.5px; margin-top: 2px; color: var(--ink-faint); }
.switch {
  flex: none; width: 44px; height: 26px; border-radius: 13px; padding: 0;
  background: var(--border); border: none; position: relative;
  transition: background 0.15s ease;
}
.switch::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 10px; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: transform 0.15s ease;
}
.switch.on { background: var(--accent); }
.switch.on::after { transform: translateX(18px); }

/* ---- sheet editors (StuffPage.modal*, field, chip) ---- */
.editor-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.sheet-act.save { color: var(--accent); font-weight: 700; }
.field {
  display: block; width: 100%; border-radius: 14px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink);
  padding: 12px 16px; font-size: 16px; margin-bottom: 10px; outline: none;
  font-family: inherit;
}
.field::placeholder { color: var(--ink-faint); }
.field:focus { border-color: var(--accent); }
.field.title { font-weight: 600; }
textarea.field { min-height: 80px; resize: vertical; }
.field-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; color: var(--ink-muted); margin: 6px 0 8px; }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.chip {
  border-radius: 16px; border: 1px solid var(--border); padding: 7px 14px;
  font-size: 13.5px; color: var(--ink-muted); background: none;
}
.chip.active { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.pair-row { display: flex; gap: 8px; }
.pair-row .field { flex: 1; min-width: 0; }
.delete-btn { display: block; width: 100%; text-align: center; margin-top: 30px; padding: 12px; color: var(--danger); font-size: 15px; }

/* ---- shared by list pages (the drive's own rules are in drive.css) ---- */
.meter { height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.meter-fill { height: 100%; border-radius: 2px; background: var(--accent); }
.busy-row { display: flex; align-items: center; gap: 8px; padding: 0 18px 8px; font-size: 13.5px; color: var(--ink-muted); flex: none; }
.row-icon.accent { color: var(--accent); }
.row-more { font-size: 17px; color: var(--ink-faint); flex: none; letter-spacing: 1px; }

/* ---- ideas (app/ideas.tsx) ---- */
.ideas-meta { font-size: 12px; color: var(--ink-faint); padding: 4px 20px 2px; flex: none; }
.ideas-editor {
  flex: 1; min-height: 0; width: 100%;
  border: none; outline: none; resize: none; background: none;
  font-family: inherit; font-size: 16.5px; line-height: 24px; color: var(--ink);
  padding: 8px 20px 12px;
}
.ideas-editor::placeholder { color: var(--ink-faint); }

/* ---- settings (components/pages/SettingsPage.tsx) ---- */
.set-body { padding: 14px; padding-bottom: 16px; }
.set-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; color: var(--ink-muted); margin: 14px 0 8px 4px; }
.set-body > .set-label:first-child { margin-top: 0; }
.set-card { border-radius: 16px; border: 1px solid var(--border); background: var(--surface); padding: 14px; }
.account-name { font-size: 15.5px; font-weight: 600; color: var(--ink); }
.account-email { font-size: 13px; margin-top: 1px; color: var(--ink-muted); }
.button-row { display: flex; gap: 8px; }
.hint { font-size: 12px; line-height: 17px; margin-top: 10px; color: var(--ink-faint); }
.feature-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; }
/* A row that is a choice (Settings → Messages → Keyboard): the whole row is
   the button, the check at the end says which one is on. */
.set-choice { width: 100%; background: none; border: 0; text-align: left; color: inherit; font: inherit; cursor: pointer; min-height: 44px; }
.set-check { width: 22px; height: 22px; color: var(--accent); flex: none; }
.set-check svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 48; stroke-linecap: round; stroke-linejoin: round; }
/* Encrypted Messages (e2eui.js): the recovery key, the pairing digits, the
   security code — big, monospaced, selectable. */
.e2e-sheet { max-width: 440px; }
.e2e-key { font: 600 17px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.04em; padding: 12px 14px; border-radius: 12px; background: var(--accent-soft); user-select: all; word-break: break-word; }
.e2e-code { font: 700 30px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.08em; text-align: center; padding: 14px 0; user-select: all; }
.e2e-digits { display: grid; gap: 6px; padding: 12px 14px; border-radius: 12px; background: var(--accent-soft); font: 600 16px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; user-select: all; }
.e2e-digits-row { white-space: pre; }
@media (max-width: 380px) { .e2e-digits { font-size: 13.5px; } .e2e-code { font-size: 26px; } }
/* A line in a conversation that the app wrote, not a person: encryption on,
   someone's keys changed, a message that would not open. */
.e2e-line { display: flex; justify-content: center; margin: 10px 12px; }
.e2e-line button, .e2e-line span { font-size: 13px; line-height: 1.35; color: var(--ink-muted); background: var(--surface); border: 0; border-radius: 10px; padding: 6px 10px; text-align: center; max-width: 520px; font-family: inherit; }
/* The sheets' buttons: side by side when they fit, else one full-width stack
   with the main one on top — never a ragged right-aligned pile. */
.e2e-actions { display: flex; flex-wrap: wrap-reverse; justify-content: flex-end; gap: 8px; margin-top: 14px; }
@media (max-width: 480px) { .e2e-actions { flex-direction: column-reverse; } .e2e-actions .btn { width: 100%; } }
/* Three choices never pile up unevenly: they stack at every width. */
.e2e-actions:has(> :nth-child(3)) { flex-direction: column-reverse; }
.e2e-actions:has(> :nth-child(3)) .btn { width: 100%; }
.e2e-field { width: 100%; font: 600 15px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.04em; text-transform: uppercase; }
.e2e-line button { cursor: pointer; }
.e2e-line.warn button, .e2e-line.warn span { color: var(--danger); }
.bubble-text.e2e-err { font-style: italic; opacity: 0.8; }
.row-text { flex: 1; font-size: 15px; font-weight: 500; color: var(--ink); }
.theme-row { display: flex; gap: 6px; }
.theme-row .tab { flex: 1; border-radius: 11px; text-align: center; padding: 9px 0; }
.pack-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.pack-grid .tab { flex: 1 1 31%; border-radius: 14px; text-align: center; padding: 12px 0; }
.set-footer { font-size: 12px; text-align: center; margin-top: 22px; color: var(--ink-faint); }

/* ---- reminder/note/to-buy rows (components/pages/ListsPage.tsx styles.row) ---- */
.list-row {
  display: flex; align-items: center;
  border-radius: 16px; border: 1px solid var(--border); background: var(--surface);
  padding: 13px 14px;
}
/* The whole title, however long, and the notes under it: a row you have to
   open to read costs a click every time you glance at the list. */
.list-row .row-title { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; line-height: 1.3; }
.list-row .row-notes {
  font-size: 13.5px; line-height: 18px; margin-top: 4px; color: var(--ink-muted); white-space: pre-wrap; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */
}
/* Reminders tied to being out get the app's coloured spine. */
.list-row.out { border-left: 3px solid var(--accent); }
.list-row .tick {
  width: 24px; height: 24px; border-radius: 12px; margin-right: 12px; flex: none;
  border: 2px solid var(--ink-muted); background: none;
}
.list-row .tick.p-important { border-color: #F59E3C; }
.list-row .tick.p-urgent { border-color: var(--danger); }
.list-row .row-body { flex: 1; min-width: 0; margin-right: 8px; text-align: left; background: none; }
.list-row .row-when { font-size: 13px; margin-top: 3px; color: var(--ink-muted); white-space: normal; overflow-wrap: break-word; }
.list-row .row-when.overdue { color: var(--danger); font-weight: 600; }

/* Four tabs plus the add button do not fit a phone width. The app does not
   wrap them, so neither does this — the row scrolls sideways and every pill
   keeps its shape. */
.sub-tabs {
  flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none;
  padding-bottom: 2px;
}
.sub-tabs::-webkit-scrollbar { display: none; }
.sub-tab { white-space: nowrap; flex: none; }
.page-head { gap: 10px; }

/* ---- messenger premium pass ---- */

/* A face at the end of each sender's run, like the app (and iMessage). */
.their-run { display: flex; align-items: flex-end; gap: 6px; }
.run-avatar {
  width: 28px; height: 28px; border-radius: 14px; flex: none;
  background: var(--accent-soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; object-fit: cover; margin-bottom: 8px;
}
.run-spacer { width: 28px; flex: none; }
.run-name { font-size: 11.5px; color: var(--ink-faint); margin: 0 0 2px 40px; }
/* Someone's assistant in the chat: the robot for a face, its name in the accent. */
.run-avatar.ai svg { width: 17px; height: 17px; }
.run-name.ai { color: var(--accent); font-weight: 700; }

/* Voice notes: play circle, live bars, time. */
.vn-row { display: flex; align-items: center; gap: 8px; padding: 3px 2px; min-width: 180px; max-width: 230px; background: none; text-align: left; }
.vn-play { display: flex; flex: none; }
.vn-play svg { width: 34px; height: 34px; fill: currentColor; stroke: none; }
.vn-bars { display: flex; align-items: center; gap: 2px; height: 26px; flex: 1; min-width: 0; overflow: hidden; }
.vn-bar { width: 2.5px; border-radius: 1.5px; transition: opacity 0.15s; }
.vn-time { font-size: 11.5px; opacity: 0.85; font-variant-numeric: tabular-nums; flex: none; }

/* The recording strip that appears over the composer. */
.rec-bar {
  display: flex; align-items: center; gap: 10px; flex: none;
  margin: 6px 16px 0; border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); padding: 7px 12px;
}
.rec-dot { width: 10px; height: 10px; border-radius: 5px; background: #E5484D; animation: rec-pulse 1.1s ease-in-out infinite; }
@keyframes rec-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.rec-clock { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.rec-hint { flex: 1; font-size: 12.5px; color: var(--ink-faint); }

/* The full emoji library for a reaction: the picker's grid (picker.js emojiPane) in a card. */
.emoji-panel {
  margin: 6px 16px 0; border: 1px solid var(--border); border-radius: 16px;
  background: var(--surface-solid); padding: 0; max-width: 340px; overflow: hidden;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
.emoji-panel.right { margin-left: auto; }

/* Emoji land big and bare — the bubble chrome would only shrink them. */
.thread-bubble.emoji-only { background: none; border: none; padding: 2px 4px; }
.thread-bubble.emoji-only.mine { background: none; color: inherit; }
.thread-bubble.emoji-only > div:last-child { color: var(--ink-faint); }


/* ===========================================================================
   Wider than a phone
   ---------------------------------------------------------------------------
   The column stays a column — this is the same product, not a second one —
   but a browser window has room a phone does not, and pretending otherwise
   left whole panes squeezed into a strip. These rules spend that room.
   =========================================================================== */

/* A desktop window gets the app as a card floating on the page rather than a
   full-bleed column with two hairlines. A short window keeps every pixel. */
@media (min-width: 720px) and (min-height: 640px) {
  /* The gap belongs to the parent: as a margin on the shell it collapses out
     through #root and the page picks up 20px of scroll it should not have.
     :has() keeps it off the sign-in screen, which fills the window itself. */
  #root:has(> .app-shell) { padding: 20px 0; }
  .app-shell {
    height: calc(var(--app-height, 100dvh) - 40px);
    border: 1px solid var(--border);
    border-radius: 22px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.10);
    /* The backdrop fills the window; the card sits on it as a pane of the
       card colour, so it reads as a card in every theme and at every depth
       — a flat backdrop included — while the shapes still show through. */
    background: color-mix(in srgb, var(--surface-solid) 42%, transparent);
  }
  [data-theme='dark'] .app-shell { box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45); }
  /* The hero measures itself against the window; the card is 40px shorter, and
     without this the Chat screen picks up that much scroll for nothing. */
  .chat-hero { min-height: calc(var(--app-height, 100dvh) - 190px); }
}

/* One at a time when there is only room for one. */
.msg-stack[data-view='list'] > .msg-convo { display: none; }
.msg-stack[data-view='convo'] > .msg-list { display: none; }

/* ---- messages: both panes at once ----
   The list and the conversation are already two sibling screens; on a phone
   one is on show at a time. Given the width they sit side by side, which is
   what makes the back arrow unnecessary and the empty pane worth filling. */
@media (min-width: 720px) {
  body[data-page='messages'] .app-shell { max-width: 1080px; }

  .msg-stack { flex-direction: row; }
  /* Beats `.msg-stack[data-view=…]` below only by matching its specificity. */
  .msg-stack[data-view] > .msg-list,
  .msg-stack[data-view] > .msg-convo { display: flex; }
  .msg-stack > .msg-list {
    flex: 0 0 min(320px, 38%);
    min-width: 0;
    border-right: 1px solid var(--border);
  }
  .msg-stack > .msg-convo { flex: 1; min-width: 0; }

  /* Both panes are on screen, so the chevron out of the conversation has
     nowhere to go, and the list header already carries the way out. */
  .msg-convo > .stack-head > .round-btn:first-child { display: none; }
  .msg-convo > .stack-head { padding-left: 18px; }
  .msg-convo > .stack-head .stack-title { text-align: left; }

  /* The conversation has its own composer; the assistant bar would be a
     second input under it. Same reason it goes in a thread on a phone. */
  body[data-page='messages']:not(.cm-ask) .shell-bar { display: none; }

  /* A line of text 700px wide stops reading as a message. */
  .msg-convo .thread-bubble { max-width: min(75%, 560px); }
}

/* Which conversation is open, now that the list stays on screen beside it. */
.thread-row.active { background: var(--accent-soft); }
.thread-row.active .name { color: var(--accent); }

/* Nothing picked yet: the pane says so instead of showing a bare header over
   a composer that cannot send. */
.msg-convo.no-thread > .stack-head,
.msg-convo.no-thread > .cmd-bar,
.msg-convo.no-thread > .composer-dock { display: none; }
.convo-empty {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  gap: 6px; padding: 24px; min-height: 100%;
}
.convo-empty-mark {
  display: flex; align-items: center; justify-content: center;
  width: 58px; height: 58px; border-radius: 29px; margin-bottom: 6px;
  background: var(--accent-soft); color: var(--accent);
}
.convo-empty-mark svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 30; }
.convo-empty .empty-text { max-width: 260px; }

/* ---- the conversation off the chat page (conversation.js) ----
   Eight shapes, one rule: nothing here ever covers the composer or the
   keyboard. Everything sits above --bar-clear, or beside it. */
.convo-root { position: absolute; inset: 0; z-index: 18; pointer-events: none; }
.convo-root > * { pointer-events: auto; }
.convo-none { display: none; }
.convo-dot { width: 8px; height: 8px; border-radius: 4px; background: var(--accent); flex: none; }
.convo-line { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; align-items: center; gap: 6px; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */ }
.spin.sm { width: 12px; height: 12px; }

/* the pill: small, draggable, sits above the composer's right end */
.convo-pill {
  position: absolute; right: 14px; bottom: calc(var(--bar-clear, 0px) + 10px);
  width: 44px; height: 44px; border-radius: 22px; border: 1px solid var(--border);
  background: var(--surface); color: var(--accent); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
  display: flex; align-items: center; justify-content: center; gap: 3px; cursor: grab; touch-action: none;
}
.convo-pill svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.convo-pill .convo-dot { position: absolute; top: 6px; right: 6px; }

/* the toast: the reply, above the composer, readable in full. It was one
   clipped line that faded in six seconds — a trip plan needs more than that
   to be read, so it wraps, stays a while, and goes when tapped. */
.convo-toast {
  position: absolute; left: 14px; right: 14px; bottom: calc(var(--bar-clear, 0px) + 10px);
  padding: 10px 34px 10px 14px; border-radius: 14px; background: var(--ink); color: var(--bg);
  font-size: 14px; line-height: 19px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  /* The foot is a border, not padding: a clamped box still lays out the line
     after its last, and padding is inside what overflow shows — the top half
     of that line was drawn in it. A border is outside. */
  padding-bottom: 0; border-bottom: 10px solid transparent;
  overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6; max-height: 134px;
  white-space: pre-line; overflow-wrap: anywhere; transition: opacity 0.7s ease; cursor: pointer;
  animation: convo-toast-in .22s cubic-bezier(.2,.7,.2,1); /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */
}
@keyframes convo-toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.style-pill_toast .convo-toast { right: 70px; }
.convo-toast.fade { opacity: 0; }
.convo-toast .convo-x { position: absolute; top: 6px; right: 8px; width: 22px; height: 22px; border-radius: 11px; border: 0; background: rgba(255,255,255,.14); color: inherit; font-size: 13px; line-height: 22px; text-align: center; cursor: pointer; padding: 0; }

/* the strip: one line above the composer */
.convo-strip {
  position: absolute; left: 16px; right: 16px; bottom: calc(var(--bar-clear, 0px) + 4px);
  display: flex; align-items: center; gap: 8px; padding: 9px 14px; border-radius: 16px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-size: 14px; text-align: left;
}
.convo-strip svg { width: 14px; height: 14px; stroke: var(--ink-faint); fill: none; stroke-width: 42; transform: rotate(180deg); }
.convo-strip.in-composer { bottom: var(--bar-clear, 0px); border-radius: 16px 16px 0 0; border-bottom: none; margin: 0; }

/* the sheet: peeks above the composer, handle on top */
.convo-sheet {
  position: absolute; left: 10px; right: 10px; bottom: calc(var(--bar-clear, 0px) + 2px);
  padding: 6px 14px 12px; border-radius: 18px 18px 0 0; border: 1px solid var(--border); border-bottom: none;
  background: var(--surface); box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.1); font-size: 14px; color: var(--ink);
}
.convo-handle { width: 36px; height: 4px; border-radius: 2px; background: var(--border); margin: 0 auto 8px; }

/* the band and the card: across the top, under the rail */
.convo-band, .convo-card {
  position: absolute; left: 0; right: 0; top: calc(52px + env(safe-area-inset-top));
  padding: 10px 16px; background: var(--surface); border-bottom: 1px solid var(--border); font-size: 14px; color: var(--ink);
}
.convo-card { left: 12px; right: 12px; border: 1px solid var(--border); border-radius: 16px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1); }
.convo-you { color: var(--ink-faint); font-size: 12.5px; margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */ }

/* the tab: on the right edge, mid-height */
.convo-tab {
  position: absolute; right: 0; top: 45%; width: 34px; height: 64px;
  border-radius: 14px 0 0 14px; border: 1px solid var(--border); border-right: none;
  background: var(--surface); color: var(--accent); box-shadow: -3px 0 12px rgba(0, 0, 0, 0.12);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
}
.convo-tab svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }

/* the thread itself: one panel for every shape, above the composer */
.convo-panel {
  position: absolute; left: 10px; right: 10px; bottom: calc(var(--bar-clear, 0px) + 2px);
  max-height: min(62%, 460px); display: flex; flex-direction: column;
  border-radius: 18px; border: 1px solid var(--border); background: var(--bg);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.2); overflow: hidden;
}
/* the pill's window: up from the circle's corner, like a support chat on a
   website, with the circle under it turned into the × */
.style-pill_toast .convo-panel {
  left: auto; right: 14px; bottom: calc(var(--bar-clear, 0px) + 64px);
  width: min(400px, calc(100% - 28px)); max-height: min(60%, 440px);
  animation: convo-toast-in .2s cubic-bezier(.2,.7,.2,1);
}
.convo-pill.is-close { color: var(--ink-muted); }
.convo-pill.is-close svg { width: 18px; height: 18px; stroke-width: 40; }

/* ---- the conversation on ShmangNav ----
   The foot of that page is the sheet and its top row is the ways to travel
   and the map's own type and Locate, end to end (styles.css .nav-modebar,
   13 Oct 2026), so the pill lives at the top right just under that row, and
   what it says drops down from it. The band, the card and the tab are drawn
   as this pill there (conversation.js shownStyle). */
body[data-page='nav'] .convo-pill { top: calc(122px + env(safe-area-inset-top)); bottom: auto; right: calc(10px + env(safe-area-inset-right)); cursor: pointer; }
body[data-page='nav'] .convo-toast {
  top: calc(174px + env(safe-area-inset-top)); bottom: auto; left: 10px; right: 10px;
  -webkit-line-clamp: 4; max-height: 96px; animation-name: convo-toast-down; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */
}
/* Following a trip, the top is the banner's and the trip line's: the pill steps away and the toast is said over the foot (as the phone does). */
body[data-page='nav'].nav-going .convo-pill, body[data-page='nav'].nav-norow .convo-pill { display: none; }
body[data-page='nav'].nav-going .convo-toast { top: auto; bottom: calc(var(--bar-clear, 0px) + 10px); -webkit-line-clamp: 2; max-height: 58px; animation-name: convo-toast-in; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */ }
@keyframes convo-toast-down { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
/* The sheet is all the way up (nav.js, body.nav-up): under the row of ways is
   its route box, so the toast is said where it is on every other page, over
   the foot, above the composer. */
/* Two lines there, not four: on a short phone the foot of a raised sheet is
   the trip's own card, and the whole reply is one tap away in the pill. */
body[data-page='nav'].nav-up .convo-toast { top: auto; bottom: calc(var(--bar-clear, 0px) + 10px); -webkit-line-clamp: 2; max-height: 58px; animation-name: convo-toast-in; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */ }
/* The strip and the peeking sheet lie over the map's own sheet: opaque there,
   so they read as its foot and not as glass over a list. */
body[data-page='nav'] .convo-strip, body[data-page='nav'] .convo-sheet { background: var(--surface-solid, var(--bg)); }
/* As tall as its thread, and never onto the box you type in. */
body[data-page='nav'] .style-pill_toast .convo-panel {
  top: calc(174px + env(safe-area-inset-top)); bottom: auto; right: 10px; width: min(400px, calc(100% - 20px));
  max-height: min(440px, calc(100% - 182px - env(safe-area-inset-top) - var(--bar-clear, 0px)));
  animation-name: convo-toast-down;
}
/* On the map the box at the bottom is the place field, and it stays where it
   is: the list of places for what is typed is the page above it. The
   conversation steps aside for that list; on a very short screen (a phone
   with its keyboard up) the rail does too. nav.js puts these classes on and
   takes them off again when it leaves. */
body.nav-typing .convo-root { display: none; }
body.nav-short .float-nav { display: none; }
/* There the placeholder is the box's only title ("Where to?", "Where from?"),
   so it is set to be read, as the sheet's own field had it. */
body[data-page='nav'] .cmd-bar input::placeholder { color: var(--ink-muted); opacity: 1; }
.convo-head { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.convo-title { flex: 1; text-align: center; font-size: 13px; font-weight: 600; color: var(--ink-muted); }
.convo-scroll { flex: 1; overflow-y: auto; padding: 12px 14px; }
/* ---- the pull-up bar (conversation.js conversationDrawer) ----
   The row above the box is the handle; the sheet is under the box, the last
   thing in the bar's own block, so the page above shrinks to make room. Its
   height is the bar's lift less the keyboard, set from script; eased between
   stops, under the finger while dragged. Since 9 Oct 2026 it is no card: the
   page's own background, the row of apps first, then the conversation's
   head, then the thread, newest first. */
.wheel-row.drawer-handle { position: relative; touch-action: none; }
/* No grip drawn (the user: "get rid of the gray bar"): the row is the handle.
   The button stays for the keyboard, seen only when it is reached. */
.convo-grip {
  position: absolute; left: 50%; top: -6px; transform: translateX(-50%);
  width: 44px; height: 12px; padding: 0; border: 0; background: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; opacity: 0;
}
.convo-grip::before { content: ''; width: 18px; height: 3px; border-radius: 1.5px; background: var(--ink); }
.convo-grip:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 7px; }
/* A reply is waiting while the bar is down: a short line in the accent, the
   size and shape of the line under the place you are on (the phone's strip). */
.drawer-lit { position: absolute; left: 50%; top: -5px; transform: translateX(-50%); width: 18px; height: 3px; border-radius: 1.5px; background: var(--accent); pointer-events: none; }
.drawer-lit[hidden] { display: none; }
.convo-drawer { height: 0; overflow: hidden; transition: height .26s cubic-bezier(.2,.7,.2,1); display: flex; flex-direction: column; }
.convo-drawer.dragging { transition: none; }
/* Where the page brings its own box and the assistant's is put away (the Claude page), the sheet meets the row
   directly: its own top edge, so it reads as a sheet over the page (21 Oct 2026 review). */
body[data-page='panes'] .convo-drawer.open { border-top: 1px solid var(--border); border-radius: 16px 16px 0 0; box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.08); margin-top: 6px; background: var(--surface-solid, var(--surface)); }
body.drawer-dragging { user-select: none; -webkit-user-select: none; cursor: grabbing; }
/* Pulled up, the box keeps a small gap above the sheet instead of the home bar's margin. */
.cmd-wrap:has(> .convo-drawer.open) > .cmd-line { margin-bottom: 6px; }
.drawer-sheet { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.drawer-top { flex: none; touch-action: none; }
/* The row of apps: sideways it scrolls by itself (pan-x); up or down is the sheet's. */
.drawer-apps { display: flex; height: 92px; padding: 12px 8px 6px; box-sizing: border-box; }
.drawer-apps[hidden] { display: none; }
.drawer-apps-scroll {
  flex: 1; min-width: 0; display: flex; overflow-x: auto; overflow-y: hidden;
  touch-action: pan-x; scrollbar-width: none; overscroll-behavior-x: contain;
}
.drawer-apps-scroll::-webkit-scrollbar { display: none; }
.app-tile {
  flex: none; width: 68px; display: flex; flex-direction: column; align-items: center; gap: 5px;
  border: 0; background: none; padding: 0; color: var(--ink); cursor: pointer; font: inherit;
  -webkit-tap-highlight-color: transparent;
}
.app-tile-art {
  --tile-size: 52px;
  width: var(--tile-size); height: var(--tile-size); border-radius: calc(var(--tile-size) * .27);
  display: flex; align-items: center; justify-content: center; color: #fff;
  background: linear-gradient(to bottom, var(--tile-top, var(--tile)), var(--tile));
  transition: transform 120ms ease, opacity 120ms ease;
}
.app-tile-art svg { width: calc(var(--tile-size) * .5); height: calc(var(--tile-size) * .5); stroke: currentColor; fill: none; stroke-width: 32; }
.app-tile:active .app-tile-art { transform: scale(.96); opacity: .7; }
.app-tile:focus-visible { outline: none; }
.app-tile:focus-visible .app-tile-art { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Whole words only: a long one sizes the name down instead (appTile.js, appRow.mjs tileNameSize; the UI review,
   21 Oct 2026: "Communitie/s" at 320). */
.app-tile-name { font-size: 11.5px; font-weight: 500; line-height: 1.22; max-width: 66px; white-space: normal; overflow-wrap: normal; word-break: normal; hyphens: manual; }
@media (hover: hover) { .app-tile:hover .app-tile-art { filter: brightness(1.06); } }
/* All apps, last: the grid on the page's own surface, a hairline round it. */
.app-tile-all .app-tile-art { background: var(--surface); border: 1px solid var(--border); color: var(--ink-muted); box-sizing: border-box; }
/* The dots over the chats (14 Oct 2026, conversation.js "the chats, side by
   side"): which chat is in front, and that there are more either side. The
   blank page's dot is a ring. A dot is also a mouse's way to a chat. */
.drawer-dots { display: flex; align-items: center; justify-content: center; gap: 5px; height: 16px; }
.drawer-dots[hidden] { display: none; }
.drawer-dot { position: relative; flex: none; width: 6px; height: 6px; padding: 0; border: 0; border-radius: 3px; background: var(--ink-faint); transition: width .18s ease, background-color .18s ease; }
button.drawer-dot { cursor: pointer; }
button.drawer-dot::after { content: ''; position: absolute; inset: -5px -3px; }
.drawer-dot.on { width: 16px; background: var(--accent); }
.drawer-dot.fresh:not(.on) { background: none; box-shadow: inset 0 0 0 1.2px var(--ink-faint); }
.drawer-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The chats, side by side, one page each, snapping one at a time like a
   listing's photos. Each reads newest first, older ones further down. Below
   the top stop a finger on one moves the sheet up or down (conversation.js),
   so the browser keeps only the sideways swipe; at the top it scrolls. */
.drawer-pages { flex: 1; min-height: 0; display: flex; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none; }
.drawer-pages::-webkit-scrollbar { display: none; }
.drawer-page { flex: 0 0 100%; min-width: 0; scroll-snap-align: start; scroll-snap-stop: always; }
.drawer-scroll { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.drawer-pages.sheet-moves .drawer-page { touch-action: pan-x; }
/* The notes page's dot is square: a page, not a chat. Its own styles are Notes' (notes.css). */
.drawer-dot.notes:not(.on) { border-radius: 1.5px; }
.drawer-list { padding: 4px 16px calc(12px + env(safe-area-inset-bottom)); }
.drawer-thinking { display: flex; align-items: center; gap: 8px; color: var(--ink-muted); font-size: 14px; font-style: italic; }
.drawer-list .msg { margin-bottom: 10px; min-width: 0; overflow-wrap: anywhere; }
.drawer-list .msg.user .bubble { font-size: 14px; }
/* A wide table scrolls in its own box rather than panning the whole thread. */
.drawer-list .md table { display: block; overflow-x: auto; max-width: 100%; }
/* …and its cells keep their words whole: the thread's anywhere-wrapping broke "$4,200" into a column of digits (UI review, 18 Oct 2026). */
.drawer-list .md th, .drawer-list .md td { overflow-wrap: normal; word-break: normal; min-width: 4.5em; }
.convo-drawer-empty { color: var(--ink-faint); font-size: 14px; text-align: center; padding: 16px 12px; }
/* The blank page's one line is all there is on it: read, not hinted. */
.convo-drawer-empty.drawer-fresh { color: var(--ink-muted); }

/* ---- All apps: the row as it stands, and its editor (pages/apps.js) ---- */
.apps-row-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.apps-row-head .section-label { margin: 0; }
.link-btn { border: 0; background: none; padding: 6px 2px; font: inherit; font-size: 14px; font-weight: 600; color: var(--accent); cursor: pointer; display: inline-flex; align-items: center; gap: 2px; }
.link-btn svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 40; }
.apps-row-preview { display: flex; overflow-x: auto; scrollbar-width: none; padding: 2px 0 4px; margin: 0 -6px; }
.apps-row-preview::-webkit-scrollbar { display: none; }
.apps-row-empty { width: 100%; border: 1px dashed var(--border); border-radius: 14px; background: none; padding: 16px; color: var(--ink-muted); font: inherit; font-size: 13.5px; cursor: pointer; }
.edit-top { display: flex; align-items: center; justify-content: space-between; padding: calc(52px + env(safe-area-inset-top)) 12px 4px; }
/* In the window its floating Back and X are over the corners: the title sits between them. */
.edit-top.in-window { padding-top: 12px; min-height: 36px; margin-bottom: 10px; }
.edit-top .edit-back { min-width: 84px; }
.edit-title { font-size: 17px; font-weight: 700; color: var(--ink); }
.edit-sub { font-size: 13.5px; color: var(--ink-muted); }
.edit-group { border: 1px solid var(--border); border-radius: 14px; background: var(--surface); }
.edit-line {
  display: flex; align-items: center; gap: 10px; height: 56px; padding: 0 4px 0 6px; box-sizing: border-box;
  border: 0; border-top: 1px solid var(--border); background: none; width: 100%; font: inherit; color: var(--ink); text-align: left;
  transition: transform 160ms ease; position: relative;
}
.edit-group > .edit-line:first-child { border-top: 0; }
.edit-line.add { cursor: pointer; }
.edit-line.lifted { border-radius: 14px; transition: none; z-index: 2; background: var(--surface-solid, var(--bg)); box-shadow: 0 6px 18px rgba(0,0,0,.18); border-top-color: transparent; }
/* An app's own tile in All apps' grid (pages/apps.js): the square the row draws. */
.app-card .card-icon:has(> .app-tile-art) { background: none; width: auto; height: auto; }
.edit-name { flex: 1; min-width: 0; font-size: 15.5px; white-space: normal; overflow-wrap: break-word; }
.edit-btn { width: 32px; height: 44px; flex: none; display: flex; align-items: center; justify-content: center; border: 0; background: none; padding: 0; cursor: pointer; }
.edit-btn svg { width: 22px; height: 22px; border-radius: 11px; stroke: #fff; fill: none; stroke-width: 64; padding: 4px; box-sizing: border-box; }
.edit-btn.remove svg { background: #E5484D; }
.edit-btn.add svg { background: #30A46C; }
.edit-handle { width: 44px; height: 44px; flex: none; border: 0; background: none; color: var(--ink-faint); cursor: grab; touch-action: none; display: flex; align-items: center; justify-content: center; }
.edit-handle svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 32; }
.edit-empty { color: var(--ink-faint); font-size: 13.5px; text-align: center; padding: 18px; }

/* ---- the browser's corner on the web (pages/browser.js) ---- */
.browser-page { padding: 0 16px 24px; }
.browser-head { padding: calc(52px + env(safe-area-inset-top)) 4px 12px; }
.browser-sub { font-size: 13.5px; color: var(--ink-muted); margin-top: 2px; }
/* The buddy asking where to (21 Oct 2026, the phone's NewTabPage the same). */
.browser-buddy { flex: none; display: flex; }
.browser-hello { font: 900 28px/1.1 var(--arc-font); letter-spacing: -0.3px; margin: 0; color: var(--ink); }
/* The box: a key on its lip, as every key of the arcade look. */
.browser-field {
  display: flex; align-items: center; gap: 6px; height: 54px; padding: 0 6px 0 14px; border-radius: 18px;
  background: var(--surface-solid, var(--bg)); border: 1px solid var(--border); border-bottom-width: 3px; box-sizing: border-box;
}
.browser-field:focus-within { border-color: var(--accent); }
.browser-field-ico svg { width: 18px; height: 18px; stroke: var(--ink-faint); fill: none; stroke-width: 36; display: block; }
.browser-box { flex: 1; min-width: 0; border: 0; background: none; outline: none; font: inherit; font-size: 16px; color: var(--ink); height: 100%; }
.browser-box::placeholder { color: var(--ink-faint); }
.browser-go { width: 40px; height: 40px; border-radius: 20px; border: 0; background: var(--border); color: var(--ink-faint); display: flex; align-items: center; justify-content: center; cursor: pointer; flex: none; }
.browser-go.ready { background: var(--accent); color: var(--on-accent, #fff); }
.browser-go svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 40; }
/* Claude's browser, for the owner (pages/cowork.js): a key like the box, under it, its words and arrow on the box's lines. */
.browser-claude {
  display: flex; align-items: center; gap: 6px; width: 100%; min-height: 54px; margin-top: 12px; padding: 7px 6px 7px 14px; box-sizing: border-box;
  border-radius: 18px; border: 1px solid var(--border); border-bottom-width: 3px; background: var(--surface-solid, var(--bg));
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.browser-claude:hover { border-color: var(--accent); }
.browser-claude-ico svg { width: 18px; height: 18px; stroke: var(--accent); fill: none; stroke-width: 36; display: block; }
.browser-claude-words { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.browser-claude-name { font-size: 15.5px; font-weight: 700; }
.browser-claude-sub { font-size: 13px; color: var(--ink-muted); }
.browser-claude-go { width: 40px; height: 40px; flex: none; display: flex; align-items: center; justify-content: center; }
.browser-claude-go svg { width: 18px; height: 18px; stroke: var(--ink-faint); fill: none; stroke-width: 40; display: block; }
.browser-engines { display: flex; align-items: center; gap: 6px; margin: 14px 2px 0; }
.browser-engines-label { font-size: 13px; color: var(--ink-muted); margin-right: 2px; white-space: nowrap; }
.browser-engines .chip { border: 1px solid var(--border); background: none; color: var(--ink-muted); border-radius: 20px; padding: 0 12px; height: 40px; font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap; }
@media (max-width: 359.98px) { .browser-engines-label { display: none; } }
.browser-engines .chip.on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.browser-recent { margin-top: 22px; }
/* The sites: big candy keys on their lips (browserRules.mjs newTabTiles, tileCandy), four a row, eight on a wide screen. */
.browser-page { container-type: inline-size; }
.browser-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px 6px; margin-top: 18px; }
@container (min-width: 720px) { .browser-tiles { grid-template-columns: repeat(8, minmax(0, 1fr)); } }
.browser-tile {
  display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; min-height: 44px; padding: 4px 0;
  border: 0; background: none; font: inherit; color: var(--ink); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.browser-tile-key {
  width: 56px; height: 56px; border-radius: 18px; margin-bottom: 4px; display: flex; align-items: center; justify-content: center;
  background: var(--c); color: var(--cink); font: 900 26px/1 var(--arc-font);
  box-shadow: 0 4px 0 color-mix(in srgb, var(--c) 72%, #000); transition: transform 70ms ease-out, box-shadow 70ms ease-out;
}
.browser-tile:active .browser-tile-key { transform: translateY(3px); box-shadow: 0 1px 0 color-mix(in srgb, var(--c) 72%, #000); }
.browser-tile:focus-visible { outline: none; }
.browser-tile:focus-visible .browser-tile-key { outline: 2px solid var(--accent); outline-offset: 3px; }
.browser-tile-name { font-size: 12px; font-weight: 600; line-height: 1.2; text-align: center; max-width: 100%; hyphens: manual; -webkit-hyphens: manual; overflow-wrap: normal; }
.browser-recent-head { display: flex; align-items: center; justify-content: space-between; }
.browser-recent-head .section-label { margin: 0; }
.browser-recent-row { display: flex; align-items: center; gap: 4px; border-top: 1px solid var(--border); }
.browser-recent-row:first-of-type { border-top: 0; }
.browser-recent-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; border: 0; background: none; padding: 10px 2px; font: inherit; color: var(--ink); text-align: left; cursor: pointer; }
.browser-recent-ico { width: 34px; height: 34px; border-radius: 10px; background: var(--surface); border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; flex: none; }
.browser-recent-ico svg { width: 16px; height: 16px; stroke: var(--ink-muted); fill: none; stroke-width: 36; }
.browser-recent-words { min-width: 0; display: flex; flex-direction: column; }
.browser-recent-label { font-size: 15px; white-space: normal; overflow-wrap: break-word; }
.browser-recent-sub { font-size: 12.5px; color: var(--ink-faint); }
.browser-recent-row .head-icon { width: 40px; height: 40px; flex: none; }

.convo-scroll .msg { margin-bottom: 10px; }
.convo-scroll .msg.user .bubble { font-size: 14px; }

/* ---- the chats list: who is online, what is unread ---- */
.avatar-wrap { position: relative; display: inline-flex; flex: none; }
.presence-dot {
  position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px; border-radius: 6px;
  border: 2px solid var(--bg); background: var(--ink-faint);
}
/* Green when they are set to online AND have been active; amber for idle, red
   for do not disturb. Someone invisible never reaches these — the server sends
   them as plain offline, so they get the grey default above. */
.presence-dot.on { background: var(--success, #2E7D4F); }
.presence-dot.idle { background: #F5A524; }
.presence-dot.dnd { background: #E5484D; }
.thread-row .bottom { display: flex; align-items: center; gap: 8px; }
.thread-row .bottom .preview { flex: 1; min-width: 0; }
.thread-row.unread .name, .thread-row.unread .preview { font-weight: 700; color: var(--ink); }
.unread-badge {
  flex: none; min-width: 22px; height: 20px; padding: 0 6px; border-radius: 10px;
  background: var(--accent); color: var(--bg); font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ---- a contact's picture ---- */
.photo-pick { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 12px; }
.photo-btn { width: 84px; height: 84px; border-radius: 42px; border: none; padding: 0; overflow: hidden; background: var(--accent-soft); color: var(--accent); display: flex; align-items: center; justify-content: center; }
.photo-big, .photo-big img { width: 100%; height: 100%; object-fit: cover; display: flex; align-items: center; justify-content: center; }
.photo-big svg { width: 26px; height: 26px; stroke: currentColor; fill: none; stroke-width: 34; }
.row-initials img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

.stuff-row .row-more { border: none; background: none; color: var(--ink-faint); font-size: 18px; padding: 0 4px; }

/* ---- a post from somewhere else, watched here ---- */
.play-badge { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; }
.play-badge svg { width: 36px; height: 36px; stroke: #fff; fill: rgba(0,0,0,0.35); stroke-width: 24; }
.post-overlay { z-index: 70; }
.post-sheet { position: absolute; left: 0; right: 0; bottom: 0; max-height: 92%; border-radius: 18px 18px 0 0; background: var(--bg); display: flex; flex-direction: column; overflow: hidden; }
@media (min-width: 700px) { .post-sheet { left: 50%; right: auto; bottom: auto; top: 50%; transform: translate(-50%, -50%); width: min(560px, 92vw); border-radius: 18px; } }
.post-body { overflow-y: auto; padding-bottom: 12px; }
.post-video { width: 100%; max-height: 70vh; background: #000; display: block; }
.post-embed { width: 100%; aspect-ratio: 16 / 9; border: 0; background: #000; display: block; }
.post-images { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; }
.post-images img { width: 100%; flex: none; scroll-snap-align: start; max-height: 70vh; object-fit: contain; background: #000; }
.post-text { padding: 12px 16px; font-size: 15px; line-height: 21px; color: var(--ink); white-space: pre-wrap; }
.post-open { display: block; text-align: center; padding: 12px; color: var(--ink-faint); font-size: 13px; text-decoration: none; }

/* ---- link cards shaped like the post they point at ---- */
.card-x { padding: 8px 10px; }
.card-x-head { font-size: 12.5px; }
.card-x-text { font-size: 13.5px; line-height: 19px; margin-top: 3px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; /* preview: a long free-text preview a tap opens in full — the one sanctioned cut */ }
.card-img-wide { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; border-radius: 8px; }
.card-img-tall { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; display: block; }
.card-blank { display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.08); }
.card-blank svg { width: 34px; height: 34px; stroke: var(--ink-muted); fill: none; stroke-width: 34; }

/* ---- mail, the conversation client (pages/mail.js ↔ components/pages/MailPage.tsx) ---- */
.mail-shell { display: flex; flex-direction: column; flex: 1; min-height: 0; height: 100%; }
.mail-top { padding: 8px 12px 6px; border-bottom: 1px solid var(--border); flex: none; display: flex; flex-direction: column; gap: 8px; }
/* The rail floats over the top of the page and now carries the shortcuts as
   well as the burger and gear, so the folder tabs cannot share its row: they
   sit under it, like every other page's controls. A phone conversation has no
   toolbar, so the split itself starts under the rail — otherwise Back is
   underneath the burger. */
.mail-top, .mail-shell > .mail-split:first-child { padding-top: calc(46px + env(safe-area-inset-top)); }
body.embedded .mail-top, body.embedded .mail-shell > .mail-split:first-child { padding-top: 8px; }
.mail-views { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; }
.mail-views::-webkit-scrollbar { display: none; }
.mail-view { border: none; background: none; padding: 6px 10px; border-radius: 999px; font-size: 13px; font-weight: 700;
  color: var(--ink-muted); white-space: nowrap; display: flex; gap: 5px; align-items: center; flex: none; }
.mail-view.on { background: var(--ink); color: var(--bg); }
.mail-view .n { font-size: 11px; background: var(--accent-soft); color: var(--accent); border-radius: 999px; padding: 1px 6px; }
.mail-view.on .n { background: rgba(255,255,255,0.22); color: inherit; }
.mail-tools { display: flex; gap: 8px; align-items: center; }
.mail-searchrow { flex: 1; display: flex; align-items: center; gap: 8px; padding: 0 10px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface); min-width: 0; }
.mail-searchrow svg { width: 16px; height: 16px; stroke: var(--ink-faint); fill: none; stroke-width: 34; flex: none; }
.mail-search { flex: 1; border: none; background: none; font-size: 14px; color: var(--ink); padding: 8px 0; outline: none; min-width: 0; }
.mail-compose-btn { display: flex; align-items: center; gap: 6px; background: var(--ink); color: var(--bg); border: none; border-radius: 12px;
  padding: 8px 12px; font-size: 13.5px; font-weight: 700; flex: none; }
.mail-compose-btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.mail-split { flex: 1; min-height: 0; display: flex; }
.mail-list { flex: 1; min-width: 0; }
.mail-read { flex: 1; min-width: 0; padding: 0 16px; }
.mail-split.wide .mail-list { flex: none; width: 380px; border-right: 1px solid var(--border); }
.mail-split.wide .mail-read { flex: 1; }
.mail-item { cursor: pointer; position: relative; }
.mail-item:hover { background: var(--surface); }
.mail-item.open { background: var(--accent-soft); }
.mail-item.cursor { box-shadow: inset 3px 0 0 var(--accent); }
.mail-item .count { color: var(--ink-faint); font-weight: 500; font-size: 13px; }
.mail-item .clip { display: flex; flex: none; }
.mail-item .clip svg { width: 14px; height: 14px; stroke: var(--ink-faint); fill: none; stroke-width: 34; }
.mail-item .star { border: none; background: none; color: var(--ink-faint); font-size: 18px; line-height: 1; padding: 6px 2px; opacity: 0.3; flex: none; }
.mail-item .star.on { color: #F59E3C; opacity: 1; }
.mail-item:hover .star { opacity: 1; }
.mail-item .by-ai { font-size: 12px; color: var(--accent); font-weight: 600; }
.mail-item.draft .avatar svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.mail-more { width: 100%; padding: 14px; border: none; background: none; color: var(--accent); font-weight: 700; font-size: 14px; }
.mail-read-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--ink-faint); gap: 8px; font-size: 14px; }
.mail-read-empty svg { width: 36px; height: 36px; stroke: currentColor; fill: none; stroke-width: 24; }
/* Edge to edge and clear at rest — a solid strip of page colour showed as a
   band on the card. Once the conversation scrolls under it, it tints and
   takes a hairline, the way a phone's navigation bar does. Every tool is in
   the quiet ink: nothing in the bar is red. */
.mail-read-bar { display: flex; align-items: center; gap: 2px; padding: 6px 10px 4px; margin: 0 -16px; position: sticky; top: 0; z-index: 2;
  border-bottom: 1px solid transparent; transition: background 120ms ease, border-color 120ms ease; }
.mail-read-bar.scrolled { background: var(--bg); border-bottom-color: var(--border); }
.mail-read-bar .icon-btn { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 12px; color: var(--ink-muted); }
.mail-read-bar .icon-btn:hover { background: var(--surface); color: var(--ink); }
.mail-read-bar .icon-btn svg { width: 21px; height: 21px; stroke-width: 32; }
.mail-read-bar .mail-back { color: var(--ink); margin-left: -2px; }
.mail-read-bar .mail-back svg { width: 23px; height: 23px; }
/* The subject: as big as a title, where it lives beside it, the star at the right. */
.mail-read-head { display: flex; align-items: flex-start; gap: 8px; margin: 6px 0 10px; }
.mail-read-subject { flex: 1; min-width: 0; margin: 0; font-size: 22px; font-weight: 600; line-height: 28px; color: var(--ink); overflow-wrap: anywhere; }
.mail-read-subject .text { margin-right: 8px; }
.mail-label { display: inline-block; vertical-align: 3px; margin-right: 4px; padding: 1px 7px; border-radius: 6px; background: var(--surface); border: 1px solid var(--border);
  font-size: 12px; font-weight: 600; line-height: 18px; color: var(--ink-muted); white-space: nowrap; }
.mail-read-star { flex: none; width: 44px; height: 44px; margin: -8px -12px 0 0; border: none; background: none; border-radius: 12px;
  font-size: 21px; line-height: 1; color: var(--ink-faint); cursor: pointer; }
.mail-read-star:hover { background: var(--surface); }
.mail-read-star.on { color: #F59E3C; }
.mail-read-star:disabled { opacity: 0.4; cursor: default; }
/* The messages: flat on the page, a hairline between one and the next. */
.mail-stack { border-top: 1px solid var(--border); }
.mail-msg { border-bottom: 1px solid var(--border); }
.mail-msg-head { display: flex; align-items: flex-start; gap: 12px; padding: 14px 0 10px; cursor: pointer; outline: none; }
.mail-msg-head:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); border-radius: 10px; }
.mail-msg:not(.expanded) .mail-msg-head { padding-bottom: 14px; }
.mail-msg-head .avatar { width: 40px; height: 40px; border-radius: 20px; flex: none; display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 600; font-size: 17px; }
.mail-msg-who { flex: 1; min-width: 0; padding-top: 1px; }
/* A long name keeps its words whole: the time drops under it rather than squeeze it. */
.mail-msg-who .line1 { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 8px; min-width: 0; }
.mail-msg-who .name { min-width: 0; font-size: 15.5px; font-weight: 600; color: var(--ink); overflow-wrap: break-word; }
.mail-msg.unseen:not(.expanded) .name { font-weight: 700; }
.mail-msg-who .when { flex: none; font-size: 12.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.mail-msg-who .snippet { /* preview: a closed message's first words; a click opens it. */ font-size: 14px; line-height: 20px; color: var(--ink-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-msg.expanded .mail-msg-who .snippet,
.mail-msg:not(.expanded) .mail-msg-who .mail-msg-to,
.mail-msg:not(.expanded) .mail-msg-acts,
.mail-msg:not(.expanded) .mail-details { display: none; }
.mail-msg-who .mail-msg-to { margin-top: 1px; min-width: 0; }
/* "to me and 5 others ▾": the ▾ runs on at the end of the words, and the
   press reaches 44px tall without moving the line (padding out, margin back). */
.mail-to { display: inline-block; max-width: 100%; min-height: 0; height: auto; padding: 13px 6px; margin: -11px 0 -11px -6px; border: none; background: none; border-radius: 8px;
  font: inherit; font-size: 13px; line-height: 18px; color: var(--ink-faint); cursor: pointer; text-align: left; }
.mail-to:hover { background: var(--surface); color: var(--ink-muted); }
.mail-to .to-text { overflow-wrap: break-word; min-width: 0; }
.mail-to .to-end { white-space: nowrap; }
.mail-to svg { display: inline-block; width: 12px; height: 12px; margin-left: 3px; vertical-align: -1px; stroke: currentColor; fill: none; stroke-width: 44; transition: transform 160ms ease; }
.mail-to.on svg { transform: rotate(180deg); }
.mail-msg-acts { flex: none; display: flex; align-items: center; margin: -9px -12px 0 0; }
.mail-msg-acts .icon-btn { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 12px; color: var(--ink-muted); }
.mail-msg-acts .icon-btn:hover { background: var(--surface); color: var(--ink); }
.mail-msg-acts .icon-btn svg { width: 19px; height: 19px; stroke-width: 34; }
/* A small phone: the name has the row to itself, the time with Reply and ⋯
   under it, and "to me and 5 others ▾" the whole width under them. */
@media (max-width: 359.98px) {
  .mail-msg.expanded .mail-msg-head { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; column-gap: 12px; row-gap: 0; align-items: center; }
  .mail-msg.expanded .mail-msg-head > .avatar { grid-row: 1 / span 2; align-self: start; }
  .mail-msg.expanded .mail-msg-who, .mail-msg.expanded .mail-msg-who .line1 { display: contents; }
  .mail-msg.expanded .mail-msg-who .name { grid-column: 2 / 4; grid-row: 1; align-self: start; padding-top: 1px; }
  .mail-msg.expanded .mail-msg-who .when { grid-column: 2; grid-row: 2; }
  .mail-msg.expanded .mail-msg-acts { grid-column: 3; grid-row: 2; margin: -8px -12px -8px 0; }
  .mail-msg.expanded .mail-msg-who .mail-msg-to { grid-column: 2 / 4; grid-row: 3; margin-top: 2px; }
}
/* "to me ▾" opens who it was from and to, when, and which account it came through. */
.mail-details { margin: 0 0 12px 52px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 13px; line-height: 18px; }
.mail-details[hidden] { display: none; }
.mail-details .row { display: contents; }
.mail-details .k { color: var(--ink-faint); text-align: right; }
.mail-details .v { color: var(--ink); min-width: 0; overflow-wrap: break-word; }
/* One person a line; the address drops whole under the name when both do not fit. */
.mail-details .v .who { display: block; }
.mail-details .v .addr { display: inline-block; max-width: 100%; overflow-wrap: anywhere; }
.mail-msg-body { display: none; padding: 0 0 16px; }
.mail-msg.expanded .mail-msg-body { display: block; }
.mail-text { font-size: 15px; line-height: 22px; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.mail-text a, .mail-quote a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
/* Folded history: "•••" shows it, in a quieter ink with a rule at its left. */
/* The same "•••" as the one drawn inside an HTML mail (mailRead.mjs) and the phone's. */
.mail-dots { display: inline-flex; align-items: center; justify-content: center; height: 22px; margin-top: 10px; padding: 0 8px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted); cursor: pointer; }
.mail-dots:hover { border-color: var(--ink-faint); }
.mail-dots[aria-expanded='true'] { background: var(--accent-soft); color: var(--accent); }
.mail-dots[hidden] { display: none; }
/* The small marks keep their look, but a finger gets 44px to press: the
   hit area reaches past the drawn edge (•••, and the reply box's ⤢, Everyone and send). */
.mail-dots, .mail-reply-bar .cmd-icon, .mail-reply-send, .mail-reply-chip { position: relative; z-index: 1; }
.mail-dots::before { content: ''; position: absolute; left: 0; right: 0; top: -11px; bottom: -11px; min-width: 44px; }
.mail-reply-bar .cmd-icon::before { content: ''; position: absolute; inset: -7px; }
.mail-reply-send::before { content: ''; position: absolute; inset: -4px; }
.mail-reply-chip::before { content: ''; position: absolute; inset: -6px -2px; }
.mail-dots svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 44; }
.mail-quote { margin-top: 10px; padding-left: 12px; border-left: 2px solid var(--border); font-size: 14px; line-height: 21px; color: var(--ink-muted);
  white-space: pre-wrap; overflow-wrap: anywhere; }
.mail-quote[hidden] { display: none; }
/* An HTML mail: flat on the page in the app's mode; "Show original colours"
   draws it as designed, on white. */
.mail-html { position: relative; }
.mail-html.original { background: #fff; border-radius: 8px; overflow: hidden; }
.mail-frame { width: 100%; border: none; display: block; height: 120px; background: transparent; }
.mail-html.preparing .mail-frame { opacity: 0; }
/* A sender the checks could not verify: said first, in the danger colour. */
.mail-spoof { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; padding: 8px 12px; border-radius: 10px;
  border: 1px solid var(--danger); color: var(--danger); font-size: 13px; font-weight: 600; }
.mail-spoof svg { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 32; }
.mail-spoof .text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* A mail's frame lays out unseen behind its preview until it has measured
   itself (pages/mail.js htmlFrame), then takes its height once and fades in. */
.mail-html.measuring .mail-frame { position: absolute; left: 0; top: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; }
.mail-html.measured .mail-frame { animation: mail-frame-in 160ms ease-out; }
@keyframes mail-frame-in { from { opacity: 0; } to { opacity: 1; } }
.mail-frame-wait { /* preview: the mail's first words while its page lays out behind them. */ padding: 0; min-height: 44px; color: var(--ink-muted); font-size: 15px; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden; }
/* "N earlier": hairlines either side of a circle with the number in it. */
.mail-stack-more { display: flex; align-items: center; gap: 10px; width: 100%; background: none; border: none; padding: 8px 0;
  border-bottom: 1px solid var(--border); color: var(--ink-muted); font: inherit; cursor: pointer; }
.mail-stack-more::before, .mail-stack-more::after { content: ''; flex: 1; height: 0; border-top: 1px solid var(--border); }
.mail-stack-more .n { flex: none; width: 32px; height: 32px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface);
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.mail-stack-more:hover .n, .mail-stack-more:focus-visible .n { color: var(--ink); border-color: var(--ink-faint); }
/* Between the click and the conversation; and an older message's body on its way. */
.mail-peek-from { font-size: 14px; color: var(--ink-muted); margin: 0 0 18px; overflow-wrap: break-word; }
.mail-peek-wait, .mail-body-wait-line { display: flex; align-items: center; gap: 8px; color: var(--ink-faint); font-size: 13px; }
.mail-spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); border-top-color: var(--ink-faint);
  animation: spin 800ms linear infinite; flex: none; }
.mail-body-wait { display: block; width: 100%; text-align: left; background: none; border: none; padding: 0; font: inherit; color: inherit; }
.mail-body-wait:not(:disabled) { cursor: pointer; }
.mail-body-wait:not(:disabled) .mail-body-wait-line { color: var(--accent); }
.mail-body-wait-text { /* preview: the message's first words while the rest is on its way. */ color: var(--ink-muted); font-size: 14px; line-height: 1.5; margin-bottom: 8px;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  .mail-html.measured .mail-frame { animation: none; }
  .mail-to svg { transition: none; }
}
/* Attachments: tiles in a row that scrolls sideways. */
.mail-files { display: flex; gap: 8px; overflow-x: auto; margin-top: 14px; padding-bottom: 2px; scrollbar-width: thin; }
/* A phone: the tiles wrap, two to a row, so none stands half off the edge with its name cut. */
@media (max-width: 719.98px) {
  .mail-files { flex-wrap: wrap; overflow: visible; }
  .mail-files > .mail-file { width: calc(50% - 4px); }
}
.mail-file { flex: none; width: 160px; display: flex; flex-direction: column; gap: 6px; align-items: stretch;
  padding: 10px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); text-align: left; font: inherit; color: var(--ink); cursor: pointer; }
.mail-file:hover { border-color: var(--ink-faint); }
.mail-file:disabled { cursor: progress; opacity: 0.8; }
.mail-file .kind { width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center; justify-content: center; }
.mail-file .kind svg { width: 19px; height: 19px; stroke: #fff; fill: none; stroke-width: 34; }
.mail-file .name { font-size: 13px; font-weight: 600; line-height: 17px; min-height: 34px; min-width: 0; overflow-wrap: anywhere; }
.mail-file .meta { font-size: 12px; line-height: 16px; color: var(--ink-faint); min-width: 0; overflow-wrap: break-word; }
.attach-chip { background: none; cursor: pointer; max-width: 100%; text-align: left; }
.attach-chip svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
.attach-chip .x { border: none; background: none; padding: 0; margin-left: 2px; color: var(--ink-faint); display: flex; }
.attach-chip .x svg { width: 12px; height: 12px; }
/* Reply, Reply all, Forward: outlined pills sharing the width. */
.mail-reply-row { display: flex; gap: 8px; margin-top: 16px; margin-bottom: 12px; }
.mail-reply-btn { flex: 1 1 0; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 6px; border: 1px solid var(--border); background: none; color: var(--ink);
  border-radius: 999px; padding: 9px 10px; min-height: 44px; font-size: 13.5px; font-weight: 600; white-space: nowrap; cursor: pointer; }
.mail-reply-btn:hover { background: var(--surface); }
.mail-reply-btn svg { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 34; }
/* A small phone: each pill as wide as its words, a little tighter, so "Reply all" keeps its room on the one row. */
@media (max-width: 359.98px) {
  .mail-reply-row { flex-wrap: wrap; gap: 6px; }
  .mail-reply-btn { flex: 1 1 auto; padding: 9px 6px; gap: 4px; font-size: 13px; }
  .mail-reply-btn .reply-all-icon { width: 18px; }
  .mail-reply-btn .reply-all-icon svg:first-child { left: 3px; }
}
/* Reply all: two reply arrows, the second a little behind the first. */
.reply-all-icon { position: relative; display: inline-block; width: 22px; height: 16px; flex: none; }
.reply-all-icon svg { position: absolute; top: 0; }
.reply-all-icon svg:first-child { left: 6px; }
.reply-all-icon svg:last-child { left: 0; }
.mail-menu-row { display: block; width: 100%; text-align: left; padding: 12px 4px; border: none; border-bottom: 1px solid var(--border); background: none; font-size: 15px; color: var(--ink); }
.mail-menu-row.danger { color: var(--danger); }
/* composer */
.mail-from { font-size: 12.5px; color: var(--ink-faint); margin-top: 12px; }
/* One From line whichever way it is drawn: text when there is one address, a
   picker when a connected account gives a choice. */
.mail-from-row { display: flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 12.5px; color: var(--ink-faint); }
.mail-from-select { font: inherit; font-size: 12.5px; color: var(--ink); background: transparent; border: 0; padding: 0; flex: 1 1 auto; min-width: 0; cursor: pointer; }
/* Which mailbox: all of them, the Shmangus one, or one connected account. */
.mail-acct-row { display: flex; gap: 6px; padding: 0 10px 6px; overflow-x: auto; scrollbar-width: none; }
.mail-acct-row::-webkit-scrollbar { display: none; }
.mail-acct-row .mail-view { flex: none; }
.mail-item .tag.acct svg { width: 10px; height: 10px; }
.acct-state { font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
.acct-state.err { color: var(--danger); }
/* A connected account's full import: one line and a thin bar (mail.js importText). */
.acct-import { margin-top: 8px; }
.acct-import-text { font-size: 12px; color: var(--ink-muted); line-height: 1.35; overflow-wrap: anywhere; }
.acct-import.done .acct-import-text { color: var(--success); }
.acct-import .progress-track { margin-top: 6px; }
.progress-track { position: relative; height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.progress-fill { height: 100%; border-radius: 2px; background: var(--accent); transition: width 0.6s ease; }
.progress-track.waiting .progress-fill { animation: progress-wait 1.4s ease-in-out infinite; }
@keyframes progress-wait { 0% { transform: translateX(-110%); } 100% { transform: translateX(340%); } }
@media (prefers-reduced-motion: reduce) { .progress-track.waiting .progress-fill { animation: none; } .progress-fill { transition: none; } }
/* The same, over the mail list while mail is still coming in; a tap opens the accounts. */
.mail-import { display: flex; flex-direction: column; gap: 6px; width: 100%; margin-top: 0; padding: 8px 12px 9px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); color: var(--ink-muted); font: inherit; font-size: 12.5px; text-align: left; cursor: pointer; }
.mail-import:hover { border-color: var(--accent); }
/* A button does not stretch its children: the track is told to, or it is 0 wide. */
.mail-import .progress-track, .acct-import .progress-track { width: 100%; align-self: stretch; }
.mail-import-text { overflow-wrap: break-word; }
.mail-import.paused .mail-import-text { white-space: normal; }
.acct-note { font-size: 12.5px; color: var(--ink-muted); line-height: 1.4; padding: 6px 2px 2px; }
.acct-note a { color: var(--accent); }
.acct-providers { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 0 8px; }
.acct-form .field { margin-top: 8px; }
/* One rule under To and Cc/Bcc together, full width like Subject's. */
.mail-to-row { display: flex; align-items: flex-start; gap: 6px; border-bottom: 1px solid var(--border); }
.mail-to-row > .mail-chip-field { border-bottom: none; }
/* Setup guides: numbered steps, the address to copy, Gmail's confirmation code. */
.mail-steps { margin: 8px 0 0; padding: 0; list-style: none; counter-reset: step; }
.mail-steps li { counter-increment: step; display: flex; gap: 10px; font-size: 13.5px; line-height: 19px; color: var(--ink); padding: 5px 0; }
.mail-steps li::before { content: counter(step); flex: none; width: 20px; height: 20px; border-radius: 10px; background: var(--accent-soft); color: var(--accent); font-size: 11.5px; font-weight: 700; display: flex; align-items: center; justify-content: center; margin-top: -1px; }
.mail-copy { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; margin-top: 10px; background: var(--surface); }
.mail-copy .text { flex: 1; min-width: 0; font-size: 14px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; word-break: normal; user-select: all; }
@media (max-width: 420px) { .mail-copy .text { font-size: 13px; } }
.mail-copy button { flex: none; font-size: 13px; font-weight: 700; color: var(--accent); }
.mail-code { font-size: 30px; font-weight: 800; letter-spacing: 3px; color: var(--ink); text-align: center; padding: 8px 0 2px; user-select: all; font-variant-numeric: tabular-nums; }
.mail-confirm { border: 1px solid var(--accent); border-radius: 14px; padding: 12px 14px; margin-top: 12px; background: var(--accent-soft); }
.mail-confirm .lbl { font-size: 12.5px; color: var(--ink-muted); text-align: center; }
.ghost-link-btn { display: block; width: 100%; text-align: center; border: 1px solid var(--border); border-radius: 14px; padding: 12px 16px; margin-top: 10px; font-weight: 600; font-size: 14.5px; color: var(--ink); background: transparent; }
.fwd-status { font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
.fwd-status.confirm { color: var(--accent); font-weight: 600; }
.fwd-status.working { color: var(--success, var(--ink-muted)); }
.mail-chip-field { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 6px 0; border-bottom: 1px solid var(--border); position: relative; cursor: text; min-height: 38px; }
.mail-chip-field .lbl { font-size: 13px; color: var(--ink-faint); margin-right: 4px; min-width: 28px; }
.mail-chips { display: contents; }
.mail-chip { display: inline-flex; align-items: center; gap: 2px; background: var(--accent-soft); color: var(--accent); border-radius: 999px; padding: 3px 4px 3px 9px; font-size: 13px; font-weight: 600; max-width: 100%; }
.mail-chip .x { border: none; background: none; color: inherit; font-size: 15px; line-height: 1; padding: 0 5px; }
.mail-chip-input { flex: 1; min-width: 60px; border: none; background: none; font-size: 14px; color: var(--ink); outline: none; padding: 4px 0; }
.mail-suggest { position: absolute; left: 0; right: 0; top: 100%; z-index: 5; background: var(--surface-solid); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); overflow: hidden; }
.mail-suggest-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px 12px; border: none; background: none; font-size: 14px; color: var(--ink); }
.mail-suggest-row .avatar.sm { width: 28px; height: 28px; border-radius: 14px; flex: none; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 12.5px; font-weight: 600; }
.mail-suggest-row .who { display: flex; flex-direction: column; min-width: 0; line-height: 18px; }
.mail-suggest-row .who b, .mail-suggest-row .who .faint { overflow-wrap: anywhere; }
.mail-suggest-row .who .faint { font-size: 12.5px; }
.mail-suggest-row:hover { background: var(--accent-soft); }
.mail-cc-toggle { border: none; background: none; color: var(--ink-faint); font-size: 12.5px; padding: 10px 0; flex: none; }
.mail-subject { width: 100%; border: none; border-bottom: 1px solid var(--border); background: none; font-size: 15px; font-weight: 600; color: var(--ink); padding: 10px 0; outline: none; }
.mail-body { width: 100%; border: none; background: none; font-size: 15px; line-height: 22px; font-family: inherit; color: var(--ink); padding: 12px 0; outline: none; resize: none; min-height: 140px; }
.mail-compose-note { font-size: 12.5px; color: var(--ink-muted); background: var(--accent-soft); border-radius: 10px; padding: 8px 10px; margin-top: 8px; }
.mail-attach-row { display: flex; flex-wrap: wrap; }
.mail-compose-bar { display: flex; align-items: center; gap: 8px; min-height: 18px; margin-top: 6px; }
.mail-compose-status { font-size: 12px; color: var(--ink-faint); }
/* The composer's head: ✕, what it is, then attach, ⋯ and Send. */
.mail-compose-head { gap: 6px; padding: 8px 10px 8px 8px; }
.mail-compose-head .sheet-title { flex: 1; min-width: 0; overflow-wrap: break-word; }
.mail-compose-head .icon-btn { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: 12px; color: var(--ink-muted); flex: none; }
.mail-compose-head .icon-btn:hover { background: var(--surface); color: var(--ink); }
.mail-compose-head .mail-compose-close { color: var(--ink); }
.mail-compose-acts { display: flex; align-items: center; gap: 2px; flex: none; }
.mail-send { display: inline-flex; align-items: center; gap: 6px; height: 36px; margin-left: 4px; padding: 0 16px 0 13px; border: none; border-radius: 18px;
  background: var(--accent); color: var(--bg); font: inherit; font-size: 14.5px; font-weight: 700; cursor: pointer; flex: none; }
.mail-send:hover { filter: brightness(1.06); }
.mail-send:disabled { opacity: 0.6; cursor: progress; }
.mail-send svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 36; }
/* A small phone: Send is the arrow alone, so the title keeps its words. */
@media (max-width: 360px) {
  .mail-send { padding: 0 10px; }
  .mail-send > span { display: none; }
  .mail-compose-head .icon-btn { width: 36px; }
}
.mail-drive-crumb { font-size: 12px; color: var(--ink-faint); margin: 10px 0 4px; }
.mail-drive-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 10px 6px; border: none; border-bottom: 1px solid var(--border); background: none; font-size: 14px; color: var(--ink); }
.mail-drive-row svg { width: 16px; height: 16px; stroke: var(--ink-muted); fill: none; stroke-width: 34; flex: none; }

/* the reply bar stands where the assistant's composer was while a conversation is open */
.mail-read { display: flex; flex-direction: column; }
/* A flex column shrinks its children to fit; a card is overflow:hidden, so on
   a phone a long message was cut off and the pane never scrolled. */
.mail-read > * { flex: none; }
.mail-read > .mail-reply-bar { margin-top: auto; }
.mail-reply-bar {
  position: sticky; bottom: 10px; display: flex; align-items: flex-end; gap: 6px;
  margin: 14px -4px 10px; padding: 8px 8px 8px 6px; border-radius: 22px;
  border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); box-shadow: 0 6px 20px rgba(0,0,0,0.08);
}
/* Under the floating bar, down to the pane's edge, the page itself: the mail
   scrolling past never shows in the strip below it. */
.mail-reply-bar::after { content: ''; position: absolute; left: -16px; right: -16px; top: calc(100% + 1px); height: 10px; background: var(--bg); pointer-events: none; }
/* On a small phone "Everyone" is its icon alone, so the hint keeps its room. */
@media (max-width: 359px) { .mail-reply-chip .label { display: none; } .mail-reply-chip { min-width: 40px; min-height: 36px; justify-content: center; padding: 6px 8px; } .mail-reply-chip svg { width: 16px; height: 16px; } }
.mail-reply-bar .cmd-icon { flex: none; }
.mail-reply-input:placeholder-shown { /* input: a placeholder cannot wrap; "Reply to …" is cut only when the box is narrower than it. */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mail-reply-input { flex: 1; min-width: 0; border: none; background: none; resize: none; font: 15px/22px inherit; font-family: inherit;
  color: var(--ink); outline: none; padding: 7px 4px; max-height: 220px; }
.mail-reply-chip { display: flex; align-items: center; gap: 4px; border: 1px solid var(--border); background: none; color: var(--ink-faint);
  border-radius: 999px; padding: 6px 9px; font-size: 12px; font-weight: 700; margin-bottom: 4px; flex: none; }
.mail-reply-chip.on { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.mail-reply-chip svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 34; }
.mail-reply-send { width: 36px; height: 36px; border-radius: 18px; border: none; background: var(--border); color: var(--ink-faint);
  display: flex; align-items: center; justify-content: center; flex: none; margin-bottom: 1px; }
.mail-reply-send.ready { background: var(--ink); color: var(--bg); }
.mail-reply-send svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 34; }
/* the list pane */
.mail-list-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; position: relative; }
.mail-list-wrap > .mail-list { flex: 1; }
.mail-split.wide .mail-list-wrap { flex: none; width: 380px; border-right: 1px solid var(--border); }
.mail-split.wide .mail-list-wrap > .mail-list { width: auto; border-right: none; }


/* ===========================================================================
   Reminders (pages/lists.js) and Ideas (pages/ideas.js), 7 Sep
   =========================================================================== */
/* The rail floats over the top of the page; the control sits under it. */
.screen > .inner.lists-page, .screen-scroll > .inner.lists-page,
.screen > .inner.ideas-page, .screen-scroll > .inner.ideas-page { padding-top: calc(46px + env(safe-area-inset-top)); }
body.embedded .inner.lists-page, body.embedded .inner.ideas-page { padding-top: 8px; }
/* One control for the four views: a track with the chosen one filled. */
.seg-tabs { flex: 1; min-width: 0; display: flex; gap: 2px; padding: 3px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); }
.seg-tab { flex: 1; min-width: 0; padding: 7px 2px; border-radius: 11px; border: none; background: none; font-size: 12.5px; letter-spacing: -0.2px; font-weight: 700; color: var(--ink-muted); white-space: normal; cursor: pointer; overflow-wrap: break-word; }
.seg-tab.active { background: var(--ink); color: var(--bg); flex: 1.35; }
.add-btn.solid { width: 36px; height: 36px; border-radius: 12px; background: var(--accent); color: var(--bg); }
.sort-row { display: inline-flex; align-items: center; gap: 5px; margin: 0 14px 4px; padding: 4px 6px; border: none; background: none; color: var(--ink-muted); font-size: 12.5px; font-weight: 700; cursor: pointer; }
.sort-row svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 34; }
.quick-box { display: flex; align-items: center; gap: 8px; margin: 0 14px 10px; padding: 0 12px; border: 1px solid var(--border); border-radius: 14px; background: var(--surface); }
.quick-box svg { width: 18px; height: 18px; stroke: var(--accent); fill: none; stroke-width: 44; flex: none; }
.quick-input { flex: 1; min-width: 0; border: none; background: none; outline: none; font-size: 16px; padding: 11px 0; color: var(--ink); }
.stuff-list.grouped { gap: 0; }
.list-section { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
.list-section-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 12px 4px 4px; font-size: 11.5px; font-weight: 800; letter-spacing: 0.06em; color: var(--ink-faint); }
.list-section-head.urgent { color: var(--danger, #C43D3D); }
.list-row.urgent { border: 1.5px solid var(--danger, #C43D3D); }
.list-row .tick.urgent { border-color: var(--danger, #C43D3D); }
.list-row .tick.p-critical { border-color: var(--danger, #C43D3D); }
.list-row .tick.p-normal { border-color: var(--ink-faint); }
.list-row .row-when { display: flex; align-items: center; gap: 5px; }
.row-glyph { display: inline-flex; color: var(--ink-faint); font-size: 12px; }
.row-glyph.out { color: var(--accent); }
.row-glyph svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 40; }
.row-snooze { flex: none; width: 30px; height: 30px; border-radius: 15px; border: none; background: none; color: var(--ink-muted); display: grid; place-items: center; cursor: pointer; }
.row-snooze svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 34; }
.buy-rank { display: inline-block; min-width: 20px; margin-right: 6px; padding: 0 5px; border-radius: 7px; background: var(--accent-soft); color: var(--accent); font-size: 12px; font-weight: 800; text-align: center; }
.done-fold { padding: 8px 14px; }

/* Ideas: bullets you edit in place. */
.ideas-page { padding-top: 8px; }
.ideas-list { display: flex; flex-direction: column; }
.idea-row { display: flex; align-items: flex-start; gap: 2px; border-radius: 12px; padding: 2px 4px; }
.idea-row:focus-within { background: var(--surface); }
.idea-tick { flex: none; width: 32px; height: 40px; border: none; background: none; cursor: pointer; display: grid; place-items: center; }
.idea-tick::before { content: ''; width: 20px; height: 20px; border-radius: 10px; border: 2px solid var(--ink-faint); box-sizing: border-box; }
.idea-tick:hover::before { border-color: var(--accent); }
.idea-tick.on::before { display: none; }
.idea-tick.on { color: var(--success, #2E8B6E); }
.idea-tick.on svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 46; }
.idea-input { flex: 1; min-width: 0; resize: none; border: none; background: none; outline: none; font: inherit; font-size: 16.5px; line-height: 23px; padding: 8px 4px; color: var(--ink); overflow: hidden; }
.idea-tools { display: flex; opacity: 0; transition: opacity 120ms; }
.idea-row:hover .idea-tools, .idea-row:focus-within .idea-tools { opacity: 1; }
.idea-move { width: 28px; height: 40px; border: none; background: none; color: var(--ink-faint); cursor: pointer; font-size: 14px; }
.idea-move:hover { color: var(--ink); }
.idea-move svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.idea-add { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: none; background: none; color: var(--accent); font-size: 16px; font-weight: 600; cursor: pointer; }
.idea-add svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 44; }
.ideas-done-head { display: flex; align-items: center; gap: 6px; margin-top: 22px; padding: 8px 10px; border: none; background: none; color: var(--ink-muted); font-size: 13px; font-weight: 700; letter-spacing: 0.04em; cursor: pointer; }
.ideas-done-head svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 40; }
.idea-row.done { opacity: 0.85; }
.idea-done-text { flex: 1; padding: 8px 4px; color: var(--ink-muted); font-size: 15.5px; }
.idea-done-text > div:first-child { text-decoration: line-through; }

/* ---- Health (pages/health*.js; components/pages/HealthPage.tsx + components/health/* on the phone) ----
   Tokens only. The house amber (#F59E3C) marks "near / over", never a number; the
   warn-box pair is the same one Nav uses. Every animated block has a
   prefers-reduced-motion switch at the end of this section. */
.health-page { padding-bottom: 24px; display: flex; flex-direction: column; gap: 10px; }
.screen > .inner.health-page, .screen-scroll > .inner.health-page { padding-top: calc(46px + env(safe-area-inset-top)); }
body.embedded .inner.health-page { padding-top: 8px; }
.health-page .page-head { padding: 0 2px 2px; gap: 8px; }
.hl-half { display: flex; flex-direction: column; gap: 10px; }
.health-page .page-head .add-btn, .health-page .page-head .icon-btn { width: 36px; height: 36px; border-radius: 10px; }
.health-page .page-head .icon-btn { border: 1px solid var(--border); background: var(--surface); }
/* The status strip: one line and a 2px bar that runs while a job does. */
.hl-strip { position: relative; display: flex; align-items: center; gap: 8px; padding: 7px 12px 9px; border-radius: 12px; background: var(--surface); border: 1px solid var(--border); font-size: 12.5px; color: var(--ink-muted); overflow: hidden; }
.hl-strip.live { color: var(--accent); }
.hl-strip.failed { color: var(--danger); }
.hl-strip-line { flex: 1; min-width: 0; }
.hl-strip-act { flex: none; border: none; background: none; color: var(--accent); font-weight: 700; font-size: 12.5px; cursor: pointer; padding: 0; }
.hl-strip-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--accent-soft); overflow: hidden; }
.hl-strip-bar::after { content: ''; position: absolute; top: 0; bottom: 0; width: 40%; background: var(--accent); border-radius: 2px; animation: hl-indeterminate 1.4s cubic-bezier(.4,0,.6,1) infinite; }
@keyframes hl-indeterminate { from { left: -40%; } to { left: 100%; } }
/* Today hero */
.hl-hero { display: flex; flex-direction: column; gap: 8px; }
.hl-hero-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.hl-hero-nums { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; cursor: pointer; border: none; background: none; padding: 0; text-align: left; color: var(--ink); font: inherit; }
.hl-hero-num { font-size: 32px; font-weight: 700; letter-spacing: 0.3px; color: var(--ink); line-height: 1.05; }
.hl-hero-of { font-size: 13px; color: var(--ink-muted); }
.hl-hero-over { font-size: 12.5px; color: var(--ink-muted); }
.hl-hero .bar-track { width: 100%; }
.hl-hero .bar-fill { transition: width .3s; }
.hl-macros { display: flex; gap: 10px; }
.hl-macro { flex: 1; min-width: 0; }
.hl-macro-label { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--ink-muted); margin-bottom: 3px; white-space: nowrap; }
.hl-macro-label b { color: var(--ink); font-weight: 700; }
.hl-macro-bar { height: 6px; border-radius: 3px; background: var(--accent-soft); overflow: hidden; }
.hl-macro-bar > div { height: 100%; border-radius: 3px; background: var(--accent); transition: width .3s; }
.hl-next { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--border); }
.hl-next-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; color: var(--ink-muted); margin-bottom: 4px; }
.hl-next-title { display: block; width: 100%; text-align: left; border: none; background: none; padding: 0; font: inherit; font-size: 15.5px; font-weight: 600; color: var(--ink); cursor: pointer; }
.hl-next-sub { font-size: 12.5px; color: var(--ink-muted); margin-top: 2px; }
.hl-next .hl-actions { padding: 8px 0 0; }
.hl-link { border: none; background: none; padding: 0; color: var(--accent); font: inherit; font-size: 12.5px; cursor: pointer; }
/* Action rows and buttons with icons */
.hl-actions { display: flex; flex-wrap: wrap; gap: 8px; padding: 6px 4px; align-items: center; }
.hl-actions .btn { display: inline-flex; align-items: center; gap: 5px; }
.hl-actions .btn svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 34; }
.hl-actions.log .btn { flex: 1 1 auto; justify-content: center; min-height: 36px; }
.hl-pad { padding: 4px 6px; line-height: 19px; }
/* Compact rows */
.hl-list { display: flex; flex-direction: column; gap: 2px; }
.hl-row { display: flex; align-items: center; gap: 10px; padding: 8px 6px; border-radius: 12px; width: 100%; text-align: left; border: none; background: none; font: inherit; color: var(--ink); }
.hl-row.tap { cursor: pointer; }
.hl-row.tap:hover { background: var(--surface); }
.hl-row.done .row-title { text-decoration: line-through; color: var(--ink-faint); }
.hl-row.skipped { opacity: .55; }
.hl-row .row-title { font-size: 15px; white-space: normal; overflow: visible; }
.hl-row .row-sub { font-size: 12.5px; display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; }
.hl-thumb { width: 44px; height: 44px; border-radius: 10px; background: var(--accent-soft) center/cover no-repeat; flex: none; }
.hl-thumb.icon { display: flex; align-items: center; justify-content: center; color: var(--accent); }
.hl-thumb.icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 34; }
.hl-row-tools { display: flex; align-items: center; gap: 4px; flex: none; }
.hl-row-tools .btn { display: inline-flex; align-items: center; gap: 4px; }
.hl-row-tools .btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 40; }
.hl-conf { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px; font-size: 10.5px; font-weight: 700; letter-spacing: .02em; background: var(--accent-soft); color: var(--ink-muted); }
.hl-conf.low { background: #FFF6E5; color: #7A4B00; }
[data-theme="dark"] .hl-conf.low { background: #3A2E12; color: #F5D08A; }
.hl-tick { width: 24px; height: 24px; border-radius: 12px; border: 1.5px solid var(--ink-faint); flex: none; display: flex; align-items: center; justify-content: center; background: none; padding: 0; cursor: pointer; }
.hl-tick.on { background: var(--success); border-color: var(--success); color: var(--bg); }
.hl-tick svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 54; }
.hl-tick.sq { border-radius: 6px; width: 22px; height: 22px; }
.hl-tick:not(.on) svg { visibility: hidden; }
/* Warnings: the code's own lines, with a button that hands each to the box */
.hl-warn { margin: 6px 0; padding: 10px 12px; border-radius: 12px; background: color-mix(in srgb, var(--danger) 10%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); }
.hl-warn-title { font-size: 12px; font-weight: 800; letter-spacing: .04em; color: var(--danger); margin-bottom: 4px; text-transform: uppercase; }
.hl-warn-line { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 12.5px; line-height: 17px; color: var(--ink); }
.hl-warn-line .btn { flex: none; }
.hl-note { margin: 6px 0; padding: 8px 12px; border-radius: 12px; background: var(--bg); font-size: 12.5px; color: var(--ink-muted); line-height: 18px; }
.hl-note-title { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 2px; }
/* Day tiles: a week with real dates */
.hl-days { display: flex; gap: 4px; padding: 6px 0; overflow-x: auto; scrollbar-width: none; }
.hl-days::-webkit-scrollbar { display: none; }
.hl-day { position: relative; flex: 1; min-width: 42px; border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted); border-radius: 12px; padding: 6px 2px; font-size: 12.5px; font-weight: 700; cursor: pointer; transition: transform .15s ease; }
.hl-day .small { display: block; font-weight: 500; color: var(--ink-faint); font-size: 11px; margin-top: 1px; }
.hl-day.today { border-color: var(--accent); }
.hl-day.on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.hl-day.on .small { color: var(--bg); opacity: .7; }
.hl-day.rest, .hl-day.past { opacity: .6; }
.hl-day.past.on { opacity: 1; }
.hl-day.done::after, .hl-day.skipped::after { content: '✓'; position: absolute; top: 2px; right: 5px; font-size: 9px; color: var(--success); }
.hl-day.skipped::after { content: '–'; color: var(--ink-faint); }
.hl-day.on.done::after { color: var(--bg); }
.hl-day .hl-dot { position: absolute; top: 4px; left: 5px; width: 6px; height: 6px; border-radius: 3px; background: var(--danger); }
.hl-day:active { transform: scale(.96); }
/* Building state */
.hl-skeleton { display: flex; flex-direction: column; gap: 8px; padding: 6px 0; }
.hl-skel-tiles { display: flex; gap: 4px; }
.hl-skel-tile, .hl-skel-row, .hl-skel-line { background: var(--accent-soft); border-radius: 12px; position: relative; overflow: hidden; }
.hl-skel-tile { flex: 1; height: 44px; }
.hl-skel-row { height: 40px; border-radius: 10px; }
.hl-skel-line { height: 12px; border-radius: 6px; }
.hl-skel-tile::after, .hl-skel-row::after, .hl-skel-line::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--surface) 55%, transparent), transparent); transform: translateX(-100%); animation: hl-shimmer 1.6s ease-in-out infinite; }
@keyframes hl-shimmer { to { transform: translateX(100%); } }
.hl-row.estimating .row-title { color: var(--ink-muted); }
.hl-row.estimating .hl-skel-line { width: 60%; margin-top: 5px; }
/* Plan rows, detail, prep line */
.hl-meal { border-bottom: 1px solid var(--border); }
.hl-meal:last-child { border-bottom: none; }
.hl-detail { padding: 0 6px 12px 6px; line-height: 19px; color: var(--ink-muted); }
.hl-swapping { font-size: 12.5px; color: var(--accent); }
.hl-prep { display: flex; gap: 8px; align-items: center; padding: 6px 6px 2px; font-size: 12.5px; color: var(--ink-muted); }
.hl-prep svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
.hl-sub-list { padding: 2px 0 6px 12px; border-left: 2px solid var(--border); margin: 2px 0 4px 8px; }
.hl-total { padding: 4px 6px; font-size: 12.5px; color: var(--ink-faint); }
/* Review card */
.hl-review { border-color: var(--accent-soft); }
.hl-review-line { font-size: 13.5px; line-height: 20px; color: var(--ink); padding: 1px 0; }
.hl-review .hl-actions { padding: 10px 0 0; }
/* Folds (grid trick, no measuring) */
.hl-fold { margin-top: 2px; }
.hl-fold-head { display: flex; align-items: center; gap: 6px; padding: 8px 6px; border: none; background: none; color: var(--ink-muted); font-size: 12.5px; font-weight: 700; letter-spacing: .04em; cursor: pointer; width: 100%; text-align: left; }
.hl-fold-head svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 44; transition: transform .16s; }
.hl-fold.open .hl-fold-head svg { transform: rotate(90deg); }
.hl-fold-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .32s cubic-bezier(.2,.7,.2,1); }
.hl-fold.open .hl-fold-body { grid-template-rows: 1fr; }
.hl-fold-in { min-height: 0; overflow: hidden; }
.hl-fold-empty { padding: 4px 8px 10px; font-size: 13.5px; line-height: 19px; color: var(--ink-muted); }
/* The conversation fold */
.hl-chat { display: flex; flex-direction: column; gap: 6px; padding: 4px 4px 8px; }
.hl-msg { font-size: 14px; line-height: 19px; padding: 7px 11px; border-radius: 14px; max-width: 92%; white-space: pre-wrap; }
.hl-msg.user { align-self: flex-end; background: var(--bubble-user); color: var(--bubble-user-ink); }
.hl-msg.assistant { align-self: flex-start; background: var(--accent-soft); color: var(--ink); }
/* Chips: ink when on, like every other selected chip in the app */
.hl-feel-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.hl-chip { border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted); border-radius: 999px; padding: 5px 11px; font-size: 12.5px; font-weight: 600; cursor: pointer; transition: transform .15s ease; }
.hl-chip.on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.hl-chip:active, .health-page .chip:active { transform: scale(.96); }
.health-page .chip-row { margin: 0 2px; }
.health-page .chip-row.suggest .chip { font-size: 13px; padding: 6px 12px; }
/* Quick-add inside a fold */
.health-page .quick-box { margin: 4px 4px 8px; }
/* Steppers, segments and the sheet controls */
.hl-stepper { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); overflow: hidden; flex: none; }
.hl-stepper button { width: 36px; height: 36px; border: none; background: none; color: var(--accent); font-size: 20px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.hl-stepper button:disabled { color: var(--ink-faint); cursor: default; }
.hl-stepper .v { min-width: 56px; text-align: center; font-size: 14px; font-weight: 700; color: var(--ink); padding: 0 4px; white-space: nowrap; }
.hl-ctl { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; border-top: 1px solid var(--border); }
.hl-ctl:first-child, .hl-ctl.first { border-top: none; }
.hl-ctl-label { font-size: 14.5px; font-weight: 600; color: var(--ink); flex: 1; min-width: 0; }
.hl-ctl-label .small { display: block; font-weight: 400; color: var(--ink-muted); margin-top: 1px; }
.hl-ctl.col { flex-direction: column; align-items: stretch; gap: 8px; }
.hl-ctl .plus-segment { flex: 1; }
.hl-ctl.col .plus-segment { width: 100%; flex-wrap: wrap; }
/* Six eating styles do not fit one phone-width row: a segment that owns the
   width wraps onto a second row rather than pushing Kosher off the card where
   it cannot be tapped. A segment beside its label keeps shrinking instead —
   wrapping there leaves one option alone on a line. */
.hl-ctl.col .plus-segment button { flex: 1 1 auto; white-space: nowrap; }
.hl-ctl .switch { cursor: pointer; }
.hl-ctl input[type="time"], .hl-ctl input[type="date"], .hl-ctl input[type="datetime-local"] { border: 1px solid var(--border); border-radius: 10px; background: var(--surface); color: var(--ink); padding: 6px 10px; font: inherit; font-size: 14px; }
.hl-sheet { display: flex; flex-direction: column; gap: 12px; padding-top: 12px; }
.hl-sheet .field { margin-bottom: 0; }
.hl-sheet .chip-row { margin-bottom: 0; }
.hl-sheet .hl-actions { padding: 2px 0; }
.hl-card { border-radius: 16px; border: 1px solid var(--border); background: var(--surface); padding: 4px 14px; }
.hl-card > .sp-label { padding-top: 10px; margin-bottom: 2px; }
.hl-card .field { background: var(--bg); }
.hl-hint { font-size: 12.5px; line-height: 18px; color: var(--ink-muted); padding: 4px 0 8px; }
.hl-saved { font-size: 13px; font-weight: 700; color: var(--success); transition: opacity .3s; opacity: 0; }
.hl-saved.show { opacity: 1; }
.hl-dots { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-muted); }
.hl-dots i { width: 7px; height: 7px; border-radius: 4px; background: var(--border); display: inline-block; }
.hl-dots i.on { background: var(--ink); }
.hl-dots i.done { background: var(--accent); }
.hl-step-title { font-size: 21px; font-weight: 700; color: var(--ink); letter-spacing: -0.2px; }
.hl-step-copy { font-size: 14.5px; line-height: 21px; color: var(--ink-muted); }
.hl-sheet-foot { display: flex; align-items: center; gap: 8px; padding-top: 6px; }
.hl-photo { width: 120px; height: 120px; border-radius: 14px; background: var(--accent-soft) center/cover no-repeat; flex: none; cursor: pointer; }
.hl-photo-large { width: 100%; max-height: 70vh; object-fit: contain; border-radius: 14px; background: var(--bg); }
.hl-items { display: flex; flex-direction: column; gap: 4px; }
/* Name, then the free-text quantity ("3 piece", "1 tbsp"), then the numbers.
   The quantity is words, so it gets the wider share of what is left after the
   fixed number columns; at 320 px the whole row still fits. */
.hl-item-head, .hl-item-row { display: grid; grid-template-columns: minmax(0, 1.9fr) minmax(0, 1.4fr) 48px 36px 36px 36px 24px; gap: 4px; align-items: center; }
.hl-item-head { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; color: var(--ink-faint); text-transform: uppercase; padding: 0 2px; }
.hl-item-head span:nth-child(n+3) { text-align: right; }
.hl-item-row input { width: 100%; min-width: 0; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--ink); padding: 6px 6px; font: inherit; font-size: 13px; }
.hl-item-row input[type="number"] { text-align: right; padding-right: 4px; -moz-appearance: textfield; }
.hl-item-row input::-webkit-outer-spin-button, .hl-item-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.hl-item-row .icon-btn { width: 26px; }
.hl-item-row .icon-btn svg { width: 16px; height: 16px; }
.hl-estimate { font-size: 12.5px; color: var(--ink-muted); font-style: italic; }
/* Grocery sheet */
.hl-grocery-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding: 10px 0 2px; font-size: 13.5px; color: var(--ink-muted); }
.hl-grocery-head b { color: var(--ink); font-size: 17px; font-weight: 700; }
.hl-aisle { padding: 12px 4px 4px; font-size: 11.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); }
.hl-aisle-row { display: flex; align-items: center; gap: 10px; padding: 7px 4px; border-radius: 10px; }
.hl-aisle-row.ticked .row-title, .hl-aisle-row.have .row-title { text-decoration: line-through; color: var(--ink-faint); }
.hl-aisle-row.have { opacity: .5; }
.hl-aisle-row .row-title { font-size: 14.5px; font-weight: 500; }
.hl-aisle-row .row-sub { font-size: 12px; }
.hl-aisle-row .hl-cost { font-size: 12.5px; color: var(--ink-muted); flex: none; }
.hl-aisle-row .row-more { border: none; background: none; cursor: pointer; padding: 0 4px; }
/* Sessions (stage 2 draws these; the classes are here so both engineers share one sheet of numbers) */
.hl-session { border-radius: 16px; border: 1px solid var(--border); background: var(--surface); padding: 14px; }
.hl-session.open { border-color: var(--ink); }
.hl-ex { padding: 10px 0; border-top: 1px solid var(--border); }
.hl-ex:first-of-type { border-top: none; }
.hl-ex-title { display: flex; align-items: center; gap: 8px; font-size: 15.5px; font-weight: 600; color: var(--ink); }
.hl-ex-pill { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); }
.hl-ex-cue { font-size: 12.5px; color: var(--ink-muted); line-height: 17px; margin-top: 2px; }
.hl-ex-target { font-size: 13px; color: var(--ink); margin-top: 3px; }
.hl-ex-last { font-size: 12px; color: var(--ink-faint); }
.hl-set-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; flex-wrap: wrap; }
.hl-set-row .hl-set-n { font-size: 12.5px; font-weight: 700; color: var(--ink-muted); width: 44px; flex: none; }
.hl-set-row .hl-set-prev { font-size: 11.5px; color: var(--ink-faint); flex: 1; min-width: 92px; }
.hl-set-row.done { opacity: .75; }
.hl-rest { display: flex; align-items: center; gap: 10px; margin-top: 6px; padding: 8px 10px; border-radius: 10px; background: var(--accent-soft); color: var(--accent); font-size: 13px; font-weight: 700; position: relative; overflow: hidden; }
.hl-rest-bar { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); transition: width 1s linear; }
/* Only the first of the pair takes the free space, so +15 s and Skip sit
   together at the end of the bar rather than splitting it between them. */
.hl-rest .btn:first-of-type { margin-left: auto; }
.hl-elapsed { font-variant-numeric: tabular-nums; }
/* Charts */
.hl-chart { padding: 6px 0; }
.hl-chart svg { width: 100%; height: auto; display: block; }
/* The chart parts carry hl- names of their own: a bare `.bar` is already a
   7 px div elsewhere in this sheet, and a CSS height beats a rect's height
   attribute, which flattened every bar to a hairline. */
.hl-chart .hl-axis { stroke: var(--ink-faint); stroke-width: 1; }
.hl-chart .hl-dot { fill: var(--ink-faint); opacity: .6; }
.hl-chart .hl-trend { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.hl-chart .hl-bar { fill: var(--accent); }
.hl-chart .hl-bar.over { fill: #F59E3C; }
.hl-chart .hl-bar.faint { fill: var(--accent-soft); }
.hl-chart .hl-lbl { fill: var(--ink-faint); font-size: 9px; }
/* Versions */
.hl-version-row { display: flex; align-items: center; gap: 10px; padding: 8px 6px; border-radius: 12px; width: 100%; text-align: left; border: none; background: none; font: inherit; color: var(--ink); cursor: pointer; }
.hl-version-row:hover { background: var(--surface); }
.hl-version-row .row-sub { font-size: 12.5px; }
.hl-current { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--accent); }
/* New rows rise in, like the map's cards; a tappable toast needs a finger to land */
.hl-in { animation: nav-card-in .35s cubic-bezier(.2,.7,.2,1) both; }
#toast.tap { pointer-events: auto; cursor: pointer; }
@media (max-width: 360px) { .hl-actions.log .btn { flex-basis: 45%; } }
@media (prefers-reduced-motion: reduce) {
  .hl-strip-bar::after, .hl-skel-tile::after, .hl-skel-row::after, .hl-skel-line::after, .hl-in { animation: none; }
  .hl-fold-body, .hl-fold-head svg, .hl-day, .hl-chip, .health-page .chip, .hl-hero .bar-fill, .hl-macro-bar > div, .hl-rest-bar, .hl-saved { transition: none; }
}
/* Move half (stage 2): the session card's head, exercise tools, the draft chip, chart captions */
.hl-session-body { display: block; }
.hl-session-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-bottom: 6px; }
.hl-session-head .small { font-weight: 500; }
.hl-ex-name { border: none; background: none; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; }
.hl-ex-name:hover { color: var(--accent); }
.hl-ex-tools { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.hl-ex-tools .btn { display: inline-flex; align-items: center; gap: 4px; }
.hl-ex-tools .btn svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 40; }
.hl-ex.skipped .hl-ex-cue, .hl-ex.skipped .hl-ex-target { opacity: .5; }
.hl-rest-slot { flex-basis: 100%; }
.hl-rest svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
.hl-set-row .hl-tick { margin-left: auto; }
/* A set row is Set n · prev · weight · reps · tick; the value only has to hold
   "6 reps" or "100 kg" at rest and may grow past it, so the whole row fits on
   one line where there is room and wraps the tick where there is not. */
.hl-set-row .hl-stepper .v { min-width: 52px; font-size: 13px; }
.hl-draft { display: flex; align-items: center; gap: 8px; margin: 6px 0; padding: 8px 10px; border-radius: 12px; background: var(--accent-soft); color: var(--ink); font-size: 13px; }
.hl-draft svg { width: 15px; height: 15px; stroke: var(--accent); fill: none; stroke-width: 34; flex: none; }
.hl-week-line { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: -4px; }
.hl-chart-cap { font-size: 12px; line-height: 17px; color: var(--ink-faint); padding: 2px 0 6px; }
.hl-stat-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 6px 0 4px; }
.hl-stat-row .hl-hero-num { font-size: 26px; }
.health-page .sp-sub .btn.sm { padding: 4px 10px; }
/* A button in a card head carries its icon at text size — without this an
   inline svg takes the whole column ("Something else" on the Recent card). */
.health-page .sp-sub .btn { display: inline-flex; align-items: center; gap: 5px; flex: none; }
.health-page .sp-sub .btn svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 34; flex: none; }
/* The prescription under an exercise ("3×12 @ 12 kg → 3 × 8–12 · hit 12, 12,
   12") is a sentence, not a label: it wraps rather than being cut mid-word. */
.hl-row .row-sub .hl-ex-last { white-space: normal; }
@media (prefers-reduced-motion: reduce) { .hl-draft, .hl-ex-name { transition: none; } }

/* ---- status: the four states, a line of your own, and how long it lasts ---- */
.status-block { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.status-states { display: flex; flex-wrap: wrap; gap: 6px; }
.status-state {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 11px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted);
  font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.status-state.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
/* The dot inside a button is a swatch, not a badge on a face. */
.presence-dot.static { position: static; border: none; width: 10px; height: 10px; flex: none; }
.status-durations { display: flex; flex-wrap: wrap; gap: 6px; }
.status-durations .chip {
  padding: 6px 11px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink-muted); font-size: 13px; font-weight: 600; cursor: pointer;
}
.status-durations .chip.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }

/* ===========================================================================
   The backdrop (components/Backdrop.tsx)
   ---------------------------------------------------------------------------
   The living background behind the page: a wash between two colours close to
   the page, a few soft shapes drifting over it, stars where a theme has them.
   theme.js paints the colours and the per-shape timings from the resolved
   theme; these rules only place the pieces and move them. It sits under
   everything — a negative z-index in the root context paints above the canvas
   and under the whole document — and takes no taps. Text stays the brightest
   thing on screen: the backdrop is a wash, never a picture.
   =========================================================================== */
.backdrop {
  position: absolute; inset: 0; z-index: -1;
  overflow: hidden; pointer-events: none; contain: layout paint;
}
.backdrop.page { position: fixed; }
.backdrop .blob {
  position: absolute;
  left: var(--x); top: var(--y);
  /* r is a fraction of the width, as on the phone. On a wide window the shape
     stops growing at 960px so it stays a wash rather than a flood. */
  width: calc(var(--r) * 2 * min(100%, 960px));
  aspect-ratio: 1;
  margin-left: calc(var(--r) * -1 * min(100%, 960px));
  margin-top: calc(var(--r) * -1 * min(100%, 960px));
  border-radius: 50%;
}
/* How a theme moves: wander, swell, or sweep like a curtain. The speed and the
   distance come from theme.js (--dur, --dx, --dy; calm is slow, lively less so). */
.backdrop.style-drift .blob { animation: bd-drift var(--dur, 70s) ease-in-out infinite; animation-delay: var(--delay, 0s); }
.backdrop.style-breathe .blob { animation: bd-breathe var(--dur, 14s) ease-in-out infinite; animation-delay: var(--delay, 0s); }
.backdrop.style-aurora .blob { transform: scale(1.5, 0.75); animation: bd-aurora var(--dur, 36s) ease-in-out infinite; animation-delay: var(--delay, 0s); }
.backdrop.motion-off .blob, .backdrop.motion-off .stars i { animation: none; }
@keyframes bd-drift {
  0% { transform: translate(0, 0) scale(1); }
  33% { transform: translate(var(--dx, 4vw), var(--dy, -3vh)) scale(1.06); }
  66% { transform: translate(calc(var(--dx, 4vw) * -0.6), calc(var(--dy, -3vh) * 0.7)) scale(0.96); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes bd-breathe {
  0%, 100% { transform: scale(0.9); opacity: 0.7; }
  50% { transform: scale(1.1); opacity: 1; }
}
@keyframes bd-aurora {
  0%, 100% { transform: translateX(calc(var(--dx, 4vw) * -1)) scale(1.5, 0.75); }
  50% { transform: translateX(var(--dx, 4vw)) scale(1.9, 0.65); }
}
.backdrop .stars { position: absolute; inset: 0; }
.backdrop .stars i {
  position: absolute; display: block; border-radius: 50%; background: currentColor; opacity: 0.6;
  animation: bd-twinkle var(--tw, 3s) ease-in-out infinite alternate; animation-delay: var(--delay, 0s);
}
@keyframes bd-twinkle { from { opacity: 0.15; transform: scale(0.8); } to { opacity: 0.95; transform: scale(1.2); } }
@media (prefers-reduced-motion: reduce) { .backdrop .blob, .backdrop .stars i { animation: none !important; } }
/* The Settings window covers the page and carries its own backdrop; the one
   underneath holds still rather than animating unseen. */
body:has(> .settings-window) > .backdrop .blob, body:has(> .settings-window) > .backdrop .stars i { animation-play-state: paused; }

/* ---- Theme Studio (components/ThemeStudio.tsx) ----
   Appearance, the theme gallery and the tune — the same studio as the phone's,
   at the phone's sizes. Every gallery card is a live miniature of its theme:
   its own backdrop, drawn with the same maths as the page's, under its name,
   its blurb and its three colours. */
.studio-gallery { display: flex; align-items: stretch; gap: 10px; overflow-x: auto; scrollbar-width: none; padding: 3px 4px 8px; margin: 0 -4px; }
.studio-gallery::-webkit-scrollbar { display: none; }
.studio-group { flex: none; display: flex; align-items: center; justify-content: center; width: 18px; }
.studio-group span {
  font-size: 10px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; color: var(--ink-faint);
  writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap;
}
.studio-card {
  position: relative; isolation: isolate; flex: none; width: 150px; height: 118px;
  border-radius: 16px; border: 1px solid var(--border); overflow: hidden;
  padding: 10px 11px; text-align: left; display: flex; flex-direction: column; gap: 4px;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.studio-card:hover { transform: translateY(-1px); }
.studio-card.active { border-color: transparent; box-shadow: 0 0 0 2px var(--accent); }
.studio-card > .backdrop { position: absolute; inset: 0; z-index: -1; }
.studio-card-head { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; line-height: 18px; white-space: nowrap; overflow: hidden; }
.studio-card-name { white-space: normal; overflow-wrap: break-word; }
.studio-card-blurb { font-size: 11.5px; line-height: 15px; max-height: 45px; display: block; overflow-wrap: break-word; }
.studio-card-foot { display: flex; align-items: center; gap: 5px; margin-top: auto; }
.studio-dot { width: 10px; height: 10px; border-radius: 5px; flex: none; }
.studio-mini-bubble { margin-left: auto; width: 34px; height: 14px; border-radius: 7px 7px 3px 7px; }

/* The tune: one card of rows, each a label over a control. Segmented controls
   are the page's .tab row, so they look like the Appearance row above them. */
.studio-tune { display: flex; flex-direction: column; }
.studio-row { padding: 10px 0; border-top: 1px solid var(--border); }
.studio-row:first-child { padding-top: 0; border-top: none; }
.studio-row-head { display: flex; align-items: center; justify-content: space-between; min-height: 22px; font-size: 14px; font-weight: 600; color: var(--ink); }
.studio-row-head + * { margin-top: 8px; }
.studio-seg .tab { font-size: 12px; line-height: 16px; padding: 8px 2px; }
.studio-swatches { display: grid; grid-template-columns: repeat(12, minmax(0, 28px)); justify-content: space-between; gap: 6px; }
.studio-swatch {
  position: relative; width: 100%; aspect-ratio: 1; border-radius: 50%; padding: 0;
  border: 2px solid transparent; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
  transition: transform 0.12s ease;
}
.studio-swatch:hover { transform: scale(1.08); }
.studio-swatch.on { border-color: var(--ink); box-shadow: inset 0 0 0 2px var(--bg); }
/* The first swatch is the theme as designed; a pip says so. */
.studio-swatch.designed::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; margin: -3px 0 0 -3px;
  border-radius: 3px; background: rgba(255, 255, 255, 0.85);
}
.studio-range { display: block; width: 100%; margin-top: 10px; accent-color: var(--accent); }
.studio-hint { font-size: 12px; line-height: 17px; color: var(--ink-faint); margin-top: 8px; }
.studio-row-head + .studio-hint { margin-top: 4px; }
.studio-foot { display: flex; gap: 8px; padding-top: 12px; border-top: 1px solid var(--border); }
.studio-foot .btn { flex: 1; }
.studio-sync { text-align: center; }

/* ---- offline ----
   One line across the top while requests are not arriving, and the card a
   page shows when it has nothing kept to draw. Both are states, not errors,
   so neither is red. */
.offline-bar {
  position: fixed; left: 0; right: 0; top: 0; z-index: 60;
  /* One line, always (app.js keeps its words short): 28px under the status
     bar, which is what the shell steps down by below. */
  box-sizing: border-box; height: calc(28px + env(safe-area-inset-top));
  padding: calc(5px + env(safe-area-inset-top)) 14px 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; /* fixed: a 28px strip; app.js keeps its words short enough to fit a 320px phone */
  background: var(--accent-soft, var(--surface)); color: var(--accent, var(--ink));
  border-bottom: 1px solid var(--border);
  font-size: 12.5px; line-height: 17px; font-weight: 700; text-align: center;
}
.offline-bar[hidden] { display: none; }
/* While the line shows, the shell starts under it — it used to sit on the
   rail of places and a page's own top row (its tabs, its back button), so
   offline was the one time the app could not be moved around in.
   (Not under a sheet that puts the line away — styles.css st-sheet, the alarm sheet.) */
body:not(.st-sheet):not(:has(.alarm-sheet)) #root:has(> .offline-bar:not([hidden])) > .app-shell { margin-top: 28px; height: calc(var(--app-height, 100dvh) - 28px); }
/* The desktop card already floats 20px down (above): there the gap grows to clear the line, and the card gives up the same. */
@media (min-width: 720px) and (min-height: 640px) {
  body:not(.st-sheet):not(:has(.alarm-sheet)) #root:has(> .offline-bar:not([hidden])) { padding-top: 36px; }
  body:not(.st-sheet):not(:has(.alarm-sheet)) #root:has(> .offline-bar:not([hidden])) > .app-shell { margin-top: 0; height: calc(var(--app-height, 100dvh) - 56px); }
}
.offline-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.offline-empty svg { width: 34px; height: 34px; stroke: var(--ink-faint, var(--ink-muted)); fill: none; stroke-width: 26; }
.offline-empty .faint { color: var(--ink-faint, var(--ink-muted)); font-size: 13px; }

/* ---- Settings: the root list, and the header of a page ----
   Identity first, a search box, then cards of rows that each open a page
   (server/web/pages/settings.js; the map is server/web/settingsMap.mjs). */
.set-head { display: flex; align-items: center; gap: 4px; padding-bottom: 8px; padding-left: 44px; }
.set-head .set-back { width: 36px; height: 36px; border-radius: 18px; border: 0; background: none; display: flex; align-items: center; justify-content: center; margin-left: -8px; cursor: pointer; color: var(--ink); }
.set-head .set-back svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 40; }
.set-head .set-title { flex: 1; font-size: 21px; font-weight: 800; letter-spacing: -0.2px; color: var(--ink); }
.set-search { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); background: var(--surface); border-radius: 12px; padding: 9px 12px; margin-bottom: 12px; }
.set-search svg { width: 17px; height: 17px; stroke: var(--ink-faint); fill: none; stroke-width: 40; flex: none; }
.set-search input { flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: 15.5px; color: var(--ink); outline: none; padding: 0; }
.set-identity { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; border: 1px solid var(--border); background: var(--surface); border-radius: 16px; padding: 14px; cursor: pointer; font: inherit; color: var(--ink); }
.set-identity .head-face { width: 44px; height: 44px; font-size: 18px; flex: none; }
.set-identity .set-idname { font-size: 16.5px; font-weight: 700; }
.set-identity > .grow { min-width: 0; }
.set-identity .set-idsub { font-size: 13px; color: var(--ink-muted); margin-top: 2px; overflow-wrap: anywhere; }
.set-rootcard { border: 1px solid var(--border); background: var(--surface); border-radius: 16px; overflow: hidden; }
.set-rootrow { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; padding: 12px 14px; border: 0; background: none; font: inherit; color: var(--ink); cursor: pointer; }
.set-rootrow + .set-rootrow { border-top: 1px solid var(--border); }
.set-rootrow:hover { background: var(--accent-soft); }
.set-rootrow .set-rowicon { width: 30px; height: 30px; border-radius: 9px; background: var(--accent-soft); display: flex; align-items: center; justify-content: center; flex: none; }
.set-rootrow .set-rowicon svg { width: 17px; height: 17px; stroke: var(--accent); fill: none; stroke-width: 36; }
.set-rootrow .set-rowtext { flex: 1; min-width: 0; }
.set-rootrow .set-rowtitle { font-size: 15.5px; font-weight: 600; }
.set-rootrow .set-rowsub { font-size: 12.5px; color: var(--ink-faint); margin-top: 1px; white-space: normal; overflow-wrap: break-word; }
.set-rootrow .set-rowvalue { font-size: 13.5px; color: var(--ink-muted); max-width: 120px; white-space: normal; overflow-wrap: break-word; }
.set-rootrow .set-chev { width: 16px; height: 16px; stroke: var(--ink-faint); fill: none; stroke-width: 40; flex: none; }
.set-identity .set-chev { width: 17px; height: 17px; stroke: var(--ink-faint); fill: none; stroke-width: 40; flex: none; }
/* The update button, first on Settings' root (19 Oct 2026; pages/settings.js): a card on its lip, the pill at its end. */
.set-update { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; margin-bottom: 12px; padding: 12px 14px; border: 1px solid var(--border); border-bottom-width: 3px; border-radius: 16px; background: var(--surface); font: inherit; color: var(--ink); cursor: pointer; }
.set-update[hidden] { display: none; }
.set-update:hover { background: color-mix(in srgb, var(--surface) 88%, var(--accent) 8%); }
.set-update:active { transform: translateY(1.5px); border-bottom-width: 1.5px; }
.set-update:disabled { cursor: default; }
.set-update .set-rowicon { width: 36px; height: 36px; border-radius: 11px; background: var(--accent-soft); display: flex; align-items: center; justify-content: center; flex: none; }
.set-update .set-rowicon svg { width: 20px; height: 20px; stroke: var(--accent); fill: none; stroke-width: 36; }
.set-update .set-rowtext { flex: 1; min-width: 0; }
.set-update .set-rowtitle { font-size: 15.5px; font-weight: 600; white-space: normal; overflow-wrap: break-word; }
.set-update .set-rowsub { font-size: 12.5px; color: var(--ink-faint); margin-top: 1px; white-space: normal; overflow-wrap: break-word; }
.set-update-go { flex: none; min-height: 34px; padding: 0 14px; border-radius: 17px; display: inline-flex; align-items: center; background: var(--accent); color: var(--on-accent, #fff); font-size: 14px; font-weight: 800; }
.set-update.waiting { border-color: var(--accent); }
.set-update.waiting .set-rowicon { background: var(--accent); }
.set-update.waiting .set-rowicon svg { stroke: var(--on-accent, #fff); }
.set-update.waiting .set-rowsub { color: var(--accent); }
.set-gap { height: 10px; }
.set-link { display: block; padding: 10px 0; color: var(--ink); text-decoration: none; font-size: 15px; font-weight: 500; }
.set-link + .set-link { border-top: 1px solid var(--border); }

/* Passwords' styles are server/web/passwords.css since 13 Oct 2026. Two rules
   it carried are the whole app's, and stay here: an icon in a .btn, and .mono. */
.btn svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 34; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* ===========================================================================
   App polish (October 2026): the shell's half of the list. The phone's twin
   of each rule is named beside it.
   =========================================================================== */

/* ---- moving between pages (the phone's pager slide) ----
   app.js changePage wraps a page change in a View Transition. Only the page
   has a name, so the rail and the box are not part of it: the root swaps at
   once and the page fades out, then rises in 6px. A browser without the API
   runs the same rise on the new page (.enter). Reduced motion: no transition
   is started at all. */
.app-shell > .screen { view-transition-name: screen; }
/* A profile switch (app.js onPersonaChange): the whole shell — rail, places,
   theme and page — dips: the old goes out quickly, and only once it is gone
   does the new come in, a little slower — one move, never two pages drawn
   over each other (UI review, 21 Oct 2026: two "Wednesday 21 Oct" titles for
   a moment once the profile coin became a toggle). */
:root[data-swap="persona"]::view-transition-old(root),
:root[data-swap="persona"]::view-transition-old(screen) { animation: 110ms ease-in both vt-swap-out; }
:root[data-swap="persona"]::view-transition-new(root),
:root[data-swap="persona"]::view-transition-new(screen) { animation: 170ms cubic-bezier(.2, .8, .2, 1) 110ms both vt-swap-in; }
@keyframes vt-swap-out { to { opacity: 0; } }
@keyframes vt-swap-in { from { opacity: 0; } }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
::view-transition-old(screen) { animation: 120ms ease-out both vt-out; }
::view-transition-new(screen) { animation: 180ms cubic-bezier(.2, .8, .2, 1) both vt-in; }
.app-shell > .screen.enter { animation: 180ms cubic-bezier(.2, .8, .2, 1) both vt-in; }
@keyframes vt-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* Along the row (app.js rowOrder): the page you go to comes in from its own
   side and the one you leave goes out the other way — the phone's rule for
   its layers and its pager (lib/pagerLayer.ts slideDir). */
:root[data-slide="right"]::view-transition-old(screen) { animation: 150ms ease-in both vt-out-left; }
:root[data-slide="right"]::view-transition-new(screen), :root[data-slide="right"] .app-shell > .screen.enter { animation: 230ms cubic-bezier(.2, .8, .2, 1) both vt-in-right; }
:root[data-slide="left"]::view-transition-old(screen) { animation: 150ms ease-in both vt-out-right; }
:root[data-slide="left"]::view-transition-new(screen), :root[data-slide="left"] .app-shell > .screen.enter { animation: 230ms cubic-bezier(.2, .8, .2, 1) both vt-in-left; }
@keyframes vt-in-right { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: none; } }
@keyframes vt-in-left { from { opacity: 0; transform: translateX(-28px); } to { opacity: 1; transform: none; } }
@keyframes vt-out-left { from { opacity: 1; } to { opacity: 0; transform: translateX(-28px); } }
@keyframes vt-out-right { from { opacity: 1; } to { opacity: 0; transform: translateX(28px); } }
/* A pop-up (ShmangNav, the browser, Messages, the pinned page — 14 Oct
   2026): a quick pop over the page, which stays where it is — in from a few
   pixels below in 180ms, out in 130ms; one pop-up straight onto another
   cross-fades. The phone's lib/popMotion.ts, the same numbers ("not a slow
   annoying drag thingy"). */
:root[data-slide="rise"]::view-transition-old(screen) { animation: none; }
:root[data-slide="rise"]::view-transition-new(screen), :root[data-slide="rise"] .app-shell > .screen.enter { animation: 180ms cubic-bezier(.33, 1, .68, 1) both vt-pop-in; }
:root[data-slide="drop"]::view-transition-old(screen) { animation: 130ms cubic-bezier(.25, .46, .45, .94) both vt-pop-out; z-index: 1; }
:root[data-slide="drop"]::view-transition-new(screen) { animation: none; }
:root[data-slide="swap"]::view-transition-old(screen) { animation: 130ms cubic-bezier(.25, .46, .45, .94) both vt-pop-out; }
:root[data-slide="swap"]::view-transition-new(screen), :root[data-slide="swap"] .app-shell > .screen.enter { animation: 180ms cubic-bezier(.33, 1, .68, 1) both vt-pop-in; }
@keyframes vt-pop-in { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@keyframes vt-pop-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(24px); } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(screen), ::view-transition-new(screen), .app-shell > .screen.enter { animation: none; }
}

/* ---- soft edges (the phone's fades over the page area) ----
   What scrolls under the rail and down to the box fades out rather than being
   cut by a straight line. On the phone the page area starts under the band;
   here the page runs to the top of the window and the rail's buttons float
   over it, so the page's top edge is the rail's bottom (44px): what scrolls up
   fades out over the 20px above that and is gone behind the buttons, instead
   of showing between them. Every page starts below 44px at rest. At the foot,
   18px above the box. Not on the map, which is a picture to the edge; and no
   bottom fade where the page's own box sits at the foot of the page (a
   conversation, a Claude chat, Study's Ask), so that box stays solid. */
.app-shell > .screen {
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(env(safe-area-inset-top) + 24px), #000 calc(env(safe-area-inset-top) + 44px), #000 calc(100% - 18px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent calc(env(safe-area-inset-top) + 24px), #000 calc(env(safe-area-inset-top) + 44px), #000 calc(100% - 18px), transparent 100%);
}
body.in-thread .app-shell > .screen,
body.st-own-bar .app-shell > .screen,
body[data-page='panes'] .app-shell > .screen {
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(env(safe-area-inset-top) + 24px), #000 calc(env(safe-area-inset-top) + 44px));
  mask-image: linear-gradient(to bottom, transparent calc(env(safe-area-inset-top) + 24px), #000 calc(env(safe-area-inset-top) + 44px));
}
/* Pages whose own title row sat at the very top, under the rail's buttons
   (Drive, Items, Contacts: StuffPage's header; Spending's), start below the
   rail like every other page. */
.app-shell > .screen > .page-head:first-child,
.app-shell > .screen > .screen-scroll > .sp-body,
.app-shell > .screen > .screen-scroll > .set-body { padding-top: calc(52px + env(safe-area-inset-top)); }
/* Study's focus mode hides the rail: nothing floats over the page to fade under. */
body.st-focus .app-shell > .screen { -webkit-mask-image: none; mask-image: none; }
/* A pushed page (Messages, Profiles) has no rail floating over it: its own
   header is the top, and is not faded. Profiles keeps the bottom fade;
   Messages has its own box at the foot, so it has none. */
body[data-page='profiles'] .app-shell > .screen {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent 100%);
}
body[data-page='nav'] .app-shell > .screen,
body[data-page='messages'] .app-shell > .screen,
body[data-page='cowork'] .app-shell > .screen { -webkit-mask-image: none; mask-image: none; }
/* A list of a page's own that scrolls under its head row (Mail's list under
   the search, the settings sheet's body) fades at its own top once scrolled
   (app.js sets .scrolled), instead of being sliced flat under the head. One
   that starts at the top of the page has this edge inside the page's own
   fade above, where it changes nothing. */
.screen-scroll.scrolled {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 14px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 14px);
}

/* ---- Settings, as a sheet on a phone-sized window (the phone's sheet) ----
   Over 700px it stays the centred window above. Under it, it rises from the
   bottom with the page dimmed behind and a strip of it still showing at the
   top; a tap on the dim, a drag down from the top, Back at the root, or
   Escape closes it, and you are where you were. */
.settings-scrim { display: none; }
.settings-grab { display: none; }
.settings-close { display: none; }
@media (max-width: 699.98px) {
  .settings-scrim {
    display: block; position: fixed; inset: 0; z-index: 59;
    background: rgba(0, 0, 0, 0.35);
    animation: panel-fade 280ms ease-out both;
  }
  /* The phone's sheet stops at 88% of the page: the screen you came from
     still shows above it, so it reads as a sheet over it, not a page. */
  .settings-window {
    top: calc(env(safe-area-inset-top) + 9vh);
    padding-top: 0;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.18);
    animation: settings-rise 280ms cubic-bezier(.2, .8, .2, 1) both;
  }
  .settings-window.closing { animation: settings-fall 200ms cubic-bezier(.4, 0, 1, 1) both; }
  .settings-scrim.closing { animation: panel-fade 200ms ease-in reverse both; }
  .settings-window .settings-back { top: 12px; }
  .settings-window .settings-body .set-body { padding-top: 56px; }
  /* One chrome with the phone's sheet: the handle and a round X at the top
     right; at the root no back and no title, inside a page the back and the
     page's title. */
  .settings-window .settings-close {
    display: flex; align-items: center; justify-content: center;
    position: absolute; top: 8px; right: 12px; z-index: 2;
    width: 28px; height: 28px; border-radius: 14px; border: none; padding: 0;
    background: var(--surface); color: var(--ink-muted);
  }
  .settings-window .settings-close svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 40; }
  .settings-window .settings-body.at-root ~ .settings-back { display: none; }
  .settings-window .settings-body.at-root .set-head { display: none; }
  .settings-window .settings-body.at-root .set-body { padding-top: 44px; }
  /* The handle: a 40x5 line in a zone across the top that drags the sheet. */
  .settings-grab {
    display: block; position: absolute; top: 0; left: 64px; right: 64px; height: 30px; z-index: 1;
    touch-action: none; cursor: grab;
  }
  .settings-grab::before {
    content: ''; display: block; width: 40px; height: 5px; border-radius: 3px;
    margin: 7px auto 0; background: var(--border);
  }
}
@keyframes settings-rise { from { transform: translateY(100%); } to { transform: none; } }
@keyframes settings-fall { from { transform: none; } to { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) {
  .settings-window, .settings-window.closing, .settings-scrim, .settings-scrim.closing { animation: none; }
}

/* ---- the keyboard, pre-applied (app.js focusin) ----
   The one height change that runs before iOS reports the keyboard eases like
   the keys do; every other height change stays instant. */
.app-shell.kb-pre { transition: height 250ms cubic-bezier(.2, .8, .2, 1); }
@media (prefers-reduced-motion: reduce) { .app-shell.kb-pre { transition: none; } }

/* ---- an empty box does not scroll (the phone's scrollEnabled) ----
   Any composer textarea's wrapper carries .composer-empty while it has no
   text, so a drag there is a drag, not a scroll of nothing. */
.composer-empty textarea { overflow-y: hidden; }

/* ---- the splash line (the phone's line under the box) ---- */
.cmd-wrap { position: relative; }
/* In the row above the box it takes whatever the two corner buttons leave,
   right-aligned, so a suggestion is cut only when it truly does not fit. */
.cmd-splash {
  flex: 1 1 0; min-width: 0;
  /* The row is 30px tall: the padding makes the whole height the tap, the
     negative margin keeps the row from growing. */
  border: 0; background: none; padding: 7px 0; margin: -7px 0 -7px 10px;
  font: inherit; font-size: 12px; line-height: 16px; color: var(--ink-faint);
  text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer;
}
.cmd-splash[hidden] { display: none; }
/* The hello in the row, where one line is too short for it: two smaller lines
   inside the row's 30px, never a third (app.js fitSplash measures the cap). */
.cmd-splash.two {
  white-space: normal; text-wrap: balance; font-size: 11px; line-height: 13px;
  max-height: 30px; padding: 2px 0; margin: -2px 0 -2px 10px;
}
.cmd-splash[data-kind='try']:hover { color: var(--ink-muted); }
/* Under the bar, inside its bottom margin, when that margin has room. */
.cmd-splash.under {
  position: absolute; left: 24px; right: 24px; max-width: none; margin: 0; padding: 0;
  bottom: calc(env(safe-area-inset-bottom) - 8px);
  font-size: 11px; line-height: 14px; text-align: center;
}

/* ---- mail: the inbox's piles, a receipt's amount, and Clean up (pages/mail-tidy.js) ---- */
.mail-piles { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; padding: 8px 0 2px; margin: 0 -2px; -webkit-overflow-scrolling: touch; }
.mail-piles::-webkit-scrollbar { display: none; }
/* A pile: an outlined chip; the one shown is filled in the accent's soft tint, no outline. */
.mail-pile { flex: none; display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px; border-radius: 16px; border: 1px solid var(--border); background: transparent; color: var(--ink-muted); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; }
.mail-pile:hover { background: var(--surface); }
.mail-pile.on { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.mail-pile .n { font-size: 11.5px; font-weight: 700; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; display: inline-flex; align-items: center; justify-content: center; background: var(--accent-soft); color: var(--accent); font-variant-numeric: tabular-nums; }
.mail-pile.on .n { background: var(--accent); color: var(--bg); }
.mail-amt { flex: none; font-size: 12.5px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; padding: 1px 7px; border-radius: 8px; background: var(--accent-soft); }
.mail-amt.refund { color: var(--success); }
.mail-tidy-btn svg { stroke: var(--accent); }

.tidy { display: flex; flex-direction: column; gap: 10px; padding-top: 12px; }
.tidy-loading { padding: 40px 0; text-align: center; color: var(--ink-faint); font-size: 14px; }
.tidy-card { border: 1px solid var(--border); border-radius: 16px; background: var(--surface); padding: 14px; }
.tidy-summary { display: flex; flex-direction: column; gap: 2px; }
.tidy-big { font-size: 34px; font-weight: 700; letter-spacing: -0.5px; font-variant-numeric: tabular-nums; color: var(--ink); line-height: 1.1; }
.tidy-sub { font-size: 14px; color: var(--ink-muted); }
.tidy-piles { font-size: 12.5px; color: var(--ink-faint); margin-top: 6px; line-height: 18px; }
.tidy-job { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.tidy-job-text { font-size: 12.5px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.tidy-label { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-muted); margin: 8px 4px -2px; }
.tidy-suggest { display: flex; align-items: center; gap: 12px; }
.tidy-suggest-text { flex: 1; min-width: 0; }
.tidy-suggest-title { font-size: 15px; font-weight: 650; color: var(--ink); }
.tidy-suggest-detail { font-size: 12.5px; line-height: 17px; color: var(--ink-faint); margin-top: 2px; }
.tidy-pill { flex: none; min-height: 34px; padding: 0 14px; border-radius: 17px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font: inherit; font-size: 13.5px; font-weight: 650; cursor: pointer; white-space: nowrap; }
.tidy-pill.strong { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.tidy-pill:disabled { opacity: 0.55; cursor: default; }
.tidy-list { padding: 4px 0; }
.tidy-sender, .tidy-rule { display: flex; align-items: center; gap: 12px; padding: 10px 14px; cursor: pointer; }
.tidy-sender + .tidy-sender, .tidy-rule + .tidy-rule { border-top: 1px solid var(--border); }
.tidy-sender:hover { background: var(--tint); }
.tidy-sender .avatar { width: 36px; height: 36px; border-radius: 18px; flex: none; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 15px; }
.tidy-sender-body { flex: 1; min-width: 0; }
.tidy-sender-name { font-size: 14.5px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tidy-sender-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; font-variant-numeric: tabular-nums; }
.tidy-chip { font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 8px; background: var(--accent-soft); color: var(--accent); }
.tidy-chip.rule { background: var(--tint); color: var(--ink-muted); }
.tidy-menu-sub { font-size: 12.5px; color: var(--ink-faint); margin: 8px 0 4px; word-break: break-word; }
.tidy-note { font-size: 14px; line-height: 20px; color: var(--ink-muted); margin: 12px 0; }
.tidy-link { display: block; text-align: center; text-decoration: none; }

/* ---- spending: the month stepper, the year's bars, receipts from mail (pages/spending.js) ---- */
.sp-hero { padding-top: 8px; }
.sp-monthnav { display: flex; align-items: center; justify-content: space-between; margin: 0 -6px 4px; }
.sp-monthnav .icon-btn:disabled { opacity: 0.3; cursor: default; }
.sp-monthname { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-muted); }
.sp-bars { display: flex; align-items: flex-end; gap: 4px; height: 72px; margin-top: 14px; }
.sp-bar { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: stretch; gap: 4px; padding: 0; border: none; background: none; cursor: pointer; font: inherit; }
.sp-bar:disabled { cursor: default; opacity: 0.35; }
.sp-bar-fill { display: block; border-radius: 4px 4px 2px 2px; background: color-mix(in srgb, var(--accent) 34%, transparent); transition: height 0.3s ease; }
.sp-bar.on .sp-bar-fill { background: var(--accent); }
.sp-bar:hover:not(:disabled) .sp-bar-fill { background: var(--glow); }
.sp-bar-m { font-size: 10.5px; text-align: center; color: var(--ink-faint); line-height: 12px; }
.sp-bar.on .sp-bar-m { color: var(--ink); font-weight: 700; }
@media (prefers-reduced-motion: reduce) { .sp-bar-fill { transition: none; } }
.sp-mailrow, .sp-since { display: flex; align-items: center; width: 100%; text-align: left; padding: 10px 0 0; margin-top: 8px; border: none; border-top: 1px solid var(--border); background: none; font: inherit; cursor: pointer; }
.sp-since .tx-merchant { color: var(--accent); }
.sp-rc { display: inline-flex; vertical-align: -2px; margin-left: 6px; color: var(--accent); }
.sp-rc svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 40; }
.sp-recur { display: flex; align-items: center; margin-bottom: 8px; }
.sp-recur-amt { text-align: right; flex: none; }
.sp-meta.strong { color: var(--ink); font-weight: 700; font-size: 13px; }
.sp-sheet-amount { font-size: 30px; font-weight: 700; margin-top: 14px; color: var(--ink); font-variant-numeric: tabular-nums; }
.sp-sheet-meta { font-size: 13px; line-height: 19px; color: var(--ink-muted); margin-top: 4px; }
.sp-sheet-raw { font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--ink-faint); margin-top: 8px; word-break: break-word; }
.sp-receipt-box { margin-top: 16px; padding: 12px 14px; border-radius: 14px; background: var(--accent-soft); }
.sp-receipt-subject { font-size: 14.5px; font-weight: 600; color: var(--ink); margin-top: 10px; line-height: 20px; word-break: break-word; }
.sp-receipt-box .sp-receipt-subject { margin-top: 0; }
.sp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.tidy-pill.danger { color: var(--danger); }
.sp-list { padding-top: 12px; }
/* The pile row fades at its right edge only while more of it is out of view,
   so a tab past it reads as "scroll", not "cut" — a mask, never a painted box. */
.mail-piles { scroll-padding-inline: 28px; }
.mail-piles.more-right { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
/* One line beside the button, so a row never changes height when its button
   turns into "Unsubscribing…"; under 360px the button sits below and the
   details may wrap (the narrow rule below). */
.tidy-sender-meta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tidy-sender-rule { font-size: 11.5px; font-weight: 600; color: var(--accent); margin-top: 2px; }
/* Clean up, round 2 (pages/mail-tidy.js): the inbox as one bar of its piles,
   what was tapped saying what it is doing in place, a line at the top for
   what finished, and placeholders the shape of the sheet while it loads. */
.tidy-title { display: inline-flex; align-items: center; gap: 7px; }
.tidy-title svg { width: 17px; height: 17px; stroke: var(--accent); fill: none; stroke-width: 34; }
.tidy-big-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tidy-updating { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-faint); }
.tidy-updating .spin, .tidy-status .spin { width: 14px; height: 14px; }
.tidy-bar { display: flex; gap: 2px; height: 10px; border-radius: 5px; overflow: hidden; margin-top: 12px; background: var(--border); }
.tidy-bar > span { display: block; min-width: 3px; }
.tidy-legend { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 10px; font-size: 12.5px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.tidy-legend-item { display: inline-flex; align-items: center; gap: 5px; }
.tidy-legend-item b { color: var(--ink); font-weight: 600; }
.tidy-legend-item i, .tidy-dot { display: inline-block; width: 8px; height: 8px; border-radius: 4px; flex: none; }
.tidy-dot { margin-right: 6px; vertical-align: 1px; }
.tidy-job-text.strong { color: var(--ink); }
.tidy-suggest-icon { flex: none; width: 38px; height: 38px; border-radius: 11px; display: flex; align-items: center; justify-content: center; }
.tidy-suggest-icon svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 34; }
.tidy-status { flex: none; display: inline-flex; align-items: center; gap: 6px; min-height: 34px; max-width: 150px; font-size: 13px; font-weight: 650; }
.tidy-status > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tidy-status.working { color: var(--accent); }
.tidy-status.ok { color: var(--success, #2E7D32); }
.tidy-status.bad { color: var(--danger); }
.tidy-status svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 44; flex: none; }
.tidy-sender.is-working { cursor: default; }
.tidy-sender.is-working:hover { background: none; }
/* Done: what it was about fades; the tick and its word stay full strength. */
.tidy-card.is-done > .tidy-suggest-head, .tidy-sender.is-done > .avatar, .tidy-sender.is-done > .tidy-sender-body { opacity: 0.6; }
.tidy-error { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.tidy-error .tidy-pill { min-height: 40px; padding: 0 18px; }
.tidy-banner { display: flex; align-items: center; gap: 8px; padding: 9px 12px; border-radius: 12px; background: var(--accent-soft); color: var(--ink); font-size: 13.5px; line-height: 19px; }
.tidy-banner svg { width: 17px; height: 17px; stroke: var(--accent); fill: none; stroke-width: 44; flex: none; }
.tidy-banner.bad { background: var(--surface); border: 1px solid var(--danger); }
/* A suggestion: its picture and words, then its button — under the words on a narrow phone. */
.tidy-suggest-head { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; }
@media (max-width: 360px) {
  .tidy-suggest { flex-direction: column; align-items: stretch; gap: 10px; }
  .tidy-suggest > .tidy-pill, .tidy-suggest > .tidy-status { align-self: flex-start; margin-left: 50px; }
}
.tidy-banner.bad svg { stroke: var(--danger); }
.tidy-ph { display: block; flex: none; background: var(--border); animation: tidy-pulse 1.3s ease-in-out infinite; }
.tidy-ph-col { display: flex; flex-direction: column; gap: 6px; }
.tidy-summary > .tidy-ph + .tidy-ph { margin-top: 8px; }
@keyframes tidy-pulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 0.9; } }
@media (prefers-reduced-motion: reduce) { .tidy-ph { animation: none; opacity: 0.6; } }
/* A small phone: the name and its count keep their line each, and the pill
   goes under them rather than squeezing them into a column of single words. */
@media (max-width: 360px) {
  .tidy-sender { flex-wrap: wrap; row-gap: 6px; }
  .tidy-sender > .tidy-sender-body { flex: 1 1 calc(100% - 48px); }
  .tidy-sender > .tidy-pill, .tidy-sender > .tidy-status { margin-left: 48px; }
  .tidy-sender-meta { white-space: normal; overflow-wrap: anywhere; }
}
.sp-upload:disabled { opacity: 0.6; cursor: default; }

/* ---- the Apps popup (appsPopup.js; the phone's components/AppsPopup.tsx) ----
   The profile's apps as icons standing on the bar, the bar still in reach
   under them: the frost stops at the bar's top edge, and the box is the
   search. Since 18 Oct 2026 not a card — the icons float on the page's own
   colour, Manage and Close round at the two ends of the row under them.
   Since 19 Oct 2026 one grid of every app, no pages and no dots ("There is
   also no need to have multiple apps pages there is plenty of room on one
   page"), scrolling up and down only when it is taller than the room. Where
   it stands (bottom, left, width) and the grid's columns and height are set
   by the script from the bar's own box. */
.apps-pop-scrim {
  position: fixed; left: 0; right: 0; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(1.1); backdrop-filter: blur(10px) saturate(1.1);
  animation: panel-fade 160ms ease-out both;
}
.apps-pop-scrim.closing { animation: panel-fade 150ms ease-in reverse both; }
.apps-pop {
  position: fixed; z-index: 41; box-sizing: border-box; display: flex; flex-direction: column; align-items: center;
  animation: apps-pop-rise 220ms cubic-bezier(.34, 1.4, .64, 1) both;
  /* A finger's drag down is the popup's (it carries it away); a grid that scrolls takes its own (.scrolls). */
  touch-action: none;
}
.apps-pop.closing { animation: apps-pop-fall 160ms cubic-bezier(.4, 0, 1, 1) both; }
@keyframes apps-pop-rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes apps-pop-fall { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(22px); } }
/* Each icon pops in a beat after the one before it. */
.apps-pop-pages .app-tile { animation: apps-tile-pop 220ms cubic-bezier(.34, 1.56, .64, 1) both; animation-delay: calc(var(--i, 0) * 14ms); }
@keyframes apps-tile-pop { from { opacity: 0; transform: scale(.82); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .apps-pop, .apps-pop-scrim, .apps-pop-pages .app-tile { animation-duration: 1ms; animation-delay: 0ms; }
}
.apps-pop-pages {
  width: 100%; box-sizing: border-box; overflow: hidden;
  display: grid; grid-template-columns: repeat(var(--apps-cols, 4), minmax(0, 1fr));
  grid-auto-rows: var(--apps-cell-h, 92px); align-content: start; height: var(--apps-view-h, auto);
}
/* Taller than the room: it scrolls up and down, and the finger's up and down is the scroll. */
.apps-pop-pages.scrolls { overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; -webkit-overflow-scrolling: touch; }
.apps-pop-pages[hidden], .apps-pop-none[hidden], .apps-pop-hint[hidden] { display: none; }
/* Each tile keeps room for the first hit's ring, drawn or not, so nothing shifts when it lands. */
.apps-pop-pages .app-tile {
  width: 100%; justify-self: stretch; align-self: start; min-width: 0; box-sizing: border-box;
  padding: 6px 0 5px; border-radius: 16px; border: 1.5px solid transparent;
}
.apps-pop-pages .app-tile-name { max-width: calc(100% - 4px); }
/* Narrow cells (a 320px screen): a smaller name, so "ShmangNav" still fits whole. */
@container (max-width: 340px) { .apps-pop-pages .app-tile-name { font-size: 11px; letter-spacing: -0.2px; } }
.apps-pop { container-type: inline-size; }
.apps-pop-pages .app-tile.first { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.apps-pop-none { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 18px 24px 14px; text-align: center; }
.apps-pop-none svg { width: 22px; height: 22px; stroke: var(--ink-faint); fill: none; stroke-width: 36; }
.apps-pop-none-title { font-size: 15.5px; font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.apps-pop-none-text { font-size: 13.5px; line-height: 19px; color: var(--ink-muted); }
.apps-pop-hint { flex: none; height: 22px; font-size: 12.5px; font-weight: 600; color: var(--ink-muted); text-align: center; padding: 0 16px; white-space: normal; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word; }
/* Manage at one end, Close at the other. */
.apps-pop-controls { display: flex; align-items: center; align-self: stretch; height: 48px; margin-top: 2px; padding: 0 6px; }
.apps-pop-coin {
  flex: none; box-sizing: border-box; width: 44px; height: 44px; padding: 0 0 2px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--border); border-bottom-width: 4px; background: var(--surface-solid, var(--surface)); color: var(--ink);
  display: flex; align-items: center; justify-content: center; transition: transform 70ms ease-out; -webkit-tap-highlight-color: transparent;
}
.apps-pop-coin:active { transform: translateY(2px); border-bottom-width: 2px; }
.apps-pop-coin svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 40; }
.apps-pop-coin:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.apps-pop-gap { flex: 1; }
.apps-pop-search {
  flex: none; display: flex; align-items: center; gap: 8px; align-self: stretch; margin: 6px 6px 0; padding: 0 12px; height: 44px;
  border-radius: 22px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface));
}
.apps-pop-search svg { width: 17px; height: 17px; flex: none; stroke: var(--ink-faint); fill: none; stroke-width: 36; }
.apps-pop-input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; font: inherit; font-size: 16px; color: var(--ink); }
.apps-pop-input::placeholder { color: var(--ink-faint); }
.apps-pop-search:focus-within { border-color: var(--accent); }

/* ---- the pop-ups: ShmangNav, the browser and Messages (14 Oct 2026) ----
   They rise over the page you were on and drop back onto it (app.js
   changePage). Since that night the rail, the row of buttons and the gear
   stand over them as over every page (the user: "put the buttons bar back in
   the popout pages"): each one's coin opens and closes it. ShmangNav keeps
   the round arrow at its top left (popupBack.js); the browser lost its on
   19 Oct 2026 (pages/browser.js). */
.popup-back {
  flex: none; box-sizing: border-box; width: 44px; height: 44px; padding: 0 0 2px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--border); border-bottom-width: 4px; background: var(--surface-solid, var(--surface)); color: var(--ink);
  display: flex; align-items: center; justify-content: center; transition: transform 70ms ease-out; -webkit-tap-highlight-color: transparent;
}
.popup-back svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 40; }
.popup-back:active { transform: translateY(2px); }
.popup-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.popup-back[hidden] { display: none; }
/* ShmangNav: first in the row of ways, the bar after it (nav.js layoutRow lays the row out in what it leaves). */
.nav-popback { position: absolute; z-index: 3; top: var(--nav-row); left: calc(8px + env(safe-area-inset-left)); box-shadow: var(--nav-float); }
body[data-page='nav'] .nav-modebar { left: calc(58px + env(safe-area-inset-left)); }
/* The browser's title (the head keeps its room at the top: the page's soft top edge fades there). */
.browser-head { display: flex; align-items: center; gap: 12px; }
.browser-head-words { min-width: 0; }

/* ---- the Shmangus picker (pickSheet.js; the phone's components/PickSheet.tsx) ----
   14 Oct 2026: the pin's pages and ShmangNav's saved-place menu, never a
   plain list or the system's alert. A card rising from the bottom over the
   page, tiles in a grid (the app-tile look), actions under them. */
.pick-sheet { position: fixed; inset: 0; z-index: 80; pointer-events: none; }
.pick-back { position: absolute; inset: 0; background: rgba(0, 0, 0, .28); opacity: 0; transition: opacity 140ms ease-in; }
.pick-card {
  /* --pick-bottom / --pick-max: on the on-screen keys when they are up (pickSheet.js pickPlaceWeb). */
  position: absolute; left: 50%; bottom: var(--pick-bottom, max(14px, env(safe-area-inset-bottom))); width: min(480px, calc(100vw - 20px));
  max-height: var(--pick-max, 72vh); display: flex; flex-direction: column; box-sizing: border-box; overflow: hidden;
  background: var(--surface-solid, var(--surface)); color: var(--ink);
  border: 1px solid var(--border); border-bottom-width: 4px; border-radius: 24px; padding-bottom: 10px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .18);
  translate: -50% 48px; opacity: 0; transition: translate 140ms ease-in, opacity 140ms ease-in;
}
.pick-sheet.up { pointer-events: auto; }
.pick-sheet.up .pick-back { opacity: 1; transition: opacity 220ms cubic-bezier(.33, 1, .68, 1); }
.pick-sheet.up .pick-card { translate: -50% 0; opacity: 1; transition: translate 220ms cubic-bezier(.33, 1, .68, 1), opacity 220ms cubic-bezier(.33, 1, .68, 1); }
.pick-head { padding: 8px 16px 6px; touch-action: none; cursor: grab; flex: none; }
.pick-grab { width: 36px; height: 4px; border-radius: 2px; background: var(--border); margin: 0 auto 10px; }
.pick-title-row { display: flex; align-items: center; gap: 10px; min-height: 28px; }
.pick-words { flex: 1; min-width: 0; }
/* Said whole, on as many lines as it takes: a saved place's own name is the title (the user's rule: nothing cut). */
.pick-title { margin: 0; font-size: 17px; font-weight: 800; letter-spacing: .1px; overflow-wrap: anywhere; }
.pick-sub { font-size: 13px; color: var(--ink-muted); overflow-wrap: anywhere; margin-top: 1px; }
/* The grid gives way to the actions on a short screen and scrolls; its rows never squash. */
.pick-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(86px, 1fr)); grid-auto-rows: max-content; align-content: start;
  flex: 1 1 auto; min-height: 0; padding: 4px 12px 6px; overflow-y: auto; overscroll-behavior: contain;
}
.pick-tile {
  display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 8px 2px; min-height: 92px; flex: none;
  border: 0; background: none; color: var(--ink); font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.pick-ring { position: relative; display: inline-flex; padding: 2px; border: 2.5px solid transparent; border-radius: 19px; }
.pick-tile.on .pick-ring { border-color: var(--ink); }
/* An app the owner marked under construction (construction.js), ringed red here too while Visibility dev mode is on. */
.pick-ring.building, .pick-tile.on .pick-ring.building { border-color: var(--building-red, #E5484D); }
.pick-card:focus { outline: none; }
.pick-art {
  width: var(--pick-size, 54px); height: var(--pick-size, 54px); border-radius: calc(var(--pick-size, 54px) * .27); flex: none;
  display: flex; align-items: center; justify-content: center; color: #fff;
  background: linear-gradient(to bottom, var(--tile-top, var(--tile)), var(--tile));
  transition: transform 120ms ease, opacity 120ms ease;
}
.pick-art svg { width: calc(var(--pick-size, 54px) * .48); height: calc(var(--pick-size, 54px) * .48); stroke: currentColor; fill: none; stroke-width: 32; }
.pick-tile:active .pick-art { transform: scale(.96); opacity: .7; }
.pick-tile:focus-visible { outline: none; }
.pick-tile:focus-visible .pick-ring { border-color: var(--accent); }
.pick-check {
  position: absolute; right: -4px; top: -4px; width: 20px; height: 20px; border-radius: 10px; box-sizing: border-box;
  background: var(--ink); border: 2px solid var(--surface-solid, var(--surface)); display: flex; align-items: center; justify-content: center;
}
.pick-check svg { width: 12px; height: 12px; stroke: var(--surface-solid, var(--bg)); fill: none; stroke-width: 56; }
/* 11.5px, a touch tight: "Communities" whole in an 86px column (UI review, 18 Oct 2026). */
.pick-name { font-size: 11.5px; letter-spacing: -0.3px; font-weight: 600; max-width: 100%; white-space: normal; overflow-wrap: break-word; }
/* The chosen tile is marked by its ring and tick; its name keeps the others' weight, so a long one is not cut ("Communi…"). */
.pick-actions { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 12px 0; flex: none; }
.pick-actions.under { border-top: 1px solid var(--border); }
.pick-action {
  flex: 1; min-height: 46px; display: flex; align-items: center; justify-content: center; gap: 7px; padding: 0 12px;
  border: 1px solid var(--border); border-bottom-width: 3px; border-radius: 14px; background: var(--bg); color: var(--ink);
  font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; transition: transform 70ms ease-out;
  /* One line each: two that do not fit side by side stack instead (UI review, 19 Oct 2026). */
  white-space: nowrap; flex-basis: auto;
}
.pick-action svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 36; }
.pick-action:active { transform: translateY(2px); }
.pick-action.danger { color: var(--danger, #d14343); border-color: color-mix(in srgb, var(--danger, #d14343) 45%, transparent); }
.pick-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Where a pick of the row is kept (18 Oct 2026, evening): This profile / All profiles, the one in force filled. */
.pick-scope { display: flex; gap: 8px; padding: 2px 12px 4px; flex: none; }
.pick-scope-key {
  flex: 1; min-height: 44px; min-width: 0; border-radius: 14px; border: 1px solid var(--border); border-bottom-width: 3px;
  background: var(--bg); color: var(--ink); font: inherit; font-size: 14px; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 6px; padding: 0 8px;
}
.pick-scope-key span { white-space: normal; overflow-wrap: break-word; }
.pick-scope-key svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 36; flex: none; }
.pick-scope-key.on { background: var(--ink); color: var(--surface-solid, var(--bg)); border-color: var(--ink); }
.pick-scope-key:active { transform: translateY(2px); }
.pick-scope-key:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A narrow phone: the words alone, so "Only Personal" is never cut. */
@media (max-width: 360px) { .pick-scope-key svg { display: none; } .pick-scope-key { font-size: 13.5px; } }
/* A held saved place's own dot, beside the title (pages/nav.js). */
.pick-title-row .nav-row-ico { flex: none; width: 40px; height: 40px; }
@media (prefers-reduced-motion: reduce) { .pick-card, .pick-back, .pick-sheet.up .pick-card, .pick-sheet.up .pick-back { transition-duration: 60ms; translate: -50% 0; } }

/* ---- Settings, a pop-up (14 Oct 2026, night: "make settings app open up nice
   and smooth and same way as a stationary app") ----
   #/settings pops over the page you were on (popupBack.js POPUP_PLACES,
   vt-pop-in/out), the bar drawn under it; the gear is its toggle, lit while it
   is up — the strip coins' lit. A page of Settings has a back chevron to the list. */
.bar-gear.on { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--surface-solid, var(--surface))); box-shadow: 0 3px 0 color-mix(in srgb, var(--accent) 68%, #000); }
/* 18 Oct 2026, night: the profile is the coin left of the box (it swapped with Settings), filled with its colour,
   and Apps the coin right of it, where the microphone stood — lit like the gear was while its popup is up. */
.bar-side.bar-profile { background: var(--place, var(--ink)); border-color: var(--place, var(--ink)); color: var(--on-place, var(--bg)); box-shadow: 0 3px 0 color-mix(in srgb, var(--place, var(--ink)) 68%, #000); }
.bar-side.bar-profile svg { stroke: var(--on-place, var(--bg)); }
.bar-side.bar-profile:active { box-shadow: 0 0 0 transparent; }
/* Since 19 Oct 2026 Apps is filled too, with its own colour (the user: "make the apps button have a background always
   so that it matches profile button"), on every box at the bottom of a screen (barSides.js appsCoin) — the phone's
   SlideButton `fill`. No ring while its popup is up: that ring was "this weird blue circle that appears when u click
   the apps button" (the user, the same day); the popup over the page says it is open. A keyboard's focus still shows
   (.bar-side:focus-visible), never a tap's. */
.bar-side.bar-apps { background: var(--place, var(--ink)); border-color: var(--place, var(--ink)); color: var(--on-place, var(--bg)); box-shadow: 0 3px 0 color-mix(in srgb, var(--place, var(--ink)) 68%, #000); }
.bar-side.bar-apps svg { width: 21px; height: 21px; fill: currentColor; stroke: none; }
.bar-side.bar-apps:active { box-shadow: 0 0 0 transparent; }
/* No box (an account the AI is not for): the two coins still stand at the line's ends. */
.cmd-bar-none { flex: 1; }
.set-head-back { display: flex; align-items: center; gap: 6px; }
.set-back {
  flex: none; width: 40px; height: 40px; margin-left: -6px; border: 0; border-radius: 20px; background: none; color: var(--ink);
  display: flex; align-items: center; justify-content: center; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.set-back svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 44; }
.set-back:active { background: var(--accent-soft); }
.set-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- 18 Oct 2026: the dock (stripPin.mjs dockOf; the phone's lib/stripGeometry.ts dockCell) ----
   The row above the box is eleven still keys: four coins a corner — the
   browser, ShmangNav, Messages and Storage; the second pin, the pin, Apps and
   the profile — and between them the profile's page, Today and its other
   page (app.js dockMiddle), Today in the very middle whatever a corner holds.
   One size for all, as big as the row allows (the user: "make all the apps
   stationary"). The keys measure the row (container units), so the column the
   app stands in on a computer and a phone's whole width both come out right.
   Since that evening thirteen: the Planner joined the left corner, a third pin
   the right, and the middle is the AI (the sparkles) — five coins a corner.
   That night eleven again: two pins went, and the right corner is the pin,
   Settings and the microphone (the profile and Apps stand either side of the
   box, .bar-profile / .bar-apps). */
/* The phone's rule (lib/stripGeometry.ts dockCell): cells that touch, each
   key's face 2px under its cell and as tall as it is wide (plus its lip), so
   the coins are round at every width; the row's sides match the phone's 8. */
/* Capped and centred on a wide window, so the corners do not drift off as islands (UI review, 18 Oct 2026, evening). */
/* Since 19 Oct 2026 nine coins and nothing that swipes (the user: "get rid of the whole swipe thing everything will
   be stationary buttons now and only different button will be ai button which will be a bit nicer and maybe pop out
   a little bit over the border"): four either side of the AI, each side an equal half hugging it, so the AI is the
   middle whatever a side holds; the coins as big as 9.3 cells allow (the AI's is 1.3 of one), the phone's
   lib/stripGeometry.ts dockCell. No lines between corners any more — there is no middle that moves. */
.wheel-dock { display: flex; align-items: flex-end; container-type: inline-size; margin: 0 auto 2px; width: calc(100% - 16px); max-width: 600px; overflow: visible; }
/* With the AI's coin centred on the row (its foot a little under it), 2px more under the row, so its lip never meets the box
   (the phone's AI_FOOT): 14 (its glow too, UI review), as the line under it already stands 8 off the row and the two margins meet as one. */
.wheel-dock:has(> .wheel-ai) { margin-bottom: 14px; }
.wheel-dock > .wheel-corner, .wheel-dock > .wheel-right { flex: 1 1 0; min-width: 0; gap: 2px; }
.wheel-dock > .wheel-corner { justify-content: flex-end; }
.wheel-dock > .wheel-right { justify-content: flex-start; margin-left: 0; }
.wheel-dock .wheel-corner::after, .wheel-dock .wheel-right::before { display: none; }
.wheel-dock .wheel-community, .wheel-dock .wheel-persona, .wheel-dock .wheel-ai {
  /* Nine faces, the AI's 1.3 of one, and the room between them (2px gaps, the AI's margins): (100cqw - 14px) / 9.6. */
  --dock-face: min(40px, calc((100cqw - 14px) / 9.6));
}
.wheel-dock .wheel-community, .wheel-dock .wheel-persona { width: var(--dock-face); height: calc(var(--dock-face) + 4px); flex: none; }
.wheel-dock .wheel-community svg, .wheel-dock .wheel-persona svg {
  width: min(23px, calc(var(--dock-face) * 0.6)); height: min(23px, calc(var(--dock-face) * 0.6));
}
.wheel-dock .wheel-community, .wheel-dock .wheel-persona { border-radius: 999px; }
/* The AI: a size up, CENTRED on the others' line (19 Oct 2026, late, the user: "I want the centers to line up …
   I want ai button to move down a bit so centered") — a little over the row above and the same under it. Since the evening of
   19 Oct 2026 a rounded square, never read as the profile's round coin by the box (both were Personal's yellow):
   white like its neighbours, its sparkles in the theme's accent, and filled with the theme's sweep — the box's
   ring, the send button — only while it is the page in front (the user: "The center ai button looks too similar
   to the profiles button make it different also it should only be filled in when u are on it"). The phone's
   lib/stripGeometry.ts aiRadius says the same 30%. */
.wheel-ai {
  --ai-face: calc(var(--dock-face) * 1.3);
  /* Its extra height half over the row's top, half under its foot (negative margins): the row is no taller than its
     coins and its middle is theirs (the phone's lib/stripGeometry.ts aiOverhang). */
  position: relative; flex: none; box-sizing: border-box; padding: 0; margin: calc((var(--dock-face) - var(--ai-face)) / 2) calc(var(--dock-face) * 0.15 + 1px);
  width: var(--ai-face); height: calc(var(--ai-face) + 4px); border-radius: 30%;
  border: 1px solid var(--border); border-bottom-width: 4px;
  background: var(--surface-solid, var(--surface)); color: var(--accent);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: transform 70ms ease-out, box-shadow 160ms ease-out; -webkit-tap-highlight-color: transparent;
}
.wheel-ai.on {
  border-color: var(--gradient-a); border-bottom-color: color-mix(in srgb, var(--gradient-b) 68%, #000);
  background: linear-gradient(135deg, var(--gradient-a), var(--gradient-b)); color: var(--on-accent);
  box-shadow: 0 3px 14px color-mix(in srgb, var(--glow) 50%, transparent);
}
.wheel-ai::after { content: ''; position: absolute; inset: -6px -2px; }
.wheel-ai:active { transform: translateY(2px); }
.wheel-ai:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Lines while it waits, the shape while it is in front — as every coin's icon fills while its page is up. */
.wheel-ai svg { width: calc(var(--ai-face) * 0.5); height: calc(var(--ai-face) * 0.5); fill: none; stroke: currentColor; stroke-width: 30; stroke-linejoin: round; }
.wheel-ai.on svg { fill: currentColor; stroke: none; }
/* The day (an account without the AI): its sun in the day's colour, filled with it while it is in front. The sun is
   lines, not a shape. */
.wheel-ai[data-place="today"] { color: var(--place, #F5B53C); }
.wheel-ai[data-place="today"].on { border-color: var(--place, #F5B53C); border-bottom-color: color-mix(in srgb, var(--place, #F5B53C) 68%, #000); background: var(--place, #F5B53C); color: var(--on-place, #1A1206); box-shadow: 0 3px 14px color-mix(in srgb, var(--place, #F5B53C) 55%, transparent); }
.wheel-ai[data-place="today"] svg, .wheel-ai[data-place="today"].on svg { fill: none; stroke: currentColor; stroke-width: 36; }
/* The AI (19 Oct 2026, night; aiFan.js): round, Shmangus's face, white like its neighbours while an app is up over it —
   never the profile coin's round fill in a profile's colour. While it is in front, gold since the afternoon of 21 Oct
   2026 (night that morning, a heavy black ring round the logo on a light page; the user: "looks terrible especially on
   the light theme … maybe just give it a dif color that matches theme better idk. Yellow?"): the logo's own yellow, a
   light sheen to a deeper gold, the rim and shadow suited to the mode (aiChats.mjs AI_GOLD; the dark one below). */
.wheel-ai[data-place="chat"] { border-radius: calc(var(--ai-face) / 2 + 1px); }
.wheel-ai[data-place="chat"].on {
  border: 1.5px solid var(--ai-gold-rim); border-bottom: 4px solid var(--ai-gold-lip);
  background: linear-gradient(135deg, var(--ai-gold-a), var(--ai-gold-b));
  box-shadow: 0 3px 10px color-mix(in srgb, var(--ai-gold-shadow) 38%, transparent);
}
[data-theme="dark"] .wheel-ai[data-place="chat"].on {
  border-color: var(--ai-gold-dark-rim); border-bottom-color: var(--ai-gold-dark-lip);
  background: linear-gradient(135deg, var(--ai-gold-dark-a), var(--ai-gold-dark-b));
  box-shadow: 0 3px 10px color-mix(in srgb, var(--ai-gold-dark-shadow) 45%, transparent);
}
.wheel-ai[data-place="chat"]:focus-visible { outline-color: var(--ai-gold-rim); }
.wheel-ai-face { display: flex; align-items: center; justify-content: center; pointer-events: none; }
/* The AI's face: the Shmangus logo, round (aiFan.js aiFaceEl; the phone's AiFace.tsx shows the same file). */
.ai-logo { display: block; border-radius: 50%; flex: none; user-select: none; -webkit-user-drag: none; }
.wheel-ai[aria-expanded="true"] { transform: translateY(2px); }
/* The microphone in the box (19 Oct 2026): where send is while there is nothing to send — quiet, like the + —
   and the candy with its rings while it listens, when a press finishes it. Words in the box: send again. */
/* Every box's send the same way since that evening (barSides.js sendCol): the assistant's and a conversation's
   .cmd-send, a Claude chat's .pane-send — whose Stop, while Claude answers and the box is empty, stays. */
.send-col.has-mic:not(:has(> :is(.cmd-send, .pane-send):is(.ready, .stop))) > :is(.cmd-send, .pane-send),
.send-col.has-mic:has(> .box-mic.listening) > :is(.cmd-send, .pane-send) { display: none; }
.send-col.has-mic:has(> :is(.cmd-send, .pane-send):is(.ready, .stop)):not(:has(> .box-mic.listening)) > .box-mic { display: none; }
/* The microphone takes its box's send's size, so the words' room never jumps as one gives way to the other. */
.composer-dock .cmd-bar .send-col > .box-mic { width: 38px; height: 38px; border-radius: 19px; margin: 0 3px; }
.st-ask-bar .send-col > .box-mic { width: 32px; height: 32px; margin-bottom: 2px; }
.box-mic { position: relative; width: 30px; height: 30px; color: var(--ink-muted); }
.box-mic svg { position: relative; }
.box-mic.listening { color: var(--on-accent); background: linear-gradient(135deg, var(--gradient-a), var(--gradient-b)); box-shadow: 0 0 14px color-mix(in srgb, var(--glow) 45%, transparent); animation: bar-mic-pop 360ms var(--arc-spring, cubic-bezier(0.34, 1.56, 0.64, 1)); }
.box-mic.listening .bar-mic-ring { animation: bar-mic-ring 1.15s ease-out infinite; }
@media (prefers-reduced-motion: reduce) { .box-mic.listening, .box-mic.listening .bar-mic-ring { animation: none; } .box-mic.listening .bar-mic-ring { opacity: 0.5; } }
/* The splash line has no room between the corners any more: where there is
   none under the box either, it is a line of its own just above the row. */
.cmd-splash.over { display: block; width: fit-content; max-width: calc(100% - 32px); box-sizing: border-box; margin: 0 auto 3px; padding: 1px 10px; border-radius: 10px; font-size: 11.5px; line-height: 15px; text-align: center;
  /* A soft patch under the words alone, so the page never runs into them and no stripe crosses the screen. */
  background: color-mix(in srgb, var(--bg) 86%, transparent); }
.cmd-splash.over.two { max-height: 32px; padding: 1px 10px; margin: 0 auto 3px; }
.cmd-splash.over[hidden] { display: none; }
/* Clear of the AI's coin, whose top stands a little over the row's top edge (19 Oct 2026; half what it was since it is centred). */
.cmd-wrap:has(.wheel-ai) .cmd-splash.over, .cmd-wrap:has(.wheel-ai) .cmd-splash.over.two { margin-bottom: 14px; }
/* Either side of the splash line: the weather on its left, the day on its right (19 Oct 2026,
   splashSides.js). Over the bar's block, never in its flow, so nothing moves; each says only what fits
   the room the words leave, and the press reaches past its words. 13px semibold since 21 Oct 2026 (10.5 was
   "too small it is not readable"; weather.mjs SIDE_FONT, SIDE_LINE). */
.splash-side {
  position: absolute; z-index: 1; border: 0; margin: 0; padding: 0; background: none; color: var(--ink-muted);
  font: inherit; font-size: 13px; line-height: 18px; font-weight: 600; white-space: nowrap; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.splash-side[hidden] { display: none; }
.splash-side::after { content: ''; position: absolute; inset: -10px -8px; }
.splash-side:hover { color: var(--ink); }
.splash-side:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Centred on the line's own 14px (2px of the 18 above it, 2 under). */
.splash-side.under { bottom: calc(env(safe-area-inset-bottom) - 10px); }
/* Over the row, each on a soft patch like the line's own, at the ends of the box's line. */
.splash-side.over { line-height: 17px; padding: 1px 3px; border-radius: 10px; background: color-mix(in srgb, var(--bg) 86%, transparent); }
/* The weather's card over the bar (the next hours, the sun, the place): a click outside or Esc closes it. */
.splash-veil { position: fixed; inset: 0; z-index: 59; background: color-mix(in srgb, var(--bg) 55%, transparent); }
.splash-card {
  position: fixed; left: 50%; transform: translateX(-50%); width: min(calc(100% - 24px), 440px); box-sizing: border-box;
  padding: 8px 8px 2px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface));
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.16); animation: splash-card-in 160ms cubic-bezier(.33,1,.68,1);
}
.splash-card .wx { margin: 0 0 6px; }
@keyframes splash-card-in { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
@media (prefers-reduced-motion: reduce) { .splash-card { animation: none; } }

/* The microphone outside the box again (19 Oct 2026, late; barSides.js micCoin): candy while the box is empty,
   plain once it has words, rings while it listens. */
.bar-mic {
  border-color: transparent; color: var(--on-accent);
  background: linear-gradient(135deg, var(--gradient-a), var(--gradient-b));
  box-shadow: 0 3px 0 color-mix(in srgb, var(--gradient-b) 70%, #000), 0 0 14px color-mix(in srgb, var(--glow) 45%, transparent);
}
.bar-mic:active { box-shadow: 0 0 0 transparent, 0 0 14px color-mix(in srgb, var(--glow) 45%, transparent); }
.cmd-line:has(.cmd-send.ready, .pane-send.ready:not(.stop)) > .bar-mic:not(.listening) {
  background: var(--surface-solid, var(--surface)); border-color: var(--border); color: var(--accent); box-shadow: 0 3px 0 var(--border);
}
.bar-mic.listening .bar-mic-ring { animation: bar-mic-ring 1.15s ease-out infinite; }
.bar-mic.listening { animation: bar-mic-pop 360ms var(--arc-spring, cubic-bezier(0.34, 1.56, 0.64, 1)); }
@media (prefers-reduced-motion: reduce) { .bar-mic.listening, .bar-mic.listening .bar-mic-ring { animation: none; } .bar-mic.listening .bar-mic-ring { opacity: 0.5; } }
/* The keys up (21 Oct 2026; barSides.js keysUp, <html class="keys-up">): the arrow that puts them away stands in the
   microphone's place — the coin right of the box, or its place in the box — a plain key, not the candy; never over a
   microphone that is listening. The phone's ComposerPill `arrowAtMic`. */
.bar-mic-face { display: contents; }
.bar-keys-down { display: none; }
:root.keys-up :is(.bar-mic, .box-mic):not(.listening) > :is(.bar-mic-face, .bar-mic-ring) { display: none; }
:root.keys-up :is(.bar-mic, .box-mic):not(.listening) > .bar-keys-down { display: contents; }
:root.keys-up .cmd-line > .bar-mic:not(.listening) {
  background: var(--surface-solid, var(--surface)); border-color: var(--border); color: var(--ink-muted); box-shadow: 0 3px 0 var(--border);
}
/* …and in a box whose microphone is inside it (Study's Ask), once there are words Send has that place: the arrow
   stands just before it, the two side by side, so the keys can always be put away (the phone's `arrowBySend`). */
:root.keys-up .send-col.has-mic:has(> :is(.cmd-send, .pane-send).ready):not(:has(> .box-mic.listening)) { flex-direction: row; }
:root.keys-up .send-col.has-mic:has(> :is(.cmd-send, .pane-send).ready):not(:has(> .box-mic.listening)) > .box-mic { display: inline-flex; }
/* Storage's coin wears a key on top of its tray (19 Oct 2026, late): it opens on Passwords. */
.wheel-storage-face { position: relative; display: inline-flex; }
.wheel-storage-key { position: absolute; top: -32%; right: -36%; width: 62%; height: 62%; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--surface-solid, var(--surface)); }
.wheel-storage-key svg { width: 86%; height: 86%; transform: rotate(-45deg); }
.wheel-storage.on .wheel-storage-key { background: transparent; }

/* ---- Explore (19 Oct 2026, late; pages/explore.js): Shmangus's own courses (prefix exl-: picker.css owns xp-). Nothing is cut: titles wrap. ---- */
.screen-scroll > .inner.exl-page { padding-top: calc(52px + env(safe-area-inset-top)); padding-bottom: 40px; max-width: 900px; margin: 0 auto; box-sizing: border-box; }
.exl-h1 { font-family: var(--font-display, inherit); font-size: 28px; font-weight: 800; letter-spacing: -0.6px; margin: 0; color: var(--ink); }
.exl-sub { margin: 2px 0 14px; font-size: 15px; color: var(--ink-muted); }
.exl-h2 { font-size: 18px; font-weight: 800; margin: 18px 0 10px; color: var(--ink); }
.exl-chips { display: flex; flex-wrap: wrap; gap: 8px; overflow: visible; border: 0; }
.exl-chip { min-height: 44px; padding: 0 16px; border-radius: 22px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; }
.exl-chip.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.exl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 14px; }
.exl-card { display: flex; flex-direction: column; align-items: stretch; text-align: left; padding: 12px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; cursor: pointer; }
.exl-card:active { opacity: 0.7; }
.exl-tile { height: 84px; border-radius: 14px; display: flex; align-items: center; justify-content: center; margin-bottom: 10px; }
.exl-mark { font-size: 40px; line-height: 1; }
.exl-card-title { font-size: 16px; font-weight: 800; line-height: 20px; overflow-wrap: anywhere; }
.exl-card-line { font-size: 13px; margin-top: 4px; color: var(--ink-muted); }
.exl-back { width: 44px; height: 44px; border-radius: 22px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); display: inline-flex; align-items: center; justify-content: center; margin-bottom: 12px; cursor: pointer; }
.exl-back svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 40; }
.exl-hero { height: 120px; border-radius: 22px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px; }
.exl-hero-mark { font-size: 64px; line-height: 1; }
.exl-meta { margin: 4px 0 0; font-size: 14px; color: var(--ink-muted); }
.exl-about { margin: 12px 0 0; font-size: 16px; line-height: 23px; color: var(--ink); }
.exl-note { margin: 10px 0 0; text-align: center; font-size: 14px; color: var(--ink-muted); }
.exl-note-row { margin: -2px 0 10px; text-align: left; padding: 0 12px; }
.exl-num { width: 32px; height: 32px; flex: none; border-radius: 16px; border: 1px solid var(--border); display: inline-flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 800; color: var(--ink-muted); background: var(--bg); }
.exl-num.next { color: #fff; }
.exl-num svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 48; }
.exl-mins { font-size: 13px; color: var(--ink-muted); flex: none; }
/* Units and lessons (20 Oct 2026): a course's subjects, its units with a quiet bar, a unit opened to its lessons. */
.exl-page .arc-candy.exl-wide { display: flex; width: 100%; white-space: normal; text-align: center; padding: 8px 18px; }
.exl-page .arc-candy:disabled { opacity: 0.6; cursor: default; }
.exl-page .arc-candy.exl-start { margin-top: 18px; }
.exl-mistakes { width: 100%; display: flex; align-items: center; gap: 10px; min-height: 52px; margin-top: 12px; padding: 0 14px; border-radius: 26px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; cursor: pointer; }
.exl-mistakes svg { width: 20px; height: 20px; flex: none; stroke: currentColor; fill: none; stroke-width: 40; }
.exl-mistakes-name { flex: 1; text-align: left; font-size: 16px; font-weight: 700; }
.exl-count { min-width: 28px; height: 28px; padding: 0 8px; box-sizing: border-box; border-radius: 14px; background: #FF4F7B; color: #2B2118; font-size: 14px; font-weight: 900; display: inline-flex; align-items: center; justify-content: center; }
.exl-subjects { margin-top: 18px; }
.exl-subjects .exl-chip { min-height: 44px; padding: 0 16px; border-radius: 22px; }
.exl-check { margin-top: 14px; padding: 14px; border-radius: 22px; border: 1px solid var(--border); border-bottom-width: 5px; background: var(--surface-solid, var(--surface)); }
.exl-check-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.exl-check-head > :first-child { flex: none; }
.exl-check-words { flex: 1; min-width: 0; }
.exl-check-title { margin: 0; font-size: 17px; font-weight: 800; color: var(--ink); }
.exl-check-about { margin: 2px 0 0; font-size: 14px; line-height: 19px; color: var(--ink-muted); }
.exl-vos { width: 100%; display: flex; align-items: center; gap: 12px; min-height: 56px; margin-top: 12px; padding: 8px 0; border: 0; border-bottom: 1px solid var(--border); background: none; color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.exl-vos-words { flex: 1; display: flex; flex-direction: column; }
.exl-vos-title { font-size: 15px; font-weight: 700; }
.exl-vos-sub { font-size: 13px; margin-top: 2px; color: var(--ink-muted); }
.exl-units { list-style: none; margin: 14px 0 0; padding: 0; }
.exl-unit { width: 100%; display: flex; align-items: center; gap: 12px; min-height: 60px; padding: 10px 12px; margin-bottom: 8px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.exl-unit:active, .exl-lesson:active, .exl-mistakes:active, .exl-card:active { opacity: 0.7; }
.exl-unit-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.exl-unit-name { font-size: 16px; font-weight: 700; line-height: 21px; overflow-wrap: anywhere; }
.exl-flag { font-size: 13px; font-weight: 700; margin-top: 3px; }
.exl-bar { display: block; height: 5px; margin-top: 7px; border-radius: 3px; background: var(--border); overflow: hidden; }
.exl-bar-fill { display: block; height: 5px; border-radius: 3px; }
.exl-chev { flex: none; display: inline-flex; color: var(--ink-muted); }
.exl-chev svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 44; }
.exl-lessons-in { margin: 0 0 12px 22px; display: flex; flex-direction: column; gap: 8px; }
.exl-lesson { width: 100%; display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 8px 12px; border-radius: 14px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.exl-lesson-name { flex: 1; font-size: 15px; font-weight: 600; line-height: 20px; }
.exl-play { width: 28px; height: 28px; flex: none; border-radius: 14px; display: inline-flex; align-items: center; justify-content: center; color: #fff; }
.exl-play svg { width: 14px; height: 14px; fill: currentColor; stroke: currentColor; stroke-width: 20; margin-left: 2px; }
/* A round. */
.exl-drill-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.exl-drill-head .exl-back { margin-bottom: 0; flex: none; }
.exl-drill-words { flex: 1; min-width: 0; }
.exl-drill-title { margin: 0; font-size: 16px; font-weight: 800; line-height: 20px; color: var(--ink); }
.exl-drill-sub { margin: 1px 0 0; font-size: 13px; color: var(--ink-muted); }
.exl-place { flex: none; font-size: 14px; font-weight: 700; color: var(--ink-muted); }
.exl-track { display: block; height: 6px; border-radius: 3px; overflow: hidden; background: var(--border); margin-bottom: 14px; }
.exl-track-fill { display: block; height: 6px; border-radius: 3px; transition: width 220ms ease-out; }
.exl-prompt { padding: 16px; border-radius: 22px; border: 2px solid var(--border); border-bottom-width: 5px; background: var(--surface-solid, var(--surface)); }
.exl-prompt.right, .exl-prompt.almost, .exl-prompt.wrong { border-color: var(--v); }
.exl-verdict { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.exl-verdict > :first-child { flex: none; }
.exl-verdict-pill { border-radius: 14px; padding: 5px 12px; color: #2B2118; font: 900 15px/1.25 var(--arc-font, inherit); }
.exl-word { margin: 0; font-family: var(--font-display, inherit); font-size: 30px; font-weight: 800; letter-spacing: -0.4px; color: var(--ink); overflow-wrap: anywhere; }
.exl-word.phrase { font-size: 24px; line-height: 30px; letter-spacing: -0.2px; }
.exl-word-sub { margin: 2px 0 0; font-size: 15px; color: var(--ink-muted); }
.exl-sentence { margin: 0; font-size: 21px; font-weight: 700; line-height: 29px; color: var(--ink); }
.exl-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.exl-tag { border-radius: 12px; border: 1px solid var(--border); background: var(--bg); padding: 4px 10px; font-size: 14px; font-weight: 700; color: var(--ink); }
.exl-answer-box { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.exl-answer { margin: 0; font-size: 24px; font-weight: 800; color: var(--ink); border-bottom: 3px solid #1FC98F; padding-bottom: 2px; }
.exl-why { margin: 0; font-size: 15px; line-height: 21px; color: var(--ink); }
.exl-after { margin: 10px 0 0; font-size: 14px; line-height: 19px; font-style: italic; color: var(--ink-muted); }
.exl-accents { display: flex; gap: 5px; margin-top: 14px; }
.exl-accent { flex: 1; min-width: 0; height: 46px; border-radius: 12px; border: 1px solid var(--border); border-bottom-width: 3px; background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; font-size: 20px; font-weight: 700; cursor: pointer; touch-action: manipulation; }
.exl-accent:active { transform: translateY(2px); border-bottom-width: 1px; }
.exl-input-row { display: flex; align-items: flex-start; gap: 8px; margin-top: 10px; }
.exl-input { flex: 1; min-width: 0; height: 52px; box-sizing: border-box; border-radius: 16px; border: 2px solid var(--border); background: var(--surface-solid, var(--surface)); color: var(--ink); padding: 0 14px; font: inherit; font-size: 20px; font-weight: 600; }
.exl-input:focus { outline: none; border-color: var(--ink-muted); }
.exl-input[readonly] { opacity: 1; }
.exl-page .arc-candy.exl-go { flex: none; width: 104px; min-height: 50px; padding: 0 12px; }
.exl-options { display: flex; flex-direction: column; gap: 8px; margin: 14px 0 6px; }
.exl-option { width: 100%; min-height: 52px; padding: 10px 14px; border-radius: 16px; border: 1px solid var(--border); border-bottom-width: 3px; background: var(--surface-solid, var(--surface)); color: var(--ink); font: inherit; font-size: 16px; font-weight: 700; line-height: 21px; text-align: left; cursor: pointer; }
.exl-option.right { background: #1FC98F; border-color: #1FC98F; color: #2B2118; }
.exl-option.wrong { background: #FF4F7B; border-color: #FF4F7B; color: #2B2118; }
.exl-option.dim { color: var(--ink-muted); }
.exl-done { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 18px; border-radius: 22px; border: 1px solid var(--border); border-bottom-width: 5px; background: var(--surface-solid, var(--surface)); }
.exl-done-line { margin: 0; font-size: 22px; font-weight: 800; text-align: center; color: var(--ink); font-family: var(--font-display, inherit); }
.exl-done-sub { margin: 0; font-size: 15px; text-align: center; color: var(--ink-muted); }
.exl-miss { border-radius: 14px; border: 1px solid var(--border); background: var(--surface-solid, var(--surface)); padding: 10px 12px; margin-bottom: 8px; }
.exl-miss-asked { margin: 0; font-size: 14px; line-height: 19px; color: var(--ink-muted); }
.exl-miss-answer { margin: 2px 0 0; font-size: 18px; font-weight: 800; color: var(--ink); }
.exl-solid { display: flex; flex-wrap: wrap; gap: 6px; }
.exl-solid .exl-tag { background: var(--surface-solid, var(--surface)); }
.exl-done-col { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.exl-done-col .arc-candy { margin-bottom: 5px; }
.exl-page .arc-candy.exl-done-btn { margin-top: 16px; }
/* ?fill=1 (app.js): the window is the card, so the card fills it. Declared
   last so it beats the desktop-card media query and the panes max-width. */
body.fill #root:has(> .app-shell) { padding: 0; }
body.fill .app-shell,
body.fill[data-page] .app-shell {
  max-width: none; height: var(--app-height, 100dvh);
  border: none; border-radius: 0; box-shadow: none;
}
