/* ============================================================================
   Aurora PWA — design system v2 (dark glassmorphism, English-only)
   Tokens lifted from designs/睡眠检测App_UI设计方案.html — see plan
   `Aurora_PWA_redesign_v2.md` for the full mapping. The light v1 palette
   that lived here is gone; this is a single dark theme.
   ========================================================================= */

/* @@ TOKENS @@ */
:root {
  /* Backgrounds — deep navy stack, lifted progressively */
  --surface:            #0B1020;   /* body bg */
  --surface-card:       #0D1533;   /* default card */
  --surface-elevated:   #142046;   /* hovered/active card */
  --surface-glass:      rgba(255, 255, 255, 0.04);
  --surface-glass-edge: rgba(255, 255, 255, 0.08);
  /* Aliases kept for v1-call-site compatibility until later phases finish
     migrating to the glass-* names. New code should NOT use these. */
  --surface-container:     var(--surface-elevated);
  --surface-container-low: var(--surface-glass);

  /* Text — light blue-white on dark. Two readable levels, not three: see
     --on-surface-faint below for where the third one went. */
  --on-background:      #D0E0FF;   /* primary readable */
  --on-surface:         #D0E0FF;
  --on-surface-variant: #7C93C4;   /* secondary / labels — the dimmest TEXT */

  /* NOT text. Both were #2A3850, which measured 1.05:1 to 1.60:1 depending on
     the backdrop — under the 3:1 graphics floor, so the icons drawn in it were
     as unreadable as the units once set in it. A third text level cannot exist
     below --on-surface-variant on this palette: clearing 4.5:1 on the palest
     surface (glass inside glass, #283258) needs a colour brighter than
     --on-surface-variant itself, which inverts the ramp. So the text call
     sites moved up to --on-surface-variant and these two kept only the
     non-text work, at the 3:1 floor WCAG 1.4.11 sets for it.
     Guarded by src/css_contrast.rs. */
  --on-surface-faint:   #6885B2;   /* icon glyphs, neutral ring stroke — 3:1 */
  --outline:            #6885B2;   /* chevrons, control borders — 3:1 */
  --outline-variant:    rgba(255, 255, 255, 0.08);   /* decorative rules only */

  /* Brand & accents */
  --primary:            #3355CC;
  --primary-container:  #142046;
  --primary-bright:     #6BA5FF;
  --primary-gradient:   linear-gradient(135deg, #2244CC 0%, #5533AA 100%);
  --accent-glow:        drop-shadow(0 0 6px #4488FF);

  /* Semantic */
  --secondary:          #00629d;
  --tertiary:           #22DD88;   /* success / "good" */
  --tertiary-container: #0d3320;
  --success:            #22DD88;
  --warning:            #FFAA44;   /* AHI elevated, abnormal */
  --error:              #EF476F;   /* destructive */
  --error-container:    #3a1019;

  /* Sleep-stage palette (hypnogram, Phase 4) */
  --stage-awake:        #99AABB;
  /* Two-stage fallback: sleep the pipeline could not break down. Its own
     colour on purpose — reusing --stage-deep or --stage-light would say
     which stage it was, which is the one thing this night does not know. */
  --stage-asleep:       #4A69B0;
  --stage-light:        #445588;
  --stage-rem:          #7755BB;
  --stage-deep:         #1133AA;

  /* Elevation */
  --shadow-card:        0 12px 32px rgba(20, 50, 200, 0.40);
  --shadow-button:      0 2px  8px rgba(20, 50, 180, 0.40);
  --shadow-fab:         0 16px 32px rgba(40, 80, 220, 0.45);

  /* Radius */
  --radius-card:   16px;
  --radius-pill:   9999px;
  --radius-button: 12px;

  /* Motion */
  --motion-fast:    180ms cubic-bezier(0.4, 0, 0.2, 1);
  --motion-default: 280ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Typography — Inter is the new single family. Manrope/Public Sans dropped
     because they didn't carry enough hierarchy weight on dark surfaces. */
  --font-headline: 'Inter', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
  --font-body:     'Inter', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
}

/* @@ RESET @@ */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  /* Subtle radial gradient over the deep navy so the screen isn't flat
     when content doesn't fill the viewport. Anchored to the top-centre,
     fades out before reaching the edges. */
  background:
    radial-gradient(ellipse at 50% -10%, #16204a 0%, #0B1020 60%) no-repeat,
    var(--surface);
  background-attachment: fixed;
  color: var(--on-background);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overscroll-behavior-y: contain;
}

body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  min-height: 100vh;
}

a {
  color: var(--primary-bright);
  text-decoration: none;
}

button {
  font-family: inherit;
  border: none;
  background: none;
  cursor: pointer;
  color: inherit;
}

input,
textarea,
select {
  font-family: inherit;
  font-size: inherit;
}

.material-symbols-outlined {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  -webkit-font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
}

/* @@ LAYOUT @@ */
/* Mobile-first: phones get the full viewport width up to a 600px breakpoint.
   Larger screens (tablet portrait, desktop, designer "phone preview") get a
   centred 480px column so the layout doesn't stretch absurdly wide. */
.app-shell {
  width: 100%;
  margin: 0 auto;
  min-height: 100vh;
  /* Transparent so the body's radial gradient shows through. */
  background: transparent;
  display: flex;
  flex-direction: column;
  padding-bottom: 84px; /* space for bottom nav */
}

@media (min-width: 600px) {
  .app-shell {
    max-width: 480px;
  }
}

.app-shell.no-nav {
  padding-bottom: 0;
}

.splash-loading {
  min-height: 100vh;
  /* No gradient — body already paints the dark bg, splash adds only its
     own brand glow centred. */
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--on-surface);
  font-family: var(--font-headline);
  font-weight: 800;
  font-size: 2rem;
  letter-spacing: 0.02em;
  filter: var(--accent-glow);
}

/* App bar — was solid primary blue on light surface; now a transparent
   header that lets the body gradient show through. Only the title +
   icon buttons need styling. */
.app-bar {
  background: transparent;
  color: var(--on-surface);
  padding: 20px 20px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.app-bar .app-bar-title {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.125rem;
  letter-spacing: 0.01em;
}

.app-bar .icon-button {
  color: var(--on-surface);
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--motion-fast);
}

.app-bar .icon-button:hover {
  background: var(--surface-glass);
}

.app-bar .avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--on-surface);
}

/* @@ PAGE CONTENT @@ */
.page {
  flex: 1;
  /* Reserve room at the bottom for the fixed .bottom-nav so the last item on
     long pages isn't hidden behind the tab bar. ~80px nav + 16px breathing
     room + iOS safe-area inset. */
  padding: 20px 20px calc(96px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 16px;
  color: var(--on-surface);
}

.page-title {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.75rem;
  color: var(--on-surface);
  line-height: 1.2;
  margin: 8px 0 4px;
}

.page-subtitle {
  color: var(--on-surface-variant);
  font-size: 0.95rem;
  margin: 0 0 12px;
}

.eyebrow {
  display: inline-block;
  color: var(--tertiary);
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 6px 12px;
  background: var(--tertiary-container);
  border-radius: var(--radius-pill);
}

/* ─── Cards ─── */
.card {
  background: var(--surface-card);
  border-radius: var(--radius-card);
  padding: 20px;
  box-shadow: var(--shadow-card);
}

.card.tinted {
  background: var(--secondary-fixed);
}

.card.primary-filled {
  background: var(--primary-gradient);
  color: white;
}

/* ─── Buttons ─── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 20px;
  border-radius: var(--radius-button);
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  transition: transform 0.12s, box-shadow 0.12s;
  min-height: 52px;
}

.btn:active {
  transform: scale(0.98);
}

.btn-primary {
  background: var(--primary-gradient);
  color: white;
  box-shadow: var(--shadow-fab);
  width: 100%;
}

.btn-outline {
  background: transparent;
  color: var(--primary);
  border: 1.5px solid var(--outline-variant);
  width: 100%;
}

.btn-ghost {
  background: transparent;
  color: var(--primary);
}

.btn-danger {
  background: var(--error-container);
  color: var(--error);
  width: 100%;
}

.btn-fab {
  position: sticky;
  bottom: 96px;
  background: var(--primary-gradient);
  color: white;
  box-shadow: var(--shadow-fab);
  width: 100%;
}

/* ─── Forms ─── */
.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.field label {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

.field .input-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface-card);
  border: 1.5px solid var(--outline-variant);
  border-radius: var(--radius-button);
  padding: 14px 16px;
  transition: border-color 0.15s;
}

.field .input-wrap:focus-within {
  border-color: var(--primary);
}

.field input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  color: var(--on-background);
  font-size: 1rem;
}

.field .input-wrap .material-symbols-outlined {
  color: var(--outline);
  font-size: 20px;
}

.field .eye-toggle {
  color: var(--outline);
  padding: 4px;
  border-radius: 4px;
}

.field .eye-toggle:hover {
  color: var(--primary);
}

.field .helper {
  font-size: 0.8rem;
  color: var(--error);
}

