@charset "UTF-8";
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap");

/* ==========================================================================
   CoLivi · Livi — theme for the SAALT chat widget
   --------------------------------------------------------------------------
   Source of truth: "Livi 2b — hero search", variant 2, in the Claude Design
   project `Livi 2b.dc.html` / `HeroSearch.dc.html`. Every value below (colors,
   radii, shadows, sizes) is copied from that component, not approximated.

   REQUIRED LOAD ORDER:
     1. https://oak.arc-nine.com/embed/open-agent-kit-core.css
     2. this file
   Our selectors have the same specificity as the widget's own
   (#saalt-chat-container .class), so load order is what decides. Loaded before
   the core stylesheet, none of this has any effect.

   Design name -> element:
     convCard         ->  .livi-shell                    (built by embed.js)
     convHeader       ->  .livi-header                   (built by embed.js)
     convBody         ->  .saalt-chat__messages
     user bubble      ->  .saalt-chat__message-content--user
     Livi bubble      ->  .saalt-chat__message-content--assistant
     followChip       ->  .saalt-chat__advice-badges-card
     convInputRow     ->  .saalt-chat__input-container
     convInput        ->  .saalt-chat__text-area-container
     sendBtn          ->  .saalt-chat__submit-button
     minimized bubble ->  .livi-launcher                 (built by embed.js)
     searchBar        ->  .livi-hero__bar                (built by embed.js)

   NOTE ON THE AGENT'S OWN customCSS
   The agent "colivi-chatbot" ships a `customCSS` block in its chat settings.
   The widget injects it into <head> at runtime, so it lands AFTER this file
   and its `!important` rules win — today that covers the empty-state heading
   and subheading sizes and the footer note. Clear `customCSS` in the SAALT
   admin so this file is the single source of truth.
   ========================================================================== */


/* ==========================================================================
   1. TOKENS — taken from the design
   ========================================================================== */

:root {
  /* --- Livi palette ------------------------------------------------------ */
  --livi-cyan:         #2AACD8;   /* brand cyan                             */
  --livi-cyan-deep:    #1C8FB8;   /* user bubble, chip text                 */
  --livi-green:        #3FAE74;   /* "online" dot                           */

  --livi-ink:          #12242C;   /* send button, field text                */
  --livi-ink-head:     #14252E;   /* header name                            */
  --livi-ink-bubble:   #243139;   /* Livi bubble text                       */
  --livi-muted:        #5B6A72;   /* status, footer                         */
  --livi-placeholder:  #7D8A91;

  --livi-surface:      #FFFFFF;   /* card background                        */
  --livi-bubble-bg:    #F1F5F6;   /* Livi bubble background                 */
  --livi-field-bg:     #F7FAFB;   /* text field background                  */
  --livi-field-border: #E2E9EC;
  --livi-divider:      #EEF2F3;   /* header and footer rules                */
  --livi-icon-bg:      #F1F4F6;   /* round header buttons                   */

  /* --- Type -------------------------------------------------------------- */
  --livi-font: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* --- Shape ------------------------------------------------------------- */
  --livi-radius-card:   22px;
  --livi-radius-pill:   999px;
  --livi-shadow-card:   0 24px 70px rgba(10, 40, 55, .32);
  /* The bubble gradient colivi.com already uses, sampled from the live site. */
  --livi-bubble-grad: linear-gradient(90deg, #00b89f 0%, #00aef0 100%);
  --livi-shadow-bar:    0 12px 32px rgba(0, 40, 50, .3);

  /* --- Mascot — must match CONFIG.mascot in colivi-chat-embed.js ---------- */
  --livi-mascot: url("/wp-content/uploads/2026/08/colivi-mascot.png");
}

/* Bridge to the widget's own custom properties.
   Declared on #saalt-chat-container (specificity 1-0-0), not on :root, so they
   also beat the `:root { --saalt-chat-*: … }` block the agent's customCSS
   injects into <head> after this file. */
#saalt-chat-container {
  --saalt-chat-color-primary:                   var(--livi-ink);
  --saalt-chat-color-secondary:                 var(--livi-surface);
  --saalt-chat-color-bg:                        var(--livi-surface);
  --saalt-chat-text-color-primary:              var(--livi-ink-bubble);
  --saalt-chat-border-color:                    var(--livi-divider);
  --saalt-chat-color-muted-foreground:          var(--livi-muted);
  --saalt-chat-border-radius:                   16px;
  --saalt-chat-error-color:                     #C0392B;
  --saalt-chat-color-chat-user-background:      var(--livi-cyan-deep);
  --saalt-chat-color-chat-user-text:            #FFFFFF;
  --saalt-chat-color-chat-assistant-background: var(--livi-bubble-bg);
  --saalt-chat-color-chat-assistant-text:       var(--livi-ink-bubble);

  /* The widget centres its message column, form and badges inside
     max-width: var(--saalt-chat-width). Our card is already 560px, so the
     column should simply fill it. */
  --saalt-chat-width:      100%;
  --saalt-chat-messages-px: 16px;
}


