/* Styles for the portable chat widget.
 *
 * Every class is prefixed ksj- and every rule is scoped under .ksj-chat, because this
 * drops into two pages whose own CSS was written with no knowledge of it. Nothing here
 * sets a global, and nothing here styles a bare tag outside the widget.
 *
 * Colour is entirely custom properties. The defaults below are a neutral light theme;
 * a host page overrides whichever it cares about:
 *
 *   .ksj-chat {
 *     --ksj-accent: #34618c;
 *     --ksj-surface: #fff;
 *   }
 */

.ksj-chat {
  --ksj-surface: #ffffff;
  --ksj-surface-2: #f1f3f6;
  --ksj-ink: #1c2128;
  --ksj-ink-2: #454c57;
  --ksj-muted: #767e8b;
  --ksj-line: #dde1e7;
  --ksj-line-2: #e9ecf1;
  --ksj-accent: #2f5fe0;
  --ksj-accent-2: #24499f;
  --ksj-accent-fg: #ffffff;
  --ksj-accent-bg: rgba(47, 95, 224, .08);
  --ksj-warn: #8a5a00;
  --ksj-warn-bg: rgba(251, 191, 37, .14);
  --ksj-log-max: 30rem;
  --ksj-font: inherit;
  --ksj-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  display: flex;
  flex-direction: column;
  font-family: var(--ksj-font);
  color: var(--ksj-ink);
  font-size: .92rem;
  line-height: 1.55;
  text-align: left;
}

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

/* The [hidden] attribute is display:none in the UA stylesheet only, so any author rule
 * that sets display beats it. .ksj-chips is display:flex, which left the suggestions on
 * screen after the first answer on kangseongjun.com. It happened to work on the Next.js
 * site because Tailwind's preflight marks [hidden] !important, and depending on the host
 * page's framework for the widget's own behaviour is not a plan. */
.ksj-chat [hidden] { display: none; }

.ksj-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------------------ head */
/* Google leaves roughly a hundred pixels between the subtitle and the composer. The
 * gap is doing real work: it is what stops the composer reading as part of the copy. */
.ksj-head { margin-bottom: 1.75rem; }

.ksj-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--ksj-ink);
  line-height: 1.3;
}

.ksj-intro {
  margin: .3rem 0 0;
  font-size: .84rem;
  color: var(--ksj-muted);
}

/* ------------------------------------------------------------------- log */
/* No border and no fixed height: at rest the element is hidden entirely, so the composer
 * is the only thing on screen. It appears on the first question and grows to its cap. */
.ksj-log {
  max-height: var(--ksj-log-max);
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-bottom: 1rem;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  .ksj-log { scroll-behavior: auto; }
}

/* -------------------------------------------------------------- messages */
/* The question is a bubble on the right and the answer is plain full-width text. No role
 * labels: the shape says who is talking, the same way every mainstream assistant does it. */
.ksj-msg { margin-bottom: 1.5rem; }
.ksj-msg:last-child { margin-bottom: 0; }

.ksj-msg-user { display: flex; justify-content: flex-end; }
.ksj-msg-user .ksj-body {
  max-width: 85%;
  background: var(--ksj-surface-2);
  color: var(--ksj-ink);
  border-radius: 1.2rem;
  padding: .6rem .95rem;
}

/* The answer gets a badge and the question does not, which is the asymmetry every
 * assistant uses to say which side is the machine. */
.ksj-msg-assistant { display: flex; gap: .7rem; align-items: flex-start; }
.ksj-turn { flex: 1; min-width: 0; }

.ksj-avatar {
  flex-shrink: 0;
  width: 1.7rem;
  height: 1.7rem;
  margin-top: .1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--ksj-accent);
  color: var(--ksj-accent-fg);
}
.ksj-avatar svg { width: .95rem; height: .95rem; fill: currentColor; stroke: none; }

/* The caret that says text is still arriving. It hangs off the last block of the answer
 * text, so it sits at the end of the sentence being written. Scoping it to .ksj-answer
 * keeps it away from the sources block, which paint() appends after the text. Before the
 * first token there is no .ksj-answer at all, so the dots below carry that stretch. */
.ksj-streaming .ksj-answer > :last-child::after {
  content: "";
  display: inline-block;
  width: .45em;
  height: 1em;
  margin-left: .12em;
  vertical-align: -.12em;
  border-radius: 1px;
  background: var(--ksj-accent);
  animation: ksj-caret 1s steps(2, start) infinite;
}