/* ─── Divider text ─── */
.or-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--outline);
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 16px 0;
}

.or-divider::before,
.or-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--outline-variant);
}

/* @@ BOTTOM-NAV @@ */
/* Dark glass nav — sits over body bg with backdrop-blur. Mirrors the
   .app-shell width strategy so it lines up with content above it. */
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
  background: rgba(11, 16, 32, 0.78);            /* deep navy glass */
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--surface-glass-edge);
  padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
  display: flex;
  justify-content: space-around;
  z-index: 20;
}

@media (min-width: 600px) {
  .bottom-nav {
    max-width: 480px;
  }
}

.bottom-nav .tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--on-surface-variant);
  font-family: var(--font-headline);
  font-weight: 500;
  font-size: 0.7rem;
  padding: 6px 12px;
  border-radius: var(--radius-button);
  min-width: 64px;
  transition: color var(--motion-fast);
}

.bottom-nav .tab.active {
  color: var(--primary-bright);
}

.bottom-nav .tab.active .material-symbols-outlined {
  font-variation-settings: 'FILL' 1;
  /* Active tab icon glows — direct lift from reference design. */
  filter: var(--accent-glow);
}

/* ─── Device card (Phase 7 glass redesign) ─── */
.device-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Whole card is a button — single tap routes to /history/{mcno}. */
.device-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  text-align: left;
  color: inherit;
  transition: transform var(--motion-fast), background var(--motion-fast);
}

.device-card:active {
  transform: scale(0.985);
}

.device-card-icon {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--primary-container);
  color: var(--primary-bright);
  font-size: 22px;
}

.device-card-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.device-card-name {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 1rem;
  color: var(--on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.device-card-sub {
  font-size: 0.78rem;
  color: var(--on-surface-variant);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.device-card-bound {
  font-size: 0.7rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.04em;
}

.device-card .chev {
  color: var(--outline);
  flex-shrink: 0;
}

/* Small status dot — left of the chevron. We don't actually track
   live online status yet, so for v2 it's a visual cue that the device
   is bound (not a real telemetry signal). */
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.status-dot-online {
  background: var(--success);
  box-shadow: 0 0 6px rgba(34, 221, 136, 0.7);
}

.devices-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 32px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.devices-empty-icon {
  font-size: 40px;
  color: var(--on-surface-faint);
}

/* Floating action button — small circle anchored bottom-right above
   the bottom nav. Replaces v1's full-width sticky bar. */
.fab {
  position: fixed;
  right: 20px;
  bottom: calc(96px + env(safe-area-inset-bottom));
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--primary-gradient);
  color: #fff;
  box-shadow: var(--shadow-fab);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 5;
  transition: transform var(--motion-fast);
}

.fab:active {
  transform: scale(0.94);
}

.fab .material-symbols-outlined {
  font-size: 28px;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.status-pill.online {
  background: var(--tertiary-container);
  color: var(--tertiary);
}

.status-pill.offline {
  background: var(--surface-container);
  color: var(--outline);
}

.status-pill.critical {
  background: var(--error-container);
  color: var(--error);
}

/* ─── History list (Phase 7 glass redesign) ─── */
.report-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.report-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  text-align: left;
  color: inherit;
  transition: transform var(--motion-fast);
}

.report-card:active {
  transform: scale(0.985);
}

.report-card-icon {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--primary-container);
  color: var(--primary-bright);
  font-size: 22px;
}

.report-card-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.report-card-date {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--on-surface);
}

.report-card-sub {
  color: var(--on-surface-variant);
  font-size: 0.75rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.report-card .chev {
  color: var(--outline);
  flex-shrink: 0;
}

.history-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 32px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.history-empty-icon {
  font-size: 40px;
  color: var(--on-surface-faint);
}

.score-pill {
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.9rem;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.score-pill.good {
  background: var(--tertiary-container);
  color: var(--tertiary);
}

.score-pill.warn {
  background: rgba(255, 170, 68, 0.15);
  color: var(--warning);
}

.score-pill.bad {
  background: var(--error-container);
  color: var(--error);
}

/* ─── Scanner ─── */
.scan-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  background: black;
  border-radius: var(--radius-card);
  overflow: hidden;
}

.scan-wrap video,
.scan-wrap canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.scan-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.scan-frame {
  width: 65%;
  aspect-ratio: 1;
  border: 3px solid white;
  border-radius: 16px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.35);
}

/* ─── Toast ─── */
.toast {
  position: fixed;
  bottom: 96px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--on-background);
  color: white;
  padding: 12px 20px;
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  box-shadow: var(--shadow-card);
  z-index: 100;
  max-width: 90vw;
}

.toast-error {
  background: var(--error);
}

.toast-success {
  background: var(--tertiary);
}

.form-error {
  color: var(--error);
}

.toast-info {
  background: var(--on-background);
}

/* ─── Language switch ─── */
.lang-switch {
  display: inline-flex;
  gap: 4px;
  background: var(--surface-container);
  border-radius: var(--radius-pill);
  padding: 4px;
}

.lang-switch button {
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.75rem;
  color: var(--on-surface-variant);
}

.lang-switch button.active {
  background: var(--primary-gradient);
  color: white;
}

/* ─── Helpers ─── */
.row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.text-center {
  text-align: center;
}

.muted {
  color: var(--on-surface-variant);
}

.spacer {
  flex: 1;
}

.hide {
  display: none !important;
}

/* ─── Profile page ─── */

/* Header block: big circular avatar, name under it. */
.profile-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 8px 0 4px;
}

.avatar-button {
  position: relative;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: var(--surface-container);
  overflow: visible; /* let the camera badge spill outside the circle */
  padding: 0;
  cursor: pointer;
  box-shadow: var(--shadow-card);
}

.avatar-button:disabled {
  cursor: progress;
}

.avatar-img,
.avatar-placeholder {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}

.avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 72px !important;
  color: var(--outline);
}

/* Small camera badge in the bottom-right of the avatar. */
.avatar-edit-badge {
  position: absolute;
  right: 4px;
  bottom: 4px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--primary-gradient);
  color: white;
  display: flex !important;
  align-items: center;
  justify-content: center;
  font-size: 20px !important;
  box-shadow: var(--shadow-card);
}

.profile-hero-name {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--on-background);
}

.profile-hero-sub {
  color: var(--on-surface-variant);
  font-size: 0.9rem;
  min-height: 1.2em; /* reserve space so layout doesn't jump when empty */
}

/* Flat field rows inside the view-mode card. */
.profile-card .profile-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--surface-container);
}
.profile-card .profile-row:last-child {
  border-bottom: none;
}

