/**
 * Salesy storefront chat — everything inside the conversation: the log, the
 * bubbles, the reply markdown, the product cards, the chips, the cart lines,
 * the delivery form and the composer. The chrome AROUND it — a launcher and a
 * floating panel, a full-screen overlay, a docked sidebar — is a shell, one
 * stylesheet per shell, and exactly one is enqueued per page.
 *
 * Theme isolation (0.15.0) is the reason this file reads the way it does, and
 * splitting it changed none of it:
 *
 * 1. EVERY rule here is scoped `#salesy-root …` (specificity 1,1,0), which
 *    outranks any theme class selector however deep — `.woocommerce
 *    button.button` is only (0,3,1). A bare `.salesy-*` selector is a defect,
 *    and WidgetAssetTest sweeps every widget*.css for one. All shells share
 *    that single root id and distinguish themselves with a modifier class, so
 *    there is exactly one specificity argument to make, not one per shell.
 * 2. The enumerated element reset below strips theme declarations with
 *    `!important`, because a theme that itself uses `!important` cannot be
 *    beaten by specificity alone. THE RULE IT CREATES: any declaration in any
 *    widget*.css that sets a forced property on one of those element types
 *    must carry `!important` too, or the reset silently zeroes it. That is
 *    swept for, not remembered — see
 *    WidgetAssetTest::test_no_declaration_loses_silently_to_the_reset.
 * 3. Widget::enqueue() registers at priority 9999, so these print last.
 *
 * `font-family` is the one channel left open, deliberately: the panel reads in
 * the STORE'S typeface and in nothing else the theme decided.
 *
 * ONE CONTRACT A SHELL MUST HONOUR: the opening screen's one/two-column switch
 * and the collage fan are `@container` queries, so whichever element a shell
 * uses as the conversation's scrolling frame must declare
 * `container-type: inline-size`. Without it those queries never match and the
 * intro silently stays in its narrow layout.
 */

#salesy-root.salesy-widget {
  --salesy-offset-side: 20px;
  --salesy-offset-bottom: 20px;
  --salesy-accent: #111827;
  --salesy-accent-text: #ffffff;
  /* The accent drawn ON the canvas rather than under text — see accentInk(). */
  --salesy-accent-ink: #111827;
  --salesy-surface: #ffffff;
  --salesy-surface-muted: #f3f4f6;
  /* The panel's own ground: a warm off-white, so a white card reads as a card. */
  --salesy-canvas: #f7f7f5;
  --salesy-border: #e7e5e1;
  --salesy-border-strong: #d8d5d0;
  --salesy-ink: #111827;
  --salesy-muted: #6b7280;
  /* The large opening line: present, but not competing with the copy under it. */
  --salesy-faint: #9ca3af;
  --salesy-radius-panel: 28px;
  --salesy-radius-card: 16px;
  --salesy-radius-bubble: 18px;
  --salesy-radius-pill: 9999px;
  --salesy-shadow-panel:
    0 32px 64px -16px rgba(17, 24, 39, 0.3),
    0 12px 24px -12px rgba(17, 24, 39, 0.18);
  --salesy-shadow-card:
    0 6px 16px -6px rgba(17, 24, 39, 0.22), 0 1px 2px rgba(17, 24, 39, 0.06);
  --salesy-shadow-chip: 0 1px 2px rgba(17, 24, 39, 0.06);
  --salesy-panel-width: 680px;
  --salesy-panel-height: 520px;

  position: fixed;
  bottom: var(--salesy-offset-bottom);
  z-index: 99999;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 12px;

  /* Inherited properties are the leak a reset cannot close, because they arrive
     from `body` rather than from a rule matching our elements. Every one of them
     is pinned here — except font-family, which is the deliberate channel. */
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.45;
  letter-spacing: normal;
  word-spacing: normal;
  text-transform: none;
  text-align: start;
  text-indent: 0;
  color: var(--salesy-ink);
  -webkit-font-smoothing: antialiased;
}

/* -- The reset -------------------------------------------------------------
   The FORCED list is exactly the declarations below carrying `!important`.
   Anything this file sets from that list, on one of these element types, must
   carry `!important` as well. `position` is deliberately NOT forced — the panel
   and the collage cards depend on it. */
#salesy-root a,
#salesy-root button,
#salesy-root code,
#salesy-root em,
#salesy-root form,
#salesy-root h3,
#salesy-root h4,
#salesy-root h5,
#salesy-root img,
#salesy-root input,
#salesy-root li,
#salesy-root ol,
#salesy-root p,
#salesy-root s,
#salesy-root select,
#salesy-root strong,
#salesy-root ul {
  font-family: inherit !important;
  font-size: inherit !important;
  font-weight: 400 !important;
  font-style: normal !important;
  line-height: inherit !important;
  letter-spacing: normal !important;
  word-spacing: normal !important;
  text-transform: none !important;
  text-decoration: none !important;
  text-shadow: none !important;
  text-align: start !important;
  text-indent: 0 !important;
  color: inherit !important;
  background: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  margin: 0 !important;
  padding: 0 !important;
  min-width: 0 !important;
  min-height: 0 !important;
  max-width: none !important;
  max-height: none !important;
  width: auto !important;
  height: auto !important;
  outline: 0 !important;
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
  animation: none !important;
  filter: none !important;
  float: none !important;
  box-sizing: border-box;
  list-style: none;
  vertical-align: baseline;
}

/* Emphasis in a reply survives the reset. Same specificity as the reset, so
   position in the file is what decides it — these must stay below it. */
#salesy-root strong {
  font-weight: 600 !important;
}

#salesy-root em {
  font-style: italic !important;
}

/* WordPress core rewrites an emoji character in our own text into
   `<img class="emoji">` (wp-emoji-release.min.js, a MutationObserver over
   document.body — the widget renders into the light DOM, so it is not
   exempt, and `wp-exclude-emoji` on the root — see widget-core.js — does NOT
   stop this: core's own observer re-parses each mutation from the added
   text node's immediate parent, never from `#salesy-root` or `body` again,
   so the ancestor opt-out is never consulted for a message the widget
   appends after boot. Proven live: with the class present, a freshly
   appended emoji still produced a new `img.emoji`.) Core prints its own
   `img.wp-smiley, img.emoji { width: 1em; height: 1em }` rule, but the reset
   above is `#salesy-root img` — a higher specificity, also `!important` — so
   core's sizing loses and the image renders at its own intrinsic size (a
   full-width replaced element for the SVG variant, which is exactly how one
   filled the panel after an add-to-cart reply). Restoring inline, 1em sizing
   HERE — same specificity as the reset above, so position in the file is
   what decides it, and this must stay below it — is the one fix confirmed
   to actually hold for real chat content. */
