/* ============================================================
   Handshake v1 → v2 component bridge

   hs-bridge.css remaps tokens. This file restyles v1's COMPONENT CLASSES to
   the v2 designs — buttons, tabs, chips, filters — without touching the
   markup that produces them. The workspace UI is 33k lines of client-rendered
   HTML; restyling by selector is the only way to convert it safely.

   Scope discipline: colour, border, radius, type and spacing only. Nothing
   here changes display, position or flow, so layout cannot break.

   Load after hs-bridge.css.
   ============================================================ */

/* ============================================================
   Buttons — v1 has four families (.hs-btn, .hsb--*, .hs-adm-btn,
   .hs-cs-btn). All four become the adopted glass treatment.
   ============================================================ */
[data-hs="2"] :is(.hs-btn, .hsb, .hs-adm-btn, .hs-cs-btn) {
  border-radius: var(--hs-radius-pill);
  font-family: var(--hs-font-ui);
  font-weight: var(--hs-weight-semibold);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-rim);
  transition:
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    border-color var(--hs-duration-fast) var(--hs-ease-standard);
}

/* ============================================================
   THREE BUTTON STYLES, everywhere

     primary    ink fill, white text     the thing to do. One per screen.
     secondary  white fill, ink text     the alternative. Always available.
     quiet      text only                inline and dismissive actions.
     danger     red fill                 RESERVED for destructive only.

   Two changes from what was here before.

   1. Primary is INK, not aquamarine. A green-filled button was arriving as the
      loudest thing on a page for actions that did not warrant it — "Apply
      selected" outshouted the content it applied to. Ink reads unambiguously as
      the primary action at 17.2:1 and stops competing. Aquamarine keeps the job
      it is genuinely good at, marking STATE: active nav, selected borders,
      focus rings, switches, data marks. It is no longer a button fill anywhere.

   2. Ghost is an alias of secondary. The system had four neutral-ish tiers
      (0.88 filled, outlined, text-only, plus soft) and Business info alone
      rendered six distinct button treatments. Four names for three jobs is how
      that happens. Every legacy class below now resolves to one of three.
   ============================================================ */
[data-hs="2"] :is(.hs-btn--primary, .hsb--primary, .hs-adm-btn--primary, .hs-btn-primary,
  .hs-cs-btn--primary) {
  background: var(--hs-fg-primary);
  color: var(--hs-fg-inverse);
  border-color: transparent;
  box-shadow: var(--hs-elevation-raised);
}

[data-hs="2"] :is(.hs-btn--primary, .hsb--primary, .hs-adm-btn--primary, .hs-btn-primary,
  .hs-cs-btn--primary):hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--hs-neutral-800);
}

[data-hs="2"] :is(.hs-btn--primary, .hsb--primary, .hs-adm-btn--primary, .hs-btn-primary,
  .hs-cs-btn--primary):active:not(:disabled) {
  background: var(--hs-neutral-900);
}

/* Secondary — the one neutral style. `.hs-btn-soft`, `--ghost` and
   `.hs-cs-btn` were all separate names for it. */
[data-hs="2"] :is(.hs-btn--secondary, .hsb--secondary, .hs-btn-secondary, .hs-btn-soft,
  .hs-adm-btn--secondary, .hs-btn--ghost, .hsb--ghost, .hs-btn-ghost, .hs-adm-btn--ghost,
  .hs-cs-btn) {
  background: var(--hs-glass-control);
  color: var(--hs-fg-primary);
  border: 1px solid var(--hs-glass-rim);
  box-shadow: var(--hs-glass-lift);
}

[data-hs="2"] :is(.hs-btn--secondary, .hsb--secondary, .hs-btn-secondary, .hs-btn-soft,
  .hs-adm-btn--secondary, .hs-btn--ghost, .hsb--ghost, .hs-btn-ghost, .hs-adm-btn--ghost,
  .hs-cs-btn):hover:not(:disabled) {
  filter: brightness(1.04);
}

/* Quiet — no fill, no border. Cancel, Dismiss, Skip. */
[data-hs="2"] :is(.hs-btn--quiet, .hs-btn-quiet, .hs-adm-btn--quiet) {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--hs-fg-secondary);
  text-decoration: none;
}

[data-hs="2"] :is(.hs-btn--quiet, .hs-btn-quiet, .hs-adm-btn--quiet):hover:not(:disabled) {
  background: var(--hs-glass-control-ghost);
  color: var(--hs-fg-primary);
}

/* ---- Aqua is no longer a button fill -----------------------------------
   Three controls painted themselves accent green directly rather than through
   a variant: the voice call action, the website-hero submit disc, and the
   scheduled-task submit. They take the primary ink treatment like everything
   else. `.call-action.live` keeps danger — it ends a call. */
[data-hs="2"] :is(.call-action, .hs-td-wcard__go, .hs-st-composer__go) {
  background: var(--hs-fg-primary);
  color: var(--hs-fg-inverse);
  border-color: transparent;
}

[data-hs="2"] :is(.call-action, .hs-td-wcard__go, .hs-st-composer__go):hover:not(:disabled) {
  background: var(--hs-neutral-800);
}

[data-hs="2"] .call-action.live {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-on-solid);
}

/* Ink / dark */
[data-hs="2"] :is(.hsb--ink, .hs-btn--ink) {
  background: var(--hs-fg-primary);
  color: var(--hs-bg-raised);
  border-color: transparent;
}

/* Danger */
[data-hs="2"] :is(.hs-btn--danger, .hsb--danger, .hs-btn-danger) {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-on-solid);
  border-color: transparent;
}

[data-hs="2"] :is(.hs-btn-danger-ghost, .hsb--danger-ghost) {
  background: var(--hs-danger-bg);
  color: var(--hs-danger-fg);
  border-color: var(--hs-danger-border);
}

/* ============================================================
   Tabs — v1 has five separate tab implementations. Normalise all of
   them onto the v2 underline treatment.
   ============================================================ */
[data-hs="2"] :is(.hs-tabs, .tabs, .tabs-u, .tabs-e, .hs-adm-prompttabs,
  .hs-adm-grouptabs, .hs-adm-agentpaneltabs) {
  border-bottom: 1px solid var(--hs-border-subtle);
}

[data-hs="2"] :is(.hs-tabs, .tabs, .tabs-u, .tabs-e, .hs-adm-prompttabs,
  .hs-adm-grouptabs, .hs-adm-agentpaneltabs) > button {
  position: relative;
  border: 0;
  border-radius: 0;
  background: transparent;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-tertiary);
  box-shadow: none;
}

[data-hs="2"] :is(.hs-tabs, .tabs, .tabs-u, .tabs-e, .hs-adm-prompttabs,
  .hs-adm-grouptabs, .hs-adm-agentpaneltabs) > button:hover {
  color: var(--hs-fg-primary);
  background: transparent;
}

[data-hs="2"] :is(.hs-tabs, .tabs, .tabs-u, .tabs-e, .hs-adm-prompttabs,
  .hs-adm-grouptabs, .hs-adm-agentpaneltabs) > button:is(.is-active, .active, [aria-selected="true"]) {
  color: var(--hs-fg-primary);
  background: transparent;
}

[data-hs="2"] :is(.hs-tabs, .tabs, .tabs-u, .tabs-e, .hs-adm-prompttabs,
  .hs-adm-grouptabs, .hs-adm-agentpaneltabs) > button:is(.is-active, .active, [aria-selected="true"])::after {
  content: "";
  position: absolute;
  inset-inline: 8px;
  inset-block-end: -1px;
  height: 2px;
  border-radius: var(--hs-radius-pill);
  background: var(--hs-accent-solid);
}

/* ============================================================
   Chips — v1's .hs-chip predates .hs-badge. Same look, and the status
   dot is suppressed: a dot in a static pill reads as a live indicator.
   ============================================================ */
[data-hs="2"] :is(.hs-chip, .hs-conv-fchip, .hs-adm-agent-ix__chip) {
  border-radius: var(--hs-radius-pill);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-semibold);
}

[data-hs="2"] :is(.hs-chip, .hs-conv-fchip)::before {
  display: none !important;
}

[data-hs="2"] .hs-chip--pass { background: var(--hs-success-bg); color: var(--hs-success-fg); }
[data-hs="2"] .hs-chip--fail { background: var(--hs-danger-bg); color: var(--hs-danger-fg); }
[data-hs="2"] .hs-chip--accent { background: var(--hs-accent-muted); color: var(--hs-accent-ink); }
[data-hs="2"] .hs-chip--neutral {
  background: var(--hs-glass-sunken);
  color: var(--hs-fg-secondary);
  border: 1px solid var(--hs-border-subtle);
}

/* ============================================================
   Filters — v1 places the toggle differently per surface. Pin it to the
   end of its row everywhere so it is always in the same place.
   ============================================================ */
[data-hs="2"] :is(.hs-conv-filters-toggle, .hs-activity-filters-toggle, [class*="filters-toggle"]) {
  margin-inline-start: auto;
  order: 99;
  height: var(--hs-size-control-sm);
  padding-inline: var(--hs-space-3);
  border-radius: var(--hs-radius-pill);
  border: 1px solid var(--hs-glass-rim);
  background: var(--hs-glass-control-quiet);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

[data-hs="2"] :is(.hs-conv-filters-toggle, [class*="filters-toggle"]):is(.is-open, [aria-expanded="true"]) {
  background: var(--hs-accent-muted);
  color: var(--hs-accent-ink);
}

/* Popovers, menus, and modal cards sit above content/scrims, so they stay
   SOLID — the standing exception to glass-everywhere. Background only here;
   each surface keeps its own radius/shadow. */
[data-hs="2"] :is(
  .hs-conv-sheet,
  .hs-conv-ff__pop,
  .hs-adm-more-sheet,
  .hs-portal-more-sheet,
  .hs-rules-cust-pop,
  .hs-portal-profile__menu,
  .hs-feedback-modal__card,
  .hs-st-composer-portal,
  .hs-adm-modal__card,
  .hs-cn-sheet__panel,
  .hs-kb-site-modal__panel,
  .hs-cf-portal__card,
  .hs-tasks-filter-menu,
  .hs-tasks-composer__tag-menu,
  .hs-personal-note-composer__tag-menu,
  .hs-address-suggest__list,
  .hs-filter__pop,
  .hs-filter-sheet,
  .hs-profile__menu
) {
  background: var(--hs-bg-raised);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Filter sheets also get the popover chrome (radius + elevation). */
[data-hs="2"] :is(.hs-conv-sheet, .hs-conv-ff__pop, .hs-adm-more-sheet, .hs-portal-more-sheet) {
  border: 1px solid var(--hs-border-default);
  border-radius: var(--hs-radius-xl);
  box-shadow: var(--hs-elevation-popover);
}

/* ============================================================
   Cards / panels — glass, with a real hairline so they read as containers
   ============================================================ */
[data-hs="2"] :is(.hs-cn-card, .hs-adm-card, .hs-card) {
  background: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-card-border);
  border-radius: var(--hs-radius-xl);
  box-shadow: var(--hs-glass-lift);
}

/* ============================================================
   Agent page heroes — persona dropped for now; phone hero stays so the
   assigned/claim AI number card remains visible.
   ============================================================ */
[data-hs="2"] .hs-persona-hero {
  display: none !important;
}

/* Phone number hero — flat on the canvas (no glass card / frost). */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-sms) .hs-td-hero--phone {
  background: transparent !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border-color: var(--hair, var(--hs-border-default)) !important;
  box-shadow: none !important;
}

/* Phone number readout — flatter under HS v2 (no graphic card chrome). */
[data-hs="2"] .hs-phone-hero-number {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 0.55rem 0.65rem;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
}

[data-hs="2"] .hs-phone-hero-kicker {
  display: block;
  margin: 0 0 0.35rem;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--hs-fg-secondary);
}

[data-hs="2"] .hs-phone-hero-number__value {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-2xl);
  font-weight: var(--hs-weight-bold);
  font-variant-numeric: tabular-nums;
  color: var(--hs-fg-primary);
  white-space: nowrap;
}

/* ============================================================
   Brand marks — no nested shapes

   mark-aquamarine.svg is already a filled circle in the brand green. v1 sat
   it inside a decorated container, which produced a circle nested in a
   rounded square with a second background colour:

     .hs-adm-auth-logo   56px rounded square, aqua→jasmine GRADIENT,
                         1px border, 6px ring — holding a 32px circle
     .ws-unlock__mark    36px square, solid #0E1B11 — holding a 20px circle

   The container contributes nothing the mark does not already carry. Strip
   its shape and let the mark fill the slot: one colour, one shape.
   ============================================================ */
[data-hs="2"] :is(.hs-adm-auth-logo, .ws-unlock__mark, .ws-mobile-topbar__mark,
  .hs-module-checkpoint__mark, .hs-brand-guide__logo-frame) {
  background: none !important;
  background-image: none !important;
  border: 0 !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
  overflow: visible;
}