.profile-label {
  color: var(--on-surface-variant);
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.profile-value {
  color: var(--on-background);
  font-weight: 600;
  font-size: 1rem;
  text-align: right;
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Edit-mode inputs live in bare .field containers (no .input-wrap), so we
   give them their own bordered chrome here rather than inheriting the
   chrome-less style from the login form. */
.profile-card .field {
  margin-bottom: 12px;
}
.profile-card .field:last-child {
  margin-bottom: 0;
}
.profile-card .field input {
  padding: 12px 14px;
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-button);
  background: var(--surface-glass);
  color: var(--on-surface);
  font-size: 1rem;
  outline: none;
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.profile-card .field input:focus {
  border-color: var(--primary-bright);
  background: var(--surface-elevated);
}

/* Cancel + Save sit side-by-side, full-width row. */
.profile-actions {
  display: flex;
  gap: 12px;
}
.profile-actions .btn {
  flex: 1;
}

.profile-logout {
  margin-top: 8px;
}

/* @@ SCANNER PULSE RINGS (Phase 7) @@ */
/* 3 concentric rings centred behind the QR target, expanding outward
   with staggered delays so the field has a "looking for the device"
   pulse. CSS-only; no JS keeps the wasm payload small. */
.scan-pulse {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.scan-pulse span {
  position: absolute;
  width: 65%;
  aspect-ratio: 1;
  border-radius: 16px;
  border: 2px solid rgba(68, 136, 255, 0.5);
  opacity: 0;
  animation: scan-pulse 2.4s cubic-bezier(0.2, 0.7, 0.3, 1) infinite;
}

.scan-pulse span:nth-child(2) { animation-delay: 0.8s; }
.scan-pulse span:nth-child(3) { animation-delay: 1.6s; }

@keyframes scan-pulse {
  0%   { transform: scale(0.85); opacity: 0; }
  20%  { opacity: 0.65; }
  100% { transform: scale(1.4);  opacity: 0; }
}

.scan-hint {
  text-align: center;
  margin: 12px 4px 4px;
}

.scan-manual-form {
  margin-top: 18px;
}

/* @@ QR SCAN — tap-to-start + retry surfaces (Phase 8.x) @@ */

/* Camera surface is always mounted (so NodeRef.get() resolves) but
   hidden until the user explicitly arms the camera with a tap. */
.scan-wrap-hidden {
  display: none;
}

/* Big call-to-action card shown when camera is Idle or Denied.
   Same chrome as `.glass-card` plus centred icon + button stack. */
.scan-start {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 36px 24px;
  margin-bottom: 8px;
  text-align: center;
}

.scan-start-icon {
  font-size: 56px;
  color: var(--primary-bright);
  filter: var(--accent-glow);
}

.scan-start-btn {
  min-width: 200px;
}

.scan-start-hint {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.45;
  max-width: 34ch;
}

/* Denied variant — warning accent on icon + message. */
.scan-denied .scan-start-icon {
  color: var(--warning);
  filter: drop-shadow(0 0 6px rgba(255, 170, 68, 0.5));
}

.scan-denied-msg {
  margin: 0;
  color: var(--warning);
  font-size: 0.9rem;
  font-weight: 600;
  max-width: 34ch;
}

/* @@ GLASS CARD (shared) @@ */
/* Reusable glass surface used by Home Dashboard and (Phase 4) Daily Report
   sections. Sits over the body's radial gradient so the blur is visible. */
.glass-card {
  background: var(--surface-glass);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  padding: 18px;
  box-shadow: var(--shadow-card);
}

/* @@ HOME DASHBOARD @@ */
.home-page {
  /* Tighten top padding — the greeting sits where the page title would,
     so we don't need the standard 20px gap above it. */
  padding-top: 8px;
}

.home-greeting {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 8px 4px 4px;
}

.home-hello {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--on-surface);
  margin: 0;
}

.home-date {
  color: var(--on-surface-variant);
  font-size: 0.85rem;
  margin: 0;
}

.home-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Score ring centred above stat cards. The ring component owns its own
   size; the wrapper just centres + adds the small caption below. Rendered
   as a <button> so it's a tap-target that drills into the Daily Report. */
.home-score {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px 0 4px;
  width: 100%;
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  transition: transform var(--motion-fast);
}

.home-score:active:not(:disabled) {
  transform: scale(0.98);
}

.home-score:disabled {
  cursor: default;
}

.home-score-caption {
  color: var(--on-surface-variant);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.home-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 28px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.home-empty-icon {
  font-size: 40px;
  color: var(--primary-bright);
  filter: var(--accent-glow);
}

.home-cta-card {
  display: flex;
  align-items: center;
  gap: 14px;
  background: linear-gradient(
    135deg,
    rgba(34, 68, 204, 0.55) 0%,
    rgba(85, 51, 170, 0.55) 100%
  );
  border-color: rgba(120, 140, 255, 0.25);
}

.home-cta-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.home-cta-title {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1rem;
  color: #fff;
}

.home-cta-sub {
  color: rgba(208, 224, 255, 0.7);
  font-size: 0.78rem;
}

.home-cta-icon {
  font-size: 28px;
  color: #fff;
  filter: var(--accent-glow);
}

.home-trend {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-trend-title {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* @@ SCORE RING @@ */
.score-ring {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.score-ring svg {
  display: block;
  filter: drop-shadow(0 0 18px rgba(68, 136, 255, 0.35));
}

.score-ring-grade {
  display: inline-block;
  padding: 4px 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  color: var(--on-surface);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Takes the grade pill's place when the night could not be scored: a plain
   sentence, not a pill — a pill reads as a verdict, and there isn't one. */
.score-ring-unscored {
  max-width: 240px;
  margin: 0;
  text-align: center;
  color: var(--on-surface-variant);
  font-size: 0.82rem;
  line-height: 1.35;
}

/* @@ STAT CARDS @@ */
.stat-card-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.stat-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px 10px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  text-align: center;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.stat-card-value-row {
  display: flex;
  align-items: baseline;
  gap: 2px;
  color: var(--on-surface);
}

.stat-card-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.4rem;
  line-height: 1.1;
}

.stat-card-unit {
  font-size: 0.7rem;
  color: var(--on-surface-variant);
}

.stat-card-label {
  font-size: 0.7rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* @@ MINI BAR CHART @@ */
.mini-bar-chart {
  width: 100%;
}

.mini-bar-chart .mini-bar {
  fill: url(#ring-grad);
  /* Fall back to a flat colour for browsers that don't honour the
     #ring-grad shared definition (it lives on the score ring's SVG;
     bars get a flat brand colour instead). */
  fill: var(--primary-bright);
  opacity: 0.85;
}

.mini-bar-chart .mini-bar-empty {
  fill: var(--surface-glass-edge);
  opacity: 0.6;
}

/* @@ DAILY REPORT @@ */
.report-page {
  padding-top: 8px;
}

.report-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Headline = score ring + metadata column.
   On phones the columns stack; ≥600px viewports get the side-by-side. */
.report-headline {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 22px 18px;
}

@media (min-width: 600px) {
  .report-headline {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 32px;
  }
}

.report-headline-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}

@media (min-width: 600px) {
  .report-headline-meta {
    align-items: flex-start;
    width: auto;
  }
}

.report-grade {
  display: inline-block;
  padding: 4px 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-surface);
}

.report-times {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
}

.report-times li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-button);
}

.report-times-label {
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

.report-times-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--on-surface);
}

/* DEAD — no Leptos page emits this class, and unlike the `.insight-*` family
   below, no mockup does either. Left on --on-surface-faint deliberately: the
   token is now non-text only, and at 3rem this would be text. Nothing renders
   it, so nothing is wrong on screen; if it is ever revived it needs
   --on-surface-variant, not this. */
.report-noscore {
  font-family: var(--font-headline);
  font-weight: 800;
  font-size: 3rem;
  color: var(--on-surface-faint);
}

.report-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The conversation's column, for the two things in it that are not the night
   summary, the thread or the composer — the header (context line + memory
   link, wrapped together in `AssistantBody`) and the send-failure message.
   Those three carry the same 48rem measure of their own below; these two
   carried none, so they took the width of whichever shell mounted the body.
   On the institutional surface that is the whole content region, because the
   cap moved off `.org-assistant-body` so the pinned composer bar could span
   it — which left the heading and the memory link starting hundreds of pixels
   to the left of the conversation they introduce. The personal surface never
   showed it: `.app-shell` caps every personal page at 480px, well inside
   48rem. */
.assistant-column {
  width: 100%;
  max-width: 48rem;
  margin-inline: auto;
}

.assistant-context { color: var(--on-surface-variant); font-size: 0.9rem; margin: 0 0 0.25rem; }
/* The span the answer drew on, under the night it was about. Quieter than the
   anchor above it — same fact family, lesser emphasis — and absent entirely
   when a turn drew on no window, so it never occupies space empty. */
.assistant-context-range { color: var(--on-surface-variant); font-size: 0.8rem; opacity: 0.8; margin: 0 0 0.25rem; }
.assistant-memory-link { display: inline-block; font-size: 0.85rem; margin-bottom: 0.75rem; }

/* The night summary shown above the thread only when arriving from the
   report page — see `pages::assistant::render_night_summary`. */
.assistant-night-summary {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  max-width: 48rem;
  margin: 0 auto 1rem;
  padding: 0.75rem 1rem;
  background: var(--surface-card);
  border: 1px solid var(--outline-variant);
  border-radius: 1rem;
}
.assistant-night-summary-date {
  margin: 0;
  font-size: 0.8rem;
  color: var(--on-surface-variant);
}
.assistant-night-summary-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}
.assistant-night-summary-figure {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.assistant-night-summary-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1rem;
  color: var(--on-surface);
}
.assistant-night-summary-label {
  font-size: 0.7rem;
  color: var(--on-surface-variant);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.assistant-thread {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  max-width: 48rem;
  margin: 0 auto 1rem;
}
.assistant-turn { border-radius: 12px; padding: 0.6rem 0.8rem; max-width: 85%; margin: 0; }
.assistant-turn.mine { align-self: flex-end; background: var(--primary-container); }
.assistant-turn.theirs { align-self: flex-start; background: var(--surface-card); }

/* Outer bar: `position: sticky` keeps it in normal document flow — unlike
   `fixed`, it still occupies its own box, so the thread above it can never
   scroll a message out of sight behind it — while sticking to the bottom of
   the viewport once a long conversation scrolls past it. Spans the full
   content region so the bottom of the page reads as a continuous bar rather
   than a floating pill. The `bottom` offset has to clear whichever nav bar
   overlays the viewport's own bottom edge, so it's set per surface below
   (`.assistant-page` here; `.org-assistant-chat-page` in the institutional
   section) rather than on this shared rule. */
.assistant-composer-bar {
  position: sticky;
  background: var(--surface);
  border-top: 1px solid var(--outline-variant);
  padding: 0.75rem 0;
}

.assistant-page .assistant-composer-bar {
  bottom: calc(96px + env(safe-area-inset-bottom));
}

/* `.app-shell`'s shared rule (`padding-bottom: 84px`, reserved for the fixed
   `.bottom-nav` so an ordinary page's last item isn't hidden behind it) adds
   84px of scrollable space past the end of this page for the same reason
   `.org-page`'s desktop padding did for the institutional composer above:
   mid-scroll the composer sticks flush above the bottom nav, but once the
   scroll reaches that trailing 84px, the composer's containing block is
   exhausted and it releases to its static position — 84px higher, one
   visible jump at the end of scroll. This page ends in the composer
   instead, which already clears the bottom nav via its own `bottom` offset
   just above, so the 84px has nothing left to do here.
   Unlike the institutional page, `.app-shell` has no page-specific class to
   layer an override onto — every personal route shares the same single
   "app-shell" wrapper (see `Shell` in app.rs) — so this reaches it via
   `:has()` instead of adding routing machinery. `.app-shell` itself, which
   every other personal page still relies on for that clearance, is
   untouched. */
.app-shell:has(.assistant-page) {
  padding-bottom: 0;
}

.assistant-composer {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  padding: 0.5rem 0.5rem 0.5rem 0.9rem;
  background: var(--surface-card);
  border: 1px solid var(--outline);
  border-radius: 1.5rem;
  transition: border-color 120ms ease;
  width: 100%;
  max-width: 48rem;
  margin: 0 auto;
}
.assistant-composer:focus-within { border-color: var(--primary); }

.assistant-composer textarea {
  flex: 1;
  border: 0;
  outline: none;
  resize: none;
  background: transparent;
  color: var(--on-surface);
  font: inherit;
  line-height: 1.5;
  max-height: 12rem;      /* ~8 lines, then it scrolls */
  overflow-y: auto;
  padding: 0.35rem 0;
}
.assistant-composer textarea::placeholder { color: var(--on-surface-variant); }
.assistant-composer textarea:disabled { opacity: 0.6; }

.assistant-send {
  flex: none;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--primary);
  color: #fff;
  cursor: pointer;
  transition: opacity 120ms ease;
}
.assistant-send:disabled { opacity: 0.35; cursor: default; }
.assistant-send .material-symbols-outlined { font-size: 1.15rem; }

.report-section-title {
  margin: 0 4px;
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.report-section-header { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.talk-to-ai { font-size: 0.85rem; white-space: nowrap; }

/* Institutional variant (see `talk_to_ai_chrome` — the class is chosen in Rust
   from `ReportViewer`, not by a descendant selector). Brighter than the shared
   `--primary-bright` #6BA5FF, and still blue: white would have thrown away the
   one thing saying "link" and left the glyph to carry the affordance alone.
   #9CC4FF measures 10.60:1 on the #0B1020 body and 8.77:1 on the #16204A
   gradient peak. A one-off literal on purpose — one control, not a token. */
.talk-to-ai-bright { color: #9CC4FF; }

/* The anchor stays a plain (blockified) flex item — deliberately NOT
   inline-flex. `.report-section-header` aligns on `baseline`, and a flex
   container hands up its *first item's* baseline, which here would be the
   icon's, floating the control off the AI INSIGHTS heading beside it. As a
   block box the anchor's baseline stays its first line box's — the label's
   own, and once the label is out of flow (below) still the anchor font's
   strut, i.e. the same baseline either way. The glyph rides that line on the
   shared `.material-symbols-outlined` `vertical-align: middle`. */
.talk-to-ai-icon {
  /* `em`, so the glyph tracks the 0.85rem label instead of the 24px default:
     ~15px, a shade above cap height, which is how it reads as one control. */
  font-size: 1.1em;
  /* Optical space. Narrower than a word space, so it binds to the label. */
  margin-right: 0.2em;
}

/* Icon-only until pointed at — but only where there is a pointer.
   `org_nav` turns into a phone bottom bar below 1024px, so these pages really
   do render on touch, where `:hover` is either absent or sticky-on-tap. So the
   collapse is an *enhancement*: the rule below is the whole of it, and any
   device that fails `(hover: hover)` keeps the label visible in flow. Failing
   open reads; failing closed leaves a mystery glyph. */
@media (hover: hover) {
  /* Out of flow, not out of the document. `display: none` and
     `visibility: hidden` would both drop the label from the accessibility
     tree and leave the link nameless — an icon whose glyph text is
     `aria-hidden`. `opacity` hides it from eyes only.

     `position: absolute` is what keeps the reveal from shoving the heading:
     the anchor's width is the icon's, hovered or not, so `space-between` has
     nothing to re-solve and the layout provably cannot move. `right: 100%`
     grows the label leftward into the header's free space; no `top`/`bottom`
     leaves the block-axis at its static position, which is the line it would
     have occupied in flow — so the revealed label lands on the icon's own
     baseline rather than needing to be re-centred. */
  .talk-to-ai-bright { position: relative; }
  /* With the label gone from the line, the icon's trailing gap would be dead
     width holding the glyph off the section's right edge. The gap moves to
     the label, which now approaches from the other side. */
  .talk-to-ai-bright .talk-to-ai-icon { margin-right: 0; }
  .talk-to-ai-bright .talk-to-ai-label {
    position: absolute;
    right: 100%;
    margin-right: 0.2em;
    opacity: 0;
    /* While invisible it overlays empty header space; don't let it eat clicks
       or the I-beam. Restored on reveal so tracking onto the text — which is
       inside the anchor — does not count as leaving it and flicker. */
    pointer-events: none;
    transition: opacity 120ms ease;
  }
  /* `:focus-visible` as well as `:hover`: a keyboard user tabbing here would
     otherwise never learn what the glyph is. */
  .talk-to-ai-bright:hover .talk-to-ai-label,
  .talk-to-ai-bright:focus-visible .talk-to-ai-label {
    opacity: 1;
    pointer-events: auto;
  }
}

.report-stages {
  padding: 14px 14px 10px;
}

.report-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 28px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.report-empty-icon {
  font-size: 40px;
  color: var(--warning);
}

.report-disclaimer {
  margin: 4px 4px 0;
  font-size: 0.75rem;
  text-align: center;
}

/* @@ HYPNOGRAM @@ */
.hypnogram {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.hypnogram svg {
  display: block;
  width: 100%;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}

.hypnogram-lane-line {
  stroke: var(--surface-glass-edge);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  opacity: 0.6;
}

.hypnogram-bar.stage-awake { fill: var(--stage-awake); opacity: 0.85; }
.hypnogram-bar.stage-rem   { fill: var(--stage-rem);   opacity: 0.9; }
.hypnogram-bar.stage-light { fill: var(--stage-light); opacity: 0.9; }
.hypnogram-bar.stage-deep  { fill: var(--stage-deep);  opacity: 0.95; }
.hypnogram-bar.stage-asleep { fill: var(--stage-asleep); opacity: 0.9; }

.hypnogram-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
}

.hypnogram-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.72rem;
  color: var(--on-surface-variant);
}

.hypnogram-legend-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 3px;
}

.hypnogram-legend-swatch.stage-awake { background: var(--stage-awake); }
.hypnogram-legend-swatch.stage-rem   { background: var(--stage-rem); }
.hypnogram-legend-swatch.stage-light { background: var(--stage-light); }
.hypnogram-legend-swatch.stage-deep  { background: var(--stage-deep); }
.hypnogram-legend-swatch.stage-asleep { background: var(--stage-asleep); }

.hypnogram-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 22px;
  color: var(--on-surface-variant);
  text-align: center;
}

.hypnogram-empty .material-symbols-outlined {
  font-size: 32px;
  opacity: 0.6;
}

/* @@ METRIC GRID @@ */
.metric-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

@media (min-width: 600px) {
  .metric-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.metric-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 14px 10px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  text-align: center;
}

.metric-card-label {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

.metric-card-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--on-surface);
  line-height: 1.1;
}

/* MOCKUP ONLY — like the `.insight-*` family documented just below, the
   `.metric-card-*` rules serve only designs/Aurora_PWA_Daily_Report_mockup.html.
   The shipped report draws its 4-up summary with `.mini-ring-*` instead. Kept
   on --on-surface-faint for that reason: it is text, but only in a file that
   is not the product. */
.metric-card-unit {
  font-size: 0.7rem;
  color: var(--on-surface-faint);
}

/* @@ AI INSIGHTS @@ */
/* MOCKUP ONLY — nothing in the Leptos app emits these class names. The whole
   `.insight-*` (SINGULAR) family below is referenced only by the static file
   designs/Aurora_PWA_Daily_Report_mockup.html. See also `.insight-detail`
   further down, which belongs to this same dead set.

   The shipping AI Insight block is rendered by `render_insights` in
   crates/wis-webapp/src/pages/report.rs and uses DIFFERENT classes:
   `.insights-headline`, `.insights-group-label`, `.report-caption` with
   `.caption-good` / `.caption-warn` / `.caption-neutral`, and
   `.report-footer.muted`.

   Note the one-letter difference: `.insight-` here vs `.insights-` there.
   Editing the rules below to restyle the real report is the obvious mistake,
   and it produces no visible change at all — you have edited the mockup's
   stylesheet. Change the `.insights-*` / `.report-caption` rules instead.

   Kept, not deleted, because the mockup still renders from this stylesheet. */
.insight-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.insight-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px;
}

