.ww-widget {
  --ww-button-color: #25d366;
  /* The WhatsApp mark only. It used to colour the label, the close X and the
     panel heading too, so a merchant could not put a dark label on a light
     button without darkening the logo along with it. */
  --ww-icon-color: #ffffff;
  /* The button's wording, separate from the mark beside it. */
  --ww-button-text: #ffffff;
  /* Everything written on a coloured surface: the button label, the close X,
     the contact menu heading and the bar's close button. */
  --ww-title-color: #ffffff;
  --ww-font-weight: 700;
  --ww-size: 56px;
  --ww-bottom: 24px;
  --ww-side: 24px;
  --ww-panel-width: 380px;
  --ww-popup-background: #ffffff;
  --ww-popup-text: #17201b;
  --ww-popup-muted: #516159;
  --ww-popup-accent: #25d366;
  --ww-popup-hover: #f6faf5;
  --ww-popup-border: #dde7de;
  --ww-bar-max-width: 480px;
  /* The ask-a-question bar is coloured by its own three settings only: the
     outline colour, the animation colour and the button colour. It used to
     borrow the contact menu's six panel colours, which meant recolouring the
     menu silently recoloured the bar. The surface and ink below are fixed
     rather than settings: the bar is a message field, and merchants asked for
     three knobs, not nine. Values match the old popup defaults exactly, so
     detaching them changed nothing on screen. */
  --ww-bar-surface: #ffffff;
  --ww-bar-surface-alt: #f6faf5;
  --ww-bar-ink: #17201b;
  --ww-bar-ink-muted: #516159;
  /* Accent and border both track the merchant's outline colour, so the one
     setting they were given actually reaches every edge of the experience. */
  --ww-bar-outline: #25d366;
  /* Chosen for the look. Note it sits at 2.48:1 on the white field, under the
     4.5:1 WCAG AA threshold for text. WhatsApp's own placeholder gray
     (#667781) is the nearest accessible match at 4.65:1 if that ever needs
     to change. */
  --ww-placeholder: #9aa8a0;
  /* Solid WhatsApp green with a white label, the same pairing the round
     button already uses. Note white on #25d366 measures 1.98:1, so it leans
     on brand recognition rather than contrast; #075e54 on #dcf8c6 was the
     accessible alternative at 6.68:1. */
  /* The hairline used across the whole ask-a-question experience: the field,
     the card, the chips, the chooser rows and the secondary buttons all share
     it, so the green line reads as one system rather than per-element trim.
     Kept faint on purpose; the resting border should be barely there. */
  --ww-hairline: rgba(37, 211, 102, 0.22);
  --ww-hairline-strong: rgba(37, 211, 102, 0.4);
  --ww-bar-hover: rgba(37, 211, 102, 0.12);
  --ww-glow-color: #25d366;
  --ww-bar-send-color: #25d366;
  --ww-qr-web-button: #25d366;
  --ww-qr-button-text: #ffffff;

  position: fixed;
  right: var(--ww-side);
  /* --ww-keyboard-lift is set by the JS from the visual viewport while the
     field has focus, so the bar rides above the on-screen keyboard instead of
     sitting behind it. It is 0 everywhere else. */
  bottom: calc(var(--ww-bottom) + var(--ww-keyboard-lift, 0px));
  z-index: 2147483000;
  font-family: inherit;
}

.ww-widget[data-position="bottom-left"]:not([data-bar-active="true"]) {
  right: auto;
  left: var(--ww-side);
}

/* The bar is centred; a round button keeps the merchant's corner. Keyed on the
   mode rather than a synthetic position value, because the mode differs by
   viewport and the position setting does not. */
.ww-widget[data-button-mode="input_bar"] {
  right: auto;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--ww-bar-max-width), calc(100vw - var(--ww-side) * 2));
}

.ww-widget__button {
  /* Anchors the attract pulse (::after) to the button box. See the pulse
     block below for why this must NOT become a stacking context. */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: var(--ww-size);
  min-height: var(--ww-size);
  padding: 0 18px;
  border-radius: 999px;
  background: var(--ww-button-color);
  color: var(--ww-button-text);
  font-size: 15px;
  font-weight: var(--ww-font-weight, 700);
  line-height: 1;
  letter-spacing: 0;
  text-decoration: none;
  /* Keep offset-y + blur/2 under --ww-bottom (a 14px floor on mobile unless the
     phone layout is on), or the screen edge clips the shadow into a hard line.
     Matches the NubeSDK build. */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  border: 0;
  transition:
    transform 160ms ease,
    box-shadow 160ms ease,
    filter 160ms ease,
    width 180ms ease,
    min-width 180ms ease,
    padding 180ms ease;
}

.ww-widget[data-open="true"] .ww-widget__button,
.ww-widget[data-effective-mode="icon_only"] .ww-widget__button {
  width: var(--ww-size);
  height: var(--ww-size);
  padding: 0;
}

.ww-widget__button:hover {
  color: var(--ww-button-text);
  filter: brightness(0.96);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.24);
}

/* OPT-IN: the button grows under the cursor as well as lifting.
   [data-button-hover-zoom="true"] comes from the merchant's own setting, so an
   install with nothing stored, every install that predates the setting, keeps
   the plain lift above and looks exactly as it did. Same gating stance as the
   attract pulse below, and for the same reason: this lands on storefronts that
   are already live.

   Round button only, and that is not just the merchant's choice, the pill is
   excluded on purpose even when the setting is on. Scaling a wide box moves its
   ends much further than its top and bottom, so a pill visibly stretches
   sideways while a circle just swells. Same reason the attract pulse below is a
   spreading shadow rather than a scale.

   Safe against the pulse, which the rule further down already switches off on
   :hover, the button was gaining a transform here regardless, so this adds no
   new stacking context that the pulse could fall in front of.

   The transform is restated whole, not appended, because a second transform
   declaration replaces the first rather than composing with it: dropping the
   translateY here would silently cancel the lift. */
.ww-widget[data-button-hover-zoom="true"][data-effective-mode="icon_only"] .ww-widget__button:hover {
  transform: translateY(-1px) scale(1.08);
}

.ww-widget__button:focus-visible {
  outline: 2px solid rgba(37, 211, 102, 0.2);
  outline-offset: 2px;
}

.ww-widget[data-open="true"] .ww-widget__button:focus-visible {
  outline: none;
}

/* ---- The attract pulse ---------------------------------------------------
   A shadow that grows evenly out of the button's edge and fades, once every
   2.8s. Only on the round button and the round-button-with-label: the
   ask-a-question bar has its own resting animation (ww-glow-spin) and running
   both would be two things competing for the same glance.

   OPT-IN. The gate is [data-button-animation="true"], not "not false", so a
   store with nothing stored for this setting, every install that predates it, gets no pulse until a merchant deliberately switches it on. This is the
   opposite of how bar_animation_enabled is gated; the difference is
   deliberate, since that one shipped with the bar itself and this one lands on
   storefronts that are already live.

   THREE THINGS THAT LOOK LIKE THEY COULD BE SIMPLER AND CANNOT

   1. The box-shadow is on the ::after, never on the button. It has to spread
      evenly on all sides, and a transform: scale() cannot do that on the
      icon+label pill, scaling a wide box moves its ends much further than its
      top and bottom, so the halo stops reading as a shadow and starts reading
      as a second, wider pill. The cost is that a spreading shadow repaints
      each frame instead of compositing like a transform would. One small
      element is worth it here; that is a very different budget from the 40
      blurred dots the bar glow used to run, which is why that one was cut.

      It cannot go on the button itself: .ww-widget a.ww-widget__button further
      down pins box-shadow with !important to survive themes that restyle
      links, and an !important declaration beats an animation. On the button
      the pulse would silently never render. The pseudo-element is unarmoured,
      which is what makes this work.

   2. z-index: -1 puts the shadow behind the button's own background, so the
      button reads as solid with something behind it rather than as a
      translucent disc laid over its face. That only holds while the button is
      NOT a stacking context, so the button must never gain a transform,
      filter or opacity of its own while the pulse runs. It does gain both a
      transform and a filter on :hover, which is exactly why the pulse stops
      there (and stopping on engagement is right anyway: the pulse exists to
      earn a first glance, and hover means it already has one).

   3. No blur. The softness comes from the opacity fade, not from a blur
      radius: a blurred spreading shadow is a far more expensive repaint, and
      at these sizes it reads the same. */
