/* ==========================================================================
   EastWest brand theme for Web Chat v3
   ==========================================================================

   Loaded AFTER botchat.css / botchat-fullwindow.css and overrides them.
   Nothing in public/css/webchat-stable/ or public/scripts/webchat-stable/
   is edited: that bundle is vendored and any override written into it is
   lost the moment it is replaced. Every rule needed to brand the client
   lives here instead.

   Colour values are taken from "Approved Digital Brand Guide on Headlines,
   April 2020", page 4 swatch block:

     EW Purple    Pantone 268C   #4F2683
     EW Magenta   Pantone 234C   #AF006E

   The guide prints RGB 81/33/120 beside #4F2683 and RGB 178/0/102 beside
   #AF006E. Those two notations disagree (#4F2683 is 79/38/131, #AF006E is
   175/0/110). The hex values are authoritative for screen, so the hex is
   what is used here. Raised with the brand owner rather than silently
   picked — see docs/BRANDING.md.

   Warm gray #80715D is the guide's body-copy colour, recovered from the
   text layer of the same PDF. Lime #C8D800 is the cover field colour; it
   is deliberately confined to the one "online" dot and nothing else,
   because the guide never names it as a type or UI colour.
   ========================================================================== */

:root {
    --ew-purple:        #4F2683;
    --ew-purple-dark:   #3B1C62;
    --ew-magenta:       #AF006E;
    --ew-magenta-dark:  #8A0057;
    --ew-warm-gray:     #80715D;
    --ew-lime:          #C8D800;

    --ew-ink:           #2B2B2B;
    --ew-canvas:        #F7F5FA;
    --ew-surface:       #FFFFFF;
    --ew-hairline:      rgba(79, 38, 131, 0.12);

    /* Composer height incl. the iOS home-indicator inset. Everything that sits
       above the composer (the message pane, the chip row) offsets from this one
       token, so a notched device can't push the composer over the chips. */
    --ew-console-h:     calc(56px + env(safe-area-inset-bottom, 0px));

    --ew-radius:        18px;
    --ew-radius-tail:   5px;

    /* MaxLF is the brand face per the guide. It is not in this repo and is
       not licensed for web delivery, so it is not referenced here — naming
       a font that will never load only produces a silent fallback nobody
       has chosen. This stack is the deliberate substitute: a rounded
       humanist sans in the same spirit. Documented as a known deviation. */
    --ew-font: "Segoe UI", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
               Roboto, Arial, sans-serif;
}

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */

html, body {
    margin: 0;
    padding: 0;
    overflow: visible;
    background: var(--ew-canvas);
    font-family: var(--ew-font);
    -webkit-text-size-adjust: 100%;
}

.ew-shell {
    position: relative;
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow: visible;
}

/* --------------------------------------------------------------------------
   Header

   Web Chat's own header is switched off (format.showHeader = false) and
   replaced with real markup, because the built-in one is a single <div>
   wrapping a <span> of title text — there is no seam to hang an avatar,
   a status line or a back affordance on.

   The vendored rule also pointed at url(/images/ewbHlogo.png), and
   public/images/ does not exist in this repo. That header has been
   requesting a 404 on every page load.
   -------------------------------------------------------------------------- */

/* The header now stacks two rows -- a brand row carrying the eastwest
   wordmark, then the bot identity row -- so the flex direction is column and
   the horizontal alignment moves down to .ew-header__identity. */
.ew-header {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 16px;
    padding-top: calc(12px + env(safe-area-inset-top, 0px));
    padding-left: calc(16px + env(safe-area-inset-left, 0px));
    padding-right: calc(16px + env(safe-area-inset-right, 0px));
    background: linear-gradient(103deg,
                var(--ew-purple) 0%, #6B2585 52%, var(--ew-magenta) 100%);
    color: #fff;
    /* Above the thread and the composer, since it now overlaps them rather
       than sharing flow with them. */
    z-index: 10;
}

.ew-header__brand {
    display: flex;
    align-items: center;
    /* The wordmark is the sole occupant of this row again. The install button
       moved to the identity row below, so the brand mark never shares billing
       with a utility action. */
    gap: 12px;
}

/* --------------------------------------------------------------------------
   Install ("Add to Home Screen") affordance
   --------------------------------------------------------------------------

   Hidden by default and revealed only by JS, never by CSS alone. The default
   state is the SAFE one: a browser that cannot install the app must not be
   shown a button that does nothing. The [hidden] attribute is the switch.

   `display: none` is restated against [hidden] because the flex context above
   would otherwise override the UA's `display: none` for hidden elements --
   a flex item's display is not inherited from the UA sheet once a rule sets
   it. Without this the button would be visible on every browser regardless of
   what the script decided.
   -------------------------------------------------------------------------- */
.ew-header__install[hidden] {
    display: none;
}

.ew-header__install {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* Never grow, and never take more than a third of the row. The brand mark
       is the primary element in this header; an install affordance that can
       outgrow it is a layout bug, not a styling preference. min-width: 0 lets
       the label ellipsize instead of forcing the pill wider than the cap. */
    flex: 0 1 auto;
    max-width: 33%;
    min-width: 0;
    padding: 5px 10px;
    border: 1px solid rgba(255, 255, 255, 0.55);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font: inherit;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

.ew-header__install-label {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* On narrow phones the icon alone carries the meaning, and the button's
   aria-label still states the full action for assistive tech. */
@media (max-width: 380px) {
    .ew-header__install-label {
        /* display:none is safe here: the accessible name comes from the
           button's own aria-label, not from this span, so hiding the text
           costs nothing to assistive tech. The pill collapses to a circle
           around the glyph. */
        display: none;
    }

    .ew-header__install {
        padding: 5px 8px;
    }
}

.ew-header__install:hover {
    background: rgba(255, 255, 255, 0.22);
}

.ew-header__install:active {
    background: rgba(255, 255, 255, 0.30);
}

/* A visible focus ring, not the browser default outline which disappears
   against this purple gradient. Keyboard users must be able to see where they
   are; `outline: none` without a replacement is an accessibility failure. */
.ew-header__install:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

.ew-header__install-icon {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
    display: block;
    fill: currentColor;
}

/* --------------------------------------------------------------------------
   Install instruction sheet (iOS) / bot-triggered offer
   --------------------------------------------------------------------------

   One panel serves both surfaces. On Android it is skipped entirely when the
   native prompt is available -- the browser's own sheet is better than
   anything rendered here. It is shown when there is something to SAY rather
   than something to DO: iOS Safari, where installation is manual.
   -------------------------------------------------------------------------- */
.ew-install-sheet[hidden] {
    display: none;
}

.ew-install-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Above the sticky header (z-index 10) and the composer. */
    z-index: 40;
    display: flex;
    justify-content: center;
    padding: 12px;
    /* Clear the iOS home indicator, and sit above the Share button the
       instructions tell the customer to reach for. */
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    pointer-events: none;
}

.ew-install-sheet__box {
    pointer-events: auto;
    width: 100%;
    max-width: 420px;
    box-sizing: border-box;
    padding: 14px 16px;
    border-radius: 14px;
    background: #fff;
    color: #2A2A2A;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
    font-size: 13.5px;
    line-height: 1.45;
}

.ew-install-sheet__title {
    margin: 0 0 6px;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--ew-purple);
}

.ew-install-sheet__body {
    margin: 0;
}

/* The Share glyph is rendered inline in the sentence so the instruction points
   at the actual thing the customer must tap, rather than describing it. */
.ew-install-sheet__glyph {
    width: 13px;
    height: 14px;
    vertical-align: -2px;
    margin: 0 1px;
    fill: currentColor;
}

.ew-install-guide {
    display: block;
    width: 100%;
    height: auto;
    margin-top: 10px;
    font-family: -apple-system, system-ui, sans-serif;
}

.ew-install-sheet__actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 12px;
}