/* The mark now fills the footprint the container used to occupy. */
[data-hs="2"] :is(.hs-adm-auth-logo, .ws-unlock__mark, .ws-mobile-topbar__mark,
  .hs-module-checkpoint__mark) > :is(img, svg) {
  width: 100% !important;
  height: 100% !important;
  object-fit: contain;
  display: block;
}

/* Keep the footprints these slots were designed around. */
[data-hs="2"] .hs-adm-auth-logo { width: 56px; height: 56px; }
[data-hs="2"] .ws-unlock__mark { width: 36px; height: 36px; }
[data-hs="2"] .ws-mobile-topbar__mark { width: 22px; height: 22px; }

/* Horizontal lockups pair the mark with the wordmark — those keep their
   intrinsic size, they are not icon slots. */
[data-hs="2"] .hs-adm-brand img { width: 30px; height: 30px; }

/* ============================================================
   Per-page gradient tone

   The workspace is a single-page app, so the wash cannot come from a static
   HTML attribute. ds/hs-page-tone.js mirrors the active nav tab onto
   <html data-hs-page="…"> and each page category takes a position on the one
   shared ramp — all Handshake primaries, no new hues.

   Grouped by what the page is FOR, so related pages feel related:
     doing the work      → aqua          (conversations, tasks)
     configuring agents  → aqua → teal   (agents, phone, sms, chat, email)
     wiring things up    → teal → blue   (connectors, information)
     automation          → blue          (routines)
     account & money     → blue → violet (billing, refer, profile)

   AMPLITUDE. The ordering below is the original and it is the right idea.
   The range was not: 0%→100% traverses the entire mint→blue→lilac ramp, so
   Conversations rendered mint and Persona rendered lavender. Two clicks apart,
   two different-looking applications. A background is the one element on a
   screen that must never argue with the user about where they are.

   The whole ramp is now compressed into 0–14%. Because the wash is sized at
   320% of the viewport, 14% of the travel is about a third of one screen
   height along the gradient — you cannot name the difference between two
   pages side by side, but moving from Conversations to Billing does warm
   very slightly. The journey survives; the identity crisis does not.

   The ratios between steps are unchanged. To retune, scale the whole set —
   never widen one entry.
   ============================================================ */
[data-hs="2"][data-hs-page="conversations"]   { --hs-wash-shift: 0%; }
[data-hs="2"][data-hs-page="tasks"]           { --hs-wash-shift: 1.5%; }

[data-hs="2"][data-hs-page="agent-phone"]     { --hs-wash-shift: 3.5%; }
[data-hs="2"][data-hs-page="agent-sms"]       { --hs-wash-shift: 4.5%; }
[data-hs="2"][data-hs-page="agent-chatbot"]   { --hs-wash-shift: 5.5%; }
[data-hs="2"][data-hs-page="agent-social"]    { --hs-wash-shift: 6.5%; }
[data-hs="2"][data-hs-page="agent-email"]     { --hs-wash-shift: 7.5%; }

[data-hs="2"][data-hs-page="information"]     { --hs-wash-shift: 10%; }
[data-hs="2"][data-hs-page="integrations"]    { --hs-wash-shift: 10.5%; }
[data-hs="2"][data-hs-page="scheduled-tasks"] { --hs-wash-shift: 11.5%; }
[data-hs="2"][data-hs-page="ai-timeline"]     { --hs-wash-shift: 12%; }

[data-hs="2"][data-hs-page="billing"]         { --hs-wash-shift: 12.5%; }
[data-hs="2"][data-hs-page="refer"]           { --hs-wash-shift: 13.5%; }
[data-hs="2"][data-hs-page="profile"]         { --hs-wash-shift: 14%; }
[data-hs="2"][data-hs-page="notifications"]   { --hs-wash-shift: 14%; }

/* Notifications — opaque white surface (no frosted glass over a texture). */
[data-hs="2"][data-hs-page="notifications"] {
  --hs-glass-card: #ffffff;
  --hs-glass-control: #ffffff;
  --hs-glass-control-quiet: #ffffff;
  --hs-glass-control-ghost: #f4f6f4;
  --hs-glass-card-border: var(--hs-border-default);
  --hs-glass-raised: #ffffff;
  --hs-glass-sunken: #f4f6f4;
  --hs-glass-rim: var(--hs-border-default);
  --hs-glass-blur: none;
  --hs-glass-lift: var(--hs-elevation-raised);
  --hs-glass-lift-strong: var(--hs-elevation-overlay);
  --panel: #ffffff;
}

/* Default for the single-page shells before a nav tab is active. Static pages
   keep their inline --hs-wash-shift; the SPAs must not, or it outranks the
   per-page rules above. Canvas stays static — no scroll/tap/nav wash motion. */
[data-hs="2"]:not([data-hs-page]) { --hs-wash-shift: 1%; }

/* ============================================================
   Page titles — liquid glass on mobile, plain on desktop

   Mobile fixed titles need a soft frost so content can scroll underneath
   without text-on-text collision. Glass lives on the head itself (not a
   z-index:-1 ::before) so backdrop-filter can sample scrolling content.
   Saturate stays under 1 so the canvas photo/wash is not amplified — the
   frost picks up card/content colour instead. A mask fades the glass out
   below the title so there is no hard line.
   ============================================================ */
[data-hs="2"] {
  --hs-title-glass-bg: rgba(247, 248, 244, 0.4);
  --hs-title-glass-blur: blur(28px) saturate(0.88) brightness(1.03);
  /* Fade is always the last ~36px (runway), so taller heads stay solid. */
  --hs-title-glass-mask: linear-gradient(
    to bottom,
    #000 0,
    #000 calc(100% - 36px),
    rgba(0, 0, 0, 0.55) calc(100% - 16px),
    transparent 100%
  );
}

[data-hs="2"] :is(.hs-portal-section__head, .hs-conv-head)::before {
  content: none !important;
}

[data-hs="2"] :is(.hs-portal-section__head, .hs-conv-head) {
  background: transparent;
  background-image: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  -webkit-mask-image: none;
  mask-image: none;
  isolation: auto;
  /* Shared rhythm — a little more room above/below so every page title
     sits at the same height and descenders are not cramped. */
  padding-block: var(--hs-space-4);
  margin-bottom: var(--hs-space-4);
}

[data-hs="2"] .hs-portal-section__title {
  line-height: 1.25;
  overflow: visible;
}

@media (max-width: 899px) {
  [data-hs="2"] :is(.hs-portal-section__head, .hs-conv-head) {
    /* Extra bottom padding is the fade runway — title stays in the solid mask. */
    padding: var(--hs-space-4) var(--hs-space-5) calc(var(--hs-space-4) + 22px);
    background: var(--hs-title-glass-bg);
    backdrop-filter: var(--hs-title-glass-blur);
    -webkit-backdrop-filter: var(--hs-title-glass-blur);
    -webkit-mask-image: var(--hs-title-glass-mask);
    mask-image: var(--hs-title-glass-mask);
  }

  /* The dock floats over the page and nothing reserved room for it, so the
     last stretch of every screen sat underneath it — on a form, that is the
     save button.

     The padding has to go on the element that actually SCROLLS, which in this
     shell is an inner pane, not the page: <body> is fixed at viewport height
     with overflow hidden, and .hs-portal-content-pane / .hs-portal-section__body
     carry `overflow-y: auto`. Putting it on the layout ancestors instead just
     inflates their height inside a clipped parent and pushes the last control
     out of reach entirely.

     The :not(:has(…)) keeps the two from stacking when a section body contains
     a content pane — otherwise nested scrollers each reserve the dock. */
  [data-hs="2"] .hs-portal-content-pane,
  [data-hs="2"] .hs-portal-section__body:not(:has(.hs-portal-content-pane)) {
    padding-bottom: calc(var(--hs-dock-h) + var(--hs-space-6) + env(safe-area-inset-bottom, 0px));
    scroll-padding-bottom: calc(var(--hs-dock-h) + var(--hs-space-6) + env(safe-area-inset-bottom, 0px));
  }
}

/* Desktop / web: titles scroll away with the page — no sticky pin. */
@media (min-width: 900px) {
  [data-hs="2"] :is(.hs-portal-section__head, .hs-conv-head) {
    position: static;
  }
}

/* The page container must stay transparent or it hides the wash entirely.
   poc5-pages.css sets `background: #fff !important` on .hs-portal-page, so
   this needs !important to win — the only place in the bridge that does. */
[data-hs="2"] :is(.hs-portal-page, .hs-portal-main, .hs-portal-shell, .hs-portal-panel, .hs-portal-section) {
  background: transparent !important;
}

/* ============================================================
   Routines — sentence composer

   The violet/green field tints are NOT inconsistent styling: `is-fill-needed`
   marks a slot you still have to complete, `is-fill-set` one you have. It is a
   progress indicator inside the sentence, and it should stay.

   What is wrong is that both colours are hardcoded values outside the palette
   (rgb(242,231,255) violet, rgb(230,251,241) green). Map them onto the tokens
   that already carry those meanings: jasmine for "needs you", aqua for "done".
   ============================================================ */
/* The composer routes its states through two tokens, so mapping those covers
   every variant (.is-pulse.is-fill-needed and friends) instead of chasing
   class combinations that out-specify a single override. */
[data-hs="2"] .hs-st-composer,
[data-hs="2"] .hs-st-composer__sheet {
  --st-needed: var(--hs-accent2-solid);
  --st-needed-soft: var(--hs-accent2-subtle);
}

[data-hs="2"] .hs-st-composer__pill.is-fill-set {
  background: var(--hs-accent-subtle);
  border-color: var(--hs-accent-solid);
  color: var(--hs-accent-ink);
}

/* The composer card itself takes the glass treatment like every other card. */
[data-hs="2"] .hs-st-composer__sheet {
  background: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-card-border);
  border-radius: var(--hs-radius-xl);
  box-shadow: var(--hs-glass-lift);
}

/* The "needs filling" pulse animates border and background directly, and an
   animated value beats a normal declaration — so mapping --st-needed-soft alone
   never showed. Redefining the keyframes under the same name replaces the
   original (last definition wins) and keeps the pulse, in jasmine. */
@keyframes hs-st-needed-pulse {
  0%,
  100% {
    border-color: var(--hs-accent2-ring);
    background: var(--hs-accent2-subtle);
  }
  50% {
    border-color: var(--hs-accent2-solid);
    background: var(--hs-accent2-muted);
  }
}

/* ============================================================
   Connectors — Wallet stack

   integrations.js now wraps each connector in .hs-cw-card instead of a table
   row. Everything else about the markup is unchanged: the logo, name,
   capability list, Private tag and action button are the same cells the grid
   used, so the data flow and wiring are untouched.

   Stacking works the way the design-system wallet does: normal flow with a
   fixed negative margin, so the peek is exact and opening a card simply makes
   it taller — the cards below slide down. No absolute positioning.
   ============================================================ */
[data-hs="2"] .hs-connectors-layout {
  /* The peek must clear the title with real space under it. At 66px against a
     112px card the fold landed 25px below the title baseline with dead space
     between, which reads as the title being cut off even though it is not.
     82px shows the whole head plus breathing room and still overlaps enough to
     read as a stack rather than a list. */
  --hs-cw-peek: 82px;
  --hs-cw-card-h: 112px;
  display: flex;
  flex-direction: column;
}

[data-hs="2"] .hs-cw-card {
  /* THE declaration this whole section depended on. Above 900px v1 sets
     `.hs-connectors-layout__row { display: contents }` so the cells become
     direct children of a 4-column grid. With `display: contents` the article
     has NO BOX, so height, radius, glass, shadow and overflow were all
     discarded and the page rendered as the flat table it always was. The card
     needs a box before any of the rest of this can mean anything. */
  /* Grid, not height.

     The expansion used `height: var(--hs-cw-card-h)` → `height: auto`. You
     cannot transition to or from `auto`, so the card SNAPPED between sizes and
     the only thing that actually animated was the neighbour's margin-top. On
     open that reads as the card popping and the stack shuffling after it; on
     close the card vanishes to its collapsed size instantly and the cards below
     spend 420ms crawling up to meet it — which is what "collapses on top of the
     cards below" looks like.

     A two-row grid animating 0fr → 1fr interpolates properly, so the card's own
     height, its neighbours' margins and the re-layering all move together and
     it settles back into the stack in one motion. min-height keeps the 112px
     footprint the 82px peek is measured against, so the tuck is unchanged. */
  display: grid;
  /* Own the COLUMNS as well as the rows.
     `.hs-connectors-layout__row` is the same element as `.hs-cw-card`, and v1
     styles that class as a row of a multi-column table. Declaring only
     grid-template-rows left v1's column template in place, so the card resolved
     to `0px 338px`: the head was crushed into a zero-width column and the body
     filled the other one at full height. On a phone that is exactly what the
     user sees — every card looks permanently expanded with no logo or title,
     because the header has no width to render in. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 0fr;
  /* And the AREAS. This is the declaration that actually mattered.

     poc5-pages.css:4646 gives `.hs-connectors-layout__row` — the same element as
     .hs-cw-card — `grid-template-areas: "name action" "meta action"`, from when
     the row's direct children WERE the table cells. The card's children are now
     a head and a body, so those names match nothing, but a named-area template
     still DEFINES THE COLUMN COUNT. Overriding grid-template-columns alone did
     nothing: the areas kept the grid at two columns, the head auto-placed into
     the first one at 0px, and the body filled the second. Even an inline
     `grid-template-columns` could not fix it, because the constraint was coming
     from a different property. */
  grid-template-areas: none;
  grid-auto-flow: row;
  position: relative;
  min-height: var(--hs-cw-card-h);
  overflow: hidden;
  border-radius: var(--hs-radius-xl);
  background: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-card-border);
  box-shadow: var(--hs-glass-lift-strong);
  cursor: pointer;
  transform: translateY(0) scale(1);
  transform-origin: center top;
  transition:
    grid-template-rows var(--hs-duration-expand) var(--hs-ease-spring),
    margin-top var(--hs-duration-expand) var(--hs-ease-spring),
    box-shadow var(--hs-duration-base) var(--hs-ease-spring),
    transform var(--hs-duration-base) var(--hs-ease-spring);
}