#salesy-root img.wp-smiley,
#salesy-root img.emoji {
  display: inline !important;
  width: 1em !important;
  height: 1em !important;
  margin: 0 0.07em !important;
  vertical-align: -0.1em !important;
  background: none !important;
  padding: 0 !important;
}

/* Safety net for the class of defect above, not just the one instance: the
   widget itself never places an <img> inside a message body (every card it
   draws lives in its own row, never inside .salesy-msg), so any <img> that
   turns up inside one is foreign — WordPress core, a theme, a browser
   extension — and carries none of the sizing this file gives its own
   images. Never let a foreign image blow out the log. */
#salesy-root .salesy-msg img {
  max-width: 100% !important;
  max-height: 1.5em !important;
}

/* Only the controls the widget draws itself lose their native look. A `select`
   keeps its dropdown arrow and a radio keeps its dot: they are native controls
   on purpose (see the delivery panel below), and stripping `appearance` from
   them would leave a shopper with a control they cannot read. */
#salesy-root button,
#salesy-root .salesy-input,
#salesy-root input.salesy-delivery-input,
#salesy-root input.salesy-account-input {
  -webkit-appearance: none !important;
  appearance: none !important;
}

/* `!important` because the reset forces `outline: 0` — a focus ring is the one
   thing that must not be lost to it. */
#salesy-root :focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

/* The preset glyphs. Each is a data URI used as a MASK rather than a
   background image, so the shape takes currentColor — that is what keeps it
   legible against a pale brand colour, where the button's text turns dark.
   Nothing here is assembled from a setting: the class name is chosen from a
   fixed table in the script. */
#salesy-root .salesy-icon--chat,
#salesy-root .salesy-icon--bag,
#salesy-root .salesy-icon--sparkle,
#salesy-root .salesy-icon--headset,
#salesy-root .salesy-icon--question,
#salesy-root .salesy-icon--plus,
#salesy-root .salesy-icon--send {
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

#salesy-root .salesy-icon--chat {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.5 11.5a8 8 0 0 1-8.6 8 9.6 9.6 0 0 1-4-.9L3.5 20.5l1.9-4.4a8 8 0 0 1-1.4-4.6 8 8 0 0 1 8.2-8 8 8 0 0 1 8.3 8z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.5 11.5a8 8 0 0 1-8.6 8 9.6 9.6 0 0 1-4-.9L3.5 20.5l1.9-4.4a8 8 0 0 1-1.4-4.6 8 8 0 0 1 8.2-8 8 8 0 0 1 8.3 8z'/%3E%3C/svg%3E");
}

#salesy-root .salesy-icon--bag {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 2 3 6.5V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.5L18 2z'/%3E%3Cpath d='M3 6.5h18'/%3E%3Cpath d='M16 10.5a4 4 0 0 1-8 0'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 2 3 6.5V20a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6.5L18 2z'/%3E%3Cpath d='M3 6.5h18'/%3E%3Cpath d='M16 10.5a4 4 0 0 1-8 0'/%3E%3C/svg%3E");
}

#salesy-root .salesy-icon--sparkle {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M11 4.5 13 10l5.5 2-5.5 2-2 5.5-2-5.5L3.5 12 9 10z'/%3E%3Cpath d='M18.5 3v4M20.5 5h-4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M11 4.5 13 10l5.5 2-5.5 2-2 5.5-2-5.5L3.5 12 9 10z'/%3E%3Cpath d='M18.5 3v4M20.5 5h-4'/%3E%3C/svg%3E");
}

#salesy-root .salesy-icon--headset {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14v-2a8 8 0 0 1 16 0v2'/%3E%3Cpath d='M20 14.5a2 2 0 0 1 0 4h-1.5v-4z'/%3E%3Cpath d='M4 14.5a2 2 0 0 0 0 4h1.5v-4z'/%3E%3Cpath d='M18.5 18.5v.5a3 3 0 0 1-3 3H13'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14v-2a8 8 0 0 1 16 0v2'/%3E%3Cpath d='M20 14.5a2 2 0 0 1 0 4h-1.5v-4z'/%3E%3Cpath d='M4 14.5a2 2 0 0 0 0 4h1.5v-4z'/%3E%3Cpath d='M18.5 18.5v.5a3 3 0 0 1-3 3H13'/%3E%3C/svg%3E");
}

#salesy-root .salesy-icon--question {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M9.2 9.2a3 3 0 0 1 5.8 1c0 2-2.9 2.6-2.9 4.3'/%3E%3Cpath d='M12 17.8h.01'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M9.2 9.2a3 3 0 0 1 5.8 1c0 2-2.9 2.6-2.9 4.3'/%3E%3Cpath d='M12 17.8h.01'/%3E%3C/svg%3E");
}

/* Start-over, in the header. */
#salesy-root .salesy-icon--plus {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
}

/* Send, in the composer. The accessible name stays on the button's aria-label. */
#salesy-root .salesy-icon--send {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19.5V5'/%3E%3Cpath d='M5.5 11.5 12 5l6.5 6.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19.5V5'/%3E%3Cpath d='M5.5 11.5 12 5l6.5 6.5'/%3E%3C/svg%3E");
}

/* The merchant's own image: a real <img>, so no mask and no tint. */
#salesy-root .salesy-icon--custom {
  width: 30px !important;
  height: 30px !important;
  border-radius: 6px !important;
  object-fit: contain;
}

/* -- Shared launcher chrome -----------------------------------------------
   In CORE, not in a shell, because the panel and the sidebar both have a
   floating launcher — and a class whose rules live in the other shell's
   stylesheet renders unstyled, which is how 0.17.0's close button reached a
   storefront with a hit area the size of its own glyph. The SIDE a shell docks
   to still belongs to that shell; only the look of the button is here. */

/* Physical sides — the panel opens on the same side as the button. */
#salesy-root.salesy-pos-right {
  right: var(--salesy-offset-side);
  align-items: flex-end;
}

#salesy-root.salesy-pos-left {
  left: var(--salesy-offset-side);
  align-items: flex-start;
}

#salesy-root .salesy-toggle {
  max-width: min(280px, calc(100vw - 2 * var(--salesy-offset-side))) !important;
  padding: 13px 20px !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  font-size: 14px !important;
  font-weight: 600 !important;
  box-shadow: 0 8px 22px -6px rgba(17, 24, 39, 0.45) !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

/* On the accent-filled button the ring goes inside, in the readable colour. */
#salesy-root .salesy-toggle:focus-visible {
  outline-color: var(--salesy-accent-text) !important;
  outline-offset: -4px !important;
}