.insight-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--primary-container);
  color: var(--primary-bright);
  font-size: 22px;
}

.insight-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.insight-row-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.insight-label {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--on-surface);
}

.insight-pill {
  flex-shrink: 0;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  color: var(--on-surface-variant);
}

.insight-pill.insight-good {
  background: var(--tertiary-container);
  border-color: rgba(34, 221, 136, 0.3);
  color: var(--success);
}

.insight-pill.insight-warn {
  background: rgba(255, 170, 68, 0.15);
  border-color: rgba(255, 170, 68, 0.35);
  color: var(--warning);
}

.insight-tip {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.45;
}

/* @@ DAILY REPORT — Phase 8 expansion @@ */

/* Headline now has 5 stat cells (was 3) — switch to a flexible grid that
   wraps on narrow viewports so labels never get cropped. */
.report-times {
  grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
}

/* Data-quality warning banner — sits directly under the headline so the
   user sees it before they trust the numbers. */
.report-quality-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: rgba(255, 170, 68, 0.12);
  border: 1px solid rgba(255, 170, 68, 0.35);
  border-radius: var(--radius-card);
  color: var(--warning);
  font-size: 0.85rem;
}

.report-quality-banner .material-symbols-outlined {
  font-size: 20px;
  flex-shrink: 0;
}