.ww-widget[data-button-animation="true"][data-effective-mode="icon_only"] .ww-widget__button::after,
.ww-widget[data-button-animation="true"][data-effective-mode="custom_text"] .ww-widget__button::after {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* inherit, so the pill keeps the pill's shape and the circle the circle's. */
  border-radius: inherit;
  /* No background: this element is invisible except for the shadow it casts. */
  box-shadow: 0 0 0 0 var(--ww-button-color);
  content: "";
  pointer-events: none;
  /* TWO animations, not one, and splitting them is the whole point.

     The spread keeps the property this file already argued for: three
     keyframes, the last two identical, so the visible sweep is ONE eased
     segment. A timing function applies to every segment separately, so an
     extra mid keyframe would decelerate into it and accelerate out again, a
     stutter in the middle of the very thing this easing exists to smooth.

     The fade needs a mid keyframe precisely BECAUSE opacity has to ramp
     rather than jump, and giving it its own animation is what lets it have
     one without imposing a segment boundary on the spread. It runs linear,
     so its own two segments meet at a constant rate and cannot stutter
     either. */
  animation:
    ww-button-pulse-spread 2.8s cubic-bezier(0.4, 0, 0.2, 1) infinite,
    ww-button-pulse-fade 2.8s linear infinite;
}

/* WHY THE FADE IS SEPARATE, AND WHAT IT FIXES

   These used to be one animation that opened at `opacity: 0.55` with zero
   spread. Opacity ended the cycle at 0 and restarted at 0.55, so every 2.8s a
   solid ring materialised instantly, clamped to the button's edge, and only
   then expanded. Reported from a phone recording as "really green, really
   tense, really dense", a pop rather than a swell, and visible at exactly
   the 2.8s loop boundaries.

   The old comment claimed "the loop restart lands on opacity 0 and is
   invisible". It does not: the restart lands on 0%, which was 0.55. Ending a
   loop at zero says nothing about where the next one begins.

   Now opacity starts at 0 and ramps up over the first 15%, by which point the
   eased spread has already lifted the ring a couple of pixels clear of the
   button. It is brightest once it has somewhere to be, so it reads as a swell
   coming off the button rather than a ring snapping onto it. */
@keyframes ww-button-pulse-spread {
  0% {
    box-shadow: 0 0 0 0 var(--ww-button-color);
  }

  /* Fully spread by 50% (1.4s); the rest of the cycle is the pause that keeps
     this from reading as a nervous, continuous throb. The spread snapping back
     to 0 at the loop is invisible because the fade below holds opacity at 0
     across that boundary, from 50% through 100% and into the next 0%. */
  50% {
    box-shadow: 0 0 0 14px var(--ww-button-color);
  }

  100% {
    box-shadow: 0 0 0 14px var(--ww-button-color);
  }
}

@keyframes ww-button-pulse-fade {
  0% {
    opacity: 0;
  }

  15% {
    opacity: 0.55;
  }

  50% {
    opacity: 0;
  }

  /* Held at zero from 50% to 100%, which is the pause, and it is also what
     lets the spread animation reset from 14px back to 0 unseen. */
  100% {
    opacity: 0;
  }
}

/* Open, hovered or focused: the button has been found. Stop asking for
   attention, and, per note 2 above, :hover is also when the button becomes a
   stacking context and the shadow would flip in front of it.

   EVERY SELECTOR BELOW MUST OUT-SPECIFY THE PULSE RULE ABOVE, which is
   (0,4,1). The obvious `.ww-widget__button:hover::after` is only (0,2,1) and
   loses, so the pulse would keep running under the cursor with the shadow in
   front of the button, the exact bug this rule exists to prevent. Hence the
   redundant-looking attribute selectors below: they are there for weight, not
   for filtering. */
.ww-widget[data-button-animation="true"][data-open="true"] .ww-widget__button::after,
.ww-widget[data-button-animation="true"][data-effective-mode] .ww-widget__button:hover::after,
.ww-widget[data-button-animation="true"][data-effective-mode] .ww-widget__button:focus-visible::after {
  display: none;
}

.ww-widget__icon {
  display: block;
  /* Half the button height, which is the ratio the fixed 28px had against the
     default 56px button. It used to stay 28px at every size, so a 72px button
     got a mark that read as too small and an undersized button got a mark that
     nearly filled it. */
  width: calc(var(--ww-size) * 0.5);
  height: calc(var(--ww-size) * 0.5);
  flex: 0 0 auto;
  transition:
    opacity 140ms ease,
    transform 180ms ease;
}

.ww-widget__close-icon {
  display: none;
  position: relative;
  /* Tracks the mark it replaces, so the button does not change weight when it
     switches between the two. */
  width: calc(var(--ww-size) * 0.5);
  height: calc(var(--ww-size) * 0.5);
  flex: 0 0 auto;
}

.ww-widget__close-icon::before,
.ww-widget__close-icon::after {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--ww-size) * 0.5);
  height: 4px;
  border-radius: 999px;
  /* Named rather than currentColor: the X is a mark, not text, so it tracks the
     logo colour. Inheriting would have tied it to the title colour instead. */
  background: var(--ww-icon-color);
  content: "";
  transform-origin: center;
}

.ww-widget__close-icon::before {
  transform: translate(-50%, -50%) rotate(45deg);
}

.ww-widget__close-icon::after {
  transform: translate(-50%, -50%) rotate(-45deg);
}

.ww-widget[data-open="true"] .ww-widget__icon {
  display: none;
  opacity: 0;
  transform: scale(0.65) rotate(-45deg);
}

.ww-widget[data-open="true"] .ww-widget__close-icon {
  display: block;
}

.ww-widget[data-open="true"] .ww-widget__label {
  display: none !important;
}

.ww-widget__label {
  white-space: nowrap;
}

.ww-widget__popup {
  position: absolute;
  right: 0;
  bottom: calc(var(--ww-size) + 14px);
  width: min(var(--ww-panel-width), calc(100vw - 28px));
  overflow: hidden;
  border: 1px solid var(--ww-popup-border);
  border-radius: 18px;
  background: var(--ww-popup-background);
  color: var(--ww-popup-text);
  opacity: 0;
  box-shadow: 0 18px 44px rgba(8, 19, 13, 0.18);
  pointer-events: none;
  transform: translateY(10px) scale(0.98);
  transform-origin: bottom right;
  transition:
    opacity 220ms ease,
    transform 220ms ease;
  will-change: opacity, transform;
}

.ww-widget[data-auto-opened="true"] .ww-widget__popup {
  transform: translateY(16px) scale(1);
  transition:
    opacity 520ms ease,
    transform 560ms cubic-bezier(0.16, 1, 0.3, 1);
}

.ww-widget[data-open="true"] .ww-widget__popup {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0) scale(1);
}

.ww-widget[data-position="bottom-left"] .ww-widget__popup {
  right: auto;
  left: 0;
}

.ww-widget__popup-head {
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 12px;
  padding: 16px 18px 18px;
  border-bottom: 0;
  background:
    linear-gradient(135deg, var(--ww-button-color), var(--ww-popup-accent));
  color: var(--ww-title-color);
}

.ww-widget__popup-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.24);
}

.ww-widget__popup-logo img,
.ww-widget__popup-logo svg {
  display: block;
  width: 22px;
  height: 22px;
}

.ww-widget__popup-title,
.ww-widget__popup-subtitle,
.ww-widget__status {
  margin: 0;
  letter-spacing: 0;
}

.ww-widget__popup-title {
  color: var(--ww-title-color);
  font-size: 18px;
  font-weight: 800;
  line-height: 1.15;
}

.ww-widget__popup-subtitle {
  margin-top: 3px;
  color: rgba(255, 255, 255, 0.82);
  font-size: 11px;
  line-height: 1.4;
}

.ww-widget__status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  max-width: 100%;
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.45);
  color: var(--ww-title-color);
  font-size: 10.5px;
  font-weight: 650;
  line-height: 1.25;
  backdrop-filter: blur(6px);
}

.ww-widget__status::before {
  display: block;
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: #25d366;
  box-shadow: 0 0 0 3px rgba(37, 211, 102, 0.2);
  content: "";
}

