/* ==========================================================================
   Chat with Connor — connorlapping.io
   Added 2026-09-28.

   Design rules followed from clp-theme.css:
   - every colour is a custom property, so this works in both themes with no
     light-mode overrides. Shadows are `none` in dark, real in light.
   - radius scale: 13px square controls, 12px chips, 16px panels/cards.
   - motion: .2s for hover, .3s/.34s for open/close, all on var(--ease).
   - env(safe-area-inset-*) on every fixed element.
   - no gradients.
   z-index 68/69 is the only band above page chrome (topbar 60, rail 65) yet
   below the drawer scrim (70), so the mobile drawer correctly covers the widget.
   ========================================================================== */

.clp-chat-fab {
  position: fixed;
  right: 18px;
  /* clears .clp-top, which is 46px tall at bottom:18px */
  bottom: calc(env(safe-area-inset-bottom, 0px) + 76px);
  z-index: 68;

  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--panel);
  color: var(--text);
  cursor: pointer;

  display: flex;
  align-items: center;
  justify-content: center;

  transition: background .2s var(--ease), border-color .2s var(--ease),
              transform .2s var(--ease);
}

.clp-chat-fab:hover {
  background: var(--panel-2);
  border-color: var(--border-2);
}

.clp-chat-fab:active { transform: scale(.96); }

.clp-chat-fab[hidden] { display: none; }

.clp-chat-fab__icon {
  font-size: 16px;
  line-height: 1;
  pointer-events: none;
}

.clp-chat-fab__dot {
  position: absolute;
  top: 9px;
  right: 9px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--avail);
  pointer-events: none;
}

.clp-chat-fab[aria-expanded="true"] { display: none; }

/* --- the panel ---------------------------------------------------------- */

.clp-chat {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  overflow: hidden;
  color: var(--text);
  /* The panel owns its own scroll context; the page behind it must not. */
  overscroll-behavior: contain;
}

/* iOS Safari: 100vh is the viewport with its toolbar HIDDEN, so a panel sized
   from it overflows as soon as the toolbar reappears. 100dvh tracks the
   actually-visible height. 100vh is kept as a fallback for older engines. */
.clp-chat--pop {
  position: fixed;
  right: 18px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 76px);
  z-index: 69;
  width: min(380px, calc(100vw - 36px));
  height: min(560px, calc(100vh - 140px));
  height: min(560px, calc(100dvh - 140px));
  opacity: 0;
  transform: translateY(10px) scale(.985);
  visibility: hidden;
  transition: opacity .3s var(--ease), transform .3s var(--ease), visibility .3s;
}

.clp-chat--pop.is-open {
  opacity: 1;
  transform: none;
  visibility: visible;
}

.clp-chat--inline {
  /* sits in the page flow on the home page, inside .clp-chat-shell */
  width: 100%;
  height: 560px;
  max-height: 70vh;
  max-height: 70dvh;
}

.clp-chat__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  flex: 0 0 auto;
}

.clp-chat__av {
  position: relative;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--panel);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Space Grotesk", sans-serif;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -.02em;
  color: var(--text);
}

.clp-chat__av::after {
  content: "";
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--avail);
  border: 2px solid var(--panel-2);
}

.clp-chat__id { flex: 1 1 auto; min-width: 0; }

.clp-chat__name {
  font-family: "Space Grotesk", sans-serif;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -.02em;
  line-height: 1.2;
}