/* Icon AND words (0.18.2). Still the pill — it has a label to hold — with the
   glyph set beside the text rather than replacing it. The glyph steps down
   from the 26px of a standalone launcher: at full size it competes with 14px
   type instead of introducing it. */
#salesy-root .salesy-toggle--withicon {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

#salesy-root .salesy-toggle--withicon .salesy-icon {
  flex: none;
  width: 18px;
  height: 18px;
}

/* The words, in their own element so the glyph can sit beside them. The pill's
   ellipsis rules live on the button; this only has to not fight them. */
#salesy-root .salesy-toggle-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* An icon button: square, so the pill's ellipsis rules do not apply. The
   accessible name has moved to aria-label — see the widget script. */
#salesy-root .salesy-toggle--iconic {
  display: grid;
  place-items: center;
  width: 56px !important;
  height: 56px !important;
  max-width: none !important;
  padding: 0 !important;
}

/* The launcher says "your conversation is still here" on a screen too narrow to
   reopen the panel over the page (see wideEnoughToReopen() in widget.js).

   Drawn as a box-shadow ring rather than a corner badge on purpose:
   .salesy-toggle sets `overflow: hidden` for its label ellipsis, which would
   clip a positioned dot on the circular --iconic variant. A shadow is painted
   outside the border box and is never clipped. Static, not animated — it is a
   standing state, not an event. */
#salesy-root .salesy-toggle--resume {
  box-shadow:
    0 8px 22px -6px rgba(17, 24, 39, 0.45),
    0 0 0 3px var(--salesy-surface),
    0 0 0 5px var(--salesy-accent) !important;
}

#salesy-root .salesy-icon {
  width: 26px;
  height: 26px;
  display: block;
}

/* Native WooCommerce sign-in, as a filled pill on the left. In CORE because
   the panel's header and the sidebar's both carry one. `margin-right:auto`
   is what anchors it there and pushes the controls to the far side; with no
   sign-in link the header's own `justify-content` does the same job. */
#salesy-root .salesy-signin {
  flex: none;
  margin-right: auto !important;
  padding: 8px 16px !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  text-decoration: none !important;
  box-shadow: var(--salesy-shadow-chip) !important;
  white-space: nowrap;
}

/* Icon buttons — every shell has them: the panel's header controls, the
   overlay's close and start-over. They live in CORE rather than in a shell
   because the class is shared, and a shell that used it without shipping these
   rules would render a button the element reset had stripped to a bare glyph:
   no box, no padding, no cursor, no hover, and a hit area the size of the
   character. That is not hypothetical — it is what the overlay shipped as in
   0.17.0, where it read as "the close button needs two clicks".
   One shape for all of them, so a row of them reads as a set. */
#salesy-root .salesy-iconbtn {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px !important;
  height: 34px !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: rgba(17, 24, 39, 0.05) !important;
  color: var(--salesy-muted) !important;
  font-size: 15px !important;
  line-height: 1 !important;
  cursor: pointer;
}

#salesy-root .salesy-iconbtn:hover {
  background: rgba(17, 24, 39, 0.1) !important;
  color: var(--salesy-ink) !important;
}

#salesy-root .salesy-iconbtn .salesy-icon {
  width: 16px;
  height: 16px;
}

/* `!important` on display alone: getting this wrong shows a panel that should
   be shut, and it is the one declaration in the file whose failure is total. */
#salesy-root .salesy-hidden {
  display: none !important;
}

/* The single links a shell offers beside its own close/new-chat icons: the
   dock's "Switch to floating chat", the chat button's way back to it, and the
   search bar's "Switch to normal search". Rules live here rather than in one
   shell's stylesheet for the same reason .salesy-iconbtn's do above — three
   shells render one of these each (ADR 0034 retired the row of pills these
   used to sit in, and with it `.salesy-switcher`; each of the three is now an
   independent link, built by the shell that shows it).

   Placed INSIDE a shell's own header row, to the LEFT of its icons, so it
   carries no padding or border of its own; `.salesy-header` /
   `.salesy-popup-bar` / `.salesy-sidebar-head` already supply the row's
   padding and alignment. */
#salesy-root .salesy-switcher-option {
  flex: none;
  padding: 6px 12px !important;
  border: 1px solid rgba(17, 24, 39, 0.15) !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: #fff !important;
  color: var(--salesy-ink) !important;
  font-size: 13px !important;
  line-height: 1.2 !important;
  cursor: pointer;
}

#salesy-root .salesy-switcher-option:hover {
  background: rgba(17, 24, 39, 0.05) !important;
}

/* Announces an interface switch to assistive technology without being seen —
   the standard clip-based visually-hidden recipe, scoped like everything else
   here. */
#salesy-root .salesy-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* min-height: 0 is load-bearing, not tidying: a flex item defaults to
   min-height: auto, so without it the log refuses to shrink below its own
   content and the rows below push it out of the panel instead of scrolling. */
#salesy-root .salesy-log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 20px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  scrollbar-width: thin;
}

/* The opening screen centres itself in an otherwise empty log. The class is
   dropped the moment real content arrives (makeBubble / showReplyCards), so a
   conversation is never centred — only the invitation to start one is.

   Centred with AUTO MARGINS on the item, not `justify-content: center` on the
   container, and that is not a style preference: a centred flex item that
   overflows its scroll container overflows at BOTH ends, and the part above the
   top edge is unreachable — `scrollTop: 0` is already the top. Measured on the
   520x400 panel with a long merchant greeting: the copy was off-screen above
   the fan with no way to scroll to it. Auto margins resolve to zero once there
   is no free space, so an over-tall opening screen starts at the top and
   scrolls normally. */
#salesy-root .salesy-log--hero .salesy-intro {
  margin-block: auto;
}

/* -- The opening screen ---------------------------------------------------
   Two variants of one block. HERO is the fresh panel: a large opening line
   over the merchant's copy, with the collage fanned beside it. COMPACT is a
   resumed conversation, where the same lines are the first thing that was said
   and must look like it — a bubble, at bubble size, with no collage. That split
   already existed (renderGreeting vs. restoreLog) and this keeps it. */
#salesy-root .salesy-intro {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 18px;
}

#salesy-root .salesy-intro-copy {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

#salesy-root .salesy-intro-hello {
  margin: 0 !important;
  font-size: 15px !important;
  font-weight: 600 !important;
  line-height: 1.45 !important;
  color: var(--salesy-ink) !important;
}

#salesy-root .salesy-intro-line {
  margin: 0 !important;
  font-size: 15px !important;
  font-weight: 600 !important;
  line-height: 1.45 !important;
  color: var(--salesy-ink) !important;
  /* The last-order line and the async personalization lines still join with
     "\n" inside one paragraph, exactly as they did in the bubble. */
  white-space: pre-wrap;
  word-break: break-word;
}

