/* ============================================================================
   Design tokens — mirror Trove/DesignSystem/DesignSystem.swift exactly (DS enum).
   When DS.swift changes, update the matching value here. Each block below is
   commented with the Swift source it tracks so a diff is easy to follow.
   ============================================================================ */
:root {
  /* DS.Palette */
  --deep-ink: #070A12;
  --accent-glow: #A88CFA;     /* violet — primary accent */
  --accent-cool: #73D0FF;     /* cyan-blue — confirmations */
  --program-sw: #5AD1E8;
  --cyan: #73DEFF;
  --blue: #73A6FF;
  --violet: #A88CFA;
  --magenta: #F28CFF;
  --pink: #FF8FDC;
  --metal-light: #F5F7FA;
  --metal-mid: #B8C0CC;
  --metal-dark: #4A5260;
  --grade-1: #57E0C2;         /* teal-mint */
  --grade-2: #73D0FF;         /* cyan */
  --grade-3: #8FA6FF;         /* periwinkle */
  --grade-4: #B58CFA;         /* violet */
  --grade-5: #F28CFF;         /* magenta */

  /* DS.Glass */
  --glass-surface-light: rgba(255,255,255,0.05);
  --glass-surface-dark: rgba(255,255,255,0.035);
  --glass-border: rgba(255,255,255,0.16);
  --glass-specular: rgba(255,255,255,0.45);

  /* DS.Radius */
  --radius-card: 22px;
  --radius-bubble: 26px;
  --radius-pill: 999px;
  --radius-chip: 12px;
  --radius-field: 22px;

  /* DS.Space */
  --space-xs: 4px;
  --space-s: 8px;
  --space-m: 14px;
  --space-l: 22px;
  --space-xl: 36px;

  /* DS.BubbleSize */
  --bubble-w: 220px;
  --bubble-h: 290px;
  --bubble-mini: 160px;

  /* DS.Font — ui-rounded is WebKit's SF Pro Rounded; falls back gracefully elsewhere */
  --font-rounded: ui-rounded, -apple-system, "SF Pro Rounded", system-ui, sans-serif;
  --font-body: -apple-system, "SF Pro Text", system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", monospace;

  /* legacy aliases kept so the login screen (unchanged) still resolves */
  --ink: var(--deep-ink);
  --panel: var(--glass-surface-light);
  --border: var(--glass-border);
  --text: rgba(255,255,255,0.92);
  --sub: rgba(255,255,255,0.55);
  --glow: var(--accent-glow);
}

/* ============================================================================
   Cloud intake — a touch-first, private resource launch chamber.
   ============================================================================ */