/* Overlap so every logo and name peeks through. Fixed px, never a percentage —
   percentage margins resolve against width, not height. */
[data-hs="2"] .hs-cw-card + .hs-cw-card {
  margin-top: calc(var(--hs-cw-peek) - var(--hs-cw-card-h));
  z-index: 1;
}

/* Rise out of the stack — do not just raise z-index. Collapsed cards are taller
   than the peek (the excess is tucked under the next card); lifting z-index alone
   paints that tucked band over the connector below and reads as an expand.
   Pull the card forward and open a breath under it so it leaves the pile. */
@media (hover: hover) and (pointer: fine) {
  [data-hs="2"] .hs-cw-card:not([data-open]):hover {
    transform: translateY(-10px) scale(1.02);
    z-index: 5;
    box-shadow: var(--hs-elevation-popover);
  }

  [data-hs="2"] .hs-cw-card:not([data-open]):hover + .hs-cw-card {
    margin-top: var(--hs-space-3);
  }
}

[data-hs="2"] .hs-cw-card[data-open] {
  grid-template-rows: auto 1fr;
  cursor: default;
  box-shadow: var(--hs-elevation-popover);
  z-index: 6;
}

[data-hs="2"] .hs-cw-card[data-open] + .hs-cw-card {
  margin-top: var(--hs-space-2);
}

[data-hs="2"] .hs-cw-card > * > * {
  border-bottom: 0 !important;
}

[data-hs="2"] .hs-cw-card__head {
  display: flex;
  align-items: center;
  gap: var(--hs-space-4);
  padding: var(--hs-space-5);
}

/* The name takes the slack; the trailing cells keep a fixed lane so every
   Private tag, account and button lines up down the stack. */
[data-hs="2"] .hs-cw-card .hs-connector-name { flex: 1 1 auto; min-width: 0; }
[data-hs="2"] .hs-cw-card .hs-connector-perm { flex: 0 0 80px; }
[data-hs="2"] .hs-cw-card .hs-connector-account { flex: 0 0 120px; text-align: end; }
[data-hs="2"] .hs-cw-card .hs-connector-action { flex: 0 0 132px; display: flex; justify-content: flex-end; }

/* The peek comes from the overlap ALONE, not from shrinking the card. Setting
   height:peek here as well applied it twice — card 66px with a -46px margin
   left only 20px of each card showing, which clipped the logo and name. A card
   at its full 112px offset by -46px shows exactly the 66px peek. */
/* Centre the collapsed head in the strip that is actually visible.

   The head was 61px tall at the top of an 82px peek, so the logo and name sat
   in the upper half with 36px of dead space beneath them — the card read as
   top-heavy and slightly cut off. Giving the head the peek's own height and
   letting flex centre it puts the logo on the optical centre line of the
   visible band, and it stays correct if --hs-cw-peek is ever retuned. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head {
  min-height: var(--hs-cw-peek);
  padding-block: var(--hs-space-3);
}

/* A collapsed card is a single row, so everything in it centres. Only an open
   card pins its trailing cells to the top while the body grows below. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head > * {
  align-self: center;
}

/* 28px was the table-row logo size. In an 82px strip it reads small; 32px is
   the avatar-md step and holds the row without crowding it. */
[data-hs="2"] .hs-cw-card .hs-connector-logo {
  width: var(--hs-size-avatar-md);
  height: var(--hs-size-avatar-md);
  flex: none;
  object-fit: contain;
}

[data-hs="2"] .hs-cw-card .hs-connector-name__text strong {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-base);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: var(--hs-tracking-tight);
}

/* The capability list now lives in the card body (see connectorGridRow), so it
   is clipped with the rest of the body rather than toggled with `display`. */
[data-hs="2"] .hs-cw-card .hs-connector-caps {
  margin: 0;
}

[data-hs="2"] .hs-cw-card__caps {
  padding-block-end: var(--hs-space-4);
}

[data-hs="2"] .hs-cw-card__caps .hs-connector-caps {
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-1);
  padding-inline-start: var(--hs-space-4);
}

/* Keep the action and tag aligned to the top of the card while it grows. */
[data-hs="2"] .hs-cw-card[data-open] :is(.hs-connector-action, .hs-connector-perm,
  .hs-connector-account) {
  align-self: flex-start;
}

/* The section labels are not cards — keep them out of the stack. */
[data-hs="2"] .hs-connectors-layout__section {
  margin-top: var(--hs-space-5);
  margin-bottom: var(--hs-space-2);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-bold);
  letter-spacing: var(--hs-tracking-caps);
  text-transform: uppercase;
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-connectors-layout__section:first-child { margin-top: 0; }

/* ============================================================
   Agent preview stages — no backdrop images

   Phone, SMS and Chatbot each sat their widget on a decorative PNG:
     .hs-phone-preview-stage    phone-preview-backdrop.png
     .hs-sms-preview-stage      sms-preview-backdrop.png
     .hs-chatbot-preview-stage  chatbot-preview-backdrop.png

   All three are removed. The page wash is the backdrop now — a photographic
   image behind a translucent widget fights the glass and gives it a texture
   that changes per channel, which is the opposite of one material language.
   Social and Email never had one, so those two pages already read correctly
   and are the reference.
   ============================================================ */

/* Container removed entirely, not just its image.

   The stage was a flat rgb(201,216,239) panel with a 24px radius sitting
   behind the widget. With the backdrop image gone it read as an empty box, so
   the whole container goes: the chat, phone and SMS previews now float
   directly on the page wash. Vertical padding is kept so they are not flush
   against the section edges. */
/* .hs-chatbot-preview-stage is the live class. This rule was written against
   .hs-dialoguebot-preview-stage — a name from a later rename that the markup
   never used — so the chatbot page kept its indigo photographic backdrop while
   phone and SMS lost theirs. A selector that matches nothing fails silently,
   which is how one page ended up in a palette the product does not own. */
[data-hs="2"] :is(.hs-chatbot-preview-stage, .hs-dialoguebot-preview-stage,
  .hs-phone-preview-stage, .hs-sms-preview-stage) {
  background: none !important;
  background-color: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  padding-inline: 0 !important;
  padding-block: var(--hs-space-6) !important;
}

/* ---- The full-bleed hero band ------------------------------------------
   On phones the same three stages did something further: they set
   `width: 100vw`, `left: 50%`, `transform: translateX(-50%)` and a NEGATIVE
   top margin (`-1 * --ws-agent-hero-chrome`) so the backdrop grew upward
   underneath the toolbar and the page title, with a ::before painting a
   100vw band on top of that.

   With the backdrop image gone the band is a coloured slab with nothing to
   justify it — and because it grows upward it lands on whatever is above,
   which on the phone agent is the "business info is incomplete" warning. Half
   the warning was covered by a blue rectangle.

   The whole mechanism goes. These previews now sit in the normal flow on the
   page wash, which is what Social and Email already do. */
@media (max-width: 899px) {
  [data-hs="2"] :is(.hs-chatbot-preview-stage, .hs-dialoguebot-preview-stage,
    .hs-phone-preview-stage, .hs-sms-preview-stage) {
    position: relative !important;
    left: auto !important;
    width: auto !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: var(--hs-space-2) 0 var(--hs-space-4) !important;
    transform: none !important;
    overflow: visible !important;
  }

  [data-hs="2"] :is(.hs-chatbot-preview-stage, .hs-dialoguebot-preview-stage,
    .hs-phone-preview-stage, .hs-sms-preview-stage)::before {
    content: none !important;
  }
}


/* ============================================================
   Segmented controls — Business/Personal, Pay as you go/Monthly

   Two more tab implementations, and neither belongs on the underline style:
     .sgc          Business / Personal      (info + persona subnav)
     .v2-pp-tabs   Pay as you go / Monthly  (billing)

   Both are toggles between two equal modes, not navigation between pages —
   so they take the v2 PILL treatment, matching .hs-tabs--pills. Underline
   tabs imply "you are somewhere"; pills imply "pick one".
   ============================================================ */
/* ---- Segmented control, rebuilt ----------------------------------------
   Three things were wrong and they compounded.

   1. The track was `rgba(255,255,255,0.40)` with a `rgba(255,255,255,0.55)`
      border — pale white on a pale mint wash, so there was no visible control
      at all, just two floating labels.

   2. The geometry could not centre. The track was 48px tall with 3px of padding
      around 40px tabs: 3 + 40 + 3 = 46, not 48, so the pill sat 2px high with
      no way to correct it from the markup.

   3. Billing's thumb is CSS-positioned at `width: calc(50% - 4px)` and slides
      by `translateX(100%)`. That arithmetic is only true if the tabs are equal
      width — and they were content-sized, 110px and 79px. So the thumb matched
      neither tab, and on "Monthly" it translated by its own width and landed
      short of the second tab entirely. That is the broken toggle.

   The fix is a real well with a raised thumb, and geometry that cannot drift:
   equal-width tabs, one padding value, track height = tab + 2×padding. A dark
   recessed tint reads as an inset track on any wash, where a white one
   disappeared into it. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs) {
  display: inline-flex;
  position: relative;
  gap: 0;
  padding: var(--hs-space-1);
  border: 1px solid var(--hs-border-default);
  border-radius: var(--hs-radius-pill);
  /* A well, not a pane: a dark 5% tint separates from the wash in a way that
     another layer of white cannot. */
  background: var(--hs-glass-recessed);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab) {
  /* Equal width. This is what makes the thumb's `calc(50% - 4px)` and
     `translateX(100%)` correct instead of approximate. */
  flex: 1 1 0;
  position: relative;
  z-index: 1;
  min-height: var(--hs-size-control-md);
  padding-inline: var(--hs-space-4);
  border: 0;
  border-radius: var(--hs-radius-pill);
  background-color: transparent;
  background-image: none;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-secondary);
  box-shadow: none;
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--hs-duration-fast) var(--hs-ease-standard);
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab):hover {
  color: var(--hs-fg-primary);
}

/* The thumb is the only marker. The active tab states the selection in its
   text — ink against secondary — and carries no fill of its own, so there is
   never a tinted pill sitting on top of a thumb. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab):is(.is-on, .is-active, .active, [aria-selected="true"]) {
  background-color: transparent !important;
  background-image: none !important;
  color: var(--hs-fg-primary);
  box-shadow: none;
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab)::after {
  display: none;
}

/* ============================================================
   No gradient cards on a gradient background

   A gradient panel on a gradient page reads as two competing washes. These
   cards become plain glass — the page supplies the colour, the card supplies
   the surface.
   ============================================================ */
[data-hs="2"] :is(.hs-billing-plan-card, .hs-billing-plan-card--hero, .v2-pp-card,
  .hs-td-hero, .hs-a2hs-card, .hs-refer-share-card) {
  background-image: none !important;
  background-color: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-card-border);
}

/* ============================================================
   Purchase — v1's own CTA class, outside the four button families
   ============================================================ */