.ew-install-sheet__btn {
    padding: 7px 14px;
    border-radius: 999px;
    border: 1px solid transparent;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

.ew-install-sheet__btn:focus-visible {
    outline: 2px solid var(--ew-purple);
    outline-offset: 2px;
}

.ew-install-sheet__btn--primary {
    background: var(--ew-purple);
    color: #fff;
}

.ew-install-sheet__btn--ghost {
    background: transparent;
    color: var(--ew-purple);
    border-color: rgba(79, 38, 131, 0.35);
}

/* Install panel: the same centred card and dimmed backdrop as the promo, so
   the steps get real screen height instead of a thin bottom strip. Scoped by
   ID so the push popup and the inbox (which share .ew-install-sheet) are not
   touched. The backdrop takes clicks, like the promo's. */
#ewInstallSheet {
    top: 0;
    /* Centred with auto margins on the card, not by centring the flex line: a
       card taller than the viewport (landscape) would be centred past the top
       edge with its top unreachable. Auto margins centre when it fits and fall
       back to top-aligned scrolling when it does not. */
    align-items: flex-start;
    padding: 16px;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    background: rgba(27, 14, 46, 0.55);
    pointer-events: auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

#ewInstallSheet .ew-install-sheet__box {
    margin: auto 0;
    flex: 0 0 auto;
    /* Only the illustrated (iOS) card claims extra height; the short Android
       and use-Chrome panels size to their text. */
    max-width: 420px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 22px 20px 18px;
    border-radius: 20px;
    font-size: 15px;
}

#ewInstallSheet .ew-install-sheet__title {
    font-size: 20px;
    line-height: 1.25;
    margin-bottom: 10px;
}

#ewInstallSheet .ew-install-sheet__body {
    flex: 0 1 auto;
}

/* The illustration is the point of the popup: let it fill the card width. */
#ewInstallSheet .ew-install-sheet__box:has(.ew-install-guide) {
    min-height: min(60vh, 500px, calc(100vh - 48px));
}

#ewInstallSheet .ew-install-guide {
    margin: 16px 0 4px;
}

#ewInstallSheet .ew-install-sheet__actions {
    padding-top: 8px;
}

/* Push detail popup: a centered poster card over a dimmed backdrop. Scoped to
   .ew-push-detail so the install prompt keeps its bottom sheet. The backdrop
   takes clicks (it blocks the chat behind the card); Close and Escape dismiss. */
.ew-push-detail,
.ew-inbox {
    top: 0;
    align-items: center;
    padding: 16px;
    background: rgba(27, 14, 46, 0.55);
    pointer-events: auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* The inbox and the promo are ONE viewbox: same width, same fixed height, so
   sliding from one to the other never resizes anything. A short promo leaves
   empty space; its buttons stay pinned to the bottom like a poster. */
.ew-push-detail__card,
.ew-inbox__clip {
    border-radius: 20px;
    max-width: 420px;
    width: 100%;
    height: min(85vh, 760px);
    max-height: calc(100% - 8px);
}

.ew-push-detail__card {
    display: flex;
    flex-direction: column;
}

/* The inbox card lives in a clipping frame (same rect the promo is clipped to),
   so when it slides out to the left it vanishes at the box edge instead of
   travelling across the screen. The frame owns the shadow. */
.ew-inbox__clip {
    overflow: hidden;
    pointer-events: auto;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
}

.ew-inbox__clip > .ew-install-sheet__box {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    border-radius: 0;           /* the frame rounds the outside; square panels meet with no notch */
    box-shadow: none;
    display: flex;
    flex-direction: column;
}

/* The promo opened from the inbox sits exactly on top of it. It draws no
   backdrop of its own (the inbox's is already there; two would double-dim the
   chat) and it is above the inbox in the stack. */
.ew-push-detail--over {
    z-index: 41;
    background: transparent;
}

.ew-push-detail--over .ew-push-detail__card {
    animation: ew-slide-in 0.28s ease-in-out;
    box-shadow: none;           /* the inbox frame under it already has the shadow */
}

/* One unit: while the promo slides in from the right, the inbox card slides out
   to the left by the same distance, same time, same easing. No fades, so the two
   read as one sheet with the promo "already there, just off screen". */
.ew-inbox--shifted .ew-install-sheet__box {
    animation: ew-inbox-out 0.28s ease-in-out forwards;
}

@keyframes ew-slide-in {
    from { transform: translateX(100%); border-radius: 0; }
    99%  { transform: translateX(0);    border-radius: 0; }   /* square while moving: no notch against the inbox panel */
    to   { transform: translateX(0);    border-radius: 20px; }
}

@keyframes ew-inbox-out {
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
}

.ew-push-detail__card {
    overflow-y: auto;
    padding: 0 0 16px;
    border-radius: 20px;
    font-size: 15px;
}

.ew-push-detail__card > .ew-install-sheet__title,
.ew-push-detail__card > .ew-install-sheet__body,
.ew-push-detail__card > .ew-install-sheet__actions {
    margin-left: 20px;
    margin-right: 20px;
}

.ew-push-detail__card > .ew-install-sheet__title {
    margin-top: 18px;
    margin-bottom: 8px;
    font-size: 20px;
    line-height: 1.25;
}

/* Reserve the picture's space up front so the card does not jump when it loads. */
.ew-push-detail__media {
    width: 100%;
    aspect-ratio: 3 / 4;
    /* Portrait is tall: cap it so the title, text and buttons always stay on
       screen. Past the cap the image is cropped to fill (object-fit: cover). */
    max-height: 52vh;
    overflow: hidden;
    border-radius: 0;           /* the card (overflow + radius) rounds the poster's top corners */
    background: var(--ew-canvas);
}

.ew-push-detail__media[hidden] {
    display: none;
}

.ew-push-detail__media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Own compositing layer: iOS keeps the decoded poster as a texture and moves
       it with the slide instead of painting it after the slide stops. */
    transform: translateZ(0);
    will-change: transform;
}