/* Sleep-architecture donut wrapper — centres the SVG and stacks the
   legend underneath on phones, side-by-side on wider screens. */
.report-architecture {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 22px 18px;
}

@media (min-width: 600px) {
  .report-architecture {
    flex-direction: row;
    justify-content: center;
  }
}

/* @@ SLEEP DONUT @@ */
.sleep-donut {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

@media (min-width: 600px) {
  .sleep-donut {
    flex-direction: row;
    gap: 28px;
  }
}

.sleep-donut svg {
  display: block;
  filter: drop-shadow(0 0 14px rgba(68, 136, 255, 0.25));
}

/* Each arc picks up its colour from the shared --stage-* palette so
   donut + hypnogram + legend stay consistent. */
.donut-arc.stage-deep  { stroke: var(--stage-deep); }
.donut-arc.stage-light { stroke: var(--stage-light); }
.donut-arc.stage-rem   { stroke: var(--stage-rem); }
.donut-arc.stage-awake { stroke: var(--stage-awake); }

.donut-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 160px;
}

.donut-legend-item {
  display: grid;
  grid-template-columns: 16px 1fr auto;
  align-items: center;
  gap: 10px;
  font-size: 0.85rem;
  color: var(--on-surface);
}

.donut-legend-swatch {
  width: 14px;
  height: 14px;
  border-radius: 3px;
}

.donut-legend-swatch.stage-deep  { background: var(--stage-deep); }
.donut-legend-swatch.stage-light { background: var(--stage-light); }
.donut-legend-swatch.stage-rem   { background: var(--stage-rem); }
.donut-legend-swatch.stage-awake { background: var(--stage-awake); }

.donut-legend-label {
  color: var(--on-surface-variant);
}

.donut-legend-pct {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--on-surface);
}

/* @@ MOVEMENT CARD @@ */
.movement-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
}

.movement-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 12px;
}

.movement-label {
  font-size: 0.78rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.movement-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--on-surface);
}

/* MOCKUP ONLY — referenced solely by
   designs/Aurora_PWA_Daily_Report_mockup.html. The shipped Movement section is
   `.movement-card-v2`. Kept on --on-surface-faint for the same reason as
   `.metric-card-unit`: text, but not in the product. */
.movement-aux {
  grid-column: 1 / -1;
  font-size: 0.78rem;
  color: var(--on-surface-faint);
}

/* @@ INSIGHT DETAIL ROW @@ */
/* MOCKUP ONLY — part of the dead `.insight-*` (singular) family; see the
   note at `.insight-list` above. The shipped report uses `.insights-*` /
   `.report-caption`, one letter apart, so editing this changes nothing. */
/* New small line between the verdict pill and the prose tip — used to
   surface the actual numeric values backing the verdict (e.g.
   "SDNN 135 ms · PNN50 47% · LF/HF 0.54"). */
.insight-detail {
  margin: 0;
  font-size: 0.78rem;
  color: var(--on-surface);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.02em;
}

/* @@ TRENDS @@ */
.trends-page {
  padding-top: 8px;
}

.trend-link {
  align-self: flex-start;
  margin: -8px 4px 0;
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.85rem;
}

.trend-link:hover {
  text-decoration: underline;
}

.trends-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* One .trend-card per device — needs the same gap as .home-body /
   .trends-body so two cards don't sit flush against each other. */
.insights-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.trends-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 28px 20px;
  text-align: center;
}

/* Segmented control — pill row, three buttons, active gets the brand
   gradient. Used at the top of the Trends page to switch periods. */
.segmented {
  display: inline-flex;
  align-self: center;
  padding: 4px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-pill);
  gap: 4px;
}

.segmented-btn {
  padding: 8px 18px;
  border-radius: var(--radius-pill);
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--on-surface-variant);
  background: transparent;
  transition: background var(--motion-fast), color var(--motion-fast);
}

.segmented-btn:hover {
  color: var(--on-surface);
}

.segmented-btn.active {
  background: var(--primary-gradient);
  color: #fff;
  box-shadow: var(--shadow-button);
}

/* One-line statement of what the line chart plots, between the section
   title and the chart card — the reviewer's "I'm unsure exactly what this
   graph is trying to show me." */
.trend-chart-caption {
  font-size: 0.8rem;
  margin: -4px 4px 0;
}

/* Line chart — area path under the line, gradient stroke for the line,
   small dots at sampled points. */
.trend-chart-card {
  padding: 14px;
}

.trend-chart {
  display: block;
  width: 100%;
  height: 200px;
}

.trend-chart .trend-dot {
  fill: var(--primary-bright);
  filter: drop-shadow(0 0 4px rgba(68, 136, 255, 0.6));
}

/* Heatmap — 7 cols × 4 rows, cells coloured by sleep score, opacity
   carries the magnitude (higher score = brighter cell). */
.trend-heatmap-card {
  padding: 14px;
}