[data-hs="2"] :is(.v2-pp-cta, .v2-sk-brief-btn, .cl-send-btn) {
  height: var(--hs-size-control-lg);
  padding-inline: var(--hs-space-6);
  border: 1px solid var(--hs-glass-rim);
  border-radius: var(--hs-radius-pill);
  background-color: var(--hs-glass-control);
  background-image: none;
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  box-shadow: var(--hs-glass-lift-strong);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

[data-hs="2"] :is(.v2-pp-cta, .v2-sk-brief-btn, .cl-send-btn):hover {
  filter: brightness(1.06);
}

/* ============================================================
   Audit repairs — 2026-08

   Found by measuring the running app rather than reading CSS. Each block
   below cites what was actually wrong.
   ============================================================ */

/* --- 1. Control geometry -------------------------------------------------
   `--hs-control-h` was unset on every .hs-btn in the app: v2's sizing
   convention never reached it, and v1 sets a literal `height` per page
   instead. The result was five heights for one button family, and the SAME
   variant differing by page — hs-btn-primary was 32px on Conversations and
   Phone, 38px on Persona and Information, 44px in the chat composer. That is
   the height inconsistency reported back in the first review round.

   One height for the family. !important is needed because the competing
   declarations are page-scoped (#ws-panel-* .hs-btn) and out-specify any
   class selector. The julia-chat widget is excluded — it is an embeddable
   preview that sizes itself to its own composer. */
[data-hs="2"] :is(.hs-btn, .hsb, .hs-adm-btn):not(.julia-chat-form .hs-btn):not(.hs-btn--lg):not(.hs-btn-lg) {
  height: var(--hs-size-control-md) !important;
  min-height: var(--hs-size-control-md) !important;
  padding-block: 0 !important;
  font-size: var(--hs-text-sm) !important;
  line-height: 1;
}

/* --- 2. Typography on bare controls -------------------------------------
   .hs-voice-option and friends are in v1's `:not()` exclusion list, so no
   author rule set a font on them and they fell back to the UA default —
   rendering literal `Arial` while the rest of the page ran Inter. Only
   font-family is set here: an earlier attempt used the `font` shorthand and
   reset every button's SIZE too, which is what broke the height scale. */
[data-hs="2"] :is(button, input, select, textarea, optgroup) {
  font-family: var(--hs-font-ui);
}

/* --- 3. Contrast repairs ------------------------------------------------
   These classes carry hardcoded v1 greys that never went through the
   contrast gate. Measured ratios against their real painted backgrounds:
     .hs-ptool__blab / .hs-ptool__sub.is-off      3.63:1  (#7A8A82 on white)
     .hs-persona-block__copy and siblings         4.17–4.33:1
     .hs-st-composer__pill-label                  4.27:1
     .v2-pp-tier-label                            4.34:1
     .v2-pp-howto-intro                           4.43:1
   All need 4.5:1 at their size. --hs-fg-secondary (neutral-600) clears it on
   every surface these sit on, and is already covered by both contrast gates.

   Every competing declaration is ID-scoped (#ws-panel-agent-phone .hs-ptool__sub
   and about a dozen siblings), so a class selector cannot reach them. !important
   is the only tool at this specificity that does not mean editing v1 page CSS. */
[data-hs="2"] :is(.hs-ptool__blab, .hs-ptool__sub, .hs-persona-block__copy,
  .hs-persona-slider-row__label, .hs-persona-identity__desc,
  .hs-st-composer__pill-label, .v2-pp-howto-intro) {
  color: var(--hs-fg-secondary) !important;
}

/* 2.73:1 — and a warm grey that is not in the palette at all. */
[data-hs="2"] .v2-pp-covers-label {
  color: var(--hs-fg-secondary) !important;
}

/* 4.34:1 on glass. accent-ink is the darkest accent step and clears it. */
[data-hs="2"] .v2-pp-tier-label {
  color: var(--hs-accent-ink) !important;
}

/* 1.88:1 — the worst in the app. Raw amber-500 as text on a near-white
   surface. The warning role pairs a darker fg with its own tinted bg. */
[data-hs="2"] .hs-pill.pending {
  color: var(--hs-warning-fg) !important;
  background-color: var(--hs-warning-bg) !important;
  border-color: var(--hs-warning-border) !important;
}

/* 3.43:1 — grey secondary text sitting on the aqua widget header. On accent
   fill the paired ink is the only safe value. */
[data-hs="2"] .julia-chat-panel__head .sub {
  color: var(--hs-accent-ink);
}

/* --- 4. Off-scale radii -------------------------------------------------
   The app used 0px, 10px and 14px alongside the 8/12/999 scale.
   .hs-training-input had no radius at all. */
[data-hs="2"] :is(.hs-input, .hs-training-input, .hs-persona-identity__opt) {
  border-radius: var(--hs-radius-md);
}

/* --- 5. Contrast repairs, second pass -----------------------------------
   Three survivors from the first pass, each a different root cause. */

/* 3.97:1 — a v1 green-grey (#6B7F72) missed by the first sweep. */
[data-hs="2"] .hs-agent-history__toggle {
  color: var(--hs-fg-secondary) !important;
}

/* 3.62:1. This sits on the aqua widget header, and accent-ink (aqua-700) is
   only rated against accent-muted — on a saturated aqua FILL the paired role
   is fg-on-accent. This is the v1 trap the contrast gate calls out: aqua is a
   fill colour, never a text colour, and text over it has to go near-black. */
[data-hs="2"] .julia-chat-panel__head .sub {
  color: var(--hs-fg-on-accent);
}

/* 4.34:1 — `.hs-billing-pricing .v2-pp-credits-line strong` hardcodes #1D8A5B.
   Same lesson as above: a mid-ramp green reads as accent on a tinted surface
   but fails on near-white glass. forest-700 keeps the brand green and clears
   AA on every surface in the app. */
[data-hs="2"] .v2-pp-credits-line strong {
  color: var(--hs-fg-link) !important;
}

/* --- 6. Segmented control — equal columns, single-line labels -----------
   These were two fixes fighting each other. "Pay as you go" wrapped onto two
   lines, so an earlier pass set `flex: 0 0 auto` to size each tab to its own
   content. That is exactly what broke billing's thumb: it is positioned at
   `width: calc(50% - 4px)` and slides by `translateX(100%)`, arithmetic that is
   only true when the tabs are equal — and they were 123px and 87px.

   A grid with equal auto-columns satisfies both at once: every column takes the
   width of the widest label, so labels never wrap AND every tab is exactly half
   the track. The track still sizes to its content. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs) {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: fit-content;
  max-width: 100%;
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab) {
  white-space: nowrap;
  /* Equalised by the grid, not by flex — flex-basis:0 inside a fit-content
     container leaves no free space to distribute, so the tabs stayed uneven. */
  flex: none;
  text-align: center;
}

/* --- 7. Off-palette warm surfaces --------------------------------------
   The "How credits work" panel painted #F7F5EF — a warm cream that belongs
   to no Handshake ramp and reads yellow next to the aqua/forest neutrals. */
[data-hs="2"] :is(.v2-pp-howto, .v2-pp-howto-intro, .v2-pp-covers) {
  background-color: var(--hs-glass-sunken);
  background-image: none;
}

/* --- 8. Website-setup hero — the purple was a PNG, not a gradient -------
   The "Start with your website" card is one of the gradient-on-gradient
   cards flagged in review, but its purple is baked into
   /assets/business-website-setup.png, so the flatten rule in section 4 could
   not touch it. v1 sets it with `!important` under an ID scope
   (#ws-panel-agent-training), so !important alone loses on specificity —
   the ID has to be matched. The card and its action stay; only the art goes,
   so the page wash is the one gradient in view. */
[data-hs="2"] #ws-panel-agent-training :is(.hs-td-hero, .hs-td-hero--website-setup, .hs-td-hero--gate) {
  background-image: none !important;
  /* v1 pins a solid white here under the same ID scope, so the glass fill
     needs !important too or the card stays opaque. */
  background-color: var(--hs-glass-card) !important;
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  border: 1px solid var(--hs-glass-card-border);
  /* 190px of top padding reserved the art's space. With the art gone the card
     hugs its content instead of opening with a blank third. */
  padding: var(--hs-space-5) !important;
  min-height: 0;
}

/* The white scrim existed only to keep text legible over that art. */
[data-hs="2"] #ws-panel-agent-training .hs-td-hero::before {
  background-image: none;
}

/* --- 9. Nav hover must not outrank nav active ---------------------------
   Hover painted solid accent-muted while the ACTIVE item got only a faint
   accent gradient — so hovering any item made it look more selected than the
   page you were actually on. Active takes the solid accent pill; hover stays
   a neutral glass tint below it. */
[data-hs="2"] .hs-portal-seg a:hover:not(.active) {
  background: var(--hs-glass-control-ghost);
  background-image: none;
}

[data-hs="2"] .hs-portal-seg a.active {
  background-color: var(--hs-accent-muted);
  background-image: none;
  color: var(--hs-accent-ink);
}

/* --- 10. Refer hero placeholder ----------------------------------------
   Was a three-colour gradient (#a8e6cf, #9ec9f5, #8b6fd4) in no Handshake
   ramp; a brand tint fails safe instead.

   The original rule also forced background-image:none, on the grounds that an
   opaque object-fit:cover photo covered the hero anyway. That photo is gone —
   the card now paints the 7e-refer confetti terrazzo as its background — so
   blanking the image here would erase it. The tint stays as the load-time and
   image-failure fallback; the app layer owns which texture goes on top. */
[data-hs="2"] .hs-refer-share-card__hero {
  background-color: var(--hs-accent-muted);
}

/* ---- Wallet cards on narrow widths ------------------------------------
   Below 900px v1 laid the row out as a grid with named areas
   ("name action" / "meta action"). `display: block` on the card replaces that
   grid, so the cells lose their placement and fall back to the desktop flex
   basis values written above — 80px + 120px + 132px of fixed cells inside
   ~300px of usable width. The name got squeezed to zero and the meta text
   printed over the logo.

   Here the head becomes two rows: name and logo on top, meta beneath, action
   pinned right across both. Nothing is hidden — the same cells, re-flowed. */
@media (max-width: 899px) {
  [data-hs="2"] .hs-cw-card__head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name action"
      "meta action";
    gap: 2px var(--hs-space-3);
    align-items: center;
  }

  [data-hs="2"] .hs-cw-card .hs-connector-name {
    grid-area: name;
    flex: initial;
    min-width: 0;
  }

  /* The permission tag and account share the meta row and no longer reserve
     a fixed track, so a long "Request access" cannot cover the logo. */
  [data-hs="2"] .hs-cw-card .hs-connector-perm,
  [data-hs="2"] .hs-cw-card .hs-connector-account {
    grid-area: meta;
    flex: initial;
    min-width: 0;
    text-align: start;
    font-size: var(--hs-text-xs);
    color: var(--hs-fg-secondary);
  }

  [data-hs="2"] .hs-cw-card .hs-connector-perm {
    justify-self: start;
  }

  [data-hs="2"] .hs-cw-card .hs-connector-account {
    justify-self: end;
  }

  [data-hs="2"] .hs-cw-card :is(.hs-connector-action, .hs-connector-actions) {
    grid-area: action;
    flex: initial;
    align-self: center;
  }

  /* On this layout the meta row sits below the 66px peek line, so the next
     card in the stack clipped it half-way through. Collapsed, a wallet card
     shows its icon and name — the meta comes with the rest of the body when
     the card opens.

     The grid has to drop to ONE row as well. Hiding the meta alone left the
     two-row template in place, and the action cell spans both rows: it kept
     centring across ~90px and hung below the peek line on every card. */
  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head {
    /* Two areas again. This was collapsed to a single "name action" row with a
       fixed peek height so the wallet tuck could not clip a second line. With
       the mobile stack there is nothing tucking, so the account gets its row. */
    grid-template-areas:
      "name action"
      "meta action";
    align-content: center;
    box-sizing: border-box;
  }

  /* Show WHICH ACCOUNT is connected.

     Both meta cells used to be hidden on a collapsed card, because the wallet
     peek only had 82px and one row fit. Mobile is a plain list now with an
     auto-height header, so there is room — and for a connected connector the
     account is the single most useful thing on the row. "Gmail" alone does not
     tell you whether it is the right inbox.

     The permission tag stays hidden: "Private" is the same on every connector,
     so it carries no information in a list. */
  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-connector-perm {
    display: none;
  }

  [data-hs="2"] .hs-cw-card .hs-connector-account {
    font-size: var(--hs-text-xs);
    color: var(--hs-fg-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* An em dash placeholder for a connector with no account is noise on a row
     whose job is to say "this one is connected, as this account". */
  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-connector-account:empty {
    display: none;
  }
}

/* ============================================================
   Component alignment — one left edge per page

   Measured across nine workspace pages: almost every component starts at the
   panel's left edge (0px), but four did not —
     .hs-training-business-shell--gate   226px  (max-width 32rem + margin:0 auto)
     .hs-agent-pane                       22px
     .hs-persona-page-content             22px
   (Connectors also measured 25px, but that is .hs-portal-panel-card's own 24px
   padding plus its border — correct card inset, left alone.)
   The gate was the visible one: the "Start with your website" tile sat 226px in
   while the Change history control below it sat at 0, so the control looked
   closer to the nav than the component it belonged to.

   Centring is dropped, not the measure — a 32rem reading width for a form is
   right, it just starts at the content edge like everything else.
   ============================================================ */
[data-hs="2"] #ws-panel-agent-training .hs-training-business-shell--gate {
  margin-inline: 0;
}

