/* The global layer: reset, the page frame, and the boot splash.

   Deliberately small. Anything that names a component belongs in that
   component's .razor.css; anything the whole product shares is a token in
   variables/. There is no third place.

   The React build had no such rule, which is how styles.css reached 3,702
   lines carrying 1,106 distinct hex colours the linter never saw. */

:root {
  /* Missing from the token set: a hairline width. Everything else on this
     page is a token. Figma has no border-width scale yet. */
  --ds-hairline: 1px;
  /* Missing from the token set: motion. Figma has no duration or easing
     scale yet, so the product's one scale lives here. Every transition and
     entrance picks from it, so screens move at the same speeds.
       fast  hover, press, colour changes on a control
       base  a panel or field changing state, a dialog arriving or leaving
       slow  a screen-sized change: content arriving after a hand-off
     Long decorative loops (the sign-in story, illustrations) set their own
     cycle lengths; those are content timing, not interaction speed. */
  --ds-motion-fast: 120ms;
  --ds-motion-base: 180ms;
  --ds-motion-slow: 300ms;
  --ds-motion-ease: cubic-bezier(0.2, 0, 0, 1);
  --ds-motion-enter: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ds-motion-exit: cubic-bezier(0.4, 0, 1, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: var(--spacing-none);
  padding: var(--spacing-none);
}

body {
  min-height: 100%;
  font-family: var(--typography-font-family-base), system-ui, sans-serif;
  font-size: var(--typography-size-text-sm);
  /* Medium is the base weight in this design system, not Regular. Text styles
     set their own weight; this is what everything else inherits. */
  font-weight: var(--typography-font-weight-medium);
  line-height: var(--typography-leading-leading-sm);
  color: var(--colors-text-primary);
  background: var(--colors-surface-background);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* Aurora is a desktop application. The React build set the same floor, then
   paired it with overflow:hidden, which meant a 1024px laptop silently clipped
   UI with no way to reach it. Scrolling horizontally is the lesser evil. */
html,
body {
  min-width: var(--ds-viewport-floor);
}

/* Except the sign-in pages. They are the public way in, often opened on a
   small laptop or a phone from a shared link, and lay themselves out to any
   width. The floor protects the app shell, which these pages do not use;
   nor does creating an agent (a full-screen flow of its own). Nor the Inbox,
   which lays itself out for tablets (one pane at a time below 1024px). Nor
   any page while "Tell us about your business" is open: on a tablet or a
   phone the floor shrank the page, and the dialog with it, to a third of
   its size; it lays itself out at the screen's width. */
html:has(.account-access-page, .account-panel-page, .legal-page, .agent-create, .inbox-workspace, .business-intro-host .dialog-backdrop),
html:has(.account-access-page, .account-panel-page, .legal-page, .agent-create, .inbox-workspace, .business-intro-host .dialog-backdrop) body {
  min-width: 0;
}

:root {
  /* Missing from the token set: layout breakpoints. Added to variables/ in a
     later phase; until then the floor lives here rather than in eight files.
     768px, a tablet in portrait (designer, 14 October 2026): at 1080px every
     tablet saw the page cut off with a blank band; under 768px (phones,
     not designed yet) the browser still shows the tablet layout, zoomed out. */
  --ds-viewport-floor: 768px;
}

body {
  overflow-x: auto;
  overflow-y: hidden;
}

/* The browser puts a 1em margin on every heading and paragraph. Those are not
   design decisions, and left in place they add a gap to every stack in the
   product that no spacing token accounts for. */
h1, h2, h3, h4, h5, h6, p, figure, blockquote {
  margin: var(--spacing-none);
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* A focus ring is a feature, not decoration. Never remove it without
   replacing it — every control in this product is reachable by keyboard.

   It is drawn quietly on purpose. :focus-visible fires only for keyboard
   navigation, never on a mouse click, so this is seen exclusively by someone
   who is tabbing and needs to know where they are. It hugs the control at a
   1px offset and sits at partial strength, which reads as the control being
   picked out rather than as a box drawn around it. */
:focus-visible {
  outline: var(--ds-focus-ring-width) solid
    color-mix(in srgb, var(--colors-interaction-focus) var(--ds-focus-ring-strength), transparent);
  outline-offset: var(--ds-focus-ring-offset);
}

:root {
  /* Missing from the token set: focus ring geometry. Figma specifies the
     colour but not the width, offset or strength. */
  --ds-focus-ring-width: 2px;
  --ds-focus-ring-offset: 1px;
  --ds-focus-ring-strength: 45%;
}

/* The one place a ring is wrong. App.razor runs <FocusOnNavigate Selector="h1">
   so that arriving on a page announces its title rather than stranding a screen
   reader at the top of the DOM. Blazor does that by making the heading focusable
   just long enough to focus it, then removing the attribute again — so the
   heading ends up focused but unreachable by Tab, and the ring marks a target
   nobody can land on. It reads as a stray box drawn around the page title on
   every load. Keep the behaviour, drop the ring. */
h1:focus,
h1:focus-visible {
  outline: none;
}

/* Links carry the design system's link colour. Without this the browser's own
   #0000EE wins, which is not a colour this product owns — and because a link
   inside prose is rare here, it goes unnoticed until it appears next to
   something branded. Set once globally so a new link cannot get it wrong. */
a {
  color: var(--colors-text-link);
  text-underline-offset: var(--ds-link-underline-offset);
}

a:hover { color: var(--colors-text-link-hover); }

:root {
  /* Missing from the token set: underline offset. */
  --ds-link-underline-offset: 2px;
}

/* The setup card. Four screens in the channel flow are each the whole page in
   turn — the guided shell, connecting, the message test and the success
   screen — and a card that changes size between them reads as changing
   product halfway through. */
:root {
  /* Missing from the token set: no layout-width, border-width, shadow or
     page-inset scale describes a full-page card. Declared here rather than in
     each component because scoped stylesheets cannot share a custom property,
     and four separate copies is how the four drifted apart in the first
     place. */
  --ds-setup-card-width: 790px;
  --ds-setup-card-hairline: 1px;
  --ds-setup-card-shadow: 45px;
  --ds-setup-card-pad: 43px 62px 36px;
}

/* Visually hidden, still announced. */
.sr-only {
  position: absolute;
  width: var(--ds-sr-only-size);
  height: var(--ds-sr-only-size);
  padding: var(--spacing-none);
  margin: calc(var(--ds-hairline) * -1);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

:root {
  /* Missing from the token set: the 1px box a screen-reader-only element is
     clipped to. Not a design value — it is the standard technique. */
  --ds-sr-only-size: 1px;
}

/* ── Boot ───────────────────────────────────────────────────────────────
   WebAssembly downloads the runtime before anything renders. React booted
   into a blank page; this is a deliberate holding state instead. */
.app-boot {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  color: var(--colors-text-muted);
}

.app-boot__mark {
  width: var(--icon-size-icon-lg);
  height: var(--icon-size-icon-lg);
  border: var(--ds-boot-ring-width) solid var(--colors-surface-border);
  border-top-color: var(--colors-brand-primary);
  border-radius: var(--radius-radius-full);
  animation: app-boot-spin var(--ds-boot-spin-duration) linear infinite;
}

:root {
  /* Missing from the token set: the spinner's ring width and revolution time.
     Figma has no motion scale. */
  --ds-boot-ring-width: 2px;
  --ds-boot-spin-duration: 900ms;
}

.app-boot__label {
  font-size: var(--typography-size-text-xs);
}

@keyframes app-boot-spin {
  to {
    transform: rotate(1turn);
  }
}

/* One block, not nineteen. The React build repeated this query 19 times. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Blazor's own error bar. Tokenised so it cannot look foreign. */
#blazor-error-ui {
  position: fixed;
  right: var(--spacing-none);
  bottom: var(--spacing-none);
  left: var(--spacing-none);
  z-index: 1000;
  display: none;
  padding: var(--spacing-md) var(--spacing-lg);
  color: var(--colors-text-primary);
  background: var(--colors-surface-elevated);
  box-shadow: 0 calc(var(--ds-hairline) * -1) 0 var(--colors-surface-border);
}

#blazor-error-ui .dismiss {
  position: absolute;
  top: var(--spacing-sm);
  right: var(--spacing-md);
  cursor: pointer;
}

/* Google's sign-in button, light and dark. These are Google's published button
   colours, allowlisted in brand-literals.json, and they follow the theme,
   which only this document-wide sheet can see. */
.google-signin-wrap {
  /* Not a token: Google's light fill and label. The outline is ours. */
  --ds-google-fill: #ffffff;
  --ds-google-label: #1f1f1f;
  /* Not a token: the 8% state layer Google's guidelines use for hover. */
  --ds-google-hover-mix: 8%;
}

:root[data-theme="aurora-dark"] .google-signin-wrap {
  /* Not a token: Google's dark fill and label. */
  --ds-google-fill: #131314;
  --ds-google-label: #e3e3e3;
}

/* Facebook's sign-in button. Meta's button is the same in light and dark: Facebook blue (allowlisted in
   brand-literals.json) with a white label and mark. */
.facebook-signin-wrap {
  /* Not a token: Meta's Facebook blue and its white label. */
  --ds-facebook-fill: #1877f2;
  --ds-facebook-label: #ffffff;
  /* Not a token: hover darkens the same blue a little, as Meta's own button does. */
  --ds-facebook-hover-brightness: 0.92;
}

/* Disabled buttons in dark mode. The disabled colours are already muted, and
   halving their opacity on top of that left a dark-on-dark label that was
   almost invisible (the Create Workspace dialog showed it worst). In dark the
   colours carry the disabled look on their own. Here rather than in
   Button.razor.css because :root is out of reach of a scoped stylesheet. */
:root[data-theme="aurora-dark"] .ds-button {
  /* Not a token: full opacity, i.e. no extra dimming. */
  --ds-button-disabled-opacity: 100;
}

/* The disabled fill is the same grey as a dialog or popover in dark mode, so
   a disabled button on one vanished into it. The border step sits one shade
   lighter than every dark surface the product uses. */
:root[data-theme="aurora-dark"] .ds-button:disabled {
  background: var(--colors-surface-border);
}

/* The signed-out story beat, shared by the Sign In and Sign Up marketing story
   and their animated glow. The other signed-out pages have no gradient. */
.account-access-page {
  /* Missing from the token set: long-form presentation timing. The 8-second
     hold gives each value statement enough reading time; the four share one cycle. */
  --ds-account-story-duration: 32s;
  --ds-account-story-delay: 8s;
}

/* The Sign In / Sign Up glow in dark mode. Here rather than in the glow's own
   stylesheet because the theme lives on :root, which a scoped stylesheet
   cannot reach. Accent colour blooms against near-black, so the subtle token
   is mixed right down: each hue's strongest point sits 0.012 (OKLab) from the
   page, the same as in light mode, and the far corner is 40% of that. */
:root[data-theme="aurora-dark"] .edge-cyan {
  --page-edge-glow: color-mix(in srgb, var(--colors-accent-cyan-subtle) 4%, transparent);
  --page-edge-far: color-mix(in srgb, var(--colors-accent-cyan-subtle) 1.6%, transparent);
}

:root[data-theme="aurora-dark"] .edge-violet {
  --page-edge-glow: color-mix(in srgb, var(--colors-accent-violet-subtle) 3.4%, transparent);
  --page-edge-far: color-mix(in srgb, var(--colors-accent-violet-subtle) 1.4%, transparent);
}

:root[data-theme="aurora-dark"] .edge-amber {
  --page-edge-glow: color-mix(in srgb, var(--colors-accent-amber-subtle) 3.7%, transparent);
  --page-edge-far: color-mix(in srgb, var(--colors-accent-amber-subtle) 1.5%, transparent);
}

:root[data-theme="aurora-dark"] .edge-pink {
  --page-edge-glow: color-mix(in srgb, var(--colors-accent-pink-subtle) 3.8%, transparent);
  --page-edge-far: color-mix(in srgb, var(--colors-accent-pink-subtle) 1.5%, transparent);
}

/* The welcome card's colour wash sits outside the form (in the dialog's
   ambient layer), so its component stylesheet cannot see a focused field.
   Pause it here while the person is in one, as the channel marks do. */
.dialog:has(.workspace-dialog :focus) .workspace-dialog-gradient__wash {
  animation-play-state: paused;
}

/* The logo tile in the workspace dialog. In dark mode surface-muted is the
   dialog colour itself, so the tile takes the text field fill instead. */
:root[data-theme="aurora-dark"] .workspace-logo-field {
  --ds-workspace-upload-fill: var(--colors-surface-card);
  --ds-workspace-upload-border: color-mix(in srgb, var(--colors-text-muted) 72%, var(--colors-surface-card));
}

/* Dark accent primitives are intentionally richer than their light-mode
   counterparts. The onboarding illustration is ambient rather than a status
   surface, so it uses restrained mixes of those same semantic colours. */
:root[data-theme="aurora-dark"] .workspace-dialog-gradient {
  --ds-workspace-gradient-brand: color-mix(in srgb, var(--colors-accent-brand-subtle) 44%, transparent);
  --ds-workspace-gradient-cyan: color-mix(in srgb, var(--colors-accent-cyan-subtle) 34%, transparent);
  --ds-workspace-gradient-cyan-wash: color-mix(in srgb, var(--colors-accent-cyan-muted) 28%, transparent);
  --ds-workspace-gradient-violet-wash: color-mix(in srgb, var(--colors-accent-violet-muted) 24%, transparent);
  --ds-workspace-gradient-pink-wash: color-mix(in srgb, var(--colors-accent-pink-muted) 22%, transparent);
  --ds-workspace-gradient-wash-peak: 0.42;
}

:root[data-theme="aurora-dark"] .workspace-preview-glow {
  opacity: 0.58;
}
