/* Waypoint — warm, dim, late-afternoon. Dark by default; the light theme is the same
   palette with the values inverted rather than a different set of colours, so the app
   reads as one place in either. */

:root {
  /* Late-afternoon light: nothing is pure white or pure black, and every neutral has
     some amber in it. A clinical #fff or #000 anywhere breaks the whole feeling. */
  --bg:          #15100d;
  --bg-raised:   #1e1814;
  --bg-sunk:     #100c0a;
  --ink:         #f2e8dc;
  --ink-soft:    #cbb9a8;
  --ink-dim:     #97877a;

  --amber:       #e6a23c;
  --amber-deep:  #b9762a;
  --gold:        #d9b371;
  --rose:        #c78d88;

  --line:        rgba(230, 162, 60, 0.13);
  --line-soft:   rgba(230, 162, 60, 0.07);

  /* The orb's colour lives here so the naming moment is a change to two custom
     properties, transitioned slowly, rather than a class swap. */
  --orb-core:    #b9bcc4;
  --orb-edge:    #6f747e;
  --orb-glow:    rgba(150, 156, 168, 0.30);

  --panel-w:     19rem;
  --ease:        cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Settled: after the entity has been named. */
:root.orb-settled {
  --orb-core:    #f0b856;
  --orb-edge:    #a8611f;
  --orb-glow:    rgba(230, 162, 60, 0.34);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg:        #f6efe4;
    --bg-raised: #fdf8ef;
    --bg-sunk:   #ece2d3;
    --ink:       #2b211a;
    --ink-soft:  #5d4d40;
    --ink-dim:   #8a7767;
    --line:      rgba(120, 78, 26, 0.16);
    --line-soft: rgba(120, 78, 26, 0.08);
  }
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background:
    /* A low, warm wash from the upper left — the light source, not a decoration. */
    radial-gradient(1200px 800px at 12% -10%, rgba(230, 162, 60, 0.10), transparent 60%),
    radial-gradient(900px 700px at 100% 110%, rgba(199, 141, 136, 0.07), transparent 62%),
    var(--bg);
  color: var(--ink);
  font: 400 16px/1.62 ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

button { font: inherit; color: inherit; cursor: pointer; }
:focus-visible { outline: 1px solid var(--amber); outline-offset: 2px; }

/* ── The orb ─────────────────────────────────────────────────────────────────
   Small on purpose. It sits beside the entity's name and is meant to be noticed
   only when you look at it. Three states, distinguished by the speed and kind of
   movement rather than by changing shape or colour. */

.orb {
  position: relative;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  /* 6s so the naming moment reads as the entity settling, not as a UI state flip. */
  transition: box-shadow 6s var(--ease);
  box-shadow: 0 0 14px 1px var(--orb-glow);
}

.orb::before,
.orb::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  transition: background 6s var(--ease);
}

/* The body of the orb: an off-centre highlight so rotation is legible at 22px. */
.orb::before {
  background:
    radial-gradient(circle at 34% 30%, var(--orb-core), transparent 58%),
    radial-gradient(circle at 68% 74%, var(--orb-edge), transparent 62%),
    var(--orb-edge);
  animation: orb-rotate 42s linear infinite;
}

/* A second, slower layer, counter-rotating — this is what makes it look like it has
   an inside rather than being a spinning texture. */
.orb::after {
  background: radial-gradient(circle at 62% 40%, var(--orb-glow), transparent 70%);
  animation: orb-rotate 27s linear infinite reverse;
  mix-blend-mode: screen;
}

/* Listening — slow drift, gentle rotation. */
.orb[data-state="listening"] { animation: orb-drift 15s ease-in-out infinite; }

/* Processing — the rotation quickens. Subtly: this is the only cue that it is working,
   and a fast spin would turn a calm presence into a loading spinner. */
.orb[data-state="processing"] { animation: orb-drift 15s ease-in-out infinite; }
.orb[data-state="processing"]::before { animation-duration: 7s; }
.orb[data-state="processing"]::after  { animation-duration: 5s; }

/* Speaking — a soft pulse. */
.orb[data-state="speaking"] { animation: orb-pulse 2.1s ease-in-out infinite; }

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

@keyframes orb-drift {
  0%, 100% { transform: translate(0, 0); }
  25%      { transform: translate(0.9px, -1.1px); }
  50%      { transform: translate(-0.7px, 0.8px); }
  75%      { transform: translate(-1px, -0.5px); }
}

@keyframes orb-pulse {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 14px 1px var(--orb-glow); }
  50%      { transform: scale(1.07); box-shadow: 0 0 22px 3px var(--orb-glow); }
}

@media (prefers-reduced-motion: reduce) {
  .orb, .orb::before, .orb::after { animation: none !important; }
}

/* ── Shell ───────────────────────────────────────────────────────────────────── */

.app {
  display: none;
  height: 100%;
  flex-direction: column;
}
.app.is-visible { display: flex; }

.topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 1.25rem;
  border-bottom: 1px solid var(--line-soft);
}

