/* ALY home screen — "Living Orb + Ice Cyan".
   Linked after aly-surface.css. Accents come only from the ice-cyan tokens in
   aly-theme.css (--aly-accent, -soft, -line, --aly-glow, --aly-accent-ink) and
   stay a trace: hairlines, focus/hover glows, the orb, active states. Surfaces
   are --aly-glass (dark glass on NEO). All motion is slow and every animation
   stops under prefers-reduced-motion. */

/* --- The orb stage ---------------------------------------------------------
   Slightly smaller than the sphere it replaces (216 → 192px, phone 200 → 176),
   sized in CSS before any script runs, so nothing shifts when the orb starts. */
.aly-identity { position: relative; }
.aly-identity-card { z-index: 1; }
.aly-identity-mark { width: clamp(140px, 19vw, 192px); height: clamp(140px, 19vw, 192px); }
@media (max-width: 760px) {
  .aly-identity-mark { width: min(46vw, 176px); height: min(46vw, 176px); }
}
/* A very faint radial glow behind the orb only — light, not a surface. */
.aly-identity-card::before {
  width: clamp(220px, 44%, 340px); top: -18px;
  background: radial-gradient(circle, var(--aly-glow) 0%, transparent 62%);
  filter: blur(28px); opacity: 0.5;
}
html[data-alyssiun-atmosphere="lux"] .aly-identity-card::before { opacity: 0.35; }

/* The dust field (aly-particles.js): its colour is this `color`. */
.aly-particles {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%;
  pointer-events: none; color: var(--aly-accent); opacity: 0.55;
  -webkit-mask-image: radial-gradient(ellipse 60% 55% at 50% 38%, #000 35%, transparent 100%);
  mask-image: radial-gradient(ellipse 60% 55% at 50% 38%, #000 35%, transparent 100%);
}
html[data-alyssiun-atmosphere="lux"] .aly-particles { color: var(--aly-accent-ink); opacity: 0.35; }

/* --- Heading and status line ---------------------------------------------- */
.aly-identity-title { margin-top: 16px; letter-spacing: 0.005em; }
.aly-orb-status {
  display: inline-flex; align-items: center; gap: 8px; margin: 14px 0 0;
  font-size: 12px; font-weight: 400; letter-spacing: 0.06em; line-height: 1;
  color: var(--muted);
}
.aly-orb-status-dot {
  position: relative; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--aly-accent);
  animation: aly-status-dot 3.2s ease-in-out infinite;
}
/* The soft ring is its own layer: transform + opacity only, so the pulse runs
   on the compositor and an idle home screen repaints nothing. */
.aly-orb-status-dot::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  background: var(--aly-accent-soft);
  animation: aly-status-ring 3.2s ease-in-out infinite;
}
@keyframes aly-status-dot { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@keyframes aly-status-ring { 0%, 100% { opacity: 0; transform: scale(0.5); } 50% { opacity: 1; transform: scale(1); } }

/* --- Suggestion cards --------------------------------------------------------
   Dark glass with a faint highlight along the top edge. On hover or keyboard
   focus a thin cyan line travels once round the border (--aly-trace), then the
   border settles on the accent hairline and the icon glows a little. */
@property --aly-trace {
  syntax: '<angle>'; inherits: false; initial-value: 0deg;
}
.aly-starter {
  position: relative; isolation: isolate;
  background: var(--aly-glass);
  -webkit-backdrop-filter: var(--aly-glass-blur); backdrop-filter: var(--aly-glass-blur);
  transition: border-color .4s ease, background .3s ease, transform .3s ease;
}
.aly-starter::before {
  content: ""; position: absolute; top: 0; left: 14px; right: 14px; height: 1px; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.14), transparent);
}
html[data-alyssiun-atmosphere="lux"] .aly-starter::before {
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.95), transparent);
}
.aly-starter::after {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none;
  background: conic-gradient(from var(--aly-trace), transparent 0deg, transparent 250deg,
    var(--aly-accent-line) 310deg, var(--aly-accent) 352deg, transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  opacity: 0;
}
.aly-starter:hover, .aly-starter:focus-visible {
  border-color: var(--aly-accent-line); background: var(--aly-glass);
}
.aly-starter:hover::after, .aly-starter:focus-visible::after {
  animation: aly-trace 1.5s cubic-bezier(.45, .05, .35, 1) 1 both;
}
@keyframes aly-trace {
  0% { --aly-trace: 0deg; opacity: 0; }
  12% { opacity: 1; }
  82% { opacity: 1; }
  100% { --aly-trace: 360deg; opacity: 0; }
}
/* Keyboard focus is never left to the animation alone. */
.aly-starter:focus-visible { outline: 2px solid var(--aly-accent-line); outline-offset: 3px; }
.aly-starter-glyph {
  width: 22px; height: 22px; margin-bottom: 16px; border-radius: 0;
  background: none; color: var(--aly-accent-ink);
  transition: filter .4s ease;
}
.aly-starter:hover .aly-starter-glyph, .aly-starter:focus-visible .aly-starter-glyph {
  filter: drop-shadow(0 0 5px var(--aly-glow)) drop-shadow(0 0 1px var(--aly-accent-line));
}

/* --- Composer ---------------------------------------------------------------
   Dark glass; a thin cyan line along the bottom that brightens on focus. */
.aly-composer .dock-composer-actions {
  position: relative;
  background: var(--aly-glass); border-color: var(--hair-2);
  -webkit-backdrop-filter: var(--aly-glass-blur); backdrop-filter: var(--aly-glass-blur);
}
.aly-composer .dock-composer-actions::after {
  content: ""; position: absolute; left: 22px; right: 22px; bottom: -1px; height: 1px; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--aly-accent-line), transparent);
  opacity: 0.7; transition: opacity .4s ease, background .4s ease;
}
.aly-composer .dock-composer-actions:focus-within {
  border-color: var(--hair-2);
  box-shadow: 0 10px 34px -20px var(--aly-glow), 0 18px 40px -30px rgb(0 0 0 / 0.35);
}
.aly-composer .dock-composer-actions:focus-within::after {
  opacity: 1; background: linear-gradient(90deg, transparent, var(--aly-accent), transparent);
}
.aly-composer .dock-composer-actions .dock-input,
.aly-composer .dock-composer-actions .dock-input:focus { background: transparent; }
/* Send: muted while the box is empty, a cyan glow once there is something to send. */
.aly-composer .dock-send.is-active {
  background: var(--aly-accent-soft); color: var(--aly-accent-ink);
  box-shadow: inset 0 0 0 1px var(--aly-accent-line), 0 0 16px -2px var(--aly-glow);
  transition: background .3s ease, color .3s ease, box-shadow .3s ease;
}
.aly-composer:has(.dock-input:placeholder-shown) .dock-send.is-active {
  background: transparent; color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--hair-2);
}
.aly-composer .dock-send:focus-visible { outline: 2px solid var(--aly-accent-line); outline-offset: 2px; opacity: 1; }