/* These are flex items with `margin: 0 auto` from an ID-scoped rule
   (#ws-panel-agent-* .hs-agent-pane), so auto margins split the slack evenly
   and each pane floated 22-25px in from the content edge. margin-inline-end
   keeps auto so the pane still stops short of the right rail; only the start
   is pinned. !important because 0-2-0 cannot reach 1-1-0. */
[data-hs="2"] :is(.hs-agent-pane, .hs-persona-page-content) {
  margin-inline-start: 0 !important;
  margin-inline-end: auto !important;
}

/* ---- Wallet card: requests and results --------------------------------
   The example exchanges live below the capability list and only exist once a
   card is open. A scroll-snap row is the swipe: native on touch, trackpad and
   shift-scroll on desktop, keyboard-reachable because the list itself is
   focusable. No JS, so nothing to keep in sync with the expansion toggle. */
/* ---- Card body ----------------------------------------------------------
   Everything the expansion reveals lives here: the examples carousel and, for
   a connected account, the manage actions that used to crowd the collapsed
   strip. Hidden with the card so none of it is tabbable while closed. */
/* The animating row. `display: none` would take it out of the grid entirely and
   there would be nothing to interpolate, so it stays in flow and is clipped to
   zero height instead. min-height: 0 is what lets a grid row actually reach 0. */
[data-hs="2"] .hs-cw-card__body {
  /* Explicit block flow. A v1 rule turns the row's element children into flex
     items, so the capability list and the examples carousel sat side by side —
     capabilities squeezed into a 146px column while the carousel took the rest.
     They are sequential sections of one card and stack. */
  display: block;
  min-height: 0;
  overflow: hidden;
  /* One inset for everything in the body, matching the head's 20px, instead of
     each section carrying its own. v1 gives this element 14px; the sections then
     added 20px of their own, and the carousel's `scroll-snap-align: start`
     scrolled straight past that padding — snap positions align to the
     scrollport, not to the padding box — so the example bubbles ended up 15px
     from the card edge while the code said 34. Padding on the body is outside
     the scroll container, so nothing can scroll past it. */
  padding-inline: var(--hs-space-5);
}

/* Keep the contents out of the tab order and off the accessibility tree while
   the card is closed — clipping alone does not do that. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__body {
  visibility: hidden;
}

/* Manage actions sit on the card floor, aligned to the trailing edge like a
   dialog footer — read the card, then act on it. */
[data-hs="2"] .hs-cw-card__manage {
  display: flex;
  justify-content: flex-end;
  gap: var(--hs-space-2);
  padding: var(--hs-space-4) 0 var(--hs-space-5);
  border-top: 1px solid var(--hs-border-subtle);
}

[data-hs="2"] .hs-cw-card__manage .hs-connector-actions {
  display: flex;
  gap: var(--hs-space-2);
}

/* When a card carries examples, the carousel already draws the divider above
   itself — a second one immediately under it would double the line. */
[data-hs="2"] .hs-cw-card__examples + .hs-cw-card__manage {
  border-top: 0;
  padding-top: 0;
}

[data-hs="2"] .hs-cw-card__examples {
  /* Was display:none / block. The body row now owns visibility, and toggling
     display here would have taken this out of flow and left the row with
     nothing to measure mid-animation. */
  padding: var(--hs-space-5) 0 var(--hs-space-6);
  border-top: 1px solid var(--hs-border-subtle);
}

[data-hs="2"] .hs-cw-card__examples-head {
  display: flex;
  align-items: baseline;
  gap: var(--hs-space-2);
  margin: 0 0 var(--hs-space-3);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--hs-fg-secondary);
}

/* Says these are samples, so real inbox and revenue numbers are never implied. */
[data-hs="2"] .hs-cw-card__examples-hint {
  font-size: 10px;
  font-weight: var(--hs-weight-regular, 400);
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-cw-exlist {
  display: flex;
  gap: var(--hs-space-4);
  margin: 0;
  /* No inline padding — the body owns the inset. A scroll container's own
     padding is what scroll-snap ignores. */
  padding: 0 0 var(--hs-space-1);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

/* Each slide is one exchange, rendered in the design system's chat dialogue
   style (hs-dialogue.css) rather than a layout invented here — the card is
   previewing the product, so it should look like the product. The slide only
   owns the carousel geometry; the bubbles own themselves. */
[data-hs="2"] .hs-cw-ex {
  scroll-snap-align: start;
  /* Wide enough to read a full exchange, narrow enough that the next card is
     always partly visible — that peek is what says "there are more". */
  flex: 0 0 min(78%, 320px);
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-3);
  padding: var(--hs-space-5);
  border: 1px solid var(--hs-border-subtle);
  border-radius: var(--hs-radius-lg);
  background: var(--hs-bg-raised);
}

/* A slide is a fixed-width column, so the default 88% cap would leave a bubble
   short of the edge it is supposed to hug. Inside a card the thread is
   evidence, not the subject: give it the full measure. */
[data-hs="2"] .hs-cw-ex .hs-dialogue {
  --hs-dialogue-max: 100%;
}

/* An open card stays CLIPPED. `overflow: visible` was added so the examples
   carousel would not be cut off, but an open card is already height:auto and
   therefore tall enough to contain it — and visible overflow means the content
   escapes the card box, so during the collapse transition the examples painted
   straight over the card below. The carousel scrolls on its own overflow-x, so
   clipping the card costs nothing. */
[data-hs="2"] .hs-cw-card[data-open] {
  overflow: hidden;
}

/* Fade the body with the height change so a collapse does not flash its
   content over the neighbour on the way down. */
/* Fade the body with the row so a collapse does not flash its content on the
   way down. The row itself is what changes size; this only softens the edge. */
[data-hs="2"] .hs-cw-card__body {
  transition: opacity var(--hs-duration-fast) var(--hs-ease-standard);
}

[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__body {
  opacity: 0;
}

/* ---- Wallet card hover: elevation, never geometry ----------------------
   The hover state used `transform: translateY(-4px)`. In a stack whose cards
   overlap to a 66px strip, that moves the card out from under the pointer:
   hover near the bottom of a strip, the card lifts 4px, the pointer falls
   outside, hover drops, the card returns — and it oscillates. The cursor
   crosses that band on every card while moving down the list, which is why the
   whole stack felt broken rather than one card.

   Hover now changes only paint: a stronger lift and a more opaque fill, so the
   hit area never moves. This is also closer to how Wallet itself behaves. */
[data-hs="2"] .hs-cw-card:not([data-open]):hover {
  transform: none;
  background-color: var(--hs-glass-control);
  /* NOT the popover elevation. A card in the stack has its bottom 30px tucked
     under the next one, and that next card is translucent glass — so a large
     dark shadow behind the hovered card bled up THROUGH its neighbour and read
     as the card's bottom edge swelling over the stack. A lift sized to the card
     stays inside the tuck. */
  box-shadow: var(--hs-glass-lift-strong);
}

/* NO z-index on hover.

   In a wallet stack every card must paint over the one above it — that overlap
   is what makes the logo of each card visible. Paint order comes from DOM
   order, so a later card is naturally on top, and nothing needs a z-index at
   all except the open card.

   Hover used to set `z-index: 5`. Click a card to CLOSE it and the pointer is
   still over it, so hover fires immediately: the closing card jumped to 5 and
   painted over every card below it for the whole 420ms collapse. It looked like
   the card was closing on top of the stack instead of back into it.

   Focus is the same story, so it does not raise either. Both states are now
   pure paint — fill and shadow — which is also what stops the hit area moving. */

/* The cards are tabbable, so focus needs a real ring rather than a fill. */
[data-hs="2"] .hs-cw-card:focus-visible {
  outline: 2px solid var(--hs-border-focus);
  outline-offset: 2px;
}

/* ---- Single-select options: show a radio dial --------------------------
   Nine groups across the workspace are marked role="radiogroup" but signalled
   selection with a background tint alone. On a large option card a tint reads
   as emphasis, not as "this one is chosen" — so "When should the agent say
   they are a virtual assistant?" gave no clear answer to which option was
   active. A dial states it.

   Keyed on the radiogroup ancestor so every group gets it and no future group
   is missed. Text options put the dial in a leading column; image tiles get it
   in a corner, since a leading column would break the tile grid. */
/* The dial is positioned, not placed in the flow. Forcing `display: grid` here
   fought seven different v1 layouts — persona identity alone is an ID-scoped
   `flex-direction: column`, which won and left the dial floating above the
   label instead of leading it. Absolute positioning works the same whether the
   option is flex-column, flex-row or grid, and padding-inline-start is the only
   layout property overridden, so each option keeps its own vertical rhythm. */
[data-hs="2"] [role="radiogroup"] :is(.hs-persona-identity__opt, .hs-ptool-focus-opt,
  .hs-voice-option, .hs-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option, .hs-conv-opt) {
  position: relative;
  /* v1 sets `padding: 14px 16px` as an ID-scoped SHORTHAND, so a longhand at
     0-3-0 loses and the dial ended up sitting on top of the label. */
  padding-inline-start: 44px !important;
  text-align: start;
}

/* The dial itself. A pseudo-element, so no markup changes and nothing to keep
   in sync with the render functions. */
[data-hs="2"] [role="radiogroup"] :is(.hs-persona-identity__opt, .hs-ptool-focus-opt,
  .hs-voice-option, .hs-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option,
  .hs-conv-opt)::before {
  content: "";
  position: absolute;
  inset-inline-start: 16px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  border-radius: var(--hs-radius-circle, 50%);
  /* border-control, not border-subtle: an unselected dial is a UI control and
     owes 3:1 against its surface under WCAG 1.4.11. */
  border: 2px solid var(--hs-border-control);
  background: transparent;
  transition:
    border-color var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-fast) var(--hs-ease-standard);
}

/* Selected: filled centre via an inset ring, so the dial needs no child. */
[data-hs="2"] [role="radiogroup"] :is(.hs-persona-identity__opt, .hs-ptool-focus-opt,
  .hs-voice-option, .hs-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option,
  .hs-conv-opt):is(.is-selected, .is-active, [aria-checked="true"], [aria-pressed="true"])::before {
  border-color: var(--hs-accent-solid);
  box-shadow: inset 0 0 0 4px var(--hs-accent-solid);
}

/* Image tiles keep their grid; the dial sits in a corner like a photo-picker
   check rather than taking a column. */
[data-hs="2"] [role="radiogroup"] :is(.hs-persona-photo-opt, .hs-brand-logo-opt,
  .hs-dialoguebot-launcher-option) {
  position: relative;
}

[data-hs="2"] [role="radiogroup"] :is(.hs-persona-photo-opt, .hs-brand-logo-opt,
  .hs-dialoguebot-launcher-option)::after {
  content: "";
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  width: 16px;
  height: 16px;
  border-radius: var(--hs-radius-circle, 50%);
  border: 2px solid var(--hs-glass-rim);
  background: var(--hs-glass-control-quiet);
  box-shadow: var(--hs-glass-lift);
  pointer-events: none;
}

[data-hs="2"] [role="radiogroup"] :is(.hs-persona-photo-opt, .hs-brand-logo-opt,
  .hs-dialoguebot-launcher-option):is(.is-selected, .is-active, [aria-checked="true"], [aria-pressed="true"])::after {
  border-color: var(--hs-accent-solid);
  background: var(--hs-accent-solid);
  box-shadow: inset 0 0 0 2px var(--hs-bg-raised);
}

/* ---- Empty containers must not paint dividers -------------------------
   .hs-st-ptool-list renders on Routines with no children at all, but its top
   and bottom hairlines still paint — a 2px line under Add routine dividing
   nothing from nothing. A rule is only meaningful with content on both sides.

   :empty covers the no-text-node case; :not(:has(*)) also catches a container
   holding nothing but the whitespace of a template literal. */
[data-hs="2"] :is(.hs-ptool-list, .hs-st-ptool-list, .hs-integrations-kb-inline):is(:empty, :not(:has(*))) {
  display: none;
}

/* ---- "Start with your website" as a composer card ----------------------
   Matched to the Routines composer: a labelled control row and a footer with
   the quiet opt-out left, circular arrow submit right.

   Deliberately NOT reusing .hs-st-composer's classes. That would inherit the
   look for free, but scheduled-task JS binds through
   `closest('.hs-st-composer')`, so borrowing the class would put this card
   inside handlers meant for the routine pills. Same look, built from v2 tokens
   rather than the v1 --st-* variables the composer still carries. */
[data-hs="2"] .hs-td-wcard {
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-4);
  max-width: 560px;
}

[data-hs="2"] .hs-td-wcard .hs-td-hero__copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

[data-hs="2"] .hs-td-wcard__row {
  display: grid;
  grid-template-columns: 68px minmax(0, 1fr);
  align-items: center;
  gap: var(--hs-space-3);
}