/* Landscape phones: a portrait poster cannot fit a short viewport, so drop the
   image and keep the text and buttons. */
@media (max-height: 480px) {
    .ew-push-detail__media { display: none; }
    .ew-push-detail__card,
    .ew-inbox__clip { height: calc(100vh - 32px); max-height: none; }
    .ew-push-detail__card > .ew-install-sheet__title { margin-top: 12px; font-size: 17px; }
    .ew-push-detail__body { max-height: 22vh; flex: 0 1 auto; }
}

/* The sender's line breaks are kept and long text scrolls inside the card. */
.ew-push-detail__body {
    white-space: pre-line;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* Buttons stay pinned to the bottom of the fixed-height card. */
.ew-push-detail__card > .ew-install-sheet__actions {
    margin-top: auto;
    flex: 0 0 auto;
}

.ew-push-detail__actions {
    display: inline-flex;
    gap: 8px;
}


/* --------------------------------------------------------------------------
   Alerts inbox: header badge + list sheet (PRD "Alerts Inbox Button")
   -------------------------------------------------------------------------- */
.ew-header__inbox {
    position: relative;
}

.ew-header__badge[hidden] {
    display: none;
}

.ew-header__badge {
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    box-sizing: border-box;
    border-radius: 999px;
    background: #FF8A00;
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

.ew-inbox__prompt[hidden] {
    display: none;
}

.ew-inbox__prompt {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 4px 0 10px;
    padding: 10px 12px;
    border-radius: 10px;
    background: rgba(79, 38, 131, 0.07);
}

.ew-inbox__prompt-text {
    margin: 0;
    flex: 1 1 auto;
    font-size: 12.5px;
}

.ew-inbox__prompt .ew-install-sheet__btn {
    flex: 0 0 auto;
}

/* Brand header band, the same gradient as the page header. It bleeds to the
   card's edges (the box pads 14px 16px; the frame clips the top corners). */
.ew-inbox__header {
    flex: 0 0 auto;
    margin: -14px -16px 12px;
    padding: 16px 16px 14px;
    background: linear-gradient(103deg,
                var(--ew-purple) 0%, #6B2585 52%, var(--ew-magenta) 100%);
    color: #fff;
}

.ew-inbox__header-title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    line-height: 1.25;
    color: #fff;
}

.ew-inbox__msg {
    margin: 6px 0;
    color: #6B6B6B;
    text-align: center;
}

.ew-inbox__msg:empty {
    display: none;
}

/* "No alerts yet" / loading / errors: a quiet centred note, not a stray line. */
.ew-inbox__msg {
    padding: 28px 12px;
    font-size: 13px;
}

.ew-inbox__list {
    list-style: none;
    margin: 0;
    /* 3px of breathing room for the focus ring, pulled back out by the margin
       so the cards line up with the header; the gutter keeps a desktop scrollbar
       off the card edges. */
    padding: 4px 3px 8px;
    margin: 0 -3px;
    scrollbar-gutter: stable;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    flex: 1 1 auto;
}

.ew-inbox__item + .ew-inbox__item {
    margin-top: 8px;
}

/* Each alert is a soft card. Unread: tinted, with a purple accent bar. */
.ew-inbox__row {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    box-sizing: border-box;
    padding: 11px 12px 11px 14px;
    border: 1px solid var(--ew-hairline);
    border-radius: 12px;
    background: #fff;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    transition: background-color 120ms ease, transform 120ms ease;
    -webkit-appearance: none;
    appearance: none;
    -webkit-tap-highlight-color: transparent;
}

.ew-inbox__row::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: transparent;
}

.ew-inbox__row--unread {
    background: #F4EFFA;
    border-color: rgba(79, 38, 131, 0.28);
}

.ew-inbox__row--unread::before {
    background: var(--ew-purple);
}

.ew-inbox__row:hover {
    background: #EEE7F7;
}

.ew-inbox__row:active {
    transform: scale(0.99);
    background: #E6DCF2;
}

.ew-inbox__row:focus-visible {
    outline: 2px solid var(--ew-purple);
    outline-offset: 1px;
    border-radius: 12px;
}

.ew-inbox__text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.ew-inbox__top {
    display: flex;
    align-items: center;
    gap: 6px;
}

.ew-inbox__chip {
    flex: 0 0 auto;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(79, 38, 131, 0.10);
    color: var(--ew-purple);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.ew-inbox__chip--offer {
    background: rgba(175, 0, 110, 0.10);
    color: var(--ew-magenta-dark);
}

.ew-inbox__title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
    font-weight: 500;
    color: var(--ew-ink);
}

.ew-inbox__row--unread .ew-inbox__title {
    font-weight: 700;
}

.ew-inbox__preview {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
    font-size: 12.5px;
    line-height: 1.4;
    color: #5C5C5C;
}

.ew-inbox__thumb {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    border-radius: 8px;
    object-fit: cover;
    background: #EEE7F7;
}

/* The word "Unread" stays in the DOM for screen readers; visually hidden
   (the accent bar and tint carry the state). */
.ew-inbox__dot {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.ew-inbox__when {
    font-size: 11.5px;
    color: #6F6F6F;
}

/* Height-constrained, width auto, so the SVG's own 266x52 ratio drives the
   box and the wordmark never distorts. */
.ew-header__logo {
    height: 22px;
    width: auto;
    display: block;
}

/* Hairline separating the brand row from the identity row, echoing the divider
   the bank's own utility bar uses. */
.ew-header__identity {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.18);
}

/* Push the install button to the far right of the identity row, away from the
   avatar/name group. margin-left:auto rather than justify-content:space-between
   on the parent, because the avatar and text block must stay tight together --
   space-between would split those two apart as well. */
.ew-header__actions {
    margin-left: auto;
    flex: 0 1 auto;
    max-width: 33%;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;      /* equal-width pills when stacked */
    gap: 4px;
}

/* The 33% cap now lives on the stack, not on each pill. */
.ew-header__actions .ew-header__install {
    max-width: none;
    justify-content: center;
}

/* Both visible: slim the pills so the pair (~44px) stays inside the 48px
   avatar row and the sticky header does not grow. Two rules, not one list:
   an engine without :has() would drop the whole list. */
.ew-header__actions .ew-header__install:not([hidden]) ~ .ew-header__install {
    padding: 3px 10px;
}

.ew-header__actions .ew-header__install:not([hidden]):has(~ .ew-header__install) {
    padding: 3px 10px;
}

/* The avatar is now a real image rather than a letter. object-fit guards
   against a non-square asset ever being swapped in, and the ring keeps the
   lime backdrop from bleeding into the purple bar. */
.ew-header__avatar {
    width: 48px;
    height: 48px;
    flex: 0 0 auto;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(255, 255, 255, 0.18);
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
}

.ew-header__text  { min-width: 0; }

.ew-header__name {
    font-size: 15px;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ew-header__status {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11.5px;
    line-height: 1.3;
    margin-top: 2px;
    opacity: 0.92;
}

.ew-header__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex: 0 0 auto;
    background: var(--ew-lime);
    box-shadow: 0 0 0 2px rgba(200, 216, 0, 0.28);
}
.ew-header__dot--offline {
    background: rgba(255, 255, 255, 0.55);
    box-shadow: none;
}

/* Device has no internet — distinct from "EastWest is unreachable".
   Amber rather than the grey used for server-side states, because the
   action the customer must take is different: fix your connection, not
   wait for us. No glow: the glow is what reads as "live". */
.ew-header__dot--no-internet {
    background: #F5A623;
    box-shadow: none;
}

/* --------------------------------------------------------------------------
   Offline banner

   In-flow, NOT overlaid. It pushes the transcript down by its own height
   instead of covering it, so the customer can still read what the assistant
   already said while they go and find a signal. A position:fixed strip here
   would sit on top of the newest message — the one they most want.
   -------------------------------------------------------------------------- */

.ew-netbanner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px 9px 16px;
    /* Light amber with dark text rather than a dark strip. Under a saturated
       purple header a dark brown band read as muddy and vaguely broken
       instead of as a deliberate warning; this reads unmistakably as a
       notice and clears contrast comfortably. */
    background: #FDECC8;
    color: #5C3D00;
    font-size: 13px;
    line-height: 1.35;
    flex: 0 0 auto;
    border-bottom: 1px solid #E8C989;
}
.ew-netbanner[hidden] { display: none; }