.trend-heatmap {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

.trend-heatmap-cell {
  aspect-ratio: 1 / 1;
  border-radius: 6px;
  background: linear-gradient(135deg, #4488FF 0%, #AA66FF 100%);
}

.trend-heatmap-cell.trend-heatmap-cell-empty {
  background: var(--surface-glass-edge);
  opacity: 0.4 !important; /* override inline style for missing cells */
}

/* Summary row — 3 stat cards laid out evenly. Re-uses the .stat-card
   styles from the Home Dashboard. */
.trend-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

/* @@ WELCOME @@ */
.welcome {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 0 24px calc(40px + env(safe-area-inset-bottom));
  overflow: hidden;
}

/* Static starfield — 12 dots positioned absolutely, each animated via
   the shared `twinkle` keyframes with its own delay (set inline) so the
   field shimmers asynchronously. Pure CSS, no JS. */
.welcome-starfield {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
}

.welcome-star {
  position: absolute;
  border-radius: 50%;
  background: #D0E0FF;
  opacity: 0.5;
  box-shadow: 0 0 6px rgba(208, 224, 255, 0.6);
  animation: welcome-twinkle 4s ease-in-out infinite;
}

@keyframes welcome-twinkle {
  0%, 100% { opacity: 0.2; transform: scale(0.8); }
  50%      { opacity: 0.9; transform: scale(1.15); }
}

.welcome-hero {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: 22vh;
  text-align: center;
}

.welcome-hero .hero-glyph {
  font-size: 72px;
  color: var(--primary-bright);
  filter: drop-shadow(0 0 18px rgba(68, 136, 255, 0.6));
}

.welcome-hero .brand-title {
  margin: 0;
  font-family: var(--font-headline);
  font-weight: 800;
  font-size: 3rem;
  letter-spacing: 0.02em;
  background: linear-gradient(135deg, #4488FF 0%, #AA66FF 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.welcome-hero .subtitle {
  margin: 4px 0 0;
  font-size: 1rem;
  color: var(--on-surface-variant);
  max-width: 28ch;
}

.welcome-cta {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 360px;
  align-self: center;
}

/* @@ AUTH PAGES (login + signup) @@ */
.auth-page .form {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-width: 420px;
  margin: 0 auto;
  width: 100%;
}

.auth-page .form .btn-primary {
  margin-top: 8px;
}

.auth-page .muted.center {
  margin-top: 18px;
  font-size: 0.9rem;
}

.auth-page .muted.center a {
  font-weight: 600;
  margin-left: 4px;
}

/* @@ PROFILE — My devices section @@ */
.profile-devices {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 18px;
}

.profile-devices-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.profile-section-title {
  margin: 0;
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.profile-devices-add {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  color: var(--primary-bright);
  transition: background var(--motion-fast), transform var(--motion-fast);
}

.profile-devices-add:hover {
  background: var(--surface-elevated);
  transform: scale(1.05);
}

.profile-devices-add .material-symbols-outlined {
  font-size: 20px;
}

.profile-devices-empty {
  margin: 4px 0;
  text-align: center;
  font-size: 0.85rem;
}

.profile-devices-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.profile-device-card {
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.profile-device-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  background: transparent;
  color: inherit;
  text-align: left;
}

.profile-device-icon {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--primary-container);
  color: var(--primary-bright);
}

.profile-device-meta {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.profile-device-name {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.profile-device-sub {
  font-size: 0.78rem;
  color: var(--on-surface-variant);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.profile-device-row .chev {
  color: var(--outline);
}

/* @@ DAILY REPORT — Phase 9 PDF parity @@ */

/* Top 4 mini-ring summary: 2-col grid on phones, 4-col on ≥600px. */
.report-rings-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
@media (min-width: 600px) {
  .report-rings-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.mini-ring {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px 8px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.mini-ring svg { display: block; }

.mini-ring-track {
  stroke: rgba(255, 255, 255, 0.08);
}

.mini-ring-value {
  font-family: var(--font-headline);
  font-weight: 800;
  font-size: 22px;
  fill: var(--on-surface);
}

.mini-ring-unit {
  font-size: 9px;
  fill: var(--on-surface-variant);
  letter-spacing: 0.04em;
}

.mini-ring-label {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  text-align: center;
}

.tone-good .mini-ring-arc      { stroke: var(--success); }
.tone-warn .mini-ring-arc      { stroke: var(--warning); }
.tone-bad  .mini-ring-arc      { stroke: var(--error); }
.tone-neutral .mini-ring-arc   { stroke: var(--on-surface-faint); }
.tone-good .mini-ring-value    { fill: var(--success); }
.tone-warn .mini-ring-value    { fill: var(--warning); }
.tone-bad  .mini-ring-value    { fill: var(--error); }

/* 2-col layout for paired sections (Sleep overview | Architecture, OSA | Movement). */
.report-grid-2 {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 600px) {
  .report-grid-2 {
    grid-template-columns: 1fr 1fr;
  }
}
.report-grid-2 > .report-section { margin: 0; }

/* Equal-height cards within each 2-col row (Sleep Overview ↔ Architecture,
   Sleep Apnea ↔ Movement). The grid already stretches the two .report-section
   items to the taller one's height; making each card flex:1 lets it fill that
   height so both columns line up. Applies to both the individual report and the
   org report (same .report-grid-2 markup). */
.report-grid-2 .report-section > .glass-card {
  flex: 1;
}

/* Sleep overview rows: label left, value right */
.report-overview {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
}

.overview-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--surface-glass-edge);
}
.overview-row:last-of-type { border-bottom: none; }

.overview-label {
  font-size: 0.85rem;
  color: var(--on-surface-variant);
}

.overview-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1rem;
  color: var(--on-surface);
}

/* Captions — coloured footnote lines under each card. */
.report-caption {
  margin: 8px 0 0;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 0.8rem;
  line-height: 1.45;
}
.caption-good {
  background: rgba(34, 221, 136, 0.10);
  border: 1px solid rgba(34, 221, 136, 0.25);
  color: var(--success);
}
.caption-warn {
  background: rgba(255, 170, 68, 0.10);
  border: 1px solid rgba(255, 170, 68, 0.30);
  color: var(--warning);
}
/* The third step the mini rings already have (.tone-bad) and the captions
   didn't. Added so the coverage caption can share `eff_tone` with the ring
   directly above it, and so it matches the React Native `Caption`'s own
   `bad` tone — both split at 85/70. */
.caption-bad {
  background: rgba(239, 71, 111, 0.10);
  border: 1px solid rgba(239, 71, 111, 0.30);
  color: var(--error);
}

/* Neutral caption tone — used by the AI Insights "Try tonight" bullets,
   where the line is a suggestion rather than a good/bad judgement. The
   good/warn variants above cover the other two groups. */
.report-caption.caption-neutral {
  color: var(--on-surface-variant);
}

/* UX Phase 1: tappable "what does this mean?" disclosure attached to one
   or more metrics on a report card (`components::metric_explainer`).
   Native <details>/<summary> — no extra JS or state needed for the
   tap-to-expand behaviour. Renders nothing at all when the shared content
   map has no entry for any metric it was asked about, so there is no
   "always-empty" layout to style around here. */
.metric-explainer {
  margin-top: 8px;
}
.metric-explainer > summary {
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--primary-bright);
}
.metric-explainer-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 8px;
  padding: 10px 12px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: 10px;
}
.metric-explainer-item + .metric-explainer-item {
  padding-top: 10px;
  border-top: 1px solid var(--surface-glass-edge);
}
.metric-explainer-label {
  margin: 0 0 2px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}
.metric-explainer-what {
  margin: 0 0 4px;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--on-surface);
}
.metric-explainer-means {
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--on-surface-variant);
}

/* AI Insights headline: the one-sentence takeaway that opens the section.
   Larger and brighter than a caption, quieter than a section title. */
.insights-headline {
  margin: 0 0 0.75rem;
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--primary-bright);
}

/* Sub-header above each bullet group inside the AI Insights card. */
.insights-group-label {
  margin: 0.9rem 0 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

/* @@ WEEKLY TREND CARD @@ */
/* components::trend_view — one renderer shared by pages::insights (personal,
   one card per device) and pages::org_trends (institutional, per subject).
   `.trend-card` pairs with `.glass-card` for the surface; `.trend-headline`
   and `.trend-flag-eyebrow` are this card's equivalents of the AI Insights
   card's `.insights-headline` / `.insights-group-label` just above — same
   hierarchy, so this reads as a sibling section rather than a new one. */
.trend-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Card's first line when more than one device can appear on the page
   (pages::insights only — pages::org_trends passes no label, since that
   page is already scoped to one subject). Same small-uppercase treatment
   as .insights-group-label, so a two-device household can tell whose
   sleep is whose before reading anything else. */
.trend-device-label {
  margin: 0;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

.trend-week {
  margin: 0;
  font-size: 0.78rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.04em;
}

.trend-headline {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--primary-bright);
}

/* Short label for the artifact's `kind` — shown for every kind, including
   `trend`, unlike .trend-kind-note below which only has prose for the other
   two. Neutral pill, not the amber of .trend-flag / .score-pill.warn: an
   unfamiliar or data-quality kind is a fact about the report, not a warning
   about the sleeper. */
.trend-kind-badge {
  align-self: flex-start;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
}

/* Zero flagged badges is the normal case — no metric has crossed a
   threshold — so this list renders nothing rather than an empty state;
   no rule is needed for that case. */
.trend-flags {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Calm amber, not alarm-red: crossing a threshold is worth a look, not an
   emergency. Same tone as .caption-warn / .score-pill.warn above. */
.trend-flag {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: rgba(255, 170, 68, 0.10);
  border: 1px solid rgba(255, 170, 68, 0.30);
  border-radius: 10px;
  font-size: 0.85rem;
  color: var(--on-surface);
}

.trend-flag-eyebrow {
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warning);
}

.trend-kind-note {
  margin: 0;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--on-surface-variant);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
}

.trend-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 0 0 0 1.1rem;
  font-size: 0.88rem;
  color: var(--on-surface);
}

.trend-caveats {
  margin: 0;
  font-size: 0.78rem;
  color: var(--on-surface-variant);
}

/* The org page's metric table adds three number columns beside the label,
   which doesn't fit a phone width — this scrolls on its own horizontal axis
   instead of widening the page (mobile-first PWA). */
.trend-metrics-scroll {
  overflow-x: auto;
}

.trend-metrics {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.trend-metrics th,
.trend-metrics td {
  padding: 8px 10px;
  text-align: left;
  white-space: nowrap;
}

.trend-metrics th {
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  border-bottom: 1px solid var(--outline-variant);
}

.trend-metrics td {
  color: var(--on-surface);
  border-bottom: 1px solid var(--outline-variant);
}

/* How many nights one value rests on, under the value itself rather than in a
   column of its own — the two weeks can rest on very different numbers of
   nights, and one shared column could not say so. --on-surface-variant, not
   --on-surface-faint: this is text, and faint measures below the 4.5:1 floor
   (see css_contrast.rs). */
.trend-metric-nights {
  display: block;
  margin-top: 2px;
  font-size: 0.66rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--on-surface-variant);
}

/* Flagged row: same calm-amber emphasis as .trend-flag, not alarm-red. */
.trend-metrics tr.flagged td {
  background: rgba(255, 170, 68, 0.08);
  color: var(--warning);
}

/* pages::org_usage — each day row drills into pages::org_usage_day. */
.trend-metrics tbody tr.usage-day-row {
  cursor: pointer;
}

.trend-metrics tbody tr.usage-day-row:hover {
  background: var(--surface-glass);
}

/* The range total (pages::org_usage's <tfoot>), computed server-side. */
.trend-metrics tfoot td {
  font-weight: 700;
  border-top: 1px solid var(--outline-variant);
  border-bottom: none;
}

