/* =========================================================================
   ThreadOptic app shell — login, dashboard, settings.

   Design language is adapted from the Modern Dashboard UI Kit, mapped onto
   this site's existing brand rather than imported wholesale:

     Figma                                  here
     ------------------------------------   ---------------------------------
     #6531f7 (kit purple)                   var(--purple) #5552ff (site purple)
     Roboto                                 Poppins / Clash Display (site type)
     glass/default effect                   --glass-inset + backdrop blur
     --tertiary 20px radius                 --r-card

   The kit is built for business owners looking at customers. This is built for
   one person looking at their own inbox, so every metric is about the user's
   own work, and the "AI Insights" panel becomes real messages that need them.
   ========================================================================= */

/* The browser's own [hidden] rule is `display: none` at user-agent weight, so
   ANY author rule that sets display beats it. .kpi-row, .row-2 and .row-3 all
   set `display: grid`, which meant setting .hidden on them did precisely
   nothing: the counts band and the inbox pair stayed painted on every view and
   the view you actually asked for was pushed to the bottom of the page.
   This has to come first so later display rules cannot outrank it. */
[hidden] { display: none !important; }

:root {
  /* --- Surface -------------------------------------------------------- */
  /* Sampled from the kit at 0%, 50%, 75%, 100% of the frame height. */
  --app-bg-top: #d6e9fc;
  --app-bg-mid: #ffffff;
  --app-bg-low: #f3ebfe;
  --app-bg-bottom: #f6e7fc;

  /* The translucency the whole look depends on. Any card that is fully
     opaque stops reading as glass and flattens the page. */
  --glass-fill: rgba(255, 255, 255, .24);
  --glass-fill-strong: rgba(255, 255, 255, .52);
  --glass-inset:
    inset 2px 2px 3px 0 rgba(255, 255, 255, .84),
    inset 0 0 1px 1px rgba(255, 255, 255, .44);
  --glass-blur: blur(10px);

  --ink-1: #1a1a1a;
  --ink-2: rgba(26, 26, 26, .60);
  --ink-3: rgba(26, 26, 26, .44);
  --hairline: rgba(26, 26, 26, .08);
  --track: rgba(26, 26, 26, .06);

  --pos: #16a34a;
  --pos-bg: #22c55e;
  --neg: #dc2626;
  --neg-bg: #ef4444;
  --warn: #f59e0b;

  --r-card: 20px;
  --r-md: 12px;
  --r-sm: 6px;
  --r-xs: 4px;

  --shadow-soft: 0 2px 8px rgba(26, 26, 26, .06);
  --shadow-lift: 0 12px 32px rgba(26, 26, 26, .10);

  --sidebar-w: 248px;
  --app-gap: 16px;
}

/* ===== Page ============================================================ */

body.app {
  min-height: 100vh;
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink-1);
  /* Fixed so the gradient belongs to the window, not to the scroll length:
     otherwise a long dashboard stretches it and the lavender never arrives. */
  background:
    linear-gradient(180deg,
      var(--app-bg-top) 0%,
      var(--app-bg-mid) 46%,
      var(--app-bg-low) 78%,
      var(--app-bg-bottom) 100%) fixed;
  -webkit-font-smoothing: antialiased;
}

/* A second, off-centre wash. The kit's background is not a clean vertical
   ramp: there is warmth pooling at the lower left, which is what stops the
   gradient reading as a stock CSS gradient. */
body.app::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(70% 55% at 12% 96%, rgba(210, 81, 255, .16), transparent 70%),
    radial-gradient(60% 45% at 92% 4%, rgba(85, 82, 255, .10), transparent 70%);
}

.app-shell {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: var(--app-gap);
  padding: var(--app-gap);
  min-height: 100vh;
  align-items: start;
}

/* ===== Glass ============================================================ */

.glass {
  position: relative;
  background: var(--glass-fill);
  border-radius: var(--r-card);
  box-shadow: var(--glass-inset);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}

/* Cards get a quiet lift on hover and nothing else.

   There was a white radial highlight here that tracked the cursor. It looked
   good on an empty card and was actively harmful on a full one: the bright
   centre sat directly under the pointer, which is exactly where the reader is
   looking, and washed out the text underneath it. A hover affordance that
   makes content harder to read is not worth the charm.

   Buttons keep their own hover states. They are small, the cursor covers most
   of them anyway, and there is no body text to obscure. */
.glass-hover {
  transition: transform .28s cubic-bezier(.2, .8, .3, 1), box-shadow .28s ease;
}
.glass-hover:hover {
  transform: translateY(-2px);
  box-shadow: var(--glass-inset), var(--shadow-lift);
}

@media (prefers-reduced-motion: reduce) {
  .glass-hover, .glass-hover:hover { transform: none; transition: none; }
}

/* ===== Sidebar ========================================================== */

.side {
  position: sticky;
  top: var(--app-gap);
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 16px;
  height: calc(100vh - var(--app-gap) * 2);
}

.side-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--hairline);
}
.side-brand img { height: 30px; width: auto; display: block; }
.side-brand-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.side-brand-name {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink-1);
}
.side-brand-sub {
  font-size: 11px;
  line-height: 1.1;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.side-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
  /* Scrollbar would sit on the glass edge and look like a seam. */
  scrollbar-width: none;
}
.side-scroll::-webkit-scrollbar { display: none; }

.side-group { display: flex; flex-direction: column; gap: 2px; }
.side-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 8px;
  padding-left: 10px;
}

.side-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-md);
  font-size: 14px;
  color: var(--ink-2);
  text-decoration: none;
  transition: background .16s ease, color .16s ease;
}
.side-link svg { width: 17px; height: 17px; flex: 0 0 17px; }
.side-link:hover { background: rgba(255, 255, 255, .55); color: var(--ink-1); }

.side-link.is-active { color: var(--ink-1); font-weight: 600; background: rgba(255, 255, 255, .68); }
/* The kit marks the active item with a purple bar in the gutter rather than a
   filled pill, which keeps the nav quiet while still being unmistakable. */
.side-link.is-active::before {
  content: '';
  position: absolute;
  left: -16px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 22px;
  border-radius: 0 3px 3px 0;
  background: var(--purple);
}

.side-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  border-radius: 999px;
  background: var(--purple);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  border: none;
  cursor: pointer;
  transition: background .16s ease, transform .16s ease;
}
.side-cta:hover { background: #4340f5; transform: translateY(-1px); }
.side-cta svg { width: 16px; height: 16px; }

/* ===== Top bar ========================================================== */

.app-main { display: flex; flex-direction: column; gap: var(--app-gap); min-width: 0; }

.topbar { display: flex; align-items: center; gap: 12px; }

.search {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--glass-fill);
  box-shadow: var(--glass-inset);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}
.search svg { width: 16px; height: 16px; color: var(--ink-3); flex: 0 0 16px; }
.search input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink-1);
}
.search input::placeholder { color: var(--ink-3); }

.icon-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border: none;
  border-radius: 999px;
  background: var(--glass-fill-strong);
  box-shadow: var(--glass-inset);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  color: var(--ink-1);
  cursor: pointer;
  transition: background .16s ease, transform .16s ease;
}
.icon-btn:hover { background: #fff; transform: translateY(-1px); }
.icon-btn svg { width: 18px; height: 18px; }
/* Show the destination, not the current state: in light mode the button offers
   the moon, and pressing it is how you get there. Two icons swapped by the
   theme attribute rather than by script, so they cannot fall out of step. */
#themeBtn .theme-sun { display: none; }
#themeBtn .theme-moon { display: block; }
html[data-theme='dark'] #themeBtn .theme-sun { display: block; }
html[data-theme='dark'] #themeBtn .theme-moon { display: none; }

.icon-btn.is-primary { background: var(--purple); color: #fff; box-shadow: none; }
.icon-btn.is-primary:hover { background: #4340f5; }

/* Unread marker on the bell. */
.icon-btn { position: relative; }
.icon-btn .dot {
  position: absolute;
  top: 10px;
  right: 11px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--neg-bg);
  border: 2px solid #fff;
}

.avatar {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--purple), var(--magenta));
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  font-family: var(--font-display);
  text-decoration: none;
  border: 2px solid rgba(255, 255, 255, .7);
}