.ew-netbanner__text { flex: 1 1 auto; }

.ew-netbanner__close {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    opacity: 0.7;
}
.ew-netbanner__close:hover  { opacity: 1; background: rgba(92, 61, 0, 0.1); }
.ew-netbanner__close:active { background: rgba(92, 61, 0, 0.18); }

/* Composer lockout. Visibly inert, but the field keeps its place so the
   layout does not jump when the network comes back. */
.wc-console.ew-console--offline {
    opacity: 0.55;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
   Chat surface
   -------------------------------------------------------------------------- */

#BotChatElement {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;          /* lets the flex child actually shrink */
    top: auto; left: auto; right: auto; bottom: auto;
}

.wc-app,
.wc-app button,
.wc-app input,
.wc-app textarea {
    font-family: var(--ew-font);
    font-size: 14px;
    color: var(--ew-ink);
}

/* The header occupies natural flex space above the chat container. */
.wc-chatview-panel {
    background: var(--ew-canvas);
    top: 0;
}

/* The vendored header rule requests a missing image. The header is disabled
   in JS, but the rule is neutralised too so a future showHeader:true cannot
   quietly reintroduce the 404. */
.wc-header {
    display: none !important;
    background-image: none !important;
}

.wc-message-groups {
    top: 0;
    bottom: var(--ew-console-h);
    padding: 12px 12px 4px;
    background: var(--ew-canvas);
    /* Bottom-anchor the thread so a short conversation grows up from the
       composer instead of stranding the opening line at the top.

       This uses `margin-top:auto` on the first child, NOT
       `justify-content:flex-end`. On a scroll container, flex-end pushes
       overflow past the TOP edge, where it is unreachable: the scrollbar
       reports the full scrollHeight but scrollTop stays pinned at 0, so the
       thread silently stops scrolling once it fills the pane. The auto-margin
       collapses to zero as soon as content overflows, which bottom-anchors
       short threads and leaves long ones scrolling normally. */
    display: flex;
    flex-direction: column;
    /* Momentum scrolling on iOS; without it the list feels stuck. */
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* Pairs with the note above: pushes a short thread down to the composer, then
   collapses to 0 once the content overflows so scrolling is never blocked. */
.wc-message-groups > :first-child {
    margin-top: auto;
}

/* A flex child may not shrink below its content, which would break scrolling
   once the thread is taller than the pane. This keeps overflow behaving. */
.wc-message-groups > * { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   Message bubbles

   v3 draws an SVG "callout" tail on every bubble. Modern rounded bubbles
   with an asymmetric corner read better and scale down to 360px without
   the tail colliding with the avatar, so the tail is hidden and the shape
   carries the direction instead.
   -------------------------------------------------------------------------- */

/* The vendored sheet fades each message in via `animation: animationFrames 2s`,
   whose 0% keyframe is `opacity: 0`.

   Two problems with that. A running animation's keyframe beats a normal
   `opacity: 1` declaration, so simply re-declaring opacity here does NOT make
   the message visible — measured: the wrapper still computed to opacity 0.
   And if the animation never advances (background tab, reduced-motion,
   offscreen or headless render, any engine that skips compositing) the element
   is pinned at that 0% keyframe and the message is silently invisible. A chat
   client whose messages depend on an animation completing in order to be
   readable is one paint bug away from losing the conversation.

   So the opacity animation is replaced outright rather than patched. The
   entrance is carried by transform only and opacity is never animated, which
   means the resting state is visible whether or not the animation ever runs. */
@keyframes ew-message-in {
    from { transform: translateY(4px); }
    to   { transform: none; }
}

.wc-message-wrapper {
    margin-bottom: 8px;
    opacity: 1 !important;
    animation: ew-message-in 0.22s ease-out both;
}

.wc-message { max-width: 84%; }

.wc-message svg.wc-message-callout { display: none !important; }

.wc-message-content {
    border-radius: var(--ew-radius);
    padding: 10px 14px;
    line-height: 1.45;
    box-shadow: 0 1px 3px rgba(79, 38, 131, 0.13);
    word-break: break-word;
}

/* From the customer */
.wc-message-from-me { margin-right: 4px; }

.wc-message-from-me .wc-message-content {
    background: linear-gradient(120deg, var(--ew-purple), var(--ew-magenta));
    background-color: var(--ew-magenta);   /* fallback if gradients fail */
    color: #fff;
    border-bottom-right-radius: var(--ew-radius-tail);
}

/* From the bot.

   The vendored sheet painted bot bubbles solid purple (#542785) with near
   white text. Both sides of the conversation then arrived as saturated
   brand colour, which flattens the single most important distinction in a
   chat log: who said it. The bot now speaks on white and the customer
   carries the gradient. */
/* Bot avatar beside the bubble.

   Web Chat v3 has no avatar slot, so it is drawn as a ::before on the message
   row rather than by editing the vendored bundle. The row becomes a flex line
   with the avatar as its first item, aligned to the bottom so that in a run of
   several bubbles the face sits next to the last one, which is the convention
   every chat client follows.

   .wc-message-from-bot is added by React at runtime -- it is NOT a literal
   string in botchat.js, so grepping the bundle for it finds nothing. Confirmed
   present by reading the live DOM. */
.wc-message-from-bot:not(.wc-message-from) {
    margin-left: 4px;
    display: flex;
    align-items: flex-end;
    gap: 8px;
}

/* :not(.wc-message-from) matters -- the sender/timestamp label carries the
   same wc-message-from-bot class (see .wc-message-from-bot.wc-message-from
   below), so an unscoped rule paints a second avatar on the label. */
.wc-message-from-bot:not(.wc-message-from)::before {
    content: "";
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: url("../img/esta-avatar-32.png") center / cover no-repeat,
                var(--ew-canvas);
    /* Faint ring: the avatar's lime backdrop sits on a near-white canvas, so
       without it the disc edge dissolves into the page. */
    box-shadow: 0 0 0 1px rgba(79, 38, 131, 0.16);
    margin-bottom: 2px;
}

.wc-message-from-bot .wc-message-content {
    background: var(--ew-surface);
    color: var(--ew-ink);
    border-bottom-left-radius: var(--ew-radius-tail);
}

.wc-message-from-bot .wc-message-content a { color: var(--ew-magenta); }

/* Sender + timestamp.

   This bundle does NOT render the .wc-time element the vendored stylesheet
   describes — verified against botchat.js, which has no such class. It emits
   a single `.wc-message-from .wc-message-from-{me|bot}` div INSIDE the message
   wrapper, holding the name and, when a timestamp is shown, the time appended
   after it ("You 9:41 AM"). So this is one line of metadata per message, and
   it inherits the wrapper's float — it needs no clearing, and aligning it is
   a matter of text-align, not float.

   Warm gray is darkened from the guide's #80715D: at 11px on the pale canvas
   that value falls under the WCAG 4.5:1 contrast floor. */
.wc-message-from {
    color: #6B5E4D;
    font-size: 11px;
    margin-top: 3px;
    margin-bottom: 2px;
}

.wc-message-from-me.wc-message-from  { text-align: right; }
/* The sender label renders as  <span>{from.name || from.id}{timeSent}</span>
   -- two sibling text nodes in one span. The first resolved to the raw Direct
   Line id, which must not face a customer.

   No CSS masking here: an earlier version zeroed the font to hide the id, but
   that took the timestamp with it since both nodes share the span. The name is
   now rewritten in place by repairWebChatText() in chat.html, which edits only
   the first text node and leaves the time intact. */
.wc-message-from-bot.wc-message-from {
    text-align: left;
}

/* .wc-time is styled defensively in case a future bundle emits it. */
.wc-time {
    clear: both;
    color: #6B5E4D;
    font-size: 10.5px;
    margin-bottom: 8px;
}

/* --------------------------------------------------------------------------
   Suggested actions (quick replies)
   -------------------------------------------------------------------------- */

/* Suggested actions are absolutely positioned OVER the bottom of the message
   list, and the vendored sheet compensates by translating the list up 40px
   when they are showing. Measured on a full pane, the last bubble still ends
   up underneath the chips and its final line becomes unreadable.

   Reserving real space below the thread is the honest fix: padding grows with
   the chip row so the last message always clears it, and the translate hack
   is cancelled so the two corrections cannot stack. */
.wc-message-pane.show-actions .wc-message-groups {
    transform: none;
    padding-bottom: 70px;
}

/* Web Chat always renders this container, even with no suggested actions --
   when idle it holds only `<!-- react-empty -->`. A min-height plus a gradient
   therefore painted a 52px band across the bottom of the transcript at all
   times, covering the newest message. Sizing to content means the element
   collapses to nothing when empty and only occupies space when it holds real
   chips. `:empty` does not match (the comment node counts as content), so the
   height must come from the content itself.

   Default is therefore NO paint; the gradient is switched on only when chips
   are actually present. */
.wc-suggested-actions {
    background: none;
    pointer-events: none;
    bottom: calc(var(--ew-console-h) + 10px);
    height: auto;
    min-height: 0;
}

/* Paint only with real chips. Gated on .show-actions (which Web Chat toggles
   on the pane itself) rather than on :has() alone, because :has() is
   unsupported on Safari < 15.4 and Chrome < 105 -- there the band would
   silently return. :has() only narrows it further where it is understood. */
.wc-message-pane.show-actions .wc-suggested-actions {
    background: linear-gradient(to top,
                var(--ew-canvas) 62%, rgba(247, 245, 250, 0));
    pointer-events: auto;
}

.wc-message-pane.show-actions .wc-suggested-actions:not(:has(button)) {
    background: none;
    pointer-events: none;
}

/* A trailing gutter so the final chip can come to rest fully inside the
   viewport when scrolled to the end, instead of being sliced by the edge and
   reading as a rendering bug. */
.wc-suggested-actions .wc-hscroll > ul {
    padding: 2px 16px;
    scroll-padding-inline: 16px;
}

/* WebKit renders a classic (space-consuming) scrollbar under the chip strip
   here, which ate most of the gap above the composer. The partially-visible
   next chip already signals that the row scrolls. */
.wc-suggested-actions .wc-hscroll {
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Fade the trailing edge so a chip crossing the boundary reads as "there
       is more to scroll" rather than as a chip clipped by a layout bug. */
    -webkit-mask-image: linear-gradient(to right, #000 88%, transparent 100%);
            mask-image: linear-gradient(to right, #000 88%, transparent 100%);
}
.wc-suggested-actions .wc-hscroll::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

.wc-suggested-actions .wc-hscroll > ul > li {
    max-width: 70%;
}

/* 34px missed the 44px minimum touch target in WCAG 2.5.5 / the iOS HIG, and
   these chips carry the authentication choice — a mis-tap between "Send a code"
   and "Not now" is a real cost. The button keeps its 38px pill look while a
   transparent ::after extends the hit area to a full 44px. */
.wc-suggested-actions .wc-hscroll > ul > li button {
    background: var(--ew-surface);
    color: var(--ew-purple);
    /* 3.77:1 against the canvas and 4.08:1 against the chip fill. The prior
       0.30 alpha measured 1.63:1 and failed WCAG 1.4.11's 3:1 floor for
       control boundaries — the chips read as plain text on a bright screen. */
    border: 1.5px solid rgba(79, 38, 131, 0.65);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    min-height: 38px;
    padding: 0 16px;
    position: relative;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.wc-suggested-actions .wc-hscroll > ul > li button::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
}

/* Spacing between chips, so the extended hit areas don't butt up against
   each other and steal one another's taps. */
.wc-suggested-actions .wc-hscroll > ul > li { margin-right: 8px; }

/* On narrow phones the row typically overruns by only a few pixels, which
   shears the final chip against the edge and reads as a bug rather than as
   "scroll for more". Wrapping to a second line keeps every option fully
   visible and removes the horizontal scroll entirely at this width. */
@media (max-width: 420px) {
    .wc-suggested-actions .wc-hscroll { overflow-x: hidden; }

    .wc-suggested-actions .wc-hscroll > ul {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-end;
        gap: 8px;
        padding: 2px 16px;
        white-space: normal;
    }

    .wc-suggested-actions .wc-hscroll > ul > li {
        display: inline-flex;
        float: none;
        margin-right: 0;
    }

    /* Wrapped chips occupy one or two rows depending on label length, so the
       thread reserves enough room for the taller case rather than assuming. */
    .wc-message-pane.show-actions .wc-message-groups { padding-bottom: 112px; }
}

.wc-suggested-actions .wc-hscroll > ul > li button:focus,
.wc-suggested-actions .wc-hscroll > ul > li button:hover {
    background: var(--ew-purple);
    border-color: var(--ew-purple);
    color: #fff;
}

.wc-suggested-actions .wc-hscroll > ul > li button:active {
    background: var(--ew-purple-dark);
    border-color: var(--ew-purple-dark);
    color: #fff;
}

.wc-suggested-actions button.scroll {
    background-color: rgba(79, 38, 131, 0.55);
    border-radius: 4px;
}
.wc-suggested-actions button.scroll:focus,
.wc-suggested-actions button.scroll:hover {
    background-color: var(--ew-purple);
}

/* --------------------------------------------------------------------------
   Composer
   -------------------------------------------------------------------------- */

.wc-console {
    border: 0;
    border-top: 1px solid var(--ew-hairline);
    background: var(--ew-surface);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    height: var(--ew-console-h);
}

.wc-console input[type=text]::placeholder,
.wc-console textarea::placeholder {
    color: #6B5E4D;
    opacity: 1;
}

.wc-console input[type=text],
.wc-console textarea {
    /* 16px is not a style choice. Any smaller and iOS Safari zooms the whole
       page when the field takes focus, and the user is left scrolled into a
       layout they cannot see out of. */
    font-size: 16px;
    color: var(--ew-ink);
    background: transparent;
}

/* Placeholder colour is set once, higher up in this file (#6B5E4D, 5.62:1).
   A second declaration here previously overrode it with #9A93A5, which
   measures 2.64:1 against the input fill and fails WCAG AA. */

.wc-console .wc-textbox {
    /* Starts after the 40px attachment button (10px + 40px + 8px gap) rather
       than at the bar edge. */
    left: 58px;
    right: 52px;
    top: 8px;
    bottom: auto;
    height: 40px;
    display: flex;
    align-items: center;
    background: var(--ew-canvas);
    border: 1.5px solid transparent;
    border-radius: 999px;
    padding: 0 16px;
    box-sizing: border-box;
    transition: border-color .15s ease, background-color .15s ease;
}

/* The pill's own active state. This was magenta, which at 1.5px against a
   light fill reads as crimson -- the "red box" that appears the moment the
   field is clicked. Note this is :focus-within, not :focus-visible, so it
   fires for mouse and touch too, making it the more visible of the two cues.
   Purple states the same thing without signalling an error. */
.wc-console .wc-textbox:focus-within {
    background: var(--ew-surface);
    border-color: var(--ew-purple);
}

/* The pill already carries the focus state via its own border, so the generic
   ring is suppressed for the inner input -- otherwise focusing the composer
   draws two concentric purple rings. */
.wc-console .wc-textbox input[type=text]:focus-visible,
.wc-console .wc-textbox textarea:focus-visible {
    box-shadow: none;
    outline: 2px solid transparent;
}

/* Attachment button.

   Restored, not hidden. An earlier revision removed it with the note "upload
   is not offered by this client" -- that was an assumption, not a fact: the
   control ships visible on dev, and the absence of multipart handling in
   server.js proves nothing, because Direct Line attachments go to the bot and
   never transit this Node server.

   The bundle renders it as
     <label class="wc-upload" tabindex="0"><svg/><input type="file" multiple/></label>
   so it is a label, not a button -- hence the explicit sizing, the pointer
   cursor and the role/keyboard affordances below. */
.wc-console .wc-upload {
    position: absolute;
    left: 10px;
    top: 8px;
    width: 40px;
    height: 40px;
    margin: 0;
    padding: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    background: transparent;
    transition: background-color .15s ease;
}

/* 44px tap target from a 40px circle, matching the send button's approach:
   WCAG 2.5.5 / iOS HIG want 44, but 44 visible would crowd a 56px bar. */
.wc-console .wc-upload::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
}

.wc-console .wc-upload:hover {
    background: rgba(79, 38, 131, 0.08);
}

.wc-console .wc-upload:active {
    background: rgba(79, 38, 131, 0.16);
}

/* The paperclip path measures 26x18 and ships without a viewBox (one is added
   at runtime in chat.html). The box below matches that 1.44:1 ratio, so the
   icon fills it instead of being letterboxed inside a mismatched box. Same
   muted grey as the idle send arrow, so neither control shouts before use. */
.wc-console .wc-upload svg {
    /* The bundle sets `.wc-console svg { margin: 11px }`, sized for the original
       square buttons. Inside this 40px flex circle that leaves only 18px of free
       space, so a wider icon silently gets flex-shrunk to fit. Zero the margin
       and opt out of shrinking so the declared width is the rendered width. */
    margin: 0;
    flex: none;
    width: 20px;
    height: 13.9px;
    fill: #B6AEC2;
    pointer-events: none;
    transition: fill .15s ease;
}

.wc-console .wc-upload:hover svg,
.wc-console .wc-upload:focus-visible svg {
    fill: var(--ew-purple);
}

/* The file input is visually hidden by the bundle's own styles; keep it out of
   the layout without display:none, which would stop the label from opening the
   picker in some browsers. */
.wc-console .wc-upload input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}


/* The visible circle stays 40px to sit comfortably in a 56px bar, but the
   tappable area is extended to 44px to satisfy WCAG 2.5.5. */
.wc-console .wc-send {
    right: 10px;
    top: 8px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--ew-canvas);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color .15s ease;
}

/* The arrow path measures 27x18.16 and ships without a viewBox (added at
   runtime in chat.html); the bundle's own 11px margin was sized for the
   original square button and collides with the 40px circle. Box matches the
   path's 1.46:1 ratio so the tip is neither cropped nor letterboxed. */
.wc-console .wc-send svg {
    fill: #B6AEC2;
    margin: 0;
    width: 17px;
    height: 11.63px;
}

/* Web Chat adds .has-text to the console only when the field is non-empty.
   Using it means the button reads as live exactly when pressing it would do
   something, instead of looking armed over an empty box. */
.wc-console .wc-send::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
}