/* The night comparison's signal-coverage row (pages::report's
   render_night_comparison): separated from the resident's own measurements by
   a rule, because its subject is the sensor rather than the person.
   Deliberately NOT a <tfoot>, which would inherit the font-weight: 700 above
   — that bold is written for org_usage's period totals, and this row is a
   measurement, not a total. Drawing data quality heavier than the
   measurements it qualifies inverts which one the reader looks at first. */
.trend-metrics tbody tr.nc-quality-row td {
  border-top: 1px solid var(--outline-variant);
  border-bottom: none;
}

.trend-nights {
  margin: 0;
  font-size: 0.78rem;
  color: var(--on-surface-variant);
}

/* The week's best and worst single nights, with the measure named. Prose, not
   a table — which is why it appears on the personal card too, where the
   metric table does not. */
.trend-night-rank {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--on-surface);
}

/* A night the producer could not interpret keeps its row and its values; the
   row is muted and the marker below says why, so the numbers beside it are not
   read as measurements. */
.trend-metrics tr.unusable td {
  color: var(--on-surface-variant);
}

.trend-night-marker {
  display: block;
  margin-top: 2px;
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  color: var(--on-surface-variant);
}

/* HR / RR analysis card */
.analysis-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.analysis-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.analysis-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: 10px;
  text-align: center;
}

.analysis-stat-label {
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}

.analysis-stat-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--on-surface);
}

/* The palest text in the app: .analysis-stat paints --surface-glass while
   already inside a .glass-card that paints it too, so at 0.65rem this is read
   against glass over glass. Even --on-surface-variant only reaches 4.05:1
   there over the gradient's bright end — pinned, not ignored, in
   src/css_contrast.rs. Undoing the nesting is what actually fixes it. */
.analysis-stat-unit {
  font-size: 0.65rem;
  color: var(--on-surface-variant);
}

/* Time-series + motion chart shared axis styling */
.time-series-chart, .motion-chart {
  width: 100%;
  display: block;
}

.ts-gridline {
  stroke: var(--surface-glass-edge);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  opacity: 0.5;
}

.ts-axis-label {
  font-family: var(--font-body);
  font-size: 9px;
  fill: var(--on-surface-variant);
}

.ts-axis-unit {
  font-family: var(--font-body);
  font-size: 8px;
  fill: var(--on-surface-variant);
}

.motion-chart .motion-bar {
  fill: var(--primary-bright);
  opacity: 0.8;
}

/* HRV 9-cell grid */
.hrv-grid-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.hrv-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.hrv-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 12px 8px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: 10px;
  text-align: center;
}

.hrv-cell-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--on-surface);
}

.hrv-cell-label {
  font-size: 0.68rem;
  color: var(--on-surface-variant);
  letter-spacing: 0.04em;
}

/* Marks all nine HRV figures — not just withholds the caption below them —
   when the pipeline's own `hrv_exception_flag` says the numbers are outside
   its plausible range (SDNN >= 200, LF/HF >= 20000, or TP >= 30000; see
   `pages::report::hrv_caption`'s doc comment). The values still render: a
   clinician may want to see them regardless. Same warning tone as
   `.caption-warn`. */
.hrv-grid-flagged .hrv-cell {
  border-color: rgba(255, 170, 68, 0.45);
}
.hrv-grid-flagged .hrv-cell-value {
  color: var(--warning);
}

/* OSA card */
.osa-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
}

.osa-rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.severity-pill {
  display: inline-block;
  padding: 3px 12px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* No severity string and no AHI — nothing was graded. Deliberately NOT
   .severity-normal: that is the green success chip, and reusing it put an em
   dash on a green pill, reading as "all clear" on a night with no respiratory
   data at all. Same glass/neutral treatment as .trend-kind-badge, for the same
   reason: an absent measurement is a fact about the report, not a verdict
   about the sleeper. Matches the RN pill's neutral surface. */
.severity-neutral {
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  color: var(--on-surface-variant);
}
.severity-normal {
  background: rgba(34, 221, 136, 0.15);
  border: 1px solid rgba(34, 221, 136, 0.3);
  color: var(--success);
}
.severity-mild {
  background: rgba(255, 170, 68, 0.15);
  border: 1px solid rgba(255, 170, 68, 0.35);
  color: var(--warning);
}
.severity-moderate {
  background: rgba(255, 119, 51, 0.18);
  border: 1px solid rgba(255, 119, 51, 0.4);
  color: #FF7733;
}
.severity-severe {
  background: rgba(239, 71, 111, 0.18);
  border: 1px solid rgba(239, 71, 111, 0.4);
  color: var(--error);
}

/* OSA severity scale */
.osa-severity { width: 100%; }
.osa-normal   { fill: #22DD88; }
.osa-mild     { fill: #FFAA44; }
.osa-moderate { fill: #FF7733; }
.osa-severe   { fill: #EF476F; }
.osa-marker line   { stroke: var(--on-surface); }
.osa-marker circle { fill: var(--on-surface); stroke: rgba(0,0,0,0.4); stroke-width: 1; }
.osa-label {
  font-family: var(--font-body);
  font-size: 9px;
  fill: var(--on-surface-variant);
}

/* Movement v2 card (Phase 9) */
.movement-card-v2 {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.movement-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.report-footer {
  margin: 18px 4px 0;
  font-size: 0.72rem;
  text-align: center;
}

/* ============================================================================
   INSTITUTIONAL (multi-tenant) shell + pages. Fully additive — none of the
   individual-app selectors above are touched. The org shell deliberately
   escapes the 480px `.app-shell` cap so the dashboard grid can breathe on
   tablet/desktop; navigation is a bottom bar on phones and a left sidebar
   from 1024px up. Design tokens are shared with the individual app.
   ========================================================================= */

/* @@ ORG SHELL @@ */
.org-shell {
  width: 100%;
  margin: 0 auto;
  min-height: 100vh;
  background: transparent;
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
}

/* Registration reuses the narrow auth form even inside the wide shell. */
.org-shell .auth-page {
  max-width: 480px;
  margin: 0 auto;
  width: 100%;
}

.org-page {
  flex: 1;
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 16px 20px calc(96px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 16px;
  color: var(--on-surface);
}

/* @@ ORG NAV — bottom bar (mobile) @@ */
/* Six items (five plus Usage) no longer fit `space-around` at a 375px
   shell width without shrinking — a prior measurement found five already
   using 359 of 375px with little slack. Scrolling horizontally keeps every
   label intact rather than shrinking to icon-only: five of the six icons
   are already visually similar, and dropping labels costs more than a swipe. */
.org-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(11, 16, 32, 0.82);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--surface-glass-edge);
  padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
  display: flex;
  /* flex-start, not space-around: with overflow-x: auto and six items no
     longer fitting, space-around degrades to center per spec once free space
     goes negative, and overflow at the *start* of a scroll container can
     never be scrolled back to — it permanently clips the first tab. The
     >=1024px sidebar media query below already uses flex-start. */
  justify-content: flex-start;
  overflow-x: auto;
  z-index: 20;
}

.org-nav .tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--on-surface-variant);
  font-family: var(--font-headline);
  font-weight: 500;
  font-size: 0.7rem;
  padding: 6px 12px;
  border-radius: var(--radius-button);
  min-width: 60px;
  flex: none;
  transition: color var(--motion-fast);
}

.org-nav .tab.active {
  color: var(--primary-bright);
}

.org-nav .tab.active .material-symbols-outlined {
  font-variation-settings: 'FILL' 1;
}

.org-nav-brand {
  display: none;
}

/* @@ ORG NAV — left sidebar (>=1024px) @@ */
@media (min-width: 1024px) {
  .org-nav {
    top: 0;
    bottom: 0;
    right: auto;
    width: 240px;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: 4px;
    padding: 24px 12px;
    border-top: none;
    border-right: 1px solid var(--surface-glass-edge);
  }

  .org-nav .tab {
    flex-direction: row;
    justify-content: flex-start;
    gap: 14px;
    padding: 12px 16px;
    width: 100%;
    min-width: 0;
    font-size: 0.9rem;
  }

  .org-nav .tab.active {
    background: var(--surface-glass);
  }

  .org-nav-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 16px 20px;
    color: var(--on-surface);
    font-family: var(--font-headline);
    font-weight: 800;
    font-size: 1.1rem;
  }

  .org-nav-brand .material-symbols-outlined {
    color: var(--primary-bright);
  }

  .org-shell {
    padding-left: 240px;
  }

  .org-page {
    padding: 28px 32px 40px;
  }
}

/* @@ ORG TOPBAR @@ */
.org-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 8px 0 4px;
}

.org-topbar-title {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.5rem;
  margin: 0;
  color: var(--on-surface);
}

.org-topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.org-seg {
  display: inline-flex;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-pill);
  padding: 3px;
}

.org-seg-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: var(--radius-pill);
  color: var(--on-surface-variant);
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.8rem;
}

.org-seg-btn.active {
  background: var(--primary-gradient);
  color: #fff;
}

.org-seg-btn .material-symbols-outlined {
  font-size: 18px;
}

.org-seg-label {
  display: none;
}