[data-hs="2"] .hs-td-wcard__label {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-secondary);
}

/* The composer's controls are 44px tall on a 12px radius, tinted once a value
   is set — the input here matches that resting state. */
/* v1 styles this as a borderless inline field via
   `#ws-panel-agent-training input.hs-training-input` (1-1-1) — height auto,
   no radius, 4px 0 padding — so it needs !important to become a real control. */
[data-hs="2"] .hs-td-wcard .hs-training-input {
  height: var(--hs-size-control-lg) !important;
  min-height: var(--hs-size-control-lg) !important;
  padding: 0 var(--hs-space-4) !important;
  border: 1.5px solid var(--hs-border-control) !important;
  border-radius: var(--hs-radius-md) !important;
  background: var(--hs-bg-raised) !important;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-td-wcard .hs-training-input:focus-visible {
  outline: none;
  border-color: var(--hs-accent-solid);
  box-shadow: 0 0 0 3px var(--hs-accent-muted);
}

[data-hs="2"] .hs-td-wcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--hs-space-3);
  margin-top: var(--hs-space-1);
}

/* The opt-out is the "Clear" slot: a quiet text control, not a button. */
[data-hs="2"] .hs-td-wcard .hs-td-hero__skip {
  /* v1 stretches this to the full row and centres the label, so the box sat
     flush left at 410px wide while the text began 130px in. Both properties
     need !important; align-self stops the flex row re-stretching it. */
  width: auto !important;
  align-self: start;
  text-align: start !important;
  height: auto;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  /* v1 painted this at 0.42 alpha on ink — 2.9:1, below the 4.5:1 floor for
     body text and easy to miss entirely, which matters because it is the only
     way past this screen for a business with no website. And `height: auto` on
     a 14px label gives a 22px box, under the 24px target minimum. */
  color: var(--hs-fg-secondary) !important;
  min-height: var(--hs-size-target-min);
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  cursor: pointer;
}

[data-hs="2"] .hs-td-wcard .hs-td-hero__skip:hover {
  color: var(--hs-fg-primary);
  text-decoration: underline;
}

/* The enter button: a 44px circle and arrow, on the primary ink fill.

   Two changes. It was aquamarine, which made "Fill from my website" the loudest
   element on the first screen of Business info — the same green-fill problem as
   "Apply selected", and the reason this rule needed !important to win in the
   first place. And it was 48px, which is not a rung on the control scale;
   control-lg is 44px and is already the size every other submit uses. */
[data-hs="2"] .hs-td-wcard__go {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--hs-size-control-lg);
  height: var(--hs-size-control-lg);
  /* v1's generic button rule carries a long :not() chain, and every :not()
     adds its argument's specificity — it out-specifies any plain class
     selector and was flattening this to an 8px radius. */
  border: 0 !important;
  border-radius: var(--hs-radius-pill) !important;
  background: var(--hs-fg-primary) !important;
  color: var(--hs-fg-inverse) !important;
  cursor: pointer;
  appearance: none;
  transition:
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    transform var(--hs-duration-instant) var(--hs-ease-standard);
}

[data-hs="2"] .hs-td-wcard__go:hover:not(:disabled) {
  background: var(--hs-neutral-800) !important;
}

[data-hs="2"] .hs-td-wcard__go:active:not(:disabled) {
  transform: translateY(1px);
}

[data-hs="2"] .hs-td-wcard__go:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ---- Lander hero headline ----------------------------------------------
   "Meet <em>Julia.</em>" relied on the v1 display serif, where an italic was
   a real cut of the face. v2's display face is Lexend, which ships no italic —
   so the browser synthesises an oblique by shearing the upright, which is why
   the word looked wrong rather than emphasised. The <em> stays (it is correct
   markup and still carries semantic emphasis); only the slant goes. */
[data-hs="2"] .v2-narr-line em,
[data-hs="2"] .v2-head1 em,
[data-hs="2"] .v2-heads em {
  font-style: normal;
}

/* ---- Lander hero: keep the narrative inside its column ------------------
   The three narrative lines are `white-space: nowrap` with `overflow: visible`
   in a fixed grid column. "Business Sidekick" measures 343px in a 340px
   column, so it already spills — and because it cannot wrap, anything that
   makes the text wider pushes it further right, into the phone. That includes
   the fallback face before the embedded Lexend loads, which is the flash of
   overlap that shows up intermittently rather than at one width.

   Wrapping is the fix: these are two-word phrases, so balanced wrapping reads
   correctly and the line can never reach the art. min-width:0 is required —
   a grid item's automatic minimum size is its content, which lets a nowrap
   child push the column wider than its track. */
[data-hs="2"] .v2-heads,
[data-hs="2"] .v2-head1 {
  min-width: 0;
}

[data-hs="2"] .v2-narr-line {
  white-space: normal;
  text-wrap: balance;
  max-width: 100%;
  overflow-wrap: break-word;
}

/* ============================================================
   Conversations tone — three explorations

   Why it reads as a flat green field on mobile: the page is pinned to
   --hs-wash-shift: 0%, the greenest point of the ramp, on the `studio` surface
   whose tint is the strongest in the system (0.36 alpha). And because the wash
   uses background-attachment: fixed, its 320% height resolves against the
   VIEWPORT — so an 812px phone shows only the top 31% of the ramp, where the
   two stops are 0.36 and 0.30 aqua. There is no gradient left to see; it is
   one colour. Desktop hides this because content covers more of the field.

   Switch with `data-hs-conv-tone` on <html>: "a", "b" or "c". No value keeps
   today's behaviour, so nothing changes until one is chosen.
   ============================================================ */

/* --- A. Retune the tone -------------------------------------------------
   Conversations is a reading surface, so it takes the calm middle of the ramp
   instead of the saturated end. One value, no new mechanism, and the page
   still sits in the same family as its neighbours. */
[data-hs="2"][data-hs-conv-tone="a"] body.hs-portal-body[data-hs-page="conversations"],
[data-hs="2"][data-hs-conv-tone="a"][data-hs-page="conversations"] body {
  --hs-wash-shift: 52%;
}

/* --- B. Thin the tint on small screens ---------------------------------
   Same hue, far less of it. A phone shows mostly bare background, so the tint
   that reads as a hint on desktop reads as a fill here. */
@media (max-width: 899px) {
  [data-hs="2"][data-hs-conv-tone="b"][data-hs-page="conversations"] body {
    --hs-bg-tint:
      linear-gradient(168deg,
        rgba(67, 229, 164, 0.13) 0%,
        rgba(67, 229, 164, 0.10) 26%,
        rgba(120, 214, 196, 0.09) 44%,
        rgba(111, 168, 240, 0.08) 62%,
        rgba(140, 150, 232, 0.07) 80%,
        rgba(163, 71, 234, 0.07) 100%);
  }
}

/* --- C. Frame it -------------------------------------------------------
   Keep the colour, but stop it being the reading surface: the list sits on a
   near-solid panel so the wash only frames the content. This is the option
   that scales if the page later fills with real conversation rows. */
[data-hs="2"][data-hs-conv-tone="c"][data-hs-page="conversations"] :is(.hs-conv-page, .hs-activity-wrap) {
  background: var(--hs-bg-raised);
  border: 1px solid var(--hs-border-subtle);
  border-radius: var(--hs-radius-xl);
  box-shadow: var(--hs-glass-lift);
  padding: var(--hs-space-5);
}

@media (max-width: 899px) {
  [data-hs="2"][data-hs-conv-tone="c"][data-hs-page="conversations"] :is(.hs-conv-page, .hs-activity-wrap) {
    padding: var(--hs-space-4);
  }
}

/* ---- Conversations header ------------------------------------------------
   This used to reserve a 116px gutter on the title because the actions were
   position:absolute and could not be separated by a flex row. The actions are
   a flex sibling now, so the row does the work and there is no magic number
   to keep in sync with the width of a button. */
[data-hs="2"] .hs-conv-head__row .hs-portal-section__title {
  min-width: 0;
  overflow-wrap: break-word;
}

/* Conversations shares the same title padding as every other section head
   (set above). Keep the mobile horizontal inset; desktop already pads via
   .hs-portal-main. */
[data-hs="2"] .hs-conv-head {
  padding-inline: var(--hs-space-5);
}

@media (min-width: 900px) {
  [data-hs="2"] .hs-conv-head {
    padding-inline: 0;
  }
}

/* ============================================================
   Information — section switcher and the Overview / Scenarios / Rules stack

   Two things were off-system here.

   1. The Business / Personal switcher is a .sgc, so it already picked up the
      pill treatment — but at control-sm with 12px labels. That is the size of a
      filter chip. This control swaps the entire contents of the page, so it
      takes the medium control height and the body text step. It also now sits
      below the title rather than above it (renderSectionHead).

   2. The three accordion sections were built before the tokens existed and
      show it: `border: 1px solid #e7edea` (a hex outside the palette),
      `border-radius: 14px` (the ladder has 12 and 16, not 14), an opaque
      `#fff` fill on a page whose whole surface is glass, and `padding: 14px
      16px` off the 4pt grid. A second rule elsewhere stripped the border and
      made them transparent, so the two treatments fought and which one you got
      depended on source order.

      One treatment: the same glass card the rest of the product uses.
   ============================================================ */
[data-hs="2"] .hs-info-subnav__seg {
  --hs-control-h: var(--hs-size-control-md);
}

/* Specificity note: the shared `.sgc > button` rule is
   [data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, .v2-pp-tab) = (0,3,0),
   because :is() takes the weight of its heaviest argument. A plain
   `.hs-info-subnav__seg > button` is (0,2,1) and loses, which is why the
   min-height applied but the font size did not. The extra ancestor class takes
   it to (0,3,1) — one step up, no !important. */
[data-hs="2"] .hs-seg-tabs .hs-info-subnav__seg > button {
  min-height: var(--hs-size-control-md);
  padding-inline: var(--hs-space-4);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
}

[data-hs="2"] .hs-info-subnav {
  margin-block-start: var(--hs-space-1);
}

/* ---- Accordion sections ------------------------------------------------- */
[data-hs="2"] .hs-training-accordion {
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-3);
}

/* Both the card rule and the "transparent with a bottom hairline" rule are
   overridden, so there is one answer regardless of which loaded last. */
[data-hs="2"] .hs-training-accordion__section,
[data-hs="2"] .hs-portal-panel .hs-training-accordion__section {
  border: 1px solid var(--hs-glass-card-border);
  border-radius: var(--hs-radius-lg);
  background: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  box-shadow: var(--hs-glass-lift);
  overflow: hidden;
}

[data-hs="2"] .hs-training-accordion__trigger {
  padding: var(--hs-space-4) var(--hs-space-5);
  background: transparent;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-base);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: var(--hs-tracking-tight);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-training-accordion__trigger:hover {
  background: var(--hs-glass-control-ghost);
}

/* The hairline only exists once the section is open, so a closed stack reads
   as separate cards rather than one block with lines through it. */
[data-hs="2"] .hs-training-accordion__section.is-open > .hs-training-accordion__trigger {
  border-bottom: 1px solid var(--hs-border-subtle);
  background: transparent;
}

[data-hs="2"] .hs-training-accordion__body {
  padding: var(--hs-space-5);
}

[data-hs="2"] .hs-training-accordion__trigger svg {
  width: var(--hs-size-icon-lg);
  height: var(--hs-size-icon-lg);
  color: var(--hs-fg-tertiary);
  transition: transform var(--hs-duration-fast) var(--hs-ease-standard);
}

/* ============================================================
   Conversations list on mobile — cards, not a hairline table

   The list was a desktop table stripped down: every container transparent,
   rows separated by a 1px hairline, sitting directly on the page wash. On a
   phone that put a borderless list immediately below a white card (the install
   prompt) and above white cards on every neighbouring screen — Tasks renders
   each item as its own card, so did Connectors after this pass. Conversations
   was the one list still pretending to be table rows.

   Each conversation is now a card, on the same glass the rest of the product
   uses. Desktop keeps the table: a dense, scannable grid is right at that
   width, and rows there are columns aligned across the whole list.
   ============================================================ */