.wc-console.has-text .wc-send {
    background: linear-gradient(135deg, var(--ew-purple), var(--ew-magenta));
    background-color: var(--ew-magenta);
}
.wc-console.has-text .wc-send svg { fill: #fff; }
.wc-console.has-text .wc-send:hover { filter: brightness(1.08); }
.wc-console.has-text .wc-send:active { filter: brightness(0.92); }

.wc-console .wc-mic.active path#micFilling { fill: var(--ew-purple); }

/* --------------------------------------------------------------------------
   Cards
   -------------------------------------------------------------------------- */

.wc-card {
    border: 1px solid var(--ew-hairline);
    border-radius: 12px;
    overflow: hidden;
    background: var(--ew-surface);
}

.wc-card button {
    color: var(--ew-purple);
    border-radius: 0;
}
.wc-card button:hover {
    border-color: var(--ew-magenta);
    color: var(--ew-magenta);
}
.wc-card button:active {
    background-color: var(--ew-magenta);
    border-color: var(--ew-magenta);
    color: #fff;
}

/* --------------------------------------------------------------------------
   Download links — vendored sheet hardcodes Microsoft blue #0078d7
   -------------------------------------------------------------------------- */

.wc-message-from-bot a.wc-link-download:hover,
.wc-message-from-bot a.wc-link-download:active {
    color: var(--ew-magenta);
}

/* --------------------------------------------------------------------------
   Scrollbar
   -------------------------------------------------------------------------- */

.wc-app ::-webkit-scrollbar { width: 7px; }
.wc-app ::-webkit-scrollbar-thumb {
    background-color: rgba(79, 38, 131, 0.22);
    border-radius: 999px;
}
.wc-app ::-webkit-scrollbar-thumb:hover {
    background-color: rgba(79, 38, 131, 0.38);
}

/* --------------------------------------------------------------------------
   Focus visibility

   Keyboard focus must stay visible. :focus-visible keeps the ring off
   pointer users without taking it away from keyboard users.

   Drawn with box-shadow rather than outline. `outline` ignores border-radius
   on older WebKit/Gecko, so a square ring appeared around the pill-shaped
   composer and read as a validation error rather than a focus cue. box-shadow
   always follows the element's radius.

   Purple, not magenta: #AF006E is a real brand colour, but as a thin 2px line
   against a light surface it reads as crimson -- i.e. as an error state. The
   purple carries the same brand weight without that false signal.

   Two stacked shadows: a white inner separator so the ring stays legible on
   filled controls, then the purple ring itself.
   -------------------------------------------------------------------------- */

.wc-app button:focus-visible,
.wc-app input:focus-visible,
.wc-app textarea:focus-visible,
.ew-status button:focus-visible {
    outline: 2px solid transparent;   /* preserved for forced-colors mode */
    outline-offset: 2px;
    box-shadow:
        0 0 0 2px var(--ew-surface),
        0 0 0 4px var(--ew-purple);
}

/* Windows High Contrast / forced-colors flattens box-shadow away entirely, so
   hand the ring back to outline there -- otherwise focus becomes invisible. */
@media (forced-colors: active) {
    .wc-app button:focus-visible,
    .wc-app input:focus-visible,
    .wc-app textarea:focus-visible,
    .ew-status button:focus-visible {
        outline: 2px solid Highlight;
        box-shadow: none;
    }
}

/* --------------------------------------------------------------------------
   Status / loading / error surface
   -------------------------------------------------------------------------- */

.ew-status {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ew-canvas);
    z-index: 10;
    padding: 24px;
}
.ew-status[hidden] { display: none; }