/* ===== Cards ============================================================ */

.card { padding: 16px; }

.card-title {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.1;
  color: var(--ink-1);
}
.card-sub { font-size: 12px; line-height: 1.1; color: var(--ink-3); margin-top: 6px; }

.card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

/* --- The "tips" strip. The kit's signature element and the best home for a
   model-written line: purple hairline, italic, fading gradient. -------- */
.tip {
  display: flex;
  gap: 8px;
  align-items: stretch;
  padding: 7px 0;
  background: linear-gradient(90deg, rgba(85, 82, 255, .07), rgba(85, 82, 255, 0));
  border-radius: var(--r-xs);
}
.tip::before {
  content: '';
  flex: 0 0 2px;
  border-radius: 10px;
  background: var(--purple);
  margin-left: 2px;
}
.tip p {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 12px;
  font-style: italic;
  line-height: 1.35;
  color: var(--ink-2);
}

/* ===== KPI cards ======================================================== */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--app-gap);
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  background: var(--glass-fill-strong);
}
.kpi-top { display: flex; align-items: center; gap: 8px; }
.kpi-top svg { width: 15px; height: 15px; flex: 0 0 15px; }
.kpi-label { font-size: 14px; line-height: 1.1; color: var(--ink-1); }

.kpi-value {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.kpi-num {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.01em;
  color: var(--ink-1);
}
.kpi-unit { font-size: 13px; color: var(--ink-3); }

.delta {
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}
.delta.is-up { background: var(--pos-bg); }
.delta.is-down { background: var(--neg-bg); }
/* No prior period to compare against yet. Saying "+0%" would be a claim we
   cannot support, so it reads as what it is. */
.delta.is-flat { background: var(--track); color: var(--ink-3); }

.kpi .tip p { font-size: 11px; }

/* ===== Layout rows ====================================================== */

.row-2 {
  display: grid;
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
  gap: var(--app-gap);
  align-items: stretch;
}
.row-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--app-gap);
  align-items: stretch;
}

/* ===== Segmented control ================================================ */

.segmented {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border-radius: var(--r-sm);
  background: var(--track);
}
.segmented button {
  flex: 1;
  padding: 5px 12px;
  border: none;
  border-radius: var(--r-xs);
  background: none;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  transition: background .16s ease, color .16s ease;
  white-space: nowrap;
}
.segmented button:hover { color: var(--ink-1); }
.segmented button[aria-pressed='true'] {
  background: #fff;
  color: var(--ink-1);
  box-shadow: var(--shadow-soft);
}

/* ===== Chart ============================================================ */

.chart-legend { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: 12px; }
.chart-legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--ink-2);
}
.swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }
.swatch.messages { background: var(--purple); }
.swatch.leads { background: var(--magenta); }

.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart .grid-line { stroke: var(--hairline); stroke-width: 1; }
.chart .axis-label { font-size: 10px; fill: var(--ink-3); font-family: var(--font-body); }
.chart .bar-messages { fill: var(--purple); opacity: .22; }
.chart .bar-leads { fill: var(--magenta); }
.chart .line-messages { fill: none; stroke: var(--purple); stroke-width: 2; stroke-linejoin: round; }
.chart-col { cursor: pointer; }
.chart-col:hover .bar-messages { opacity: .40; }

/* ===== Alerts (the triage list) ========================================= */

.alerts { display: flex; flex-direction: column; gap: 10px; }

.alert {
  display: block;
  padding: 12px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .58);
  border-left: 3px solid var(--ink-3);
  text-decoration: none;
  color: inherit;
  transition: background .16s ease, transform .16s ease;
}
.alert:hover { background: #fff; transform: translateX(2px); }
.alert.is-high { border-left-color: var(--neg-bg); }
.alert.is-medium { border-left-color: var(--warn); }
.alert.is-low { border-left-color: var(--purple); }

.alert-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 5px;
}
.alert-from {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.alert-time { font-size: 11px; color: var(--ink-3); white-space: nowrap; flex: 0 0 auto; }
.alert-summary { font-size: 12.5px; line-height: 1.45; color: var(--ink-2); margin: 0; }
.alert-meta { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }

.chip {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--track);
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-2);
  white-space: nowrap;
}
.chip.is-urgent { background: rgba(239, 68, 68, .12); color: #b91c1c; }
.chip.is-purple { background: rgba(85, 82, 255, .12); color: var(--purple); }

/* ===== Distribution bars (categories, usage) ============================ */

.dist { display: flex; flex-direction: column; gap: 11px; }
.dist-row { display: grid; grid-template-columns: 1fr auto; gap: 8px 10px; align-items: center; }
.dist-name { font-size: 12.5px; color: var(--ink-2); }
.dist-pct { font-size: 12.5px; font-weight: 600; color: var(--ink-1); }
.dist-track {
  grid-column: 1 / -1;
  height: 8px;
  border-radius: 999px;
  background: var(--track);
  overflow: hidden;
}
.dist-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--purple), var(--magenta));
  transition: width .6s cubic-bezier(.2, .8, .3, 1);
}

/* ===== Donut ============================================================ */

.donut-wrap { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.donut { flex: 0 0 132px; width: 132px; height: 132px; transform: rotate(-90deg); }
.donut circle { fill: none; stroke-width: 22; transition: stroke-dasharray .6s ease; }
.donut-legend { flex: 1; min-width: 150px; display: flex; flex-direction: column; gap: 9px; }
.donut-legend-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.donut-legend-row .swatch { border-radius: 50%; width: 11px; height: 11px; flex: 0 0 11px; }
.donut-legend-row .name { flex: 1; color: var(--ink-2); }
.donut-legend-row .val { font-weight: 600; color: var(--ink-1); }

/* ===== Health score ===================================================== */

.score-num {
  font-family: var(--font-display);
  font-size: 42px;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  color: var(--ink-1);
}
.score-caption { text-align: center; font-size: 12px; color: var(--ink-3); margin-top: 6px; }

.score-bar {
  display: flex;
  height: 16px;
  border-radius: var(--r-sm);
  overflow: hidden;
  margin: 16px 0 12px;
}
.score-bar span { display: block; height: 100%; }
.score-bar .seg-good { background: var(--pos-bg); }
.score-bar .seg-ok { background: #fcd34d; }
.score-bar .seg-bad { background: #fb7185; }

.score-key { display: flex; gap: 16px; flex-wrap: wrap; }
.score-key span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-2); }

/* ===== Funnel =========================================================== */

.funnel { display: flex; gap: 16px; align-items: flex-start; }
.funnel-graphic { flex: 0 0 118px; display: flex; flex-direction: column; gap: 4px; }
.funnel-step {
  display: grid;
  place-items: center;
  height: 32px;
  border-radius: 4px;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  margin: 0 auto;
}
.funnel-rows { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 11px; }
.funnel-row { display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
.funnel-row .label { color: var(--ink-2); white-space: nowrap; }
.funnel-row .rule {
  flex: 1;
  border-bottom: 1px dotted rgba(26, 26, 26, .22);
  transform: translateY(-3px);
}
.funnel-row .pct { font-weight: 600; color: var(--ink-1); }

/* ===== Empty + loading states =========================================== */

.empty { text-align: center; padding: 34px 20px; }
.empty h3 {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink-1);
  margin-bottom: 8px;
}
.empty p { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); max-width: 420px; margin: 0 auto 18px; }

.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--track) 25%, rgba(255, 255, 255, .6) 50%, var(--track) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  color: transparent !important;
}
@keyframes shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* A banner, not a silent fallback: if the numbers on screen are not the
   user's real data, the page has to say so where they cannot miss it. */
.demo-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 16px;
  border-radius: var(--r-md);
  background: rgba(245, 158, 11, .14);
  border: 1px solid rgba(245, 158, 11, .34);
  font-size: 13px;
  color: #92400e;
}
.demo-banner svg { width: 16px; height: 16px; flex: 0 0 16px; }
.demo-banner a { color: inherit; font-weight: 600; text-decoration: underline; }

/* ===== Buttons ========================================================== */