@media (max-width: 899px) {
  /* .hs-activity-tbl keeps `overflow: auto` from the desktop table, where it
     exists so a wide grid can scroll sideways. There is no grid on a phone —
     just a stack of cards — and an auto overflow establishes a clipping box, so
     each card's shadow was sliced flat where it met the container's top and
     bottom edges. Nothing to scroll, nothing to clip. */
  [data-hs="2"] :is(.hs-activity-tbl, .hs-activity-tbl__frame) {
    overflow: visible;
  }

  [data-hs="2"] .hs-activity-rows {
    display: flex;
    flex-direction: column;
    gap: var(--hs-space-2);
    /* Room for the shadow to fall outside the last card without the flex
       container cropping it. */
    padding-block: var(--hs-space-1);
  }

  /* ID-scoped, because v1 sets `#ws-panel-conversations .hs-activity-row
     { padding-left: 4px; padding-right: 4px }` — (1,1,0) — so the table could
     bleed to the panel edges. A plain `[data-hs="2"] .hs-activity-row` is
     (0,2,0) and lost: the shorthand's top and bottom applied while its left and
     right were overruled, giving cards 16px of vertical padding and 4px of
     horizontal. Matching the ID scope takes it to (1,2,0). */
  [data-hs="2"] #ws-panel-conversations .hs-activity-row,
  [data-hs="2"] .hs-activity-row {
    padding: var(--hs-space-4);
    border: 1px solid var(--hs-glass-card-border);
    /* The hairline separator was the only thing dividing rows. Cards divide
       themselves, and leaving it drew a line through each card's floor. */
    border-bottom: 1px solid var(--hs-glass-card-border);
    border-radius: var(--hs-radius-md);
    background: var(--hs-glass-card);
    backdrop-filter: var(--hs-glass-blur);
    -webkit-backdrop-filter: var(--hs-glass-blur);
    box-shadow: var(--hs-glass-lift);
  }

  /* The time column was a fixed 5.25rem so titles lined up vertically down the
     list — correct for a table, where that shared line is what makes rows
     scannable. Each row is its own card now, so the card edge does that job and
     84px of fixed width for "1h ago" only steals room from the name: "Maria
     Reyes" truncated to "Maria R…" while a third of the column sat empty. Size
     it to content and give the slack to the title. */
  [data-hs="2"] .hs-activity-row__top {
    grid-template-columns: 22px auto minmax(0, 1fr) auto;
  }

  /* The summary line indents to sit under the title, using a calc that hard-codes
     the old fixed column. With an auto column that measurement no longer holds,
     and the summary is more readable aligned to the card's own padding anyway. */
  [data-hs="2"] .hs-activity-row__sum {
    padding-left: 0;
  }

  /* Last-child had its border zeroed to avoid a trailing line under a table.
     As a card it needs its border like every other one. */
  [data-hs="2"] .hs-activity-daygroup .hs-activity-row:last-child {
    border-bottom-color: var(--hs-glass-card-border);
  }

  /* The tint overlay was inset 2px with its own 14px radius to sit inside a
     borderless row. It now has a card to fill, so it matches the card. */
  [data-hs="2"] .hs-activity-row::before {
    inset: 0;
    border-radius: inherit;
  }

  [data-hs="2"] .hs-activity-row--selected {
    border-color: var(--hs-accent-solid);
    box-shadow: var(--hs-glass-lift-strong);
  }

  [data-hs="2"] .hs-activity-row--selected::before {
    background: var(--hs-accent-subtle);
  }

  /* Status bars were `inset 3px 0 0 #d97706` and `#16a34a` — two hex values
     from outside the palette doing the job warning-solid and success-solid
     already do. As a card shadow they also replaced the card's own lift, so
     the marked rows sat flat while the rest floated. */
  [data-hs="2"] .hs-activity-row--action {
    box-shadow: inset 3px 0 0 var(--hs-warning-solid), var(--hs-glass-lift);
    background: var(--hs-glass-card);
  }

  [data-hs="2"] .hs-activity-row--action.hs-activity-row--selected::before {
    background: var(--hs-warning-bg);
  }

  [data-hs="2"] .hs-activity-row--handled {
    box-shadow: inset 3px 0 0 var(--hs-success-solid), var(--hs-glass-lift);
  }

  /* The wrapper stays flush. The section body already insets by 20px, so
     adding it here too pushed the cards to 40px — a double inset that made the
     list narrower than every other card stack on the phone. The row's own
     padding is what the cards needed, not another gutter. */
}

/* ============================================================
   Page header actions — one control, not three

   Notifications and Filter sit side by side in the same header doing the same
   class of job (open a panel), and they were built three different ways:

     .hs-conv-notif-btn      36px, 14px text, transparent + control border
     .hs-conv-filters-toggle 28px, 12px text, 0.76 white + glass rim, 17px icon
     .hs-tasks-filter-button 40px, 14.7px text, opaque white + rgba(16,24,40,.12),
                             18px icon

   Three heights, three fills, three borders, three icon sizes — for two buttons
   the user sees together. They now all take the secondary treatment at the
   medium control height: filled neutral, because outlined reads thin against
   the wash and the pair needs to look like a pair.
   ============================================================ */
[data-hs="2"] :is(.hs-conv-notif-btn, .hs-conv-filters-toggle, .hs-tasks-filter-button) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--hs-space-2);
  min-height: var(--hs-size-control-md);
  padding-inline: var(--hs-space-4);
  border: 1px solid var(--hs-glass-rim);
  border-radius: var(--hs-radius-pill);
  background: var(--hs-glass-control);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  box-shadow: var(--hs-glass-lift);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
  white-space: nowrap;
}

/* Tasks filter is icon-only — keep it square so it pairs with Notifications. */
[data-hs="2"] .hs-tasks-filter-button {
  width: var(--hs-size-control-md);
  min-width: var(--hs-size-control-md);
  padding-inline: 0;
  gap: 0;
}

[data-hs="2"] :is(.hs-conv-notif-btn, .hs-conv-filters-toggle, .hs-tasks-filter-button):hover {
  filter: brightness(1.04);
}

[data-hs="2"] :is(.hs-conv-notif-btn, .hs-conv-filters-toggle, .hs-tasks-filter-button) svg {
  width: var(--hs-size-icon-md);
  height: var(--hs-size-icon-md);
  flex: none;
}

/* The active-filter count keeps its own pill, sized to sit inside the control. */
[data-hs="2"] .hs-conv-filters-toggle .hs-conv-ff__count {
  min-width: 18px;
  height: 18px;
  padding-inline: 5px;
  border-radius: var(--hs-radius-pill);
  background: var(--hs-accent-solid);
  color: var(--hs-fg-on-accent);
  font-size: var(--hs-text-2xs);
  font-weight: var(--hs-weight-bold);
  line-height: 18px;
  text-align: center;
}

/* Both collapse to their glyph at 760px — the SAME width at which the label is
   hidden, which is the only breakpoint that can be correct: a square control
   with a visible label is clipped, and a pill with no label is a stretched
   blank. The old rule fired at 560px and left 200px of clipped label above it.
   Square, 44px, so the pair stays a pair and clears the 24px target minimum. */
@media (max-width: 760px) {
  [data-hs="2"] :is(.hs-conv-notif-btn, .hs-conv-filters-toggle, .hs-tasks-filter-button) {
    min-width: var(--hs-size-control-lg);
    width: var(--hs-size-control-lg);
    min-height: var(--hs-size-control-lg);
    padding-inline: 0;
    gap: 0;
  }

  [data-hs="2"] :is(.hs-conv-notif-btn, .hs-conv-filters-toggle, .hs-tasks-filter-button) svg {
    width: var(--hs-size-icon-lg);
    height: var(--hs-size-icon-lg);
  }
}

/* ============================================================
   Segmented controls — one marker

   The control was drawing the selection twice. `.sgc__ind` is a sliding thumb
   that vtoggle.js positions under the active tab (white glass, 34px tall), and
   the active button ALSO took an aqua-100 fill (36px tall) painted on top of
   it. Two rounded pills in the same place, 1px out of register, one mint and
   one white — which is what reads as a strange marker rather than a selection.

   The thumb wins: it is the mechanism that actually animates between tabs, and
   a moving thumb on a recessed track is what a segmented control is. The active
   button keeps a colour and weight change so the selection is still stated in
   the text, then drops its fill.

   Groups with no thumb keep the tint — the billing Pay-as-you-go / Monthly tabs
   have no indicator element, and without a fill their selection would vanish.
   ============================================================ */
/* Both thumbs, one treatment. `.sgc__ind` is positioned by vtoggle.js;
   `.v2-pp-tabs-thumb` is positioned in CSS by billing. They were drawn
   differently — a 0.88 white pill and a solid aqua one — for the same job.

   White and raised, on the recessed track. Not aqua: an accent fill is how this
   product says "this is the action to take", and a mode toggle is not an action.
   Solid aqua here also made the toggle louder than the page's actual primary
   button. */
[data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb) {
  position: absolute;
  top: var(--hs-space-1);
  bottom: var(--hs-space-1);
  z-index: 0;
  border: 1px solid var(--hs-border-subtle);
  border-radius: var(--hs-radius-pill);
  /* Control weight (0.88), not --hs-bg-raised: the bridge remaps that to the
     0.72 card glass on a bridged page, which is too sheer to read as raised
     against the recessed track. */
  background: var(--hs-glass-control);
  box-shadow: var(--hs-elevation-raised);
  transition:
    left var(--hs-duration-base) var(--hs-ease-spring),
    width var(--hs-duration-base) var(--hs-ease-spring);
  pointer-events: none;
}

/* The track insets by 3px; the thumb should fill that box, not sit 1px short
   of it — the mismatch is why the two pills looked misaligned. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs):is(:has(.sgc__ind), :has(.v2-pp-tabs-thumb)) {
  position: relative;
  padding: var(--hs-space-1);
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs):is(:has(.sgc__ind), :has(.v2-pp-tabs-thumb)) > :is(button, .v2-pp-tab) {
  position: relative;
  z-index: 1;
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs):is(:has(.sgc__ind), :has(.v2-pp-tabs-thumb))
  > :is(button, .v2-pp-tab):is(.is-on, .is-active, .active, [aria-selected="true"]) {
  background-color: transparent !important;
  color: var(--hs-fg-primary);
  box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb) {
    transition-duration: 1ms;
  }

  [data-hs="2"] .hs-cw-card:not([data-open]):hover {
    transform: none;
  }
}

/* ============================================================
   Call card — token corrections only

   Measured on a 390px phone, this card was off several scales:

     radius        24px             the ladder has 20 and 28, not 24
     background    white @ 0.72     the glass card fill…
     backdrop      none             …with no blur, so it read as a flat
                                    translucent panel while every other card
                                    in the product frosts
     border        rgb(239,241,238) neutral-100 used as a literal
     type          Inter            the UI face is Plus Jakarta Sans

   Those are corrected below.

   DELIBERATELY NOT TOUCHED: padding, min-height, the avatar ring size, and the
   action button's height. Those look off-grid — 14px 16px 16px, a 56px ring, a
   50px button — but they are frames of an animated state machine:

     #hs-voice-call-mount .hs-callcard            idle      min-height 0, padding 14/16/16
     #hs-voice-call-mount.is-voice-expanded …     in call   min-height 420, padding 22/22/24
     #ws-voice-fullscreen-shell …                 fullscreen min-height min(72dvh, 560px)

   with 0.42s transitions on min-height and padding, and `.call-tx` growing from
   height 0 to 260px as the transcript opens. Snapping any of them to a token
   would freeze the geometry the transition interpolates between.

   Putting this card properly on the scales means retuning all three states
   together, which needs a live call to watch. That is a separate pass, not a
   token swap.
   ============================================================ */
[data-hs="2"] .hs-callcard {
  border: 1px solid var(--hs-glass-card-border);
  border-radius: var(--hs-radius-xl);
  background: var(--hs-glass-card);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  box-shadow: var(--hs-glass-lift);
}

[data-hs="2"] .hs-callcard :is(.hs-callcard__name, .hs-callcard__title, .call-id strong) {
  font-family: var(--hs-font-ui);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: var(--hs-tracking-tight);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-callcard .call-action {
  font-family: var(--hs-font-ui);
  font-weight: var(--hs-weight-semibold);
  border-radius: var(--hs-radius-pill);
}

/* Live call: the button becomes the hang-up. Accent green on "End call" says
   the opposite of what the control does. */
[data-hs="2"] .hs-callcard .call-action.live {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-on-solid);
}

/* ============================================================
   Connector example results

   Each example now leads with a descriptive title — what the connector is FOR,
   in the user's words — and what comes back is rendered as whatever the answer
   actually is, not always a chat bubble:

     stats  figures worth reading      label · value · optional delta
     bars   comparing magnitudes       one hue, direct labels, no legend
     list   several items, ranked      status glyph + label + trailing meta
     file   an artefact was produced   draft, PDF, photo set
     chat   a reply in words           the default

   Chart specs honoured: bars carry a 4px rounded data-end and sit square on the
   baseline; a 2px surface gap separates adjacent bars; values are direct-labelled
   at the tip in TEXT tokens, never in the series colour; a single series takes no
   legend, so the caption names the measure. Status tones always pair colour with
   a glyph, never colour alone.
   ============================================================ */

[data-hs="2"] .hs-cw-ex__title {
  margin: 0;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  line-height: var(--hs-leading-snug);
  letter-spacing: var(--hs-tracking-tight);
  color: var(--hs-fg-primary);
  text-wrap: pretty;
}

[data-hs="2"] .hs-cw-res {
  margin: 0;
  font-size: var(--hs-text-xs);
}