.ew-status__box { text-align: center; max-width: 320px; }

.ew-status__spinner {
    width: 34px;
    height: 34px;
    margin: 0 auto 16px;
    border: 3px solid rgba(79, 38, 131, 0.16);
    border-top-color: var(--ew-magenta);
    border-radius: 50%;
    animation: ew-spin 0.9s linear infinite;
}
@keyframes ew-spin { to { transform: rotate(360deg); } }

.ew-status__icon { font-size: 30px; line-height: 1; margin-bottom: 12px; }

/* The guide's chevron device: headline enclosed by two inward-facing
   chevrons, left purple, right magenta. */
.ew-status__title {
    font-size: 16px;
    font-weight: 700;
    color: var(--ew-magenta);
    margin: 0 0 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
}
.ew-status__title::before,
.ew-status__title::after {
    font-size: 19px;
    font-weight: 800;
    line-height: 1;
}
.ew-status__title::before { content: "\276F"; color: var(--ew-purple); }
.ew-status__title::after  { content: "\276E"; color: var(--ew-magenta); }

.ew-status__detail {
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--ew-warm-gray);
    margin: 0 0 20px;
}

.ew-status__retry {
    appearance: none;
    border: 0;
    cursor: pointer;
    background: linear-gradient(135deg, var(--ew-purple), var(--ew-magenta));
    background-color: var(--ew-magenta);
    color: #fff;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    padding: 11px 26px;
    border-radius: 999px;
}
.ew-status__retry:hover  { filter: brightness(1.08); }
.ew-status__retry:active { filter: brightness(0.92); }