.btn-app {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 22px;
  border: none;
  border-radius: 999px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background .16s ease, transform .16s ease, opacity .16s ease;
}
.btn-app.primary { background: var(--purple); color: #fff; }
.btn-app.primary:hover { background: #4340f5; transform: translateY(-1px); }
.btn-app.ghost { background: rgba(255, 255, 255, .66); color: var(--ink-1); box-shadow: var(--glass-inset); }
.btn-app.ghost:hover { background: #fff; }
.btn-app.danger { background: rgba(239, 68, 68, .12); color: #b91c1c; }
.btn-app.danger:hover { background: rgba(239, 68, 68, .2); }
.btn-app[disabled] { opacity: .55; cursor: not-allowed; transform: none; }
.btn-app.is-busy { pointer-events: none; opacity: .8; }

/* ===== Forms ============================================================ */

.field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.field label { font-size: 13px; font-weight: 500; color: var(--ink-1); }
.field input,
.field select {
  height: 46px;
  padding: 0 15px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .74);
  font-family: inherit;
  font-size: 15px;
  color: var(--ink-1);
  outline: none;
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
  width: 100%;
}
.field input:focus,
.field select:focus {
  border-color: var(--purple);
  background: #fff;
  box-shadow: 0 0 0 3px rgba(85, 82, 255, .14);
}
.field input[aria-invalid='true'] { border-color: var(--neg); }
.field-hint { font-size: 12px; color: var(--ink-3); }
.field-error { font-size: 12.5px; color: var(--neg); min-height: 16px; }

.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 62px; }
.pw-toggle {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  height: 34px;
  padding: 0 12px;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-3);
  cursor: pointer;
}
.pw-toggle:hover { color: var(--purple); background: var(--track); }

.form-status { font-size: 13.5px; line-height: 1.5; margin-top: 4px; min-height: 20px; }
.form-status.is-err { color: var(--neg); }
.form-status.is-ok { color: var(--pos); }

/* ===== Auth pages ======================================================= */

.auth-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px 20px;
  position: relative;
  z-index: 1;
}

.auth-card { width: 100%; max-width: 428px; padding: 34px; }

.auth-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: center;
  margin-bottom: 26px;
  text-decoration: none;
}
.auth-brand img { height: 34px; width: auto; }

.auth-title {
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 500;
  line-height: 1.15;
  text-align: center;
  color: var(--ink-1);
}
.auth-sub {
  font-size: 14px;
  line-height: 1.5;
  text-align: center;
  color: var(--ink-2);
  margin: 9px 0 26px;
}

.auth-alt { text-align: center; font-size: 13.5px; color: var(--ink-2); margin-top: 20px; }
.auth-alt a { color: var(--purple); font-weight: 600; text-decoration: none; }
.auth-alt a:hover { text-decoration: underline; }

.auth-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
.check input { width: 16px; height: 16px; accent-color: var(--purple); cursor: pointer; }
.auth-link { font-size: 13px; color: var(--purple); text-decoration: none; }
.auth-link:hover { text-decoration: underline; }

.auth-foot { text-align: center; font-size: 12px; color: var(--ink-3); margin-top: 26px; }
.auth-foot a { color: var(--ink-2); text-decoration: none; }
.auth-foot a:hover { color: var(--purple); }

/* ===== Settings ========================================================= */

.settings-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--app-gap); }
.setting-card { padding: 22px; }
.setting-head { margin-bottom: 18px; }
.setting-head h2 {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink-1);
}
.setting-head p { font-size: 13px; color: var(--ink-2); margin-top: 5px; line-height: 1.5; }

.setting-form { max-width: 460px; }

.conn {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .58);
}
.conn + .conn { margin-top: 10px; }
.conn-icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  border-radius: var(--r-md);
  background: var(--track);
}
.conn-icon svg { width: 20px; height: 20px; color: var(--ink-2); }
.conn-body { flex: 1; min-width: 0; }
.conn-address { font-size: 14px; font-weight: 600; color: var(--ink-1); overflow: hidden; text-overflow: ellipsis; }
.conn-meta { font-size: 12px; color: var(--ink-3); margin-top: 3px; }

.status-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; }
.status-dot.ok { background: var(--pos-bg); }
.status-dot.warn { background: var(--warn); }
.status-dot.err { background: var(--neg-bg); }

.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 0;
}
.toggle-row + .toggle-row { border-top: 1px solid var(--hairline); }
.toggle-copy { flex: 1; min-width: 0; }
.toggle-copy strong { display: block; font-size: 14px; font-weight: 600; color: var(--ink-1); }
.toggle-copy span { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 3px; line-height: 1.45; }

.switch { position: relative; flex: 0 0 44px; width: 44px; height: 25px; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch span {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--track);
  transition: background .18s ease;
  pointer-events: none;
}
.switch span::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-soft);
  transition: transform .18s cubic-bezier(.2, .8, .3, 1);
}
.switch input:checked + span { background: var(--purple); }
.switch input:checked + span::after { transform: translateX(19px); }
.switch input:focus-visible + span { box-shadow: 0 0 0 3px rgba(85, 82, 255, .22); }

.danger-zone { border: 1px solid rgba(239, 68, 68, .28); background: rgba(239, 68, 68, .05); }

/* ===== Page heading ===================================================== */

.page-head-app { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.page-head-app h1 {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 500;
  line-height: 1.15;
  color: var(--ink-1);
}
.page-head-app p { font-size: 13.5px; color: var(--ink-2); margin-top: 6px; }

/* ===== Mobile =========================================================== */

.side-toggle { display: none; }

@media (max-width: 1180px) {
  .kpi-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .row-2 { grid-template-columns: minmax(0, 1fr); }
  .row-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); padding: 12px; }

  /* Off-canvas rather than squeezed: a 248px rail on a phone leaves nothing
     for the content it is meant to navigate. */
  .side {
    position: fixed;
    inset: 12px auto 12px 12px;
    width: var(--sidebar-w);
    z-index: 60;
    transform: translateX(calc(-100% - 24px));
    transition: transform .28s cubic-bezier(.2, .8, .3, 1);
    background: rgba(255, 255, 255, .86);
    box-shadow: var(--glass-inset), var(--shadow-lift);
    height: auto;
  }
  .side.is-open { transform: none; }

  .side-toggle {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
    border: none;
    border-radius: 999px;
    background: var(--glass-fill-strong);
    box-shadow: var(--glass-inset);
    color: var(--ink-1);
    cursor: pointer;
  }
  .side-toggle svg { width: 20px; height: 20px; }

  .side-scrim {
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(26, 26, 26, .34);
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease;
  }
  .side-scrim.is-open { opacity: 1; pointer-events: auto; }
}

@media (max-width: 760px) {
  .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .row-3 { grid-template-columns: minmax(0, 1fr); }
  .funnel { flex-direction: column; }
  .funnel-graphic { flex: none; width: 100%; }
  .auth-card { padding: 26px 20px; }
}

@media (max-width: 480px) {
  .kpi-row { grid-template-columns: minmax(0, 1fr); }
  .page-head-app h1 { font-size: 22px; }
  .search { padding: 0 14px; }
}

/* =========================================================================
   ThreadOptic loader.

   The mark is a single ribbon that loops back through itself, so the natural
   loading animation is to trace it: the stroke is drawn from one end, pulled
   around the loop, and pulled out the far side. That is the "tugged around the
   loop" idea from the original logo, redrawn for the eye.

   pathLength="100" is doing real work here. It normalises every path to 100
   units regardless of its actual geometry, so one set of dash values animates
   the almond and the pupil identically without measuring either.
   ========================================================================= */

.to-loader {
  /* inline-block, not block. Every empty state that shows this centres its
     contents with `text-align: center`, which only moves INLINE boxes: as a
     block the loader ignored it completely and sat hard against the left edge,
     126px off centre in the summary pane. Flex containers blockify their
     children anyway, so the loading veil is unaffected. */
  display: inline-block;
  vertical-align: middle;
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
}
.to-loader svg { width: 100%; height: 100%; display: block; overflow: visible; }