.ww-widget__status[data-status="closed"] {
  background: rgba(0, 0, 0, 0.45);
}

.ww-widget__status[data-status="closed"]::before {
  background: #ff5a5f;
  box-shadow: 0 0 0 3px rgba(255, 90, 95, 0.2);
}

.ww-widget__status[hidden] {
  display: none !important;
}

.ww-widget__contacts {
  display: grid;
  gap: 10px;
  padding: 12px;
  /* Flat, not layered. A white radial gradient used to sit on top of this,
     which read as a smudge in the corner once the colour was anything but the
     near-white default and made the chosen colour look wrong. */
  background: var(--ww-popup-hover);
}

.ww-widget__contact {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 56px;
  overflow: hidden;
  padding: 9px 10px 9px 14px;
  border-left: 3px solid var(--ww-popup-accent);
  border-radius: 12px;
  background: var(--ww-popup-background);
  color: var(--ww-popup-text);
  text-decoration: none;
  box-shadow: 0 4px 14px rgba(12, 18, 14, 0.06);
  transition:
    background-color 140ms ease,
    color 140ms ease,
    transform 140ms ease,
    box-shadow 140ms ease;
}

.ww-widget__contact:hover,
.ww-widget__contact:focus-visible {
  /* A flat colour the merchant picks. This used to be a hardcoded green wash
     fading in over an overlay, which ignored their palette and left a gradient
     across a surface they had chosen a colour for. The default is that wash
     resolved against white, so nothing changes until they change it. */
  background: var(--ww-popup-card-hover);
  color: var(--ww-popup-text);
  transform: translateY(-1px);
  box-shadow: 0 8px 20px rgba(12, 18, 14, 0.1);
}

.ww-widget__contact-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  /* The same disc as the floating button, so it follows the button colour
     rather than the panel heading's accent. It carries the WhatsApp mark, so
     the two read as the same object in two places. */
  background: var(--ww-button-color);
}

.ww-widget__contact-action img,
.ww-widget__contact-action svg {
  display: block;
  width: 19px;
  height: 19px;
  filter: none;
}

.ww-widget__contact-copy {
  display: grid;
  min-width: 0;
  gap: 2px;
  position: relative;
  z-index: 1;
}

.ww-widget__contact-name {
  font-size: 14px;
  font-weight: 650;
  line-height: 1.25;
}

.ww-widget__contact-meta {
  color: var(--ww-popup-muted);
  font-size: 11px;
  font-weight: 400;
  line-height: 1.3;
}

.ww-widget[hidden] {
  display: none !important;
}

.ww-widget__popup[hidden],
.ww-widget__label[hidden] {
  display: none !important;
}

