:root {
  --ink: #1c1c22;
  --muted: #61616b;
  --line: rgba(28, 28, 34, 0.12);
  --accent: #4338ca;
  --danger: #b3261e;
  --surface: rgba(255, 255, 255, 0.92);
  --radius: 14px;
}

* { box-sizing: border-box; }

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

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: linear-gradient(155deg, #eef2ff 0%, #f8fafc 45%, #fdf2f8 100%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

.auth-shell,
.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 32px 20px;
}

.auth-shell { justify-content: center; }

.brand {
  display: inline-flex;
  color: var(--ink);
  margin-bottom: 24px;
  text-decoration: none;
}

.card {
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: 0 18px 50px rgba(28, 28, 34, 0.08);
}

.card h1 {
  margin: 0 0 6px;
  font-size: 22px;
}

.card p.lead {
  margin: 0 0 20px;
  color: var(--muted);
  line-height: 1.5;
}

.field { margin-bottom: 16px; }

.field label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}

.field input[type="email"],
.field input[type="password"],
.field input[type="text"] {
  width: 100%;
  padding: 11px 13px;
  font-size: 15px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
}

.field input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}

.checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  font-size: 14px;
  color: var(--muted);
}

.button {
  display: inline-block;
  width: 100%;
  padding: 12px 16px;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
}

.button.secondary {
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--line);
}

.button:disabled { opacity: 0.5; cursor: default; }

/*
 * Replacements for what would otherwise be inline style attributes. The Content-Security-Policy
 * in public/index.php sets style-src 'self' with no 'unsafe-inline', so a style="..." attribute
 * is dropped by the browser and logged as a violation. Use these classes instead.
 */
.form-inline { display: inline; }

.button--inline {
  width: auto;
  padding: 6px 12px;
}

.stack-top { margin-top: 12px; }

.alert {
  margin: 0 0 18px;
  padding: 11px 13px;
  font-size: 14px;
  border-radius: 10px;
  border: 1px solid transparent;
}