#salesy-root .salesy-intro--hero .salesy-intro-hello {
  font-size: 30px !important;
  font-weight: 400 !important;
  line-height: 1.15 !important;
  color: var(--salesy-faint) !important;
}

/* A resumed conversation: the same lines, drawn as the agent bubble they are. */
#salesy-root .salesy-intro--compact .salesy-intro-copy {
  align-self: flex-start;
  max-width: 85%;
  padding: 10px 14px;
  background: var(--salesy-surface);
  border: 1px solid var(--salesy-border);
  border-radius: var(--salesy-radius-bubble) var(--salesy-radius-bubble)
    var(--salesy-radius-bubble) 6px;
  box-shadow: var(--salesy-shadow-chip);
  gap: 0;
}

/* Two columns once the PANEL is wide enough for them — not the viewport, since
   the width is a merchant preset. Below the threshold, and in any browser
   without container queries, the block stays stacked, which is the right shape
   for the full-screen phone layout where height is plentiful and width is not.

   480px, so EVERY floating preset splits — including Compact at 520px. Stacked,
   the panel has to find room for the copy PLUS the fan; side by side it needs
   only the taller of the two, and Compact is 400px tall in total. */
@container (min-width: 480px) {
  #salesy-root .salesy-intro--hero {
    flex-direction: row;
    align-items: center;
    gap: 18px;
  }

  #salesy-root .salesy-intro--hero .salesy-intro-copy {
    flex: 1 1 52%;
  }

  #salesy-root .salesy-intro--hero .salesy-collage {
    flex: 1 1 48%;
    height: 150px;
  }

  /* Compact's copy is ~250px wide. At the hero's full size the greeting alone
     ran taller than the whole log, so the type steps down with the panel — the
     opening line still reads as the opening line, just not at 30px. */
  #salesy-root .salesy-intro--hero .salesy-intro-hello {
    font-size: 22px !important;
  }

  #salesy-root .salesy-intro--hero .salesy-intro-line {
    font-size: 14px !important;
  }
}

/* Standard (680) and Tall (720): room for the third card and for the type to
   come back up. */
@container (min-width: 620px) {
  #salesy-root .salesy-intro--hero {
    gap: 24px;
  }

  #salesy-root .salesy-intro--hero .salesy-intro-copy {
    flex: 1 1 44%;
  }

  #salesy-root .salesy-intro--hero .salesy-collage {
    flex: 1 1 56%;
    height: 190px;
  }

  #salesy-root .salesy-intro--hero .salesy-intro-hello {
    font-size: 30px !important;
  }

  #salesy-root .salesy-intro--hero .salesy-intro-line {
    font-size: 15px !important;
  }
}

/* Expanded (1040): the fan gets the room the reference gives it. */
@container (min-width: 900px) {
  #salesy-root .salesy-intro--hero .salesy-collage {
    height: 250px;
  }
}

/* -- The collage fan ------------------------------------------------------
   The merchant's own products, identity-only, before any turn has happened —
   so no price, no stock, nothing live (see shape_collage in Widget.php).

   Every offset and every rotation is a FIXED class: widget.js is forbidden
   from writing a style attribute, so the fan cannot be composed at runtime.

   Every `top`, `left` AND `height` is a percentage, which is what makes the fan
   safe at four panel widths from one geometry: an absolutely positioned card
   resolves a percentage height against `.salesy-collage`, whose height IS
   definite, so `top + height <= 100%` holds by construction and no card can
   fall out of the frame however wide the column gets. The image then takes
   whatever the caption leaves (`flex: 1; min-height: 0`) rather than dictating
   the card's height from its own aspect ratio — which is what used to push the
   bottom card past the frame on the small panel.

   Two cards by default, three from 620px up: at Compact the column is ~230px
   and a third card would be a sliver. The third slot is hidden, not
   unrendered — which panel size is on screen is a runtime fact (expand/restore)
   and CSS is the only thing here that sees it. */
#salesy-root .salesy-collage {
  position: relative;
  flex: none;
  height: 190px;
  overflow: hidden;
}

#salesy-root .salesy-collage-card {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 48% !important;
  /* `!important` because a card is an <a> and the reset forces `height: auto`
     on one — and `!important` beats specificity, so a plain `height: 84%` here
     lost silently. The card then sized to its content, the image fell back to
     its own aspect ratio, and the title was pushed out of the clipped frame.
     Every forced property set on a reset element type needs this; see the
     stylesheet header, and the test that now sweeps for it. */
  height: 84% !important;
  padding: 8px !important;
  background: var(--salesy-surface) !important;
  border: 1px solid rgba(17, 24, 39, 0.04) !important;
  border-radius: var(--salesy-radius-card) !important;
  color: var(--salesy-ink) !important;
  text-decoration: none !important;
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease !important;
}

#salesy-root .salesy-collage-card--a {
  left: 4%;
  top: 9%;
  z-index: 1;
  transform: rotate(-8deg) !important;
}

/* The one in front, and the largest — the reference's centre card. */
#salesy-root .salesy-collage-card--b {
  left: 34%;
  top: 0;
  height: 100% !important;
  z-index: 3;
  width: 52% !important;
  transform: rotate(2deg) !important;
}

/* Hidden below 620px: see the container queries above. */
#salesy-root .salesy-collage-card--c {
  display: none;
  left: 58%;
  top: 13%;
  z-index: 2;
  width: 46% !important;
  transform: rotate(8deg) !important;
}

/* Three cards from 620px up: the third appears and the two beside it pull in to
   make room. Kept here, beside the slot rules it overrides, rather than up with
   the intro's own container queries — this is fan geometry, and reading it in
   two places is how one of these offsets ends up contradicting the other. */
@container (min-width: 620px) {
  #salesy-root .salesy-collage-card--a {
    left: 0;
    width: 42% !important;
  }

  #salesy-root .salesy-collage-card--b {
    left: 26%;
    width: 46% !important;
  }

  #salesy-root .salesy-collage-card--c {
    display: flex;
  }
}

#salesy-root .salesy-collage-card:hover,
#salesy-root .salesy-collage-card:focus-visible {
  z-index: 4;
  transform: rotate(0deg) translateY(-4px) !important;
}

/* Takes what the caption leaves rather than claiming a height of its own from
   an aspect ratio — that is what keeps the card inside its slot. */
#salesy-root .salesy-collage-card-image {
  width: 100% !important;
  height: auto !important;
  border-radius: 10px !important;
  flex: 1;
  min-height: 0 !important;
  object-fit: cover;
}

