/* ============================================================
   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.
   ============================================================ */

/* Press. hs-base.css carries this for v2 pages, but bridged pages deliberately
   do not load hs-base.css — its reset would restyle v1 layout — and those are
   19 of the 20 pages in the product. Without this rule the floor reaches almost
   nothing: poc5-pages.css alone has 241 :hover rules to 20 :active ones, so on
   touch, where hover does not exist, most of the product answers a press with
   nothing until the action itself completes.

   `scale` is a separate property from `transform`, so this composes with
   whatever transform a v1 rule already set instead of replacing it — which is
   the only reason it is safe to apply this broadly. Scope discipline holds:
   scale paints, it does not reflow. Opt a control out with .hs-no-press.

   `.hs-cw-card__head` is excluded, and the reason is the one case where two
   press rules stack. A wallet card head is the whole visible strip of its card,
   so the surface rule below is already answering the tap by scaling the CARD.
   Business information renders that head as a `<button>` (every other wallet
   renders a `<div>`), so it also matched here and scaled a second time, inside
   the card. Nested scale does not move the card's edge — it moves the CONTENT
   away from it: the title stepped ~3px right and the chevron ~3px left on every
   tap, which reads as the text indenting itself rather than the card being
   pressed. One press per tap, on the card, the same as Conversations.

   `.hs-ptool__row` and `.hs-ptool__chev--expand` are excluded for exactly the
   same reason, and they are the case that made the agent pages read as a
   different product. A tool card's opener IS a `<button>`, so a tap scaled the
   ROW — tile, title and subtitle stepping inward inside a card whose edge never
   moved — while the card the owner thinks they pressed sat still. On the head
   variant `.hs-ptool__head` scaled as well, so the content moved twice on one
   tap. The card takes the press now (see the `.hs-ptool` rule further down),
   the same as a conversation card.

   `.hs-cw-card__opener` is the general form of the same exclusion, and any new
   wallet head should use it rather than adding a fourth class here. It marks a
   button INSIDE a card head that opens and shuts the card — the admin task
   card's title is the one today. The card answers that press; the button does
   not, or the title indents itself inside a card that never moves. A head that
   IS the button needs no marker: `.hs-cw-card__head` above already covers it.
   Action buttons in a head — Connect, Update, Add — are not openers and keep
   their own press.

   `.hs-cw-card` is excluded for a reason the exclusions above do not cover: a
   card that carries `role="button"` on the CARD is a control by this rule's
   reckoning, and `:active` is on the whole ancestor chain, so every press
   anywhere inside it — a button in the open fold, a transcript line — squeezed
   the entire card at the button scale. The admin console's conversation card
   shipped that way. A wallet card presses from its head (the rule below), and
   that is true whatever element the card is; this keeps the two rules from
   both claiming one press. */
[data-hs="2"] :is(button, [role="button"], summary):active:not(:disabled):not([aria-disabled="true"]):not(.hs-no-press):not(.hs-cw-card):not(.hs-cw-card__head):not(.hs-cw-card__opener):not(.hs-ptool__row):not(.hs-ptool__chev--expand) {
  scale: var(--hs-press-scale);
}

[data-hs="2"] :is(button, [role="button"], summary) {
  transition: scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* Press for the tap targets that are not controls.

   The rule above reaches `button`, `[role="button"]` and `summary`. The
   product's busiest tap targets are none of those — a Conversations cell is an
   `<article>`, a scheduled task is an `<article>`, a tool head is a `<div>` —
   so the press floor missed them entirely. An audit of every `cursor: pointer`
   rule found 69 such families, and the only press feedback any of them ever
   gave was the grey square the OS paints, which hs-mobile.css now turns off.
   Something has to take its place, or those surfaces answer a tap with nothing.

   `scale`, for the same reason as above, plus one this section makes obvious:
   a transform CANNOT MISMATCH A RADIUS. The same declaration reads correctly on
   a 999px pill, a 20px glass card and a square thumbnail without any of them
   telling it their shape. A painted overlay is the opposite — every one of the
   69 would have to hand it a radius, and the ones that forgot are exactly how
   the grey rectangle got reported in the first place.

   --hs-press-scale is 0.97, tuned on a control a few hundred px wide. On a
   full-bleed card that is ~11px of travel per edge, which reads as the card
   jumping rather than being pushed — so cards take a gentler number, the
   direction primitives.json already sanctions for large hit areas.

   Deliberately NOT in the list: `<tr>` and `<table>` families. Scaling a table
   row is not a paint-only change, and scope discipline in this file is the
   reason it is safe to apply anything this broadly. Those rows keep hover, and
   on touch they now have no press state — see the audit, not a fix here.

   Wallet cards are NOT in this list — they press from their head instead, see
   the rule below. */
[data-hs="2"] :is(
  .hs-st-task-card,
  .hs-adm-skillcard--click,
  .hs-sms-link-item,
  .hs-td-connected-site-option,
  .hs-td-wcard,
  .hs-scenario-row__main,
  .hs-adm-entity-alert__row,
  .hs-kb-configure,
  .hs-pressable
):active:not(.hs-no-press) {
  scale: var(--hs-press-scale-surface);
}

/* ---- A wallet card presses from its HEAD ---------------------------------

   The head is the strip that opens and shuts the card, and it is the only part
   of a card that behaves like a button. Everything else is either content or a
   control with a press of its own.

   `.hs-cw-card:active` was the old rule and it was wrong in both directions.
   `:active` propagates to ancestors, so once a card was open, Play on the call
   recording, Delete, Review with Julia, a transcript toggle — every button in
   the fold — pushed the whole card as well as itself. Two presses for one tap,
   and the outer one said "this collapses" about a control that does nothing of
   the sort. In the other direction the rule carried `:not([data-open])` on
   `.hs-cw-card`, so a card being SHUT answered the tap only through the looser
   `.hs-activity-row` entry beside it and its day tab, which reads the card's
   state, did not follow it down.

   One condition covers both: the head is pressed, and no control inside the
   head is. Open or shut, the card and its tab move together, and a button in
   the fold moves only itself.

   The exclusion has to name the controls rather than test the event target,
   because `:active` is on the whole ancestor chain: a press on the Connect
   button in a connector card's head makes the head `:active` too. It is scoped
   to descendants OF the head, so a head that IS a button — Business
   information and the lab's call rows render it that way, every other wallet
   renders a `<div>` — still presses, since an element is not its own
   descendant.

   `.hs-cw-card__opener` is the one control the exclusion lets through: a button
   in the head whose whole job is to open and shut the card, which is the card's
   own gesture wearing a `<button>`. The admin task card's title is that. */
[data-hs="2"] .hs-cw-card:not(.hs-no-press):has(> .hs-cw-card__head:active):not(
  :has(> .hs-cw-card__head :is(button, a, input, select, label, summary, [role="button"]):not(.hs-cw-card__opener):active)
) {
  scale: var(--hs-press-scale-surface);
}

/* THE PRESS HOLDS WHILE THE CARD IS FETCHING, so the release and the open are
   one motion instead of three.

   Conversations does not flip `data-open` on the click. It fills the fold first
   so the expansion animates straight to its final height — `openActivityDetail`
   races the transcript fetch against 200ms, and that decision is what stopped
   the card opening twice in one gesture. On a desktop mouse the wait disappears
   into the click, which is why the card there looks like it expands on release.
   On a phone the fetch nearly always wins the race, and the press was released
   into the gap: the card shrank, sprang back to full size, sat still for the
   rest of the fetch, and only then unfolded. Three motions for one tap, and the
   two that came first said nothing.

   `.hs-activity-row--opening` is on the card for exactly the length of that
   wait, so holding the same scale means the finger lifting moves nothing at
   all. The card stays squeezed until there is something to show, then releases
   as it unfolds — the one motion the mouse has always had.

   Not gated to touch: a slow network puts the same gap in front of a mouse
   click. The Enter/Space path gets the squeeze too, without a press before it,
   which reads as the card acknowledging the commit rather than as a glitch. */
[data-hs="2"] .hs-cw-card.hs-activity-row--opening {
  scale: var(--hs-press-scale-surface);
}

/* A tool card presses from its OPENER, not from `:active` on the card.

   `:active` propagates to ancestors, so `.hs-ptool:active` would also fire when
   the owner reaches for the switch in the card's trailing lane — pressing a
   toggle would push the whole card, which says "this opens" about a control
   that does not. The two things that DO open the card are the row button and
   the chevron beside it, so those are what the card answers to. Everything else
   in the strip keeps its own press and leaves the card alone.

   An open card is excluded for the same reason `.hs-cw-card` is: at that point
   it is a container being read. */
[data-hs="2"] .hs-ptool:not(.is-open):has(
  :is(.hs-ptool__row.is-btn, .hs-ptool__chev--expand):active
) {
  scale: var(--hs-press-scale-surface);
}

/* Separate rule, and it has to be: `transition` is one property, so listing it
   with the `:active` scale above would do nothing — the resting state is where
   the ease has to live. At 0-2-0 this beats the plain-class transitions these
   families declare in poc5-pages.css. The one exception is
   `#ws-panel-scheduled-tasks .hs-st-task-card` (1-1-0), which keeps its own
   list and therefore snaps rather than eases; 80ms of ease is the whole
   difference, so it is not worth an id selector here to win it back. */
[data-hs="2"] :is(
  .hs-cw-card,
  .hs-activity-row,
  .hs-st-task-card,
  .hs-adm-skillcard--click,
  .hs-sms-link-item,
  .hs-td-connected-site-option,
  .hs-td-wcard,
  .hs-scenario-row__main,
  .hs-adm-entity-alert__row,
  .hs-ptool,
  .hs-kb-configure,
  .hs-pressable
) {
  /* `--hs-press-scale-surface` is a root token (primitives.json), not a local
     declaration here. It has to be inheritable: a Conversations day tab and the
     card it labels press together and are SIBLINGS, so a number declared on the
     card cannot reach the tab, and the tab's `scale` silently computed to
     `none` — an unresolvable var is invalid at computed-value time, which fails
     as "no press" rather than as an error. */
  transition: scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* THE TOP EDGE IS THE ANCHOR for a card that lives in a tucked stack, and on a
   card with a day tab above it that is not a preference — it is what stops the
   tab shimmering.

   A card scaling about its centre walks its own top edge DOWN by half the height
   it gives up: 1.22px on a 122px phone card. The tab above it scales about its
   own bottom, so the tab's bottom edge holds still. The seam between them
   therefore moved by the difference, and the strip of tab left showing above the
   card GREW 0.41px on every press — a rounded 1px border landing on a different
   sub-pixel each frame, which is exactly what reads as the tab dancing rather
   than being pressed.

   Pinned at the top, the seam does not move at all (measured: 0.000px), and the
   only thing that changes is the tab's own 2% — one object being squeezed
   instead of two arguing. The bottom edge rises instead, which in a stack is
   dead space the next card already covers.

   Only the three stacked families. A lone tile has no seam to protect and reads
   better pressed from its middle, which is the default it keeps.

   It lives on a RESTING rule, never inside `:active`. An origin is not an
   animatable property, so switching it on press would move the box by a whole
   origin's worth in one frame, at both ends of the gesture. */
[data-hs="2"] :is(.hs-cw-card, .hs-activity-row, .hs-ptool) {
  transform-origin: top center;
}

/* A card is a slice of a phone screen and the whole width of a desktop one, so
   one percentage is not one amount of movement. 0.98 is 3.35px per edge on a
   335px phone card — measured, and reported as almost invisible. 0.965 was the
   first correction and still read as small on a phone in the hand, so the phone
   number is 0.955: 7.5px per edge there, and still inside the 0.94 floor
   primitives.json sets. Desktop keeps 0.98: the same 2% is 9.6px on a 960px
   card, which is plenty. */
@media (max-width: 899px) {
  [data-hs="2"] {
    --hs-press-scale-surface: 0.955;
  }
}
[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);
  /* `scale` is listed so the press eases back rather than snapping. Declaring
     `transition` replaces any earlier one wholesale — it does not layer. */
  transition:
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    border-color var(--hs-duration-fast) var(--hs-ease-standard),
    scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* ============================================================
   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);
}

/* The press fill was `--hs-neutral-900`, a raw primitive. `--hs-neutral-800` is
   aliased to `--hs-fg-primary` in hs-bridge.css so it follows the theme;
   `--hs-neutral-900` is not, so it stayed #1A1E1B in dark, where the text on a
   primary button is forest-900 — dark text on a dark fill for the length of
   every tap. Mixing the fill toward its own text colour moves it in whichever
   direction the theme needs and can never close the gap it is mixing across.
   `color` is restated because a press rule that changes the fill and inherits
   the text from somewhere else is how this broke in the first place. */
[data-hs="2"] :is(.hs-btn--primary, .hsb--primary, .hs-adm-btn--primary, .hs-btn-primary,
  .hs-cs-btn--primary):active:not(:disabled) {
  background: color-mix(in oklab, var(--hs-fg-primary) 86%, var(--hs-fg-inverse));
  color: var(--hs-fg-inverse);
}

/* Secondary — the one neutral style. `.hs-btn-soft` and `--ghost` were
   separate names for it.

   `.hs-cs-btn` is listed by its `--ghost` modifier, not by its base class. The
   base is a FAMILY name — every connector button carries it and then adds
   `--primary`, `--ghost` or `--disconnect` — so aliasing the base to secondary
   matched primary and disconnect buttons too. `:is()` takes the specificity of
   its widest argument, which made that match a tie with the primary rule above,
   and a tie goes to whichever rule is written later: this one. Reconnect lost
   its ink fill and, worse, kept ink TEXT into `:active`, where the primary press
   rule does win on `background` — black text on a black fill for the length of
   the tap. The other three families (`.hs-btn`, `.hsb`, `.hs-adm-btn`) were
   already listed by modifier only; this one now matches them. */
[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--ghost) {
  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--ghost):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);
}

/* Delete, on a wallet card, is the exception to quiet's borderless rule.
   It is the one control on the card that destroys something, it sits alone at
   the far edge of the action row, and with no edge of its own it read as a label
   rather than as a button. The pill gets an outline; the fill stays away until
   hover, where the card's own red takes over. Here rather than in
   `poc5-pages.css` because the quiet variant above zeroes the border with
   `[data-hs="2"]` specificity and loads after it.

   The task card's Delete is listed with the conversation card's, not given a
   rule of its own: it is the same control doing the same thing, and one
   declaration is what stops the two drifting apart. */
[data-hs="2"] .hs-activity-detail-card__delete,
[data-hs="2"] .hs-activity-row__delete,
[data-hs="2"] .hs-tasks-card__delete,
[data-hs="2"] .hs-adm-card__danger {
  border: 1px solid var(--hs-border-default);
}

[data-hs="2"] .hs-activity-detail-card__delete:hover:not(:disabled),
[data-hs="2"] .hs-activity-detail-card__delete:focus-visible,
[data-hs="2"] .hs-activity-row__delete:hover:not(:disabled),
[data-hs="2"] .hs-activity-row__delete:focus-visible,
[data-hs="2"] .hs-tasks-card__delete:hover:not(:disabled),
[data-hs="2"] .hs-tasks-card__delete:focus-visible,
[data-hs="2"] .hs-adm-card__danger:hover:not(:disabled),
[data-hs="2"] .hs-adm-card__danger:focus-visible {
  border-color: var(--hs-danger-border);
  background: var(--hs-danger-bg);
  color: var(--hs-danger-fg);
}

/* ---- 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);
}

/* The add-to-home-screen banner CTA was a fourth green: a two-stop gradient
   (#24a86a → #1a7f4b) reachable by no token, and its poc5 :focus-visible
   killed the outline while relying on the gradient swap alone. Ink, like
   every other primary — and the focus ring comes back. `background` longhand
   pair because poc5 sets background-image (the gradient), not color. */
[data-hs="2"] .hs-a2hs-card__cta {
  background-color: var(--hs-fg-primary);
  background-image: none;
  color: var(--hs-fg-inverse);
  border-color: transparent;
  box-shadow: var(--hs-elevation-raised);
}

[data-hs="2"] .hs-a2hs-card__cta:hover:not([disabled]) {
  background-color: var(--hs-neutral-800);
  background-image: none;
}

[data-hs="2"] .hs-a2hs-card__cta:focus-visible {
  background-image: none;
  outline: var(--hs-size-focus-ring) solid var(--hs-border-focus);
  outline-offset: 2px;
}

[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, .hs-cs-btn--disconnect) {
  background: var(--hs-danger-bg);
  color: var(--hs-danger-fg);
  border-color: var(--hs-danger-border);
}

/* Disconnect fills on hover, the way every other destructive control does.
   `poc5-pages.css` already said this in raw hex; it is repeated here in tokens
   because the secondary rule that used to swallow this button also swallowed
   its hover, and a red that only exists in one stylesheet cannot follow the
   theme into dark. */
[data-hs="2"] .hs-cs-btn--disconnect:hover:not(:disabled) {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-on-solid);
  border-color: transparent;
  filter: none;
}

/* ============================================================
   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-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-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-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-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-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);
}

/* The naming choice on the connector settings sheet. Colour only; the layout
   is in poc5-pages.css. The chosen segment takes the accent so it reads as a
   selection rather than a fourth button. */
[data-hs="2"] .hs-cs-choice {
  border-color: var(--hs-border-default);
  border-radius: var(--hs-radius-md);
  background: var(--hs-glass-control);
  color: var(--hs-fg-secondary);
  font-family: var(--hs-font-ui);
}

[data-hs="2"] .hs-cs-choice[aria-checked="true"] {
  border-color: var(--hs-border-field-focus);
  background: var(--hs-accent-subtle);
  color: var(--hs-accent-ink);
}

[data-hs="2"] .hs-cs-choice:hover:not([aria-checked="true"]) {
  border-color: var(--hs-border-strong);
  color: var(--hs-fg-primary);
}

/* The connector settings screen used to pick a mailbox with a tab strip
   (.hs-cs-account-tab). Every account is now a card in a list — see the note in
   connect-app.js — so the picker and its theming went with it. What replaced it
   is coloured here, since these cards carry a status the theme has to follow. */
[data-hs="2"] .hs-cs-acct {
  border-color: var(--hs-border-subtle);
  background: var(--hs-bg-sunken);
}

[data-hs="2"] .hs-cs-acct--expired {
  border-color: var(--hs-danger-border);
  background: var(--hs-danger-bg);
}

[data-hs="2"] .hs-cs-acct__email {
  color: var(--hs-fg-primary);
  font-family: var(--hs-font-ui);
}

[data-hs="2"] .hs-cs-acct__state {
  background: var(--hs-success-bg);
  color: var(--hs-success-fg);
  font-family: var(--hs-font-ui);
}

[data-hs="2"] .hs-cs-acct__state--bad {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-inverse);
}

[data-hs="2"] .hs-cs-acct__why {
  color: var(--hs-danger-fg);
}

/* ============================================================
   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-md);
  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);
  -webkit-appearance: none;
  appearance: none;
}

[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.
   Use an opaque token — bridge remaps --hs-bg-raised to --hs-glass-card (0.72). */
[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-neutral-0, #ffffff);
  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-card);
  -webkit-backdrop-filter: var(--hs-glass-blur-card);
  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. The AI number lives in
   Tools & settings (Number and Forwarding Instructions), not as a page hero.
   ============================================================ */