@media (min-width: 768px) {
  .org-seg-label {
    display: inline;
  }
}

.org-icon-btn {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--on-surface-variant);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  transition: color var(--motion-fast);
}

.org-icon-btn:hover {
  color: var(--on-surface);
}

.org-icon-btn.danger {
  color: var(--error);
}

/* Push a topbar action to the far right, past the centred title. */
.org-topbar-action {
  margin-left: auto;
}

.org-occupancy-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.org-occupancy-stay {
  padding: 16px;
}

.org-occupancy-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.org-occupancy-who {
  flex: 1 1 auto;
  min-width: 0;
}

.org-occupancy-badge {
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.75rem;
  border: 1px solid var(--surface-glass-edge);
}

.org-occupancy-badge.current {
  color: var(--primary);
  border-color: var(--primary);
}

.org-occupancy-badge.past {
  color: var(--on-surface-variant);
}

.org-occupancy-count {
  color: var(--on-surface-variant);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.org-occupancy-none {
  color: var(--on-surface-variant);
  font-size: 0.85rem;
  margin: 0;
}

.org-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 20px 0 8px;
}

.org-page-btn {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--on-surface-variant);
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  transition: color var(--motion-fast);
}

.org-page-btn:hover:not(:disabled) {
  color: var(--on-surface);
}

.org-page-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.org-page-indicator {
  min-width: 56px;
  text-align: center;
  color: var(--on-surface-variant);
  font-variant-numeric: tabular-nums;
}

.org-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  border-radius: var(--radius-button);
  background: var(--primary-gradient);
  color: #fff;
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.85rem;
  box-shadow: var(--shadow-button);
}

.org-add-btn .material-symbols-outlined {
  font-size: 20px;
}

/* @@ ORG CARD GRID @@ */
.org-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}

.org-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
  align-items: stretch;
  padding: 16px;
  transition: transform var(--motion-fast), border-color var(--motion-fast);
}

.org-card:active {
  transform: scale(0.99);
}

.org-card.problem {
  border-color: rgba(239, 71, 111, 0.45);
}

.org-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.org-card-id {
  min-width: 0;
}

.org-card-name {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.org-card-sub {
  color: var(--on-surface-variant);
  font-size: 0.8rem;
}

.org-score {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-headline);
  font-weight: 800;
  font-size: 1.1rem;
  border: 2px solid var(--outline);
}

.org-score.good {
  color: var(--success);
  border-color: var(--success);
}

.org-score.fair {
  color: var(--warning);
  border-color: var(--warning);
}

.org-score.poor {
  color: var(--error);
  border-color: var(--error);
}

.org-score.none {
  color: var(--on-surface-variant);
  font-size: 1rem;
}

.org-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.org-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  font-family: var(--font-headline);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.org-chip-warn {
  background: rgba(255, 170, 68, 0.15);
  color: var(--warning);
}

.org-chip-alert {
  background: rgba(239, 71, 111, 0.16);
  color: var(--error);
}

.org-chip-muted {
  background: var(--surface-glass);
  color: var(--on-surface-variant);
  margin-left: 8px;
}

.org-metrics {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  padding: 10px 0 2px;
  border-top: 1px solid var(--outline-variant);
}

.org-metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: center;
}

.org-metric-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--on-surface);
}

.org-metric-label {
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
}

.org-noreport {
  color: var(--on-surface-variant);
  font-size: 0.85rem;
  padding: 8px 0;
  border-top: 1px solid var(--outline-variant);
}

.org-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--on-surface-variant);
  font-size: 0.72rem;
}

.org-card-foot .chev {
  color: var(--outline);
  font-size: 20px;
}

/* @@ ORG ROWS (list mode + management) @@ */
.org-row-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.org-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
  text-align: left;
}

.org-row-icon {
  color: var(--primary-bright);
}

.org-row-meta {
  flex: 1;
  min-width: 0;
}

.org-row-name {
  font-family: var(--font-headline);
  font-weight: 600;
  color: var(--on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
}

.org-row-sub {
  color: var(--on-surface-variant);
  font-size: 0.78rem;
}

.org-row .chev {
  color: var(--outline);
}

.org-dot-problem {
  background: var(--error);
  box-shadow: 0 0 6px rgba(239, 71, 111, 0.7);
}

/* History-row total sleep (right-aligned; .org-row-meta flex:1 pushes it here).
   > 1h renders in the normal light text; < 1h in red. */
.org-row-sleep {
  flex-shrink: 0;
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 0.95rem;
}

.org-row-sleep.good {
  color: var(--on-surface);
}

.org-row-sleep.bad {
  color: var(--error);
}

.org-subject-row {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
}

.org-subject-row.inactive {
  opacity: 0.55;
}

.org-row-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* @@ ORG FORMS @@ */
.org-form {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.org-form-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.org-form-actions {
  display: flex;
  gap: 12px;
  margin-top: 4px;
}

.org-form-actions .btn {
  flex: 1;
}

.org-select {
  background: var(--surface-card);
  border: 1.5px solid var(--outline-variant);
  border-radius: var(--radius-button);
  padding: 12px 14px;
  color: var(--on-background);
  font-size: 1rem;
}

.org-select-sm {
  padding: 8px 10px;
  font-size: 0.8rem;
}

/* @@ ORG EMPTY / LOADING / SETTINGS @@ */
.org-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 40px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.org-empty-icon {
  font-size: 44px;
  color: var(--on-surface-faint);
}

.org-loading {
  padding: 40px 20px;
  text-align: center;
  color: var(--on-surface-variant);
}

.org-settings-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.org-settings-danger {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
}

/* @@ ORG HISTORY / REPORT (full-width, sidebar persists on wide screens) @@ */
/* Escape the 1280px .org-page cap so history/report fill the content area to
   the right of the sidebar. */
.org-wide {
  max-width: none;
}

.org-report-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* @@ ORG ASSISTANT (chat + memory bodies shared with the personal surface) @@ */
/* The shell is full width like every other `/org` page; the conversation
   itself is prose, and a line running the width of a wide monitor is hard
   to scan. */
.org-assistant-body {
  max-width: 48rem;
}

/* The chat page pins its composer to the bottom of the viewport with
   `position: sticky` and scrolls the thread past it in normal document flow
   (`.assistant-thread` / `.assistant-composer-bar`, styled in the personal
   section above — this institutional page shares those classes with the
   personal surface). The 48rem cap moves off this wrapper and onto the
   thread and the composer's inner element instead, or the pinned bar
   couldn't span the full content region. The memory page keeps the cap on
   this wrapper unchanged. */
.org-assistant-chat-page > .org-assistant-body {
  max-width: none;
}

/* Bottom-bar nav overlays the viewport's bottom edge below 1024px (see
   `.org-nav` above); the composer has to stick above it, matching `.org-
   page`'s own reservation for that bar. From 1024px the nav becomes a left
   sidebar and no longer claims the bottom edge, so the composer can sit
   flush at the bottom of the viewport. */
.org-assistant-chat-page .assistant-composer-bar {
  bottom: calc(96px + env(safe-area-inset-bottom));
}

@media (min-width: 1024px) {
  .org-assistant-chat-page .assistant-composer-bar {
    bottom: 0;
  }

  /* `.org-page`'s desktop rule (above) reserves 40px of bottom padding for
     institutional pages that end in ordinary content scrolling out of view.
     This page ends in the composer instead, and the composer already
     supplies its own bottom breathing room (`.assistant-composer-bar`'s own
     padding) — so that 40px has nothing left to do here. Left in place, it
     is 40px of extra scrollable space past the end of `.org-assistant-body`,
     which is exactly what the composer's `position: sticky` is measured
     against: mid-scroll it sticks flush to the viewport (`bottom: 0` above),
     but once the scroll reaches that trailing padding, the composer has run
     out of containing block to stick within and releases to its static
     position — 40px higher, one visible jump at the end of scroll. Zeroing
     the padding only here (not on `.org-page` itself, which every other
     institutional page still relies on) removes the mismatch instead of
     papering over it. */
  .org-assistant-chat-page {
    padding-bottom: 0;
  }
}

/* pages::org_trends — wraps Loading / org-empty / the single .trend-card. */
.org-trends-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Report header meta bar: resident / bed-room / date / device. */
.org-report-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 32px;
  padding: 14px 18px;
  background: var(--surface-glass);
  border: 1px solid var(--surface-glass-edge);
  border-radius: var(--radius-card);
}

.org-meta-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.org-meta-label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--on-surface-variant);
}

.org-meta-value {
  font-family: var(--font-headline);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--on-surface);
}

/* Total sleep time — pushed to the right edge of the header bar, emphasised. */
.org-report-total {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.org-report-total-value {
  font-family: var(--font-headline);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--primary-bright);
  line-height: 1.1;
}

/* Reuse the individual report's section styles verbatim (render_report), but
   let the 2-column blocks span the wider org layout. */
.org-report-body .report-grid-2 {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .org-report-body .report-grid-2 {
    grid-template-columns: 1fr 1fr;
  }
}