#salesy-root .salesy-collage-card-title {
  font-size: 11px;
  line-height: 1.3;
  color: var(--salesy-muted);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* Asked for less motion: the fan is still a fan, it just stops moving. */
@media (prefers-reduced-motion: reduce) {
  #salesy-root .salesy-collage-card {
    transition: none !important;
  }

  #salesy-root .salesy-collage-card:hover,
  #salesy-root .salesy-collage-card:focus-visible {
    transform: none !important;
  }
}

#salesy-root .salesy-msg {
  max-width: 85%;
  padding: 10px 14px;
  border-radius: var(--salesy-radius-bubble);
  font-size: 14px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}

#salesy-root .salesy-msg-user {
  align-self: flex-end;
  background: var(--salesy-accent);
  color: var(--salesy-accent-text);
  border-bottom-right-radius: 6px;
}

/* White on the warm canvas, rather than the old grey-on-white: on this ground
   a muted fill is invisible, so the bubble is a card instead. */
#salesy-root .salesy-msg-agent {
  align-self: flex-start;
  background: var(--salesy-surface);
  color: var(--salesy-ink);
  border: 1px solid var(--salesy-border);
  box-shadow: var(--salesy-shadow-chip);
  border-bottom-left-radius: 6px;
}

#salesy-root .salesy-msg-info {
  align-self: center;
  background: transparent;
  color: var(--salesy-muted);
  font-size: 12px;
}

/* The pending bubble is a ROW while a turn is in flight (0.24.0, ADR 0035):
   four dots that keep moving for the whole turn, and one line of words beside
   them saying what the assistant is actually doing. The words are replaced as
   the turn moves on; they never accumulate.

   The bubble no longer breathes as a whole. It used to, because a motionless
   "…" was all there was — but two overlapping opacity animations read as a
   flicker, and a throbbing step label is unreadable. The dots carry the motion
   now, and `.salesy-pending` stays purely as the layout and state hook.

   Untouched by the reset: a bubble is a div and the dots are spans, and neither
   is in the enumerated element list above. Nothing here needs `!important`, and
   nothing here may be moved onto an element type that is in that list. */
#salesy-root .salesy-pending {
  display: flex;
  align-items: center;
  gap: 8px;
}

#salesy-root .salesy-dots {
  display: grid;
  grid-template-columns: repeat(2, 6px);
  grid-auto-rows: 6px;
  gap: 4px;
  /* The card-row lesson: never let it shrink to nothing beside flexible text. */
  flex: none;
}

#salesy-root .salesy-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  /* The merchant's own brand colour — as INK on the pale canvas, so it comes
     through `accentInk()` rather than `--salesy-accent` directly: a near-white
     brand colour would otherwise be four dots nobody can see. The lighter dots
     are the SAME colour at a lower alpha, so there is one value to validate,
     no second setting, and no color-mix() (Safari only learned it in 16.2). */
  background: var(--salesy-accent-ink);
  animation: salesy-dot-pulse 1.2s ease-in-out infinite;
}

@keyframes salesy-dot-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.25;
  }
}

/* Two diagonals, half a period apart, so at every instant one pair is strong
   and the other faint. A NEGATIVE delay starts the second pair mid-cycle
   instead of leaving it dead for the first 0.6s. */
#salesy-root .salesy-dot--tr,
#salesy-root .salesy-dot--bl {
  animation-delay: -0.6s;
}

/* Asked for less motion: the same four dots, held at the still frame. */
@media (prefers-reduced-motion: reduce) {
  #salesy-root .salesy-dot {
    animation: none;
  }

  #salesy-root .salesy-dot--tr,
  #salesy-root .salesy-dot--bl {
    opacity: 0.35;
  }
}

/* The synthetic reveal (0.24.0). Every text run in a reply is already its own
   span, so this is one class per word and no second renderer — and spans are
   outside the reset, so nothing here needs `!important`.

   `opacity`, deliberately not `display: none`. The bubble takes its final size
   from the first frame and the words fade in place, which means the COMPLETE
   text is in the aria-live log in one announcement instead of a hundred
   stuttering partial ones, nothing reflows, and the log's existing
   scroll-on-insert is enough in both log directions.

   Scoped to `.salesy-revealing` so a RESTORED reply — which never gets that
   class — is fully visible with no per-word cleanup, and so finishing early is
   removing one class rather than writing to every word. */
#salesy-root .salesy-revealing .salesy-word {
  opacity: 0;
}

#salesy-root .salesy-revealing .salesy-word--in {
  opacity: 1;
  transition: opacity 120ms linear;
}

/* Asked for less motion: the reply simply arrives. Belt and braces — the script
   checks the same query and never starts a reveal — but a stylesheet that
   somehow shipped without the script's half must not leave a shopper looking at
   an invisible reply. */
@media (prefers-reduced-motion: reduce) {
  #salesy-root .salesy-revealing .salesy-word {
    opacity: 1;
    transition: none;
  }
}

/* The reply's markdown subset. Deliberately plain: the theme's own typography
   should not be fought, only the browser's default block margins — and now the
   theme's own block margins too, which the reset has already removed. */
#salesy-root .salesy-msg .salesy-p,
#salesy-root .salesy-msg .salesy-h,
#salesy-root .salesy-msg .salesy-list {
  margin: 0 0 6px !important;
}

#salesy-root .salesy-msg .salesy-p:last-child,
#salesy-root .salesy-msg .salesy-h:last-child,
#salesy-root .salesy-msg .salesy-list:last-child {
  margin-bottom: 0 !important;
}

#salesy-root .salesy-msg .salesy-h {
  font-size: 14px !important;
  font-weight: 600 !important;
  line-height: 1.3 !important;
}

#salesy-root .salesy-msg h5.salesy-h {
  font-size: 13px !important;
}

#salesy-root .salesy-msg .salesy-list {
  padding-left: 20px !important;
}

/* The reset clears `list-style` so a theme's own bullet cannot arrive; these
   put the widget's own back. Higher specificity than the reset, so no
   `!important` is needed and none is used. */
#salesy-root ul.salesy-list {
  list-style: disc outside;
}

#salesy-root ol.salesy-list {
  list-style: decimal outside;
}

#salesy-root .salesy-msg .salesy-code {
  padding: 1px 4px !important;
  border-radius: 4px !important;
  background: rgba(0, 0, 0, 0.06) !important;
  font-family: monospace !important;
  font-size: 12px !important;
  word-break: break-word;
}

#salesy-root .salesy-msg .salesy-link {
  color: inherit !important;
  text-decoration: underline !important;
}

/* Product cards: a horizontally scrollable row; each card is one link. The
   row is focusable so it scrolls with the keyboard, and one card looks and
   behaves exactly like six. */