[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. Both are mark-ONLY
   slots — no wordmark beside them, so no ratio to hold. The mobile topbar mark
   used to be sized here too; it is half of a lockup, so hs-brand.css owns it
   now and sizes it from the lockup font-size. */
[data-hs="2"] .hs-adm-auth-logo { width: 56px; height: 56px; }
[data-hs="2"] .ws-unlock__mark { width: 36px; height: 36px; }

/* 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%; }

/* ============================================================
   Content cards are OPAQUE on the app canvas

   A 0.72 white card with `blur(24px) saturate(1.7)` behind it shows 28% of the
   wash and then amplifies its chroma. That makes a card's colour a function of
   where it sits on the page: the admin Overview KPI row is four cards from one
   rule, and the leftmost reads lavender while the rightmost reads white-green.
   Position-dependent component colour is not a background decision any more.

   So the surfaces the reader looks THROUGH go solid, and glass survives on the
   chrome that floats OVER content, where the sampling is the point:

     stays glass   .hs-dock__pill (--hs-dock-*, --hs-glass-blur-strong)
                   .hs-dialog, .hs-toast, buttons, filter toggles
                   (--hs-glass-control*, which this block does not touch)
     goes solid    .hs-card / .hs-adm-card / .hs-cn-card, .hs-stat,
                   .hs-wallet__card, and everything reaching them through
                   --hs-bg-raised, which hs-bridge.css remaps to --hs-glass-card

   Popovers and menus were already opaque a few hundred lines up, so they need
   nothing here.

   Scoped to [data-hs-bg="photo"] rather than to the surface: two pages
   (profile, joined) use these same surfaces on the plain token wash and are out
   of scope for this pass.

   The fills come from --hs-bg-solid-card / --hs-bg-solid-sunken, which are
   theme roles, so this block is correct in dark without restating itself.
   Pointing them at --hs-bg-raised instead would be a cycle: hs-bridge.css
   defines --hs-bg-raised AS --hs-glass-card.

   Not a contrast regression — semantic.json's $reducedTransparency has shipped
   this same swap under `prefers-reduced-transparency` since the tokens existed,
   and its note is the reason why: "the solids are the roles the glass was
   already compositing towards, so check-contrast.mjs already covers the text
   on them."
   ============================================================ */
[data-hs="2"][data-hs-bg="photo"] {
  --hs-glass-card: var(--hs-bg-solid-card);
  --hs-glass-raised: var(--hs-bg-solid-card);
  --hs-glass-sunken: var(--hs-bg-solid-sunken);
  --hs-glass-card-border: var(--hs-border-default);
  --hs-glass-rim: var(--hs-border-default);
  --hs-glass-lift: var(--hs-elevation-raised);
  --hs-glass-lift-strong: var(--hs-elevation-overlay);
  --panel: var(--hs-bg-solid-card);
  /* An opaque element's backdrop-filter is invisible, so this is a GPU saving
     rather than a visual change — every card was compositing a 24px blur of the
     wash that nothing could see. Only the CARD blur goes: --hs-glass-blur still
     frosts dialogs, toasts, the dock sheet and controls, and
     --hs-glass-blur-strong still belongs to the dock pill. */
  --hs-glass-blur-card: none;
}

/* 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, trimmed 2026-08. This band measured 114px on the rail —
     16px either side of a 40px line box, plus a 16px bottom margin — for a page
     title that never changes on the screen the owner came to read. It is the
     first thing on EVERY page, so it was also the most expensive.

     4px/0 and a 12px margin puts it near 52px. The dock block below restores
     the old figures: there the band is a fixed title bar and its bottom padding
     is the fade runway, which cannot be trimmed without breaking the mask. */
  padding-block: 4px 0;
  /* Keep title flush with content: desktop gutter comes from .hs-portal-main;
     mobile sets matching inline padding on head + body below. */
  padding-inline: 0;
  margin-bottom: var(--hs-space-3);
}

[data-hs="2"] .hs-portal-section__title {
  line-height: 1.25;
  overflow: visible;
  /* 2xl caps at 26px where 3xl caps at 32px. Restored to 3xl on the dock below:
     3xl already floors at 26px on a phone, so the rail is the only place this
     was ever 32px, and the only place it needed trimming. */
  font-size: var(--hs-text-2xl);
}

/* One content gutter for head + body. Titles must start on the same edge as
   the first content column — never inset less than the body. */
[data-hs="2"] .hs-portal-section {
  --hs-portal-gutter: var(--hs-space-5);
}

[data-hs="2"] .hs-portal-section__body {
  padding-inline: 0;
  padding-bottom: var(--hs-space-6);
}

/* ============================================================
   The layout breakpoint

   Both halves are documented in apps/handshake/public/poc5-pages.css:

     rail  @media (min-width: 900px), (min-width: 744px) and (min-height: 900px)
     dock  @media (max-width: 743px), (max-width: 899px) and (max-height: 899px)

   The height term keeps an iPad in portrait (744-834px wide, tall) on the rail
   while a phone in landscape (844 x 390) stays on the dock. Only the rules that
   SWITCH layout carry the pair — in this file that is the page-head band and the
   conversations page tint. The plain `max-width: 899px` blocks below are content
   refinements for a narrow content pane and are right in either layout.
   ============================================================ */
@media (max-width: 743px), (max-width: 899px) and (max-height: 899px) {
  /* The dock keeps the pre-2026-08 band: the shorthand below restores the 16px
     top the rail gave up, and `margin-bottom` goes back with it. Nothing here
     is a density decision — the runway and the mask depend on these figures. */
  [data-hs="2"] .hs-portal-section__title {
    font-size: var(--hs-text-3xl);
  }

  [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-portal-gutter, var(--hs-space-5)) calc(var(--hs-space-4) + 22px);
    margin-bottom: var(--hs-space-4);
    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);
  }

  [data-hs="2"] .hs-portal-section__body,
  [data-hs="2"] .hs-portal-section--agents .hs-portal-section__body--agents {
    padding-inline: var(--hs-portal-gutter, var(--hs-space-5));
  }

  /* Where the dock exists it is the tallest thing at the bottom, so it — not
     the assistant — sets the inset. One override, and every pane that reads
     --hs-page-bottom-inset follows.

     The selector list has to MATCH the base declaration in hs-bridge.css,
     which escalates onto `.surface-admin` / `.surface-user` as well as the
     root. A root-only override here inherits down to html and is then
     overwritten on those wrappers by the base value — the console read
     `44px + 24px` (the desktop assistant) while html read `52px + 24px`, and
     the dock went on covering the last card. Re-declaring a custom property on
     a descendant beats inheritance, every time. */
  [data-hs="2"],
  [data-hs="2"] .surface-admin,
  [data-hs="2"] .surface-user {
    --hs-page-bottom-inset: calc(var(--hs-dock-h) + var(--hs-space-6) + env(safe-area-inset-bottom, 0px));
  }

  /* 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: var(--hs-page-bottom-inset);
    scroll-padding-bottom: var(--hs-page-bottom-inset);
  }
}

/* Desktop / web: titles scroll away with the page — no sticky pin. */
@media (min-width: 900px), (min-width: 744px) and (min-height: 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 fill-state SEMANTICS stay — `is-fill-needed` marks a slot you still have
   to complete, `is-fill-set` one you have — but the 2026-08 audit (UI-002,
   WP-4b, approved) moved the colour off the field body and onto the leading
   icon. A sentence of violet- and green-filled controls was a third form
   language on one page; a neutral shell with a coloured icon carries the same
   progress signal in the system's voice. An earlier pass here had kept the
   body tints and tokenised them — superseded by that decision.

   `--st-needed` still maps to the token so the ICON reads jasmine, not the
   old raw violet. poc5 paints the neutral shells; nothing here re-adds fills. */
[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);
}

/* 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 animated border AND background directly, and an
   animated value beats a normal declaration — the old keyframes were how the
   violet body fill survived every override. Same trick, inverted: redefining
   the keyframes under the same name (last definition wins) keeps the pulse on
   the border alone, and the body stays the neutral shell (UI-002/WP-4b). */
@keyframes hs-st-needed-pulse {
  0%,
  100% {
    border-color: var(--hs-border-strong);
    background: var(--hs-bg-raised);
  }
  50% {
    border-color: var(--hs-accent2-solid);
    background: var(--hs-bg-raised);
  }
}