.intake-button {
  position: relative; isolation: isolate; min-width: 72px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: 999px; border: 1px solid rgba(151,221,255,.35);
  background: linear-gradient(135deg, rgba(73,188,255,.16), rgba(183,109,255,.2));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.22), 0 8px 24px rgba(85,79,205,.16);
  overflow: hidden; transition: transform .28s cubic-bezier(.2,.8,.2,1), box-shadow .28s ease;
}
.intake-button::before {
  content: ""; position: absolute; z-index: -1; width: 55px; height: 110px; top: -34px; left: -70px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent);
  transform: rotate(24deg); transition: left .65s cubic-bezier(.2,.7,.2,1);
}
.intake-button:hover, .intake-button:focus-visible { transform: translateY(-2px); box-shadow: 0 10px 34px rgba(111,102,255,.3), inset 0 1px 0 rgba(255,255,255,.32); }
.intake-button:hover::before, .intake-button:focus-visible::before { left: 100px; }
.intake-button svg { width: 18px; height: 18px; }
.portal-intake { --portal-accent: #8fe9ff; }

.intake-overlay {
  position: fixed; inset: 0; z-index: 110; display: grid; place-items: center;
  padding: max(18px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) max(18px, env(safe-area-inset-bottom)) max(18px, env(safe-area-inset-left));
  background: rgba(2,4,14,.68); backdrop-filter: blur(20px) saturate(1.25); -webkit-backdrop-filter: blur(20px) saturate(1.25);
  animation: intakeVeil .3s ease both;
}
.intake-sheet {
  width: min(760px, 100%); max-height: min(840px, 100%); overflow: hidden;
  display: flex; flex-direction: column; padding: 22px;
  border-radius: 30px; background:
    radial-gradient(circle at 18% -8%, rgba(72,199,255,.2), transparent 34%),
    radial-gradient(circle at 90% 12%, rgba(203,109,255,.18), transparent 33%),
    linear-gradient(155deg, rgba(21,27,55,.94), rgba(10,11,29,.94));
  box-shadow: 0 28px 90px rgba(0,0,0,.55), 0 0 90px rgba(85,94,255,.12);
  animation: intakeArrive .5s cubic-bezier(.18,.86,.22,1) both;
}
.intake-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 18px; }
.intake-heading { display: flex; align-items: center; gap: 13px; min-width: 0; }
.intake-spark {
  width: 46px; height: 46px; flex: 0 0 auto; display: grid; place-items: center; border-radius: 16px;
  color: #fff; font-size: 20px; background: linear-gradient(145deg, rgba(85,211,255,.28), rgba(182,104,255,.36));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.34), 0 0 28px rgba(116,168,255,.25);
  animation: intakeSpark 4s ease-in-out infinite;
}
.intake-heading h2 { margin: 0; font: 700 25px/1.1 var(--font-rounded); letter-spacing: -.02em; }
.intake-heading p { margin: 5px 0 0; color: rgba(230,237,255,.62); font-size: 13px; }
.intake-close { width: 44px; height: 44px; padding: 0; flex: 0 0 auto; border-radius: 50%; background: rgba(255,255,255,.07); }
.intake-dropzone {
  position: relative; min-height: 176px; width: 100%; padding: 20px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 5px; border-radius: 24px; overflow: hidden;
  border: 1px dashed rgba(144,213,255,.46); background:
    radial-gradient(circle at 50% 12%, rgba(115,222,255,.13), transparent 44%),
    rgba(255,255,255,.035); transition: border-color .25s ease, transform .25s ease, background .25s ease;
}
.intake-dropzone::after {
  content: ""; position: absolute; inset: -100% -40%; pointer-events: none;
  background: conic-gradient(from 90deg, transparent, rgba(115,222,255,.12), transparent 35%, rgba(210,127,255,.1), transparent 70%);
  animation: intakeOrbit 18s linear infinite;
}
.intake-dropzone:hover, .intake-dropzone.is-dragging { transform: translateY(-2px); border-color: rgba(160,228,255,.85); background-color: rgba(102,137,255,.08); }
.intake-drop-orb { width: 54px; height: 54px; display: grid; place-items: center; border-radius: 50%; margin-bottom: 4px; background: linear-gradient(145deg, rgba(115,222,255,.2), rgba(179,116,255,.28)); box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 0 30px rgba(115,166,255,.25); }
.intake-drop-orb svg { width: 27px; height: 27px; }
.intake-dropzone strong { font: 650 17px var(--font-rounded); }
.intake-dropzone > span:not(.intake-drop-orb) { color: rgba(232,238,255,.62); }
.intake-dropzone small { color: rgba(219,228,255,.4); margin-top: 5px; }
.intake-queue-head { display: flex; justify-content: space-between; align-items: center; margin: 18px 2px 8px; color: rgba(237,242,255,.72); font: 650 12px var(--font-rounded); text-transform: uppercase; letter-spacing: .08em; }
.intake-queue-head button { min-height: 36px; padding: 7px 12px; border: 0; background: transparent; color: rgba(145,220,255,.82); text-transform: none; letter-spacing: 0; }
.intake-jobs { min-height: 80px; overflow-y: auto; overscroll-behavior: contain; display: grid; gap: 9px; padding: 2px 3px 3px; }
.intake-empty { min-height: 78px; display: grid; place-items: center; color: rgba(226,234,255,.42); border: 1px solid rgba(255,255,255,.07); border-radius: 18px; }
.intake-job {
  --job-hue: #8fdfff; position: relative; display: grid; grid-template-columns: 43px minmax(0,1fr) auto;
  align-items: center; gap: 12px; padding: 12px; border-radius: 18px; border: 1px solid rgba(255,255,255,.09);
  background: linear-gradient(110deg, rgba(255,255,255,.06), rgba(255,255,255,.025)); overflow: hidden;
  animation: intakeItem .42s cubic-bezier(.2,.8,.2,1) both;
}
.intake-job::after { content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: var(--job-progress, 0%); background: linear-gradient(90deg, #73deff, #a88cfa, #f28cff); box-shadow: 0 0 10px var(--job-hue); transition: width .45s ease; }
.intake-job-icon { width: 43px; height: 43px; display: grid; place-items: center; border-radius: 14px; color: var(--job-hue); background: color-mix(in srgb, var(--job-hue) 15%, transparent); border: 1px solid color-mix(in srgb, var(--job-hue) 24%, transparent); }
.intake-job-icon svg { width: 21px; height: 21px; }
.intake-job.is-working .intake-job-icon { animation: intakeBreathe 2s ease-in-out infinite; }
.intake-job-main { min-width: 0; }
.intake-job-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 620; }
.intake-job-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 3px; color: rgba(224,233,255,.5); font-size: 12px; }
.intake-job-status { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; color: rgba(225,235,255,.76); font-size: 12px; white-space: nowrap; }
.intake-job-status small { color: rgba(215,225,255,.42); font: 10px var(--font-mono); }
.intake-footer { min-height: 42px; display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; padding: 0 4px; color: rgba(220,229,255,.45); font-size: 11px; }
.intake-live-dot { display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: #57e0c2; box-shadow: 0 0 12px #57e0c2; }

@keyframes intakeVeil { from { opacity: 0; } }
@keyframes intakeArrive { from { opacity: 0; transform: translateY(24px) scale(.96); } }
@keyframes intakeItem { from { opacity: 0; transform: translateY(8px); } }
@keyframes intakeSpark { 50% { transform: rotate(8deg) scale(1.08); box-shadow: 0 0 38px rgba(153,112,255,.36); } }
@keyframes intakeOrbit { to { transform: rotate(360deg); } }
@keyframes intakeBreathe { 50% { transform: scale(1.06); filter: brightness(1.25); } }

@media (max-width: 760px) {
  .intake-button { min-width: 44px; padding-inline: 11px; }
  .intake-button span { display: none; }
  .intake-overlay { place-items: end center; padding: max(8px, env(safe-area-inset-top)) 0 0; }
  .intake-sheet { width: 100%; max-height: 94dvh; border-radius: 30px 30px 0 0; padding: 18px 15px max(14px, env(safe-area-inset-bottom)); }
  .intake-dropzone { min-height: 156px; }
  .intake-footer { align-items: flex-start; }
}

@media (max-width: 520px) {
  .intake-heading p { max-width: 260px; }
  .intake-job { grid-template-columns: 40px minmax(0,1fr); }
  .intake-job-status { grid-column: 2; align-items: flex-start; flex-direction: row; }
}

@media (prefers-reduced-motion: reduce) {
  .intake-overlay, .intake-sheet, .intake-job { animation: none; }
  .intake-spark, .intake-dropzone::after, .intake-job.is-working .intake-job-icon { animation: none; }
  .intake-button, .intake-dropzone { transition-duration: .01ms; }
}

/* DS.iridescentGradient — angular cyan→blue→violet→magenta→pink→cyan */
.iridescent-border {
  background: conic-gradient(from 0deg, #8FE9FF, #73A6FF, #A88CFA, #F28CFF, #FF8FDC, #8FE9FF);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

[hidden] { display: none !important; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:where(button, input, textarea, select, a):focus-visible {
  outline: 3px solid #73deff;
  outline-offset: 3px;
}
.bubble:focus-visible, .rising-bubble:focus-visible {
  outline: 3px solid #73deff;
  outline-offset: 4px;
}

html, body {
  margin: 0; padding: 0; height: 100%;
  background: var(--deep-ink);
  color: var(--text);
  font: 15px/1.4 var(--font-body);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
  /* The page itself never scrolls — only .mode-panel does, internally. This is what
     keeps #app (and the fixed #backdrop behind it) locked to exactly one viewport
     height instead of a page-level scrollbar appearing. See #app/.mode-panel below:
     a real bug shipped here once already from guessing the command bar's height with
     a hardcoded `calc(100dvh - 100px)` instead of measuring it via flex — the guess
     was off by ~22px, leaving the page 66px taller than the viewport and (on real
     iPad Safari, whose dynamic toolbar makes fixed-position sizing unreliable across
     scroll) a visible black gap at the bottom of the backdrop. Flex layout below
     removes the guess entirely: the command bar takes whatever height it needs, and
     .mode-panel fills the exact remainder, no matter how tall the bar renders. */
  overflow: hidden;
}

button, input, select {
  font: inherit; color: var(--text);
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 12px; padding: 10px 14px;
}
button { cursor: pointer; }
/* Touch feedback comes from each control's material/pressed state. A global
   scale made unrelated controls jump under an iPad tap. */
button:active { opacity: .9; }

/* ============================================================================
   Glass panel — mirrors GlassPanel.swift's .glass() modifier: ultraThinMaterial
   base + specular top-edge highlight + metallic top-left→bottom-right border.
   ============================================================================ */
.glass {
  position: relative;
  background: rgba(255,255,255,0.10);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  border-radius: var(--radius-card);
  box-shadow: 0 4px 18px rgba(0,0,0,0.18);
}
.glass::before {
  /* metallic border gradient, DS.metalBorder */
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: linear-gradient(135deg,
    rgba(245,247,250,0.5), rgba(115,222,255,0.18), rgba(168,140,250,0.18), rgba(184,192,204,0.25));
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events: none;
}
.glass::after {
  /* 1px specular top-edge highlight */
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(to bottom, var(--glass-specular), transparent 50%);
  opacity: 0.35; mix-blend-mode: screen; pointer-events: none;
  -webkit-mask: linear-gradient(#fff, transparent 50%);
  mask: linear-gradient(#fff, transparent 50%);
}
.glass-bright::before { padding: 1.4px; }

/* #app is a flex COLUMN pinned to exactly one viewport tall (not min-height — height,
   so it can never exceed the viewport and force a page scrollbar). .commandbar takes
   whatever height its content needs as the first flex item; .mode-panel below it is
   the only thing that scrolls (internally, via its own overflow-y), sized to exactly
   fill whatever space remains — no guessing the command bar's height anywhere. */
.app { display: flex; flex-direction: column; height: 100dvh; position: relative; }

/* ============================================================================
   App backdrop — mirrors RootView.TroveBackground: full-bleed art + dark overlay.
   Two states: home (WelcomeView showing) vs search-chat (results/other modes).
   ============================================================================ */
.backdrop {
  /* z-index MUST NOT be negative: #backdrop's parent (#app) doesn't establish its own
     stacking context (position:relative with no explicit z-index), so a negative
     z-index here promotes #backdrop into the ROOT stacking context where it competes
     with body/html's own painting — confirmed via WebKit to silently fail to
     composite the ::before background-image entirely (solid color only) on portrait/
     iPad-width viewports, even though every computed style reports correctly. Since
     #backdrop is already the FIRST child of #app, z-index:0 paints it before all its
     normal-flow siblings with no visual difference from -1 in browsers that don't hit
     this bug, and fixes the ones that do. */
  position: fixed; inset: 0; z-index: 0; overflow: hidden;
  background-color: var(--deep-ink);
  /* iOS/iPadOS: a `fixed; inset:0` element is sized to the SMALL viewport, so when the
     Safari toolbar hides (or under the home-indicator safe area) a strip at the bottom
     was left uncovered — the "black cutoff" Sky reported. Pin the height to the LARGE
     viewport so the art always fills the maximal screen area behind every chrome state. */
  height: 100vh; height: 100lvh; width: 100vw; width: 100lvw;
}
.backdrop::before {
  content: ""; position: absolute; inset: -6%;
  background-size: cover;
  background-position: center;
  transition: opacity .5s ease-in-out, filter .5s ease-in-out;
  animation: skyDrift 75s ease-in-out infinite alternate;
}
.backdrop::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 45%, transparent 0 38%, rgba(0,0,0,.2) 85%), rgba(1,3,10,.08);
}
.backdrop.home::before { background-image: url('/assets/trove-home-background.webp'); }
.backdrop.search::before {
  background-image: url('/assets/trove-search-chat-background.webp');
  filter: saturate(.96) brightness(.86);
}
@keyframes skyDrift {
  0%   { transform: scale(1.05) translate3d(0, 0, 0); }
  100% { transform: scale(1.12) translate3d(-1.1%, -.8%, 0); }
}

/* Aurora ribbons — the sky's heartbeat. */
.backdrop .aurora {
  position: absolute; border-radius: 50%; pointer-events: none;
  width: 62vmax; height: 62vmax; filter: blur(70px); mix-blend-mode: screen;
}
.backdrop .aurora-a {
  left: -18vmax; top: -14vmax; opacity: 0.34;
  background: radial-gradient(circle at 42% 46%, rgba(115,222,255,0.6), rgba(115,166,255,0.2) 55%, transparent 72%);
  animation: auroraA 52s ease-in-out infinite alternate;
}
.backdrop .aurora-b {
  right: -22vmax; bottom: -18vmax; opacity: 0.30;
  background: radial-gradient(circle at 56% 52%, rgba(168,140,250,0.56), rgba(242,140,255,0.18) 55%, transparent 72%);
  animation: auroraB 67s ease-in-out infinite alternate;
}
.backdrop .aurora-c {
  left: 30%; top: 34%; width: 54vmax; height: 40vmax; opacity: 0.16;
  background: radial-gradient(ellipse at 50% 50%, rgba(87,224,194,0.42), rgba(115,208,255,0.14) 52%, transparent 70%);
  animation: auroraC 83s ease-in-out infinite alternate;
}
.backdrop.search .aurora { opacity: 0.14; }
@keyframes auroraA {
  0%   { transform: translate(0, 0) scale(1); }
  100% { transform: translate(9vmax, 7vmax) scale(1.18); }
}
@keyframes auroraB {
  0%   { transform: translate(0, 0) scale(1.12); }
  100% { transform: translate(-8vmax, -6vmax) scale(0.94); }
}
@keyframes auroraC {
  0%   { transform: translate(0, 0) rotate(-6deg) scale(1); }
  100% { transform: translate(-6vmax, 5vmax) rotate(8deg) scale(1.22); }
}

#skyCanvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  /* Reduce, don't freeze: the aurora keeps a slow opacity pulse (no translation,
     nothing vestibular) so the sky still feels alive. Per L36, never leave
     near-zero-duration infinite animations. */
  .backdrop::before, .backdrop .aurora { animation: none; }
  #skyCanvas { display: none; }
}

/* ============================================================================
   Command bar — mirrors CommandBar.swift: glyph, search field, mode pills,
   filter row (grade pills + program/category/type facet menus).
   ============================================================================ */
.commandbar {
  position: sticky; top: max(12px, env(safe-area-inset-top)); z-index: 30;
  margin: 12px 12px 0;
  padding: var(--space-m);
  display: flex; flex-direction: column; gap: var(--space-s);
}
/* On home the bubble IS the command center — the bar leaves entirely (display, not
   transform: animating layout height is jankier than a one-shot entrance when it
   returns). barIn plays whenever the bar comes back for results/other modes. */
.commandbar.bar-hidden { display: none; }
.commandbar.bar-entering { animation: barIn 0.35s ease; }
@keyframes barIn {
  from { transform: translateY(-14px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .commandbar.bar-entering { animation: none; } }
.commandbar-row { display: flex; align-items: center; gap: var(--space-m); }
.glyph {
  width: 34px; height: 34px; flex: none;
  object-fit: cover; border-radius: 10px;
  filter: drop-shadow(0 0 8px rgba(115,222,255,0.2)) drop-shadow(0 0 12px rgba(168,140,250,0.15));
}
.searchfield {
  flex: 1; min-width: 100px; display: flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 14px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--glass-border);
  transition: border-color 0.2s, box-shadow 0.2s;
}
.searchfield:focus-within {
  border: none;
  box-shadow: 0 0 0 1.4px transparent;
  background-image: linear-gradient(rgba(20,22,34,0.9), rgba(20,22,34,0.9)),
    conic-gradient(from 0deg, #8FE9FF, #73A6FF, #A88CFA, #F28CFF, #FF8FDC, #8FE9FF);
  background-origin: border-box; background-clip: padding-box, border-box;
  border: 1.4px solid transparent;
}
.searchfield svg { flex: none; width: 15px; height: 15px; }
.searchfield-submit { width: 24px; height: 24px; flex: none; display: grid; place-items: center; padding: 0; background: none; border: 0; color: rgba(255,255,255,.6); }
.searchfield input {
  flex: 1; min-width: 0; background: none; border: none; padding: 0;
  font: 15px var(--font-rounded); color: var(--text);
  /* On narrow (iPad-portrait/phone) widths the full placeholder doesn't fit and was
     hard-clipping mid-word with no visual cue — every other truncated label in this
     file (collection items, sheet rows, preview titles) already fades with an
     ellipsis; the search field was the one place missing it. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.searchfield input::placeholder { color: rgba(255,255,255,0.4); }
.searchfield-clear { background: none; border: none; padding: 4px; color: rgba(255,255,255,0.4); border-radius: 999px; }

.modepills { display: flex; gap: 6px; flex: none; }
.modepill {
  display: flex; align-items: center; gap: 5px;
  height: 34px; padding: 0 12px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.06); border: 1px solid var(--glass-border);
  font: 13px/1 var(--font-rounded); font-weight: 600; color: rgba(255,255,255,0.6);
  white-space: nowrap;
}
.modepill svg { width: 13px; height: 13px; }
.modepill.active {
  color: #fff;
  background-color: rgba(168,140,250,0.22);
  box-shadow: 0 0 12px rgba(168,140,250,0.5);
}

.filterrow { display: flex; align-items: center; gap: var(--space-m); flex-wrap: wrap; }
.filterrow .label { font: 12px var(--font-body); font-weight: 500; color: rgba(255,255,255,0.45); }

.gradepills { display: flex; align-items: center; gap: 6px; }
.gradepill {
  height: 30px; padding: 0 12px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.06); border: 1px solid var(--glass-border);
  font: 12px var(--font-rounded); font-weight: 600; color: rgba(255,255,255,0.6);
}
.gradepill.active { color: #fff; }
.gradepill.active[data-grade="1"] { background-color: rgba(87,224,194,0.30); box-shadow: 0 0 10px rgba(87,224,194,0.55); }
.gradepill.active[data-grade="2"] { background-color: rgba(115,208,255,0.30); box-shadow: 0 0 10px rgba(115,208,255,0.55); }
.gradepill.active[data-grade="3"] { background-color: rgba(143,166,255,0.30); box-shadow: 0 0 10px rgba(143,166,255,0.55); }
.gradepill.active[data-grade="4"] { background-color: rgba(181,140,250,0.30); box-shadow: 0 0 10px rgba(181,140,250,0.55); }
.gradepill.active[data-grade="5"] { background-color: rgba(242,140,255,0.30); box-shadow: 0 0 10px rgba(242,140,255,0.55); }

.facetmenu {
  display: flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 4px;
  background: none; border: none; color: rgba(255,255,255,0.6);
  font: 12px var(--font-body); font-weight: 500;
}
.facetmenu .val { color: #fff; }
.facetmenu svg { width: 9px; height: 9px; opacity: 0.5; }
.clear-filters {
  display: flex; align-items: center; gap: 5px; height: 30px; padding: 0 8px;
  background: none; border: none; color: rgba(255,255,255,.48);
  font: 11px var(--font-body); font-weight: 500;
}
.clear-filters svg { width: 13px; height: 13px; }

/* ============================================================================
   Welcome bubble — mirrors WelcomeView.swift: the big glass orb with the
   wordmark + search field inside, floating resource bubbles rising behind it.
   ============================================================================ */
.welcome {
  min-height: 100%;
  /* Column: the Chat/Students pills rest just under the orb, not beside it. Shared
     --orb-size lets those pills nest into the orb's lower glass responsively. */
  --orb-size: clamp(310px, min(84vh, 72vw), 600px);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  position: relative; overflow: hidden; padding: var(--space-l);
}
.welcome-bubble {
  position: relative; z-index: 1;
  width: var(--orb-size); aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  animation: breathe 6.5s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes breathe {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(0, -9px, 0) scale(1.015); }
}
.welcome-bubble img.orb {
  display: block; position: absolute; inset: -1%; width: 102%; height: 102%; object-fit: contain;
  filter: drop-shadow(0 0 34px rgba(115,222,255,.34)) drop-shadow(0 0 64px rgba(168,140,250,.3));
  pointer-events: none;
  /* Defensive scope: this artwork must move only with .welcome-bubble. */
  animation: none !important; opacity: 1; transform: none;
}
.welcome-content {
  position: relative; z-index: 1; width: 69%; transform: translateY(-1%);
  display: flex; flex-direction: column; align-items: center; gap: 0;
}
.welcome-content img.logo {
  width: min(100%, 320px); margin-bottom: 8px;
  filter: drop-shadow(0 0 16px rgba(115,222,255,0.4));
}
.welcome-search {
  width: 100%; max-width: 320px; height: 50px;
  display: flex; align-items: center; gap: 8px; padding: 0 18px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.10); backdrop-filter: blur(18px);
  border: 1.4px solid transparent;
  background-image: linear-gradient(rgba(255,255,255,0.10), rgba(255,255,255,0.10)),
    conic-gradient(from 0deg, #8FE9FF, #73A6FF, #A88CFA, #F28CFF, #FF8FDC, #8FE9FF);
  background-origin: border-box; background-clip: padding-box, border-box;
  opacity: 0.95;
}
.welcome-search svg { width: 15px; height: 15px; color: var(--violet); flex: none; }
.welcome-search input {
  flex: 1; min-width: 0; background: none; border: none; padding: 0;
  font: 15px var(--font-rounded); font-weight: 500; color: #fff;
}
.welcome-search input::placeholder { color: rgba(255,255,255,.66); }
.welcome-random {
  width: 28px; height: 28px; flex: none; display: grid; place-items: center;
  padding: 0; border: 0; border-radius: 50%; background: transparent;
  color: rgba(255,255,255,.55);
}
.welcome-random:hover { color: #fff; background: rgba(255,255,255,.10); }
.welcome-random svg { width: 15px; height: 15px; }
.welcome-stat {
  margin-top: 7px; font: 10px var(--font-mono); font-weight: 560; color: rgba(235,242,255,.58);
  text-shadow: 0 1px 12px rgba(2,5,20,.75);
}

/* Bubble command center — grade + program filters INSIDE the orb (the command bar
   stays hidden on home; these are the same state, smaller clothes). */
.bubble-filters {
  margin-top: 12px; display: flex; align-items: center; justify-content: center;
  gap: 7px; flex-wrap: wrap; padding: 5px 6px;
  border: 1px solid rgba(221,237,255,.22); border-radius: 999px;
  background: linear-gradient(180deg, rgba(255,255,255,.13), rgba(112,109,210,.055));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 9px 28px rgba(2,5,22,.2);
  backdrop-filter: blur(18px) saturate(1.25); -webkit-backdrop-filter: blur(18px) saturate(1.25);
}
.bubble-grades { display: flex; align-items: center; gap: 5px; }
.bubble-grades .gradepill {
  width: 26px; height: 26px; padding: 0; border-radius: 50%;
  font: 12px var(--font-rounded); font-weight: 600;
  background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.22);
  color: rgba(255,255,255,0.85);
}
.bubble-grades .gradepill.active {
  background: rgba(168,140,250,0.45); border-color: rgba(198,180,255,0.9); color: #fff;
  box-shadow: 0 0 10px rgba(168,140,250,0.5);
}
.bubble-program {
  display: flex; align-items: center; gap: 6px; height: 26px; padding: 0 11px;
  border-radius: 13px; background: rgba(255,255,255,0.10);
  border: 1px solid rgba(255,255,255,0.22); font: 11px var(--font-rounded); font-weight: 600;
}
.bubble-program .bp-label { color: rgba(255,255,255,0.5); }
.bubble-program .bp-val { color: #fff; }
.bubble-program.active-filter {
  background: rgba(115,222,255,0.28); border-color: rgba(143,233,255,0.85);
  box-shadow: 0 0 10px rgba(115,222,255,0.4);
}
.recent-searches {
  max-width: 330px; margin-top: 8px; display: flex; align-items: center;
  justify-content: center; gap: 6px;
}
.recent-searches button {
  min-width: 0; max-width: 108px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; padding: 5px 10px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,.08); border: .8px solid rgba(255,255,255,.15);
  color: rgba(255,255,255,.75); font: 11px var(--font-rounded); font-weight: 500;
}

/* Chat & Students — two clean destinations below the search crystal. Each pill is
   self-contained: no surrounding glass box and no overlap with the orb artwork. */
.welcome-portals {
  position: relative; z-index: 2; display: flex; gap: 14px;
  margin-top: -10px;
  padding: 0; border: 0; background: none; box-shadow: none; backdrop-filter: none;
}
.portal {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: 46px; padding: 0 24px; border-radius: 999px;
  background: linear-gradient(180deg, rgba(29,36,72,.76), rgba(11,16,43,.7));
  border: 1px solid rgba(226,239,255,.30);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.32), 0 12px 32px rgba(3,6,20,.4);
  backdrop-filter: blur(22px) saturate(1.35); -webkit-backdrop-filter: blur(22px) saturate(1.35);
  font: 15px var(--font-rounded); font-weight: 650; color: #fff;
  text-shadow: 0 1px 10px rgba(2,5,20,.5);
  transition: transform .24s cubic-bezier(.16,1,.3,1), background .2s ease, border-color .2s ease, box-shadow .24s ease;
}
.portal svg { width: 18px; height: 18px; opacity: .92; }
.portal:hover {
  transform: translateY(-3px) scale(1.03);
  background: linear-gradient(180deg, rgba(255,255,255,.26), rgba(154,139,250,.16));
  border-color: rgba(167,226,255,.66);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 16px 40px rgba(115,166,255,.32), 0 0 26px rgba(194,126,255,.18);
}
.portal:active { transform: translateY(-1px) scale(.99); }
.rising-field {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
}
.rising-bubble {
  position: absolute; border-radius: 50%; overflow: hidden; pointer-events: auto;
  border: 1.6px solid transparent;
  background-image: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0)),
    conic-gradient(from 0deg, #8FE9FF, #73A6FF, #A88CFA, #F28CFF, #FF8FDC, #8FE9FF);
  background-origin: border-box; background-clip: padding-box, border-box;
  box-shadow: 0 0 12px rgba(0,0,0,0.3);
}
.rising-bubble img { width: 100%; height: 100%; object-fit: cover; }
/* Was JS-injected as a <style> tag; lives here so the CSP can stay style-src 'self'. */
@keyframes riseUp {
  0%   { transform: translateY(100vh); opacity: 0; }
  10%  { opacity: 0.9; }
  85%  { opacity: 0.9; }
  100% { transform: translateY(-20vh); opacity: 0; }
}

/* ============================================================================
   Result bubble field — mirrors CommandView's FlowField/BubbleView: wrapping
   grid of glass tiles, preview filling the rounded top, meta strip below.
   ============================================================================ */
.results-wrap { padding: var(--space-l); padding-bottom: 140px; max-width: 1400px; margin: 0 auto; }
.result-banner { position: relative; z-index: 1; display: flex; align-items: center; gap: var(--space-m); flex-wrap: wrap; padding: 0 var(--space-s); margin-bottom: var(--space-l); }
.result-count { font: 13px var(--font-rounded); font-weight: 600; color: rgba(255,255,255,0.85); }
.result-query { font: 13px var(--font-body); color: rgba(255,255,255,0.55); }
.result-spacer { flex: 1; }
.result-tool { min-height: 30px; padding: 0 12px; border-radius: var(--radius-pill); border: 1px solid rgba(168,140,250,.55); background: rgba(168,140,250,.22); color: #fff; font: 12px var(--font-rounded); font-weight: 650; }
.result-sort-label { display: flex; align-items: center; gap: 6px; color: rgba(255,255,255,.5); font: 11px var(--font-body); }
.result-sort { min-height: 30px; padding: 0 28px 0 9px; border-radius: 9px; background-color: rgba(255,255,255,.07); color: rgba(255,255,255,.82); border: 1px solid var(--glass-border); font: 11px var(--font-body); }
.filter-pills { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: 6px; }
.filter-pills:empty { display: none; }
.filter-pill { min-height: 26px; padding: 0 9px; border-radius: var(--radius-pill); background: rgba(168,140,250,.16); border: 1px solid var(--glass-border); color: rgba(255,255,255,.84); font: 11px var(--font-body); }
.filter-pill span { color: rgba(255,255,255,.48); margin-left: 3px; }
.show-more { position: relative; z-index: 1; display: block; margin: var(--space-l) auto 0; padding: 10px 22px; border: 1px solid var(--glass-border); border-radius: var(--radius-pill); color: #fff; font: 14px var(--font-rounded); font-weight: 600; }

.bubble-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(calc(var(--bubble-w) + 24px), 1fr));
  gap: var(--space-l);
  justify-items: center;
}
/* Shimmer placeholder shown while a search round-trips (same footprint as .bubble). */
.bubble-skel {
  width: var(--bubble-w); height: var(--bubble-h);
  border-radius: var(--radius-bubble);
  background: linear-gradient(100deg, rgba(255,255,255,0.05) 35%, rgba(255,255,255,0.12) 50%, rgba(255,255,255,0.05) 65%);
  background-size: 240% 100%;
  animation: skelShimmer 1.2s linear infinite;
  border: 1px solid rgba(255,255,255,0.08);
}
@keyframes skelShimmer { from { background-position: 120% 0; } to { background-position: -120% 0; } }
@media (prefers-reduced-motion: reduce) { .bubble-skel { animation: none; } }
.bubble {
  width: var(--bubble-w); height: var(--bubble-h);
  border-radius: var(--radius-bubble);
  background: rgba(255,255,255,0.08); backdrop-filter: blur(20px);
  border: 2px solid transparent;
  background-image: linear-gradient(rgba(255,255,255,0.08), rgba(255,255,255,0.08)),
    conic-gradient(from 0deg, #8FE9FF, #73A6FF, #A88CFA, #F28CFF, #FF8FDC, #8FE9FF);
  background-origin: border-box; background-clip: padding-box, border-box;
  opacity: 0.9;
  overflow: hidden; position: relative;
  display: flex; flex-direction: column;
  box-shadow: 0 8px 26px rgba(0,0,0,0.3);
  animation: bubble-in 0.4s cubic-bezier(.2,1.4,.4,1) backwards;
  transition: transform 0.2s ease;
}
.bubble.keyboard-active {
  outline: 3px solid var(--accent-cool); outline-offset: 4px;
  box-shadow: 0 0 0 7px rgba(115,222,255,.14), 0 12px 34px rgba(0,0,0,.38);
}
.bubble:active { transform: scale(0.97); }
@keyframes bubble-in {
  from { opacity: 0; transform: scale(0.82) translateY(6px); filter: blur(5px); }
  to   { opacity: 1; transform: scale(1) translateY(0); filter: blur(0); }
}
.bubble-preview {
  width: 100%; height: 63%; flex: none; position: relative; overflow: hidden;
  background: rgba(255,255,255,0.04);
}
.bubble-preview img { width: 100%; height: 100%; object-fit: cover; }
.bubble-preview::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(255,255,255,0.18), transparent 40%);
  mix-blend-mode: screen;
}
.bubble-preview-wash {
  position: absolute; left: 0; right: 0; bottom: 0; height: 45%;
  background: linear-gradient(to bottom, transparent, var(--wash, rgba(115,222,255,0.28)));
}
.bubble-meta {
  flex: 1; padding: 9px 13px 12px; display: flex; flex-direction: column; gap: 5px;
  background: linear-gradient(to top, var(--wash, rgba(115,222,255,0.10)), transparent 70%);
}
.bubble-title {
  font: 13px var(--font-rounded); font-weight: 600; color: #fff;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bubble-unit { font: 11px var(--font-mono); font-weight: 500; color: rgba(255,255,255,0.55); }
.bubble-chip {
  align-self: flex-start; font: 11px var(--font-body); font-weight: 500; color: rgba(255,255,255,0.75);
  padding: 4px 9px; border-radius: var(--radius-chip);
  background: color-mix(in srgb, var(--wash-solid, var(--accent-cool)) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--wash-solid, var(--accent-cool)) 30%, transparent);
}
.bubble-foot { margin-top: auto; display: flex; align-items: center; gap: 6px; }
.bubble-dots { display: flex; gap: 5px; flex: 1; }
.bubble-dot { width: 7px; height: 7px; border-radius: 50%; }
.bubble-download {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.4); border: 1px solid var(--glass-border); color: rgba(255,255,255,0.8);
  text-decoration: none; font-size: 12px;
}
.bubble-download svg { width: 15px; height: 15px; }
.bubble-open-hit {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; padding: 0;
  border: 0; border-radius: inherit; background: transparent;
  cursor: pointer;
}
.bubble-open-hit:focus-visible {
  outline: 3px solid #73deff;
  outline-offset: -5px;
}
.bubble-download, .bubble-check { position: relative; z-index: 2; }
.preview-fallback { height: 100%; display: grid; place-items: center; color: rgba(255,255,255,.5); }
.preview-fallback svg { width: 38px; height: 38px; }
.bubble-check {
  position: absolute; top: 8px; left: 8px; width: 22px; height: 22px; border-radius: 50%;
  background: rgba(0,0,0,0.5); border: 1.5px solid rgba(255,255,255,0.6); z-index: 2;
  display: flex; align-items: center; justify-content: center; font-size: 12px; color: #fff;
}
.bubble.selected .bubble-check { background: var(--accent-glow); border-color: var(--accent-glow); }

.empty-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-m);
  padding: 80px 20px; text-align: center; color: rgba(255,255,255,0.5);
}
.empty-state svg { width: 44px; height: 44px; opacity: 0.4; }
.empty-state .headline { font: 17px var(--font-rounded); font-weight: 500; color: rgba(255,255,255,0.7); }
.empty-state .sub { font: 13px var(--font-body); color: rgba(255,255,255,0.45); max-width: 360px; }