/* ==========================================================================
   2. CONVERSATION CARD  (convCardStyle)
   ========================================================================== */

.livi-shell {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  height: 100%;
  font-family: var(--livi-font);
  background: var(--livi-surface);
  border: 1px solid rgba(20, 60, 80, .06);
  border-radius: var(--livi-radius-card);
  box-shadow: var(--livi-shadow-card);
  overflow: hidden;
  animation: livi-in .35s ease both;
}

/* The mount div (#chat-container) sits BETWEEN the card and the widget's own
   container. Left at automatic height, the widget's `height: 100%` resolves
   against it instead of against the card: the conversation ends up short with
   dead space below. So the body is flex and its direct child stretches. */
.livi-shell__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.livi-shell__body > * {
  flex: 1;
  min-height: 0;
}

/* The widget draws its own "card"; inside our shell it is only the middle. */
.livi-shell #saalt-chat-container {
  border: none;
  border-radius: 0;
  box-shadow: none;
  max-width: none;
  background: transparent;
}

#saalt-chat-container {
  font-family: var(--livi-font);
  color: var(--livi-ink-bubble);
  background-color: var(--livi-surface);
  height: 100%;
  max-height: 100%;
}

@keyframes livi-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}


/* ==========================================================================
   3. HEADER  (convHeaderStyle)
   ========================================================================== */

.livi-shell .livi-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--livi-divider);
  flex-shrink: 0;
  background: var(--livi-surface);
}

.livi-shell .livi-header__avatar {
  width: 32px;
  height: 32px;
  object-fit: contain;
  flex-shrink: 0;
}

.livi-shell .livi-header__text { flex: 1; min-width: 0; }

.livi-shell .livi-header__name {
  font-weight: 600;
  font-size: 15px;
  color: var(--livi-ink-head);
  line-height: 1.2;
}

.livi-shell .livi-header__status {
  font-size: 11.5px;
  color: var(--livi-muted);
  margin-top: 1px;
  display: flex;
  align-items: center;
}

.livi-shell .livi-header__status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--livi-green);
  display: inline-block;
  margin-right: 5px;
  flex-shrink: 0;
}

/* Round buttons: minimize and restart */
.livi-shell .livi-header__btn {
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  background: var(--livi-icon-bg);
  color: var(--livi-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
  transition: background-color .2s ease;
}

.livi-shell .livi-header__btn:hover { background: #E6EBEE; }
.livi-shell .livi-header__btn svg { width: 12px; height: 12px; }


/* ==========================================================================
   4. CONVERSATION BODY  (convBodyStyle)
   ========================================================================== */

#saalt-chat-container .saalt-chat__messages {
  padding: 18px 16px;
  scrollbar-width: thin;
  scrollbar-color: #D8E1E4 transparent;
}

#saalt-chat-container .saalt-chat__messages::-webkit-scrollbar { width: 6px; }
#saalt-chat-container .saalt-chat__messages::-webkit-scrollbar-track { background: transparent; }
#saalt-chat-container .saalt-chat__messages::-webkit-scrollbar-thumb {
  background-color: #D8E1E4;
  border-radius: var(--livi-radius-pill);
  border: none;
}

/* The widget spaces messages with gap: 1.25rem and nudges the column 4px to
   the right; the design uses a 12px gap and no offset. */