/* --------------------------------------------------------------------------
   Responsive

   Mobile and desktop are the two audiences. On a narrow screen the client
   is the whole window. On a wide one it is a centred, contained panel
   rather than a chat log stretched across 1440px, which is unreadable.
   -------------------------------------------------------------------------- */

@media (min-width: 900px) {
    body { background: #EFEBF4; }

    /* The panel stays WIDTH-constrained but is now FULL HEIGHT.
       
       It previously used height: min(84vh, 820px) with centring margins,
       which left dead bands above and below the client: measured at 72px top
       and 70px bottom on a 1440x900 display, and 130px/128px on 1920x1080.
       On a tall monitor that is a quarter of the screen spent on empty
       canvas while the message pane scrolls inside a short box.

       Width is deliberately still capped. A chat log stretched across
       1440px is unreadable, so the horizontal constraint is doing real
       work; the vertical one was only ever making the panel look like a
       floating card. */
    .ew-shell {
        position: relative;
        inset: auto;
        width: min(760px, 100%);
        height: 100vh;
        height: 100dvh;
        margin: 0 auto;
        /* No radius and no shadow: both only make sense on a detached card.
           Edge to edge, a rounded corner would cut the header against the
           viewport edge and the shadow would have nothing to fall on. */
        border-radius: 0;
        box-shadow: none;
        border: 0;
        border-left: 1px solid var(--ew-hairline);
        border-right: 1px solid var(--ew-hairline);
    }

    /* Corners squared off to match the full-height panel. */
    .ew-header { border-radius: 0; }
    #BotChatElement { border-radius: 0; overflow: hidden; }

    .wc-message { max-width: 72%; }
}

@media (max-width: 360px) {
    .ew-header { padding: 10px 12px; }
    .ew-header__avatar { width: 40px; height: 40px; }
    /* Stacked Install/Inbox must fit the 40px avatar row here too. */
    .ew-header__actions { gap: 2px; }
    .ew-header__actions .ew-header__install:not([hidden]) ~ .ew-header__install,
    .ew-header__actions .ew-header__install:not([hidden]):has(~ .ew-header__install) {
        padding-top: 2px;
        padding-bottom: 2px;
    }
    /* Tighter gutter on the narrowest phones; the attachment button loses
       margin, never its 44px hit area. */
    .wc-console .wc-upload { left: 6px; }
    .wc-console .wc-textbox { left: 52px; }
    .ew-header__logo { height: 19px; }
    .wc-message { max-width: 88%; }
}

/* Respect a stated preference for reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .ew-status__spinner { animation-duration: 2.4s; }
    .wc-message-wrapper { animation: none; }
    * { transition-duration: 0.01ms !important; }
    .ew-push-detail--over .ew-push-detail__card { animation: none; }
    .ew-inbox--shifted .ew-install-sheet__box { animation: none; transform: translateX(-100%); }
}

/* ===================================================================
   ATTACHMENT CUE
   ===================================================================
   When the bot asks for a file, it sets channelData.attachmentCue and
   chat.html adds .ew-cue to the upload label. The paperclip then pulses
   so the customer can see WHERE to upload -- the prompt text says what
   to do, this says where.

   Scoped to .wc-console .wc-upload.ew-cue so it inherits every layout
   rule above (position, 40px circle, 44px hit area) and only overrides
   paint. Nothing here changes geometry, so the cue cannot shift the
   composer or reflow the bar.

   The class is removed as soon as a file is chosen -- see chat.html.
   ------------------------------------------------------------------- */

/* Animate a separate, pointer-transparent layer. The label's own shadow is
   reserved for keyboard focus, so animation cannot override the focus ring.
   Neither layer changes geometry or the existing 44px hit target. */
@keyframes ew-cue-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(79, 38, 131, 0.45); }
    /* 20px button radius + 60px spread = 80px outer radius (4x). */
    100% { box-shadow: 0 0 0 60px rgba(79, 38, 131, 0); }
}