/* `flex: none` is load-bearing, for a reason that is easy to miss: this row is
   a flex item of the column-flex .salesy-log, and `overflow-y: hidden` means its
   automatic minimum size resolves to ZERO rather than to its content
   (CSS Flexbox §4.5 — min-height:auto only tracks content when overflow is
   visible). With the default `flex-shrink: 1`, the moment the conversation grew
   past the panel the whole card row was squashed to 0px high and its titles
   spilled out of it — measured on a real 300x380 panel: row height 6px, card
   height 0px, with the titles still in the DOM.

   The chip row already carries `flex: none` for the mirror-image reason. Same
   rule, same fix: rows inside or beside a scrollable log keep their own height
   and the LOG is what scrolls. */
#salesy-root .salesy-cards {
  display: flex;
  flex: none;
  flex-direction: row;
  gap: 10px;
  padding: 2px 0 8px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

#salesy-root .salesy-cards:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

#salesy-root .salesy-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 0 0 148px;
  padding: 8px !important;
  border: 1px solid rgba(17, 24, 39, 0.04) !important;
  border-radius: var(--salesy-radius-card) !important;
  background: var(--salesy-surface) !important;
  box-shadow: var(--salesy-shadow-card) !important;
  color: var(--salesy-ink) !important;
  text-decoration: none !important;
  transition:
    transform 0.16s ease,
    box-shadow 0.16s ease !important;
  scroll-snap-align: start;
}

#salesy-root .salesy-card:hover {
  transform: translateY(-2px) !important;
  box-shadow: 0 12px 24px -8px rgba(17, 24, 39, 0.28) !important;
}

@media (prefers-reduced-motion: reduce) {
  #salesy-root .salesy-card,
  #salesy-root .salesy-card:hover {
    transform: none !important;
    transition: none !important;
  }
}

#salesy-root .salesy-card-image {
  width: 100% !important;
  height: 108px !important;
  border-radius: 10px !important;
  flex: none;
  object-fit: cover;
}

#salesy-root .salesy-card-title {
  font-size: 13px;
  line-height: 1.3;
  word-break: break-word;
}

/* Live facts: current price, the original struck through when on sale, the
   variation this price belongs to, and stock. */
#salesy-root .salesy-card-prices {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}

#salesy-root .salesy-card-price {
  font-size: 13px;
  font-weight: 600;
}

/* An <s>, so the reset stripped its line-through: put it back, or the "was"
   price reads as a second current price. */
#salesy-root .salesy-card-was {
  font-size: 11px !important;
  text-decoration: line-through !important;
  color: var(--salesy-muted) !important;
}

#salesy-root .salesy-card-variant {
  font-size: 11px;
  color: var(--salesy-muted);
  word-break: break-word;
}

#salesy-root .salesy-card-stock {
  font-size: 11px;
  font-weight: 600;
  color: var(--salesy-muted);
}

/* A single large-format product photo (ADR 0044) — a shopper's own
   "show me a picture", answered. Its own block, not a card: no price, no
   variant, and never more than one per turn, so it needs none of
   .salesy-cards' horizontal-scroll machinery. */
#salesy-root .salesy-photo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  max-width: 260px;
  margin: 4px 0 8px;
}

#salesy-root .salesy-photo-button {
  display: block;
  width: 100% !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-card) !important;
  background: var(--salesy-surface) !important;
  box-shadow: var(--salesy-shadow-card) !important;
  overflow: hidden;
  cursor: zoom-in;
}

#salesy-root .salesy-photo-image {
  display: block;
  width: 100% !important;
  height: auto !important;
  max-width: none !important;
  max-height: 260px !important;
  object-fit: cover;
}

/* The caption is the product-page link the photo bubble itself does not
   carry — clicking the picture opens the lightbox, clicking this goes to
   the store. An <a>, so the reset's own text-decoration/color removal is
   restored here, same specificity rule as `strong`/`em` above: position in
   the file is what decides it. */
#salesy-root .salesy-photo-caption {
  font-size: 13px !important;
  color: var(--salesy-ink) !important;
  text-decoration: underline !important;
  word-break: break-word;
}

/* The fullscreen lightbox a photo bubble opens into. Fixed to the
   VIEWPORT, not the panel — a real modal sits above everything on the
   page, which is why this is the one thing in the widget that does not
   stay inside the panel's own stacking context.

   `position` is deliberately NEVER `!important` anywhere in this file (see
   the reset's own comment above) — the panel and the collage cards depend
   on setting it themselves, and forcing it here would be the same defect
   for a different element. A plain `position: fixed` still wins: nothing
   else in this stylesheet declares `position` for `.salesy-lightbox`/
   `.salesy-lightbox-close`, so there is nothing for it to lose to. */
#salesy-root .salesy-lightbox {
  position: fixed;
  inset: 0;
  z-index: 2147483647;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(17, 24, 39, 0.85) !important;
}

#salesy-root .salesy-lightbox-image {
  max-width: 90% !important;
  max-height: 90% !important;
  width: auto !important;
  height: auto !important;
  object-fit: contain;
  border-radius: 4px !important;
}

/* Same .salesy-iconbtn base as the panel's own close button, overridden for
   a dark backdrop rather than the panel's pale surface — position in the
   FILE is what wins the specificity tie, exactly as every other override of
   a shared class in this file does. Fixed (not absolute): the lightbox
   itself is fixed to the viewport, and this sits with it regardless of
   which element in the DOM tree happens to be positioned. */
#salesy-root .salesy-lightbox-close {
  position: fixed;
  top: 16px;
  right: 16px;
  background: rgba(255, 255, 255, 0.15) !important;
  color: #fff !important;
  font-size: 22px !important;
}

#salesy-root .salesy-lightbox-close:hover {
  background: rgba(255, 255, 255, 0.28) !important;
  color: #fff !important;
}

/* Quick-reply chips: one horizontally scrolling strip, mirroring .salesy-cards.
   `flex: none` plus `flex-wrap: nowrap` is what keeps this row exactly one chip
   tall — wrapping chips used to grow downward without limit and squeeze the log
   to nothing on a compact panel. Like the card row, it is focusable so the
   overflow scrolls by keyboard. */
#salesy-root .salesy-chips {
  display: flex;
  flex: none;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 0 15px 10px;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

#salesy-root .salesy-chips:empty {
  display: none;
}

#salesy-root .salesy-chips:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