#saalt-chat-container .saalt-chat__messages-container {
  gap: 12px;
  transform: none;
}

#saalt-chat-container .saalt-chat__message {
  animation: livi-in .3s ease both;
}


/* ==========================================================================
   5. BUBBLES
   ========================================================================== */

#saalt-chat-container .saalt-chat__message-content {
  font-size: 14px;
  line-height: 1.45;
}

/* User — deep cyan bubble, bottom-right corner squared off */
#saalt-chat-container .saalt-chat__message-content--user {
  background: var(--livi-cyan-deep);
  color: #FFFFFF;
  border-radius: 16px 16px 4px 16px;
  padding: 10px 14px;
}

/* Livi — plain text, no bubble.
   Only the user's side is a container; Livi reads as the page talking back, so
   her turns lose the grey box, the rounded corners and the padding, and the
   line spacing plus the gap between turns carry the rhythm instead. */
#saalt-chat-container .saalt-chat__message-content--assistant {
  background: transparent;
  color: var(--livi-ink-bubble);
  border-radius: 0;
  padding: 0;
}

/* The 78% cap is what keeps a bubble from running the full width of the card.
   It only applies to the user now — unboxed text has no edge to pull in, and
   capping it would just make the column look arbitrarily narrow.
   The cap goes on the inner container: the widget's own 90% cap lives there,
   and putting 78% on the content container instead would nest the two and
   leave the bubble at 78% of 90%, i.e. 70%. */
#saalt-chat-container .saalt-chat__message--user .saalt-chat__message-inner-container {
  max-width: 78%;
}

#saalt-chat-container .saalt-chat__message--assistant .saalt-chat__message-inner-container {
  max-width: 100%;
}

#saalt-chat-container .saalt-chat__message-content-container {
  gap: 4px;
}

/* The widget pads the last assistant turn by 2rem to make room for its own
   scroll affordance; inside a fixed-height card that just adds a gap. */
#saalt-chat-container .saalt-chat__message--assistant:last-child .saalt-chat__message-content-container {
  padding-bottom: 0;
}

/* No avatar beside Livi's turns — colivi-chat-embed.js pins
   showMessageAvatar to "none", so nothing renders here to style. */

#saalt-chat-container .saalt-chat__message-content--copy-button {
  margin-top: 6px;
  opacity: 0;
  color: var(--livi-muted);
  padding: 3px;
  border-radius: 6px;
  transition: opacity .2s ease, background-color .2s ease;
}

#saalt-chat-container .saalt-chat__message:hover .saalt-chat__message-content--copy-button,
#saalt-chat-container .saalt-chat__message-content--copy-button:focus-visible { opacity: 1; }

#saalt-chat-container .saalt-chat__message-content--copy-button:hover { background: var(--livi-icon-bg); }


/* ==========================================================================
   6. MARKDOWN INSIDE BUBBLES
   ========================================================================== */

.saalt-chat__message-content a {
  color: var(--livi-cyan-deep);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.saalt-chat__message-content--user a {
  color: #FFFFFF;
  text-decoration-color: rgba(255, 255, 255, .6);
}

.saalt-chat__message-content ul,
.saalt-chat__message-content ol { padding-left: 20px; }

.saalt-chat__message-content strong { font-weight: 600; }

.saalt-chat__message-content hr {
  margin: 14px 0;
  border: none;
  border-top: 1px solid var(--livi-divider);
}

.saalt-chat__message-content pre {
  margin: 12px 0;
  background: var(--livi-ink);
  color: #F2F7F8;
  border-radius: 12px;
  padding: 12px 14px;
  overflow-x: auto;
}

.saalt-chat__message-content code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
}

.saalt-chat__message-content p > code {
  background: rgba(42, 172, 216, .12);
  color: var(--livi-cyan-deep);
  border-radius: 5px;
  padding: 1px 5px;
}

/* Tables can be wider than the card; let them scroll rather than push it. */
#saalt-chat-container .saalt-chat__table-container { overflow-x: auto; }


/* ==========================================================================
   7. EMPTY STATE — Livi's greeting
   --------------------------------------------------------------------------
   Heading and subheading text come from the agent's `intro` setting.
   ========================================================================== */