@keyframes ksj-caret { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

.ksj-body > :first-child { margin-top: 0; }
.ksj-body > :last-child { margin-bottom: 0; }
.ksj-body p { margin: 0 0 .7rem; }
.ksj-body ul, .ksj-body ol { margin: 0 0 .7rem; padding-left: 1.3rem; }
.ksj-body li { margin-bottom: .25rem; }
.ksj-body h3, .ksj-body h4, .ksj-body h5, .ksj-body h6 {
  margin: 1.1rem 0 .4rem;
  font-size: .95rem;
  font-weight: 700;
  color: var(--ksj-ink);
  line-height: 1.35;
}
.ksj-body a { color: var(--ksj-accent); text-decoration: underline; text-underline-offset: 2px; }
.ksj-body code {
  font-family: var(--ksj-mono);
  font-size: .85em;
  background: var(--ksj-surface-2);
  border: 1px solid var(--ksj-line-2);
  border-radius: 4px;
  padding: .05em .3em;
}
.ksj-body pre {
  margin: 0 0 .7rem;
  padding: .7rem .85rem;
  background: var(--ksj-surface-2);
  border: 1px solid var(--ksj-line-2);
  border-radius: 10px;
  overflow-x: auto;
}
.ksj-body pre code { background: none; border: 0; padding: 0; }

/* ------------------------------------------------------------- citations */
.ksj-cite {
  display: inline-block;
  font-family: var(--ksj-mono);
  font-size: .68rem;
  font-weight: 700;
  color: var(--ksj-accent);
  background: var(--ksj-accent-bg);
  border-radius: 4px;
  padding: 0 .28em;
  margin: 0 .08em;
  text-decoration: none;
  vertical-align: .08em;
  transition: background .15s, color .15s;
}
.ksj-cite:hover { background: var(--ksj-accent); color: var(--ksj-accent-fg); }

.ksj-sources {
  margin-top: .9rem;
  border-top: 1px solid var(--ksj-line-2);
  padding-top: .6rem;
}
.ksj-sources summary {
  cursor: pointer;
  font-size: .76rem;
  color: var(--ksj-muted);
  list-style: none;
  display: flex;
  align-items: center;
  gap: .35rem;
}
.ksj-sources summary::-webkit-details-marker { display: none; }
.ksj-sources summary::before { content: "\25B8"; font-size: .7em; transition: transform .15s; }
.ksj-sources[open] summary::before { transform: rotate(90deg); }
.ksj-sources ol {
  margin: .5rem 0 0;
  padding-left: 1.2rem;
  font-size: .78rem;
  color: var(--ksj-muted);
}
.ksj-sources li { margin-bottom: .22rem; }
.ksj-sources a { color: var(--ksj-ink-2); text-decoration: none; }
.ksj-sources a:hover { color: var(--ksj-accent); text-decoration: underline; }
.ksj-path { font-family: var(--ksj-mono); font-size: .92em; opacity: .7; }

/* --------------------------------------------------------------- pending */
.ksj-thinking { display: inline-flex; gap: .3rem; align-items: center; padding: .35rem 0; }
.ksj-thinking span {
  width: .4rem; height: .4rem; border-radius: 50%;
  background: var(--ksj-muted);
  animation: ksj-pulse 1.1s ease-in-out infinite;
}
.ksj-thinking span:nth-child(2) { animation-delay: .15s; }
.ksj-thinking span:nth-child(3) { animation-delay: .3s; }

@keyframes ksj-pulse {
  0%, 60%, 100% { opacity: .25; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-2px); }
}

@media (prefers-reduced-motion: reduce) {
  .ksj-thinking span { animation-duration: 0.01ms; animation-iteration-count: 1; opacity: .6; }
  .ksj-streaming > :last-child::after { animation: none; }
  .ksj-chips button, .ksj-cite, .ksj-send, .ksj-composer, .ksj-copy { transition: none; }
}

/* --------------------------------------------------------------- actions */
.ksj-actions { display: flex; gap: .3rem; margin-top: .6rem; }

.ksj-copy {
  display: inline-flex;
  align-items: center;
  gap: .32rem;
  font: inherit;
  font-size: .72rem;
  color: var(--ksj-muted);
  background: none;
  border: 1px solid transparent;
  border-radius: 7px;
  padding: .25rem .45rem;
  cursor: pointer;
  transition: color .15s, background .15s, border-color .15s;
}
.ksj-copy:hover { color: var(--ksj-ink-2); background: var(--ksj-surface-2); }
.ksj-copy.is-done { color: var(--ksj-accent); }
.ksj-copy svg {
  width: .82rem; height: .82rem;
  fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

.ksj-error {
  margin-top: .5rem;
  font-size: .82rem;
  color: var(--ksj-warn);
  background: var(--ksj-warn-bg);
  border-radius: 8px;
  padding: .45rem .6rem;
}

/* -------------------------------------------------------------- composer */
/* The block has to announce itself as the thing you type into, and it sits directly
 * above a stack of suggestion buttons. An outlined pill loses that fight: it is the same
 * shape, the same border and the same height as a suggestion, so the eye reads a row of
 * five buttons and no input.
 *
 * Three things separate them, all taken from how Google, OpenAI and Anthropic build it:
 * the composer is a FILLED surface against an unfilled page, it is TALL because the text
 * gets its own line above a toolbar, and it is pushed well clear of the suggestions. The
 * suggestions stay outlined and transparent, so the two never share a look. */
.ksj-composer {
  background: var(--ksj-surface-2);
  border: 1px solid transparent;
  border-radius: 1.5rem;
  padding: .95rem 1rem .7rem 1.15rem;
  transition: border-color .15s, box-shadow .15s;
}
.ksj-composer:focus-within {
  border-color: var(--ksj-accent);
  box-shadow: 0 0 0 3px var(--ksj-accent-bg);
}

.ksj-composer textarea {
  display: block;
  width: 100%;
  font: inherit;
  font-family: var(--ksj-font);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ksj-ink);
  background: none;
  border: 0;
  padding: 0;
  resize: none;
  overflow-y: auto;
  max-height: 160px;
}
.ksj-composer textarea::placeholder { color: var(--ksj-muted); }
.ksj-composer textarea:focus { outline: none; }