/* Collection tray — mirrors CollectionTray.swift's bottom-right retractable shelf. */
.collection-tray {
  position: fixed; right: var(--space-l); bottom: var(--space-l); z-index: 25;
  width: 280px; display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-s);
}
.collection-toggle {
  width: auto; min-width: 160px; height: 42px; display: flex; align-items: center;
  justify-content: center; gap: 8px; padding: 0 14px; border: 0;
  border-radius: var(--radius-pill); color: rgba(255,255,255,.9);
  font: 13px var(--font-rounded); font-weight: 600;
}
.collection-toggle svg { width: 16px; height: 16px; }
.collection-toggle.has-items { box-shadow: 0 0 18px rgba(115,208,255,.28); }
.collection-count {
  padding: 2px 7px; border-radius: var(--radius-pill); background: rgba(115,208,255,.35);
  font: 11px var(--font-mono); font-weight: 700;
}
.collection-chevron { font-size: 11px; opacity: .7; transition: transform .2s; }
.collection-toggle[aria-expanded="true"] .collection-chevron { transform: rotate(180deg); }
.collection-panel { width: 280px; padding: var(--space-m); }
.collection-list { max-height: 300px; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.collection-item {
  width: 100%; display: flex; align-items: center; gap: 8px; padding: 6px;
  text-align: left; background: rgba(255,255,255,.03); border: 0; border-radius: 10px;
}
.collection-item:hover { background: rgba(255,255,255,.08); }
.collection-item img { width: 34px; height: 42px; object-fit: cover; border-radius: 6px; }
.collection-item > span { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.collection-item strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 11px var(--font-body); font-weight: 600; }
.collection-item small { color: rgba(255,255,255,.42); font: 9px var(--font-mono); }
.collection-item b { color: rgba(255,255,255,.38); font-size: 16px; }
.collection-primary, .collection-secondary, .collection-clear {
  width: 100%; margin-top: var(--space-s); border-radius: var(--radius-pill); font: 13px var(--font-rounded); font-weight: 600;
}
.collection-primary { border: 0; background: rgba(168,140,250,.9); color: #fff; }
.collection-secondary { background: rgba(255,255,255,.08); border: 1px solid var(--glass-border); }
.collection-clear { margin-top: 5px; padding: 6px; background: transparent; border: 0; color: rgba(255,255,255,.45); font-size: 11px; }

/* Native-style modal sheets shared by Note, Save & Download, and Add to Slate. */
.web-sheet-overlay {
  position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center;
  padding: var(--space-l); background: rgba(2,4,10,.42); backdrop-filter: blur(18px) saturate(1.1);
}
.web-sheet { width: min(520px,100%); max-height: min(680px,calc(100dvh - 32px)); overflow-y: auto; padding: var(--space-xl); border-radius: 24px; }
.web-sheet h2 { margin: 0; font: 20px var(--font-rounded); font-weight: 700; }
.web-sheet .sheet-subtitle { margin: 5px 0 var(--space-m); color: rgba(255,255,255,.55); font: 13px var(--font-body); }
.web-sheet label { display: flex; flex-direction: column; gap: 6px; margin-top: var(--space-m); color: rgba(255,255,255,.72); font: 12px var(--font-body); }
.web-sheet input, .web-sheet textarea, .web-sheet select { width: 100%; }
.web-sheet textarea { min-height: 120px; resize: vertical; }
.sheet-segments { display: grid; grid-template-columns: repeat(3,1fr); gap: 4px; margin: var(--space-m) 0; padding: 4px; border-radius: 12px; background: rgba(255,255,255,.06); }
.sheet-segments button { min-height: 34px; padding: 5px 8px; border: 0; border-radius: 9px; background: transparent; color: rgba(255,255,255,.58); font: 11px var(--font-rounded); }
.sheet-segments button.active { color: #fff; background: rgba(168,140,250,.34); }
.sheet-list { max-height: 260px; overflow-y: auto; margin: var(--space-m) 0; padding-top: 8px; border-top: 1px solid rgba(255,255,255,.1); }
.sheet-list-row { display: flex; align-items: center; gap: 9px; padding: 6px 2px; }
.sheet-list-row img { width: 28px; height: 36px; object-fit: cover; border-radius: 5px; }
.sheet-list-row span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 12px var(--font-body); }
.sheet-list-row button { border: 0; background: transparent; color: rgba(255,255,255,.4); }
.sheet-info { margin: var(--space-m) 0; color: var(--accent-cool); font: 12px var(--font-body); }
.sheet-error { color: #ff9d9d; }
.sheet-actions { display: flex; justify-content: flex-end; gap: var(--space-s); margin-top: var(--space-l); }
.sheet-actions button { border-radius: 10px; padding: 9px 14px; background: rgba(255,255,255,.08); border: 1px solid var(--glass-border); }
.sheet-actions .primary { color: #fff; background: rgba(168,140,250,.88); border-color: transparent; font-weight: 700; }
.sheet-actions button:disabled { opacity: .42; }
.sheet-check { flex-direction: row !important; align-items: center; gap: 8px !important; }
.sheet-check input { width: auto; }

/* ============================================================================
   Login screen (unchanged visual language, retained as-is)
   ============================================================================ */
.login-gate { position: fixed; inset: 0; overflow: hidden; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login-orbs { position: absolute; inset: 0; z-index: 0; }
.login-orb { position: absolute; border-radius: 50%; filter: blur(60px); opacity: 0.55; animation: orbDrift 22s ease-in-out infinite; }
.login-orb-a { width: 380px; height: 380px; left: -80px; top: -60px; background: var(--cyan); animation-duration: 26s; }
.login-orb-b { width: 320px; height: 320px; right: -60px; top: 10%; background: var(--violet); animation-duration: 19s; animation-delay: -6s; }
.login-orb-c { width: 300px; height: 300px; left: 20%; bottom: -120px; background: var(--glow); animation-duration: 24s; animation-delay: -12s; }
@keyframes orbDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(30px, 40px) scale(1.08); }
  66%      { transform: translate(-24px, 18px) scale(0.94); }
}
.login-card {
  position: relative; z-index: 1; max-width: 380px; width: 100%; text-align: center;
  background: rgba(255,255,255,0.07); backdrop-filter: blur(24px);
  border: 1px solid var(--border); border-radius: 24px; padding: 36px 30px 30px;
  box-shadow: 0 30px 90px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.08);
  animation: cardRise 0.6s cubic-bezier(.16,1,.3,1);
}
@keyframes cardRise { from { opacity: 0; transform: translateY(16px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } }
.login-mark { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 6px; }
.login-app-icon {
  width: 58px; height: 58px; border-radius: 16px; object-fit: cover;
  box-shadow: 0 10px 34px rgba(64,102,255,.32), 0 0 26px rgba(255,91,223,.2);
  animation: iconArrive .7s cubic-bezier(.16,1,.3,1) both;
}
@keyframes iconArrive { from { opacity: 0; transform: translateY(8px) scale(.82) rotate(-4deg); } to { opacity: 1; transform: none; } }
.login-card h1 {
  margin: 0; font-size: 30px; letter-spacing: -0.02em; font-family: var(--font-rounded);
  background: linear-gradient(90deg, var(--cyan), var(--violet));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.login-sub { color: var(--sub); font-size: 13px; margin: 0 0 20px; }
.login-card input {
  width: 100%; margin-bottom: 12px; text-align: center; font-size: 15px;
  padding: 13px 14px; letter-spacing: 0.02em; transition: border-color 0.2s, box-shadow 0.2s;
}
.login-card input:focus { border-color: var(--glow); box-shadow: 0 0 0 3px rgba(168,140,250,0.25); }
.login-card button {
  width: 100%; border: none; font-weight: 700; font-size: 14.5px; padding: 13px 14px;
  color: #14101f; background: linear-gradient(90deg, var(--cyan), var(--violet));
  box-shadow: 0 8px 24px rgba(168,140,250,0.35); transition: transform 0.15s, box-shadow 0.15s;
  font-family: var(--font-rounded);
}
.login-card button:hover { box-shadow: 0 10px 30px rgba(168,140,250,0.5); }
.login-card button:active { transform: scale(0.98); }
.login-card :disabled { cursor: not-allowed; opacity: 0.55; box-shadow: none; }
.login-error { color: #ff9d9d; font-size: 12.5px; margin: 14px 0 0; animation: shake 0.4s; }
@keyframes shake {
  0%, 100% { transform: translateX(0); } 20% { transform: translateX(-6px); } 40% { transform: translateX(5px); }
  60% { transform: translateX(-4px); } 80% { transform: translateX(3px); }
}
@media (prefers-reduced-motion: reduce) {
  .login-orb, .login-card, .login-error, .bubble, .login-app-icon { animation: none; }
  /* Keep the orb breathing, barely — opacity only, nothing spatial. */
  .welcome-bubble { animation: bubbleCalm 12s ease-in-out infinite alternate; }
}
@keyframes bubbleCalm { from { filter: brightness(1); } to { filter: brightness(1.06); } }

.signOut {
  background: transparent; border: 1px solid var(--border); border-radius: 10px;
  width: 34px; height: 34px; padding: 0; font-size: 15px; color: var(--sub); flex: none;
  display: flex; align-items: center; justify-content: center;
}
.signOut:hover { color: var(--text); border-color: var(--glow); }

/* Full-page Quick Look — mirrors QuickLookOverlay.swift. */
.preview {
  position: fixed; inset: 0; z-index: 40; display: flex; align-items: stretch; justify-content: center;
  padding: var(--space-l); background: rgba(2,4,10,.28); backdrop-filter: blur(18px) saturate(1.15);
}
.preview-shell { width: min(1180px,100%); min-height: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-m); }
.preview-toolbar {
  width: 100%; min-height: 54px; display: flex; align-items: center; gap: var(--space-m);
  padding: 8px var(--space-l); border-radius: var(--radius-pill);
}
.preview-heading { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.preview-heading strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 15px var(--font-rounded); font-weight: 700; }
.preview-heading span { color: rgba(255,255,255,.48); font: 11px var(--font-mono); }
.preview-action {
  display: inline-flex; align-items: center; justify-content: center; min-height: 32px;
  padding: 0 12px; border-radius: 9px; background: rgba(255,255,255,.08);
  border: 1px solid var(--glass-border); color: #fff; text-decoration: none;
  font: 12px var(--font-body); font-weight: 500;
}
.preview-primary { background: var(--accent-cool); color: #101522; border-color: transparent; font-weight: 700; }
.previewClose { width: 30px; height: 30px; padding: 0; border-radius: 50%; background: transparent; border: 0; color: rgba(255,255,255,.58); }
.preview-page-wrap { flex: 1; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; position: relative; }
.preview-page-wrap > img {
  max-width: calc(100% - 110px); max-height: 100%; object-fit: contain; border-radius: 16px;
  border: 3px solid transparent; background-image: linear-gradient(#fff,#fff),conic-gradient(from 0deg,#8FE9FF,#73A6FF,#A88CFA,#F28CFF,#FF8FDC,#8FE9FF);
  background-origin: border-box; background-clip: padding-box,border-box; box-shadow: 0 18px 52px rgba(168,140,250,.28);
}
.preview-nav {
  position: absolute; top: 50%; transform: translateY(-50%); width: 42px; height: 42px; padding: 0;
  border-radius: 50%; background: rgba(255,255,255,.10); border: 1px solid var(--glass-border);
  font-size: 28px; color: #fff;
}
.preview-nav:disabled { opacity: 0; pointer-events: none; }
.preview-prev { left: 8px; } .preview-next { right: 8px; }
.preview-counter { padding: 7px 14px; border-radius: var(--radius-pill); background: rgba(255,255,255,.08); border: 1px solid var(--glass-border); font: 13px var(--font-mono); }
.preview-related { min-height: 62px; display: flex; align-items: center; gap: var(--space-s); padding: 8px var(--space-m); border-radius: var(--radius-pill); }
.preview-related > span { color: rgba(255,255,255,.55); font: 12px var(--font-rounded); font-weight: 600; }
#previewRelatedList { display: flex; gap: 8px; }
.preview-related-item { width: 46px; height: 46px; padding: 0; border: 0; border-radius: 50%; overflow: hidden; position: relative; }
.preview-related-item img { width: 100%; height: 100%; object-fit: cover; }
.preview-related-item span { position: absolute; width: 1px; height: 1px; overflow: hidden; }

/* Mode panels (Search results / Chat / Students) — the ONLY scrolling region on the
   page; #app itself is fixed to exactly one viewport tall (see .app above). */
.mode-panel { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: var(--space-l); }
#panel-command { padding: 0; }
/* .coming-soon itself is defined in app.js's injected chat styles (needs height:100% to
   fill the thread panel, not a viewport-relative min-height) — this file only needed it
   back when Chat/Roster were still placeholders. */

@media (max-width: 480px) {
  .bubble-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  :root { --bubble-w: 150px; --bubble-h: 200px; }
}

@media (max-width: 680px) {
  .commandbar-row { gap: var(--space-s); }
  .modepill { width: 34px; padding: 0; justify-content: center; }
  .modepill span { display: none; }
  .filterrow { gap: var(--space-s) var(--space-m); }
}

@media (max-width: 700px) {
  /* On phone-width canvases the hero intentionally fills nearly the whole
     screen, leaving no honest collision-free orbital lane. */
  .rising-field { display: none; }
  .commandbar { margin: 8px 8px 0; padding: 10px; }
  .glyph { width: 30px; height: 30px; }
  .signOut { display: none; }
  .chat-shell { flex-direction: column; height: auto; min-height: 100%; }
  .chat-sidebar { width: 100%; max-height: 220px; }
  .chat-thread { min-height: 460px; }
  .preview { padding: var(--space-s); }
  .preview-toolbar { gap: 6px; padding: 7px 10px; }
  .preview-action { padding: 0 8px; }
  #previewAnnotate { display: none; }
  .preview-heading span { display: none; }
  .collection-tray { right: var(--space-s); bottom: var(--space-s); }
}

@media (max-width: 520px) {
  .web-sheet-overlay { padding: 8px; align-items: flex-end; }
  .web-sheet { padding: var(--space-l); max-height: calc(100dvh - 16px); border-radius: 22px 22px 14px 14px; }
  .sheet-segments { grid-template-columns: 1fr; }
  .preview-toolbar { flex-wrap: wrap; }
  .preview-heading { flex-basis: calc(100% - 40px); }
}

/* ============================================================================
   Chat (Compass) + Roster (Students) — these used to be injected into <head> as
   separate <style> blocks on each mode's first boot (one for Chat, one for Roster).
   That was a real, confirmed bug: modals shared between the two modes (the New
   Session form reuses .import-overlay/.import-sheet, built for Roster's import
   flow) rendered completely unstyled — a plain div flowing to the bottom of the
   page, invisible — if opened from Chat before Roster had ever booted and injected
   those classes. Static, always-loaded CSS here removes the whole "depends on
   which mode you visited first" class of bug rather than just this one instance
   of it.
   ============================================================================ */
.chat-shell { display: flex; gap: var(--space-m); height: 100%; }
.chat-sidebar { width: 260px; flex: none; display: flex; flex-direction: column; padding: var(--space-m); }
.chat-sidebar.glass { background: transparent; box-shadow: none; }
.chat-sidebar.glass::before, .chat-sidebar.glass::after { display: none; }
.chat-sidebar-head { display: flex; align-items: center; justify-content: space-between; font: 16px var(--font-rounded); font-weight: 700; color: #fff; margin-bottom: var(--space-s); padding: 0 var(--space-s); }
.chat-new-btn { width: 26px; height: 26px; border-radius: 50%; background: rgba(168,140,250,.25); border: 1px solid var(--glass-border); color: #fff; padding: 0; font-size: 16px; line-height: 1; }
.chat-session-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.chat-session-row { display: flex; align-items: center; gap: 8px; padding: var(--space-s); border-radius: var(--radius-chip); cursor: pointer; background: rgba(255,255,255,.03); }
.chat-session-row.active { background: rgba(255,255,255,.10); border: 1px solid var(--glass-border); }
.row-delete { flex: none; margin-left: auto; width: 22px; height: 22px; border-radius: 50%; padding: 0; background: none; border: none; color: rgba(255,255,255,.3); font-size: 11px; opacity: 0; }
.chat-session-row:hover .row-delete, .chat-session-row:focus-within .row-delete { opacity: 1; }
.chat-session-trailing ~ .row-delete { margin-left: 0; }
.row-delete:hover { color: rgba(255,120,120,.9); background: rgba(255,255,255,.08); }
.chat-session-bar { width: 4px; border-radius: 3px; flex: none; }
.chat-session-text { min-width: 0; }
.chat-session-title { font: 13px var(--font-rounded); font-weight: 600; color: #fff; }
.chat-session-preview { font: 11px var(--font-body); color: rgba(255,255,255,.5); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.chat-session-trailing { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; margin-left: auto; }
.chat-session-dot { width: 6px; height: 6px; border-radius: 50%; }
.chat-session-time { font: 9px var(--font-body); color: rgba(255,255,255,.35); white-space: nowrap; }
.chat-empty-hint { font: 12px var(--font-body); color: rgba(255,255,255,.4); padding: var(--space-s); }
.chat-thread { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.chat-header { padding: var(--space-m); border-bottom: 1px solid var(--glass-border); display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-m); }
.chat-header-title { font: 17px var(--font-rounded); font-weight: 700; color: #fff; }
.chat-header-sub { min-height: 22px; display: flex; align-items: center; gap: 6px; font: 11px var(--font-body); color: rgba(255,255,255,.5); margin-top: 2px; }
.chat-grade-pill { min-width: 26px; height: 22px; padding: 0 8px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-pill); color: #fff; font: 11px var(--font-rounded); font-weight: 700; border: 1px solid rgba(255,255,255,.2); }
.chat-grade-pill[data-grade="1"] { background: rgba(87,224,194,.30); }
.chat-grade-pill[data-grade="2"] { background: rgba(115,208,255,.30); }
.chat-grade-pill[data-grade="3"] { background: rgba(143,166,255,.30); }
.chat-grade-pill[data-grade="4"] { background: rgba(181,140,250,.30); }
.chat-grade-pill[data-grade="5"] { background: rgba(242,140,255,.30); }
.chat-model-status { flex: none; display: flex; align-items: center; gap: 5px; font: 10px var(--font-mono); font-weight: 500; color: rgba(255,255,255,.5); white-space: nowrap; padding-top: 2px; }
.chat-model-status.degraded { color: rgba(255,255,255,.4); }
.chat-starters { display: flex; flex-wrap: wrap; gap: 8px; padding: var(--space-s) 0; }
.chat-starter-chip { font: 12px var(--font-rounded); font-weight: 500; color: rgba(255,255,255,.85); padding: 7px 12px; border-radius: 999px; background: rgba(255,255,255,.06); border: .8px solid rgba(255,255,255,.16); }
.chat-starter-chip:hover { background: rgba(255,255,255,.1); }
.chat-messages { flex: 1; overflow-y: auto; padding: var(--space-m); display: flex; flex-direction: column; gap: var(--space-m); }
.chat-msg { display: flex; flex-direction: column; gap: 6px; max-width: 78%; }
.chat-msg.user { align-self: flex-end; align-items: flex-end; }
.chat-msg.assistant { align-self: flex-start; align-items: flex-start; }
.chat-bubble-text { font: 14px var(--font-body); color: #fff; padding: 10px 14px; border-radius: 16px; border: .8px solid rgba(255,255,255,.3); white-space: pre-wrap; }
.chat-msg.user .chat-bubble-text { background: rgba(168,140,250,.18); }
.chat-msg.assistant .chat-bubble-text { background: rgba(255,255,255,.06); }
.chat-msg-bubbles { display: flex; gap: 10px; overflow-x: auto; max-width: 100%; padding-bottom: 4px; }
.chat-typing { display: flex; gap: 4px; padding: 14px; }
.chat-typing span { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.6); animation: chatDot 1.2s ease-in-out infinite; }
.chat-typing span:nth-child(2) { animation-delay: .15s; } .chat-typing span:nth-child(3) { animation-delay: .3s; }
@keyframes chatDot { 0%, 60%, 100% { transform: scale(1); opacity: .5; } 30% { transform: scale(1.4); opacity: 1; } }
.chat-input-bar { display: flex; align-items: flex-end; gap: var(--space-m); padding: var(--space-m); border-top: 1px solid var(--glass-border); }
.chat-glyph { width: 40px; height: 40px; flex: none; object-fit: cover; border-radius: 11px; filter: drop-shadow(0 0 8px rgba(115,222,255,.25)); }
.chat-input-bar textarea {
  flex: 1; resize: none; max-height: 110px; min-height: 20px; background: rgba(255,255,255,.06);
  border: 1px solid var(--glass-border); border-radius: 18px; padding: 10px 14px; font: 14px var(--font-body); color: #fff;
}
.chat-send { width: 32px; height: 32px; border-radius: 50%; padding: 0; background: none; border: none; color: rgba(255,255,255,.4); flex: none; }
.chat-send:not(:disabled) { color: var(--accent-glow); }
.bubble.compact { animation: none; }

.roster-mono { width: 34px; height: 34px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; font: 12px var(--font-rounded); font-weight: 700; color: #fff; }
.roster-search { width: 100%; margin: 0 0 var(--space-s); background: rgba(255,255,255,.06); border: 1px solid var(--glass-border); border-radius: 10px; padding: 8px 10px; font: 13px var(--font-body); color: #fff; }
.roster-group-btn { display: flex; align-items: center; justify-content: center; gap: 6px; margin-bottom: var(--space-s); padding: 8px; border-radius: 999px; background: rgba(168,140,250,.22); color: var(--accent-glow); font: 12px var(--font-rounded); font-weight: 600; cursor: pointer; }
.roster-row { position: relative; }
.roster-check { flex: none; background: none; border: none; padding: 0; color: rgba(255,255,255,.4); opacity: 0; }
.roster-row:hover .roster-check, .roster-row.checked .roster-check { opacity: 1; }
.roster-row.checked .roster-check { color: var(--accent-glow); }
.roster-health { width: 120px; height: 3px; border-radius: 2px; background: rgba(255,255,255,.1); margin-top: 4px; overflow: hidden; }
.roster-health-fill { height: 100%; background: var(--accent-cool); }
.info-card { background: rgba(255,255,255,.04); border-radius: var(--radius-card); padding: var(--space-m); margin-bottom: var(--space-m); }
.up-next-btn { display: flex; align-items: center; gap: 6px; background: none; border: none; padding: 0; margin-bottom: var(--space-s); font: 12px var(--font-rounded); font-weight: 600; color: var(--accent-cool); }
.up-next-btn:hover { text-decoration: underline; }
.info-card h3 { margin: 0 0 var(--space-s); font: 14px var(--font-rounded); font-weight: 700; color: rgba(255,255,255,.9); }
.tag-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--space-s); }
.tag { font: 11px var(--font-body); font-weight: 500; padding: 3px 9px; border-radius: 999px; background: rgba(115,222,255,.16); border: .8px solid rgba(115,222,255,.3); color: rgba(255,255,255,.85); }
.tag.strength { background: rgba(115,208,255,.16); border-color: rgba(115,208,255,.3); }
.tag.avoid { background: rgba(255,255,255,.06); border-color: var(--glass-border); color: rgba(255,255,255,.5); }
.prog-toggle { background: rgba(255,255,255,.05); border-color: var(--glass-border); color: rgba(255,255,255,.6); cursor: pointer; }
.prog-toggle.active-prog { background: rgba(168,140,250,.22); border-color: rgba(168,140,250,.5); color: #fff; }
.coming-soon { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-m); height: 100%; color: rgba(255,255,255,.4); text-align: center; padding: var(--space-l); }
.coming-soon .headline { font: 16px var(--font-rounded); font-weight: 500; color: rgba(255,255,255,.6); max-width: 320px; }
.roster-header { display: flex; align-items: center; gap: var(--space-m); margin-bottom: var(--space-l); }
.roster-avatar { width: 72px; height: 72px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font: 26px var(--font-rounded); font-weight: 800; color: #fff; flex: none; border: 1px solid var(--glass-border); }
.roster-identity { min-width: 0; }
.roster-name { font: 26px var(--font-rounded); font-weight: 800; color: #fff; }
.roster-meta { font: 13px var(--font-body); color: rgba(255,255,255,.5); }
.roster-header-actions { margin-left: auto; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.roster-find-btn { margin-left: auto; background: var(--accent-glow); color: #14101f; border: none; font: 13px var(--font-rounded); font-weight: 700; padding: 10px 18px; border-radius: 999px; }
.profile-two-col { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: var(--space-l); }
.snap-row { display: flex; gap: 8px; font: 11px var(--font-body); color: rgba(255,255,255,.6); padding: 6px 0; border-top: 1px solid rgba(255,255,255,.06); }
.roster-edit-link { display: block; margin-top: 4px; background: none; border: none; padding: 0; font: 11px var(--font-body); color: rgba(115,222,255,.8); text-decoration: underline; }

/* Secondary workspaces use the same quiet liquid-glass controls as Home. */
.chat-new-btn, .chat-starter-chip, .roster-find-btn, .preview-nav, .prog-toggle {
  border: 1px solid rgba(216,234,255,.23);
  background: linear-gradient(180deg, rgba(255,255,255,.13), rgba(255,255,255,.045));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), 0 7px 22px rgba(1,4,20,.16);
  transition: transform .22s cubic-bezier(.16,1,.3,1), border-color .2s ease,
    background .2s ease, box-shadow .22s ease, filter .2s ease;
}
.chat-new-btn:hover, .chat-starter-chip:hover, .preview-nav:hover, .prog-toggle:hover {
  transform: translateY(-2px); border-color: rgba(164,225,255,.52);
  background: linear-gradient(180deg, rgba(255,255,255,.2), rgba(132,128,243,.1));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 10px 28px rgba(64,74,160,.24);
}
.roster-find-btn {
  color: #fff; border-color: rgba(165,225,255,.5);
  background: linear-gradient(135deg, rgba(82,208,255,.52), rgba(128,112,244,.58) 52%, rgba(231,119,255,.42));
}
.roster-find-btn:hover { transform: translateY(-2px) scale(1.015); filter: brightness(1.08) saturate(1.1); }
.up-next-btn, .roster-edit-link { text-decoration: none; transition: color .2s ease, text-shadow .2s ease, transform .2s ease; }
.up-next-btn:hover, .roster-edit-link:hover { color: #dff8ff; text-decoration: none; text-shadow: 0 0 14px rgba(115,222,255,.55); transform: translateX(2px); }
.popover-menu-item { transition: transform .18s ease, background .18s ease, box-shadow .18s ease; }
.popover-menu-item:hover, .popover-menu-item:focus-visible {
  background: rgba(145,135,248,.2) !important; transform: translateX(2px);
  box-shadow: inset 0 0 0 1px rgba(210,232,255,.12);
}
.edit-label { display: block; font: 11px var(--font-body); color: rgba(255,255,255,.45); margin: var(--space-s) 0 4px; }
.edit-input { width: 100%; background: rgba(255,255,255,.06); border: 1px solid var(--glass-border); border-radius: 10px; padding: 8px 10px; font: 13px var(--font-body); color: #fff; resize: vertical; }
.import-overlay { position: fixed; inset: 0; z-index: 50; background: rgba(2,4,10,.7); backdrop-filter: blur(8px); display: flex; align-items: center; justify-content: center; padding: var(--space-l); }
.import-sheet { width: 540px; max-width: 100%; max-height: 80vh; display: flex; flex-direction: column; padding: var(--space-xl); }
.import-sheet h2 { margin: 0 0 var(--space-s); font: 20px var(--font-rounded); font-weight: 700; color: #fff; }
.import-summary { font: 13px var(--font-body); color: rgba(255,255,255,.55); margin-bottom: var(--space-m); padding-bottom: var(--space-m); border-bottom: 1px solid var(--glass-border); }
.import-rows { overflow-y: auto; max-height: 280px; display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-m); }
.import-row { display: flex; align-items: center; gap: 10px; padding: 6px 4px; }
.import-row-text { flex: 1; min-width: 0; }
.import-row-name { font: 13px var(--font-body); font-weight: 500; color: #fff; }
.import-row-meta { display: flex; gap: 8px; font: 10px var(--font-mono); color: rgba(255,255,255,.4); }
.import-score { color: var(--accent-cool); }
.import-badge { font: 10px var(--font-body); font-weight: 600; flex: none; }
.import-badge.new { color: var(--accent-cool); }
.import-badge.update { color: var(--program-sw); }
.import-actions { display: flex; align-items: center; gap: 10px; }
.import-actions .roster-find-btn:disabled { opacity: .4; }

@media (max-width: 700px) {
  .chat-shell { flex-direction: column; height: auto; min-height: 100%; }
  .chat-sidebar { width: 100%; max-height: 220px; }
  .chat-thread { min-height: 440px; }
  .roster-header { align-items: flex-start; flex-wrap: wrap; }
  .roster-header-actions { width: 100%; margin-left: 0; align-items: flex-start; }
  .roster-find-btn { margin-left: 0; }
  .profile-two-col { grid-template-columns: 1fr; gap: 0; }
}

#panel-roster .chat-sidebar { width: 280px; }

/* ============================================================================
   Fable pass — hover/keyboard glow, skip burst, touch & coarse-pointer tuning.
   ============================================================================ */

/* Hover + keyboard cursor: lift and glow in the card's own accent (matches
   BubbleView's hovering/isActive treatment). --wash-solid is set per-card in JS. */
.bubble { transition: transform 0.28s cubic-bezier(.3,1.3,.4,1), box-shadow 0.28s ease; }
@media (hover: hover) {
  .bubble:hover { transform: translateY(-4px) scale(1.04); box-shadow: 0 14px 40px rgba(0,0,0,0.4), 0 0 30px var(--wash, rgba(168,140,250,0.4)); z-index: 2; }
}
.bubble.keyboard-active {
  transform: translateY(-4px) scale(1.04);
  box-shadow: 0 14px 40px rgba(0,0,0,0.4), 0 0 34px var(--wash, rgba(168,140,250,0.5));
  z-index: 2;
}

/* Skip burst — the card inflates, blurs, and pops (ShardBurst's spirit in CSS). */
.bubble.dismissing {
  animation: bubblePop 0.38s cubic-bezier(.4,0,.9,.6) forwards;
  pointer-events: none;
}
@keyframes bubblePop {
  0%   { transform: scale(1); opacity: 1; filter: blur(0); }
  55%  { transform: scale(1.14); opacity: 0.7; filter: blur(1px); }
  100% { transform: scale(1.3); opacity: 0; filter: blur(6px); }
}
@media (prefers-reduced-motion: reduce) {
  .bubble.dismissing { animation: none; opacity: 0; transition: opacity 0.18s; }
}

/* Touch & coarse pointers — iPad-first. Hover affordances must not be the only
   path: downloads stay visible, targets grow, long-press stands in for
   right-click (wired in app.js). */
@media (pointer: coarse) {
  .bubble-download { opacity: 1 !important; width: 40px; height: 40px; }
  .preview-nav { width: 56px; height: 56px; font-size: 26px; }
  .preview-action { min-height: 44px; padding: 0 16px; }
  .gradepill { height: 38px; min-width: 38px; }
  .facetmenu { height: 38px; }
  .modepill { min-height: 40px; }
  .collection-toggle { min-height: 48px; }
  .popover-menu button { min-height: 44px; }
}
/* No accidental text selection or iOS callouts during long-press on cards. */
.bubble, .rising-bubble { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }
button, .preview-action { touch-action: manipulation; }

/* Swipe feedback in the preview: the page eases as you drag (JS sets --swipe-x). */
#previewImg { transform: translateX(var(--swipe-x, 0)); transition: transform 0.2s ease; will-change: transform; }

/* ============================================================================
   Web-first motion system — “Reading Observatory”

   One signature transition does the expressive work: the current view funnels
   into the chosen mode control and the next view unfurls from that same point.
   Everything else uses short transform/opacity feedback so touch stays instant.
   ============================================================================ */

/* Resting state uses the bulletproof clip technique — NOT an off-screen transform,
   which peeked into view under iPad's safe-area inset (the stray blue box at top-left).
   Clipped to a 1px box it can never render until it actually receives keyboard focus. */
.skip-link {
  position: fixed; z-index: 100; top: 0; left: 0;
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  color: #08101f; background: var(--accent-cool); font-weight: 700; text-decoration: none;
  border-radius: var(--radius-pill);
}
.skip-link:focus {
  width: auto; height: auto; margin: 0; overflow: visible; clip: auto; clip-path: none;
  top: max(12px, env(safe-area-inset-top)); left: max(12px, env(safe-area-inset-left));
  padding: 10px 14px;
}
.active-panel-anchor { position: fixed; width: 1px; height: 1px; pointer-events: none; }

.modepills { position: relative; isolation: isolate; padding: 3px; border-radius: var(--radius-pill); background: rgba(4,8,20,.22); }
.modepill { position: relative; z-index: 2; min-height: 38px; background: transparent !important; overflow: hidden; }
.modepill.active { box-shadow: none; border-color: transparent; }
.mode-orbit {
  position: absolute; z-index: 1; top: 3px; left: 3px; height: calc(100% - 6px);
  width: var(--orbit-w, 86px); transform: translateX(var(--orbit-x, 0));
  border-radius: var(--radius-pill); pointer-events: none;
  background:
    linear-gradient(135deg, rgba(115,222,255,.24), rgba(168,140,250,.3) 52%, rgba(242,140,255,.2)),
    rgba(255,255,255,.08);
  border: 1px solid rgba(188,229,255,.48);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.34), 0 0 22px rgba(115,208,255,.2);
  transition: transform .52s cubic-bezier(.18,1.35,.36,1), width .42s cubic-bezier(.2,.9,.3,1);
}
.mode-orbit::after {
  content: ""; position: absolute; inset: -5px; border-radius: inherit; opacity: .58;
  background: conic-gradient(from 120deg, transparent, rgba(115,222,255,.45), transparent 36%, rgba(242,140,255,.34), transparent 75%);
  filter: blur(8px); animation: orbitShimmer 4.8s linear infinite;
}
@keyframes orbitShimmer { to { transform: rotate(1turn); } }

.mode-warp {
  position: fixed; z-index: 36; left: var(--portal-x, 50vw); top: var(--portal-y, 40px);
  width: 20px; height: 20px; margin: -10px; border-radius: 50%; opacity: 0;
  pointer-events: none; mix-blend-mode: screen;
  background: radial-gradient(circle, #fff 0 8%, #73deff 18%, #a88cfa 44%, transparent 70%);
  box-shadow: 0 0 22px #73deff, 0 0 50px rgba(168,140,250,.8);
}
.mode-warp.run { animation: portalPulse .72s cubic-bezier(.2,.8,.25,1); }
@keyframes portalPulse {
  0% { opacity: 0; transform: scale(.2); }
  22% { opacity: 1; transform: scale(1.05); }
  55% { opacity: .9; transform: scale(2.5,.58); filter: blur(1px); }
  100% { opacity: 0; transform: scale(5.5,.12); filter: blur(6px); }
}

body.mode-transitioning #panel-command:not([hidden]),
body.mode-transitioning #panel-compass:not([hidden]),
body.mode-transitioning #panel-roster:not([hidden]) { view-transition-name: mode-stage; }
::view-transition-group(root) { animation: none; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
::view-transition-group(mode-stage) { overflow: clip; }
::view-transition-old(mode-stage) {
  transform-origin: var(--portal-x, 50vw) 0;
  animation: genieFold .24s cubic-bezier(.55,0,.85,.36) both;
}
::view-transition-new(mode-stage) {
  transform-origin: var(--portal-x, 50vw) 0;
  animation: genieUnfold .52s cubic-bezier(.12,.88,.24,1.08) both;
}
@keyframes genieFold {
  to { clip-path: ellipse(22px 4px at var(--portal-x, 50vw) 0); transform: translateY(-22px) scale(.96,.08); filter: blur(10px) saturate(1.8); opacity: .2; }
}
@keyframes genieUnfold {
  from { clip-path: ellipse(18px 3px at var(--portal-x, 50vw) 0); transform: translateY(-18px) scale(.96,.06); filter: blur(12px) saturate(1.8); opacity: .18; }
  64% { filter: blur(0) saturate(1.12); }
  to { clip-path: ellipse(150% 150% at 50% 50%); transform: none; filter: none; opacity: 1; }
}

.modepill, .portal, .gradepill, .facetmenu, .preview-action, .collection-toggle,
.collection-primary, .collection-secondary, .show-more { position: relative; overflow: hidden; }

/* Mouse gets spatial depth; touch receives the same content through tap/long-press. */
.bubble { --tilt-x: 0deg; --tilt-y: 0deg; transform: perspective(900px) rotateX(var(--tilt-x)) rotateY(var(--tilt-y)); transform-style: preserve-3d; }
@media (hover: hover) and (pointer: fine) {
  .bubble:hover, .bubble.keyboard-active { transform: perspective(900px) translateY(-5px) scale(1.035) rotateX(var(--tilt-x)) rotateY(var(--tilt-y)); }
  .bubble-preview img { transition: transform .32s ease-out; }
  .bubble:hover .bubble-preview img { transform: scale(1.035); }
}

/* Sheets arrive from below on touch and bloom from center with a mouse. */
.web-sheet-overlay:not([hidden]) .web-sheet { animation: sheetBloom .34s cubic-bezier(.18,.9,.28,1.05) both; }
@keyframes sheetBloom { from { opacity: 0; transform: translateY(18px) scale(.96); filter: blur(5px); } }

/* iPad safe areas and minimum controls. 16px inputs also prevent Safari's focus zoom. */
input, textarea, select { font-size: max(16px, 1em); }
.collection-tray { right: max(var(--space-l), env(safe-area-inset-right)); bottom: max(var(--space-l), env(safe-area-inset-bottom)); }
.preview { padding-top: max(var(--space-l), env(safe-area-inset-top)); padding-right: max(var(--space-l), env(safe-area-inset-right)); padding-bottom: max(var(--space-l), env(safe-area-inset-bottom)); padding-left: max(var(--space-l), env(safe-area-inset-left)); }
.signOut svg { width: 17px; height: 17px; }

@media (pointer: coarse) {
  .modepill { min-width: 48px; min-height: 48px; }
  .portal, .signOut, .welcome-random, .searchfield-submit, .searchfield-clear,
  .clear-filters, .show-more, .collection-primary, .collection-secondary, .collection-clear { min-height: 44px; min-width: 44px; }
  .facetmenu, .gradepill, .preview-action, .previewClose { min-height: 44px; }
  .bubble-grades .gradepill { min-width: 44px; }
  .bubble-program { min-height: 44px; }
  .mode-orbit { top: 3px; height: calc(100% - 6px); }
  .web-sheet-overlay { align-items: flex-end; }
  .web-sheet-overlay:not([hidden]) .web-sheet { animation-name: sheetRise; }
  @keyframes sheetRise { from { opacity: 0; transform: translateY(34px); } }
}

@media (prefers-reduced-motion: reduce) {
  .backdrop .aurora, .welcome-bubble, .rising-bubble, .bubble, .bubble-skel,
  .commandbar.bar-entering, .mode-orbit::after { animation: none !important; }
  .mode-orbit { transition-duration: .01ms; }
  .mode-warp { display: none; }
  .web-sheet-overlay:not([hidden]) .web-sheet { animation: none; }
  ::view-transition-old(mode-stage), ::view-transition-new(mode-stage) { animation: none; }
  .bubble, .bubble:hover, .bubble.keyboard-active { transform: none; }
}

/* ============================================================================
   Premium observatory controls

   The photographic sky and glass orb are the visual stars. Controls use one
   coherent liquid-glass material: quiet at rest, luminous only on interaction.
   ============================================================================ */

.commandbar {
  border: 1px solid rgba(217,231,255,.2);
  border-radius: 24px;
  background: linear-gradient(155deg, rgba(25,31,62,.72), rgba(17,15,42,.58));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), 0 18px 55px rgba(0,0,12,.28);
  backdrop-filter: blur(30px) saturate(1.55);
  -webkit-backdrop-filter: blur(30px) saturate(1.55);
}
.home-button {
  width: 46px; height: 46px; flex: none; padding: 4px; border: 0; border-radius: 50%;
  display: grid; place-items: center; background: transparent;
  transition: transform .28s cubic-bezier(.16,1,.3,1), filter .24s ease, background .24s ease;
}
.home-button .glyph { width: 38px; height: 38px; }
.home-button:hover {
  transform: translateY(-2px) scale(1.07);
  filter: drop-shadow(0 0 12px rgba(115,222,255,.65)) drop-shadow(0 0 20px rgba(242,140,255,.34));
  background: rgba(255,255,255,.07);
}
.home-button:active { transform: scale(.94); }

.searchfield {
  height: 48px; padding: 0 16px;
  background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.045));
  border-color: rgba(221,234,255,.2);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.09), 0 8px 28px rgba(0,0,16,.12);
}
.searchfield:focus-within {
  box-shadow: 0 0 0 1px rgba(120,225,255,.78), 0 0 0 5px rgba(115,208,255,.11), 0 12px 35px rgba(50,78,156,.2);
}

.modepills {
  gap: 4px; padding: 4px; border: 1px solid rgba(214,229,255,.14);
  background: rgba(3,7,22,.28); box-shadow: inset 0 1px 8px rgba(0,0,0,.24);
}
.modepill {
  min-height: 40px; padding: 0 14px; border: 0 !important;
  color: rgba(228,235,255,.62); font-weight: 640;
  transition: color .2s ease, transform .24s cubic-bezier(.16,1,.3,1), text-shadow .2s ease;
}
.modepill:hover { color: #fff; transform: translateY(-1px); text-shadow: 0 0 14px rgba(157,218,255,.62); }
.modepill svg { width: 15px; height: 15px; }
.mode-orbit {
  background: linear-gradient(135deg, rgba(120,224,255,.24), rgba(145,139,255,.3) 48%, rgba(244,143,255,.2)), rgba(255,255,255,.075);
  border-color: rgba(211,239,255,.42);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.34), 0 8px 24px rgba(68,70,166,.2), 0 0 24px rgba(115,208,255,.13);
}
.signOut {
  width: 42px; height: 42px; border-radius: 50%; border-color: rgba(221,230,255,.16);
  background: rgba(255,255,255,.035); color: rgba(226,232,250,.55);
  transition: color .2s ease, background .2s ease, transform .2s ease, box-shadow .2s ease;
}
.signOut:hover { color: #fff; background: rgba(255,255,255,.1); box-shadow: 0 0 22px rgba(157,163,255,.2); transform: translateY(-1px); }

.gradepill, .result-tool, .result-sort,
.filter-pill, .show-more, .preview-action, .previewClose,
.collection-toggle, .collection-primary, .collection-secondary, .collection-clear,
.sheet-actions button {
  border-color: rgba(216,231,255,.2);
  background: linear-gradient(180deg, rgba(255,255,255,.105), rgba(255,255,255,.045));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 7px 20px rgba(0,0,16,.12);
  transition: transform .22s cubic-bezier(.16,1,.3,1), background .2s ease,
    border-color .2s ease, box-shadow .22s ease, color .2s ease;
}
.gradepill:hover, .result-tool:hover,
.filter-pill:hover, .show-more:hover, .preview-action:hover, .previewClose:hover,
.collection-toggle:hover, .collection-secondary:hover, .collection-clear:hover,
.sheet-actions button:hover {
  transform: translateY(-2px); color: #fff;
  border-color: rgba(166,222,255,.46);
  background: linear-gradient(180deg, rgba(255,255,255,.17), rgba(138,135,246,.09));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 10px 28px rgba(54,73,145,.2);
}
.collection-primary, .preview-primary, .sheet-actions .primary, .result-tool {
  color: #fff; border-color: rgba(161,224,255,.48);
  background: linear-gradient(135deg, rgba(92,210,255,.46), rgba(129,116,245,.52) 52%, rgba(234,124,255,.38));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), 0 10px 28px rgba(98,97,225,.25), 0 0 24px rgba(105,205,255,.12);
}
.collection-primary:hover, .preview-primary:hover, .sheet-actions .primary:hover, .result-tool:hover {
  transform: translateY(-2px) scale(1.015); filter: saturate(1.12) brightness(1.08);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.36), 0 14px 34px rgba(102,97,230,.34), 0 0 28px rgba(115,222,255,.2);
}

/* Search refinements belong to one toolbar, not four separate glossy buttons. */
.filterrow { gap: 8px; }
.facetmenu {
  min-height: 34px; padding: 0 10px; border: 0; border-radius: 10px;
  background: transparent; box-shadow: none;
  transition: color .18s ease, background .18s ease;
}
.facetmenu:hover, .facetmenu:focus-visible { color: #fff; background: rgba(255,255,255,.075); }
#programSel { margin-left: 4px; padding-left: 16px; border-left: 1px solid rgba(220,235,255,.14); border-radius: 0 10px 10px 0; }
.clear-filters { background: transparent; border: 0; box-shadow: none; }
.clear-filters:hover, .clear-filters:focus-visible { color: #fff; background: rgba(255,255,255,.07); }

.bubble-program, .bubble-grades .gradepill {
  background: linear-gradient(180deg, rgba(255,255,255,.17), rgba(255,255,255,.065));
  border-color: rgba(226,239,255,.28);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16), 0 5px 16px rgba(0,0,20,.12);
  transition: transform .2s cubic-bezier(.16,1,.3,1), background .2s ease, box-shadow .2s ease;
}
.bubble-program:hover, .bubble-grades .gradepill:hover { transform: translateY(-2px) scale(1.04); background: rgba(255,255,255,.2); }

/* The rising resources pause in space and come alive under a mouse. The outer
   orbit keeps its current location while the artwork magnifies and gleams. */
.rising-bubble {
  transition: border-color .2s ease, box-shadow .24s ease, filter .22s ease;
  isolation: isolate;
}
.rising-bubble img {
  transition: transform .26s cubic-bezier(.16,1,.3,1), filter .22s ease;
  transform: scale(1.01);
}
.rising-bubble::after {
  content: ""; position: absolute; inset: -35%; z-index: 2; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(255,255,255,.48) 49%, transparent 60%);
  transform: translateX(-70%) rotate(8deg); opacity: 0;
}
/* The hover reaction is driven by the JS `.is-hovered` class (added only on a real
   pointerenter) and by :focus-visible — NOT bare :hover. That's deliberate: iPadOS
   reports `pointer: coarse` even with a mouse/trackpad attached, so gating this behind
   `@media (hover:hover) and (pointer:fine)` (the old code) killed it on exactly the
   device Sky uses. Applying it unconditionally is safe because the class never gets
   added by a touch tap. */
.rising-bubble.is-hovered, .rising-bubble:focus-visible {
  animation-play-state: paused; z-index: 5; filter: brightness(1.12) saturate(1.16);
  border-color: rgba(206,241,255,.88);
  box-shadow: 0 0 0 4px rgba(115,222,255,.1), 0 12px 38px rgba(24,28,94,.42), 0 0 30px rgba(192,115,255,.38);
}
.rising-bubble.is-hovered img, .rising-bubble:focus-visible img {
  transform: scale(1.22) rotate(var(--orb-tilt, -2deg)); filter: contrast(1.06) brightness(1.08);
}
.rising-bubble.is-hovered::after, .rising-bubble:focus-visible::after {
  animation: orbGleam .72s cubic-bezier(.16,1,.3,1);
}
@media (hover: hover) and (pointer: fine) {
  .rising-bubble:hover { animation-play-state: paused; z-index: 5; filter: brightness(1.12) saturate(1.16); border-color: rgba(206,241,255,.88); box-shadow: 0 0 0 4px rgba(115,222,255,.1), 0 12px 38px rgba(24,28,94,.42), 0 0 30px rgba(192,115,255,.38); }
  .rising-bubble:hover img { transform: scale(1.22) rotate(var(--orb-tilt, -2deg)); filter: contrast(1.06) brightness(1.08); }
  .rising-bubble:hover::after { animation: orbGleam .72s cubic-bezier(.16,1,.3,1); }
}
@keyframes orbGleam {
  0% { opacity: 0; transform: translateX(-70%) rotate(8deg); }
  38% { opacity: .75; }
  100% { opacity: 0; transform: translateX(70%) rotate(8deg); }
}

@media (max-width: 520px) {
  .welcome { padding: 10px; }
  .welcome-bubble { width: min(92vw, 430px); }
  .welcome-content { width: 70%; }
  .welcome-content img.logo { margin-bottom: 4px; }
  .welcome-search { height: 46px; padding: 0 13px; }
  .bubble-filters { margin-top: 8px; gap: 6px; }
  .welcome-portals { margin-top: -6px; gap: 8px; }
  .portal { height: 44px; padding: 0 15px; font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .rising-bubble:hover::after, .rising-bubble.is-hovered::after, .rising-bubble:focus-visible::after { animation: none; }
  .rising-bubble:hover img, .rising-bubble.is-hovered img, .rising-bubble:focus-visible img { transform: none; }
  .home-button, .portal, .modepill, .gradepill, .facetmenu, .preview-action { transition-duration: .01ms; }
}

/* ============================================================================
   Nebula observatory — supplied home art + generated cosmic-glass material

   The supplied ring is the scene's visual anchor. The orb is centered independently
   of the workspace portals so its center always lands in the ring's central glow.
   The generated texture is used only as optical material inside interface chrome.
   ============================================================================ */

:root { --cosmic-glass-art: url('/assets/cosmic-glass-ui.webp'); }

.backdrop.home::before {
  inset: 0;
  background-image: url('/assets/trove-home-nebula.webp');
  background-position: 50% 50%;
  filter: saturate(1.04) brightness(.92);
  animation: nebulaBreathe 24s cubic-bezier(.45,0,.55,1) infinite alternate;
}
.backdrop.home::after {
  background:
    radial-gradient(circle at 50% 50%, transparent 0 31%, rgba(2,4,16,.08) 63%, rgba(0,0,8,.34) 100%),
    rgba(1,3,12,.035);
}
.backdrop.home .aurora { opacity: .075; }
@keyframes nebulaBreathe {
  from { transform: scale(1.012); filter: saturate(1.02) brightness(.9); }
  to { transform: scale(1.04); filter: saturate(1.09) brightness(.96); }
}

.welcome {
  display: grid;
  place-items: center;
}
.welcome-bubble {
  grid-area: 1 / 1;
  width: var(--orb-size);
}
.welcome-portals {
  position: absolute;
  top: calc(50% + (var(--orb-size) / 2) - 8px);
  margin-top: 0;
}

/* One continuous generated material replaces the old stack of nested grey panels. */
.commandbar,
.preview-toolbar {
  isolation: isolate;
  overflow: clip;
  background-image:
    linear-gradient(145deg, rgba(9,13,39,.72), rgba(10,8,31,.62)),
    var(--cosmic-glass-art);
  background-size: auto, cover;
  background-position: center, 50% 45%;
  border: 1px solid rgba(210,231,255,.3);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.26),
    inset 0 -1px 0 rgba(109,120,255,.13),
    0 18px 56px rgba(0,2,24,.38),
    0 0 34px rgba(92,116,255,.09);
}
.commandbar::after,
.preview-toolbar::after {
  opacity: .46;
  background: linear-gradient(105deg, rgba(255,255,255,.22), transparent 24%, transparent 73%, rgba(113,219,255,.12));
}

.modepills {
  background-image:
    linear-gradient(rgba(3,7,27,.72), rgba(4,5,24,.76)),
    var(--cosmic-glass-art);
  background-size: auto, 165% auto;
  background-position: center, 58% 52%;
  border-color: rgba(211,232,255,.2);
}
.mode-orbit {
  background-image:
    linear-gradient(135deg, rgba(112,221,255,.18), rgba(150,128,255,.21) 55%, rgba(233,118,255,.14)),
    var(--cosmic-glass-art);
  background-size: auto, 230% auto;
  background-position: center, var(--orbit-x, 0) 48%;
  border-color: rgba(207,239,255,.56);
}

.portal,
.preview-primary,
.collection-primary,
.sheet-actions .primary,
.result-tool {
  background-image:
    linear-gradient(135deg, rgba(34,35,89,.42), rgba(75,43,116,.28)),
    var(--cosmic-glass-art);
  background-size: auto, 245% auto;
  background-position: center, 50% 52%;
  border-color: rgba(192,231,255,.48);
}
.portal:hover,
.preview-primary:hover,
.collection-primary:hover,
.sheet-actions .primary:hover,
.result-tool:hover {
  background-position: center, 62% 45%;
}

/* Preview commands are one quiet constellation inside the toolbar. Secondary
   actions shed their old boxed bevel; the selected action retains a luminous body. */
.preview-toolbar { gap: 6px; padding: 7px 12px 7px 20px; }
.preview-action {
  min-height: 40px;
  padding: 0 15px;
  border-radius: var(--radius-pill);
  border-color: transparent;
  background: rgba(255,255,255,.035);
  box-shadow: none;
  color: rgba(240,245,255,.78);
  font-family: var(--font-rounded);
  font-weight: 620;
}
.preview-action:hover {
  background: rgba(255,255,255,.105);
  border-color: rgba(190,229,255,.24);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 8px 24px rgba(42,55,135,.16);
}
.preview-primary {
  color: #fff;
  border-color: rgba(190,231,255,.52);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 8px 25px rgba(101,92,229,.27);
}
.previewClose {
  margin-left: 2px;
  background: rgba(255,255,255,.045);
  border: 1px solid rgba(223,233,255,.11);
  box-shadow: none;
}

@media (max-width: 520px) {
  .welcome-bubble { width: var(--orb-size); }
  .welcome-portals { top: calc(50% + (var(--orb-size) / 2) - 6px); margin-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .backdrop.home::before { animation: none; transform: none; }
}