#saalt-chat-container .saalt-chat__empty-state {
  padding: 32px 24px 0;
  justify-content: flex-start;
}

/* Mascot above the greeting, without touching the React tree */
#saalt-chat-container .saalt-chat__empty-state::before {
  content: "";
  width: 64px;
  height: 64px;
  margin-bottom: 14px;
  background-image: var(--livi-mascot);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  animation: livi-in .4s ease both;
  flex-shrink: 0;
}

#saalt-chat-container .saalt-chat__empty-state-heading {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -0.4px;
  color: var(--livi-ink-head);
  text-wrap: balance;
  margin-bottom: 8px;
  max-width: 22ch;
}

#saalt-chat-container .saalt-chat__empty-state-subheading {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--livi-muted);
  margin-bottom: 22px;
  max-width: 34ch;
}


/* ==========================================================================
   8. SUGGESTION CHIPS  (followChipStyle — variant 2)
   --------------------------------------------------------------------------
   The widget renders these as a card grid. The design uses pills on one row,
   so grid becomes flex-wrap.
   ========================================================================== */

/* The widget nests the badges inside .saalt-chat__empty-state, which already
   carries the side padding — so this block adds none of its own. */
#saalt-chat-container .saalt-chat__advice-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  padding: 0;
  grid-template-columns: none;
}

#saalt-chat-container .saalt-chat__advice-badges-card {
  display: inline-flex;
  align-items: center;
  max-width: 100%;      /* a long suggestion must wrap, not overflow the card */
  padding: 8px 14px;
  border-radius: var(--livi-radius-pill);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  background: rgba(42, 172, 216, .1);
  color: var(--livi-cyan-deep);
  border: 1px solid rgba(42, 172, 216, .28);
  transition: background-color .2s ease, border-color .2s ease;
}

#saalt-chat-container .saalt-chat__advice-badges-card:hover {
  background: rgba(42, 172, 216, .18);
  border-color: rgba(42, 172, 216, .5);
}

#saalt-chat-container .saalt-chat__advice-badges-card-title {
  font-weight: 500;
  font-size: 13px;
  color: inherit;
}


/* ==========================================================================
   9. INPUT ROW  (convInputRowStyle + convInputStyle + sendBtnStyle)
   --------------------------------------------------------------------------
   The widget stacks the textarea on top of an action row, both inside one
   grey block. The design wants a pill field with a round button beside it, so
   .saalt-chat__form becomes a flex row and loses its own background.
   ========================================================================== */

#saalt-chat-container .saalt-chat__input-container {
  margin: 0;
  padding: 12px 14px;
  border-top: 1px solid var(--livi-divider);
  flex-shrink: 0;
}

/* The widget fades the messages out behind a gradient above the input. Inside
   a card with a hard divider that reads as a smudge. */
#saalt-chat-container .saalt-chat__input-container::before { content: none; }

/* The row that holds the field and the button.
   Which element that is depends on the build: on oak.arc-nine.com the form
   holds both directly; dev.saalt.ai wraps them in .saalt-chat__input-box and
   moves the grey blurred block onto it. Both are named so the theme survives
   the switch either way — the one that is not there simply matches nothing. */
#saalt-chat-container .saalt-chat__form,
#saalt-chat-container .saalt-chat__input-box {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  max-width: none;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* On the newer build the box is a child of the form rather than the row
   itself, and left to itself it shrinks to its content instead of spanning the
   card. Matches nothing on the older build. */
#saalt-chat-container .saalt-chat__input-box {
  flex: 1;
  width: 100%;
}

/* The pill field. No align-items here: the newer build makes this a grid for
   its collapse animation, and centring the row would stop the textarea
   filling it. */
#saalt-chat-container .saalt-chat__text-area-container {
  flex: 1;
  min-width: 0;
  background: var(--livi-field-bg);
  border: 1px solid var(--livi-field-border);
  border-radius: var(--livi-radius-pill);
  transition: border-color .2s ease, box-shadow .2s ease;
}

#saalt-chat-container .saalt-chat__form:focus-within .saalt-chat__text-area-container {
  border-color: var(--livi-cyan);
  box-shadow: 0 0 0 3px rgba(42, 172, 216, .15);
}