.to-loader .lid,
.to-loader .pupil {
  fill: none;
  stroke: url(#toLoaderGradient);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  animation: to-trace 1.5s cubic-bezier(.65, 0, .35, 1) infinite;
}
/* The pupil follows a beat behind, so the ribbon reads as one line being
   pulled through rather than two shapes appearing at once. */
.to-loader .pupil { animation-delay: .18s; }

@keyframes to-trace {
  0%   { stroke-dashoffset: 100; }
  45%  { stroke-dashoffset: 0; }
  55%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -100; }
}

/* Reduced motion still needs to signal "working", so it breathes instead of
   tracing. A frozen spinner reads as a broken page. */
@media (prefers-reduced-motion: reduce) {
  .to-loader .lid,
  .to-loader .pupil {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    animation: to-pulse 1.6s ease-in-out infinite;
  }
  @keyframes to-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
}

.to-loader.is-sm { width: 26px; height: 26px; }
.to-loader.is-lg { width: 64px; height: 64px; }

/* ---- Card-level loading veil ------------------------------------------- */
/*
   Dims the card and traces the mark over it while its data is in flight. A veil
   rather than replacing the content: the card keeps its height, so nothing
   below it jumps when the data lands.
*/

.is-loading { position: relative; }

.loader-veil {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border-radius: inherit;
  background: rgba(255, 255, 255, .66);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  animation: veil-in .18s ease both;
}
@keyframes veil-in { from { opacity: 0; } to { opacity: 1; } }

/* Fades out rather than vanishing, so a fast response does not flash. */
.loader-veil.is-leaving { animation: veil-out .22s ease both; }
@keyframes veil-out { to { opacity: 0; } }

.loader-veil p {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-3);
  text-align: center;
}

/* Inline loader for buttons and small rows. */
.loader-inline {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink-3);
}

/* ===== Confirmation page ================================================= */

.confirm-icon {
  width: 76px;
  height: 76px;
  margin: 0 auto 20px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(85, 82, 255, .10);
}
.confirm-icon svg { width: 34px; height: 34px; }
.confirm-icon.is-ok { background: rgba(34, 197, 94, .14); }
.confirm-icon.is-err { background: rgba(239, 68, 68, .12); }

.confirm-email {
  display: inline-block;
  margin-top: 4px;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--track);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-1);
  word-break: break-all;
}

/* A success state that then redirects needs to show the wait, or the page
   looks stuck for the second before it moves. */
.confirm-progress {
  height: 3px;
  border-radius: 999px;
  background: var(--track);
  overflow: hidden;
  margin-top: 22px;
}
.confirm-progress span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--purple), var(--magenta));
  animation: confirm-fill 2.4s linear forwards;
}
@keyframes confirm-fill { to { width: 100%; } }

/* =========================================================================
   Post-purchase onboarding (welcome.html) and the settings repeat of it.
   ========================================================================= */

.welcome-wrap {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 40px 20px;
}

.welcome-card {
  width: 100%;
  max-width: 760px;
  padding: 40px;
  text-align: center;
}

.welcome-tick {
  width: 64px;
  height: 64px;
  margin: 6px auto 20px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(34, 197, 94, .14);
}
.welcome-tick svg { width: 30px; height: 30px; }

.welcome-title {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.15;
  color: var(--ink-1);
}
.welcome-sub {
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-2);
  max-width: 470px;
  margin: 10px auto 30px;
}

/* ---- The two choices ---------------------------------------------------- */

.welcome-choices {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  text-align: left;
}

.choice {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 22px;
  border-radius: var(--r-card);
  background: rgba(255, 255, 255, .62);
  border: 1px solid var(--hairline);
}
/* The add-in is the experience the product is actually for, so it is visibly
   the primary path rather than one of two equal-looking options. */
.choice.is-featured {
  background: rgba(255, 255, 255, .86);
  border-color: rgba(85, 82, 255, .34);
  box-shadow: 0 12px 32px rgba(85, 82, 255, .12);
}

.choice-flag {
  position: absolute;
  top: -10px;
  right: 18px;
  padding: 3px 11px;
  border-radius: 999px;
  background: var(--purple);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.choice-icon {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: var(--r-md);
  background: var(--track);
  color: var(--ink-2);
  margin-bottom: 14px;
}
.choice-icon svg { width: 20px; height: 20px; }
.choice-icon.is-purple { background: rgba(85, 82, 255, .12); color: var(--purple); }

.choice h2 {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 500;
  color: var(--ink-1);
  margin-bottom: 7px;
}
.choice p {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-2);
  /* Pushes the button to the bottom so both cards align regardless of copy. */
  flex: 1;
  margin-bottom: 18px;
}
.choice .btn-app { width: 100%; }

.choice-note {
  margin-top: 10px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-3);
  flex: none;
}
.choice-note.is-soon { color: #92400e; }

/* ---- Walkthrough --------------------------------------------------------- */

.walkthrough {
  margin-top: 28px;
  padding-top: 26px;
  border-top: 1px solid var(--hairline);
  text-align: left;
  /* Slides in rather than appearing, so it is obvious it is new content and
     not something that was always there. */
  animation: walkthrough-in .34s cubic-bezier(.2, .8, .3, 1) both;
}
@keyframes walkthrough-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .walkthrough { animation: none; }
}

.walkthrough-head h2 {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 500;
  color: var(--ink-1);
  margin-bottom: 6px;
}
.walkthrough-head p { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); margin-bottom: 20px; }

.steps-list {
  list-style: none;
  counter-reset: step;
  margin-bottom: 22px;
}
.steps-list li {
  counter-increment: step;
  position: relative;
  padding-left: 40px;
  margin-bottom: 13px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-2);
}
.steps-list li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -1px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(85, 82, 255, .12);
  color: var(--purple);
  font-size: 12.5px;
  font-weight: 700;
}
.steps-list li strong { color: var(--ink-1); font-weight: 600; }

.walkthrough-actions { display: flex; gap: 10px; flex-wrap: wrap; }