.clp-chat__sub {
  font-size: 12px;
  color: var(--dim);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.clp-chat__act {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  transition: background .2s var(--ease), color .2s var(--ease);
}

.clp-chat__act:hover {
  background: var(--panel);
  color: var(--text);
  border-color: var(--border);
}

/* --- transcript --------------------------------------------------------- */

.clp-chat__log {
  /* flex:1 + min-height:0 is what actually lets a flex child scroll. Without
     min-height:0 the log refuses to shrink below its content height and the
     panel grows forever instead of scrolling — the classic flexbox overflow bug.
     Scroll anchoring keeps the viewport still while tokens stream in, so the
     page does not visibly jump on every delta. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  -webkit-overflow-scrolling: touch;
  overflow-anchor: auto;
}

/* Bottom fade so scrolled content dissolves rather than being guillotined by the
   composer. Purely decorative, so it never intercepts taps. */
.clp-chat__logwrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.clp-chat__logwrap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 24px;
  pointer-events: none;
  background: linear-gradient(to top, var(--panel), transparent);
  opacity: .9;
}

.clp-chat__msg {
  display: flex;
  flex-direction: column;
  gap: 5px;
  max-width: 84%;
  /* Never let a streamed word or a pasted URL force the panel sideways. */
  min-width: 0;
}

.clp-chat__msg--user { align-self: flex-end; align-items: flex-end; }
.clp-chat__msg--ai   { align-self: flex-start; }

.clp-chat__bubble {
  padding: 10px 13px;
  border-radius: 13px;
  font-size: 14.5px;
  line-height: 1.55;
  /* Model and user text both render as literal text. pre-wrap so newlines in a
     reply are preserved without ever interpreting any markup. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.clp-chat__msg--user .clp-chat__bubble {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-bottom-right-radius: 5px;
}

.clp-chat__msg--ai .clp-chat__bubble {
  background: var(--panel);
  border: 1px solid var(--border);
  border-bottom-left-radius: 5px;
}

.clp-chat__time {
  font-size: 11px;
  color: var(--dim);
  padding: 0 2px;
}

/* --- typing indicator --------------------------------------------------- */

.clp-chat__typing {
  align-self: flex-start;
  display: flex;
  gap: 4px;
  padding: 13px;
  border: 1px solid var(--border);
  border-radius: 13px;
  border-bottom-left-radius: 5px;
  background: var(--panel);
}

.clp-chat__typing[hidden] { display: none; }

.clp-chat__typing i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--dim);
  animation: clp-chat-bob 1.2s var(--ease) infinite;
}

.clp-chat__typing i:nth-child(2) { animation-delay: .18s; }
.clp-chat__typing i:nth-child(3) { animation-delay: .36s; }

@keyframes clp-chat-bob {
  0%, 60%, 100% { opacity: .3; transform: translateY(0); }
  30%           { opacity: 1;  transform: translateY(-3px); }
}

/* --- error / retry ------------------------------------------------------ */

.clp-chat__err {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 13px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
}

.clp-chat__err[hidden] { display: none; }

.clp-chat__retry {
  align-self: flex-start;
  padding: 7px 14px;
  border: 1px solid var(--border-2);
  border-radius: 12px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background .2s var(--ease);
}

.clp-chat__retry:hover { background: var(--panel-2); }

/* --- suggested prompts -------------------------------------------------- */

.clp-chat__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 16px 12px;
  flex: 0 0 auto;
}

.clp-chat__chips[hidden] { display: none; }

.clp-chat__chip {
  padding: 7px 13px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}

.clp-chat__chip:hover {
  background: var(--panel);
  border-color: var(--border-2);
}

/* --- composer ----------------------------------------------------------- */

.clp-chat__form {
  flex: 0 0 auto;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding: 12px 16px 14px;
  border-top: 1px solid var(--border);
  background: var(--panel-2);
}

.clp-chat__input {
  flex: 1 1 auto;
  min-width: 0;
  max-height: 110px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 14.5px;
  line-height: 1.5;
  resize: none;
  outline: none;
  transition: border-color .2s var(--ease);
}

.clp-chat__input::placeholder { color: var(--dim); }

.clp-chat__input:focus { border-color: var(--border-2); }

/* --- mobile composer -----------------------------------------------------
   iOS Safari zooms the whole page when a focused field computes to under
   16px, and the user is then stuck at that zoom until they double-tap. 16px
   is the documented threshold, so the input is never allowed below it on a
   touch viewport. This is the single most common mobile-chat bug and it is
   invisible on desktop, which is why it is pinned here rather than left to
   the base rule above. */
@media (pointer: coarse), (max-width: 640px) {
  .clp-chat__input {
    font-size: 16px;         /* must not go below: iOS zoom trap */
    line-height: 1.45;
  }
  .clp-chat__send {
    width: 44px;             /* comfortable touch target */
    height: 44px;
  }
  .clp-chat__act {
    width: 44px;             /* close / reset must be finger-sized too */
    height: 44px;
    /* keep the visual weight of the 30px box while enlarging the hit area */
    margin: -7px;
  }
  .clp-chat__chip {
    font-size: 15px;
    padding: 10px 14px;
  }
  .clp-chat__bubble {
    font-size: 15.5px;
  }
}

.clp-chat__send {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  transition: background .2s var(--ease), opacity .2s var(--ease);
}

.clp-chat__send:hover:not(:disabled) { background: var(--panel-2); }