/* The toolbar row. This is the slot Google fills with "Pro" and OpenAI with the model
 * picker, and it is most of what gives the block its height before anything is typed.
 * Here it holds the keyboard hint: it earns the space by teaching the interaction, and
 * naming the keys is a stronger cue that this is a text field than a model name was. */
.ksj-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-top: .55rem;
  min-height: 2.2rem;
}

.ksj-hint {
  margin: 0;
  font-size: .72rem;
  color: var(--ksj-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ksj-send {
  flex-shrink: 0;
  margin-left: auto;
  width: 2.2rem;
  height: 2.2rem;
  display: grid;
  place-items: center;
  color: var(--ksj-accent-fg);
  background: var(--ksj-accent);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: background .15s, opacity .15s;
}
.ksj-send:hover:not(:disabled) { background: var(--ksj-accent-2); }
.ksj-send:disabled { opacity: .25; cursor: default; }
.ksj-send svg {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}

/* ----------------------------------------------------------------- chips */
/* Outlined and transparent, against a filled composer. Held well clear of it: at the old
 * .7rem they crowded the composer into looking like the first item in their own list.
 * Google leaves about 85px here, and the gap is not decoration, it is the thing that
 * says these two are different kinds of object. */
.ksj-chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  padding: 0;
  margin: 3rem 0 0;
}
.ksj-chips li { margin: 0; }

/* Once the transcript is up, the big gap has done its job. At rest it is what tells the
 * composer and the suggestions apart; during a conversation the transcript above already
 * makes that obvious, and holding the gap open just pushes the tail down the page. */
.ksj-log:not([hidden]) ~ .ksj-chips { margin-top: 1.1rem; }

.ksj-chips button {
  font: inherit;
  font-size: .8rem;
  color: var(--ksj-ink-2);
  background: none;
  border: 1px solid var(--ksj-line);
  border-radius: 999px;
  padding: .4rem .8rem;
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
  text-align: left;
}
.ksj-chips button:hover {
  border-color: var(--ksj-accent);
  color: var(--ksj-accent);
  background: var(--ksj-accent-bg);
}

/* Already asked. Dimmed rather than removed, and still clickable. */
.ksj-chips button.is-used { opacity: .45; }
.ksj-chips button.is-used:hover { opacity: 1; }

/* ------------------------------------------------------------------ foot */
.ksj-disclaimer {
  margin: 1.5rem 0 0;
  font-size: .72rem;
  color: var(--ksj-muted);
}

/* ---------------------------------------------------------------- figures */
/* Figures attached to the sources an answer cited. Sized so two sit side by side on a
   desktop and one fills the width on a phone. Height is capped rather than fixed: a
   panel figure and a tall schematic both have to look deliberate in the same row. */
.ksj-figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: .6rem;
  margin: .9rem 0 0;
}
.ksj-fig {
  margin: 0;
  border: 1px solid var(--ksj-line, #dfe2e8);
  border-radius: 8px;
  overflow: hidden;
  background: var(--ksj-raised, #fff);
  /* Caption sits at the bottom of every card, so a one-line and a two-line caption still
     leave the images aligned across the row. */
  display: flex;
  flex-direction: column;
}
.ksj-fig figcaption { margin-top: auto; }
.ksj-fig img {
  display: block;
  width: 100%;
  /* Journal figures arrive at wildly different aspect ratios: a wide NMF heatmap next to
     a near-square scatter. A fixed height leaves one of them floating in dead space, so
     the height follows the image and only very tall figures get cropped to keep the row
     from running away. */
  max-height: 15rem;
  object-fit: cover;
  object-position: top;
  background: var(--ksj-sunk, #f4f5f7);
  cursor: zoom-in;
}
.ksj-fig figcaption {
  font-size: .72rem;
  line-height: 1.45;
  color: var(--ksj-muted, #6b7280);
  padding: .4rem .55rem;
  border-top: 1px solid var(--ksj-line, #dfe2e8);
}

/* Click to enlarge. The dialog is plain CSS so the widget keeps its no-dependency rule. */
.ksj-lightbox {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  display: grid;
  place-items: center;
  padding: 2rem 1rem;
  background: rgba(10, 12, 16, .82);
  cursor: zoom-out;
}
.ksj-lightbox img {
  max-width: min(64rem, 100%);
  max-height: 88vh;
  object-fit: contain;
  border-radius: 6px;
  background: #fff;
}
.ksj-lightbox figcaption {
  color: #e8eaee;
  font-size: .8rem;
  text-align: center;
  margin-top: .7rem;
  max-width: 44rem;
}