/* ============================================================
   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 {
  /* ONE number runs the stack: how deep the next card sits on this one.

     It used to be two — a 112px card height and an 82px peek — and the head was
     centred in the 112, so every card centred its content against a band 30px
     taller than the strip anyone can see. That is why the collapsed rows all
     carried a fat gap above the logo and a thin one below, why a task title
     jumped whenever a subtext line appeared under it, and why the last card in
     a list (which nothing sits on) looked different from the rest.

     Now a collapsed card is padding — content — padding — tuck: it is as tall
     as what it holds, and it only carries the tuck when another card actually
     overlaps it. The visible strip is therefore the head, exactly, with equal
     space above and below. */
  --hs-cw-tuck: 30px;
  /* The shallower tuck a Conversations day tab carries — declared here, on the
     stack, because the CARD BELOW the tab reads it too, and a custom property on
     the tab itself inherits down to its own children, never sideways. */
  --hs-cw-tab-tuck: 14px;
  /* The tab's VISIBLE strip — the part that sticks out above the next card:
     its 8px top padding, the 19px date line, and 4px of air. The card above a
     tab carries this on top of the tuck, so the strip fits inside that card's
     dead space and the next card still has a full 30px tuck to sit on. Without
     it the two edges met flush and the seam showed two bare corners.

     A pixel of drift in the date line only moves the overlap a pixel; it can
     never eat the strip, because the strip is set by the tab's own padding and
     the 4px is the difference between that padding and the next card's pull-up. */
  --hs-cw-tab-lead: 31px;
  /* The stack's row inset. 20px is the right figure for a card you READ; these
     are rows you scan past, and at 20px the inset was two thirds of a collapsed
     Your info row (60px of card around 20px of title). 13px sits deliberately
     between space-3 and space-4 — on the scale it would be 12px, which crowds
     the 40px connector logo. Measured 2026-08: this one value is 98-182px per
     screen across Your info, Connectors, Conversations, Tasks and the three
     agent tool lists, because they all read it from here.
     Keep in step with the copy on .hs-ptool-list. */
  --hs-cw-pad: 13px;
  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 to animate a fixed card height → `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 0fr → 1fr grid interpolates properly. That grid is on `.hs-cw-card__body`
     (one track, nothing else in it) — NOT here, and this is not a style choice.

     The card's rows were `auto 0fr` → `auto 1fr`, and Chrome interpolates the
     whole track list: mid-transition it resolves something like `auto 0.14fr`,
     and in an indefinite-height grid the content-sized track absorbs whatever
     the fraction has not claimed yet. Measured on a phone-width conversation
     card, the head row went 90px → 533px → 90px inside one 420ms open, and a
     connector title rode 18px down and back up with it. Everything below the
     head was shoved ~440px down and then slid back up to meet it — which is
     the shudder, and why the transcript looked like it flew in from the bottom
     of the page rather than unfolding under its own title.

     One track cannot be robbed by a sibling track, so the body owns the
     animation and the head is left alone. The card still holds both rows; it
     just sizes to them instead of interpolating between them. */
  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 auto;
  /* 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;
  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;
  /* `scale` is in the list because this rule REPLACES the surface-press
     transition declared at the top of this file — `transition` is one property
     and does not layer, and this selector is both equally specific and later.
     Without it the card's press snaps on and off instead of easing.

     The two trailing corners are here for the same reason: they square off
     while a delete pane is showing (see "the card gives up ITS trailing
     corners"), and this list is the only place that can ease them. */
  transition:
    margin-top var(--hs-duration-wallet) var(--hs-ease-spring),
    padding-bottom var(--hs-duration-wallet) var(--hs-ease-spring),
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-base) var(--hs-ease-spring),
    scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* 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(-1 * var(--hs-cw-tuck));
  z-index: 1;
}

/* ---- The stack's z ladder ------------------------------------------------
     1  a card in the stack (the rule above), and a row being dragged
     2  the swipe-delete pane (poc5-pages.css)
     6  an open card
     7  a focused card

   A dragged row stays level with its neighbours and lets DOM order decide.
   This rule exists to HOLD it there.

   It read `4` for a while, to lift the row over the card below it. That was the
   wrong axis. A card's bottom `--hs-cw-tuck` is dead space the next card sits
   IN — 30px of this card's own glass that its neighbour is meant to paint over.
   Lifting the dragged row inverted that: a row dragged left grew a 30px skirt
   across the top of the card below, so the neighbour's icon and title vanished
   behind blank glass while its trailing tag, which sits to the right of the
   translated box, stayed where it was. The row below looked torn in half, and
   that is the artifact the swipe appeared to leave behind.

   Nothing needed the lift. The pane already clears the stack at `2`, and no
   card overlaps the dragged row's VISIBLE strip: the next card's top edge is
   exactly where that strip ends. `1` rather than `auto` so the FIRST card in a
   list — which has no `+ .hs-cw-card` rule to give it a value — joins the same
   layer as the rest instead of falling to poc5's `3`. That poc5 rule is 0-2-0
   against this one's 0-3-0, so it now reaches only non-wallet hosts, which
   carry no tuck to skirt. */
[data-hs="2"] .hs-cw-card.hs-rowdel-dragging {
  z-index: 1;
}

/* The tuck is dead space for the next card to sit on, so a card only carries it
   when something actually follows it. The last card in a list has nothing on top
   of it and would otherwise show the tuck as a stray 30px of bottom padding.
   The selector mirrors the overlap rules exactly: a card, or a Conversations day
   tab, which sits on this card's dead space the same way a card does.

   `+ .hs-cw-card:not([data-open])` for the same reason: an OPEN card does not
   sit on this one (see the rule below that gives it its own margin), so the
   dead space it would have sat in is just an empty strip at the bottom of this
   card — which read as the card above being cut off. */
[data-hs="2"] .hs-cw-card:not([data-open]):has(+ .hs-cw-card:not([data-open])),
[data-hs="2"] .hs-cw-card:not([data-open]):has(+ .hs-activity-daygroup) {
  padding-bottom: var(--hs-cw-tuck);
}

/* The card above an open one keeps a normal edge rather than none at all.
   Dropping the tuck entirely left its last line of text against the card's
   bottom border — an abrupt cut, which is exactly what the dead space was
   hiding when a neighbour sat in it.

   The second selector is the same case with a day tab in between: when the day's
   first card opens, the tab lifts out of this card's dead space along with it
   (see the tab rules further down), so nothing is sitting there either. */
[data-hs="2"] .hs-cw-card:not([data-open]):has(+ .hs-cw-card[data-open]),
[data-hs="2"] .hs-cw-card:not([data-open]):has(
    + .hs-activity-daygroup + .hs-cw-card[data-open]
  ) {
  padding-bottom: var(--hs-space-3);
}

/* A day tab needs MORE dead space than a card does, because the tab is not what
   sits on this card — the next day's first card is, and the tab's visible strip
   stands between them. One tuck only covered the strip, so the next card landed
   flush on this card's bottom edge instead of 30px up it, and the seam was the
   one place in the stack where two corners showed. Tuck plus strip gives the
   next card the same 30px bite it takes anywhere else. */
[data-hs="2"] .hs-cw-card:not([data-open]):has(+ .hs-activity-daygroup) {
  padding-bottom: calc(var(--hs-cw-tuck) + var(--hs-cw-tab-lead));
}

/* An open card stands out of the stack rather than in it.
   It used to keep the 30px overlap it had while closed, so the moment it opened
   it laid a popover-strength shadow across the card above and grew out from
   under it — a grey band cutting the previous card in half, and a second seam
   where the next card's own shadow met this one's growing edge. Standing it off
   both neighbours by the same gap puts the shadow on the background, which is
   what a raised card's shadow is for. */
[data-hs="2"] .hs-cw-card[data-open] {
  cursor: default;
  /* Overlay, not popover. A popover shadow (e5: 40px blur at 18% ink) is sized
     for a layer floating over a page; on a card that is still in the stack it
     spread across three cards below it and read as grime rather than lift. e3
     puts the card above its neighbours and stops there. */
  box-shadow: var(--hs-elevation-overlay);
  z-index: 6;
}

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

/* `.hs-cw-card__fold` is in the list because it is the body's only child now,
   so `> * > *` stops at the fold and never reaches the sections inside it. */
[data-hs="2"] .hs-cw-card > * > *,
[data-hs="2"] .hs-cw-card__fold > * {
  border-bottom: 0 !important;
}

/* Block inset from the stack's token, so an OPEN head sits on the same rhythm
   as the collapsed ones below it (`:not([data-open])` further down already read
   the token; this rule is what an open head lands on, and it used to keep the
   old 20px on its own).

   INLINE stays at space-5 on purpose: `.hs-cw-card__fold` sets
   `padding-inline: var(--hs-space-5)`, so pulling the head in would step the
   title 7px off the body it introduces. */
[data-hs="2"] .hs-cw-card__head {
  display: flex;
  align-items: center;
  gap: var(--hs-space-4);
  padding: var(--hs-cw-pad, 13px) var(--hs-space-5);
}

/* Title and caption on one shut head. The head itself centres — that is right
   for a logo, a checkbox, a caret — and wrong for two sizes of type: the
   smaller line floats in the title's box and every row looks randomly aligned.
   Put the words in this wrap; leave the mark on the head. */
[data-hs="2"] .hs-cw-card__line {
  display: flex;
  align-items: baseline;
  gap: var(--hs-space-4);
  flex: 1 1 auto;
  min-width: 0;
}

/* The account STACKS UNDER the connector name, at every width.
 *
 * This used to be a right-hand lane: name on the left, address far away on the
 * right, action after it. Three problems, all of them the lane's fault. The
 * address is the longest string on the row and the lane had to guess a width
 * for it (120px, then 240px). Its right edge moved with whatever the action
 * happened to be, so a Connected row and an Add row for the same connector
 * disagreed about where an email started. And an address separated from its
 * connector by 500px of nothing reads as two unrelated facts.
 *
 * Stacked, all three lines — connector, address, purpose — share one left edge
 * beside a full-size logo, which is the shape the narrow layout always had and
 * the one that reads. The indent keeps the address aligned with the NAME rather
 * than with the logo, so the text block is a block; it is derived from the logo
 * box so the two cannot drift.
 *
 * `.hs-connector-perm` had a lane here too. connectorPermCell() has returned an
 * empty string since the identical per-row "Private" chip was retired, so the
 * selector matches nothing on a connector row and reserved 80px of nothing. */
[data-hs="2"] .hs-cw-card__head:has(.hs-connector-name) {
  /* One gap, read twice: the name cell puts it between the logo and the title,
     and the account row uses it to compute the indent that lines the address up
     under that title. Declared here rather than hardcoded in both places —
     first attempt used space-3 for the indent while the name cell used 10px,
     and the address sat 2px off the name all the way down the stack. */
  --hs-conn-logo-gap: 10px;
  --hs-conn-indent: calc(var(--hs-size-avatar-lg) + var(--hs-conn-logo-gap));
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "name action"
    "meta action";
  align-items: center;
  gap: 2px var(--hs-space-4);
}

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

[data-hs="2"] .hs-cw-card .hs-connector-account {
  grid-area: meta;
  flex: initial;
  min-width: 0;
  text-align: start;
  padding-inline-start: var(--hs-conn-indent);
}

[data-hs="2"] .hs-cw-card .hs-connector-action {
  grid-area: action;
  flex: initial;
  display: flex;
  justify-content: flex-end;
  align-self: center;
}

/* The head IS the visible strip, so its own padding is the breathing room —
   nothing is centred against a taller phantom band any more. Equal top and
   bottom, and a card grows when its content does. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head {
  padding-block: var(--hs-cw-pad);
}

/* 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.

   Scoped to the connector head, which is the row flex this was written for. On
   the Conversations head — a COLUMN flex — `align-self: center` centres on the
   cross axis, which is horizontal: the summary line stopped stretching to the
   head's width, took its full 735px max-content instead, and spilled equally off
   both edges of a 335px card. Ellipsis never fired because there was nothing to
   truncate against. */
[data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head:has(.hs-connector-name) > * {
  align-self: center;
}

/* The name cell is a list row, so the logo and the text sit on one centre line.
   v1 top-aligns it (`.hs-connector-name { align-items: flex-start }` plus a 2px
   nudge on the logo) because in the old table the capability bullets hung under
   the name and the logo had to stay level with the first line. Those bullets
   moved to the card body, so all that is left is a 32px logo next to one or two
   short lines — top-aligned, that reads as the text having slipped upward. */
[data-hs="2"] .hs-cw-card .hs-connector-name {
  align-items: center;
}

[data-hs="2"] .hs-cw-card .hs-connector-name .hs-connector-logo,
[data-hs="2"] .hs-cw-card .hs-connector-name .hs-connector-logo--kb {
  margin-top: 0;
}

/* 28px was the table-row logo size, then 32px once the row became a card strip.
   The head stacks three lines now — connector, address, purpose — and a 32px
   mark beside a 3-line block reads as an afterthought rather than as the thing
   the row is about. 40px is the avatar-lg step and squares up with the stack. */
[data-hs="2"] .hs-cw-card .hs-connector-logo {
  width: var(--hs-size-avatar-lg);
  height: var(--hs-size-avatar-lg);
  flex: none;
  object-fit: contain;
}

/* The logo spans the whole stack, so it centres against all of it rather than
   against the title line alone. */
[data-hs="2"] .hs-cw-card .hs-connector-name {
  gap: var(--hs-conn-logo-gap);
}

[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 status line is a block inside the ellipsis span, so the span's own
   truncation does not reach it: on a narrow card it was sheared off mid-word at
   the action button's edge with no ellipsis to say so. */
[data-hs="2"] .hs-cw-card .hs-connector-name__status {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 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;
}

/* Account rows inside a grouped connector card. Colour only — the layout is in
   poc5-pages.css, same split as every other bridged component. */
[data-hs="2"] .hs-cw-acctrow {
  border-top-color: var(--hs-border-subtle);
}

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

[data-hs="2"] .hs-cw-acctrow__state {
  background: var(--hs-danger-solid);
  color: var(--hs-fg-inverse);
  font-family: var(--hs-font-ui);
}

[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);
  padding: 0;
  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; }

/* Feedback / report modals: match dialog radius, not square anti-glass hacks.
   Background stays the opaque token: --hs-bg-raised is remapped to
   --hs-glass-card (0.72) on bridged pages, and this rule comes after the
   solid-surface block above, so using it here made cards see-through
   (the billing cancellation modal, in particular). */
[data-hs="2"] .hs-feedback-modal__card {
  border-radius: var(--hs-radius-xl);
  opacity: 1;
  background: var(--hs-neutral-0, #ffffff);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* ============================================================
   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;
  /* Extra bottom pad so chatbot panel shadow isn’t clipped by the stage */
  padding-block: var(--hs-space-6) calc(var(--hs-space-6) + 12px) !important;
  overflow: visible !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) {
  /* Two numbers, and the gap between them IS the effect.

       --hs-seg-lift    how far the thumb's visible pill spreads past its cell
       --hs-seg-inset   the track's padding, and the thumb box's own offsets

     The pill stands `lift - inset` proud of the track's outer edge. At 3 and 2
     that is 1px on all four sides: a 38px cell gives a 44px pill in a 42px
     track. Equal values would make the pill flush with the track vertically
     while still overhanging the ends — an asymmetry that reads as a mistake.

     Anything that consumes these must use both, or the thumb desynchronises
     from the track it sits in. Set --hs-seg-lift to 0 to opt an instance out;
     see the scroll-container carve-out below, the one place the overhang would
     be clipped. */
  --hs-seg-lift: 3px;
  --hs-seg-inset: 2px;
  /* ---- The size ladder ---------------------------------------------------
     Three more numbers, and they are the ONLY three a size variant may move.
     Everything else about the control — the lift, the inset, the radius, the
     thumb — is identical at every size, which is what stops a small strip from
     becoming a second design.

       --hs-seg-cell   the tab's own height; the track is cell + 2 × inset
       --hs-seg-pad    the tab's side padding
       --hs-seg-size   the label's font size

     Defaults are the tab size the product already ships: 38 + 2×2 = a 42px
     track. Nothing that exists today moves by a pixel when this lands.

     Why custom properties and not a taller selector: the variants below set
     three values on the TRACK, and the button rule reads them from there. A
     variant written as `.sgc--sm button { height: … }` is how v1 did it, and
     that rule lost to the bridge's own `min-height` for months without
     anything failing — see the dead rules this replaces in handshake.css. */
  --hs-seg-cell: 38px;
  --hs-seg-pad: var(--hs-space-5);
  --hs-seg-size: var(--hs-text-sm);
  display: inline-flex;
  position: relative;
  gap: 0;
  padding: var(--hs-seg-inset);
  /* No border. A ring around the track competes with the raised thumb for the
     eye — the recessed tint alone is enough to read as a well, and the thumb's
     own shadow is what separates figure from ground. */
  border: 0;
  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;
}

/* ---- Size ladder: three steps, one job each ----------------------------
   Every variant moves cell / pad / size and NOTHING else. The lift and inset
   are deliberately left alone, so the thumb's pill still stands exactly 1px
   proud of the track on all four sides at every size — the detail that makes
   the control read as a physical object rather than a drawn highlight.

     (default)   42px track   page navigation. Which screen am I on.
     .sgc--sm    34px track   a choice inside a settings row, beside a label.
     .sgc--xs    28px track   filters in a card header or a table toolbar.

   Below 26px the thumb's shadow stops separating from the track, so --xs is
   the floor. There is no fourth step, and adding one is a design decision, not
   a CSS one.

   These replace two dead v1 rules in `public/handshake.css`:

     .sgc--lg button { height: 44px; … }   never used by any markup
     .sgc--sm button { height: 30px; … }   used by nine sites, and INERT —
                                           `min-height` on the bridge's
                                           (0,3,0) rule clamps a `height` set
                                           at (0,1,1), so every "small" strip
                                           rendered at the full 38px cell.

   That is why this is a variable and not a taller selector: the bridge owns
   the geometry now, so a variant cannot quietly lose to it. */
/* Keeps --hs-text-sm. The mock for this proposed 13px, which is not on the type
   scale; at a 30px cell 14px still clears with 6px above and below, and holding
   the size means a settings row does not introduce a third type size next to
   its own label and description. Only the box gets smaller. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs).sgc--sm {
  --hs-seg-cell: 30px;
  --hs-seg-pad: var(--hs-space-3);
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs).sgc--xs {
  --hs-seg-cell: 26px;
  --hs-seg-pad: var(--hs-space-2);
  --hs-seg-size: var(--hs-text-xs);
  /* The one place the lift moves. At a 26px cell a 3px overhang is 12% of the
     control's height and reads as a pill that has outgrown its track; 2px
     against a 1px inset keeps the same 1px proud with a shorter shadow. */
  --hs-seg-lift: 2px;
  --hs-seg-inset: 1px;
}

/*
 * `a` is in the list because a tab that navigates to another DOCUMENT has to be a
 * link, not a button. The admin console's group strip has two — Prompt Lab and the
 * Style Guide are their own pages — and until 2026-08-22 no rule here matched them:
 * they rendered at the inherited font size with no padding, no equal-width share of
 * the track, and a live link underline, wedged against the neighbouring tab. Adding
 * `a` costs no specificity, because `:is()` takes its heaviest argument and
 * `.v2-pp-tab` (0,1,0) already outweighs both element selectors.
 */
[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, a, .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;
  /* Link tabs only. A button has none to remove. */
  text-decoration: none;
  /* `height: auto` is load-bearing, and it is the other half of why the old
     .sgc--sm never worked. v1 sets a FIXED height on the cell:

       .sgc button, .vtoggle button { height: 36px; }   (0,1,1)

     A `min-height` only clamps upward. At the default 38px cell the bridge's
     min-height wins and the fixed 36 is invisible — which is why nobody found
     this — but any variant SMALLER than 36px is silently floored at 36. That
     is the whole size ladder, dead on arrival, for the same reason the v1
     variant was. Releasing the height here lets --hs-seg-cell govern alone. */
  height: auto;
  min-height: var(--hs-seg-cell);
  padding-inline: var(--hs-seg-pad);
  border: 0;
  border-radius: var(--hs-radius-pill);
  background-color: transparent;
  background-image: none;
  font-family: var(--hs-font-ui);
  font-size: var(--hs-seg-size);
  /* Medium, not semibold. Every label at 600 made all of them look selected;
     the weight step to the active label is now half the selection signal.
     Measured across all four active states of the widest control in the app:
     the track holds 395.6px either way, so this cannot jitter the layout. */
  font-weight: var(--hs-weight-medium);
  color: var(--hs-fg-tertiary);
  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, a, .v2-pp-tab):hover {
  color: var(--hs-fg-secondary);
}

/*
 * Link tabs only, and deliberately not folded into the rule above.
 *
 * A `button` centres its own content; a grid item that was an inline `a` is
 * blockified to `block` and does not, so its label sat on the top edge of the
 * 38px track. Grid here rather than a line-height, because a line-height tall
 * enough to centre also changes where the text sits inside a wrapped label.
 *
 * This must stay `a`-only. Several of these controls put a count badge beside the
 * label (`.hs-conv-ff__count`), and `place-items: center` on a grid with two
 * children stacks them vertically.
 */
[data-hs="2"] :is(.sgc, .v2-pp-tabs) > a {
  display: grid;
  place-items: center;
}

/* The thumb is the only fill. The active tab states the selection in its text —
   primary ink at semibold against tertiary at medium — 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, a, .v2-pp-tab):is(.is-on, .is-active, .active, [aria-selected="true"]) {
  background-color: transparent !important;
  background-image: none !important;
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
  box-shadow: none;
}

[data-hs="2"] :is(.sgc, .v2-pp-tabs) > :is(button, a, .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-billing-topup-picker--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-card);
  -webkit-backdrop-filter: var(--hs-glass-blur-card);
  border: 1px solid var(--hs-glass-card-border);
}

/* ============================================================
   Purchase — v1's own CTA class, outside the four button families

   2026-08 audit (UI-003): this was the one page whose PRIMARY action wore the
   secondary white-glass fill — the paid-conversion CTA read quieter than the
   "See usage" pill above it. It takes the primary ink treatment like every
   other page's main action. (.v2-sk-brief-btn and .cl-send-btn stayed in the
   selector from a since-removed page; .v2-pp-cta is the only live consumer.)
   ============================================================ */
[data-hs="2"] .v2-pp-cta {
  height: var(--hs-size-control-lg);
  padding-inline: var(--hs-space-6);
  border: 1px solid transparent;
  border-radius: var(--hs-radius-pill);
  background-color: var(--hs-fg-primary);
  background-image: none;
  box-shadow: var(--hs-elevation-raised);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-sm);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-inverse);
}

[data-hs="2"] .v2-pp-cta:hover:not(:disabled):not(.is-current-plan) {
  background-color: var(--hs-neutral-800);
}

/* ============================================================
   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.

   .hs-cf-portal__choice is excluded too, and it is not a button in the sense
   this rule means. Call-forwarding builds its wizard answers — line type,
   carrier, always-vs-sometimes — as `.hs-btn` elements that carry an icon, a
   title and a description line and need ~70px. Clamped to the 36px control
   height with `padding-block: 0`, each one kept its full-height contents and
   simply overflowed: every answer's title and description printed across the
   card below it, which is the buttons-intersecting the forwarding flow showed
   on a phone. They size themselves in poc5-pages.css.

   .hs-conv-notif-btn is excluded because it is half of a pair. Notifications
   carries `.hs-btn`; the Filter button beside it does not. So this rule pinned
   Notifications to the 36px control height with `!important` while Filter took
   the 44px square from the page-header block below — the two sat side by side
   in the same header, one 8px shorter, on every phone. The page-header block
   owns both heights instead. */
[data-hs="2"] :is(.hs-btn, .hsb, .hs-adm-btn):not(.julia-chat-form .hs-btn):not(.hs-btn--lg):not(.hs-btn-lg):not(.hs-cf-portal__choice):not(.hs-conv-notif-btn) {
  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);
}

/* Empty-field hint text. v1 painted placeholders with --ink-3 / --hs-text-subtle
   / --hs-neutral-400 — the same family as filled values — so an empty Business
   info field read as already completed. Placeholder is a separate role and
   stays lighter than disabled. !important is required because training, portal,
   and admin sheets scope these to IDs. The scheduled-task token that inherits
   its pill colour is the one exception. */
[data-hs="2"] :is(input, textarea)::placeholder {
  color: var(--hs-fg-placeholder) !important;
  -webkit-text-fill-color: var(--hs-fg-placeholder) !important;
  opacity: 1;
  font-weight: var(--hs-weight-regular);
}

[data-hs="2"] .hs-st-composer__token.is-placeholder::placeholder {
  color: inherit !important;
  -webkit-text-fill-color: inherit !important;
  opacity: 0.85;
}

/* --- 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, a, .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-card);
  -webkit-backdrop-filter: var(--hs-glass-blur-card);
  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 once 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 on. The two states now say different things instead of
   competing: hover is a neutral pill any row can wear, and selected is a bold
   label with a filled icon and no background at all. That means hover applies
   to the active row too — the `:not(.active)` guard is deliberately gone.

   This block has to repeat the app layer's decision (poc5-pages.css,
   `.hs-portal-seg a.active`): at `[data-hs="2"] .hs-portal-seg a.active` it
   outranks it, so an app-layer change alone silently does nothing. */
/* Recessed, not the control ghost. `--hs-glass-control-ghost` is 62% white and
   the sidebar panel is 92% white, so it painted a pill nobody could see — which
   did not matter while the accent pill marked the selected row, and matters a
   lot now that hover is the only background in the nav. */
[data-hs="2"] .hs-portal-seg a:hover {
  background: var(--hs-glass-recessed);
  background-image: none;
}

[data-hs="2"] .hs-portal-seg a.active {
  background-color: transparent;
  background-image: none;
  color: var(--hs-fg-primary);
  font-weight: var(--hs-weight-bold);
  box-shadow: none;
}

[data-hs="2"] .hs-portal-seg a.active:hover {
  background: var(--hs-glass-recessed);
}

/* --- 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 wash strip — 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__wash,
[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.

   Scoped to heads that actually hold connector cells. This grid used to match
   EVERY .hs-cw-card__head, and Conversations and Tasks share the class with
   completely different children — a `__top` strip and a `__sum` line, neither of
   which carries a grid-area. They auto-placed into the two tracks, and because
   `__top`'s contents are all `min-width: 0` with nowrap ellipsis, the
   `minmax(0, 1fr)` track resolved to ZERO while `auto` gave the summary its full
   max-content width. The channel icon, the timestamp, the title and the outcome
   tag were then laid out inside a 0px box and printed straight over the summary
   text — which is the icons-on-top-of-text the conversations list showed on a
   phone. Everything else keeps the flex head declared above. */
@media (max-width: 899px) {
  /* The stacked head is the layout at every width now (see the head grid
     above), so this block no longer restates it — only what narrow changes.
     A tighter column gap, smaller meta type, and the address loses its indent:
     at 375px the 44px of it is width the longest string on the card cannot
     spare. */
  [data-hs="2"] .hs-cw-card__head:has(.hs-connector-name) {
    gap: 2px var(--hs-space-3);
  }

  /* The permission tag shares the meta row and no longer reserves 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-account {
    padding-inline-start: 0;
  }

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

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

  /* Collapsed, a wallet card still shows the account so two Gmail or Calendar
     rows can be told apart and tabbed onto. Only the Private tag hides.

     The areas are no longer restated here: the head declares them once,
     unconditionally, for every width and both open states. This rule keeps only
     what a SHUT card changes — two explicit rows so the account line has a
     track even when the meta is empty, and content centred in them. Hiding the
     meta alone used to leave a two-row template with an empty second row;
     dropping to one row then hid the account and two Calendar cards looked
     identical. */
  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-cw-card__head:has(.hs-connector-name) {
    grid-template-rows: auto auto;
    align-content: center;
  }

  [data-hs="2"] .hs-cw-card:not([data-open]) .hs-connector-perm {
    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 element, and the only one. A single-track grid going 0fr → 1fr:
   one track, so there is no content-sized sibling for the fraction to leak into
   (see .hs-cw-card above for what that cost). The card's own rows stay static.

   `display: none` on the fold would take it out of the grid entirely and leave
   nothing to interpolate, so it stays in flow and is clipped to zero height. */
[data-hs="2"] .hs-cw-card__body {
  display: grid;
  grid-template-rows: 0fr;
  /* No padding here. Padding is not something a track can shrink out of, so a
     body carrying 16/20px held its own `0fr` open at 36px — a 36px step onto
     the front of every expansion, before the smooth part started. Inside the
     fold it is clipped with everything else and the track reaches a true 0. */
  padding: 0;
}

[data-hs="2"] .hs-cw-card[data-open] > .hs-cw-card__body {
  grid-template-rows: 1fr;
}

/* The clipped content box. min-height: 0 is what lets a grid row actually
   reach 0; the overflow is what hides the part not yet revealed. */
[data-hs="2"] .hs-cw-card__fold {
  /* 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 the body 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 fold is outside
     the scroll container, so nothing can scroll past it.

     INLINE only. `min-height: 0` floors the CONTENT box, not the border box, so
     block padding here is 36px the track can never shrink below — the same
     floor that used to sit on the body, one level down. Measured: it put a 36px
     step on the front of every conversation expansion, before the smooth part
     began. The vertical inset is margins on the first and last child instead;
     the fold's `overflow: hidden` makes it a BFC, so those stay inside it. */
  padding-inline: var(--hs-space-5);
}

[data-hs="2"] .hs-cw-card__fold > :first-child {
  margin-block-start: var(--hs-cw-fold-lead, 0);
}

[data-hs="2"] .hs-cw-card__fold > :last-child {
  margin-block-end: var(--hs-cw-fold-tail, 0);
}

/* 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;
}

/* `.hs-cw-card__manage` was the card-floor footer that held Reconnect and
   Settings. Those live in the head's action lane now (see "Connector Settings"
   below), connectorGridRow no longer emits the wrapper, and the rules that
   styled it went with it. */

[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;
}

/* The expansion, plus the fade that softens its edge. One declaration, because
   a second `transition` on this element would replace the first — which is how
   the opacity fade used to be the body's only transition while the size change
   sat on the card.

   `visibility` is in the list on purpose: it interpolates discretely, so it
   flips to visible on the first frame of an open and holds until the last frame
   of a close. Without it the content vanishes the instant the card is
   deselected and an empty box collapses. */
[data-hs="2"] .hs-cw-card__body {
  transition:
    grid-template-rows var(--hs-duration-wallet) var(--hs-ease-spring),
    visibility var(--hs-duration-wallet) var(--hs-ease-spring),
    opacity var(--hs-duration-fast) var(--hs-ease-standard);
}

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

/* ---- ONE FOLD, and a conversation row is not a special case ------------

   There used to be two overrides here — a pixel `0px` shut state for
   conversation rows, and an `.is-foldsnap` escape hatch that switched the
   transition off while the app rewrote the track by hand. Both existed to serve
   a claim about `fr` tracks that turns out to be false: that an `fr` row in an
   auto-height grid cannot re-resolve when its content changes, so a fold filled
   by a fetch has to be driven in pixels.

   Measured on a tasks card, which is the plain fold with nothing helping it:
   open at `1fr`, append 400px of content, and the track goes 274px -> 674px,
   then back to 274px when the content is removed. `getAnimations()` on the body
   is EMPTY at every step. So an `fr` track does follow its content, and it
   follows it WITHOUT a transition — which is exactly the behaviour
   `.is-foldsnap` was hand-rolling: the reader's open animates, and everything
   the card does to itself afterwards lands.

   Deleting both overrides is what makes a conversation card and a task card the
   same component again. What it cost while they differed: the app wrote the
   track twice on every conversation open — `0 -> 88px` against the loading body,
   then a fresh spring to `709px` starting 71ms into the first — and two
   accelerations in one gesture is what the page was reported for as a shake.

   `.hs-activity-row` keeps ONE deliberate exception, and it borrows the track
   rather than owning it: a disclosure the reader opened inside the card
   ("Review with Julia", Conversation details) is animated by writing pixels for
   one transition and clearing them when it lands. See `resizeActivityFold` in
   workspace-app.js. An inline height wins over `1fr` on its own, so nothing here
   has to make room for it. */

/* ---- Wallet card hover: paint only, never geometry ---------------------
   Same idea as tasks cards: a soft wash on hover. No transform, no z-index,
   no sibling margin shift — those make the stack expand and hide cards below. */
[data-hs="2"] .hs-cw-card:not([data-open]):hover {
  background-color: var(--hs-neutral-50);
  box-shadow: var(--hs-glass-lift-strong);
}

/* The cards are tabbable, so focus needs a real ring rather than a fill.

   INSET, and in ink rather than in accent. A mint ring on a full-width card
   reads as brand chrome, not as focus. --hs-border-focus is fg-primary in
   both themes. Inset, the way the admin console's is, where a wallet that
   clips overflow cannot slice it off. */
[data-hs="2"] .hs-cw-card:focus-visible {
  outline: 2px solid var(--hs-border-focus);
  outline-offset: -3px;
  z-index: 7;
}

/* ---- 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-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option, .hs-conv-opt,
  .hs-chatbot-launcher-option, .hs-midcall-sms-mode__option) {
  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-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option,
  .hs-conv-opt, .hs-chatbot-launcher-option, .hs-midcall-sms-mode__option)::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-email-tone-opt, .hs-rules-opt, .hs-agent-setting-option,
  .hs-conv-opt, .hs-chatbot-launcher-option, .hs-midcall-sms-mode__option):is(.is-selected, .is-active, [aria-checked="true"], [aria-pressed="true"])::before {
  border-color: var(--hs-fg-primary);
  box-shadow: inset 0 0 0 4px var(--hs-fg-primary);
}

/* 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-fg-primary);
  background: var(--hs-fg-primary);
  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-border-field-focus);
  box-shadow: none;
}

[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. The longest pillar label 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: 743px), (max-width: 899px) and (max-height: 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: 743px), (max-width: 899px) and (max-height: 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), (min-width: 744px) and (min-height: 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, a, .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.

   Which also means this rule OUTRANKS the shared one, so its values have to
   track it. It exists to restate the shared geometry under a heavier v1 scope,
   not to differ from it — if you change the shared control, change these too. */
[data-hs="2"] .hs-seg-tabs .hs-info-subnav__seg > button {
  height: auto;
  min-height: var(--hs-seg-cell);
  padding-inline: var(--hs-seg-pad);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-seg-size);
  font-weight: var(--hs-weight-medium);
}

[data-hs="2"] .hs-seg-tabs .hs-info-subnav__seg > button:is(.is-on, .is-active, .active, [aria-selected="true"]) {
  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-card);
  -webkit-backdrop-filter: var(--hs-glass-blur-card);
  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 — wallet stack is the source of truth

   An earlier mobile pass turned rows into gap-separated glass cards. Tasks and
   Conversations now use the same .hs-cw-card wallet stack as Connectors
   (see “Tasks + Conversations wallet stacks” below). Keep only the overflow
   and title-column fixes that still help inside that stack.
   ============================================================ */
@media (max-width: 899px) {
  [data-hs="2"] :is(.hs-activity-tbl, .hs-activity-tbl__frame) {
    overflow: visible;
  }

  /* The head owns the grid now (see the "Conversation card" block in
     poc5-pages.css) and every line already shares one left edge, so there is no
     narrow-width column template to patch and no preview indent to zero out.
     Those two rules are what put the name on a 95px track and started the
     preview 126px to its left. */
}

/* ============================================================
   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);
  height: var(--hs-size-control-md);
  min-height: var(--hs-size-control-md);
  padding-inline: var(--hs-space-4);
  border: 1px solid var(--hs-glass-rim);
  background: var(--hs-glass-control);
  backdrop-filter: var(--hs-glass-blur);
  -webkit-backdrop-filter: var(--hs-glass-blur);
  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;
  -webkit-appearance: none;
  appearance: none;
}

[data-hs="2"] :is(.hs-conv-notif-btn, .hs-tasks-filter-button) {
  border-radius: var(--hs-radius-pill);
  box-shadow: var(--hs-glass-lift);
}

/* "Filter" is a short label. Pill radius reads as a circle around the word.
   iOS also paints a native bezel on <button> text, which stacked a second
   outline. One 12px rim, no lift, no OS chrome. */
[data-hs="2"] button.hs-conv-filters-toggle {
  -webkit-appearance: none;
  appearance: none;
  border-radius: var(--hs-radius-md);
  box-shadow: none;
  -webkit-tap-highlight-color: transparent;
}

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

[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);
    height: 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.

   ---- The thumb box paints nothing ----------------------------------------
   The raised thumb has to be BIGGER than the cell it marks, and that is where
   every obvious approach breaks:

     - vtoggle.js writes `left` and `width` inline from the active button's
       offsets. Anything that changes the box width desynchronises it.
     - billing positions its own thumb at `width: calc(50% - 4px)` and slides it
       by `translateX(100%)`. A wider box makes that translate overshoot by
       exactly twice the overhang, so "Monthly" lands short of its cell — the
       same class of bug this file already documents once.
     - `transform: scale()` stretches the pill radius and the shadow with it.

   So the box stays a pure positioning frame — transparent, exact, untouched —
   and `::before` paints the visible pill inset by a negative amount on all four
   sides. Both thumbs keep their arithmetic; neither needs to know about the
   lift. */
[data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb) {
  position: absolute;
  top: var(--hs-seg-inset, 2px);
  bottom: var(--hs-seg-inset, 2px);
  z-index: 0;
  border: 0;
  border-radius: var(--hs-radius-pill);
  /* !important, and it is load-bearing. v1 paints this same element at a higher
     specificity than the bridge can reach without a contrived selector:

       body.hs-adm-page .sgc--raised .sgc__ind  (0,3,1)  background: var(--panel)
       body.hs-adm-page .sgc--solid  .sgc__ind  (0,3,1)  an aqua→purple gradient

     Both are (0,3,1) against this rule's (0,2,0). Without !important every admin
     page would draw the old flush pill AND the new lifted one — the exact
     two-pills-one-marker bug documented at the top of this section. */
  background: transparent !important;
  box-shadow: none !important;
  transition:
    left var(--hs-duration-slow) var(--hs-ease-overshoot),
    width var(--hs-duration-slow) var(--hs-ease-overshoot);
  pointer-events: none;
}

[data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb)::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--hs-seg-lift, 3px));
  border-radius: inherit;
  /* Opaque, not the 0.88 control glass. The lift only reads if the pill is a
     solid object sitting over the track; at 0.88 the track tint shows through
     and the two surfaces merge again. */
  background: var(--hs-seg-thumb, var(--hs-neutral-0));
  box-shadow: var(--hs-seg-thumb-shadow,
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 2px rgba(13, 16, 14, 0.06),
    0 4px 10px -2px rgba(13, 16, 14, 0.10));
  transition:
    inset var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-fast) var(--hs-ease-standard);
}

/* One marker, enforced in CSS as well as in script. A control must never carry
   two thumbs: whichever one has no selection to measure stays 0 wide, and a
   0-wide thumb still paints a `lift × 2` pill parked at the track's left edge.
   vtoggle.js also guards against creating the duplicate, but script order is a
   weaker guarantee than a rule that cannot match twice. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs):has(.v2-pp-tabs-thumb) .sgc__ind {
  display: none;
}

/* Pressed: the pill drops toward the track and its shadow tightens, then
   springs back on release. Elevation you can feel is what makes the lift read
   as a physical object rather than a drawn highlight. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs):active :is(.sgc__ind, .v2-pp-tabs-thumb)::before {
  inset: -1px;
  box-shadow:
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 1px rgba(13, 16, 14, 0.05);
}

/* Dark: a white pill would be the loudest thing on the page. A lifted scrim of
   white with a rim reads as raised against the darker track, and keeps the
   active label — near-white at semibold — legible on top of it. */
[data-hs="2"][data-hs-theme="dark"] :is(.sgc, .v2-pp-tabs, .hs-agent-switch, .hs-rules-switch, .hs-agent-slider__well) {
  --hs-seg-thumb: rgba(255, 255, 255, 0.18);
  --hs-seg-thumb-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.14),
    0 4px 12px -2px rgba(0, 0, 0, 0.45);
}

@media (prefers-color-scheme: dark) {
  [data-hs="2"]:not([data-hs-theme]) :is(.sgc, .v2-pp-tabs, .hs-agent-switch, .hs-rules-switch, .hs-agent-slider__well) {
    --hs-seg-thumb: rgba(255, 255, 255, 0.18);
    --hs-seg-thumb-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.14),
      0 4px 12px -2px rgba(0, 0, 0, 0.45);
  }
}

/* Restated under the heavier :has() scope so a group WITH a thumb cannot end up
   on a different padding than the thumb's own offsets. Same variable, so the two
   can never drift. */
[data-hs="2"] :is(.sgc, .v2-pp-tabs):is(:has(.sgc__ind), :has(.v2-pp-tabs-thumb)) {
  position: relative;
  padding: var(--hs-seg-inset);
}

/* Billing's thumb is the one vtoggle.js does not position, so its offsets are
   written in CSS against the track padding — and they were written when that
   padding was a hardcoded 4px. At a 336px track, `left: 4px; width: calc(50% -
   4px)` put the thumb 1px right of its cell and 1px narrow, which then compounds
   through `translateX(100%)` into a visible gap at the track's right edge.

   Restated against the shared inset, the arithmetic is exact on both tabs and
   stays exact if the inset ever changes. `translateX(100%)` needs no adjustment
   once the width is right. */
[data-hs="2"] .v2-pp-tabs-thumb {
  left: var(--hs-seg-inset);
  width: calc(50% - var(--hs-seg-inset));
}


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

/* Belt and braces: hs-tokens.css already collapses every --hs-duration-* to 1ms
   under reduced motion, and the thumb now transitions on those tokens. This
   stays for the case where the token layer is not loaded, and covers ::before
   too — the pill's press animation is a motion the reduce query means to stop. */
@media (prefers-reduced-motion: reduce) {
  [data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb),
  [data-hs="2"] :is(.sgc__ind, .v2-pp-tabs-thumb)::before {
    transition-duration: 1ms;
  }
}

/* ============================================================
   On/off switch — the segmented control with two cells

   Three switches shipped, none of them in the system:

     .hs-agent-switch   44×26, 13 render sites, raw hex (#d8e0dc track,
                        #c5cfc9 border) — no tokens, so no dark mode
     .hs-rules-switch   42×25, and a DIFFERENT on-colour: forest #1a7f4b
                        where the other says aqua. The same answer, in two
                        greens, two rows apart.
     .hs-switch         the design system's own, written properly on tokens
                        in hs-components.css — which no HTML page loads.

   A switch is a segmented control with two cells, so it is built as one here:
   same well, same raised thumb, same --hs-seg-lift / --hs-seg-inset, same size
   ladder. One control, one file, one dark mode.

   ---- Why the words stay on screen ---------------------------------------
   The knob-only form was the narrower option. This is the labelled one
   (founder, 2026-08-25): the cell the thumb sits on says "On" or "Off" in
   words, so the state is stated twice — by position AND by reading it. On/off
   always uses the smallest step (24px cell, 26px track). A larger switch is
   a settings-row *choice* (`sgc--sm`), not a capability that is on or off.

   ---- Why the markup did not have to be rewritten ------------------------
   Every one of the 13 sites already emitted the same three children, and
   every sync handler in the app already does exactly two things:

       el.classList.toggle('is-on', checked);
       label.textContent = checked ? 'On' : 'Off';

   So `.hs-agent-switch__label` keeps its job and keeps that JS working — it
   just stops being painted, and becomes the accessible state text instead.
   The two visible cells are added to `__track` as markup and chosen by
   `is-on` in CSS. No handler in workspace-app.js, admin-app.js or
   admin-profile.js changed.
   ============================================================ */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) {
  --hs-seg-lift: 2px;
  --hs-seg-inset: 1px;
  --hs-seg-cell: 24px;
  --hs-seg-pad: var(--hs-space-2);
  --hs-seg-size: var(--hs-text-xs);
  display: inline-flex;
  align-items: center;
  gap: 0;
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  user-select: none;
}

/* The well. `__track` was a 44px pill with a knob in it; it is now the track
   of a two-cell segment, and it is the element every one of the 13 sites
   already renders. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__track {
  position: relative;
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  /* Width and height come from the cells and the inset, exactly as the tab
     strip's do. The old fixed 44×26 is gone — a box that states its own size
     cannot follow a size ladder. */
  width: auto;
  height: auto;
  padding: var(--hs-seg-inset);
  border: 0;
  border-radius: var(--hs-radius-pill);
  background: var(--hs-glass-recessed);
  box-sizing: border-box;
  transition: background var(--hs-duration-fast) var(--hs-ease-standard);
}

[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__cell {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--hs-seg-cell);
  padding-inline: var(--hs-seg-pad);
  border-radius: var(--hs-radius-pill);
  font-family: var(--hs-font-ui);
  font-size: var(--hs-seg-size);
  font-weight: var(--hs-weight-medium);
  color: var(--hs-fg-tertiary);
  white-space: nowrap;
  transition: color var(--hs-duration-fast) var(--hs-ease-standard);
}

/* The active cell. Same treatment as an active tab: a colour and weight step,
   and no fill of its own — the thumb underneath is the fill. */
[data-hs="2"] .hs-agent-switch:not(.is-on) .hs-agent-switch__cell--off,
[data-hs="2"] .hs-rules-switch:not(.is-on) .hs-agent-switch__cell--off,
[data-hs="2"] .hs-agent-switch.is-on .hs-agent-switch__cell--on,
[data-hs="2"] .hs-rules-switch.is-on .hs-agent-switch__cell--on {
  color: var(--hs-fg-primary);
  font-weight: var(--hs-weight-semibold);
}

/* The thumb. Positioned in CSS, not by script: a two-cell control is always
   50/50, so there is nothing to measure and no `syncSegInd` to call after a
   re-render. `left`/`width` are stated against the shared inset for the same
   reason the billing thumb is — so the arithmetic stays exact if the inset
   ever moves. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__thumb {
  position: absolute;
  z-index: 0;
  top: var(--hs-seg-inset);
  bottom: var(--hs-seg-inset);
  left: var(--hs-seg-inset);
  width: calc(50% - var(--hs-seg-inset));
  height: auto;
  border-radius: var(--hs-radius-pill);
  /* The box paints nothing — ::before paints the pill, inset by a negative
     amount, so the visible thumb can stand proud of the track without
     breaking the 50% arithmetic above. Same mechanism as .sgc__ind, and for
     the same reason: a wider box would make translateX(100%) overshoot by
     exactly twice the overhang. */
  background: transparent;
  box-shadow: none;
  transform: none;
  transition: transform var(--hs-duration-slow) var(--hs-ease-overshoot);
}

[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__thumb::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--hs-seg-lift));
  border-radius: inherit;
  background: var(--hs-seg-thumb, var(--hs-neutral-0));
  box-shadow: var(--hs-seg-thumb-shadow,
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 2px rgba(13, 16, 14, 0.06),
    0 4px 10px -2px rgba(13, 16, 14, 0.10));
  transition:
    background var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-fast) var(--hs-ease-standard);
}

/* On. The thumb slides to the second cell and takes the accent.

   This is the one place in the product where an accent fill on a segmented
   thumb is right. The tab strip deliberately refuses it — "an accent fill is
   how this product says this is the action to take, and a mode toggle is not
   an action". A switch is not a mode: it is a capability that is either doing
   something or not, and "is Julia answering my phone" has to be readable from
   across the room. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch).is-on .hs-agent-switch__thumb {
  transform: translateX(100%);
}

[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch).is-on .hs-agent-switch__thumb::before {
  background: var(--hs-accent-solid);
  box-shadow:
    0 1px 2px rgba(13, 16, 14, 0.10),
    0 4px 10px -2px rgba(18, 153, 106, 0.35);
}

/* Aqua-400 is a light accent in BOTH themes, so the label on top of it stays
   dark ink in both — which is exactly what --hs-fg-on-accent is for, and it
   resolves to forest-900 on either ground. Taking --hs-fg-primary here would
   put near-white text on a mint pill in dark mode. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch).is-on .hs-agent-switch__cell--on {
  color: var(--hs-fg-on-accent);
}

/* `__label` still carries the live state text every sync handler writes. It
   stops being painted and becomes the accessible name instead — removed from
   the flow, not `display: none`, which would take it out of the accessibility
   tree along with the state. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  min-width: 0;
}

/* `:focus-visible`, not `:focus-within`. The switch is a LABEL wrapping the
   checkbox, so a mouse click moves focus into it and `:focus-within` painted a
   full ring around every switch the moment it was tapped — on rows of them, in
   the admin lists, on every toggle in the product. Keyboard focus still rings;
   a click no longer does. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch):has(.hs-agent-switch__input:focus-visible) .hs-agent-switch__track {
  outline: var(--hs-size-focus-ring) solid var(--hs-border-focus);
  outline-offset: var(--hs-size-focus-offset);
}

[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch).is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Named step for on/off. Same tokens as the base rule: product markup must
   still ask for `--xs`, so a future larger default cannot silently inflate
   every switch. Dense rows used to hide the word (`display: none` on
   `__label`); the word is the control now, so the answer is this size. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch).hs-agent-switch--xs {
  --hs-seg-cell: 24px;
  --hs-seg-pad: var(--hs-space-2);
  --hs-seg-size: var(--hs-text-xs);
  --hs-seg-lift: 2px;
  --hs-seg-inset: 1px;
}

/* Pressed, matching the tab strip: the pill drops toward the track and its
   shadow tightens. */
[data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch):active .hs-agent-switch__thumb::before {
  inset: -1px;
  box-shadow:
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 1px rgba(13, 16, 14, 0.05);
}

@media (prefers-reduced-motion: reduce) {
  [data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__thumb,
  [data-hs="2"] :is(.hs-agent-switch, .hs-rules-switch) .hs-agent-switch__thumb::before {
    transition-duration: 1ms;
  }
}

/* ============================================================
   Fine-tuning slider — thin track, white fill, round knob

   There are two slider SHAPES in this product and they are not sizes of one
   another:

     Value in the thumb    a 52px well with a wide pill carrying "$449/mo".
                           The lander price bar, the billing top-up, and phone
                           setup's ring delay, which moved to it on 2026-09-06
                           — see `.hs-pb-*` in poc5-pages.css. When a screen
                           asks ONE question, the answer belongs on the handle
                           and the handle should be big enough to grab.
                           Untouched by this block.

     Value beside it       this. A setting whose number reads better standing
                           still than moving, sitting next to several others in
                           a card: max call length, call focus, the voice
                           tuning sliders.

   ---- Which sliders have a fill, and which do not ------------------------
   A filled bar is a picture of an AMOUNT. Most of these have one: max call
   length is 12 minutes out of 30, the voice tuning sliders are milliseconds.
   Those take the fill, in `--hs-accent-muted`.

   Call focus does not. Strict / Balanced / Light is a POSITION on a named
   scale, not a quantity — "Light" is not more focus used up than "Strict" —
   so it takes `--stops`, paints no fill, and lets the three stop labels carry
   the state instead. Adding a fill there would claim a quantity about
   something that has none (founder, 2026-08-26).

   ---- Why these are the tab strip's colours ------------------------------
   Every colour here comes from the segmented control: `--hs-glass-recessed`
   for the well, `--hs-seg-thumb` and `--hs-seg-thumb-shadow` for the cap, and
   the same lift/inset pair that makes its pill stand 1px proud. The thumb was
   already shared; the track was not, and the divergence was the bug — the
   groove had been darkened to 12% to compensate for being 6px tall, which is
   the wrong lever. A recess reads from area, not tint (founder, 2026-08-31).

   ---- Why no JS changed --------------------------------------------------
   Every slider in the app already shares one contract, set from script in px:

     --pb-x      the cap's offset from the left of the well's padding box
     --pb-fill   the fill's width  ( = x + tabw / 2 )

   and `travel` is measured from the RENDERED thumb width and the well's own
   `--pb-inset`, not from constants. So changing the cap from a 20px circle to
   a 32px pill inside a padded well is a CSS change and the drag maths follows
   on its own.
   ============================================================ */
[data-hs="2"] .hs-agent-slider__well {
  /* Two numbers, and every fine-tuning slider in the app is one of them. */
  --hs-sld-well: 22px;
  --hs-sld-cap: 32px;
  /* The lift/inset system is BACK ON, and switching it on is the whole change.

     What this replaces: a 6px groove tinted rgba(13,16,14,.12) with a 20px
     circle riding it. That tint was picked to compensate for having no area,
     and it did not work — measured 1.29:1 against a white card, invisible in
     use. The tab strip's well is a LIGHTER 5% tint at 1.11:1 and reads
     perfectly, because 22px of height is what makes a recess read as one.
     Area, not tint. Every number below now comes from the segmented control.

     2 against 1, not the segmented control's 3 against 2. Same 1px proud, and
     the same swap `.sgc--xs` already makes at small sizes, for the reason it
     states there: a 3px overhang on a short control reads as a pill that has
     outgrown its track. */
  --pb-inset: 1px;
  --pb-lift: 2px;
  --pb-tabw: var(--hs-sld-cap);
  position: relative;
  display: block;
  /* The well IS the track now. Nothing is painted inside it. */
  height: var(--hs-sld-well);
  min-height: 0;
  padding: var(--pb-inset);
  border: 0;
  border-radius: var(--hs-radius-pill);
  /* The tab strip's own token, so the two cannot drift apart again. It carries
     its own dark value — rgba(0,0,0,.26) — so there is no second declaration
     for dark anywhere in this section. */
  background: var(--hs-glass-recessed);
  box-shadow: none;
  touch-action: pan-y;
  cursor: grab;
}

/* No groove. The well is the track, exactly as it is on the tab strip, and a
   6px bar painted inside a 22px well would read as two tracks. */
[data-hs="2"] .hs-agent-slider__well::before {
  content: none;
}

/* The fill — mint, and only where there is an amount to show.

   It was white with a hairline rim, which is the shape a fill takes when it has
   to be visible against a groove that is barely there. It does not any more:
   the well is a real recess, so the fill can be the DS's own quantity colour
   and drop the rim entirely.

   A fill is a picture of an amount. Sliders that pick a POSITION on a named
   scale — Call focus is the one today — take `--stops` below and paint none. */
[data-hs="2"] .hs-agent-slider__fill {
  position: absolute;
  top: var(--pb-inset);
  bottom: var(--pb-inset);
  left: var(--pb-inset);
  height: auto;
  margin-top: 0;
  border-radius: var(--hs-radius-pill);
  background: var(--hs-sld-fill, var(--hs-accent-muted));
  box-shadow: none;
  inset-inline-end: auto;
  clip-path: none;
}

/* Stops at the MIDDLE of the cap, so the bar never lags the value. --pb-fill
   is px, written by script: a percentage multiplied by a length is not valid
   calc, and a fill that never resolves is a fill you cannot see. */
[data-hs="2"] .hs-agent-slider__fill {
  width: var(--pb-fill, 0px);
  z-index: 1;
}

/* The knob. `__tab` was a 92px pill holding the number; it is a 20px circle
   now, and the number moved out beside the track. */
[data-hs="2"] .hs-agent-slider__tab {
  position: absolute;
  top: var(--pb-inset);
  bottom: var(--pb-inset);
  left: var(--pb-inset);
  width: var(--pb-tabw);
  height: auto;
  border-radius: var(--hs-radius-pill);
  transform: translateX(var(--pb-x, 0px));
  will-change: transform;
  pointer-events: none;
  z-index: 1;
}

/* Same split the segmented control uses, and for the same reason stated there:
   the box stays an exact positioning frame that the drag maths owns, and
   `::before` paints a pill inset by a negative amount on all four sides. The
   cap ends up 36 × 24 against a 22px well — 1.5:1, still a cap.

   Do not "simplify" this by growing the box. Every consumer measures `travel`
   from the RENDERED width of this element, so a box that includes the overhang
   shortens the scale by 2 × lift at both ends. */
[data-hs="2"] .hs-agent-slider__tab::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--pb-lift));
  border-radius: inherit;
  background: var(--hs-seg-thumb, var(--hs-neutral-0));
  box-shadow: var(--hs-seg-thumb-shadow,
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 2px rgba(13, 16, 14, 0.06),
    0 4px 10px -2px rgba(13, 16, 14, 0.10));
  transition:
    inset var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-fast) var(--hs-ease-standard);
}

/* The value, now beside the track rather than inside the knob. */
[data-hs="2"] .hs-agent-slider__tabin {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 2px;
  /* Left, not right. Right-aligned they all ENDED together, which put the
     first digit of each in a different place — and the first digit is what the
     eye reads down the column. The width comes from the grid track now, so
     nothing here needs a min-width. */
  min-width: 0;
  justify-content: flex-start;
  font-variant-numeric: tabular-nums;
}

[data-hs="2"] .hs-agent-slider__tabin b {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-base);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

[data-hs="2"] .hs-agent-slider__tabin i {
  font-style: normal;
  font-size: var(--hs-text-xs);
  font-weight: var(--hs-weight-medium);
  color: var(--hs-fg-tertiary);
}

/* Two columns: the track, and the value beside it.

   `display: contents` on the row is what makes the END LABELS line up. They
   are a sibling of the row, not a child, so with a plain flex row they spanned
   the whole control while the track had shrunk by the width of the value —
   "Faster" then sat a value-width past the end of the bar it labels. Dissolving
   the row into the grid puts the well and the ends in the same column, and
   they track each other for free at any value width. */
[data-hs="2"] .hs-agent-slider {
  display: grid;
  /* The value column is a FIXED width, not `auto`, and that is what makes the
     numbers line up down a card. With `auto` the column sizes to its own
     content, so a slider showing "0.42" got a narrower column than one showing
     "420 ms" — the boundary moved, and every number started at a different x.
     Pinning the column pins the track length too, so all four bars end
     together as well.

     ONE width for every thin slider, deliberately — not one per variant. A
     word slider ("Balanced") next to a number slider ("300 ms") in the same
     card is the case that would quietly break the alignment again, and giving
     `--words` its own width guarantees that break the first time someone puts
     the two side by side. 6rem holds the longest value any of these carries.

     A card with longer values still widens it once, on the container. */
  grid-template-columns: minmax(0, 1fr) var(--hs-sld-valw, 6rem);
  align-items: center;
  column-gap: var(--hs-space-4);
}

[data-hs="2"] .hs-agent-slider__row {
  display: contents;
}

/* Everything that is not the value belongs over the track. */
[data-hs="2"] .hs-agent-slider > :is(label, .hs-agent-slider__ends),
[data-hs="2"] .hs-agent-slider > .hs-agent-slider__well {
  grid-column: 1;
}

[data-hs="2"] .hs-agent-slider__row > .hs-agent-slider__well {
  grid-column: 1;
  min-width: 0;
}

[data-hs="2"] .hs-agent-slider__row > .hs-agent-slider__tabin {
  grid-column: 2;
}

/* The native input stays on top for keyboard and screen readers, invisible,
   and its own thumb paints nothing — the same split the price bar uses. Two
   thumbs for one drag is what makes a slider lock instead of follow. */
[data-hs="2"] .hs-agent-slider__range {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  margin: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
}

/* The well owns the drag. A live native range snaps to `step` under the
   finger and the painted cap jumps off the cursor. Keyboard still reaches
   the input because pointerdown on the well focuses it. */
[data-hs="2"] .hs-agent-slider__range {
  pointer-events: none;
}


/* The native thumb paints nothing, but its WIDTH is the browser's own mapping
   from finger-x to value. It has to match the cap, or keyboard and native drag
   land on a different value than the pill shows. */
[data-hs="2"] .hs-agent-slider__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: var(--pb-tabw);
  height: 100%;
  border: 0;
  border-radius: var(--hs-radius-pill);
  background: transparent;
  box-shadow: none;
}

[data-hs="2"] .hs-agent-slider__range::-moz-range-thumb {
  width: var(--pb-tabw);
  height: 100%;
  border: 0;
  border-radius: var(--hs-radius-pill);
  background: transparent;
  box-shadow: none;
}

/* Focus and drag, on the knob rather than the input, because the input is the
   invisible one. */
[data-hs="2"] .hs-agent-slider__well.is-focus .hs-agent-slider__tab::before {
  box-shadow:
    0 0 0 var(--hs-size-focus-ring, 2px) var(--hs-border-focus),
    0 4px 10px -2px rgba(13, 16, 14, 0.10);
}

/* Dragging: the cap drops toward the track and its shadow tightens, the same
   gesture `.sgc:active` makes. It used to GROW on drag, which is the opposite
   of what a pressed physical object does. */
[data-hs="2"] .hs-agent-slider__well.is-dragging .hs-agent-slider__tab::before {
  inset: -1px;
  box-shadow:
    0 0 0 0.5px rgba(13, 16, 14, 0.06),
    0 1px 1px rgba(13, 16, 14, 0.05);
}

/* No dark block. Every colour this section paints now comes from a token that
   carries its own dark value — `--hs-glass-recessed` for the well,
   `--hs-accent-muted` for the fill, and `--hs-seg-thumb` / `--hs-seg-thumb-shadow`
   for the cap, which the segmented control's dark rule above already lists this
   well in. The three `--hs-sld-*` overrides that used to live here existed only
   to re-tint a groove that no longer exists.

   ---- Position, not amount -------------------------------------------------
   `--stops` is the slider that answers with a NAME rather than a number: the
   cap carries nothing, the stop labels underneath carry the state, and there is
   no fill because there is no quantity. Call focus is the only one today.

   It keeps the same two-column grid as a value slider so two tracks in one
   card end at the same x. Column 2 stays empty; labels and the description
   stay in column 1. */
[data-hs="2"] .hs-agent-slider--stops > :is(.hs-agent-slider__ends, .sub, p) {
  grid-column: 1;
}

[data-hs="2"] .hs-agent-slider--stops .hs-agent-slider__fill {
  display: none;
}

[data-hs="2"] .hs-agent-slider--stops .hs-agent-slider__ends > span {
  color: var(--hs-fg-tertiary);
  transition: color var(--hs-duration-fast) var(--hs-ease-standard);
}

[data-hs="2"] .hs-agent-slider--stops .hs-agent-slider__ends > .is-current {
  color: var(--hs-fg-primary);
  font-weight: var(--hs-weight-semibold);
}

/* Snap dots sit on the thumb's travel line — first over the left rest,
   last over the right — so a named stop is a mark, not only a label.
   Under the fill and the cap. A mask punches a hole where the cap sits,
   because the white pill is not opaque enough to hide a mark on its own. */
[data-hs="2"] .hs-agent-slider__ticks {
  position: absolute;
  top: var(--pb-inset);
  bottom: var(--pb-inset);
  left: calc(var(--pb-inset) + var(--pb-tabw) / 2);
  right: calc(var(--pb-inset) + var(--pb-tabw) / 2);
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  z-index: 0;
  --hs-sld-tick-hole: calc(var(--pb-tabw) / 2 + 3px);
  -webkit-mask-image: linear-gradient(
    to right,
    #000 0,
    #000 max(0px, calc(var(--pb-x, 0px) - var(--hs-sld-tick-hole))),
    transparent max(0px, calc(var(--pb-x, 0px) - var(--hs-sld-tick-hole))),
    transparent calc(var(--pb-x, 0px) + var(--hs-sld-tick-hole)),
    #000 calc(var(--pb-x, 0px) + var(--hs-sld-tick-hole)),
    #000 100%
  );
  mask-image: linear-gradient(
    to right,
    #000 0,
    #000 max(0px, calc(var(--pb-x, 0px) - var(--hs-sld-tick-hole))),
    transparent max(0px, calc(var(--pb-x, 0px) - var(--hs-sld-tick-hole))),
    transparent calc(var(--pb-x, 0px) + var(--hs-sld-tick-hole)),
    #000 calc(var(--pb-x, 0px) + var(--hs-sld-tick-hole)),
    #000 100%
  );
}

[data-hs="2"] .hs-agent-slider__tick {
  width: 3px;
  height: 3px;
  border-radius: var(--hs-radius-circle, 50%);
  background: var(--hs-fg-tertiary);
  opacity: 0.18;
  flex: none;
}

/* ============================================================
   The value-in-thumb slider, as a modifier

   `.hs-agent-slider` is the CAP shape by default — a 22px well with a 32px
   cap — because most of what it renders is fine tuning. Two things are not:

     the share link's "how was the call" rating   a word rides in the pill
     anything else where the answer IS the thumb

   Those take `--value`, which grows the same well and pill to hold text: a
   taller well, a wider cap, and the value inside the thumb where the markup
   already puts it.

   Since the cap landed, the two shapes differ only in their numbers — the
   well, the tint, the lift/inset pair and the thumb tokens are shared. What
   `--value` still owns is the geometry a word needs, and the fact that the
   value column is off because the value is in the thumb.

   Both are the same two-shapes rule the style guide states: thick when the
   thumb carries the value, thin when it does not. The rating slider was the
   case that proved the modifier was needed — its markup already carried
   `__tabin` INSIDE `__tab`, so when the shape briefly shrank the knob to a
   20px circle the word printed straight across the track (founder,
   2026-08-26).

   Money keeps its own classes (`.pb-*`, `.hs-pb-*`) and is not affected.
   ============================================================ */
/* The pb-* trio is declared ON THE WELL, not on the container, and that is the
   whole reason this works. The cap shape sets `--pb-tabw: var(--hs-sld-cap)`
   on `.hs-agent-slider__well` itself; a custom property declared on the element
   that reads it always beats one inherited from an ancestor, whatever the
   specificity of the ancestor's rule. Setting these on `.hs-agent-slider--value`
   looked right and left the pill 20px wide.

   `--hs-sld-well-h` and `--hs-sld-tabw` are the two a CARD may set, so they are
   read here and declared nowhere in this block — see `.share-note__rate`, whose
   longest stop is "Outstanding" and which needs a taller well and a wider pill.
   A card sets those on the container and they inherit down cleanly, because the
   well does not declare them itself. */
[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__well {
  --pb-inset: 2px;
  --pb-lift: 3px;
  --pb-tabw: var(--hs-sld-tabw, 5.75rem);
  position: relative;
  display: block;
  height: var(--hs-sld-well-h, 2.25rem);
  min-height: 0;
  padding: var(--pb-inset);
  border-radius: var(--hs-radius-pill);
  background: var(--hs-glass-recessed);
  cursor: grab;
  touch-action: pan-y;
}

/* No groove: the well IS the track at this size. */
[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__well::before {
  content: none;
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__fill {
  /* width/height back to auto FIRST, then all four offsets — the thin shape
     sets an explicit 6px height and a --pb-fill width, and either one left
     standing beats the inset and collapses the box to nothing. */
  width: auto;
  height: auto;
  margin-top: 0;
  inset: var(--pb-inset);
  border-radius: var(--hs-radius-pill);
  background: var(--hs-accent-muted);
  box-shadow: none;
  /* Revealed by a clip, not sized by a width — both paint-only, and the fill
     stops at the MIDDLE of the pill so the bar never lags the number. */
  clip-path: inset(0 calc(100% - var(--pb-fill, 0px)) 0 0 round var(--hs-radius-pill));
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__tab {
  top: var(--pb-inset);
  bottom: var(--pb-inset);
  left: var(--pb-inset);
  width: var(--pb-tabw);
  height: auto;
  border-radius: var(--hs-radius-pill);
  transform: translateX(var(--pb-x, 0px));
  will-change: transform;
  display: flex;
  align-items: center;
  justify-content: center;
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__tab::before {
  inset: calc(-1 * var(--pb-lift));
  z-index: 0;
}

/* Back inside the pill, and back to being the thing you read. */
[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__tabin {
  position: relative;
  z-index: 1;
  grid-column: auto;
  min-width: 0;
  justify-content: center;
  gap: 1px;
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__tabin b {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-base);
  font-weight: var(--hs-weight-semibold);
  color: var(--hs-fg-primary);
}

/* The grid the thin shape needs is off here: there is no value column, because
   the value is in the thumb. */
[data-hs="2"] .hs-agent-slider--value {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__row {
  display: contents;
}

[data-hs="2"] .hs-agent-slider--value .hs-agent-slider__well.is-dragging .hs-agent-slider__tab::before {
  inset: calc(-1 * var(--pb-lift) - 1px);
}

/* ---- Scroll strips — the carve-out the lift needs ----------------------
   A tab strip too wide for its column scrolls sideways, and that is the one
   place the raised thumb breaks. Measured on the workspace Notifications
   channel tabs:

     - `overflow-x: auto` makes the box a scroll container on BOTH axes. A
       visible overflow-y computes to auto next to a clipping overflow-x —
       that is the spec, not a browser quirk.
     - The thumb's pill stands 1px proud of the track on all four sides
       (--hs-seg-lift 3 against --hs-seg-inset 2). One pixel of vertical
       overflow is a full scrollbar: 15px of grey furniture standing beside
       the tabs wherever the platform draws classic scrollbars. That is the
       "side artifact" the control was reported for.
     - The same clip erases the thumb's shadow at the top and bottom edges,
       and the shadow is the whole of the raised read. What is left is a flat
       white pill on a 5% track — a selected state you have to look for.

   The fix is room, not a smaller thumb. The scroll box takes block padding
   for the lift and the shadow, never scrolls vertically, and hides its own
   scrollbars — a tab strip states its overflow by the tab clipped at the
   edge, not by a bar.

   Put `hs-seg-scroll` on the WRAPPER, never on the `.sgc` itself: padding on
   the track would grow the well, and the track's own scroll box clips at its
   padding box, 2px inside the pill. A strip that scrolls today by a rule on
   the `.sgc` wants a wrapper instead — see `.hs-cn-channel-nav`.

   The pad is scaffolding, not spacing. It is cancelled back out, so a strip
   sits exactly where it did; state the real gap in --hs-seg-scroll-gap-top /
   --hs-seg-scroll-gap-bottom and keep the margin shorthand off the element. */
[data-hs="2"] .hs-seg-scroll {
  --hs-seg-scroll-pad: 12px;
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: var(--hs-seg-scroll-pad);
  margin-block:
    calc(var(--hs-seg-scroll-gap-top, 0px) - var(--hs-seg-scroll-pad))
    calc(var(--hs-seg-scroll-gap-bottom, 0px) - var(--hs-seg-scroll-pad));
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}

[data-hs="2"] .hs-seg-scroll::-webkit-scrollbar {
  display: none;
}

/* The well scrolls as one object. Clamped to the wrapper it would instead sit
   still while its labels spilled past the tint — the track is an inline-grid
   whose columns cannot shrink below their labels. */
[data-hs="2"] .hs-seg-scroll > :is(.sgc, .v2-pp-tabs) {
  max-width: none;
  overflow: visible;
}

/* ============================================================
   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;
}

/* ============================================================
   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-fg-primary) !important;
  color: var(--hs-fg-primary) !important;
  box-shadow: var(--hs-elevation-raised);
}

/* ============================================================
   Connector Settings — its own lane in the head

   Two earlier homes, both wrong. As a footer under the examples carousel it
   was three tiles of scrolling away on a card you had just opened. Pinned to
   the open card's top-right corner it painted over the account: the rule's
   comment reasoned that the head's action cell is empty there — true, because
   connectorGridRow lifted it out — but the ACCOUNT cell was never moved and
   sits flush at that same right padding. Measured at 1440px the button covered
   86.5 x 12px of the live email text.

   So the manage block goes back into the head's own action lane, and
   connectorGridRow stops stripping it out. Nothing overlaps because nothing
   is out of flow, and Settings is now reachable on a SHUT card — which is what
   an owner with two Gmail accounts is actually looking for.
   ============================================================ */
[data-hs="2"] .hs-cw-card .hs-connector-action .hs-connector-actions {
  display: flex;
  align-items: center;
  gap: var(--hs-space-2);
}

/* The head is a row of centred cells; an open card pins its trailing cells to
   the top (see the [data-open] rule above) so the button stays on the title
   line while the body grows beneath it. */
[data-hs="2"] .hs-cw-card .hs-cw-card__head .hs-connector-action {
  align-self: center;
}
[data-hs="2"] .hs-cw-card[data-open] .hs-cw-card__head .hs-connector-action {
  align-self: flex-start;
}

/* ============================================================
   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);
}

/* ============================================================
   Tasks + Conversations wallet stacks

   Same container and expand behaviour as Connectors. Legacy list/button
   chrome from poc5 is reset when the row also carries .hs-cw-card.
   ============================================================ */
/* Both stacks inherit --hs-cw-tuck and --hs-cw-pad from .hs-connectors-layout,
   which they also carry — one geometry for all three surfaces. */

[data-hs="2"] #ws-integrations-mount .hs-connectors-request {
  margin-top: var(--hs-space-6);
  padding-top: 0;
  border-top: 0;
  display: flex;
  justify-content: flex-start;
}

[data-hs="2"] #ws-integrations-mount .hs-connectors-request__btn {
  width: auto;
  display: inline-flex;
}

[data-hs="2"] .hs-tasks-card.hs-cw-card {
  background: var(--hs-glass-card);
  border-color: var(--hs-glass-card-border);
  border-radius: var(--hs-radius-xl);
  box-shadow: var(--hs-glass-lift-strong);
  opacity: 1;
}

[data-hs="2"] .hs-tasks-card.hs-cw-card.is-completed {
  opacity: 1;
}

[data-hs="2"] .hs-tasks-card.hs-cw-card.is-completed .hs-tasks-card__title-row h2,
[data-hs="2"] .hs-tasks-card.hs-cw-card.is-completed h2 {
  color: var(--hs-fg-tertiary);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* Routines join the wallet. The grid is restated at the id in poc5-pages.css
   (`#ws-panel-scheduled-tasks`) because that id outranks anything here; this rule
   is what gives the card the shell — glass, radius, press, fold — now that its
   own box declarations are gone. */