#saalt-chat-container .saalt-chat__text-area {
  font-family: var(--livi-font);
  font-size: 14px;
  line-height: 1.4;
  color: var(--livi-ink);
  padding: 11px 16px;
  background: transparent;
  /* A capsule stops looking right past about three lines. If you expect long
     messages, drop the pill radius above to 22px. */
  max-height: 120px;
}

#saalt-chat-container .saalt-chat__text-area::placeholder {
  color: var(--livi-placeholder);
  opacity: 1;
}

/* The action row shrinks and sits to the right of the field */
#saalt-chat-container .saalt-chat__action-row {
  width: auto;
  flex-shrink: 0;
  align-items: center;
  justify-content: flex-end;
  padding: 0;
  gap: 6px;
}

/* Holds the plus menu and the model picker, both off for this agent — without
   this it still eats the row's gap. */
#saalt-chat-container .saalt-chat__action-row__buttons:empty { display: none; }

/* Send button — 42px, #12242C, white arrow.
   The widget builds it from its shared button component
   (.saalt-button.variant-icon), NOT from the .saalt-chat__submit-button rules
   that are still sitting in open-agent-kit-core.css. Those are dead; this is
   the element that actually renders. */
#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"] {
  width: 42px;
  height: 42px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--livi-ink);
  color: #FFFFFF;
  box-shadow: none;
  transition: background-color .2s ease, transform .15s ease, opacity .2s ease;
}

#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"] svg {
  width: 18px;
  height: 18px;
}

/* The design's send glyph points right; the widget ships an up arrow and the
   icon is not swappable from CSS, so it is turned a quarter turn. Scoped to
   the label so the streaming "stop" icon and the loader are left alone.
   Delete this rule to keep the widget's default arrow. */
#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"] .saalt-button-label .icon {
  transform: rotate(90deg);
}

#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"]:hover:not(:disabled) {
  background: #1D3947;
  opacity: 1;
  transform: scale(1.05);
}

#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"]:active:not(:disabled) {
  transform: scale(.95);
}

/* The widget only dims the disabled button to 50%; the design uses a solid
   grey so the arrow stays legible. */
#saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"]:disabled {
  background: #C9D5DA;
  color: #FFFFFF;
  opacity: 1;
  cursor: not-allowed;
}

/* The container query measures #saalt-chat-container, not the window — which
   is what we want: in a narrow WordPress column the chat should go compact
   even on desktop. */
@container chat-container (max-width: 480px) {
  #saalt-chat-container .saalt-chat__action-row > .saalt-button[type="submit"] {
    width: 38px;
    height: 38px;
  }
}


/* ==========================================================================
   10. THINKING, FOOTER, ERRORS, TOOL CALLS
   --------------------------------------------------------------------------
   File upload and the plus menu are switched off for this agent, so nothing
   here styles attachments.
   ========================================================================== */

#saalt-chat-container .saalt-chat__thinking-message {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--livi-muted);
  margin-bottom: 12px;
}

#saalt-chat-container .saalt-chat__scroll-down-indicator {
  background: var(--livi-surface);
  color: var(--livi-ink);
  border: 1px solid var(--livi-divider);
  box-shadow: 0 4px 14px rgba(10, 40, 55, .16);
  padding: 7px;
  bottom: 12px;
}

#saalt-chat-container .saalt-chat__scroll-down-indicator svg { width: 16px; height: 16px; }

#saalt-chat-container .saalt-chat__footer-note {
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--livi-muted);
  padding: 0 16px 12px;
}

#saalt-chat-container .saalt-message__error-chip {
  background: rgba(192, 57, 43, .07);
  border: 1px solid rgba(192, 57, 43, .28);
  border-radius: 14px;
  color: #C0392B;
  font-size: 13px;
  font-weight: 500;
}

#saalt-chat-container .saalt-chat__message-tool-invocations {
  font-size: 11.5px;
  color: var(--livi-muted);
  padding-left: 12px;
  margin-bottom: 12px;
}

#saalt-chat-container .saalt-chat__message-tool-invocation,
#saalt-chat-container .saalt-chat__message-tool-invocations-mcp-content > div {
  border-left: 2px solid var(--livi-divider);
}

