/* ALY shell — web research, composer, signed-out gate and responsive rules.
   Split verbatim from aly.css for the 300-line rule; it is linked immediately
   after aly.css, so the cascade order is unchanged. */

/* --- Web research --------------------------------------------------------- */
/* Quiet by design: what ALY looked at is context for the answer, not a second
   answer competing with it. Both blocks exist only when a web tool really ran. */
.aly-research { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.aly-research li { font-size: 12px; color: var(--muted); background: var(--void-2); border-radius: 999px; padding: 3px 10px; }
.aly-research li.is-failed { color: var(--danger); }
.aly-sources { margin: 4px 0 0; padding: 10px 0 0; border-top: 1px solid var(--hair-2); }
.aly-sources-title { margin: 0 0 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.aly-sources-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.aly-sources-list li { display: flex; align-items: baseline; gap: 8px; font-size: 13.5px; }
.aly-source { color: var(--text); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.aly-source-host { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* --- Composer ------------------------------------------------------------- */
.aly-composer-region {
  /* The composer and the disclaimer under it sit at the bottom of the window:
     only the safe area plus 8px below. The 72px that used to be here kept the
     composer clear of the old OS mini launcher, a fixed centre-bottom circle.
     That circle is gone: the OS's floating Λ now rests just above an app's
     pinned bottom controls on its own (lambda-position.js `avoid`), so the
     reserve was a dead block under the composer, largest on phones. */
  padding: 4px clamp(14px, 4vw, 28px)
           calc(8px + max(env(safe-area-inset-bottom, 0px), var(--alyssiun-safe-bottom, 0px)));
  background: linear-gradient(to top, var(--void) 62%, transparent);
}
/* The outer wrapper only: centred, capped width, a plain column of
   [attachments][the row]. Same role as the Project workspace's
   .dock-composer-body — the actual bordered/filled box is the row itself
   (.dock-composer-actions, aly-workspace-composer.css), shared by both
   composers, so it is not redefined here. */
/* `relative` so the + menu can anchor to the composer itself. */
.aly-composer { position: relative; width: min(760px, 100%); margin: 0 auto; display: flex; flex-direction: column; gap: 6px; }

/* READING SOMEBODY ELSE'S SHARED CHAT.
   The composer stays in place and is disabled, rather than disappearing: a
   missing composer reads as a broken page, while a dimmed one carrying a
   sentence explains itself. The placeholder says whose chat it is and that it
   cannot be written in — see aly-composer.js setReadOnly(). */
.aly-composer.is-read-only { opacity: .62; }
.aly-composer.is-read-only .aly-input { cursor: not-allowed; }
.aly-composer.is-read-only .aly-send,
.aly-composer.is-read-only .aly-composer-actions { pointer-events: none; }

/* .aly-input / .aly-send / .aly-composer-actions carry no rules of their own
   any more — the row and everything in it (.dock-composer-actions,
   .dock-input, .dock-composer-tools, .dock-icon, .dock-send, .dock-settings,
   .dock-model-toggle) is the shared chrome from aly-workspace-composer.css,
   loaded globally via aly-workspace.css's @import. Same shape, same spacing,
   same treatment as the Project workspace composer — one ruleset, not two
   that have to be kept in sync by hand. */

/* Voice control. Pressed state is a real aria-pressed button, and the live
   microphone is announced in text beside it. */
.aly-voice-btn[aria-pressed="true"] {
  color: var(--text); background: var(--void-2); border-color: var(--hair-2);
}
.aly-mic-state {
  display: inline-flex; align-items: center; gap: 7px; margin-left: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aly-mic-state[hidden] { display: none; }
.aly-mic-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--danger); flex: 0 0 auto;
  animation: aly-mic-pulse 1.9s ease-in-out infinite;
}
@keyframes aly-mic-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .aly-mic-dot { animation: none; } }
.aly-attachments { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 7px; }
.aly-chip {
  display: inline-flex; align-items: center; gap: 8px; max-width: 260px;
  border: 1px solid var(--hair-2); border-radius: 6px; background: var(--glass-2);
  padding: 5px 7px 5px 10px; font-size: 12.5px; color: var(--muted);
}
.aly-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aly-chip.is-pending { opacity: 0.6; }
.aly-chip button { border: 0; background: none; color: var(--muted); cursor: pointer; font: inherit; padding: 0 3px; }
.aly-chip button:hover { color: var(--danger); }

/* An attached image shows what it is, not its filename — a small square
   thumbnail rather than a text chip. The remove button rides on top, in the
   corner, so it stays reachable without competing with the picture for space. */
.aly-chip-image {
  position: relative; padding: 0; max-width: none;
  width: 56px; height: 56px; border-radius: 8px; overflow: hidden;
}
.aly-chip-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Fixed black/white here rather than a theme token — this scrim sits on top
   of the person's own photo, whose colours neither atmosphere controls, the
   same reasoning `.preview-frame` uses for staying white in both themes. */
.aly-chip-image .aly-chip-remove {
  position: absolute; top: 2px; right: 2px; padding: 0;
  width: 18px; height: 18px; border-radius: 50%; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.55); color: #fff; font-size: 10px;
}
.aly-chip-image .aly-chip-remove:hover { background: var(--danger); color: #fff; }

/* The line under the box: small and quiet, never competing with it. */
.aly-fineprint {
  width: min(760px, 100%); margin: 6px auto 0; text-align: center;
  font-size: 11px; line-height: 1.3; letter-spacing: 0.01em; opacity: 0.48;
}

/* Errors speak as ALY, never as infrastructure. */
.aly-notice {
  width: min(760px, 100%); margin: 0 auto 10px;
  border: 1px solid var(--hair-2); border-left: 2px solid var(--danger);
  border-radius: 6px; background: var(--glass-2); padding: 11px 14px; font-size: 13.5px;
}
.aly-notice[hidden] { display: none; }
.aly-support-contact { display: inline-flex; margin-top: 12px; }

/* "Out of credits — back in about 4:12." A wait, not a fault: hairline and
   calm, no danger accent, sits directly above the message box. */
.aly-credit-pause {
  width: min(760px, 100%); margin: 0 auto 8px;
  border: 1px solid var(--hair-2); border-radius: 6px;
  background: var(--glass-2); padding: 9px 13px;
  font-size: 13px; color: var(--muted); line-height: 1.45;
}
.aly-credit-pause[hidden] { display: none; }

/* --- Signed-out gate ------------------------------------------------------ */
.aly-gate { height: 100dvh; display: grid; place-items: center; padding: 24px; }
.aly-gate[hidden] { display: none; }
.aly-gate-inner { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 16px; max-width: 460px; }
.aly-gate-mark { width: 180px; margin-bottom: 10px; }
.aly-gate-title { margin: 0; font-size: clamp(44px, 9vw, 68px); font-weight: 200; letter-spacing: 0.16em; text-indent: 0.16em; }
.aly-gate-sub { margin: 0; font-size: 15px; }

/* --- Responsive ----------------------------------------------------------- */
.aly-scrim { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 40; }
.aly-scrim[hidden] { display: none; }

@media (max-width: 900px) {
  .aly-shell { grid-template-columns: minmax(0, 1fr); width: 100%; max-width: 100%; }
  .aly-sidebar {
    position: fixed; inset: 0 auto 0 0; width: min(304px, 86vw); z-index: 50;
    background: var(--void); transform: translateX(-100%);
    transition: transform 0.2s ease;
  }
  .aly-sidebar.is-open { transform: none; }
  .aly-sidebar-open, .aly-sidebar-close { display: inline-block; }
  /* On a phone this is the ONLY way back to conversations, so it has to read as
     a real button, not a faint glyph. A 44px bordered tap target, full-contrast
     icon. */
  .aly-sidebar-open {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0;
    font-size: 24px; line-height: 1;
    color: var(--text);
    border-color: var(--hair-2);
    background: var(--glass);
  }
  .aly-sidebar-open:hover:not(:disabled), .aly-sidebar-open:focus-visible {
    color: var(--text); background: var(--glass-2); border-color: var(--text);
  }
  .aly-identity { min-height: 44vh; min-height: 44dvh; padding-top: 24px; }
  /* ALY has to stay recognisable on a phone. The 88px box was sized for the
     typographic mark she replaced; at that size the sphere reads as a dot. */
  .aly-identity-mark { width: min(52vw, 200px); height: min(52vw, 200px); }
  .aly-identity-mark > span { width: 88px; height: 88px; }
}
@media (prefers-reduced-motion: reduce) { .aly-sidebar { transition: none; } }

/* A notice with one useful action — opening ALY directly when a framed window
   cannot reach the microphone. Built as a DOM node, never as injected markup. */
.aly-notice-action {
  display: inline-block; margin-left: 2px; font-weight: 600;
  color: inherit; text-decoration: underline; text-underline-offset: 2px;
}

/* The capability cards (.aly-starters / .aly-starter) are in aly-surface.css. */

/* Docked in the OS panel: the OS draws only its close button, floated into the
   right end of THIS header, so there is exactly one header row. Room is left for
   it, and the conversation menu stays where it is. */
.aly-in-panel .aly-topbar { padding-right: 56px; }

/* The multi-step plan card and the app suggestion card. */
.aly-plan-steps { margin: 10px 0; padding-left: 20px; display: grid; gap: 8px; }
.aly-plan-step-detail { color: var(--muted); }
.aly-plan-step-status { display: block; font-size: 12px; color: var(--muted); }
.aly-plan-step-status.is-ok { color: var(--jade); }
.aly-plan-step-status.is-failed { color: var(--danger); }
.aly-plan-step-link { font-size: 12px; }
.aly-agent-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; align-items: center; }
.aly-choice-option { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.aly-choice-last { font-size: 0.7em; opacity: 0.7; }
.aly-choice-input { flex: 1 1 180px; min-width: 0; font: inherit; }
.aly-plan-step-cost { display: block; font-size: 12px; color: var(--muted); }
.aly-plan-step-blocked { display: block; font-size: 12px; color: var(--danger); }
/* What a third-party app step will do to the person's data. Uses the shared
   Alyssiun semantic tokens; a delete reads in the danger colour because that
   is the one a person must not skim past. */
.aly-plan-step-risk { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 4px;
  font-size: 11px; color: var(--muted); border: 1px solid var(--hair-2); }
.aly-plan-step-risk.danger { color: var(--danger); border-color: var(--danger); }
.aly-agent-permissions { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; }
.aly-permission { display: flex; gap: 8px; align-items: baseline; font-size: 12px; }
.aly-permission-label { flex: 0 0 auto; padding: 1px 6px; border-radius: 4px; color: var(--muted);
  border: 1px solid var(--hair-2); }
.aly-permission.warn .aly-permission-label { color: var(--text); }
.aly-permission.danger .aly-permission-label { color: var(--danger); border-color: var(--danger); }
.aly-permission-copy { color: var(--muted); }
.aly-builder-card { margin-top: 8px; }
.aly-builder-bar { position: relative; height: 4px; margin: 6px 0 10px; border-radius: 2px; background: var(--hair-2); overflow: hidden; }
.aly-builder-bar > span { position: absolute; inset: 0 auto 0 0; width: 40%; border-radius: 2px; background: var(--jade); }
.aly-builder-bar.is-active > span { animation: aly-builder-slide 1.4s ease-in-out infinite; }
@keyframes aly-builder-slide { from { transform: translateX(-100%); } to { transform: translateX(260%); } }
.aly-builder-check.is-ok { color: var(--jade); }
.aly-builder-check.is-failed { color: var(--danger); }
.aly-builder-link { font-size: 13px; }
.aly-builder-publish { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--hair-2); }

/* The "+" menu (aly-plus-menu.js): attach something, or ask for a picture.
   Anchored above the composer's tool row rather than below it — the composer
   sits at the bottom of the viewport, so a menu opening downward would be off
   screen on every phone. */
.aly-plus-menu {
  position: absolute;
  right: 8px;
  bottom: calc(100% + 8px);
  z-index: 40;
  min-width: 190px;
  padding: 5px;
  border: 1px solid var(--hair-2);
  border-radius: 10px;
  background: var(--surface-1, var(--glass-2));
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.18);
}

.aly-plus-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.aly-plus-item:hover { background: var(--glass-2); }

.aly-plus-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  flex: 0 0 auto;
}