/* `white-space: nowrap` is what keeps a long label on one line, and therefore
   what keeps one chip from making the row taller — together with `flex: none`
   on the row above. That is the whole height fix, and it stays.

   No `max-width` and no ellipsis. 0.5.0 added both while bounding the height,
   but they bound the WIDTH, which horizontal scrolling already solves — and
   measured on a compact panel they clipped every real label
   ("Tell me more about Floati…", client 179px vs content 226px). A chip's text
   is sent verbatim as the shopper's next message, so a chip that cannot show
   its own text is a button whose effect the shopper cannot read. */
#salesy-root .salesy-chip {
  flex: 0 0 auto;
  padding: 8px 14px !important;
  border: 1px solid var(--salesy-border-strong) !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-surface) !important;
  color: var(--salesy-ink) !important;
  box-shadow: var(--salesy-shadow-chip) !important;
  font-size: 13px !important;
  text-align: start !important;
  transition:
    transform 0.14s ease,
    border-color 0.14s ease !important;
  white-space: nowrap;
  scroll-snap-align: start;
  cursor: pointer;
}

#salesy-root .salesy-chip:hover {
  border-color: var(--salesy-accent) !important;
}

@media (prefers-reduced-motion: reduce) {
  #salesy-root .salesy-chip,
  #salesy-root .salesy-chip:hover {
    transform: none !important;
    transition: none !important;
  }
}

/* Said once, quietly, above the suggestions: the replies are written by a
   model. Empty translation = no row, which is why it is its own element rather
   than a line inside another. */
#salesy-root .salesy-disclaimer {
  flex: none;
  padding: 0 20px 6px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--salesy-muted);
}

#salesy-root .salesy-disclaimer:empty {
  display: none;
}

/* Cart summary: one non-growing row between the log and the chips, holding only
   what WooCommerce returned in the response that changed the cart. `flex: none`
   for the same reason the card and chip rows carry it. Empty = invisible, which
   is also what a page load starts with, since nothing about it is stored.
   `flex-wrap: wrap` is new in 0.10.0 (ADR 0021): it is what lets the itemized
   rows below fall onto their own line without disturbing this one — an older
   backend with no `items` renders exactly as it always has. */
#salesy-root .salesy-cart {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  padding: 0 20px 8px;
  font-size: 12px;
  color: var(--salesy-ink);
}

#salesy-root .salesy-cart:empty {
  display: none;
}

#salesy-root .salesy-cart-total {
  font-weight: 600;
}

#salesy-root .salesy-cart-link {
  margin-left: auto !important;
  color: var(--salesy-accent) !important;
  text-decoration: underline !important;
  white-space: nowrap;
}

/* Itemized cart rows (0.10.0, ADR 0021). `flex: 1 0 100%` is the whole trick:
   inside `.salesy-cart`'s wrapping flex row, an item asking for the full
   width leaves no room beside it, so it always starts a new line under the
   count/total/link summary rather than squeezing in beside them. */
#salesy-root .salesy-cart-items {
  flex: 1 0 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 2px;
}

#salesy-root .salesy-cart-item {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--salesy-ink) !important;
  text-decoration: none !important;
}

#salesy-root .salesy-cart-item-image {
  width: 32px !important;
  height: 32px !important;
  border-radius: 8px !important;
  flex: none;
  object-fit: cover;
}

#salesy-root .salesy-cart-item-details {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

#salesy-root .salesy-cart-item-title {
  font-size: 12px;
  line-height: 1.3;
  word-break: break-word;
}

#salesy-root .salesy-cart-item-variant {
  font-size: 11px;
  color: var(--salesy-muted);
  word-break: break-word;
}

#salesy-root .salesy-cart-item-line {
  font-size: 11px;
  color: var(--salesy-muted);
}

/* Delivery panel. It sits between the log and the composer, scrolls on its own
   when the store's checkout asks for more than the panel is tall, and does not
   grow — the log above it keeps its own room, which is what makes this usable at
   the compact size rather than only at 1080p.

   Every control here is a NATIVE input. No control is re-implemented, so a
   shopper's keyboard, their autofill and their screen reader behave exactly as
   they do on the store's own checkout page — and that is also why the reset
   above leaves `appearance` alone on a select and a radio. */
#salesy-root .salesy-delivery {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 20px;
  border-top: 1px solid var(--salesy-border);
  background: var(--salesy-surface-muted);
}

#salesy-root .salesy-delivery-title {
  margin: 0 0 4px !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  color: var(--salesy-ink) !important;
}

#salesy-root .salesy-delivery-intro,
#salesy-root .salesy-delivery-note {
  margin: 0 0 8px !important;
  font-size: 11px !important;
  line-height: 1.4 !important;
  color: var(--salesy-muted) !important;
}

#salesy-root .salesy-delivery-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 8px;
}

#salesy-root .salesy-delivery-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--salesy-ink);
}

#salesy-root .salesy-delivery-optional {
  font-weight: 400;
  color: var(--salesy-muted);
}

/* Deliberately NOT `outline: none`. The composer input carries it from 0.3.0
   and is a single obvious target; a form of eight fields completed by keyboard
   is the one place a missing focus ring is disqualifying. */
#salesy-root .salesy-delivery-input {
  width: 100% !important;
  padding: 7px 9px !important;
  border: 1px solid var(--salesy-border-strong) !important;
  border-radius: 8px !important;
  background: var(--salesy-surface) !important;
  color: var(--salesy-ink) !important;
  font-size: 13px !important;
  box-sizing: border-box;
}

#salesy-root .salesy-delivery-input:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 1px !important;
}

#salesy-root .salesy-delivery-help {
  margin: 0 !important;
  font-size: 11px !important;
  color: var(--salesy-muted) !important;
}

/* The verdict is carried by the WORDS; the colour is an addition, never a
   replacement — the same rule the admin status rows follow. Empty = invisible,
   so a field with no error takes no vertical space. */
#salesy-root .salesy-delivery-error {
  margin: 0 !important;
  font-size: 11px !important;
  color: #b32d2e !important;
}

#salesy-root .salesy-delivery-error:empty {
  display: none;
}

#salesy-root .salesy-delivery-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

#salesy-root .salesy-delivery-submit {
  flex: none;
  padding: 8px 14px !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  cursor: pointer;
}

#salesy-root .salesy-delivery-submit:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

#salesy-root .salesy-delivery-cancel {
  flex: none;
  padding: 8px 4px !important;
  background: none !important;
  color: var(--salesy-muted) !important;
  font-size: 13px !important;
  text-decoration: underline !important;
  cursor: pointer;
}

/* Sign-up panel (ADR 0036). Deliberately the delivery panel's own metrics,
   selector for selector: it sits in the same slot, on the same muted band,
   and a shopper who has seen one form in this window should not have to read
   a second visual language to fill in the other. Only two rules are its own —
   the sign-in link, which the delivery panel has no equivalent of. */