/* Both the orb and the name are small and ambient — present, not intrusive. */
.entity-name {
  font-size: 0.9rem;
  letter-spacing: 0.055em;
  text-transform: lowercase;
  color: var(--ink-soft);
}

.topbar-spacer { flex: 1 1 auto; }

.icon-btn {
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-dim);
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 0.36rem 0.8rem;
  transition: color 0.25s, border-color 0.25s;
}
.icon-btn:hover { color: var(--ink); border-color: var(--amber); }

.body {
  flex: 1 1 auto;
  display: flex;
  min-height: 0;
}

/* ── Conversation ────────────────────────────────────────────────────────────── */

.conversation {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.thread {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 2rem 1.5rem 1rem;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

/* Full width, centred. The measure is held short because this is reading, not scanning. */
.thread-inner {
  max-width: 44rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.msg {
  max-width: 82%;
  padding: 0.78rem 1.05rem;
  font-size: 1.02rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  animation: msg-in 0.5s var(--ease) both;
}

/* The notch is the whole distinction: entity messages are cut in at the bottom left,
   the user's at the bottom right. Clear at a glance, but 5px rather than a tail or a
   pointer — nothing that turns the conversation into a chat app. */
.msg.entity {
  align-self: flex-start;
  border-radius: 16px 16px 16px 5px;
  background: linear-gradient(170deg, var(--bg-raised), var(--bg-sunk));
  border: 1px solid var(--line-soft);
  color: var(--ink);
}

.msg.you {
  align-self: flex-end;
  border-radius: 16px 16px 5px 16px;
  background: linear-gradient(170deg, rgba(230, 162, 60, 0.13), rgba(199, 141, 136, 0.09));
  border: 1px solid var(--line);
  color: var(--ink);
}

@keyframes msg-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Three drifting dots while the entity is composing. */
.thinking {
  align-self: flex-start;
  display: flex;
  gap: 0.3rem;
  padding: 0.9rem 1.1rem;
}
.thinking span {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--amber);
  opacity: 0.4;
  animation: dot 1.5s ease-in-out infinite;
}
.thinking span:nth-child(2) { animation-delay: 0.22s; }
.thinking span:nth-child(3) { animation-delay: 0.44s; }
@keyframes dot {
  0%, 100% { opacity: 0.24; transform: translateY(0); }
  50%      { opacity: 0.85; transform: translateY(-3px); }
}

/* ── Input ───────────────────────────────────────────────────────────────────── */

.composer {
  flex: 0 0 auto;
  padding: 0.9rem 1.5rem 1.4rem;
}

.composer-inner {
  max-width: 44rem;
  margin: 0 auto;
  display: flex;
  align-items: flex-end;
  gap: 0.7rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 0.55rem 0.6rem 0.55rem 1.05rem;
  transition: border-color 0.3s;
}
.composer-inner:focus-within { border-color: rgba(230, 162, 60, 0.4); }

.composer textarea {
  flex: 1 1 auto;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 1.02rem;
  line-height: 1.55;
  resize: none;
  max-height: 9rem;
  padding: 0.28rem 0;
  outline: none;
}
.composer textarea::placeholder { color: var(--ink-dim); font-style: italic; }

.send {
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  width: 2.1rem; height: 2.1rem;
  background: linear-gradient(160deg, var(--amber), var(--amber-deep));
  color: #21160b;
  font-size: 1rem;
  line-height: 1;
  transition: opacity 0.25s, transform 0.2s;
}
.send:disabled { opacity: 0.32; cursor: default; }
.send:not(:disabled):hover { transform: translateY(-1px); }

/* ── Side panel ──────────────────────────────────────────────────────────────
   Right by default, draggable to the left. Slides away when the user commits to the
   conversation and comes back when they stop. */

.panel {
  flex: 0 0 var(--panel-w);
  width: var(--panel-w);
  display: flex;
  flex-direction: column;
  border-left: 1px solid var(--line-soft);
  background: linear-gradient(200deg, var(--bg-sunk), var(--bg));
  transition: margin 0.55s var(--ease), opacity 0.4s var(--ease);
}

.panel.dock-left {
  order: -1;
  border-left: 0;
  border-right: 1px solid var(--line-soft);
}

/* Hidden by sliding out through its own edge, so the conversation reflows into the
   space rather than the panel covering it. */
.panel.is-hidden { margin-right: calc(-1 * var(--panel-w)); opacity: 0; pointer-events: none; }
.panel.dock-left.is-hidden { margin-right: 0; margin-left: calc(-1 * var(--panel-w)); }

.panel-grip {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 1.1rem 0.5rem;
  color: var(--ink-dim);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: grab;
  user-select: none;
}
.panel-grip:active { cursor: grabbing; }
.panel-grip::before {
  content: "";
  width: 12px; height: 2px;
  border-top: 4px double var(--line);
}

.panel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0.5rem 1.1rem;
  display: flex;
  flex-direction: column;
}

.panel-section { padding: 0.9rem 0; }

.panel-label {
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 0.55rem;
}

.panel-note {
  font-size: 0.86rem;
  color: var(--ink-dim);
  font-style: italic;
  line-height: 1.5;
}

/* The nudge sits at the bottom of the panel, below whatever fills it in Layer 3. */
.panel-spacer { flex: 1 1 auto; min-height: 1.5rem; }

.nudge {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 0.9rem 1rem;
  margin-bottom: 1.1rem;
  background: linear-gradient(165deg, rgba(230, 162, 60, 0.09), rgba(199, 141, 136, 0.05));
  font-size: 0.93rem;
  color: var(--ink-soft);
  line-height: 1.5;
}

/* Dragging feedback: the side the panel would land on lights up. */
.drop-hint {
  position: fixed;
  top: 0; bottom: 0;
  width: var(--panel-w);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s;
  z-index: 40;
}
.drop-hint.left  { left: 0;  background: linear-gradient(90deg, rgba(230, 162, 60, 0.13), transparent); }
.drop-hint.right { right: 0; background: linear-gradient(270deg, rgba(230, 162, 60, 0.13), transparent); }
.drop-hint.is-on { opacity: 1; }

/* ── Auth ────────────────────────────────────────────────────────────────────── */

.gate {
  display: none;
  height: 100%;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}
.gate.is-visible { display: flex; }

.gate-card {
  width: 100%;
  max-width: 24rem;
  background: linear-gradient(175deg, var(--bg-raised), var(--bg-sunk));
  border: 1px solid var(--line-soft);
  border-radius: 20px;
  padding: 2.1rem 1.9rem;
}

.gate-mark {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin-bottom: 1.6rem;
}
.gate-mark h1 {
  margin: 0;
  font-size: 1.12rem;
  font-weight: 400;
  letter-spacing: 0.13em;
  text-transform: lowercase;
  color: var(--ink-soft);
}

.gate-lede {
  font-size: 0.95rem;
  color: var(--ink-dim);
  line-height: 1.6;
  margin: 0 0 1.5rem;
}

.field { margin-bottom: 0.95rem; }
.field label {
  display: block;
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-dim);
  margin-bottom: 0.35rem;
}
.field input {
  width: 100%;
  background: var(--bg-sunk);
  border: 1px solid var(--line-soft);
  border-radius: 11px;
  color: var(--ink);
  font: inherit;
  font-size: 0.98rem;
  padding: 0.6rem 0.8rem;
  outline: none;
  transition: border-color 0.25s;
}
.field input:focus { border-color: rgba(230, 162, 60, 0.42); }