#saalt-chat-container .saalt-chat__loading-logo { width: 44px; height: 44px; }

#saalt-chat-container .saalt-chat__loading-text {
  font-size: 14px;
  font-weight: 500;
  color: var(--livi-muted);
}


/* ==========================================================================
   11. MINIMIZED BUBBLE  (bubbleStyle) and PANEL
   ========================================================================== */

/* Matched to the bubble colivi.com already runs, measured off the live site:
   64px, the teal-to-blue gradient at 90deg, a white hummingbird at 50/64 of
   the circle, and a soft neutral shadow. The design's variant 2 drew a white
   bubble with the coloured bird and a pulsing green dot; the site's own is the
   one visitors know, so that is what this follows. */
.livi-launcher {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 2147483000;
  width: 64px;
  height: 64px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--livi-bubble-grad);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .15);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .25s ease, opacity .25s ease, visibility .25s ease;
}

.livi-launcher:hover  { transform: scale(1.06); }
.livi-launcher:active { transform: scale(.94); }

/* Hidden by fading out rather than display:none, so arriving and leaving on
   scroll is a movement instead of a flicker. Doubled class so it outranks the
   :hover rule above, which would otherwise re-inflate a bubble on its way
   out from under a stationary cursor. */
.livi-launcher.livi-launcher--hidden {
  opacity: 0;
  visibility: hidden;
  transform: scale(.8);
  pointer-events: none;
}

/* The mascot PNG is the cyan-to-green bird; the site shows it as a flat white
   silhouette, which is what the filter does — same trick the site uses. */
.livi-launcher__mascot {
  width: 78%;
  height: 78%;
  object-fit: contain;
  filter: brightness(0) invert(1);
}

/* The online dot. Reads as "Livi is there" and the slow pulse is what catches
   the eye once the bubble is the only thing left on screen. The white ring
   keeps it legible over the gradient. */
.livi-launcher__dot {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  /* White disc: this is the ring, and it never fades. Fading the whole dot —
     ring included — would let it dissolve into the gradient underneath and
     read as broken rather than alive. */
  background: #FFFFFF;
}

/* The green fill, and the only thing that moves. */
.livi-launcher__dot::before {
  content: "";
  position: absolute;
  inset: 1px;                 /* leaves a 1px white ring */
  border-radius: 50%;
  background: var(--livi-green);
  animation: livi-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;
}

/* @keyframes livi-pulse, below, is what the dot animates with.

   This follows what the market actually does rather than the expanding-ring
   idea: checked live, Tawk.to's online dot runs
     @keyframes pulse { 50% { opacity: .5 } }
     animation: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite
   — Tailwind's animate-pulse. The dot fades and comes back, nothing moves.
   An expanding halo is Tailwind's animate-ping, which sits in the same
   stylesheet and is deliberately NOT what they put on the indicator: a ring
   flying outwards reads as an alert, not as presence.
   (Crisp, Tidio and Intercom show no dot at all on the bubble — they use an
   unread badge instead.)

   Taken a little further than the 0.5 default so it carries on a small dot
   over a coloured bubble. Raise the floor towards 1 to calm it down. */
@keyframes livi-pulse {
  50% { opacity: .4; }
}

/* The teaser card that pops out of the bubble.
   Measured off colivi.com's own: a white card 19px above the bubble with its
   right edge aligned, 12px radius, 1px #EEE hairline, 0 2px 8px rgba(0,0,0,.08),
   14px text in #171717, and a small round × hanging off the top-right corner. */
.livi-teaser {
  position: fixed;
  right: 22px;
  bottom: 105px;              /* 64px bubble + 22px inset + 19px gap */
  z-index: 2147483000;
  max-width: min(268px, calc(100vw - 44px));
  font-family: var(--livi-font);
  transition: opacity .3s ease, transform .3s ease, visibility .3s ease;
}

.livi-teaser--hidden {
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  pointer-events: none;
}

.livi-teaser__text {
  display: block;
  width: 100%;
  padding: 12px;
  border: 1px solid #EEEEEE;
  border-radius: 12px;
  background: #FFFFFF;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
  color: #171717;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: background-color .2s ease;
}