[data-hs="2"] .hs-st-task-card.hs-cw-card .hs-cw-card__head {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 3px;
  align-items: start;
}

[data-hs="2"] .hs-st-task-card.hs-cw-card .hs-st-task-card__sched,
[data-hs="2"] .hs-st-task-card.hs-cw-card .hs-st-task-card__settings {
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-st-task-card.hs-cw-card .hs-st-task-card__desc {
  color: var(--hs-fg-secondary);
}

[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-cw-card__head {
  align-items: stretch;
}

/* The task card's head is the same two-track grid as the conversation card's —
   checkbox gutter, then one text column. Declared here as well as in
   poc5-pages.css because the base `[data-hs="2"] .hs-cw-card__head` rule is also
   two selectors wide and this file loads later; an app-side `display: grid`
   parses, matches, and is then discarded. Keep the two copies in step. */
[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-cw-card__head {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 3px;
  align-items: start;
}

/* Open card: a third track for Edit and Delete, the same one the conversation
   card opens for Review and Delete. The template has to be restated here for
   the same reason the base grid is — the rule above is four selectors wide and
   an app-side `[data-open]` variant is two, so poc5-pages.css cannot reach it.

   Below 560px the third track goes away and the pair takes its own row instead:
   at that width a content-sized column costs the title the room it needs, and
   the conversation card — which does keep its column here — runs Delete off the
   card edge at 375px. poc5-pages.css owns the actions box itself. */
[data-hs="2"] .hs-tasks-card.hs-cw-card[data-open] .hs-cw-card__head {
  grid-template-columns: 26px minmax(0, 1fr) auto;
}

@media (max-width: 560px) {
  [data-hs="2"] .hs-tasks-card.hs-cw-card[data-open] .hs-cw-card__head {
    grid-template-columns: 26px minmax(0, 1fr);
  }

  [data-hs="2"] .hs-tasks-card.hs-cw-card .hs-tasks-card__actions {
    grid-column: 2;
    grid-row: auto;
    justify-content: flex-start;
    margin-top: 6px;
  }
}

[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-tasks-card__facts {
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-tasks-card__main {
  width: 100%;
  min-width: 0;
  /* The checkbox belongs to the title, not to the block. Centred against
     title + subtext it floated into the gap between the two lines. */
  align-items: flex-start;
}

/* Keep the trailing pills from setting the height of the title line: a source
   tag is 23px against a 20px title, so it grew the row and nudged the title —
   and with it the checkbox — down on exactly the cards that carry one. */
[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-tasks-card__aside .hs-tasks-pill {
  min-height: 1.25rem;
}

/* ---- Swipe to delete ----------------------------------------------------
   Nothing here any more. A task row swipes the way a conversation row does,
   from the same module — see /row-swipe-delete.js and the "Delete a row" block
   in poc5-pages.css.

   What stood here drew the red panel INSIDE the card (`.hs-tasks-card__reveal`)
   and slid `__head` and `__body` over it, because a panel parented to the card
   travels with the card and so the card could not be the thing that moved. The
   cost was the whole illusion: a wallet card clips its own overflow, so the
   title was cut off at the card's left edge rather than carried away with it,
   and the panel sat in the card's padding box, which left the card's 1px border
   and 20px radius drawn around the red as a hairline of glass with square
   corners inside round ones.

   The pane is a sibling of the rows now, sized to exactly the strip the row
   vacates, so the card slides and the red is uncovered behind it. */

/* Vertical inset as margins, not padding — see .hs-cw-card__fold for why. */
[data-hs="2"] .hs-tasks-card.hs-cw-card {
  --hs-cw-fold-lead: var(--hs-space-4);
  --hs-cw-fold-tail: var(--hs-space-5);
}

[data-hs="2"] .hs-tasks-card.hs-cw-card .hs-tasks-detail__body {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

[data-hs="2"] .hs-tasks-toolbar__title .hs-tasks-selection {
  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);
  margin: 0;
}

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

/* v1's 12px/14px row padding is reset here, but NOT as the `padding` shorthand:
   this selector carries an id, so a shorthand outranks the shared tuck rule and
   the bottom padding it adds. Every card in Conversations then lost its tuck and
   the next card sat on its summary line. Reset the three sides that need it and
   leave padding-bottom to the stack. */
[data-hs="2"] #ws-panel-conversations .hs-activity-row.hs-cw-card,
[data-hs="2"] .hs-activity-row.hs-cw-card {
  display: grid;
  width: auto;
  /* v1's 4px column gap survives the switch to grid as a ROW gap between head
     and body, so every conversation card sat 4px lower on the fold than a task
     or connector card at the same padding. */
  gap: 0;
  padding-block-start: 0;
  padding-inline: 0;
  text-align: inherit;
  font: inherit;
  border-radius: var(--hs-radius-xl);
  /* `background-color`, NOT the `background` shorthand. This selector carries an
     id, so the shorthand outranked every class-level rule AND reset
     `background-image` to none along the way — an attention gradient declared on
     `.hs-activity-row--action` parsed fine, matched the card, sat in the last
     stylesheet, and was still discarded. Same class of bug the hover wash hit
     below. The rule only ever wanted a colour. */
  background-color: var(--hs-glass-card);
  border: 1px solid var(--hs-glass-card-border);
  box-shadow: var(--hs-glass-lift-strong);
  /* Conversation rows are content cards, so they take the CARD blur — which the
     app canvas nulls. Reached last of all the content-card sites because this
     rule is id-scoped and its selector list does not contain the word "card". */
  backdrop-filter: var(--hs-glass-blur-card);
  -webkit-backdrop-filter: var(--hs-glass-blur-card);
}

/* ------------------------------------------------------------
   Needs-attention fade

   A warm tint at the card's left edge, falling off before the name begins. It
   replaces `.hs-activity-row--action`'s old inset rail + tint, which never
   rendered at all (see the note in poc5-pages.css).

   Two things this depends on:

   - `background-image`, not a `::before`. An absolutely-positioned pseudo
     element paints ABOVE in-flow text, so a rail needs z-index on the card's
     children to stay legible. A background paints under them by definition.
   - Every stop is the same hue with only alpha moving. `transparent` is
     rgba(0,0,0,0), so interpolating toward it in sRGB drags the midpoint through
     grey and the tint reads dirty.

   The channels are a variable because red-100 at 72% over a forest card goes
   chalky; dark mode needs its own hue, not the same one restated.
   ------------------------------------------------------------ */
[data-hs="2"] .hs-activity-row.hs-cw-card {
  --hs-conv-attn-fade: 253 228 227;
}

[data-hs="2"][data-hs-theme="dark"] .hs-activity-row.hs-cw-card {
  --hs-conv-attn-fade: 92 38 42;
}

@media (prefers-color-scheme: dark) {
  [data-hs="2"]:not([data-hs-theme]) .hs-activity-row.hs-cw-card {
    --hs-conv-attn-fade: 92 38 42;
  }
}

[data-hs="2"] .hs-activity-row--action.hs-cw-card {
  background-image: linear-gradient(
    90deg,
    rgba(var(--hs-conv-attn-fade) / 1) 0px,
    rgba(var(--hs-conv-attn-fade) / 0.72) 18px,
    rgba(var(--hs-conv-attn-fade) / 0.38) 42px,
    rgba(var(--hs-conv-attn-fade) / 0.14) 64px,
    rgba(var(--hs-conv-attn-fade) / 0) 90px
  );
}

/* An open card goes opaque white (see below), and the fade would sit on top of
   that as a stripe down a card the reader is already inside. */
[data-hs="2"] .hs-activity-row--action.hs-cw-card[data-open] {
  background-image: none;
}

/* The fourth side, deliberately weaker: three classes beats v1's single-class
   row rule but loses to the tuck, so a card with another card under it keeps its
   30px and one at the end of a day group sits flush. */
[data-hs="2"] .hs-activity-row.hs-cw-card {
  padding-bottom: 0;
}

/* Conversations had no hover at all, and this is why: the fill above is an
   ID-scoped `background` SHORTHAND, which outranks the shared
   `.hs-cw-card:not([data-open]):hover` wash and so ate it on this page only.
   Tasks and Connectors carry the same wash at class specificity and kept it, so
   the same list of cards lit up on two pages and stayed dead on the third.

   Restated at the id, with the same two declarations as the shared rule — this
   is a specificity repair, not a second hover treatment. Change the wash in one
   place and change it here too. */
[data-hs="2"] #ws-panel-conversations .hs-activity-row.hs-cw-card:not([data-open]):hover,
[data-hs="2"] .hs-activity-row.hs-cw-card:not([data-open]):hover {
  background-color: var(--hs-neutral-50);
  box-shadow: var(--hs-glass-lift-strong);
}

/* Open wallet cards stay opaque.

   Collapsed cards use --hs-glass-card so the stack peeks through. Once expanded,
   that same glass lets the studio photo wash through body content. Bridge also
   remaps --hs-bg-raised → --hs-glass-card, so the raised token is not opaque
   here — use --hs-neutral-0. Declared after Tasks/Conversations glass fills so
   it wins for every .hs-cw-card surface. */
[data-hs="2"] .hs-cw-card[data-open],
[data-hs="2"] .hs-tasks-card.hs-cw-card[data-open],
[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] {
  background: var(--hs-neutral-0, #ffffff);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

[data-hs="2"] .hs-activity-row.hs-cw-card::before {
  display: none;
}

/* The conversation card's head is a two-track grid — icon gutter, then one text
   column, so name / status / preview share ONE left edge.

   This has to be declared HERE and not only in poc5-pages.css. The base rule
   `[data-hs="2"] .hs-cw-card__head { display: flex }` above is also two
   selectors wide, and this file loads after poc5-pages.css, so an app-side
   `.hs-activity-row .hs-cw-card__head { display: grid }` ties on specificity and
   loses on order — the card renders as a flex ROW with the grid template applied
   but inert, which stacks the clock, the tag and the preview on top of each
   other. Keep the two copies in step; poc5-pages.css owns the children. */
[data-hs="2"] .hs-activity-row.hs-cw-card .hs-cw-card__head {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 3px;
  align-items: start;
}

[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] .hs-cw-card__head {
  grid-template-columns: 26px minmax(0, 1fr) auto;
}

@media (max-width: 899px) {
  [data-hs="2"] .hs-activity-row.hs-cw-card .hs-cw-card__head {
    grid-template-columns: 22px minmax(0, 1fr);
    column-gap: 8px;
  }

  [data-hs="2"] .hs-activity-row.hs-cw-card[data-open] .hs-cw-card__head {
    grid-template-columns: 22px minmax(0, 1fr) auto;
  }
}

/* ============================================================
   Admin card rows — Feedback and Requests

   The same head grid as the conversation and task cards. It has to be declared
   here for the same reason theirs are: the base `[data-hs="2"] .hs-cw-card__head
   { display: flex }` rule above is also two selectors wide and this file loads
   after admin-dashboard.css, so an app-side `display: grid` parses, matches, and
   is discarded. admin-dashboard.css owns the children.
   ============================================================ */
[data-hs="2"] .hs-adm-card-row.hs-cw-card .hs-cw-card__head {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 3px;
  align-items: start;
}

[data-hs="2"] .hs-adm-card-row.hs-cw-card[data-open] .hs-cw-card__head {
  grid-template-columns: 26px minmax(0, 1fr) auto;
}

/* The menu is positioned from the head but extends below the card. Wallet
   cards clip their fold animation, so the menu was cut off at the card edge.
   Lift only the active menu card out of that clip, then restore clipping when
   the menu shuts. Level 8 sits above the wallet's focused-card level 7. */
[data-hs="2"] .hs-adm-card-row.hs-cw-card.is-triage-menu-open {
  overflow: visible;
  z-index: 8;
}

@media (max-width: 560px) {
  [data-hs="2"] .hs-adm-card-row.hs-cw-card[data-open] .hs-cw-card__head {
    grid-template-columns: 26px minmax(0, 1fr);
  }

  [data-hs="2"] .hs-adm-card-row.hs-cw-card .hs-adm-card__actions {
    grid-column: 2;
    grid-row: auto;
    justify-content: flex-start;
    margin-top: 6px;
  }
}

/* Phone: the actions take a row of their own, the same as the task card's.
   A content-sized third track costs the name every pixel Review and Delete
   need, and at 375px those two are wide enough to leave the name nothing at
   all — the card opens showing an outcome pill and two buttons, with no
   indication of whose conversation it is. poc5-pages.css moves the box. */
@media (max-width: 560px) {
  [data-hs="2"] .hs-activity-row.hs-cw-card[data-open] .hs-cw-card__head {
    grid-template-columns: 22px minmax(0, 1fr);
  }
}

[data-hs="2"] .hs-activity-row.hs-cw-card .hs-activity-row__sum {
  color: var(--hs-fg-secondary);
}

[data-hs="2"] .hs-activity-row.hs-cw-card .hs-activity-row__time,
[data-hs="2"] .hs-activity-row.hs-cw-card .hs-activity-row__meta {
  color: var(--hs-fg-tertiary);
}

/* Vertical inset as margins, not padding — see .hs-cw-card__fold for why. */
[data-hs="2"] .hs-activity-row.hs-cw-card {
  --hs-cw-fold-lead: var(--hs-space-4);
  --hs-cw-fold-tail: var(--hs-space-5);
}

[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] {
  max-height: var(
    --hs-activity-open-max,
    calc(100dvh - var(--ws-mobile-chrome-h, 96px) - 5.5rem)
  );
  /* THE declaration the cap above depends on, and it is not optional.

     The card is a two-row grid (`auto auto`, see `.hs-cw-card`). An `auto`
     track is content-sized: it is stretched to fill spare room and is NEVER
     shrunk below the height its content asks for. So the cap clipped the CARD
     while the body track kept resolving to the whole transcript — measured on a
     390x844 phone, a 731px card whose body was 1724px. `overflow: hidden` on
     the card then amputated 1132px of conversation, including the controls at
     the foot of it, and nothing scrolled because no descendant was ever handed
     a height it had to fit inside.

     `minmax(0, 1fr)` is the same track for a card at rest — a fraction of an
     indefinite height resolves to the content, so a short conversation still
     sizes to itself and nothing stretches — and a shrinkable one once the cap
     bites. That single change is what lets `min-height: 0` below do its job and
     the transcript's own `overflow-y: auto` finally engage.

     Open state only, and deliberately so: `.hs-cw-card` explains at length why
     the CARD's track list must not be interpolated. This swaps discretely with
     `data-open` and the card transitions no grid property, so there is nothing
     here for Chrome to animate between. */
  grid-template-rows: auto minmax(0, 1fr);
}

[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] > .hs-cw-card__body {
  min-height: 0;
  overflow: hidden;
}

[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] .hs-cw-card__fold {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* The safety net, for a card too short to hold even the summary.

   Normally the transcript is the one scroll region and the recap and player
   stay put above it — that is what `.hs-activity-detail-card.is-expanded` sets
   up in poc5-pages.css. On a landscape phone the cap floors at 280px, and the
   recap (190px) and player (81px) alone overrun it: the transcript flexed to
   ZERO and there was no way to reach it. So the transcript keeps a floor it
   cannot be squeezed under, and the card scrolls to it when that is the only
   way to show it. Above ~470px of viewport the outer never scrolls, so this
   costs a nested scroller only in the one case that has nothing to lose. */
[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] .hs-activity-detail-card {
  flex: 1 1 auto;
  min-height: 0;
  /* `overflow-y: auto` alone made this a scroller on BOTH axes: the x value was
     `visible`, and `visible` beside a scrolling value computes to `auto`. So the
     open card could be dragged sideways on a phone, and something always gave it
     somewhere to go — the rotated chevron on "Conversation details" overhangs its
     own box by 6px, which is enough. A sideways drag on a card is a position no
     control returns you from, and nothing here is meant to be read across.
     `clip` rather than `hidden`, so the y axis keeps its own scroller. */
  overflow-x: clip;
  overflow-y: auto;
  overscroll-behavior: contain;
}

[data-hs="2"]
  .hs-activity-row.hs-cw-card[data-open]
  .hs-activity-detail-card.is-expanded
  > .hs-activity-detail {
  min-height: 12rem;
}

/* Row delete affordances — see the "Delete a row from the conversation view"
   block in poc5-pages.css for the anatomy.

   The trash button sits in the card's VISIBLE strip, whose bottom edge is not
   the card's bottom edge: a card followed by another carries dead space for the
   next one to tuck onto. These selectors mirror the tuck padding rules above one
   for one; change one and change the other. (The swipe pane needs the same
   measurement and takes it from the card head at drag time instead, because it
   is not a child of the card — see row-swipe-delete.js.)

   Task rows read the same numbers. They sit in a plain stack with no day tabs,
   so they take the first two rules and skip the third. */
[data-hs="2"] .hs-activity-row.hs-cw-card,
[data-hs="2"] .hs-tasks-card.hs-cw-card {
  --hs-rowdel-inset-b: var(--hs-cw-pad);
  --hs-rowdel-inset-r: var(--hs-space-5);
}

[data-hs="2"] .hs-activity-row.hs-cw-card:not([data-open]):has(+ .hs-cw-card),
[data-hs="2"] .hs-activity-row.hs-cw-card:not([data-open]):has(+ .hs-activity-daygroup),
[data-hs="2"] .hs-tasks-card.hs-cw-card:not([data-open]):has(+ .hs-cw-card) {
  --hs-rowdel-inset-b: calc(var(--hs-cw-pad) + var(--hs-cw-tuck));
}

[data-hs="2"] .hs-activity-row.hs-cw-card:not([data-open]):has(+ .hs-activity-daygroup) {
  --hs-rowdel-inset-b: calc(
    var(--hs-cw-pad) + var(--hs-cw-tuck) + var(--hs-cw-tab-lead)
  );
}

/* The pane's corners follow the card's radius, on all four now, so the red
   reads as a second object of the same family standing beside the row — not a
   red rectangle behind a rounded card. row-swipe-delete.js overwrites all four
   from the row itself at drag time; this is the value before it does. */
[data-hs="2"] .hs-rowdel-pane {
  /* --hs-radius-xl, the card's own radius. At `lg` the pane's corners cut 4px
     inside the row's, and the pair read as two different shapes. */
  border-radius: var(--hs-radius-xl, 20px);
}

/* ...and the card keeps ITS corners for the whole swipe.

   What stood here squared the row's trailing pair for as long as the pane
   showed, so the two could butt flush and read as one rounded object, glass at
   the leading end and red at the trailing one. Flush is what made it necessary:
   the pane started exactly where the row's border box ended, so a row that kept
   its radius left two wedges of page background at the corners.

   The wedges were the symptom. The joint was the cause. A card is one object,
   and a card that changes shape halfway through a gesture is a change nothing
   in the gesture explains — on a phone the row reads as a slab from the first
   pixel of the drag, which is the whole length of the swipe. The pane stands
   one `--hs-rowdel-gap` clear of the row now (poc5-pages.css), rounded on all
   four corners, so there is a deliberate lane between white and red and no
   joint to hide. Nothing has to give up a corner. */

[data-hs="2"] .hs-activity-detail-card--embedded,
[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail-card__head,
[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail {
  border: 0;
  box-shadow: none;
  background: transparent !important;
  border-radius: 0;
}

[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail-card__head {
  padding: 0 0 var(--hs-space-3);
  border-bottom: 1px solid var(--hs-border-subtle);
}

[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail-card__title {
  font-family: var(--hs-font-ui);
  font-size: var(--hs-text-base);
  font-weight: var(--hs-weight-semibold);
  margin: 0;
}

[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail-card__meta,
[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail__facts {
  color: var(--hs-fg-secondary);
}

[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail-card__toolbar {
  margin-bottom: var(--hs-space-2);
}

[data-hs="2"] .hs-activity-detail-card--embedded .hs-activity-detail {
  padding: var(--hs-space-3) 0 0;
}

/* ---- The day break: a BAND that spans the stack, holding one small tab ----

   Two elements, and the split is not cosmetic. `.hs-activity-daygroup` is a
   transparent band exactly as wide as the cards; `.hs-activity-daygroup__tab`
   is the little dated pill inside it, indented to line up with the card content
   below.

   The band exists so the tab can be PRESSED WITH ITS CARD. A wallet card scales
   to 0.98 under a press, about its own centre, which walks each side edge ~6px
   inward. The tab used to be the shrink-wrapped element itself, sitting 20px in
   from the stack's leading edge and scaling from a centre 20px-plus-half-a-label
   away from the card's — so on every tap the card pulled away from underneath it
   and the tab sat still, two objects instead of one. A band the width of the
   card shares the card's centre line exactly, so the same `scale` on both moves
   them together and the pair reads as one thing being pushed.

   The admin console already drew its day break this way (band + tab). Both
   consoles now use this structure and this file's rules, so a change to the day
   break lands in one place. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup {
  display: flex;
  align-items: flex-start;
  padding-left: 0;
  padding-right: 0;
  background: transparent;
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup .hs-activity-daygroup__tab {
  display: inline-flex;
  align-items: baseline;
  gap: var(--hs-space-2);
  font-family: var(--hs-font-ui);
  /* 11/650/0.06em is one step down from the caps scale and is what keeps the tab
     reading as a label ON the stack rather than a second card in it. Raw values,
     because a token step would move it against the trace tag in the AI timeline
     that it is modelled on. */
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--hs-fg-tertiary);
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup .hs-activity-daygroup__day {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup .hs-activity-daygroup__date {
  font-weight: var(--hs-weight-regular, 400);
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--hs-fg-secondary);
}

/* The day break is a TAB, tucked into the stack — the trace-card shape the AI
   timeline uses for a thinking tag (`.hs-adm-agent-ix__think-tag`), reused here
   to mark a date.

   It was a pill on its own line, which cut the wallet into one stack per day:
   the card above lost the dead space the next card sits on, the card below
   started a fresh stack, and the reader saw as many stacks as there were dates.
   As a tab it sits IN the stack — its own dead bottom is covered by the first
   card of that day, and it in turn covers the dead bottom of the last card of
   the day before. One connected stack, top to bottom, with a date peeking out
   where the day turns over.

   Same trace tokens as the timeline card, so the two surfaces cannot drift.
   ai-timeline.css defines them on :root, and both workspace.html and admin.html
   load it; the fallbacks keep the tab readable on any page that does not. They
   are set on the PILL below, not on the band — the band paints nothing.

   The id-qualified twin is not redundant: poc5 pins the day label's side
   padding through `#ws-panel-conversations .hs-activity-daygroup` under 900px,
   which outranks any class selector and squashed the tab to a 4px inset. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup {
  /* How deep the day's first card sits on the tab. Smaller than --hs-cw-tuck:
     the tab is one short line, and 30px would bury it. */
  --hs-cw-tab-tuck: 14px;
  position: relative;
  /* The band spans the stack — that is what puts its centre line on the card's,
     so the press below moves the two as one. The TAB inside it is what stays
     small. */
  align-self: stretch;
  align-items: flex-start;
  width: auto;
  max-width: none;
  /* The overlap lives on the CARD below (see the sibling rule), never on both:
     flex items do not collapse margins, so a -14px here and a -14px there add up
     to 28px and eat the label. */
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: default;
  user-select: none;
  z-index: 1;
  /* The tab moves when the card above it opens — from sitting in that card's
     dead space to standing one space unit clear of it. Untransitioned, that was
     a ~38px jump on frame one while the fold below eased open over the whole
     wallet duration: the tab arrived before the card it belongs to had started
     moving. Same tokens as the card, so the two travel together — plus `scale`,
     so the press eases the same way the card's does. */
  transition:
    margin-top var(--hs-duration-wallet) var(--hs-ease-spring),
    padding-bottom var(--hs-duration-wallet) var(--hs-ease-spring),
    scale var(--hs-duration-instant) var(--hs-ease-out);
  /* The seam with the card below is the anchor, so the band holds its bottom
     edge while the card holds its top. Declared here and not in the press rule
     below: `transform-origin` does not animate, so setting it alongside
     `:active` would jump the band by a full origin's distance on the first
     frame of the press AND on the first frame of the release. */
  transform-origin: center bottom;
}

/* The pill itself. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup > .hs-activity-daygroup__tab,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup > .hs-activity-daygroup__tab {
  max-width: calc(100% - var(--hs-space-6));
  /* Lines up with the card content under it, not with the stack's edge. */
  margin-inline-start: var(--hs-space-5);
  /* 5/11/6 around the label, plus the tuck the card below covers — so the strip
     that stays visible is a 5/11/6 tab exactly, and the pill's two bottom
     corners are buried under the card rather than showing at the seam. */
  padding: 5px 11px calc(var(--hs-cw-tab-tuck) + 6px);
  border: 1px solid var(--hs-trace-border, var(--hs-border-strong));
  border-bottom: 0;
  border-radius: 12px 12px 4px 4px;
  background: var(--hs-trace-bg, var(--hs-bg-subtle));
  color: var(--hs-trace-ink, var(--hs-fg-secondary));
}

/* The tab is pressed with the card it labels.

   A tab names the day BELOW it, so the card it belongs to is the next sibling —
   the same card the open-state rules further down anchor it to. Pressing any
   other card in the day leaves the tab alone, because no other card is attached
   to it.

   The condition is the card's own press condition, reached across the sibling:
   the card's head is down, and no control inside that head is. It has to be
   restated rather than reused because `:has()` cannot be nested inside `:has()`
   — a relative selector that descends into the sibling is the way through. Keep
   the two in step; they are one gesture.

   No `[data-open]` test, which is the whole point of this pair. The card is
   pressed on the way open AND on the way shut, so the tab travels with it both
   times; gating on the shut state left the tab sitting still while the card it
   is attached to shrank underneath it on the closing tap.

   `bottom` for the vertical origin, `center` for the horizontal one: the band
   is the card's width, so centre-x is the card's centre-x exactly, and holding
   the band's bottom edge still keeps the pill sitting on the card's top edge
   while the card's own top travels its sub-pixel share of the scale. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:has(
    + .hs-cw-card > .hs-cw-card__head:active
  ):not(
    :has(+ .hs-cw-card > .hs-cw-card__head :is(button, a, input, select, label, summary, [role="button"]):not(.hs-cw-card__opener):active)
  ) {
  scale: var(--hs-press-scale-surface);
}

/* And it holds through the fetch with the card — the second half of the rule
   near the top of this file that keeps the card squeezed while the transcript
   loads. The pair is one gesture, so it has to be both or neither: a tab that
   released at touchend while the card held would move the seam by the tab's
   whole 4.5%, which is the exact shimmer the `transform-origin` above exists to
   prevent. */
[data-hs="2"] .hs-activity-wallet
  > .hs-connectors-layout__section.hs-activity-daygroup:has(
    + .hs-cw-card.hs-activity-row--opening
  ) {
  scale: var(--hs-press-scale-surface);
}

/* Sits on the previous day's last card, exactly the way a card would.
   Both these rules carry the id twin as well, for the same reason the block
   above does — and because the block above IS the id-qualified one they have to
   outrank to set a margin at all. */
[data-hs="2"] .hs-activity-wallet > .hs-cw-card + .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-cw-card + .hs-connectors-layout__section.hs-activity-daygroup {
  /* Deep enough that the whole tab — strip and its own dead bottom — sits inside
     the card's dead space, which is why the card above matches this exactly. Pull
     up only one tuck and the tab pushes the next day's card a strip further down,
     which is what opened the seam. */
  margin-top: calc(-1 * (var(--hs-cw-tuck) + var(--hs-cw-tab-lead)));
}

/* An open card owns its own bottom edge — nothing tucks onto it. */
[data-hs="2"] .hs-activity-wallet > .hs-cw-card[data-open] + .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-cw-card[data-open] + .hs-connectors-layout__section.hs-activity-daygroup {
  margin-top: var(--hs-space-2);
}

/* The day's first card closes the tab's dead space. Equal z-index to the tab,
   so DOM order decides — the card paints over it. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup + .hs-cw-card {
  margin-top: calc(-1 * var(--hs-cw-tab-tuck));
  z-index: 1;
}

/* Unless it is open — and then the TAB comes with it.

   A tab labels the day BELOW it, so it belongs to the card underneath, not the
   one above. The seam used to open on both sides of it: the tab stood off the
   card above by a space unit and the open card stood off the tab by another,
   which left the tab stranded in mid-air with nothing attached on either side.
   Anchoring it to the card above instead was the opposite mistake — the card it
   names slid 22px out from under it and the tab stayed behind.

   One gap, above the tab. The tab keeps its tuck with the day's first card, so
   the two move as one object: the tab lifts out of the previous day's dead
   space (which that card gives up, see the padding rule near the top of this
   file) and the open card stays glued underneath it. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:has(+ .hs-cw-card[data-open]),
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:has(+ .hs-cw-card[data-open]) {
  margin-top: var(--hs-space-2);
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup .hs-activity-daygroup__tab .hs-activity-daygroup__date {
  color: inherit;
  opacity: 0.72;
}

/* ---- The conversations wallet holds still while a card opens -------------
 *
 * One tap used to start six animations. The owner's word for it was "they
 * vibrate small, big, small, like there are competing animations trying to
 * resolve at the same time", and that is literally what was happening: the
 * wallet transitions `margin-top` and `padding-bottom` over 280ms alongside
 * `scale` and `box-shadow`, and opening a card changed all of them at once —
 *
 *   the opening card's own margin-top   -30px -> +8px   (it slid DOWN 38px)
 *   the card or tab below it            -30/-61 -> +8   (slid down 38-69px)
 *   the card above it, padding-bottom    61px -> 12px   (pulled UP 49px)
 *   its fold                              0 -> ~790px   (grew down)
 *
 * — so the card's top edge travelled one way while its content grew the other,
 * and its neighbours moved a third. Every one of those is a side effect of an
 * attribute flip, not a gesture the owner made. Only the fold is the gesture.
 *
 * So: in THIS wallet the geometry does not depend on any card's state, and no
 * layout property animates. A card tucks onto whatever is above it, open or
 * shut; the day break is a band of its own with the day's first card tucked
 * onto it; and the only thing that moves on a tap is the fold.
 *
 * The rules above are left where they are — they are right for the wallets that
 * open something already in the markup (tasks, connectors, business info),
 * where there is no fetch and no fold to animate against. This block is the
 * Conversations exception, and it is last in the file on purpose: several of the
 * declarations it replaces are id-qualified or carry `:has()`, so the state
 * variants are named explicitly here rather than out-specified.
 */
/* The id twin is not redundant, for the same reason it is not redundant twenty
   rules up: the band's own transition is declared through
   `#ws-panel-conversations`, and a class selector cannot outrank it. Without the
   twin the tabs kept animating their margin while the cards had stopped — which
   is the two-clock problem again, in miniature. */
[data-hs="2"] .hs-activity-wallet > .hs-cw-card,
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-cw-card,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup {
  transition:
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-base) var(--hs-ease-spring),
    scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* Dead space for the next card to sit on — the same amount whether that card is
   open or shut. */
[data-hs="2"] .hs-activity-wallet > .hs-cw-card,
[data-hs="2"] .hs-activity-wallet > .hs-cw-card:has(+ .hs-cw-card[data-open]) {
  padding-bottom: var(--hs-cw-tuck);
}

/* Nothing sits on the last card, and nothing sits on the card above a day
   break: the tab is its own band now, not a strip inside this card's bottom.
   That 61px of dead space is what put the tab INSIDE the card above it — the
   date pill floating in the middle of a conversation, which is what the wallet
   looked wrong for standing still. */
/* `:not(:has(~ .hs-cw-card))` and not `:last-child`: the swipe-delete pane is
   appended to the wallet as its last child (see row-swipe-delete.js), so the
   moment a finger touched the last row that row stopped being `:last-child`,
   picked up a 30px tuck nothing sits on, and grew taller than the red pane
   beside it — the "delete cell is shorter than the row" bug. Asking whether
   another CARD follows cannot be answered by a pane. */
[data-hs="2"] .hs-activity-wallet > .hs-cw-card:not(:has(~ .hs-cw-card)),
[data-hs="2"] .hs-activity-wallet > .hs-cw-card:has(+ .hs-activity-daygroup),
[data-hs="2"] .hs-activity-wallet > .hs-cw-card:has(
    + .hs-activity-daygroup + .hs-cw-card[data-open]
  ) {
  padding-bottom: 0;
}

[data-hs="2"] .hs-activity-wallet > .hs-cw-card + .hs-cw-card,
[data-hs="2"] .hs-activity-wallet > .hs-cw-card[data-open] + .hs-cw-card,
[data-hs="2"] .hs-activity-wallet > .hs-cw-card + .hs-cw-card[data-open] {
  margin-top: calc(-1 * var(--hs-cw-tuck));
}

/* The day break: a gap, then the band. It labels the day BELOW it, so it keeps
   its own air above and the day's first card stays tucked onto it. */
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] .hs-activity-wallet > .hs-cw-card + .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] .hs-activity-wallet > .hs-cw-card[data-open] + .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:has(+ .hs-cw-card[data-open]),
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-cw-card + .hs-connectors-layout__section.hs-activity-daygroup,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:has(+ .hs-cw-card[data-open]) {
  margin-top: var(--hs-space-4);
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:first-child,
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup:first-child {
  margin-top: 0;
}

[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup + .hs-cw-card,
[data-hs="2"] .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup + .hs-cw-card[data-open],
[data-hs="2"] #ws-panel-conversations .hs-activity-wallet > .hs-connectors-layout__section.hs-activity-daygroup + .hs-cw-card {
  margin-top: calc(-1 * var(--hs-cw-tab-tuck));
}

/* The swipe pane's inset follows the card's real bottom edge, and the card above
   a day break no longer has a strip to clear. */
[data-hs="2"] .hs-activity-wallet > .hs-activity-row.hs-cw-card:has(+ .hs-activity-daygroup),
[data-hs="2"] .hs-activity-wallet > .hs-activity-row.hs-cw-card:not(:has(~ .hs-cw-card)) {
  --hs-rowdel-inset-b: var(--hs-cw-pad);
}

/* The closed body's row reaches a true zero on its own now: every padding the
   expansion reveals lives on `.hs-cw-card__fold`, inside the clip, so there is
   nothing left on the track's own box to floor it. The rule that used to zero
   `padding-block` here for the closed state is gone with it. */

@media (max-width: 899px) {
  [data-hs="2"] #ws-panel-conversations .hs-activity-row.hs-cw-card,
  [data-hs="2"] .hs-activity-row.hs-cw-card {
    /* poc5 pins 14px of side padding here with !important, on top of the head's
       own 20px — 34px of inset on a 375px screen, which is what squeezed the
       title column before the grid above finished the job. One inset, owned by
       the head. */
    padding-inline: 0 !important;
    padding-block-start: 0;
    border-radius: var(--hs-radius-xl);
  }

  [data-hs="2"] #ws-panel-conversations .hs-portal-section__body,
  [data-hs="2"] .hs-conv-page {
    padding-bottom: var(--hs-space-6);
  }
}

/* Card bottoms stay rounded. All four corners, always.

   They used to be squared whenever something was stacked on the card, so the
   wallet read as one continuous stack rather than a row of slabs — invisible
   inside a day, because the next card covers the whole 30px tuck, and only
   visible at a day turnover, where the tab is narrower than the card.

   The cost was paid on every click. A radius cannot be transitioned into place
   the way the margins around it are, so the flip is instantaneous while the
   geometry takes a full wallet duration to follow: close a card and the
   neighbour above it went square on frame one and then spent 280ms sliding back
   over its own hard corners, in an open gap, with nothing covering them. Two
   square corners appearing in a page of rounded ones, on the way OUT of every
   conversation. The day-turnover detail this was fixing is worth less than that.

*/

/* The open card's lift, at the specificity the id-qualified rule above set.
   `.hs-cw-card[data-open]` alone could never win here: the conversations rule
   carries `#ws-panel-conversations`, so every card in this list — open or not —
   kept `--hs-glass-lift-strong`, and a strong lift on a card standing out of a
   tucked stack spread across the three cards below it.

   Both shadows are the design's own tokens. They were briefly rebuilt with a
   negative spread, to stop the wallet's `overflow-x: clip` slicing them flat at
   the sides — which worked, and left the closed stack with no separation at all:
   one blob of cards. The clip margin on `.hs-activity-wallet` is the right lever
   for that (poc5-pages.css), and it is now wide enough for these. */
[data-hs="2"] #ws-panel-conversations .hs-activity-row.hs-cw-card[data-open],
[data-hs="2"] .hs-activity-row.hs-cw-card[data-open] {
  box-shadow: var(--hs-elevation-overlay);
}

/* The conversation overlay was neutralized here while the detail lived only
   in-card. Below 900px it is live again — a conversation is a full-screen sheet
   there, and it has to leave the wallet card's subtree to be one, because
   `.hs-cw-card` above carries a backdrop-filter and so becomes the containing
   block for any fixed-position descendant. The hide is gone rather than scoped
   to wide screens: it would have turned a sheet left open across a resize into
   a conversation the owner cannot see. `poc5-pages.css` styles it at both
   widths instead. */

/* ============================================================
   Agent tool stacks — Phone, Chatbot and Email share one wallet

   The tool rows on the three agent pages were the last lists in the workspace
   still drawn as separate cards: a 12px gap, a hair border, a 16px radius and a
   flat opaque fill. Tasks, Conversations and Connectors all read as one stack of
   glass passes; the pages an owner opens to CONFIGURE an agent read as a
   different product.

   All three are `.hs-ptool` in a `.hs-ptool-list`, and poc5-pages.css gave each
   one its own id-scoped copy of the same slab treatment — which is how the three
   pages drifted apart. This block is the single owner now: one `:is()` scope over
   the three panel ids, at the same id specificity those copies carry, so the
   later sheet wins every declaration it restates. Add a fourth agent page by
   adding its panel id to the scope — never by copying this block.

   The markup does not move. `.hs-ptool` keeps its own `.is-open` class, its
   chevron button and its `0fr → 1fr` body grid — this block restates only the
   surface, the geometry and the states in the wallet's terms, so nothing in
   workspace-app.js has to know the stack exists.

   ---- Why the tuck is not the `+` rule the other three stacks use ----------
   `.hs-ptool-list` holds wrappers at `display: contents` — the core tools and
   the skills mount on Phone, Appearance and Embed on Chatbot. Their BOXES
   vanish; their DOM nodes do not. So `.hs-ptool + .hs-ptool` stops dead at the
   seam between two groups, and the seventh card — the first skill — would sit in
   a 30px hole that no other card in the list has.

   Every card therefore carries the tuck and the pull-up unconditionally, the
   LIST cancels the first card's pull-up with an equal padding-top, and one
   `:has()` finds the true last card to take its tuck back off. Empty wrappers
   are skipped rather than counted — `:not(:has(~ * .hs-ptool))` reads as "no
   later sibling holds a card" — so a workspace with no skills still ends its
   stack cleanly on the last core tool instead of leaving a stray 30px of
   dead glass under it.
   ============================================================ */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list {
  /* Same two numbers as .hs-connectors-layout, and they must stay the same two:
     the whole point is that a tool card and a task card overlap identically. */
  --hs-cw-tuck: 30px;
  --hs-cw-pad: 13px;
  display: flex;
  flex-direction: column;
  /* The overlap is a negative margin, and a gap would fight it. */
  gap: 0;
  /* Cancels the first card's pull-up, so the stack starts where the heading
     says it starts. */
  padding-top: var(--hs-cw-tuck);
  margin-top: var(--hs-space-4);
  /* No extra strip under the stack. The page owns dock clearance; adding a
     second 32px margin here left a dead band the Julia FAB sat in while the
     last card could not (founder, 2026-08-24). */
  margin-bottom: 0;
  overflow: visible;
}

[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool {
  position: relative;
  z-index: 1;
  overflow: hidden;
  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-strong);
  /* Never the `padding` shorthand here — see the Conversations note above for
     what a shorthand at this specificity does to the tuck. The head and the
     fold own their own insets; the card's bottom padding IS the tuck. */
  padding-bottom: var(--hs-cw-tuck);
  margin-top: calc(-1 * var(--hs-cw-tuck));
  /* `scale` is in the list for the reason `.hs-cw-card` states: `transition` is
     one property and does not layer, and this id-scoped rule outranks the press
     transition at the top of this file. Without it the card's press snaps on and
     off instead of easing, which is the one way a tool card could still feel
     unlike a conversation card. */
  transition:
    margin-top var(--hs-duration-wallet) var(--hs-ease-spring),
    padding-bottom var(--hs-duration-wallet) var(--hs-ease-spring),
    background-color var(--hs-duration-fast) var(--hs-ease-standard),
    box-shadow var(--hs-duration-base) var(--hs-ease-spring),
    scale var(--hs-duration-instant) var(--hs-ease-out);
}

/* Nothing sits on the last card, and nothing sits on an open one. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > *:not(:has(~ * .hs-ptool)) > .hs-ptool:last-child,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > .hs-ptool:last-child,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool.is-open {
  padding-bottom: 0;
}

/* ---- The open top end of the stack -------------------------------------
   NEVER `clip-path` A GLASS CARD. There used to be a
   `clip-path: inset(0 -60px -60px -60px)` on the first card of the stack, to
   stop its shadow bleeding upward onto the heading above. It has been removed
   and must not come back in that form.

   A clip on an element that also carries `backdrop-filter` makes Chrome apply
   the filter to the element's BORDER BOX — a rectangle — instead of to its
   rounded shape. The clip rectangle sits 60px out on three sides, so it never
   trims what the radius carved away, and the filtered backdrop stayed visible
   in all four corners as a pale square notch outside the curve. Verified by
   elimination: clip on, filter off → clean; clip off, filter on → clean.

   No inset can fix it. Adding `round` rounds the CLIP rectangle's corners,
   which are 60px away from the card's, and pulling the sides in to 0 so they
   could be rounded would flatten the stack's side shadow against the page.

   Moving the clip to `.hs-ptool-list` is worse, not better: `clip-path` on an
   ancestor makes that ancestor a BACKDROP ROOT, so every card inside it would
   sample the list instead of the page and the whole stack would go flat grey.

   What the clip was buying was never worth this. A `0 12px 34px` shadow
   extends blur/2 = 17px from the box and is offset 12px down, so it reaches
   5px above the top edge, not the 22px the old comment claimed — and those 5px
   are the tail of the falloff at 16% alpha. */

[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool.is-open {
  z-index: 6;
  box-shadow: var(--hs-elevation-popover);
}

/* The card below an open one steps off it. The second selector is the same
   rule across the wrapper seam: last card of one group is open, so the first
   card of the next group is the one that has to move. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool.is-open + .hs-ptool,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > *:has(> .hs-ptool.is-open:last-child) + * > .hs-ptool:first-child {
  margin-top: var(--hs-space-2);
}

/* ---- ...and the card ABOVE it, which is the half that was missing ---------
   An open card stands out of the stack, which means it has to let go of BOTH
   neighbours. Only the one below it ever did. The open card kept its full
   -30px pull-up, so it stayed welded to the card above while the card below
   stepped away — one seam open, one seam shut, on every agent page. Measured
   with transitions disabled, on every stack and every seam: open card
   `margin-top: -30px` where the business-information wallet, which has had the
   matching pair since it was written, gives `8px`.

   Three shapes of "the card above", because `.hs-ptool-list` holds
   `display: contents` wrappers — the core-tools and skills mounts on Phone,
   Appearance and Embed on Chatbot, the locked preview on Email — so the card
   above is a DOM sibling only some of the time:

     1. both cards inside the same wrapper
     2. the card above is a bare list child, the open card starts a wrapper
     3. the card above ends a wrapper, the open card is a bare list child

   `:is(.hs-ptool, :has(.hs-ptool))` is "a list child that carries a card",
   which covers a bare card and a wrapper alike — a card is not its own
   descendant, so `:has()` on its own misses case 3.

   Deliberately no match when nothing above carries a card: Email's first
   locked card follows a note, not a card, and keeps the `margin-top: 0` the
   locked-group rule gives it. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool + .hs-ptool.is-open,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > :is(.hs-ptool, *:has(.hs-ptool)) + .hs-ptool.is-open,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > :is(.hs-ptool, *:has(.hs-ptool)) + * > .hs-ptool.is-open:first-child {
  margin-top: var(--hs-space-2);
}

/* The card above an open one gives up the dead space nothing is sitting in any
   more — the same `--hs-space-3` edge `.hs-cw-card` takes, so a tool card and a
   conversation card open identically. Without it the card above kept a 30px
   tuck under its last line and read as being cut off.

   The four selectors are the three seam shapes above, plus the same-parent
   case. `:has(+ * > .hs-ptool.is-open:first-child)` and not
   `:has(+ * .hs-ptool.is-open)`: only the FIRST card of the next group sits on
   this one, so a deeper card opening must not move it. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool:not(.is-open):has(+ .hs-ptool.is-open),
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > .hs-ptool:not(.is-open):has(+ * > .hs-ptool.is-open:first-child),
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > *:has(+ .hs-ptool.is-open) > .hs-ptool:last-child,
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool-list > *:has(+ * > .hs-ptool.is-open:first-child) > .hs-ptool:last-child {
  padding-bottom: var(--hs-space-3);
}

/* Hover: paint only, never geometry — a transform or a z-index bump here makes
   the stack shuffle. The wash is keyed to the row rather than the card because
   only the row and the chevron are clickable; the tuck is not a target, and a
   card that lights up when the pointer crosses the strip below it is lying
   about what is under the cursor. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__row.is-btn:hover {
  background: transparent;
}

[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool:not(.is-open):has(.hs-ptool__row.is-btn:hover) {
  background-color: var(--hs-neutral-50);
  box-shadow: var(--hs-glass-lift-strong);
}

/* The row is what takes focus, but the card is what the owner sees, so the ring
   goes on the card. It also has to come forward: a ring drawn on a card that
   the next one overlaps is clipped along its bottom edge. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool:has(.hs-ptool__row.is-btn:focus-visible),
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool:has(.hs-ptool__chev--expand:focus-visible) {
  z-index: 7;
  outline: 2px solid var(--hs-border-focus);
  outline-offset: 2px;
}

/* ---- The strip, at wallet scale ----------------------------------------
   11px/14px was a row inset. This is a card, and it takes the stack's own
   padding so the title of a tool lands on the same line as the title of a
   task. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__row {
  gap: var(--hs-space-4);
  padding: var(--hs-cw-pad);
}

/* In the head variant the row shares the strip with a switch and a chevron, so
   it gives back its trailing inset rather than pushing them off the edge. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__head .hs-ptool__row.is-btn {
  padding-right: var(--hs-space-3);
}

[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__head .hs-ptool__chev--expand {
  padding: var(--hs-space-3) var(--hs-space-4);
}

/* 34px read small once the inset doubled — 40px is the avatar-lg step and holds
   the strip without crowding the title. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__tile {
  width: var(--hs-size-avatar-lg);
  height: var(--hs-size-avatar-lg);
  border-radius: var(--hs-radius-md);
}

[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__tile svg {
  width: var(--hs-size-icon-lg);
  height: var(--hs-size-icon-lg);
}

/* The fold takes the same inset as the strip above it, and its divider becomes
   a palette hairline: #f1f2ed was mixed for an opaque white card and all but
   disappears on glass. */
[data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool__body-in {
  padding: var(--hs-space-2) var(--hs-cw-pad) var(--hs-cw-pad);
  border-top: 1px solid var(--hs-border-subtle);
}

/* ---- Mobile ------------------------------------------------------------
   poc5 flattens `.hs-portal-panel .hs-ptool` to an opaque 16px card with
   `!important` under 900px, from when these rows were the odd list out. Tasks
   keeps its glass at every width, so the phone tools have to as well or the
   two stacks diverge on exactly the screen most owners use. Same specificity
   trick the rest of this file uses: the id outranks it, and `!important`
   answers `!important`. */
@media (max-width: 899px) {
  [data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool {
    border: 1px solid var(--hs-glass-card-border) !important;
    border-radius: var(--hs-radius-xl) !important;
    background: var(--hs-glass-card) !important;
    box-shadow: var(--hs-glass-lift-strong) !important;
  }

  [data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool.is-open {
    box-shadow: var(--hs-elevation-popover) !important;
  }

  [data-hs="2"] :is(#ws-panel-agent-phone, #ws-panel-agent-chatbot, #ws-panel-agent-email) .hs-ptool:not(.is-open):has(.hs-ptool__row.is-btn:hover) {
    background-color: var(--hs-neutral-50) !important;
  }
}

/* ---- Chatbot: four groups, one stack -----------------------------------
   Appearance & greeting, Embed on your site, Calendars, and the Scenarios +
   Rules pair are rendered by four separate functions, each returning its own
   wrapper, and the page used to stand them apart as lone slabs.
   `display: contents` is the same device the Phone page uses for its
   core-tools and skills mounts: the wrappers keep their DOM nodes so poc5's
   `.hs-chatbot-appearance .hs-ptool` and `.hs-channel-behavior .hs-ptool`
   descendant rules still resolve, and their boxes leave the layout so every
   card becomes a direct child of the one `.hs-ptool-list` and tucks onto the
   card above it.

   `.hs-channel-behavior--inline` is the only one of the four that holds TWO
   cards. That is why the seam rules above test
   `> *:has(> .hs-ptool.is-open:last-child)` and `> * > .hs-ptool:first-child`
   rather than assuming one card per wrapper — Scenarios opening must not move
   Rules' neighbours, and Rules is the card the end-of-stack test finds. */
[data-hs="2"] :is(
  .hs-chatbot-appearance,
  .hs-chatbot-enable,
  .hs-chatbot-calendars,
  .hs-channel-behavior--inline
) {
  display: contents;
}


/* ---- Email: the pre-connect preview is one group in the stack ----------
   Same device as the Chatbot wrappers and the Phone skills mount: the group
   keeps its DOM node, so `[data-email-locked-preview]` and poc5's
   `.hs-email-locked-preview .hs-ptool` still resolve, and its box leaves the
   layout so the three cards tuck onto each other and onto whatever the list
   puts above them. */
[data-hs="2"] .hs-email-locked-preview {
  display: contents;
}

/* Pre-connect, the list leads with the Connect card and a one-line note, not
   with a tool card. Two consequences, and both have to be answered here:

     - the list's `padding-top` exists only to pay for the FIRST CARD's pull-up.
       With a connect card at the top there is no pull-up to pay for, and the
       30px became a hole under the page heading.
     - the first tool card still carries its pull-up unconditionally, so it rode
       up over the note above it.

   `:not(:has(> .hs-ptool:first-child))` is the test for "this list does not
   start with a card". It is scoped to the Email panel on purpose: on Chatbot the
   list's first child is a `display: contents` wrapper, so the same test would
   read false there and zero a padding that page does need. */
[data-hs="2"] #ws-panel-agent-email .hs-ptool-list:not(:has(> .hs-ptool:first-child)) {
  padding-top: 0;
}

/* The head of the locked group takes the first card's treatment: no pull-up,
   because there is a note above it rather than a card to sit on.

   No clip. It used to carry the same `inset(0 -60px -60px -60px)` the top of
   the stack did, and it is where the square-corner artifact was reported —
   see the "NEVER `clip-path` A GLASS CARD" note above for why it cannot come
   back. What it leaves behind is 5px of shadow tail under the note, which is
   what a card sitting on a page looks like. */
[data-hs="2"] #ws-panel-agent-email .hs-email-locked-preview > .hs-ptool:first-child {
  margin-top: 0;
}

/* The note sat on the list's 12px flex gap for its breathing room, and a wallet
   has no gap — the tuck is the gap. Give it its own. */
[data-hs="2"] #ws-panel-agent-email .hs-email-locked-tools__note {
  margin-block: var(--hs-space-4) var(--hs-space-3);
}

/* ---- Email: the Connect card is the first card of the stack -------------
   `.hs-connect-card` is a shared component — it is the ask at the top of Phone,
   Chatbot and Email alike — and it carries a flat opaque slab: a 14px radius, a
   hairline border and a 1px shadow, from before any of these pages were wallets.
   In the Email stack that put three surfaces in one column: a slab, then a note,
   then three cards. Scoped to a card that actually sits IN a `.hs-ptool-list`,
   so the component keeps its own look everywhere it is not part of a stack. */
[data-hs="2"] .hs-ptool-list > .hs-connect-card {
  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-strong);
}

/* ---- Email: the pre-connect cards are wallet cards ---------------------
   They are the same three tool cards the owner gets the moment an inbox is
   linked, shown early so the ask has something behind it. They used to be drawn
   as a different object — `--hs-glass-recessed` is a 5% INK wash, which over
   this page's gradient reads as a green pane rather than as a card, and poc5
   put `opacity: .72` on top of that. Stacked under a white Connect card, the
   column showed three surfaces and no two alike, which is what made the page
   read as unfinished rather than as locked.

   One surface for every card in the stack. The lock is said by the things that
   mean it: the Locked pill directly above, the greyscale tile and switch that
   poc5 still applies, and every switch reading Off.

   The second selector is the point of the rule. The wallet's hover wash would
   otherwise light these cards up under the pointer and promise a click that
   does nothing. Both selectors carry the same declarations: hovering a locked
   card changes nothing, which is the honest answer. */
[data-hs="2"] #ws-panel-agent-email .hs-email-locked-preview .hs-ptool,
[data-hs="2"] #ws-panel-agent-email .hs-email-locked-preview .hs-ptool:not(.is-open):has(.hs-ptool__row.is-btn:hover) {
  background-color: var(--hs-glass-card);
  box-shadow: var(--hs-glass-lift-strong);
  /* poc5 dims the whole group to .72, which took the card's fill with it and
     dropped the title below the contrast floor. Full strength here; the
     greyscale below is the signal. */
  opacity: 1;
}

/* ============================================================
   Accessibility floor — 2026-08 audit (WP-2)
   ============================================================ */

/* ---- 24px minimum hit areas (UI-005) ------------------------------------
   Measured in the running app: the conversation-filter “×” was a 17px target,
   “Conversation details” 22px, “+ Tag” 23px, the task source-link 24px on the
   nose. WCAG 2.5.8 sets 24px as the floor; these paddings buy it without
   moving any layout — every one of these is an inline-flex island whose
   neighbours are gapped, so growing the box grows into dead space.
   Selector list, not a blanket rule: a blanket `button { min-height }` would
   stretch the wallet-card rows and the icon tiles that are already large. */
[data-hs="2"] :is(.hs-tasks-source-link, .hs-activity-meta__toggle, .hs-conv-clear-all,
  .hs-tasks-pill--addtag, .hs-st-composer__clear, .hs-knowledge-tag__delete,
  .hs-conv-fchip > button, .hs-tasks-card__edit, .hs-tasks-card__delete) {
  min-height: 24px;
  min-width: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ---- Focus border for the stray field families (UI-015) -----------------
   These composers built their own inputs. They use the same stable one-pixel
   focus border as the system field. */
[data-hs="2"] :is(.hs-tasks-composer__title, .hs-tasks-tagadd__input,
  .hs-st-composer__pill input, .hs-st-composer__pill select,
  .hs-td-wcard input):focus {
  outline: none;
  border-color: var(--hs-border-field-focus);
  box-shadow: none;
}

/* ---- Reduced motion (UI-024) --------------------------------------------
   The wallet stack animates margin/padding with a spring on every card, and
   nothing gated it. Near-zero rather than `none`, so transitionend events
   that drive open/close state still fire. */
@media (prefers-reduced-motion: reduce) {
  [data-hs="2"] *,
  [data-hs="2"] *::before,
  [data-hs="2"] *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}


/* ============================================================
   Bottom chrome inset — the admin console

   The studio side has reserved room for its dock since the rule above was
   written; the console never did. Same symptom on both: the last stretch of
   the page sits under floating chrome. On the console at 390px the dock lay
   across the final user card, and on desktop the Julia assistant rests over
   the bottom-right of the content column.

   The console scrolls the page rather than an inner pane — <body> is
   `overflow-y: auto` here, not fixed — so the inset goes on the view, which is
   the box that actually holds the content. Both surfaces now read one token
   (--hs-page-bottom-inset in hs-bridge.css), so the dock height and the
   assistant height are stated once for the whole product.
   ============================================================ */
[data-hs="2"] body.hs-adm-page .hs-adm-view:not(.lab-harness),
[data-hs="2"] body.hs-adm-page .hs-adm-main {
  padding-bottom: var(--hs-page-bottom-inset);
  scroll-padding-bottom: var(--hs-page-bottom-inset);
}

/* Prompt Lab is the one console view that does not scroll the page: the pane,
   the harness and the wallet stack are one clipped flex column that has to
   bottom out on the pane's own edge. So the reserve above applied TWICE, once
   on `.hs-adm-main` and once on the harness, and the stack ended 136px short of
   the glass with empty canvas under the last card (founder, 2026-08-25). The
   harness states its own page inset in admin-dashboard.css; `.bench__wallet`
   carries a 44px scroll tail, which is the assistant's own height. */
[data-hs="2"] body.hs-adm-page.hs-adm-lab-open .hs-adm-main {
  padding-bottom: 0;
  scroll-padding-bottom: 0;
}

/* The embedded client workspace scrolls inside `.hs-portal-main`; the console
   main is only its frame. Reserving the console dock inset here creates a
   68px footer band over the inner scroller and clips the last visible tool. */
[data-hs="2"] body.hs-adm-page.hs-adm-client-open .hs-adm-main {
  padding-bottom: 0;
  scroll-padding-bottom: 0;
}