.welcome-foot {
  margin-top: 26px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.welcome-foot a { color: var(--purple); font-weight: 600; text-decoration: none; }
.welcome-foot a:hover { text-decoration: underline; }

/* The manifest download fallback, shown when there is no marketplace listing. */
.manifest-box {
  margin-top: 14px;
  padding: 14px;
  border-radius: var(--r-md);
  background: rgba(245, 158, 11, .10);
  border: 1px solid rgba(245, 158, 11, .30);
}
.manifest-box p { font-size: 12.5px; line-height: 1.55; color: #92400e; margin-bottom: 10px; }
.manifest-box a { color: #92400e; font-weight: 600; }

@media (max-width: 700px) {
  .welcome-card { padding: 28px 20px; }
  .welcome-choices { grid-template-columns: minmax(0, 1fr); }
  .welcome-title { font-size: 26px; }
  .choice-flag { right: 14px; }
}

/* =========================================================================
   Dashboard v2.

   Three problems being fixed here:
     - The summary read as a grey wall. Nothing was weighted, so nothing was
       scannable. Headers are now near-black and heavy, urgency carries colour,
       and red is reserved for genuinely urgent so it still means something.
     - The mail list looked nothing like mail. It now follows the shape Outlook
       uses: sender, time, subject, one line of preview, with an unread rail.
     - Every card carried an explanatory sentence. They are gone.
   ========================================================================= */

/* ---- KPI: number first, no essay ---------------------------------------- */

.kpi {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 15px 16px;
  background: var(--glass-fill-strong);
}
.kpi-label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.kpi-num {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -.02em;
  color: var(--ink-1);
}
.kpi-meta { font-size: 12px; color: var(--ink-3); min-height: 16px; }
.kpi.is-urgent .kpi-num { color: #dc2626; }
.kpi.is-urgent .kpi-meta { color: #dc2626; font-weight: 600; }
.kpi.is-warn .kpi-num { color: #b45309; }

/* ---- Mail list ----------------------------------------------------------- */

.mail-list { display: flex; flex-direction: column; }

.mail-row {
  display: flex;
  gap: 10px;
  padding: 10px 10px 10px 0;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
  transition: background .14s ease;
}
.mail-row:hover { background: rgba(255, 255, 255, .55); }
.mail-row.is-selected { background: #fff; }
.mail-row:last-child { border-bottom: none; }

/* The unread rail: a bar in the gutter rather than colouring everything. */
.mail-rail {
  flex: 0 0 3px;
  border-radius: 0 3px 3px 0;
  background: transparent;
}
.mail-row.is-unread .mail-rail { background: var(--purple); }
.mail-rail.is-high { background: #ef4444; }
.mail-rail.is-lead { background: var(--purple); }

.mail-main { flex: 1; min-width: 0; }
.mail-line1 { display: flex; align-items: baseline; gap: 10px; }
.mail-from {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mail-row.is-unread .mail-from { font-weight: 700; }
.mail-time { flex: 0 0 auto; font-size: 11.5px; color: var(--ink-3); }

.mail-line2 { display: flex; align-items: center; gap: 6px; margin-top: 1px; }
.mail-subject {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mail-row.is-unread .mail-subject { font-weight: 600; }

.mail-flag { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 7px; }
.mail-flag.is-high { background: #ef4444; }
.mail-flag.is-lead { background: var(--purple); }

.mail-preview {
  margin-top: 2px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ink-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---- Summary: weighted, not grey ---------------------------------------- */

.verdict {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 11px 13px;
  border-radius: var(--r-md);
  margin-bottom: 16px;
  background: var(--track);
  border-left: 3px solid var(--ink-3);
}
.verdict.is-high { background: rgba(239, 68, 68, .09); border-left-color: #ef4444; }
.verdict.is-medium { background: rgba(245, 158, 11, .11); border-left-color: #f59e0b; }
.verdict.is-low { background: rgba(85, 82, 255, .08); border-left-color: var(--purple); }

.verdict-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: 0 0 8px; background: var(--ink-3); }
.verdict.is-high .verdict-dot { background: #ef4444; }
.verdict.is-medium .verdict-dot { background: #f59e0b; }
.verdict.is-low .verdict-dot { background: var(--purple); }

.verdict-text { flex: 1; min-width: 0; }
.verdict-text strong { display: block; font-size: 13.5px; font-weight: 700; color: #111; }
.verdict.is-high .verdict-text strong { color: #b91c1c; }
.verdict-text span { display: block; margin-top: 2px; font-size: 12.5px; line-height: 1.45; color: var(--ink-2); }

.verdict-tag {
  flex: 0 0 auto;
  align-self: center;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--purple);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
}

/* The one-line answer. Larger and darker than everything under it. */
.lede {
  font-size: 15px;
  line-height: 1.55;
  color: #141414;
  font-weight: 500;
  margin-bottom: 20px;
}

.blk { margin-bottom: 18px; }
.blk h3 {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #111;
  margin-bottom: 9px;
}

ul.todo { list-style: none; }
ul.todo li {
  display: flex;
  gap: 9px;
  margin-bottom: 8px;
  font-size: 13.5px;
  line-height: 1.5;
  color: #1a1a1a;
  font-weight: 500;
}
ul.todo li::before {
  content: '';
  flex: 0 0 14px;
  width: 14px;
  height: 14px;
  margin-top: 3px;
  border: 1.6px solid var(--purple);
  border-radius: 4px;
}

ul.pts { list-style: none; }
ul.pts li {
  position: relative;
  padding-left: 15px;
  margin-bottom: 6px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
}
ul.pts li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 8px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--purple);
}

.when-list { display: flex; flex-direction: column; gap: 7px; }
.when {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 8px 11px;
  border-radius: 9px;
  background: rgba(85, 82, 255, .07);
}
.when-key { flex: 0 0 auto; font-size: 12.5px; font-weight: 700; color: var(--purple); }
.when-val { flex: 1; min-width: 0; font-size: 13px; color: #1a1a1a; }

.mini-card {
  padding: 9px 11px;
  border-radius: 9px;
  background: rgba(255, 255, 255, .7);
  border: 1px solid var(--hairline);
  margin-bottom: 7px;
}
.mini-card strong { display: block; font-size: 13px; color: #111; }
.mini-card span { display: block; font-size: 12px; color: var(--ink-2); word-break: break-all; }

.sum-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.btn-app.sm { height: 34px; padding: 0 15px; font-size: 13px; }
.soft { font-size: 13px; color: var(--ink-2); }

/* A disabled button never fires title=, so the reason it is greyed has to come
   from the wrapper around it. */
.tip-wrap { position: relative; display: inline-flex; }
.tip-wrap::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 10px;
  border-radius: 7px;
  background: #1a1a1a;
  color: #fff;
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease;
  z-index: 10;
}
.tip-wrap:hover::after { opacity: 1; }

/* ---- Contacts ------------------------------------------------------------ */

.tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 14px; }
.tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: none;
  border-radius: 999px;
  background: var(--track);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
  cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
.tab span { font-size: 11px; opacity: .7; }
.tab:hover { background: rgba(0, 0, 0, .1); color: var(--ink-1); }
.tab.is-on { background: var(--purple); color: #fff; }
.tab.is-on span { opacity: .85; }

.people { display: flex; flex-direction: column; }
.person {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
  transition: background .14s ease;
}
.person:hover { background: rgba(255, 255, 255, .6); }
.person:last-child { border-bottom: none; }

.person-av {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--purple), var(--magenta));
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
}
.person-av.lg { flex: 0 0 52px; width: 52px; height: 52px; font-size: 18px; }

.person-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.person-name { font-size: 13.5px; font-weight: 600; color: var(--ink-1); }
.person-mail {
  font-size: 12px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.person-count { flex: 0 0 auto; font-size: 12px; color: var(--ink-3); }

.back-link {
  border: none;
  background: none;
  padding: 0 0 12px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--purple);
  cursor: pointer;
}
.back-link:hover { text-decoration: underline; }

.profile-head { display: flex; gap: 14px; align-items: center; margin-bottom: 18px; }
.profile-id { flex: 1; min-width: 0; }
.profile-name-row { display: flex; align-items: center; gap: 8px; }
.profile-id h3 { font-family: var(--font-display); font-size: 19px; font-weight: 500; color: var(--ink-1); }
.profile-id p { font-size: 12.5px; color: var(--ink-3); word-break: break-all; }
.profile-meta { margin-top: 2px; }

.icon-mini {
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.icon-mini svg { width: 13px; height: 13px; }
.icon-mini:hover { background: var(--track); color: var(--purple); }

.rename-input {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 500;
  color: var(--ink-1);
  border: none;
  border-bottom: 2px solid var(--purple);
  background: transparent;
  outline: none;
  padding: 0 0 2px;
  max-width: 260px;
}

.chips { display: flex; gap: 6px; flex-wrap: wrap; }

.dist-fill.is-high { background: linear-gradient(90deg, #ef4444, #f87171); }
.dist-fill.is-medium { background: linear-gradient(90deg, #f59e0b, #fbbf24); }
.dist-fill.is-low { background: linear-gradient(90deg, var(--purple), var(--magenta)); }
.dist-fill.is-none { background: var(--ink-3); }

.demo-banner.is-ok {
  background: rgba(34, 197, 94, .12);
  border-color: rgba(34, 197, 94, .34);
  color: #166534;
}

/* The manual install route sits inside a sentence, so on its own it read as
   more prose. Given a button-like treatment it is obvious it can be pressed. */
.choice-note.is-soon a {
  display: inline-block;
  margin-top: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  background: #92400e;
  color: #fff;
  font-weight: 700;
  text-decoration: none;
  transition: background .15s ease, transform .15s ease;
}
.choice-note.is-soon a:hover { background: #7c3209; transform: translateY(-1px); }
.choice-note.is-soon a::after { content: " ->"; }

/* =========================================================================
   Reading type at large viewports, dashboard side.

   Same reasoning as the marketing stylesheet: the layout already widens on a
   big screen but the text never grew with it, so a 1920 monitor showed a very
   wide page of laptop-sized copy. Only type moves here. No card padding, no
   gaps, no grid columns, so nothing reflows.
   ========================================================================= */

@media (min-width: 1536px) {
  /* Mail list: the densest text on the page, and the most read. */
  .mail-from { font-size: 14.5px; }
  .mail-subject { font-size: 14px; }
  .mail-preview { font-size: 13.5px; }
  .mail-time { font-size: 12.5px; }

  /* Summary panel */
  .lede { font-size: 16px; }
  .blk h3 { font-size: 11.5px; }
  ul.todo li { font-size: 14.5px; }
  ul.pts li { font-size: 14px; }
  .when-key { font-size: 13.5px; }
  .when-val { font-size: 14px; }
  .verdict-text strong { font-size: 14.5px; }
  .verdict-text span { font-size: 13.5px; }
  .mini-card strong { font-size: 14px; }
  .mini-card span { font-size: 13px; }

  /* Cards and headings */
  .card-title { font-size: 17px; }
  .card-sub { font-size: 13px; }
  .page-head-app h1 { font-size: 28px; }
  .page-head-app p { font-size: 14.5px; }
  .kpi-label { font-size: 12px; }
  .kpi-num { font-size: 33px; }
  .kpi-meta { font-size: 13px; }

  /* Contacts */
  .person-name { font-size: 14.5px; }
  .person-mail { font-size: 13px; }
  .person-count { font-size: 13px; }
  .tab { font-size: 13.5px; }
  .profile-id h3 { font-size: 21px; }
  .profile-id p { font-size: 13.5px; }

  /* Sidebar and chrome */
  .side-link { font-size: 15px; }
  .side-brand-name { font-size: 16px; }
  .side-brand-sub { font-size: 12px; }
  .search input { font-size: 15px; }
  .btn-app { font-size: 16px; }
  .btn-app.sm { font-size: 14px; }

  /* Settings */
  .setting-head h2 { font-size: 19px; }
  .setting-head p { font-size: 14px; }
  .field label { font-size: 14px; }
  .field input, .field select { font-size: 16px; }
  .toggle-copy strong { font-size: 15px; }
  .toggle-copy span { font-size: 13.5px; }
  .conn-address { font-size: 15px; }
  .conn-meta { font-size: 13px; }

  /* Onboarding */
  .welcome-title { font-size: 34px; }
  .welcome-sub { font-size: 16px; }
  .choice h2 { font-size: 18px; }
  .choice p { font-size: 14.5px; }
  .steps-list li { font-size: 15px; }
}

@media (min-width: 1800px) {
  .mail-preview { font-size: 14px; }
  .lede { font-size: 16.5px; }
  .card-sub { font-size: 13.5px; }
  .side-link { font-size: 15.5px; }
  ul.pts li { font-size: 14.5px; }
  .person-mail { font-size: 13.5px; }
}

/* =========================================================================
   Dashboard v3: dark mode, the two streams, to-dos, density, daily brief.
   ========================================================================= */

/* ---- Dark mode ----------------------------------------------------------
   Driven by data-theme on <html>, written by app.js from the stored
   preference. 'system' resolves to a real value before paint, so there is no
   flash of the wrong theme.

   The gradient inverts to the same hues at low lightness rather than going
   flat grey, so the identity survives the switch. */

html[data-theme='dark'] {
  --glass-fill: rgba(255, 255, 255, .05);
  --glass-fill-strong: rgba(255, 255, 255, .08);
  --glass-inset:
    inset 1px 1px 2px 0 rgba(255, 255, 255, .07),
    inset 0 0 1px 1px rgba(255, 255, 255, .05);

  --ink-1: #f2f2f6;
  --ink-2: rgba(242, 242, 246, .70);
  --ink-3: rgba(242, 242, 246, .48);
  --hairline: rgba(255, 255, 255, .10);
  --track: rgba(255, 255, 255, .07);

  --purple: #8b88ff;
  --magenta: #e08cff;

  --shadow-soft: 0 2px 8px rgba(0, 0, 0, .35);
  --shadow-lift: 0 12px 32px rgba(0, 0, 0, .45);
}

html[data-theme='dark'] body.app {
  background:
    linear-gradient(180deg, #12131b 0%, #171826 46%, #1b1526 78%, #1d1424 100%) fixed;
  color: var(--ink-1);
}
html[data-theme='dark'] body.app::before {
  background:
    radial-gradient(70% 55% at 12% 96%, rgba(210, 81, 255, .13), transparent 70%),
    radial-gradient(60% 45% at 92% 4%, rgba(85, 82, 255, .11), transparent 70%);
}

html[data-theme='dark'] .mail-row:hover { background: rgba(255, 255, 255, .05); }
html[data-theme='dark'] .mail-row.is-selected { background: rgba(255, 255, 255, .09); }
html[data-theme='dark'] .side-link:hover { background: rgba(255, 255, 255, .06); }
html[data-theme='dark'] .side-link.is-active { background: rgba(255, 255, 255, .09); }
html[data-theme='dark'] .lede { color: #f4f4f8; }
html[data-theme='dark'] .blk h3 { color: #f4f4f8; }
html[data-theme='dark'] ul.todo li { color: #ececf2; }
html[data-theme='dark'] .verdict-text strong { color: #f4f4f8; }
html[data-theme='dark'] .verdict.is-high .verdict-text strong { color: #fca5a5; }
html[data-theme='dark'] .kpi.is-urgent .kpi-num,
html[data-theme='dark'] .kpi.is-urgent .kpi-meta { color: #fca5a5; }
html[data-theme='dark'] .mini-card,
html[data-theme='dark'] .date-row,
html[data-theme='dark'] .person:hover { background: rgba(255, 255, 255, .05); }
html[data-theme='dark'] .mini-card strong,
html[data-theme='dark'] .when-val { color: var(--ink-1); }
html[data-theme='dark'] .btn-app.ghost { background: rgba(255, 255, 255, .08); color: var(--ink-1); }
html[data-theme='dark'] .btn-app.ghost:hover { background: rgba(255, 255, 255, .13); }
html[data-theme='dark'] .field input,
html[data-theme='dark'] .field select { background: rgba(255, 255, 255, .06); color: var(--ink-1); }
html[data-theme='dark'] .segmented button[aria-pressed='true'] { background: rgba(255, 255, 255, .16); color: var(--ink-1); }
html[data-theme='dark'] .tab.is-on { background: var(--purple); color: #14141c; }
html[data-theme='dark'] .loader-veil { background: rgba(18, 19, 27, .72); }
html[data-theme='dark'] .icon-btn:hover { background: rgba(255, 255, 255, .14); }

/* ---- Stream tabs --------------------------------------------------------
   Two inboxes, ranked on different scales. People come first because a person
   waiting is the only thing that can genuinely go cold. */

.stream-tabs {
  display: flex;
  gap: 4px;
  padding: 3px;
  border-radius: 999px;
  background: var(--track);
  margin-bottom: 14px;
  width: fit-content;
}
.stream-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 15px;
  border: none;
  border-radius: 999px;
  background: transparent;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-3);
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.stream-tab:hover { color: var(--ink-1); }
.stream-tab[aria-pressed='true'] {
  background: #fff;
  color: var(--ink-1);
  box-shadow: var(--shadow-soft);
}
html[data-theme='dark'] .stream-tab[aria-pressed='true'] { background: rgba(255, 255, 255, .14); }

.stream-count {
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--track);
  font-size: 11px;
  font-weight: 700;
}
.stream-tab[aria-pressed='true'] .stream-count { background: rgba(85, 82, 255, .16); color: var(--purple); }
/* An automated item that genuinely needs action still earns a marker, it just
   never competes with the people tab for the top of the list. */
.stream-count.is-act { background: rgba(245, 158, 11, .2); color: #b45309; }

/* Automated mail is ranked by whether it needs doing, not by how fast. */
.mail-rail.is-act { background: #f59e0b; }
.mail-rail.is-review { background: #94a3b8; }
.mail-flag.is-act { background: #f59e0b; }

.auto-kind {
  display: inline-flex;
  align-items: center;
  height: 17px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--track);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.auto-kind.is-security { background: rgba(239, 68, 68, .13); color: #b91c1c; }
.auto-kind.is-billing { background: rgba(245, 158, 11, .16); color: #92400e; }
html[data-theme='dark'] .auto-kind.is-security { color: #fca5a5; }
html[data-theme='dark'] .auto-kind.is-billing { color: #fcd34d; }

.verdict.is-act { background: rgba(245, 158, 11, .11); border-left-color: #f59e0b; }
.verdict.is-act .verdict-dot { background: #f59e0b; }
.verdict.is-review { background: var(--track); border-left-color: #94a3b8; }
.verdict.is-review .verdict-dot { background: #94a3b8; }

/* ---- Compact density ----------------------------------------------------
   Drops the preview line and gives the width back to the summary panel, for
   people who would rather read the summary than the inbox. */

.is-compact .mail-preview { display: none; }
.is-compact .mail-row { padding-top: 7px; padding-bottom: 7px; }
.is-compact .mail-line2 { margin-top: 0; }

/* The pair re-weights so the reclaimed space actually goes somewhere. */
.row-2.is-compact-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); }

/* ---- To-do list --------------------------------------------------------- */

.todo-groups { display: flex; flex-direction: column; gap: 20px; }

.todo-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.todo-group-head .bead { width: 7px; height: 7px; border-radius: 50%; }
.bead.is-critical { background: #dc2626; }
.bead.is-high { background: #f59e0b; }
.bead.is-normal { background: var(--purple); }
.bead.is-low { background: #94a3b8; }

.todo-item {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  padding: 11px 12px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--hairline);
  margin-bottom: 7px;
  transition: opacity .2s ease, background .15s ease;
}
html[data-theme='dark'] .todo-item { background: rgba(255, 255, 255, .05); }
.todo-item.is-done { opacity: .45; }
.todo-item.is-done .todo-task { text-decoration: line-through; }

.todo-check {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border: 1.7px solid var(--purple);
  border-radius: 5px;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
}
.todo-check svg { width: 11px; height: 11px; opacity: 0; color: #fff; }
.todo-item.is-done .todo-check { background: var(--purple); }
.todo-item.is-done .todo-check svg { opacity: 1; }

.todo-body { flex: 1; min-width: 0; }
.todo-task { font-size: 14px; line-height: 1.45; color: var(--ink-1); font-weight: 500; }
.todo-meta { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 5px; font-size: 11.5px; color: var(--ink-3); }
.todo-due { font-weight: 700; color: var(--purple); }
.todo-src {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 260px;
}

/* ---- Daily brief -------------------------------------------------------- */

.brief-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(20, 20, 28, .5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: fade-in .2s ease both;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.brief-card {
  width: 100%;
  max-width: 520px;
  padding: 30px;
  background: var(--glass-fill-strong);
  border-radius: var(--r-card);
  box-shadow: var(--glass-inset), var(--shadow-lift);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  animation: brief-in .28s cubic-bezier(.2, .8, .3, 1) both;
}
html[data-theme='dark'] .brief-card { background: #1b1c28; }
html[data-theme='light'] .brief-card, html:not([data-theme='dark']) .brief-card { background: #fff; }
@keyframes brief-in { from { opacity: 0; transform: translateY(10px) scale(.985); } to { opacity: 1; transform: none; } }

.brief-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--purple);
  margin-bottom: 9px;
}
.brief-headline {
  font-family: var(--font-display);
  font-size: 23px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--ink-1);
  margin-bottom: 10px;
}
.brief-body { font-size: 14.5px; line-height: 1.6; color: var(--ink-2); margin-bottom: 20px; }
.brief-close { margin-top: 22px; width: 100%; }

/* The sidebar entry glows briefly after the brief is dismissed, so the user
   learns where it went without being told in words. */
.side-link.is-glowing {
  animation: brief-glow 2.4s ease-in-out 3;
}
@keyframes brief-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(85, 82, 255, 0); }
  50% { box-shadow: 0 0 0 3px rgba(85, 82, 255, .28); background: rgba(85, 82, 255, .10); }
}
@media (prefers-reduced-motion: reduce) {
  .side-link.is-glowing { animation: none; background: rgba(85, 82, 255, .10); }
  .brief-card, .brief-backdrop { animation: none; }
}

/* ---- Silent resume on the login page ------------------------------------ */

.resume-veil {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  border-radius: inherit;
  background: rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: fade-in .16s ease both;
}
html[data-theme='dark'] .resume-veil { background: rgba(20, 21, 30, .86); }
.resume-veil.is-gone { animation: fade-out .2s ease both; }
@keyframes fade-out { to { opacity: 0; } }

.resume-inner { text-align: center; }
.resume-title {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 500;
  color: var(--ink-1);
  margin-top: 10px;
}
.resume-note { font-size: 13px; color: var(--ink-3); margin-top: 4px; }

/* ---- Avatar image ------------------------------------------------------- */

.avatar { overflow: hidden; }
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person-av img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* ===== Simple search: a contact's mail, below their contact list ==========
   The contacts view is now two stacked cards rather than one, so it needs the
   same gap the other multi-card views get. */

#view-contacts {
  display: flex;
  flex-direction: column;
  gap: var(--app-gap);
}

/* The selected person. A left bar rather than a fill, so the row does not
   fight the hover state and the selection survives a mouse moving over it. */
.person.is-selected {
  background: var(--track);
  box-shadow: inset 3px 0 0 var(--purple);
  border-radius: 8px;
}
.person.is-selected .person-name { color: var(--purple); font-weight: 600; }

/* Revealed on hover so the row stays quiet at rest. Always present for
   keyboard users, who never trigger hover and would otherwise never reach it. */
.person-profile { opacity: 0; transition: opacity .14s ease; }
.person:hover .person-profile,
.person:focus-within .person-profile,
.person-profile:focus-visible { opacity: 1; }

/* ===== "Bring me there" =================================================== */

.goto-btn {
  position: relative;
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
  align-self: center;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  opacity: .55;
  transition: opacity .14s ease, background .14s ease, color .14s ease, transform .14s ease;
}
.goto-btn svg { width: 15px; height: 15px; }
.goto-btn:hover {
  opacity: 1;
  background: var(--purple);
  color: #fff;
  transform: translateX(2px);
}
.goto-btn:focus-visible { opacity: 1; outline: 2px solid var(--purple); outline-offset: 2px; }

/* The label. A title attribute would work but appears after a browser delay of
   about a second, which is long enough that nobody ever sees it. */
.goto-tip {
  position: absolute;
  right: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%) translateX(4px);
  padding: 5px 9px;
  border-radius: 7px;
  background: var(--ink-1);
  color: var(--app-bg-mid);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity .14s ease, transform .14s ease;
  z-index: 5;
}
.goto-btn:hover .goto-tip,
.goto-btn:focus-visible .goto-tip { opacity: 1; transform: translateY(-50%) translateX(0); }

/* Where the arrow lands. Long enough to find with your eyes, short enough not
   to still be glowing when you start reading. */
@keyframes landed {
  0%   { background: rgba(85, 82, 255, .22); }
  100% { background: transparent; }
}
.mail-row.is-landed { animation: landed 1.6s ease-out; }

/* Rows in the contact mail list and to-do rows both carry an arrow on the
   right, so both need to stop their text running underneath it. */
.todo-item, .mail-row { position: relative; }
.todo-item .goto-btn, .mail-row .goto-btn { margin-left: auto; }

.brief-task {
  display: flex;
  align-items: center;
  gap: 9px;
}
.brief-task-text { flex: 1 1 auto; min-width: 0; }

@media (prefers-reduced-motion: reduce) {
  .goto-btn, .goto-tip { transition: none; }
  .mail-row.is-landed { animation: none; outline: 2px solid var(--purple); }
}

/* ===== Read state =========================================================
   Bold text and a coloured rail were the only signals, and the rail also
   carries whether automated mail needs action, so the two were competing for
   the same three pixels. A filled dot is unread, a hollow ring is read. */

.read-dot {
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--ink-4, #b9b9c6);
  background: transparent;
  align-self: center;
}
.read-dot.is-unread {
  border-color: var(--purple);
  background: var(--purple);
  box-shadow: 0 0 0 3px rgba(85, 82, 255, .16);
}

html[data-theme='dark'] .read-dot { border-color: rgba(255, 255, 255, .34); }
html[data-theme='dark'] .read-dot.is-unread {
  border-color: var(--purple);
  background: var(--purple);
}

.mail-line1 { align-items: center; }

/* =========================================================================
   Voice mode.

   The orb is built from layered gradients rather than a WebGL canvas: no
   dependency, no build step, and if anything here is unsupported it degrades
   to a static purple disc rather than a black rectangle.

   Four phases, each visibly different from across a room:
     idle       slow breathing, dim
     listening  bright, rings pushing outward
     thinking   swirls accelerate
     speaking   fast pulse
   ========================================================================= */

.voice-panel {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(12, 12, 22, .58);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  animation: veil-in .2s ease both;
}

.voice-sheet {
  width: min(460px, 100%);
  max-height: 92vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 30px 26px 26px;
  border-radius: 26px;
  background: var(--app-bg-mid, #fff);
  box-shadow: 0 40px 90px -30px rgba(20, 20, 60, .5);
  position: relative;
}
html[data-theme='dark'] .voice-sheet { background: #171826; }

.voice-close {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-3);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}
.voice-close:hover { background: var(--track); color: var(--ink-1); }

.voice-stage {
  display: grid;
  place-items: center;
  width: 100%;
  height: 190px;
}

/* ---- The orb ------------------------------------------------------------ */

.orb {
  position: relative;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  isolation: isolate;
  transition: transform .35s cubic-bezier(.2, .8, .2, 1);
}
.orb > span {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: block;
}

/* The body of the sphere. Purple into blue, with the light off centre so it
   reads as a lit ball rather than a flat circle. */
.orb-core {
  background:
    radial-gradient(circle at 34% 30%, #8f8bff 0%, #5552ff 34%, #2f2bb8 68%, #171a4d 100%);
  box-shadow:
    inset -12px -16px 34px rgba(8, 8, 40, .62),
    inset 10px 12px 30px rgba(160, 190, 255, .3);
}

/* Three offset conic sweeps at different speeds. Overlapping rotation at
   incommensurate periods is what stops the motion looking like a spinning
   texture and starts it looking like something moving inside the glass. */
.orb-swirl {
  mix-blend-mode: screen;
  filter: blur(11px);
  opacity: .72;
}
.orb-swirl-a {
  background: conic-gradient(from 0deg, transparent 0%, #4f7cff 18%, transparent 42%, #b06bff 66%, transparent 88%);
  animation: orb-spin 14s linear infinite;
}
.orb-swirl-b {
  background: conic-gradient(from 140deg, transparent 8%, #29d0ff 30%, transparent 55%, #7a5cff 78%, transparent 96%);
  animation: orb-spin 21s linear infinite reverse;
  opacity: .55;
}
.orb-swirl-c {
  background: conic-gradient(from 300deg, transparent 0%, #d251ff 22%, transparent 48%, #3aa0ff 74%, transparent 100%);
  animation: orb-spin 31s linear infinite;
  opacity: .4;
}

/* A specular highlight, so the sphere has a surface. */
.orb-sheen {
  background:
    radial-gradient(ellipse 44% 30% at 32% 24%, rgba(255, 255, 255, .78), rgba(255, 255, 255, 0) 62%);
  mix-blend-mode: screen;
}

.orb-ring {
  inset: -10px;
  border: 1.5px solid rgba(130, 140, 255, .5);
  opacity: 0;
}
.orb-ring-2 { inset: -10px; animation-delay: .9s; }

@keyframes orb-spin { to { transform: rotate(360deg); } }

@keyframes orb-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.035); }
}

@keyframes orb-ping {
  0%   { opacity: .65; transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.42); }
}

@keyframes orb-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.07); }
}

/* ---- Phases ------------------------------------------------------------- */

.orb[data-phase="idle"] { animation: orb-breathe 5.5s ease-in-out infinite; }
.orb[data-phase="idle"] .orb-core { filter: saturate(.82) brightness(.86); }

.orb[data-phase="listening"] { transform: scale(1.06); }
.orb[data-phase="listening"] .orb-ring { animation: orb-ping 1.8s ease-out infinite; }
.orb[data-phase="listening"] .orb-swirl-a { animation-duration: 9s; }

.orb[data-phase="thinking"] { animation: orb-breathe 1.5s ease-in-out infinite; }
.orb[data-phase="thinking"] .orb-swirl-a { animation-duration: 3.4s; }
.orb[data-phase="thinking"] .orb-swirl-b { animation-duration: 5s; }
.orb[data-phase="thinking"] .orb-swirl-c { animation-duration: 7s; }

.orb[data-phase="speaking"] { animation: orb-pulse 620ms ease-in-out infinite; }
.orb[data-phase="speaking"] .orb-core { filter: saturate(1.15) brightness(1.08); }
.orb[data-phase="speaking"] .orb-swirl-a { animation-duration: 6s; }

/* A spinning gradient is exactly the kind of motion that causes trouble, so
   reduced motion gets a still orb that only changes brightness. */
@media (prefers-reduced-motion: reduce) {
  .orb, .orb-swirl, .orb-ring { animation: none !important; }
  .orb[data-phase="listening"] .orb-core { filter: brightness(1.15); }
  .orb[data-phase="speaking"] .orb-core { filter: brightness(1.25); }
}

/* ---- Copy and controls -------------------------------------------------- */

.voice-phase {
  margin: 0;
  font-family: var(--font-display, inherit);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink-1);
}

.voice-transcript {
  margin: 0;
  min-height: 20px;
  font-size: 14px;
  color: var(--ink-2);
  text-align: center;
}
.voice-transcript.is-interim { color: var(--ink-3); font-style: italic; }

.voice-note {
  margin: 0;
  font-size: 13px;
  color: var(--purple);
  text-align: center;
}

.voice-log {
  width: 100%;
  max-height: 150px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.voice-msg {
  padding: 9px 13px;
  border-radius: 14px;
  font-size: 13.5px;
  line-height: 1.5;
  max-width: 88%;
}
.voice-msg.is-you {
  align-self: flex-end;
  background: var(--purple);
  color: #fff;
  border-bottom-right-radius: 5px;
}
.voice-msg.is-assistant {
  align-self: flex-start;
  background: var(--track);
  color: var(--ink-1);
  border-bottom-left-radius: 5px;
}

.voice-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: center;
}

.voice-modes {
  display: flex;
  gap: 3px;
  padding: 3px;
  border-radius: 999px;
  background: var(--track);
}
.voice-mode {
  border: none;
  background: transparent;
  padding: 7px 13px;
  border-radius: 999px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-3);
  cursor: pointer;
}
.voice-mode.is-on { background: var(--app-bg-mid, #fff); color: var(--purple); }
html[data-theme='dark'] .voice-mode.is-on { background: #24263a; }

.voice-talk {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 46px;
  padding: 0 22px;
  border: none;
  border-radius: 999px;
  background: linear-gradient(135deg, #5552ff, #8f5cff);
  color: #fff;
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  /* Holding a button should not select the label text under the cursor. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: transform .12s ease, box-shadow .2s ease;
}
.voice-talk svg { width: 17px; height: 17px; }
.voice-talk:active { transform: scale(.96); box-shadow: 0 0 0 6px rgba(85, 82, 255, .18); }

.voice-type {
  display: flex;
  gap: 8px;
  width: 100%;
}
.voice-type input {
  flex: 1 1 auto;
  min-width: 0;
  height: 42px;
  padding: 0 14px;
  border-radius: 12px;
  border: 1px solid var(--hairline);
  background: var(--app-bg-mid, #fff);
  color: var(--ink-1);
  font-family: inherit;
  font-size: 14px;
}
html[data-theme='dark'] .voice-type input { background: #1e1f30; }

.voice-picker {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  font-size: 13px;
  color: var(--ink-3);
}
.voice-picker select {
  flex: 1 1 auto;
  min-width: 0;
  height: 38px;
  padding: 0 10px;
  border-radius: 10px;
  border: 1px solid var(--hairline);
  background: var(--app-bg-mid, #fff);
  color: var(--ink-1);
  font-family: inherit;
  font-size: 13px;
}
html[data-theme='dark'] .voice-picker select { background: #1e1f30; }

.icon-btn.is-voice { background: linear-gradient(135deg, #5552ff, #8f5cff); color: #fff; box-shadow: none; }
.icon-btn.is-voice:hover { filter: brightness(1.08); background: linear-gradient(135deg, #5552ff, #8f5cff); }

@media (max-width: 480px) {
  .voice-sheet { padding: 24px 18px 20px; }
  .orb { width: 120px; height: 120px; }
  .voice-stage { height: 155px; }
}