.alert.error { color: var(--danger); background: #fdecea; border-color: rgba(179, 38, 30, 0.2); }
.alert.notice { color: #1b5e20; background: #eaf5ec; border-color: rgba(27, 94, 32, 0.2); }

.links {
  margin-top: 18px;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
}

.links a { color: var(--accent); text-decoration: none; }

.device-list { list-style: none; margin: 0 0 20px; padding: 0; }

.device-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.device-list .meta { font-size: 13px; color: var(--muted); }

.site-footer {
  margin-top: 28px;
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 13px;
  color: var(--muted);
}

.site-footer form { display: flex; gap: 8px; }

.site-footer button {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--muted);
  cursor: pointer;
  text-decoration: underline;
}

.site-footer button[aria-current="true"] { color: var(--ink); font-weight: 600; text-decoration: none; }

.app-header {
  width: 100%;
  max-width: 900px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
}

.app-header .who { font-size: 14px; color: var(--muted); }

.hidden { display: none !important; }

/*
 * `hidden` MUST ACTUALLY HIDE.
 *
 * The `hidden` attribute is only honoured by the user-agent rule `[hidden] { display: none }`,
 * which any author rule setting `display` on the same element beats - and this file sets one:
 * `.chat__thread` is `display: flex` with `flex: 1 1 auto`. views/chat/index.php renders that
 * thread `hidden` and empty on first load, so the browser was laying out an invisible,
 * content-free box that still claimed every pixel of free space in the chat column. That is why
 * the first-request heading and its input sat pinned to the bottom of the card instead of in
 * the middle of it: the space they were meant to share had already been eaten by something with
 * nothing in it.
 *
 * `!important` rather than source order. An ordinary `[hidden]` rule has the same specificity
 * (0,1,0) as `.chat__thread`, so which one wins would depend on which appears later in the file
 * - a rule that silently breaks the next time somebody moves a block or adds a `display` to a
 * class that also gets hidden. `!important` is what makes the guarantee unconditional, and is
 * the same idiom normalize.css uses for exactly this attribute.
 *
 * The other elements rendered `hidden` here - `#chat-history`, `#chat-target-wrap`, `#chat-mic`
 * and `#chat-notice` - all DO carry a `display` of their own now (`inline-flex` on the first two,
 * `block` from `<p>` on the last), so this rule is what actually hides them rather than a
 * coincidence about which classes happen to declare what.
 */
[hidden] { display: none !important; }

/*
 * OFF SCREEN BUT STILL IN THE ACCESSIBILITY TREE - the opposite of `[hidden]` above, and the two
 * must not be confused. `display: none` removes an element from every modality at once; this
 * keeps it announced, focusable and still able to label something, while taking it out of the
 * picture. `#chat-target`'s <label> is the reason it exists: there is no room for "Millest
 * räägime?" inside the composer pill, and dropping the label rather than moving it would leave a
 * screen reader announcing a combo box that says only "Veebileht".
 *
 * The 1px clipped box rather than `width: 0` or `text-indent: -9999px`: a zero-sized element is
 * skipped by some screen readers outright, and a huge negative indent is still painted (and
 * still scrolls the page sideways in a right-to-left context). `white-space: nowrap` stops a
 * clipped-to-1px box from wrapping its text into a tall column that some browsers then read out
 * one word per line.
 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * NEAT animated gradient — the client-facing background.
 *
 * .gradient-backdrop is a fixed full-viewport box painted with a plain linear-gradient in the
 * NEAT palette. The <canvas> lives inside it and covers it once the WebGL gradient is running.
 * If WebGL is unavailable, if /assets/js/neat-gradient.js fails to load, or if JavaScript is
 * disabled, the canvas simply stays transparent and this gradient is what shows: same element,
 * same box, so there is no white flash and no layout shift.
 *
 * z-index -1 puts the backdrop above the propagated body background and below every shell.
 */
.gradient-backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: linear-gradient(155deg, #06102B 0%, #071D33 22%, #2E0EC7 48%, #0E73E4 72%, #8B6AE6 100%);
}

/* The sizing the NEAT documentation puts in a style="" attribute. Task 25's style-src 'self'
   drops inline style attributes, so it lives here instead. */
.neat-canvas {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/*
 * Reduced motion: the bundle returns before constructing NeatGradient, so the canvas is never
 * painted. Hiding it as well costs nothing and makes the intent legible in the stylesheet.
 */
@media (prefers-reduced-motion: reduce) {
  .neat-canvas { display: none; }
}

/* Chrome that sits directly on the dark backdrop needs light text; the .card keeps its own
   near-opaque white surface and is unaffected.
   Both client-facing shells, not just .app-shell: .auth-shell stacks its brand and footer
   OUTSIDE .card, so once the gradient moved behind /login, /forgot and /reset those two ended
   up on it too. .card's own contents are untouched either way. */
.auth-shell .brand,
.app-shell .brand { color: #fff; }

.app-shell .app-header .who,
.auth-shell .site-footer,
.auth-shell .site-footer button,
.app-shell .site-footer,
.app-shell .site-footer button {
  color: rgba(255, 255, 255, 0.82);
}

.auth-shell .site-footer a,
.auth-shell .site-footer button[aria-current="true"],
.app-shell .site-footer a,
.app-shell .site-footer button[aria-current="true"] { color: #fff; }

/* ---------- Chat ---------- */

/* The chat view is the client's whole working screen, not a login form - it needs the width
   .card otherwise caps at 420px for the auth/dashboard cards. Scoped to :has(.chat) so every
   other view still gets the narrow card. */
.card:has(.chat) {
  max-width: 60rem;
  padding: 20px 28px 28px;
}

/*
 * THE FIRST REQUEST HAS NO CARD, and the card comes back for the conversation.
 *
 * The empty state is a heading and an input floating in the middle of the screen, so a white
 * 60rem panel behind them is a box drawn around nothing: it is the thing that made the hero
 * read as "a mostly-empty form" rather than "ask me something". Dropping it lets the gradient
 * - the only piece of brand this product has - actually reach the client.
 *
 * It comes back the moment there is a conversation, and that is a judgement rather than an
 * inconsistency. A thread is a column of small grey type, activity lines and disclosure
 * triangles; those need a stable, legible surface, and an animated gradient is the opposite of
 * one. The hero can afford to sit on the gradient because it is two large elements, one of
 * which is an opaque white pill. The transition is not a flicker either: sending the first
 * request already replaces the heading and re-docks the composer, so the surface arriving
 * underneath reads as part of that same move.
 *
 * `:has(.chat__composer--centered)`, NOT `:has(.chat:not(.chat--active))`, and the difference
 * is a real screen. chat.js's activate() removes that class and adds `chat--active` in the same
 * two lines, so the two conditions agree on every page that has a composer at all - but a
 * client with no website connected renders `.chat__empty` and NO form, and that screen is a
 * single line of muted grey prose which would have been left sitting on the gradient with
 * nothing behind it. Naming the composer says "the first-request hero" instead of "not yet
 * talking", and only the first one is a state that can afford to lose its surface.
 */
.card:has(.chat__composer--centered) {
  max-width: 64rem;
  padding: 0;
  background: none;
  border-color: transparent;
  box-shadow: none;
}

.chat { display: flex; flex-direction: column; min-height: 65vh; gap: 1rem; }

/* Nothing framing it any more, so the hero is centred in the shell rather than in a box. */
.chat:has(.chat__composer--centered) { min-height: calc(100vh - 14rem); }

.chat__empty {
  margin: auto;
  max-width: 32rem;
  text-align: center;
  color: var(--muted);
}

.chat__header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.chat__site-name { font-weight: 600; }

/* Same problem the auth shell's footer has: with the card gone these two sit on the gradient,
   and both are coloured for a white surface (--ink, and the purple --accent). White alone is
   so they get the same soft dark shadow as the heading rather than a second colour to maintain.
   The current palette is uniformly dark (#06102B through #8B6AE6), so white alone would very
   nearly do - the shadow stays because the palette is configuration, not a constant: it lives in
   the NEAT bundle and has been re-tuned once already, and a future lighter colour would silently
   make plain white type unreadable here. */
.chat:has(.chat__composer--centered) .chat__site-name,
.chat:has(.chat__composer--centered) .chat__history-button {
  color: #fff;
  text-shadow: 0 1px 3px rgba(11, 25, 40, 0.5), 0 2px 14px rgba(11, 25, 40, 0.45);
}

.chat__history-button {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--accent);
  font: inherit;
  font-size: 13px;
  padding: 0;
}
.chat__history-button:hover { text-decoration: underline; }

.chat__history {
  list-style: none;
  margin: 0;
  padding: .5rem 0;
  border-bottom: 1px solid var(--line);
}
.chat__history-item { padding: .3rem 0; font-size: 13px; color: var(--muted); }
.chat__history-open {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
  color: inherit;
  width: 100%;
}
.chat__history-open:hover { color: var(--accent); }

/*
 * The first-request hero: one centred heading sitting directly on top of one pill, both centred
 * in the free space on both axes, and both gone the moment the conversation has anything in it.
 *
 * The centring is split across two rules on purpose. `.chat` is a flex column, and putting
 * `margin: auto` on the composer alone (what this file did before the hero existed) makes the
 * composer absorb ALL the free space, which would strand the heading at the top of the card
 * instead of above the input. `margin-top: auto` here plus `margin-bottom: auto` on the
 * composer splits that free space between the two ends, so the pair rides together in the
 * middle - no wrapper element, and nothing to undo when .chat--active turns the composer back
 * into a docked row.
 *
 * This only ever worked on the horizontal axis, because a hidden-but-still-laid-out
 * `.chat__thread` was eating the vertical free space before either auto margin could see it.
 * See `[hidden]` near the top of this file.
 */
.chat__hero {
  margin: auto auto 0;
  width: min(46rem, 100%);
  text-align: center;
}

/*
 * `.chat__hero` in the selector, not for tidiness: `.card h1` (0,1,1) beats a bare
 * `.chat__hero-title` (0,1,0) and was quietly winning both declarations below, so the "large
 * centred heading" this state is built around was rendering at the 22px meant for a login
 * card's title.
 *
 * White with a soft shadow because the hero drops the card (see `.card:has(.chat:not(...))`
 * above) and therefore sits directly on an animating gradient rather than a known surface. White
 * plus a wide, low-opacity dark shadow is legible across any of it, and costs no box, no scrim
 * and no extra element. See the note on `.chat__site-name` above for why the shadow stays even
 * though the palette is currently dark enough not to need it.
 */
.chat__hero .chat__hero-title {
  margin: 0 0 1.75rem;
  font-size: clamp(2rem, 4.6vw, 3rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: #fff;
  text-shadow: 0 1px 3px rgba(11, 25, 40, 0.45), 0 6px 28px rgba(11, 25, 40, 0.55);
}

.chat--active .chat__hero { display: none; }

.chat__thread {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  overflow-y: auto;
  flex: 1 1 auto;
}

.chat__bubble {
  padding: .85rem 1.1rem;
  border-radius: var(--radius);
  max-width: 42rem;
  line-height: 1.5;
}
.chat__bubble--user {
  align-self: flex-end;
  background: var(--accent);
  color: #fff;
}
.chat__bubble--assistant {
  align-self: flex-start;
  background: #f4f4f7;
  border: 1px solid var(--line);
}

.chat__activity { align-self: flex-start; font-size: 13px; color: var(--muted); padding: 0 1.1rem; }
.chat__activity-line { margin: .2rem 0; }

.chat__result-summary { margin: 0 0 .5rem; }

.chat__changes { margin: .5rem 0; font-size: 13px; }
.chat__changes-summary { cursor: pointer; color: var(--muted); }
.chat__changes-list { margin: .4rem 0 0; padding-left: 1.2rem; }
.chat__changes-item { margin: .15rem 0; }

.chat__rollback { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-top: .5rem; }
.chat__rollback-button,
.chat__rollback-yes,
.chat__rollback-no {
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  border-radius: 999px;
  border: 1px solid var(--danger);
  color: var(--danger);
  background: transparent;
  padding: .3rem .9rem;
}
.chat__rollback-button:disabled,
.chat__rollback-yes:disabled,
.chat__rollback-no:disabled { opacity: .5; cursor: default; }
.chat__rollback-question { font-size: 13px; color: var(--muted); }

/*
 * The composer is a column in BOTH states - the pill on its own row, the two pickers under it -
 * and the only things `.chat--active` changes are the auto margins and the width. It used to be
 * a wrapping row when docked, which put the send button on a line of its own at any width where
 * the textarea and the button did not both fit.
 */
.chat__composer {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: .6rem;
}
.chat__composer--centered {
  /* `margin-bottom: auto`, not `margin: auto` - see .chat__hero above for why the free space
     is shared between the two rather than swallowed here. */
  margin: 0 auto auto;
  width: min(46rem, 100%);
}

/*
 * The pill's own box. It carries no chrome: the border, radius and shadow stay on #chat-input
 * (see THE PILL below for why that element cannot be swapped out), and this wrapper exists to
 * be the thing the control cluster at its right end is laid out against.
 */
.chat__pill { display: flex; align-items: flex-end; gap: .5rem; width: 100%; }

/*
 * The site picker, below the pill and deliberately quiet: 13px, muted, centred under it in the
 * empty state and left-aligned once the composer docks. It is a detail most accounts never see -
 * it only exists above one website. The TARGET selector used to live out here beside it and now
 * sits inside the pill (see .chat__target-wrap below); the site picker stays because its options
 * are client-supplied website names of any length, which is not a width this row can budget for
 * inside a 4rem bar - views/chat/index.php has the full argument.
 */
.chat__site-wrap {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: 13px;
}

.chat:has(.chat__composer--centered) .chat__site-wrap { justify-content: center; }

.chat__site-label { font-size: 13px; color: var(--muted); }

.chat__site {
  font: inherit;
  font-size: 13px;
  padding: .3rem .5rem;
  border-radius: 99px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink);
}

/* On the gradient there is no card behind this, so the grey chosen for a white surface is not
   legible. The control itself stays a white box and keeps its own dark text. */
.chat:has(.chat__composer--centered) .chat__site-label {
  color: #fff;
  text-shadow: 0 1px 3px rgba(11, 25, 40, 0.55), 0 2px 12px rgba(11, 25, 40, 0.5);
}

/*
 * THE TARGET SELECTOR, SEATED IN THE PILL between the textarea and the two buttons.
 *
 * `flex: 0 0 auto` on every member of the cluster and `flex: 1 1 auto` on the textarea alone is
 * what keeps the dictate and send buttons still when chat.js un-hides this on a site change: the
 * textarea is the only item with any give, so the width this takes comes out of the text, never
 * out of the buttons' position. `[hidden] { display: none !important }` near the top of this file
 * is what makes `hidden` actually remove it from the row, against the `display` set here.
 *
 * `appearance` is left alone on purpose - the platform's own disclosure chevron is exactly the
 * `⌄` the reference draws, at the size and in the direction the client's OS already uses, and
 * re-drawing one by hand costs a background image and gets it subtly wrong on Safari.
 */
.chat__target-wrap {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  max-width: 9rem;
  padding: .25rem .1rem .25rem .55rem;
  border-radius: 99px;
  background: rgba(28, 28, 34, 0.05);
}

/* Decorative, and generated content rather than an element so that it cannot become text: an
   empty `content` with a background colour is a coloured circle to a browser and nothing at all
   to a screen reader. Fixed accent colour in both states - the <select>'s own value is the only
   thing that says which target is selected. */
.chat__target-wrap::before {
  content: "";
  flex: 0 0 auto;
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: var(--accent);
}

.chat__target {
  font: inherit;
  font-size: 13px;
  min-width: 0;
  padding: .15rem .1rem;
  border: 0;
  border-radius: 99px;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
.chat__target:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/*
 * DICTATE. A quiet ghost button beside the filled send one - two solid circles side by side
 * would read as two equally important actions, and this one is an aid to filling in the text,
 * not a second way to submit it.
 */
.chat__mic {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  font: inherit;
  cursor: pointer;
  border: 0;
  border-radius: 50%;
  color: var(--muted);
  background: transparent;
}
.chat__mic:hover { background: rgba(28, 28, 34, 0.06); color: var(--ink); }
.chat__mic:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chat__mic-icon { display: block; }

/* LISTENING, and it has to be obvious at a glance rather than a tint: the client is being
   recorded, and the only way to stop is to press this again. Filled in the danger red, with a
   slow pulse for anyone whose motion preference allows one. */
.chat__mic--listening,
.chat__mic--listening:hover {
  color: #fff;
  background: var(--danger);
}
.chat__mic--listening { animation: chat-mic-pulse 1.6s ease-in-out infinite; }

@keyframes chat-mic-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(179, 38, 30, 0.45); }
  50% { box-shadow: 0 0 0 .5rem rgba(179, 38, 30, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .chat__mic--listening { animation: none; }
}

.chat__mic[disabled] { opacity: .5; cursor: progress; }

/* What a refused or failed microphone is said in. Quiet, and white-on-gradient in the empty
   state for the same reason the site label above is. */
.chat__notice {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted);
}

.chat:has(.chat__composer--centered) .chat__notice {
  text-align: center;
  color: #fff;
  text-shadow: 0 1px 3px rgba(11, 25, 40, 0.55), 0 2px 12px rgba(11, 25, 40, 0.5);
}

.chat__input {
  flex: 1 1 auto;
  /* `min-width: 0` beats the automatic minimum size flexbox gives a form control, which for a
     textarea is its `cols` default - roughly 20 characters that no amount of flex-shrink can
     take back. Without it the pill's own contents overflow it the moment the cluster at the
     right end is three controls wide and the viewport is a phone's. */
  min-width: 0;
  font: inherit;
  padding: .85rem 1.1rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: #fff;
  resize: vertical;
  min-height: 3rem;
}
.chat__input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/*
 * A COMPACT CIRCULAR ICON BUTTON, not a full-width block. It is still `<button type="submit">`
 * and it still carries the translated `chat_send` string - as `aria-label`, because the glyph
 * inside it is an `aria-hidden` <svg> and an icon button with no accessible name is a button a
 * screen reader announces as "button".
 */
.chat__send {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  font: inherit;
  cursor: pointer;
  border: 0;
  border-radius: 50%;
  color: #fff;
  background: var(--accent);
}
.chat__send:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.chat__send-icon { display: block; }
.chat__send[disabled],
.chat__input[disabled] { opacity: .5; cursor: progress; }

/*
 * THE PILL, and it is a restyled <textarea>, not an <input type="text">.
 *
 * #chat-input stays one element across both states because chat.js keeps a single reference to
 * it for the whole page life (`input.value`, `input.value = ''`, `input.disabled = true/false`
 * in three separate places) and never re-reads the DOM - so the empty state and the working
 * state MUST be the same node, and that node has to be the one that still works once the
 * conversation is running and a client wants to type more than one line. Swapping the tag would
 * also silently change what Enter does: a textarea inserts a newline, an <input> submits the
 * form, and nothing in this project currently submits on Enter.
 *
 * Single-line is therefore geometry, not markup: one line's worth of height, no resize grip,
 * a 99px radius, and `white-space: nowrap`, which is what actually makes a textarea behave like
 * one line - without it the text wraps inside a box only tall enough for one row, so a long
 * request (and this view's own placeholder, at a narrow width) disappears behind a scrollbar.
 * With it, the row simply scrolls sideways under the caret exactly as `<input type="text">`
 * does, and `overflow: hidden` keeps the scrollbar itself out of the pill the same way. Nothing
 * is lost: the first send docks the composer, and the rules below hand the client back the full
 * multi-line textarea for everything after it.
 */
/*
 * THE VISIBLE PILL IS THE WRAPPER IN THIS STATE, and the textarea inside it is transparent.
 *
 * The obvious construction - keep the chrome on the input, reserve `padding-right` for the
 * button and lay the button over it - does not work, and the way it fails is not cosmetic.
 * Chrome scrolls a textarea's text THROUGH its own right padding, so a request longer than the
 * pill slides underneath the button and takes the caret with it: the client is then typing at a
 * position they cannot see. Making the button a real flex sibling inside a padded wrapper is
 * what actually reserves the space, at every width and for any length of text.
 *
 * The 99px radius and `white-space: nowrap` stay on the input as well as the wrapper. The
 * radius is what keeps the caret and the text clipped to the pill's shape rather than to a
 * rectangle inside it, and `nowrap` is still the thing that makes a textarea behave as one line
 * at all - see THE PILL above.
 *
 * .5rem of wrapper padding around a 3rem button is a 4rem pill, so the three numbers below move
 * together.
 */
.chat:not(.chat--active) .chat__pill {
  align-items: center;
  /* Not 0: Chrome scrolls the text through the input's own right padding too, so without a real
     gap a long request ends flush against the button rather than a comfortable distance from
     it. */
  gap: .4rem;
  padding: .5rem;
  border-radius: 99px;
  border: 1px solid var(--line);
  background: #fff;
  box-shadow: 0 14px 40px rgba(11, 25, 40, 0.18);
}

.chat:not(.chat--active) .chat__input {
  border-radius: 99px;
  padding: .5rem 1.25rem;
  min-height: 0;
  height: 3rem;
  resize: none;
  white-space: nowrap;
  overflow: hidden;
  border-color: transparent;
  background: transparent;
}

/* The focus ring belongs to the whole pill now, not to the transparent box inside it - and it
   is white, because out here there is no card behind it for the indigo to read against. */
.chat:not(.chat--active) .chat__input:focus { outline: none; }
.chat:not(.chat--active) .chat__pill:focus-within {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.chat:not(.chat--active) .chat__send { width: 3rem; height: 3rem; }
.chat:not(.chat--active) .chat__mic { width: 3rem; height: 3rem; }

.chat--active .chat__composer {
  margin: 0;
  width: 100%;
}

@media (max-width: 40rem) {
  .chat__bubble { max-width: 100%; }
}

/*
 * A PHONE, where the cluster is at its worst: at 375px the shell's own padding leaves the pill
 * about 335px, and a site with both connections spends three controls plus a selector of it. The
 * three numbers below buy the textarea back roughly 70px without any of them wrapping - the
 * selector gives up the room a long option never needed (its two are "Veebileht" and "Tabel"),
 * and the two buttons drop to the 2.5rem that is still comfortably over the 44px touch target
 * once their own padding is counted.
 *
 * Shrinking rather than wrapping is the point. A pill that breaks onto a second line at some
 * widths is no longer a pill, and the send button ends up somewhere different depending on
 * whether the client's site happens to have a spreadsheet attached.
 */
@media (max-width: 26rem) {
  .chat:not(.chat--active) .chat__pill { padding: .35rem; gap: .2rem; }
  .chat:not(.chat--active) .chat__input { padding: .5rem .85rem; }

  .chat__target-wrap {
    max-width: 6.5rem;
    gap: .2rem;
    padding-left: .4rem;
  }
  .chat__target { font-size: 12px; }

  .chat:not(.chat--active) .chat__send,
  .chat:not(.chat--active) .chat__mic { width: 2.5rem; height: 2.5rem; }
}