.primary {
  width: 100%;
  border: 0;
  border-radius: 11px;
  padding: 0.72rem;
  margin-top: 0.4rem;
  background: linear-gradient(160deg, var(--amber), var(--amber-deep));
  color: #21160b;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
  transition: opacity 0.25s, transform 0.2s;
}
.primary:disabled { opacity: 0.4; cursor: default; }
.primary:not(:disabled):hover { transform: translateY(-1px); }

.link-btn {
  display: block;
  width: 100%;
  margin-top: 1.05rem;
  background: none;
  border: 0;
  color: var(--ink-dim);
  font-size: 0.86rem;
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
}
.link-btn:hover { color: var(--ink-soft); }

.notice {
  font-size: 0.88rem;
  line-height: 1.5;
  border-radius: 11px;
  padding: 0.65rem 0.8rem;
  margin-bottom: 1rem;
}
.notice.error { color: #f0b6ab; background: rgba(199, 92, 76, 0.13); border: 1px solid rgba(199, 92, 76, 0.28); }
.notice.calm  { color: var(--ink-soft); background: rgba(230, 162, 60, 0.09); border: 1px solid var(--line); }
.notice[hidden] { display: none; }

.recovery-code {
  display: block;
  margin: 0.55rem 0 0;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 1.02rem;
  letter-spacing: 0.09em;
  color: var(--gold);
}

/* ── Small screens ───────────────────────────────────────────────────────────
   The panel becomes a bottom drawer. Pull-up only: it never opens on its own, and
   nothing in the conversation moves it. */

@media (max-width: 46rem) {
  .thread { padding: 1.4rem 1rem 0.6rem; }
  .composer { padding: 0.7rem 1rem 1rem; }
  .msg { max-width: 90%; }

  .panel,
  .panel.dock-left {
    position: fixed;
    left: 0; right: 0;
    bottom: 0;
    top: auto;
    order: 0;
    width: auto;
    height: 68vh;
    max-height: 68vh;
    border: 1px solid var(--line-soft);
    border-radius: 20px 20px 0 0;
    /* Parked with only the grip showing. */
    transform: translateY(calc(100% - 2.9rem));
    transition: transform 0.45s var(--ease);
    margin: 0 !important;
    opacity: 1 !important;
    pointer-events: auto;
    z-index: 30;
    box-shadow: 0 -18px 40px rgba(0, 0, 0, 0.35);
  }

  .panel.drawer-open { transform: translateY(0); }

  /* is-hidden is the desktop auto-hide state and must not reach the drawer. */
  .panel.is-hidden { transform: translateY(calc(100% - 2.9rem)); }

  .panel-grip {
    padding: 0.85rem 1.1rem;
    justify-content: center;
    touch-action: none;
  }
  .drop-hint { display: none; }
}