@media (max-width: 749px) {
  .ww-widget {
    right: max(14px, env(safe-area-inset-right));
    bottom: max(14px, env(safe-area-inset-bottom));
  }

  .ww-widget[data-position="bottom-left"]:not([data-bar-active="true"]) {
    right: auto;
    left: max(14px, env(safe-area-inset-left));
  }

  /* The phone layout, opt-in. Off, a round button sits at the flat 14px above,
     so no store shifts on update. On, the script has already written the phone
     values over --ww-side and --ww-bottom, and this reads them. The safe-area
     inset stays a floor rather than an addition, so a notched phone clears its
     home indicator without the setting being added on top.

     Stands down for the bar, which is centred and has its own phone rules:
     this rule's weight would otherwise put `right` back on it. */
  .ww-widget[data-phone-layout="true"]:not([data-bar-active="true"]) {
    right: max(var(--ww-side), env(safe-area-inset-right, 0px));
    bottom: max(var(--ww-bottom), env(safe-area-inset-bottom, 0px));
  }

  .ww-widget[data-phone-layout="true"][data-position="bottom-left"]:not([data-bar-active="true"]) {
    right: auto;
    left: max(var(--ww-side), env(safe-area-inset-left, 0px));
  }

  .ww-widget__button {
    padding: 0 14px;
  }

  .ww-widget__label {
    max-width: 46vw;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .ww-widget__popup {
    bottom: 68px;
    border-radius: 16px;
  }

  .ww-widget__popup-head {
    gap: 9px;
    padding: 14px;
  }

  .ww-widget__popup-logo {
    width: 36px;
    height: 36px;
  }

  .ww-widget__popup-logo img {
    width: 21px;
    height: 21px;
  }

  .ww-widget__popup-title {
    font-size: 17px;
  }

  .ww-widget__popup-subtitle {
    font-size: 11px;
  }

  .ww-widget__status {
    gap: 6px;
    margin-top: 6px;
    padding: 5px 8px;
    border-radius: 10px;
    font-size: 9.5px;
  }

  .ww-widget__contacts {
    gap: 8px;
    padding: 10px;
  }

  .ww-widget__contact {
    min-height: 52px;
    padding: 8px 9px 8px 12px;
    border-radius: 11px;
  }

  .ww-widget__contact-name {
    font-size: 13px;
  }

  .ww-widget__contact-meta {
    font-size: 11px;
  }

  .ww-widget__contact-action {
    width: 34px;
    height: 34px;
  }

  .ww-widget__contact-action img {
    width: 17px;
    height: 17px;
  }
}

/* The settings screen previews a phone inside a wide page, where the media
   query above cannot apply. These mirror it for a widget that has been told
   it is on a phone, so the preview is not quietly showing desktop layout. */
.ww-widget[data-force-mobile="true"] {
  right: max(14px, env(safe-area-inset-right));
  bottom: max(14px, env(safe-area-inset-bottom));
}

.ww-widget[data-force-mobile="true"][data-position="bottom-left"]:not([data-bar-active="true"]) {
  right: auto;
  left: max(14px, env(safe-area-inset-left));
}

.ww-widget[data-force-mobile="true"][data-phone-layout="true"]:not([data-bar-active="true"]) {
  right: max(var(--ww-side), env(safe-area-inset-right, 0px));
  bottom: max(var(--ww-bottom), env(safe-area-inset-bottom, 0px));
}

.ww-widget[data-force-mobile="true"][data-phone-layout="true"][data-position="bottom-left"]:not([data-bar-active="true"]) {
  right: auto;
  left: max(var(--ww-side), env(safe-area-inset-left, 0px));
}

.ww-widget[data-force-mobile="true"] .ww-widget__button {
  padding: 0 14px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__label {
  max-width: calc(var(--ww-preview-stage-width, 100vw) * 0.46);
  overflow: hidden;
  text-overflow: ellipsis;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup {
  bottom: 68px;
  border-radius: 16px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup-head {
  gap: 9px;
  padding: 14px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup-logo {
  width: 36px;
  height: 36px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup-logo img {
  width: 21px;
  height: 21px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup-title {
  font-size: 17px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__popup-subtitle {
  font-size: 11px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__status {
  gap: 6px;
  margin-top: 6px;
  padding: 5px 8px;
  border-radius: 10px;
  font-size: 9.5px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contacts {
  gap: 8px;
  padding: 10px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact {
  min-height: 52px;
  padding: 8px 9px 8px 12px;
  border-radius: 11px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact-name {
  font-size: 13px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact-meta {
  font-size: 11px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact-action {
  width: 34px;
  height: 34px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact-action img {
  width: 17px;
  height: 17px;
}

/* ---------------------------------------------------------------------------
 * Ask-a-question bar
 *
 * Placeholder styling: built from the existing widget tokens so it reads as
 * one system, but the visual treatment has not been through a design pass yet.
 * ------------------------------------------------------------------------ */

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

/* Theme-proofing. Until this release the widget had no form controls, so it
 * never collided with theme CSS. It does now, and a theme's
 * `input[type="text"]` (0,1,1) outranks a plain `.ww-widget__bar-input`
 * (0,1,0): so without this the field inherits the theme's border, height,
 * background and radius and stops looking like part of the widget. Themes
 * style bare `button` just as hard. The widget is a self-contained overlay
 * that must look identical everywhere, so these resets are deliberate. */
.ww-widget .ww-widget__bar-form,
.ww-widget .ww-widget__bar-input,
.ww-widget .ww-widget__bar-send,
.ww-widget .ww-widget__chip,
.ww-widget .ww-widget__qr-toggle,
.ww-widget .ww-widget__qr-hide,
.ww-widget .ww-widget__contact--choice,
.ww-widget .ww-widget__bar-close {
  box-sizing: border-box !important;
  margin: 0 !important;
  min-height: 0 !important;
  max-width: none !important;
  text-transform: none !important;
  letter-spacing: normal !important;
  text-shadow: none !important;
  float: none !important;
  -webkit-appearance: none !important;
  appearance: none !important;
}

/* The form is the visible field, so its box model is protected too. */
.ww-widget form.ww-widget__bar-form {
  padding: 6px 6px 6px 18px !important;
  border: 1px solid var(--ww-hairline) !important;
  border-radius: calc(var(--ww-size) / 2) !important;
  background: var(--ww-bar-surface) !important;
}

.ww-widget form.ww-widget__bar-form:hover,
.ww-widget form.ww-widget__bar-form:focus-within {
  border-color: var(--ww-hairline-strong) !important;
}

.ww-widget input.ww-widget__bar-input {
  width: auto !important;
  min-width: 0 !important;
  height: calc(var(--ww-size) - 14px) !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  color: var(--ww-bar-ink) !important;
  font: inherit !important;
  font-size: 17px !important;
  line-height: 1.2 !important;
}

.ww-widget .ww-widget__bar-send-icon {
  width: 22px !important;
  height: 22px !important;
  max-width: none !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: none !important;
}

.ww-widget button.ww-widget__bar-send {
  /* Pinned because the two icons inside are absolutely positioned against this
     box so they can cross-fade in place. A host theme that sets `position:
     static` on bare buttons would re-anchor them to whatever ancestor is
     positioned instead and throw them across the page. */
  position: relative !important;
  width: calc(var(--ww-size) - 14px) !important;
  height: calc(var(--ww-size) - 14px) !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 50% !important;
  background: var(--ww-bar-send-color) !important;
  box-shadow: none !important;
}

.ww-widget button.ww-widget__chip {
  height: auto !important;
  padding: 7px 12px !important;
  border: 1px solid var(--ww-hairline) !important;
  border-radius: 999px !important;
  background: transparent !important;
  box-shadow: none !important;
  color: var(--ww-placeholder) !important;
  font: inherit !important;
  font-size: 12px !important;
  line-height: 1.2 !important;
}

.ww-widget button.ww-widget__chip:hover {
  background: var(--ww-bar-hover) !important;
  border-color: var(--ww-hairline-strong) !important;
  color: var(--ww-bar-ink) !important;
}

.ww-widget button.ww-widget__bar-close {
  width: var(--ww-size) !important;
  height: var(--ww-size) !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 50% !important;
  background: var(--ww-bar-send-color) !important;
}

/* Author `display` beats the user-agent rule behind the `hidden` attribute, so
   anything the script hides has to say so explicitly. Without this the bar
   stayed on screen next to the round button. */
.ww-widget__bar[hidden] {
  display: none;
}

.ww-widget__bar {
  /* The containing block for a card being swapped out. Only one of the two
     screens can hold the column's flow, they are siblings here and two of
     them stack, so the one leaving is lifted out with position:absolute and
     anchored against this box while it dissolves. Its bottom edge is the
     stable one: the widget is pinned to the bottom of the viewport and the
     column grows upwards. */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

/* The 12px that used to be `gap` on the column above. It lives on the close
   button instead because the script animates it: a flex gap belongs to the
   container and cannot be interpolated per child, so when the button appeared
   the column grew by the button *and* the gap in a single frame, moving
   everything above it 68px while the card underneath was still easing. Only
   ever one card is visible, so this is the only gap the column had.

   !important because the theme-proofing block above resets `margin: 0
   !important` on this very button to survive host themes, and a normal
   declaration loses to it no matter where it sits in the file. Without this
   the offset computed to 0 and the gap went back to landing in one frame. */
.ww-widget .ww-widget__bar-close {
  margin-top: 12px !important;
}

.ww-widget__bar-panel,
.ww-widget__qr {
  width: 100%;
  /* animateBarBox() animates `height` on these two, and pinCard() sets a
     measured height and width on them, and all of that means the content box
     under `box-sizing: content-box` and the whole box under `border-box`. Left
     to the host it was whichever the theme happened to set, so the same widget
     animated to two different targets depending on where it was installed.
     Pinned here so the script compensates the same way everywhere. The one
     cost: on a theme without a global border-box the open card used to sit
     30px wider than the bar, and now matches it, as it already did elsewhere. */
  box-sizing: border-box;
}

/* Close button under the open bar. Same affordance the contact picker uses:
 * the panel rises and a round X takes the spot at the bottom. */
.ww-widget__bar-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ww-size);
  height: var(--ww-size);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ww-bar-send-color);
  color: var(--ww-icon-color);
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.ww-widget__bar-close[hidden] {
  display: none;
}

.ww-widget__bar-close:hover {
  transform: scale(1.05);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.24);
}

.ww-widget__bar-close:focus-visible {
  outline: 3px solid var(--ww-bar-outline);
  outline-offset: 3px;
}

.ww-widget__bar-close .ww-widget__close-icon {
  display: block;
  width: 20px;
  height: 20px;
}

.ww-widget__bar-close .ww-widget__close-icon::before,
.ww-widget__bar-close .ww-widget__close-icon::after {
  width: 20px;
  height: 3px;
}

/* The glowing border.
 *
 * A short streak of light travels the field's own border path. It is animated
 * with `offset-distance`, not by rotating a conic gradient, and that is the
 * whole point: a conic sweep advances at a constant *angular* rate from the
 * centre, which on a 480x56 pill makes the spot crawl along the long edges and
 * whip around the ends: measured at roughly 67x between slowest and fastest.
 * `offset-path` advances along the perimeter itself, so the speed is constant
 * the whole way round regardless of how wide the bar gets.
 *
 * Browsers without CSS motion paths fall back to the conic version below,
 * which animates everywhere but with that uneven speed. */
.ww-widget__bar-field {
  position: relative;
  width: 100%;
}

.ww-widget__bar-glow {
  position: absolute;
  inset: -1px;
  border-radius: calc(var(--ww-size) / 2 + 1px);
  overflow: hidden;
  pointer-events: none;
  opacity: 0.4;
  /* The RETURN leg, at its plain speed. A transition on the base rule is what
     runs on the way back to rest, and there are two different ways back: a
     pointer that grazed the field and left, and the bar closing. This is the
     first one, 180ms, immediate, near enough the 200ms it had before. */
  transition: opacity 180ms cubic-bezier(0.5, 1, 0.89, 1);
}

/* ...and this is the second. The ring waits out the whole 280ms of the box and
   only then fades in, so on a close it arrives on a bar that has finished
   moving instead of flashing on at full strength in the first frame of the
   collapse. The two cases end in the same state and CSS cannot tell them
   apart, so setBarStep() marks the close for the length of the run; without
   the mark this delay also applied to a stray hover and made it take 460ms to
   come back. */
.ww-widget[data-bar-closing="true"] .ww-widget__bar-glow {
  transition-delay: 280ms;
}

.ww-widget__bar-glow-dot {
  display: none;
}

.ww-widget__bar-glow::before {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 180%;
  aspect-ratio: 1 / 1;
  background: conic-gradient(
    transparent 0deg,
    transparent 282deg,
    var(--ww-glow-color) 336deg,
    var(--ww-glow-color) 360deg
  );
  filter: blur(0.8px);
  content: "";
  animation: ww-glow-spin 6.8s linear infinite;
}

@keyframes ww-glow-spin {
  from {
    transform: translate(-50%, -50%) rotate(0deg);
  }

  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

@supports (offset-path: border-box) and (offset-distance: 50%) {
  /* Sit on the border line itself rather than behind the field, so no
     masking is needed and the streak stays a true hairline. */
  .ww-widget__bar-glow {
    inset: 0;
    border-radius: calc(var(--ww-size) / 2);
    overflow: visible;
    z-index: 2;
  }

  /* The trail is six short segments, not one long one.
   *
   * Each segment is a straight element held tangent to the path, so at the
   * rounded caps (radius = half the field height) it can only approximate the
   * curve, and how far it strays goes as length squared: L^2/8r when centred
   * on the travelling point. 18px stays within ~1.4px, which is invisible.
   * A single element five times longer would stray ~36px and fly off the
   * corners: worse than the version this replaced. Chaining short segments
   * with staggered negative delays buys the length without the overshoot,
   * because each piece follows the curve on its own.
   *
   * Segment COUNT is a separate constraint from segment length. Each piece is
   * rotated to the tangent at its own point, so the trail turns in steps, and
   * too few pieces reads as a polygon rather than a curve. Across a cap the
   * turn per step is spacing/radius: six segments gave 33 degrees and looked
   * visibly faceted, twenty gives 10 and reads as a curve. They are also short
   * enough (8px) that overlap does not stack opacity into bright blobs.
   *
   * There are two trails of twenty. The second carries an extra half-duration
   * of delay, which puts it exactly half a lap from the first, so the pair sit
   * opposite each other the whole way round.
   *
   * Delays are negative so the animation starts already part-way through:
   * more negative means further along the path, so the first segment leads
   * and the rest trail behind it, fading out. Spacing is a share of the lap
   * rather than a pixel gap, so the trail keeps its proportions at any width. */
  .ww-widget__bar-glow::before {
    display: none;
  }

  .ww-widget__bar-glow-dot {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 8px;
    height: 1px;
    border-radius: 1px;
    background: var(--ww-glow-color);
    filter: blur(0.5px);
    offset-path: border-box;
    offset-rotate: auto;
    offset-anchor: 50% 50%;
    animation: ww-glow-travel 6.8s linear infinite;
  }

  .ww-widget__bar-glow-dot:nth-child(1) {
    opacity: 1.0;
    animation-delay: -0.6176s;
  }

  .ww-widget__bar-glow-dot:nth-child(2) {
    opacity: 0.951;
    animation-delay: -0.5851s;
  }

  .ww-widget__bar-glow-dot:nth-child(3) {
    opacity: 0.901;
    animation-delay: -0.5526s;
  }

  .ww-widget__bar-glow-dot:nth-child(4) {
    opacity: 0.852;
    animation-delay: -0.5201s;
  }

  .ww-widget__bar-glow-dot:nth-child(5) {
    opacity: 0.802;
    animation-delay: -0.4876s;
  }

  .ww-widget__bar-glow-dot:nth-child(6) {
    opacity: 0.753;
    animation-delay: -0.4551s;
  }

  .ww-widget__bar-glow-dot:nth-child(7) {
    opacity: 0.703;
    animation-delay: -0.4226s;
  }

  .ww-widget__bar-glow-dot:nth-child(8) {
    opacity: 0.654;
    animation-delay: -0.3900s;
  }

  .ww-widget__bar-glow-dot:nth-child(9) {
    opacity: 0.604;
    animation-delay: -0.3575s;
  }

  .ww-widget__bar-glow-dot:nth-child(10) {
    opacity: 0.555;
    animation-delay: -0.3250s;
  }

  .ww-widget__bar-glow-dot:nth-child(11) {
    opacity: 0.505;
    animation-delay: -0.2925s;
  }

  .ww-widget__bar-glow-dot:nth-child(12) {
    opacity: 0.456;
    animation-delay: -0.2600s;
  }

  .ww-widget__bar-glow-dot:nth-child(13) {
    opacity: 0.406;
    animation-delay: -0.2275s;
  }

  .ww-widget__bar-glow-dot:nth-child(14) {
    opacity: 0.357;
    animation-delay: -0.1950s;
  }

  .ww-widget__bar-glow-dot:nth-child(15) {
    opacity: 0.307;
    animation-delay: -0.1625s;
  }

  .ww-widget__bar-glow-dot:nth-child(16) {
    opacity: 0.258;
    animation-delay: -0.1300s;
  }

  .ww-widget__bar-glow-dot:nth-child(17) {
    opacity: 0.208;
    animation-delay: -0.0975s;
  }

  .ww-widget__bar-glow-dot:nth-child(18) {
    opacity: 0.159;
    animation-delay: -0.0650s;
  }

  .ww-widget__bar-glow-dot:nth-child(19) {
    opacity: 0.109;
    animation-delay: -0.0325s;
  }

  .ww-widget__bar-glow-dot:nth-child(20) {
    opacity: 0.06;
    animation-delay: -0.0000s;
  }

  .ww-widget__bar-glow-dot:nth-child(21) {
    opacity: 1.0;
    animation-delay: -4.0176s;
  }

  .ww-widget__bar-glow-dot:nth-child(22) {
    opacity: 0.951;
    animation-delay: -3.9851s;
  }

  .ww-widget__bar-glow-dot:nth-child(23) {
    opacity: 0.901;
    animation-delay: -3.9526s;
  }

  .ww-widget__bar-glow-dot:nth-child(24) {
    opacity: 0.852;
    animation-delay: -3.9201s;
  }

  .ww-widget__bar-glow-dot:nth-child(25) {
    opacity: 0.802;
    animation-delay: -3.8876s;
  }

  .ww-widget__bar-glow-dot:nth-child(26) {
    opacity: 0.753;
    animation-delay: -3.8551s;
  }

  .ww-widget__bar-glow-dot:nth-child(27) {
    opacity: 0.703;
    animation-delay: -3.8226s;
  }

  .ww-widget__bar-glow-dot:nth-child(28) {
    opacity: 0.654;
    animation-delay: -3.7900s;
  }

  .ww-widget__bar-glow-dot:nth-child(29) {
    opacity: 0.604;
    animation-delay: -3.7575s;
  }

  .ww-widget__bar-glow-dot:nth-child(30) {
    opacity: 0.555;
    animation-delay: -3.7250s;
  }

  .ww-widget__bar-glow-dot:nth-child(31) {
    opacity: 0.505;
    animation-delay: -3.6925s;
  }

  .ww-widget__bar-glow-dot:nth-child(32) {
    opacity: 0.456;
    animation-delay: -3.6600s;
  }

  .ww-widget__bar-glow-dot:nth-child(33) {
    opacity: 0.406;
    animation-delay: -3.6275s;
  }

  .ww-widget__bar-glow-dot:nth-child(34) {
    opacity: 0.357;
    animation-delay: -3.5950s;
  }

  .ww-widget__bar-glow-dot:nth-child(35) {
    opacity: 0.307;
    animation-delay: -3.5625s;
  }

  .ww-widget__bar-glow-dot:nth-child(36) {
    opacity: 0.258;
    animation-delay: -3.5300s;
  }

  .ww-widget__bar-glow-dot:nth-child(37) {
    opacity: 0.208;
    animation-delay: -3.4975s;
  }

  .ww-widget__bar-glow-dot:nth-child(38) {
    opacity: 0.159;
    animation-delay: -3.4650s;
  }

  .ww-widget__bar-glow-dot:nth-child(39) {
    opacity: 0.109;
    animation-delay: -3.4325s;
  }

  .ww-widget__bar-glow-dot:nth-child(40) {
    opacity: 0.06;
    animation-delay: -3.4000s;
  }

  @keyframes ww-glow-travel {
    from {
      offset-distance: 0%;
    }

    to {
      offset-distance: 100%;
    }
  }
}

/* Switched off entirely by the merchant. */
.ww-widget[data-bar-animation="false"] .ww-widget__bar-glow {
  display: none;
}

/* The loop runs only at rest. Once the visitor hovers or starts typing it has
 * done its job, and a light circling the field you are reading is a
 * distraction. */
/* The OUTBOUND leg, on the rule that wins while the bar is engaged: gone in
   140ms with no delay, so the ring is out of the way before the card has grown
   around the field it was circling. */
.ww-widget__bar-field:hover .ww-widget__bar-glow,
.ww-widget__bar-field:focus-within .ww-widget__bar-glow {
  opacity: 0;
  transition: opacity 140ms cubic-bezier(0.5, 1, 0.89, 1);
}

/* The button sets an author `display`, which beats the user-agent rule behind
 * the `hidden` attribute. Without this the round button stays on screen above
 * the bar. */
.ww-widget__button[hidden] {
  display: none;
}

.ww-widget__qr {
  padding: 18px 16px 30px;
  border: 1px solid var(--ww-hairline);
  border-radius: 18px;
  background: var(--ww-bar-surface);
  color: var(--ww-bar-ink);
  text-align: center;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
}

/* Collapsed, the panel is nothing but the field. Only once the visitor
 * engages does it grow the card around it. */
.ww-widget__bar-panel {
  color: var(--ww-bar-ink);
  /* The radius lives here, not only on the expanded rule, so nothing can ever
     paint a hard-cornered card around the rounded pill. Costs nothing while
     collapsed, because there is no background or border to round. */
  border-radius: 18px;
  /* Declared on the base rule on purpose, so it runs in both directions: the
     card fades its paint out as it closes, not only in as it opens.
     This is only safe because animateBarBox() in the widget script animates
     the card's height over the same duration. Paint and box have to move
     together: when the height snapped and only the paint faded, the result
     was a white, shadowed card left sitting around a collapsed bar for the
     length of the fade. If you change this duration, change BAR_MOTION_MS
     with it; tests/widget-motion.js asserts the two agree.

     Only paint is transitioned here. Padding is animated by the script, in
     the same run as the height: it is the difference between the rect and the
     content box, so the two have to be interpolated together or the card
     overshoots by exactly the padding and snaps back at the end.

     The border is declared at a constant width on both states so only its
     colour changes. Width and style are not interpolable from `none`/`0`, so
     a border that appeared with the expanded rule drew itself at full
     strength in frame one, around a card whose fill had not begun to fade
     in: and vanished instantly on the way out, leaving an outline-less blob
     fading. It also kept the box a size the animation had not measured. */
  border: 1px solid transparent;
  transition: background 280ms cubic-bezier(0.5, 1, 0.89, 1),
    box-shadow 280ms cubic-bezier(0.5, 1, 0.89, 1),
    border-color 280ms cubic-bezier(0.5, 1, 0.89, 1);
}

/* The second selector is the ghost the script pins while one screen dissolves
   into the next (pinCard). It is a picture of the card the visitor was just
   looking at, so it has to keep looking like that card for the length of the
   fade. Bound to the panel's own attribute rather than to the widget's step,
   because by then the widget has already committed to the NEXT step: without
   it, pressing send made the input card revert to its resting-pill look, send
   arrow back to the WhatsApp glyph, drop shadow back, padding gone, on the
   frame after the click, and the visitor read that as the bar closing on them.
   The paint here is overridden by pinCard's own inline transparency; the
   padding is the part that matters, and it is why this shares the rule. */
.ww-widget[data-bar-expanded="true"] .ww-widget__bar-panel,
.ww-widget .ww-widget__bar-panel[data-ww-ghost] {
  padding: 14px;
  border-color: var(--ww-hairline);
  border-radius: 18px;
  background: var(--ww-bar-surface);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
}

/* Suggestions sit under the field, not above it. The field is the thing the
   eye should land on first; chips are a fallback for visitors who would
   rather not type, so they are deliberately quieter than the input. */
.ww-widget__bar-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 12px;
}

.ww-widget__chip {
  padding: 7px 12px;
  border: 1px solid var(--ww-hairline);
  border-radius: 999px;
  background: transparent;
  color: var(--ww-placeholder);
  font: inherit;
  font-size: 12px;
  line-height: 1.2;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.ww-widget__chip:hover {
  background: var(--ww-bar-hover);
  border-color: var(--ww-bar-outline);
}

.ww-widget__chip:focus-visible {
  outline: 2px solid var(--ww-bar-outline);
  outline-offset: 2px;
}

/* The form is the field: it carries the border, background and shadow, and
 * the <input> inside it is transparent and borderless. That way the send
 * button sits inside the same rounded box instead of beside it. */
.ww-widget__bar-form {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 18px;
  border: 1px solid var(--ww-hairline);
  border-radius: calc(var(--ww-size) / 2);
  background: var(--ww-bar-surface);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
  cursor: text;
  /* The bar's clock, not its own. The field stops floating the moment it is
     inside a card, so this shadow and the card's are one cross-fade, and at
     150ms `ease` it was not: on the close the field's shadow was fully back at
     t=194ms while the card was still painting at alpha 0.165, so the last
     ~100ms of every close carried two stacked drop shadows. On the same
     duration and curve the two alphas sum to 1 at every frame instead.
     The border tint rides along; it only ever changes on hover, which never
     overlaps the gesture, and keeping it here means the whole widget speaks in
     exactly one easing. */
  transition: border-color 280ms cubic-bezier(0.5, 1, 0.89, 1),
    box-shadow 280ms cubic-bezier(0.5, 1, 0.89, 1);
}

.ww-widget__bar-form:hover {
  border-color: var(--ww-bar-outline);
}

/* Inside the expanded card the field no longer needs to float on its own. */
.ww-widget[data-bar-expanded="true"] .ww-widget__bar-form,
.ww-widget__bar-panel[data-ww-ghost] .ww-widget__bar-form {
  box-shadow: none;
}

/* Focus is a 1px tint on the border and nothing more, matching the resting
 * hairline. A pointer user gets no ring; a keyboard user still needs a real
 * focus indicator, so :focus-visible adds one below. */
.ww-widget__bar-form:focus-within {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
}

.ww-widget[data-bar-expanded="true"] .ww-widget__bar-form:focus-within,
.ww-widget__bar-panel[data-ww-ghost] .ww-widget__bar-form:focus-within {
  box-shadow: none;
}

/* No focus ring at all. :focus-visible is not the keyboard-only switch it is
 * for buttons: a field that accepts typing matches it however it was focused,
 * so a ring here fired on every mouse click and put back the thick outline
 * this was meant to remove. The focus indicator is the border tint plus the
 * caret, which is what a text field normally relies on. */

.ww-widget__bar-input {
  flex: 1 1 auto;
  min-width: 0;
  height: calc(var(--ww-size) - 14px);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ww-bar-ink);
  font: inherit;
  font-size: 17px;
  line-height: 1.2;
}

.ww-widget__bar-input::placeholder {
  color: var(--ww-placeholder);
  opacity: 1;
}

/* The focus ring lives on the form, so a second one on the input would
 * double up. */
.ww-widget__bar-input:focus,
.ww-widget__bar-input:focus-visible {
  outline: none;
}

.ww-widget__bar-send {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--ww-size) - 14px);
  height: calc(var(--ww-size) - 14px);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ww-bar-send-color);
  cursor: pointer;
  transition: transform 0.15s ease;
}

.ww-widget__bar-send:hover {
  transform: scale(1.05);
}

.ww-widget__bar-send:focus-visible {
  outline: 3px solid var(--ww-bar-outline);
  outline-offset: 2px;
}

/* WhatsApp mark at rest, send arrow once the visitor is typing. Both icons
   ship in the markup and only the visible one changes, so there is no image
   swap to wait for mid-interaction. */
/* Stacked and cross-faded, not swapped with `display`. The swap landed on the
   first frame of the open: the visitor clicked the field and the WhatsApp mark
   became an arrow instantly, ahead of everything else that was still easing.
   Stacking also means the button's size never depends on which icon is up, so
   the pinned 42px box stays 42px through the fade. */
.ww-widget__bar-send-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  display: block;
  width: 22px;
  height: 22px;
  max-width: none;
  transform: translate(-50%, -50%);
  transition: opacity 280ms cubic-bezier(0.5, 1, 0.89, 1);
}

.ww-widget__bar-send-icon--send,
.ww-widget[data-bar-expanded="true"] .ww-widget__bar-send-icon--rest,
.ww-widget__bar-panel[data-ww-ghost] .ww-widget__bar-send-icon--rest {
  opacity: 0;
}

.ww-widget[data-bar-expanded="true"] .ww-widget__bar-send-icon--send,
.ww-widget__bar-panel[data-ww-ghost] .ww-widget__bar-send-icon--send {
  opacity: 1;
}

.ww-widget__bar-foot {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 10px;
  margin-top: 10px;
  padding: 0 6px;
  text-align: center;
}

.ww-widget__bar-note,
.ww-widget__bar-count {
  color: var(--ww-placeholder);
  font-size: 10.5px;
  line-height: 1.4;
}

.ww-widget__bar-count[data-state="full"] {
  color: #b3261e;
}

.ww-widget__bar-count[hidden],
.ww-widget__bar-chips[hidden],
.ww-widget__bar-foot[hidden],
.ww-widget__qr-contacts[hidden],
.ww-widget__qr-web[hidden],
.ww-widget__bar-panel[hidden],
.ww-widget__qr[hidden] {
  display: none;
}

.ww-widget__qr-title {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ww-bar-ink);
}

.ww-widget__qr-subtitle {
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ww-placeholder);
}

/* 340px is not arbitrary. A 200-character question in pt-BR or es encodes to
 * a version-14 code (73x73 modules) at error-correction M, and 340px puts that
 * at ~4.4px per module, above the ~4px where phone cameras start to struggle.
 * Shrinking this box, raising MAX_QUESTION_CHARS, or raising the error
 * correction further all cost module size and quietly hurt scanning. */
.ww-widget__qr-canvas {
  display: flex;
  align-items: center;
  justify-content: center;
  /* The centre logo is positioned against .ww-widget__qr-code, so the two boxes
     have to line up. Left as content-box this element measured 340px + 20px of
     padding while its wrapper measured 340px, which put the code's centre 10px
     to the right of the badge and left the mark visibly off-centre. */
  box-sizing: border-box;
  width: min(340px, 100%);
  aspect-ratio: 1 / 1;
  margin: 0 auto 12px;
  padding: 10px;
  border-radius: 12px;
  background: #ffffff;
  color: #000000;
}

.ww-widget__qr-canvas svg {
  width: 100%;
  height: 100%;
  display: block;
}

.ww-widget__qr-canvas[data-state="loading"]::after {
  content: "…";
  font-size: 24px;
  color: var(--ww-bar-ink-muted);
}

.ww-widget__qr-canvas[data-state="error"] {
  padding: 16px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ww-bar-ink-muted);
  background: var(--ww-bar-surface-alt);
}

/* The badge is a white patch punched out of the middle of the code with the
   green WhatsApp mark on it. Kept to 20% of the code's width: that wipes 4% of
   the area, which error-correction M (~15% recovery) absorbs with margin. */
.ww-widget__qr-code {
  position: relative;
  width: min(340px, 100%);
  margin: 0 auto 12px;
}

.ww-widget__qr-code .ww-widget__qr-canvas {
  width: 100%;
  margin: 0;
}

.ww-widget__qr-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  border-radius: 8px;
  background: #ffffff;
  pointer-events: none;
}

.ww-widget__qr-badge[hidden] {
  display: none;
}

.ww-widget .ww-widget__qr-badge img {
  display: block;
  width: 62% !important;
  height: auto !important;
  max-width: none !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  box-shadow: none !important;
  background: none !important;
}

/* Desktop action as a button. Kept as an <a> on purpose: it navigates to
   WhatsApp Web, and a real link is what screen readers and middle-click
   expect. Only the styling is a button. */
.ww-widget a.ww-widget__qr-web {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 38px;
  padding: 7px 16px;
  /* Its own colour: it is the one button a visitor presses when WhatsApp is
     already open on their computer, so a merchant may want it to stand apart
     from the bar's own controls. */
  border: 1px solid var(--ww-qr-web-button);
  border-radius: 999px;
  background: var(--ww-qr-web-button);
  color: var(--ww-qr-button-text);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}

/* The link sets an author `display`, which beats the user-agent rule behind
   the `hidden` attribute, so hiding it needs saying explicitly. Same trap as
   the round button. */
.ww-widget a.ww-widget__qr-web[hidden] {
  display: none;
}

.ww-widget a.ww-widget__qr-web:hover {
  filter: brightness(0.96);
}

.ww-widget .ww-widget__qr-web img,
.ww-widget .ww-widget__qr-web .ww-widget__qr-web-icon {
  display: block;
  width: 18px !important;
  height: 18px !important;
  max-width: none !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  box-shadow: none !important;
  background: none !important;
}

.ww-widget a.ww-widget__qr-web:focus-visible {
  outline: 2px solid var(--ww-bar-outline);
  outline-offset: 2px;
}

/* Two ways on: opening WhatsApp, or a code for a phone. The first is the
   common path on a computer, so it leads and the code is one click away. */
.ww-widget__qr-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.ww-widget__qr-actions[hidden],
.ww-widget__qr-reveal[hidden] {
  display: none;
}

.ww-widget__qr-or {
  color: var(--ww-placeholder);
  font-size: 11px;
  line-height: 1;
}

.ww-widget .ww-widget__qr-hide {
  margin-top: 14px !important;
}

.ww-widget .ww-widget__qr-toggle,
.ww-widget .ww-widget__qr-hide {
  padding: 8px 16px !important;
  border: 1px solid var(--ww-hairline) !important;
  border-radius: 999px !important;
  background: transparent !important;
  color: var(--ww-placeholder) !important;
  font: inherit !important;
  font-size: 12px !important;
  line-height: 1.2 !important;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.ww-widget .ww-widget__qr-toggle:hover,
.ww-widget .ww-widget__qr-hide:hover {
  border-color: var(--ww-hairline-strong) !important;
  color: var(--ww-bar-ink) !important;
}

.ww-widget .ww-widget__qr-toggle:focus-visible,
.ww-widget .ww-widget__qr-hide:focus-visible {
  outline: 2px solid var(--ww-bar-outline);
  outline-offset: 2px;
}

.ww-widget__qr-scan {
  margin: 0 0 12px;
  color: var(--ww-placeholder);
  font-size: 12px;
  line-height: 1.45;
}

.ww-widget__qr-contacts {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
  text-align: left;
}

/* These rows are <button>, and the user-agent stylesheet centres button text.
   The boxes line up perfectly, but each label centres inside its own line, so
   names of different lengths look ragged. Force the text left. */
/* Gray at rest, green on hover. These rows carry a selection meaning, unlike
   the field and the card, so the green line is reserved for the one being
   pointed at rather than sitting on all three at once. */
.ww-widget__contact--choice {
  width: 100%;
  border: 1px solid var(--ww-popup-border);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.ww-widget__contact--choice:hover,
.ww-widget__contact--choice:focus-visible {
  border-color: var(--ww-hairline-strong);
  background: var(--ww-bar-hover);
}

/* Phones. The bar is a real option here, so the parts that were sized for a
   mouse get sized for a thumb: chips and the send button clear the ~44px
   minimum tap target, and the panel gains room below for the home indicator.
   The input stays at 17px because anything under 16px makes iOS Safari zoom
   the page the moment the field is focused. */
@media (max-width: 749px) {
  .ww-widget[data-button-mode="input_bar"][data-mobile-button-mode="input_bar"] {
    width: calc(100vw - var(--ww-side) * 2);
  }

  /* Phones showing a round button instead: undo the centring entirely and give
     the corner back. Without this the widget stayed a centred full-width box
     and the button sat at its left edge whatever corner was chosen. */
  .ww-widget[data-button-mode="input_bar"]:not([data-mobile-button-mode="input_bar"]) {
    left: auto;
    right: var(--ww-side);
    width: auto;
    transform: none;
  }

  .ww-widget[data-button-mode="input_bar"]:not([data-mobile-button-mode="input_bar"])[data-position="bottom-left"] {
    right: auto;
    left: var(--ww-side);
  }

  .ww-widget[data-bar-active="true"] {
    bottom: calc(var(--ww-bottom) + env(safe-area-inset-bottom, 0px) + var(--ww-keyboard-lift, 0px));
  }

  .ww-widget__bar {
    gap: 10px;
  }

  .ww-widget[data-bar-expanded="true"] .ww-widget__bar-panel,
  .ww-widget .ww-widget__bar-panel[data-ww-ghost] {
    padding: 12px;
  }

  .ww-widget button.ww-widget__chip {
    min-height: 40px !important;
    padding: 10px 14px !important;
    font-size: 13px !important;
  }

  .ww-widget__bar-chips {
    gap: 8px;
    margin-top: 10px;
  }

  .ww-widget__contact--choice {
    min-height: 60px;
  }

  .ww-widget a.ww-widget__qr-web,
  .ww-widget .ww-widget__qr-toggle,
  .ww-widget .ww-widget__qr-hide {
    min-height: 44px !important;
  }

  /* The bar already sits at the bottom of a small screen, so the extra close
     button underneath costs a row of thumb space for nothing: tapping outside
     or pressing done closes it just as well. */
  .ww-widget__bar-close {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ww-widget__button,
  .ww-widget__icon,
  .ww-widget__popup,
  .ww-widget__bar-panel,
  .ww-widget__bar-form,
  .ww-widget__bar-send,
  .ww-widget__bar-send-icon,
  .ww-widget__bar-glow,
  .ww-widget__bar-close,
  .ww-widget__chip {
    transition: none;
  }

  .ww-widget__bar-glow::before,
  .ww-widget__bar-glow-dot {
    animation: none;
  }

  /* The transition reset above kills the easing, not the movement, so the
     round button's hover zoom would stop growing and start snapping 8% wider
     in a single frame, which is more motion than it had before, not less.
     Dropped back to the plain lift; the deeper shadow still marks the hover.

     This overrides the merchant's setting rather than reading it, which is the
     point: a visitor asking their OS for less motion outranks a shop's
     preference for more.

     Same specificity as the rule it undoes, so it wins on source order alone;
     the media query itself adds no weight. */
  .ww-widget[data-button-hover-zoom="true"][data-effective-mode="icon_only"] .ww-widget__button:hover {
    transform: translateY(-1px);
  }

  /* display:none, not animation:none. Pausing would freeze the pulse on
     whatever keyframe it had reached and leave a permanent static ring around
     the button for exactly the people who asked for less motion.

     The attribute selectors are here for specificity, same as on the hover
     rule: a media query adds no weight of its own, so a bare
     `.ww-widget__button::after` would lose to the (0,4,1) pulse rule and this
     whole block would be decorative. */
  .ww-widget[data-button-animation="true"][data-effective-mode] .ww-widget__button::after {
    display: none;
  }
}

/* Theme-proofing for the round button.
   The ask-a-question bar got this treatment; the round button never did, and
   it is an <a>, so any host that styles links wins over it. In the WordPress
   admin `a:focus` recolours it and draws its own ring, and because the close
   X is two pseudo-elements filled with `currentColor`, the whole icon turned
   the host's link colour the moment the button was clicked.

   Deliberately no padding here: three separate rules set it (pill, icon-only,
   phone), and forcing one value would flatten all of them. Only the properties
   a host actually reaches for are pinned. */
.ww-widget a.ww-widget__button,
.ww-widget a.ww-widget__button:link,
.ww-widget a.ww-widget__button:visited,
.ww-widget a.ww-widget__button:hover,
.ww-widget a.ww-widget__button:focus,
.ww-widget a.ww-widget__button:focus-visible,
.ww-widget a.ww-widget__button:active {
  /* Pinned because the attract pulse is positioned against this box: a host
     that sets `position: static` on links would otherwise re-anchor the halo
     to the widget root and park it in the corner. Stays `relative` (never a
     z-index) so the button does not become a stacking context. See the pulse
     block above. */
  position: relative !important;
  box-sizing: border-box !important;
  margin: 0 !important;
  border: 0 !important;
  border-radius: 999px !important;
  background: var(--ww-button-color) !important;
  color: var(--ww-button-text) !important;
  text-decoration: none !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  text-shadow: none !important;
  -webkit-appearance: none !important;
  appearance: none !important;
}

/* Shadows stay per-state, so they are pinned separately rather than in the
   blanket rule above. The host's focus ring is a box-shadow, which would
   otherwise replace the widget's own lift. */
.ww-widget a.ww-widget__button,
.ww-widget a.ww-widget__button:focus,
.ww-widget a.ww-widget__button:focus-visible {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
}

.ww-widget a.ww-widget__button:hover {
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.24) !important;
}

/* The widget's own focus ring, restated so it outranks the pinned rules above
   and the host's outline alike. */
.ww-widget a.ww-widget__button:focus-visible {
  outline: 2px solid rgba(37, 211, 102, 0.2) !important;
  outline-offset: 2px !important;
}

.ww-widget[data-open="true"] a.ww-widget__button:focus-visible {
  outline: none !important;
}

/* The close X is drawn from currentColor, so it follows any colour the host
   manages to set on the link. Pin it to the icon colour outright. */
.ww-widget a.ww-widget__button .ww-widget__close-icon::before,
.ww-widget a.ww-widget__button .ww-widget__close-icon::after {
  background: var(--ww-icon-color) !important;
}

/* The WhatsApp mark is inline SVG filled with currentColor, so it follows the
   same colour as the label and needs the same protection from the host. */
.ww-widget a.ww-widget__button .ww-widget__icon {
  color: var(--ww-icon-color) !important;
  fill: currentColor !important;
}

/* The heading's mark sits inside .ww-widget__popup-head, which carries the
   title colour, so it names its own or it would follow the heading text. */
.ww-widget__popup-logo svg {
  color: var(--ww-icon-color);
  fill: currentColor;
}

/* Same for the mark cloned into each contact row. */
.ww-widget__contact-action svg {
  color: var(--ww-icon-color);
  fill: currentColor;
}

/* Preview mirror for the second phone block.
   The first block has had a mirror for a long time; this one never did, so the
   admin's Mobile preview showed desktop-sized chips and QR buttons, and showed
   a close button that a real phone hides. tests/css-mobile-mirror.js scanned
   only the first block, so it reported all of this as covered. */

.ww-widget[data-force-mobile="true"][data-button-mode="input_bar"][data-mobile-button-mode="input_bar"] {
  width: calc(var(--ww-preview-stage-width, 100vw) - var(--ww-side) * 2);
}

.ww-widget[data-force-mobile="true"][data-button-mode="input_bar"]:not([data-mobile-button-mode="input_bar"]) {
  left: auto;
  right: var(--ww-side);
  width: auto;
  transform: none;
}

.ww-widget[data-force-mobile="true"][data-button-mode="input_bar"]:not([data-mobile-button-mode="input_bar"])[data-position="bottom-left"] {
  right: auto;
  left: var(--ww-side);
}

.ww-widget[data-force-mobile="true"][data-bar-active="true"] {
  bottom: calc(var(--ww-bottom) + env(safe-area-inset-bottom, 0px) + var(--ww-keyboard-lift, 0px));
}

.ww-widget[data-force-mobile="true"] .ww-widget__bar {
  gap: 10px;
}

.ww-widget[data-force-mobile="true"][data-bar-expanded="true"] .ww-widget__bar-panel,
.ww-widget[data-force-mobile="true"] .ww-widget__bar-panel[data-ww-ghost] {
  padding: 12px;
}

.ww-widget[data-force-mobile="true"] button.ww-widget__chip {
  min-height: 40px !important;
  padding: 10px 14px !important;
  font-size: 13px !important;
}

.ww-widget[data-force-mobile="true"] .ww-widget__bar-chips {
  gap: 8px;
  margin-top: 10px;
}

.ww-widget[data-force-mobile="true"] .ww-widget__contact--choice {
  min-height: 60px;
}

.ww-widget[data-force-mobile="true"] a.ww-widget__qr-web,
.ww-widget[data-force-mobile="true"] .ww-widget__qr-toggle,
.ww-widget[data-force-mobile="true"] .ww-widget__qr-hide {
  min-height: 44px !important;
}

.ww-widget[data-force-mobile="true"] .ww-widget__bar-close {
  display: none;
}


/* The bar's send mark and the QR link's mark are inline SVG now, so the
   WhatsApp logo colour finally reaches them. Before this the disc took the
   merchant's colour while the mark inside it stayed white, because it was an
   <img> and no stylesheet can reach inside one. */
/* Both icons share this class, but they are drawn differently: the WhatsApp
   mark is filled paths, the arrow is a stroked line with a chevron head. A
   blanket `fill: currentColor` overrode the arrow's own fill="none" attribute
   (CSS beats presentation attributes) and filled it into a solid wedge. */
.ww-widget .ww-widget__bar-send-icon--rest {
  color: var(--ww-icon-color);
  fill: currentColor;
}

.ww-widget .ww-widget__bar-send-icon--send {
  color: var(--ww-icon-color);
  fill: none;
  stroke: currentColor;
}

/* The WhatsApp mark follows the logo colour here as it does everywhere else.
   Tying it to this button's text colour meant the logo colour silently stopped
   applying on the send screen. */
.ww-widget .ww-widget__qr-web-icon {
  color: var(--ww-icon-color);
  fill: currentColor;
}