.livi-teaser__text:hover { background: #F7FAFB; }

.livi-teaser__close {
  position: absolute;
  top: -10px;
  right: -10px;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid #EEEEEE;
  border-radius: 50%;
  background: #FFFFFF;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
  color: #888888;
  font: 16px/1 var(--livi-font);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color .2s ease, background-color .2s ease;
}

.livi-teaser__close:hover { background: #F1F5F6; color: var(--livi-ink); }

@media (max-width: 620px) {
  /* Tracks the smaller bubble and its tighter inset. */
  .livi-teaser { right: 16px; bottom: 93px; max-width: calc(100vw - 32px); }
}

.livi-panel {
  position: fixed;
  right: 22px;
  bottom: 100px;
  z-index: 2147483000;
  width: min(560px, calc(100vw - 44px));
  height: min(620px, calc(100dvh - 140px));
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .3s ease, transform .3s ease, visibility .3s ease;
}

.livi-panel--open { opacity: 1; visibility: visible; transform: translateY(0); }

/* The design shows a 340px card floating inside a phone frame. On a real
   phone that leaves no room for the keyboard, so the panel goes full-bleed.
   Pinned with left+right rather than width: 100vw — 100vw counts the
   scrollbar, which is enough to overflow the page and drag every fixed
   element sideways. */
@media (max-width: 620px) {
  .livi-launcher { width: 58px; height: 58px; right: 16px; bottom: 16px; }
  .livi-panel {
    left: 0; right: 0; bottom: 0;
    width: auto; height: 100dvh;
    max-width: 100%;
  }
  .livi-panel .livi-shell {
    max-width: none;
    border: none;
    border-radius: 0;
  }
}


/* ==========================================================================
   12. HERO — the search bar over the site  (searchBarStyle, variant 2)
        Only used when embed.js runs in mode: "hero".
   ========================================================================== */

/* The design sizes the hero for an 820x560 browser mock, where 560px fills the
   frame. On a real full-width page that reads small, so every dimension scales
   with the viewport between the design value (the clamp minimum, which is what
   a narrow column still gets) and a ceiling about a third larger. */
.livi-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(18px, 1.8vw, 26px);
  width: 100%;
  max-width: clamp(560px, 54vw, 760px);
  margin: 0 auto;
  font-family: var(--livi-font);
  animation: livi-in .4s ease both;
}

/* The scrim: softly darkens the photo behind the hero so the white greeting
   keeps its contrast. It belongs to the hero and not to the page, because it
   has no reason to exist once the hero steps aside — this way it leaves with
   it instead of lingering over the banner.
   Sized as a percentage of the hero, so it grows with it and can never be
   wider than the viewport the hero already fits in. */
.livi-hero::before {
  content: "";
  position: absolute;
  inset: -50% -8%;
  z-index: 0;
  /* closest-side, so the gradient reaches full transparency at the box edges
     instead of still being visible when the box clips it — which is what turns
     a soft veil into a faint rectangle over the photo. */
  background: radial-gradient(ellipse closest-side at center,
    rgba(8, 22, 30, .50) 0%,
    rgba(8, 22, 30, .36) 45%,
    rgba(8, 22, 30, .14) 70%,
    rgba(8, 22, 30, 0) 100%);
  pointer-events: none;
}

/* Lifted over the scrim. Positioned rather than given a negative z-index on
   the scrim, so nothing can slip behind an ancestor's background on a host
   page we do not control. */
.livi-hero__greeting,
.livi-hero__bar {
  position: relative;
  z-index: 1;
}

.livi-hero__greeting {
  font-size: clamp(30px, 3vw, 42px);
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -0.6px;
  color: #FFFFFF;
  margin: 0;
  max-width: clamp(520px, 50vw, 700px);
  text-wrap: balance;
  text-shadow: 0 2px 22px rgba(0, 0, 0, .6), 0 1px 4px rgba(0, 0, 0, .45);
}

.livi-hero__bar {
  display: flex;
  align-items: center;
  gap: clamp(11px, 1vw, 14px);
  width: 100%;
  padding: clamp(11px, 1vw, 14px);
  padding-left: clamp(18px, 1.6vw, 24px);
  border-radius: var(--livi-radius-pill);
  background: rgba(255, 255, 255, .92);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow: var(--livi-shadow-bar);
  box-sizing: border-box;
  transition: border-color .2s ease, box-shadow .2s ease;
}

/* The bar is the focus affordance for the input inside it — see the note in
   section 13 on why the input itself has no outline. */
.livi-hero__bar:focus-within {
  border-color: rgba(42, 172, 216, .9);
  box-shadow: var(--livi-shadow-bar), 0 0 0 3px rgba(42, 172, 216, .25);
}

.livi-hero__mascot {
  width: clamp(40px, 3.4vw, 52px);
  height: clamp(40px, 3.4vw, 52px);
  object-fit: contain;
  flex-shrink: 0;
}

.livi-hero__input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: clamp(14.5px, 1.15vw, 17px);
  color: var(--livi-ink);
}