.wc-console .wc-upload.ew-cue {
    background: var(--ew-purple);
}

.wc-console .wc-upload.ew-cue::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    animation: ew-cue-pulse 1.4s ease-out 6;
}

/* The solid fill and white icon persist after the brief attention animation.
   Keep the same white icon on hover/focus; do not inherit idle purple. */
.wc-console .wc-upload.ew-cue svg {
    fill: #fff;
}

.wc-console .wc-upload.ew-cue:hover {
    background: #613399;
}

.wc-console .wc-upload.ew-cue:active {
    background: #3D1D66;
}

.wc-console .wc-upload:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--ew-surface), 0 0 0 4px var(--ew-purple);
}

/* Reduced motion keeps the solid highlight but removes the attention pulse. */
@media (prefers-reduced-motion: reduce) {
    .wc-console .wc-upload.ew-cue::before {
        animation: none;
    }
}

/* High contrast uses system colours and a real outline (shadows are dropped).
   Keep a separate, outer focus outline so cue and keyboard focus coexist. */
@media (forced-colors: active) {
    .wc-console .wc-upload.ew-cue {
        background: Highlight;
        forced-color-adjust: none;
        outline: 2px solid Highlight;
        outline-offset: -2px;
    }
    .wc-console .wc-upload.ew-cue svg { fill: HighlightText; }
    .wc-console .wc-upload.ew-cue::before { animation: none; }
    .wc-console .wc-upload:focus-visible {
        outline: 2px solid CanvasText;
        outline-offset: 2px;
        box-shadow: none;
    }
}