[data-hs="2"] .hs-cw-res__caption {
  margin: 0 0 var(--hs-space-2);
  font-size: var(--hs-text-2xs);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: var(--hs-tracking-caps);
  text-transform: uppercase;
  color: var(--hs-fg-tertiary);
}

/* ---- Stat tiles --------------------------------------------------------- */
[data-hs="2"] .hs-cw-res--stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hs-space-2);
}

[data-hs="2"] .hs-cw-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 0;
  min-width: 84px;
  padding: var(--hs-space-3);
  border: 1px solid var(--hs-border-subtle);
  border-radius: var(--hs-radius-md);
  background: var(--hs-glass-sunken);
}

[data-hs="2"] .hs-cw-stat__label {
  font-size: var(--hs-text-2xs);
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-cw-stat__value {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-lg);
  font-weight: var(--hs-weight-semibold);
  letter-spacing: var(--hs-tracking-tight);
  /* Proportional, not tabular — these are single figures, not a column. */
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-cw-stat__delta {
  font-size: var(--hs-text-2xs);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-secondary);
}

/* Direction, not sign: a fall in spend is the good news. */
[data-hs="2"] .hs-cw-stat__delta.is-good { color: var(--hs-success-fg); }
[data-hs="2"] .hs-cw-stat__delta.is-bad { color: var(--hs-danger-fg); }

[data-hs="2"] .hs-cw-stat__vs {
  font-weight: var(--hs-weight-regular);
  color: var(--hs-fg-tertiary);
}

/* ---- Bars --------------------------------------------------------------- */
[data-hs="2"] .hs-cw-bars {
  display: flex;
  flex-direction: column;
  /* The 2px surface gap that separates touching marks. */
  gap: var(--hs-space-2);
}

[data-hs="2"] .hs-cw-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'label value' 'track track';
  align-items: center;
  gap: 2px var(--hs-space-2);
}

[data-hs="2"] .hs-cw-bar__label {
  grid-area: label;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--hs-fg-secondary);
}

/* Direct label at the tip, in a text token — never in the bar's own colour. */
[data-hs="2"] .hs-cw-bar__value {
  grid-area: value;
  font-family: var(--hs-font-ui);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-cw-bar__track {
  grid-area: track;
  display: block;
  height: 8px;
  border-radius: 0;
  background: var(--hs-glass-recessed);
}

/* Square on the baseline it grows from, 4px rounded at the data end. */
[data-hs="2"] .hs-cw-bar__fill {
  display: block;
  height: 100%;
  min-width: 4px;
  border-radius: 0 4px 4px 0;
  background: var(--hs-accent-solid);
}

/* ---- Ranked list -------------------------------------------------------- */
[data-hs="2"] .hs-cw-res--list {
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

[data-hs="2"] .hs-cw-resrow {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--hs-space-2);
  padding: var(--hs-space-2) 0;
  border-bottom: 1px solid var(--hs-border-subtle);
}

[data-hs="2"] .hs-cw-resrow:last-child {
  border-bottom: 0;
}

[data-hs="2"] .hs-cw-resrow__ic {
  display: grid;
  place-items: center;
  width: var(--hs-size-icon-md);
  height: var(--hs-size-icon-md);
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-cw-resrow__ic svg {
  width: 100%;
  height: 100%;
}

/* Colour rides the glyph; the label stays in ink, so the state is never
   carried by colour alone. */
[data-hs="2"] .hs-cw-resrow--warning .hs-cw-resrow__ic { color: var(--hs-warning-fg); }
[data-hs="2"] .hs-cw-resrow--success .hs-cw-resrow__ic { color: var(--hs-success-fg); }
[data-hs="2"] .hs-cw-resrow--info .hs-cw-resrow__ic { color: var(--hs-info-fg); }
[data-hs="2"] .hs-cw-resrow--file .hs-cw-resrow__ic { color: var(--hs-fg-secondary); }
[data-hs="2"] .hs-cw-resrow--muted .hs-cw-resrow__ic { color: var(--hs-fg-disabled); }

[data-hs="2"] .hs-cw-resrow__label {
  min-width: 0;
  color: var(--hs-fg-primary);
  text-wrap: pretty;
}

[data-hs="2"] .hs-cw-resrow__meta {
  color: var(--hs-fg-tertiary);
  white-space: nowrap;
}

[data-hs="2"] .hs-cw-resrow--muted .hs-cw-resrow__label {
  color: var(--hs-fg-tertiary);
}

/* ---- Artefact ----------------------------------------------------------- */
[data-hs="2"] .hs-cw-res--file {
  display: flex;
  align-items: center;
  gap: var(--hs-space-3);
  padding: var(--hs-space-3);
  border: 1px solid var(--hs-border-subtle);
  border-radius: var(--hs-radius-md);
  background: var(--hs-glass-sunken);
}

[data-hs="2"] .hs-cw-file__ic {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--hs-size-control-sm);
  height: var(--hs-size-control-sm);
  border-radius: var(--hs-radius-sm);
  background: var(--hs-accent-subtle);
  color: var(--hs-accent-ink);
}

[data-hs="2"] .hs-cw-file__ic svg {
  width: var(--hs-size-icon-md);
  height: var(--hs-size-icon-md);
}

[data-hs="2"] .hs-cw-file__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

[data-hs="2"] .hs-cw-file__label {
  font-family: var(--hs-font-ui);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-cw-file__meta {
  color: var(--hs-fg-tertiary);
  text-wrap: pretty;
}

/* ============================================================
   Connectors on mobile — a list, not a wallet

   The overlapping wallet stack is a good desktop affordance: cards tucked 30px
   under each other read as one object you leaf through, and there is room to
   spend on the tuck.

   On a phone it earns nothing and costs a lot. 30px of every 112px card is
   hidden, so a six-connector list gives up 180px of a 844px screen to an effect
   nobody can act on. The peek then has to be maintained as arithmetic —
   `margin-top: calc(peek - card-h)` — against a head whose height changes with
   the label, the account and the action button, and every generation of fixes
   to that arithmetic has left another rule behind. It is also what made the
   hover shadow bleed through the card below: there is only a tuck to bleed into
   because the cards overlap.

   So on mobile the cards simply stack, with the gap doing the separating —
   the same model the conversations list uses. The expansion, the animation and
   the content are all unchanged; only the overlap goes. Nothing has to be kept
   in sync with anything.
   ============================================================ */
@media (max-width: 899px) {
  [data-hs="2"] .hs-connectors-layout {
    gap: var(--hs-space-2);
  }

  /* No tuck, so the card is exactly as tall as its header. The 112px footprint
     only existed to leave 30px to hide. */
  [data-hs="2"] .hs-cw-card {
    min-height: 0;
  }

  [data-hs="2"] .hs-cw-card + .hs-cw-card {
    margin-top: 0;
  }

  [data-hs="2"] .hs-cw-card[data-open] + .hs-cw-card {
    margin-top: 0;
  }

  /* With nothing overlapping, the collapsed header sizes to its own content
     instead of being pinned to the peek height. */
  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head {
    height: auto;
    min-height: var(--hs-size-control-lg);
  }

  /* Every card is fully visible, so none of them needs to out-paint a
     neighbour. */
  [data-hs="2"] .hs-cw-card,
  [data-hs="2"] .hs-cw-card + .hs-cw-card,
  [data-hs="2"] .hs-cw-card[data-open] {
    z-index: auto;
  }
}

/* ============================================================
   Billing — plan and top-up selection

   A selected card was marked with `rgba(67, 229, 164, 0.12)`: a 12% aqua wash
   on a page whose background is already a mint wash. The two are close enough
   that the Starter card read as unfilled — the selection was invisible, which
   on a pricing page is the one thing that has to be obvious.

   Selection is now stated by the frame, not a tint: an accent border and a
   raised white fill, so it comes forward off the wash instead of dissolving
   into it. Same treatment for plan cards and top-up amounts.
   ============================================================ */
[data-hs="2"] :is(.v2-pp-tier-pick, .hs-billing-plan-card--selected) {
  background: var(--hs-glass-control);
  border: 1px solid var(--hs-glass-rim);
}

/* !important, matched not chosen. poc5-pages.css:9352 pins
   `.hs-billing-pricing .v2-pp-tier-pick.is-selected` with three of them —
   background, border-color and color — at the same (0,3,0) specificity as this
   rule, so nothing here lands without them. The values it pins are the problem:
   a 12% aqua wash on a mint page, a hardcoded #1d8a5b border and #0a0a0a ink,
   none of which are palette values. */
[data-hs="2"] .v2-pp-tier-pick.is-selected,
[data-hs="2"] .hs-billing-plan-card.is-selected {
  background: var(--hs-glass-control) !important;
  border-color: var(--hs-accent-solid) !important;
  color: var(--hs-fg-primary) !important;
  box-shadow: var(--hs-elevation-raised);
}

/* ============================================================
   Connector Settings — top right of the open card

   Settings was rendered as a footer below the examples carousel, so on a card
   you had just opened you had to scroll past three example tiles to reach the
   one control that manages the connection.

   It belongs in the corner. For a connected connector the head's action cell is
   empty — connectorGridRow moves the manage block out of it — so the top right
   is free, which is where a settings affordance is looked for on a card.
   ============================================================ */
[data-hs="2"] .hs-cw-card[data-open] .hs-cw-card__manage {
  position: absolute;
  top: var(--hs-space-4);
  right: var(--hs-space-5);
  z-index: 2;
  padding: 0;
  border-top: 0;
}

/* Closed cards clip their body, so the absolutely-positioned block would still
   paint over the collapsed header. Keep it out until the card opens. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__manage {
  display: none;
}

/* The examples carousel drew a divider on the assumption that a footer sat
   under it. Nothing does now. */
[data-hs="2"] .hs-cw-card__examples + .hs-cw-card__manage {
  border-top: 0;
  padding-top: 0;
}

/* ============================================================
   Embedded chat previews must not float

   `.julia-chat-dock` carries `z-index: 9000` because in its original job it IS
   the floating launcher — fixed to the corner, deliberately above everything.

   The chatbot and SMS pages reuse the same component as an in-page preview. It
   inherited the 9000, so a preview sitting in the page flow outranked the fixed
   page header (z-index 35) and the notification button: scroll the page and the
   chat widget slid up over the title. Nothing was broken about the scroll — the
   preview was simply claiming the layer of a floating overlay.

   Embedded instances go back into the page's own stacking order. The floating
   launcher is untouched.
   ============================================================ */
[data-hs="2"] :is(.hs-chatbot-preview-wrap, .hs-chatbot-preview-stage, .hs-chatbot-direct-preview)
  .julia-chat-dock {
  position: relative;
  z-index: auto;
}

/* The thread's own z-index: 1 is fine inside the panel, but with the dock no
   longer opening a stacking context it would escape into the page. */
[data-hs="2"] :is(.hs-chatbot-preview-wrap, .hs-chatbot-preview-stage, .hs-chatbot-direct-preview)
  .julia-chat-panel {
  isolation: isolate;
}

/* ---- Preview head: title left, Reset right ------------------------------
   Reset chat was a full-width row UNDER the widget, so the control that clears
   the preview sat further from it than the preview's own title. It is in the
   head now, opposite the title. */
[data-hs="2"] .hs-chatbot-direct-preview__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--hs-space-3);
}

[data-hs="2"] .hs-chatbot-direct-preview__head > h2 {
  margin: 0;
  min-width: 0;
}

/* Secondary, not ghost. Reset is a real action on the preview, and an outlined
   ghost button read as disabled against the stage behind it. */
[data-hs="2"] .hs-preview-reset {
  flex: none;
}

/* ---- Microphone recovery panel ------------------------------------------
   Shown inside the call card when the browser has blocked the mic. It takes the
   warning role: something is wrong, it is recoverable, and the user has to act.
   Not danger — nothing has broken or been lost. */
[data-hs="2"] .call-mic-block {
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-2);
  margin-block-end: var(--hs-space-3);
  padding: var(--hs-space-4);
  border: 1px solid var(--hs-warning-border);
  border-radius: var(--hs-radius-md);
  background: var(--hs-warning-bg);
  color: var(--hs-warning-fg);
  text-align: start;
}

[data-hs="2"] .call-mic-block__title {
  margin: 0;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
}

[data-hs="2"] .call-mic-block__body {
  margin: 0;
  font-size: var(--hs-text-xs);
  text-wrap: pretty;
}

[data-hs="2"] .call-mic-block__steps {
  margin: 0;
  padding-inline-start: var(--hs-space-5);
  font-size: var(--hs-text-xs);
  display: flex;
  flex-direction: column;
  gap: var(--hs-space-1);
}

/* The action is the way out, so it reads as the primary thing to do — and it
   keeps ink on accent rather than inheriting the warning colour. */
[data-hs="2"] .call-mic-block__retry {
  align-self: flex-start;
  margin-block-start: var(--hs-space-1);
  color: var(--hs-fg-on-accent);
}