#salesy-root .salesy-account {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 20px;
  border-top: 1px solid var(--salesy-border);
  background: var(--salesy-surface-muted);
}

#salesy-root .salesy-account-title {
  margin: 0 0 4px !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  color: var(--salesy-ink) !important;
}

#salesy-root .salesy-account-intro,
#salesy-root .salesy-account-note {
  margin: 0 0 8px !important;
  font-size: 11px !important;
  line-height: 1.4 !important;
  color: var(--salesy-muted) !important;
}

#salesy-root .salesy-account-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 8px;
}

#salesy-root .salesy-account-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--salesy-ink);
}

#salesy-root .salesy-account-optional {
  font-weight: 400;
  color: var(--salesy-muted);
}

#salesy-root .salesy-account-input {
  width: 100% !important;
  padding: 7px 9px !important;
  border: 1px solid var(--salesy-border-strong) !important;
  border-radius: 8px !important;
  background: var(--salesy-surface) !important;
  color: var(--salesy-ink) !important;
  font-size: 13px !important;
  box-sizing: border-box;
}

#salesy-root .salesy-account-input:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 1px !important;
}

#salesy-root .salesy-account-help {
  margin: 0 !important;
  font-size: 11px !important;
  color: var(--salesy-muted) !important;
}

/* Same rule as the delivery panel's: the verdict is carried by the WORDS and
   the colour is an addition. Empty = invisible, so a valid field costs no
   vertical space. */
#salesy-root .salesy-account-error {
  margin: 0 !important;
  font-size: 11px !important;
  color: #b32d2e !important;
}

#salesy-root .salesy-account-error:empty {
  display: none;
}

#salesy-root .salesy-account-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

#salesy-root .salesy-account-submit {
  flex: none;
  padding: 8px 14px !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  cursor: pointer;
}

#salesy-root .salesy-account-submit:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

/* "No thanks" is as reachable as "Create my account" and looks like a real
   choice rather than a dismissal, because declining is the answer this panel
   has to take gracefully — the cart worked without it. */
#salesy-root .salesy-account-cancel {
  flex: none;
  padding: 8px 4px !important;
  background: none !important;
  color: var(--salesy-muted) !important;
  font-size: 13px !important;
  text-decoration: underline !important;
  cursor: pointer;
}

/* The one thing this panel has that the delivery panel does not: the way out
   of "you already have an account", rendered in the log as a real link. It
   reads as the accent-coloured action it is, because it is the next step for a
   shopper who cannot finish the form. */
#salesy-root .salesy-account-signin {
  display: flex;
}

#salesy-root .salesy-account-signin-link {
  color: var(--salesy-accent-ink) !important;
  font-size: 12px !important;
  font-weight: 600 !important;
  text-decoration: underline !important;
}

/* The same way out as a CHOICE, for a shopper the duplicate email stopped on
   their way to checkout. Wraps rather than shrinks: two real labels at the
   panel's narrowest width belong on two lines, not squeezed onto one. */
#salesy-root .salesy-account-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Signing in is the suggested way, so it carries the accent — the same
   treatment "Create my account" had in the form that has just closed. It is an
   anchor, so the text decoration and colour a theme would otherwise give it
   have to be stated. */
#salesy-root .salesy-account-choice-signin {
  flex: none;
  padding: 8px 14px !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  text-decoration: none !important;
}

/* Carrying on as a guest is as reachable as signing in and looks like a real
   choice rather than a dismissal — the same reasoning "No thanks" is styled
   on, and for the same reason: the shopper owes this store no account. */
#salesy-root .salesy-account-choice-guest {
  flex: none;
  padding: 8px 4px !important;
  border: 0 !important;
  background: none !important;
  color: var(--salesy-muted) !important;
  font-size: 13px !important;
  text-decoration: underline !important;
  cursor: pointer;
}

#salesy-root .salesy-account-choice-signin:focus-visible,
#salesy-root .salesy-account-choice-guest:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 2px !important;
}

/* Delivery summary: the same posture as the cart summary — one non-growing row
   holding only what WooCommerce returned in the response that produced it, and
   invisible when empty, which is what every page load starts as. */
#salesy-root .salesy-deliverysummary {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 2px;
  padding: 0 20px 8px;
  font-size: 12px;
  color: var(--salesy-ink);
}

#salesy-root .salesy-deliverysummary:empty {
  display: none;
}

#salesy-root .salesy-deliverysummary-title {
  font-weight: 600;
}

#salesy-root .salesy-deliverysummary-options {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

#salesy-root .salesy-deliverysummary-option {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

#salesy-root .salesy-deliverysummary-radio:focus-visible {
  outline: 2px solid var(--salesy-accent) !important;
  outline-offset: 1px !important;
}

#salesy-root .salesy-deliverysummary-optionlabel {
  cursor: pointer;
}

#salesy-root .salesy-deliverysummary-total {
  font-weight: 600;
}

#salesy-root .salesy-deliverysummary-total:empty {
  display: none;
}

#salesy-root .salesy-deliverysummary-checkout {
  align-self: flex-start;
  color: var(--salesy-accent) !important;
  text-decoration: underline !important;
  white-space: nowrap;
}

/* The composer IS the pill: one rounded field with the send button inside it,
   rather than an edge-to-edge input with a square button bolted to its side.
   The focus ring is on the whole field (focus-within), which is what lets the
   input itself keep `outline: 0` as it has since 0.3.0. */
#salesy-root .salesy-form {
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
  margin: 2px 16px 16px !important;
  padding: 6px 6px 6px 18px !important;
  background: var(--salesy-surface) !important;
  border: 1px solid var(--salesy-border-strong) !important;
  border-radius: var(--salesy-radius-pill) !important;
  box-shadow: var(--salesy-shadow-chip) !important;
}

#salesy-root .salesy-form:focus-within {
  border-color: var(--salesy-accent) !important;
  box-shadow: var(--salesy-shadow-card) !important;
}

#salesy-root .salesy-input {
  flex: 1;
  min-width: 0 !important;
  min-height: 42px !important;
  padding: 0 !important;
  border: 0 !important;
  background: none !important;
  color: var(--salesy-ink) !important;
  font-size: 15px !important;
  outline: 0 !important;
}

#salesy-root .salesy-send {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px !important;
  height: 38px !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: var(--salesy-radius-pill) !important;
  background: var(--salesy-accent) !important;
  color: var(--salesy-accent-text) !important;
  cursor: pointer;
}

#salesy-root .salesy-send .salesy-icon {
  width: 18px;
  height: 18px;
}

#salesy-root .salesy-send:focus-visible {
  outline-color: var(--salesy-accent-text) !important;
  outline-offset: -4px !important;
}