.livi-hero__input::placeholder { color: var(--livi-placeholder); }

/* The send button carries the bubble's gradient, so the two ways of reaching
   Livi — the hero bar and the corner bubble — read as the same thing. The
   design uses the dark ink here; this follows the site instead. */
.livi-hero__send {
  width: clamp(42px, 3.4vw, 52px);
  height: clamp(42px, 3.4vw, 52px);
  flex-shrink: 0;
  border: none;
  border-radius: 50%;
  background: var(--livi-bubble-grad);
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color .2s ease, transform .15s ease;
}

/* Same proportion the widget uses on its own send button — an 18px icon in a
   42px circle — and the same quarter turn to point it right. */
.livi-hero__send svg {
  width: 43%;
  height: 43%;
  transform: rotate(90deg);
}

/* Brightened rather than recoloured: a gradient cannot be nudged by swapping
   a single hex the way the flat ink could. */
.livi-hero__send:hover  { filter: brightness(1.08); transform: scale(1.05); }
.livi-hero__send:active { transform: scale(.95); }

@media (max-width: 620px) {
  .livi-hero { gap: 14px; max-width: 320px; }
  .livi-hero__greeting { font-size: 20px; max-width: 300px; }
  .livi-hero__bar { padding: 8px 8px 8px 14px; gap: 9px; }
  .livi-hero__mascot { width: 32px; height: 32px; }
  .livi-hero__input { font-size: 13.5px; }
  .livi-hero__send { width: 38px; height: 38px; }
}


/* ==========================================================================
   13. QUALITY FLOOR
   ========================================================================== */

#saalt-chat-container :focus-visible,
.livi-shell :focus-visible,
.livi-launcher:focus-visible,
.livi-hero :focus-visible {
  outline: 2px solid var(--livi-cyan);
  outline-offset: 2px;
}

/* …except the two text fields. A hard cyan ring around a pill-shaped field
   reads as a blue selection box sitting on top of the input, and it doubles up
   with the ring the field's own container already draws on focus. Those
   containers — .saalt-chat__text-area-container and .livi-hero__bar — turn
   cyan and pick up a soft glow instead, which keeps focus just as visible
   without the hard edge. */
#saalt-chat-container .saalt-chat__text-area:focus,
#saalt-chat-container .saalt-chat__text-area:focus-visible,
.livi-hero__input:focus,
.livi-hero__input:focus-visible {
  outline: none;
}

/* Mobile browsers flash a translucent blue box over whatever you tap. Our
   fields and buttons all have their own press states, so the flash is only
   noise. */
#saalt-chat-container,
#saalt-chat-container *,
.livi-shell, .livi-shell *,
.livi-hero, .livi-hero *,
.livi-launcher {
  -webkit-tap-highlight-color: transparent;
}

/* The blue highlight when dragging across text should be Livi's blue, not the
   browser default. */
.livi-shell ::selection,
.livi-hero ::selection {
  background: rgba(42, 172, 216, .22);
  color: var(--livi-ink);
}

#saalt-chat-container .saalt-chat__message-content--user ::selection {
  background: rgba(255, 255, 255, .3);
  color: #FFFFFF;
}

@media (prefers-reduced-motion: reduce) {
  #saalt-chat-container *,
  .livi-shell, .livi-launcher, .livi-panel, .livi-hero, .livi-launcher__dot::before {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