/* --- Floating Λ and its app circles (OS launcher, in ALY's own page) --------
   The launcher's own values are OS-wide; ALY only restates them for its own
   document: dark glass, a cyan hairline edge, a cyan mark. */
html[data-alyssiun-atmosphere] .aos-lambda-shell {
  --aos-lambda-accent: var(--aly-accent);
  --aos-lambda-edge: var(--aly-accent-line);
  --aos-lambda-ink: var(--aly-accent-ink);
}
html[data-alyssiun-atmosphere="neo"] .aos-lambda-shell { --aos-lambda-glass: var(--aly-glass); }
/* A modal outranks page furniture, and Λ is page furniture. The OS draws it at
   z-index 2147483450 — above even aly-dialog.css's backdrop — so on a phone it
   sat on a dialog's buttons (Get more storage, Delete files too) and on the
   Settings sheet. While ALY shows a dialog or Settings, Λ steps aside. */
body.aly-dialog-open .aos-lambda-shell,
body.aly-settings-open .aos-lambda-shell { visibility: hidden; }

/* --- The orb during a conversation ------------------------------------------
   Floating, not docked: centred on the header's bottom edge, half over the
   header and half over the chat, 96px on desktop and 72px at ≤900px (where the
   ☰ button shows). It is out of the header's flow, so the header is only as
   tall as the title and ☰ need (60px) on every view, and moving from home into
   a conversation shifts nothing; aly-presence.js glides the orb across.
   Margins centre it, leaving `transform` free for that glide.
   Only the orb's disc takes clicks: the mount is pointer-events:none and its
   ::before disc is the hit area, so a message under the box's transparent
   corners still gets its click. */