.clp-chat__send:disabled {
  opacity: .4;
  cursor: not-allowed;
}

/* --- "jump to latest" ----------------------------------------------------
   Sits over the bottom of the transcript, above the composer. Shown only when
   the reader has scrolled up during a stream. */
.clp-chat__jump {
  position: absolute;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  z-index: 2;
  padding: 7px 14px;
  border: 1px solid var(--border-2);
  border-radius: 12px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 12.5px;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
  transition: background .2s var(--ease), opacity .2s var(--ease);
}

.clp-chat__jump:hover { background: var(--panel); }
.clp-chat__jump[hidden] { display: none; }

html.clp-light .clp-chat__jump { box-shadow: var(--shadow-2); }

/* --- inline shell on the home page --------------------------------------- */

.clp-chat-shell {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 24px;
}

.clp-chat-shell__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.clp-chat-shell__pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--avail);
  flex: 0 0 auto;
}

.clp-chat-shell__title {
  font-family: "Space Grotesk", sans-serif;
  font-weight: 600;
  font-size: clamp(1.15rem, 2.4vw, 1.5rem);
  letter-spacing: -.02em;
  margin: 0;
}

.clp-chat-shell__sub {
  font-size: 14px;
  color: var(--muted);
  margin: 0;
  width: 100%;
}

/* --- small screens ------------------------------------------------------
   On a phone the floating panel becomes a bottom sheet: edge to edge, pinned
   to the bottom, with the keyboard lifting it. A floating box inset from the
   edge wastes the screen and, worse, leaves the composer sitting under the
   on-screen keyboard. dvh tracks the visible height so the sheet resizes with
   the keyboard instead of being clipped by it. */
@media (max-width: 640px) {
  .clp-chat--inline { height: 480px; max-height: 64vh; max-height: 64dvh; }

  .clp-chat--pop {
    left: 0;
    right: 0;
    /* --clp-kb is the keyboard height reported by visualViewport; 0 normally. */
    bottom: var(--clp-kb, 0px);
    width: 100%;
    /* Only top corners rounded: it is a sheet, not a card. */
    border-radius: 18px 18px 0 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    /* Bottom padding clears the iOS home indicator. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
    height: min(78vh, 640px);
    height: min(78dvh, 640px);
    transform: translateY(100%);
    transition: transform .34s var(--ease), visibility .34s;
  }
  .clp-chat--pop.is-open { transform: none; }

  .clp-chat-fab {
    right: 12px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 70px);
  }
  .clp-chat-shell  { padding: 0 16px; }
  .clp-chat__head  { padding: 12px 14px; }
  .clp-chat__log   { padding: 14px 12px; }
  .clp-chat__form  { padding: 10px 12px 12px; }
  .clp-chat__chips { padding: 0 12px 10px; }
  .clp-chat__msg   { max-width: 90%; }

  /* A short grab handle reads as "sheet" and gives the sheet a drag affordance
     even though it is not draggable. */
  .clp-chat--pop .clp-chat__head::before {
    content: "";
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--border-2);
  }
  .clp-chat--pop .clp-chat__head { position: relative; padding-top: 16px; }

  /* When the keyboard is up there is no room to be polite about it. */
  .clp-chat--pop .clp-chat__chips { display: none !important; }
}

/* Landscape phones have almost no vertical room: give the log priority and
   collapse the header to just the identity row. */
@media (max-width: 900px) and (orientation: landscape) and (pointer: coarse) {
  .clp-chat--pop { height: calc(100dvh - 12px); border-radius: 16px 16px 0 0; }
  .clp-chat--inline { height: 60dvh; }
  .clp-chat__head { padding: 6px 12px; }
  .clp-chat__sub { display: none; }
  .clp-chat__log { padding: 10px 12px; gap: 10px; }
}

/* --- elevation: none in dark, soft in light ------------------------------ */

html.clp-light .clp-chat-fab     { box-shadow: var(--shadow-1); }
html.clp-light .clp-chat-fab:hover { box-shadow: var(--shadow-2); }
html.clp-light .clp-chat--pop,
html.clp-light .clp-chat--inline { box-shadow: var(--shadow-2); }

/* --- motion off --------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .clp-chat-fab,
  .clp-chat--pop { transition: none; }
  .clp-chat--pop { transform: none; }
  .clp-chat__typing i { animation: none; opacity: .55; }
}