.aly-topbar { z-index: 3; }
.aly-sphere-compact.has-sphere {
  --orb-size: 72px;
  position: absolute; left: 50%; top: 100%; z-index: 1; transform: none;
  width: var(--orb-size); height: var(--orb-size);
  margin: calc(var(--orb-size) / -2) 0 0 calc(var(--orb-size) / -2);
  pointer-events: none;
}
.aly-sphere-compact.has-sphere::before {
  content: ""; position: absolute; inset: 8%; border-radius: 50%; pointer-events: auto;
}
.aly-sphere-compact.has-sphere > * { pointer-events: none; }
@media (min-width: 901px) {
  .aly-sphere-compact.has-sphere { --orb-size: 96px; }
}
/* The title and the status give up their room before they reach the orb's
   box: half the header's content width, less half the orb and a 10px gap
   (and, at ≤900px, less the 44px ☰ button and its 12px gap). */
.aly-topbar:has(.aly-sphere-compact.has-sphere) .aly-title { flex: 0 1 auto; max-width: calc(50% - 36px - 10px - 56px); }
.aly-topbar:has(.aly-sphere-compact.has-sphere) .aly-status { margin-left: auto; max-width: calc(50% - 36px - 10px); }
@media (min-width: 901px) {
  .aly-topbar:has(.aly-sphere-compact.has-sphere) .aly-title,
  .aly-topbar:has(.aly-sphere-compact.has-sphere) .aly-status { max-width: calc(50% - 48px - 10px); }
}
/* In the OS panel the header's right padding is wider (its close button), which
   moves the centre right of the content's middle: the status gives up that too. */
.aly-in-panel .aly-topbar:has(.aly-sphere-compact.has-sphere) .aly-status { max-width: calc(50% - 48px - 10px - 28px); }
/* Messages dissolve as they scroll up under the orb instead of cutting hard,
   and the first one starts below the orb's lower half. */
.aly-main:has(.aly-sphere-compact.has-sphere) .aly-transcript {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 48px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 48px);
}
.aly-main:has(.aly-sphere-compact.has-sphere) .aly-messages { padding-top: 52px; }
@media (min-width: 901px) {
  .aly-main:has(.aly-sphere-compact.has-sphere) .aly-messages { padding-top: 64px; }
}

/* Both orb mounts are buttons (the wake reaction). A round cyan focus ring,
   drawn inside the box so the docked orb's ring never crosses the header edge. */
.aly-sphere-mount[role="button"] { cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; border-radius: 50%; }
.aly-sphere-mount[role="button"]:focus-visible { outline: 2px solid var(--aly-accent-line); outline-offset: -2px; border-radius: 50%; }

/* --- Composer scrollbar: only when the text is taller than the box ----------
   aly-composer.js sets .is-overflowing once the text passes max-height. Before
   that there is no scrollbar at all — a fractional line height used to leave a
   1px overflow that showed a grey thumb on an empty box. */
.aly-composer .dock-composer-actions .dock-input { overflow-y: hidden; scrollbar-width: none; }
.aly-composer .dock-composer-actions .dock-input::-webkit-scrollbar { width: 0; height: 0; }
.aly-composer .dock-composer-actions .dock-input.is-overflowing {
  overflow-y: auto; scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--aly-accent) 32%, transparent) rgb(0 0 0 / 0.18);
}
.aly-composer .dock-composer-actions .dock-input.is-overflowing::-webkit-scrollbar { width: 6px; }
.aly-composer .dock-composer-actions .dock-input.is-overflowing::-webkit-scrollbar-track { background: rgb(0 0 0 / 0.18); border-radius: 999px; }
.aly-composer .dock-composer-actions .dock-input.is-overflowing::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--aly-accent) 32%, transparent); border-radius: 999px;
}

/* --- No hard line above the composer ------------------------------------------
   The transcript is focusable (tabindex="0", so the keyboard can scroll it),
   and the shared focus rule drew a 2px --text outline round it. .aly-main clips
   its sides, which left only its bottom edge: a bright full-width line right
   above the composer. Its focus now shows as a thin accent bar down its left
   edge instead — still visible, never a separator. */
.aly-transcript:focus-visible { outline: none; box-shadow: inset 2px 0 0 var(--aly-accent-line); border-radius: 0; }

@media (prefers-reduced-motion: reduce) {
  .aly-orb-status-dot, .aly-orb-status-dot::after, .aly-starter:hover::after, .aly-starter:focus-visible::after { animation: none; }
  .aly-starter, .aly-starter-glyph,
  .aly-composer .dock-composer-actions::after, .aly-composer .dock-send.is-active { transition: none; }
}
