/*
 * The live chat frame's faces (`R3-361`): the app's Heebo and Inter, the same
 * files under `/fonts/` that `base.css` declares, for the frame's document
 * only — `GET /chat/v1/frame.css` serves this sheet first; the app never loads
 * it, so the desk keeps its own declarations (`font-display: block`).
 *
 * `swap` here, not `block`: the window is a visitor's first sight of the
 * business, and its words should not wait on a font file; the swap happens
 * inside a small panel, not across a whole page.
 */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/inter-latin-400.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/inter-latin-500.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/inter-latin-600.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/inter-latin-700.woff2') format('woff2');
}

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/heebo-hebrew-400.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/heebo-hebrew-500.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/heebo-hebrew-600.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

@font-face {
  font-family: 'Heebo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/heebo-hebrew-700.woff2') format('woff2');
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

/*
 * ============================================================================
 * HeyBase design tokens — V4.1.
 *
 * The single place a colour, a size, a radius or a duration is decided.
 * Everything below this file consumes a token; nothing re-invents one.
 * Canonical owners:
 *
 * - the visual art direction — `docs/product/HEY32_VISUAL_ART_DIRECTION_V4_1.md`;
 * - primitive-state semantics — `docs/product/UI_CRAFT_STANDARD.md`;
 * - the visual calibration reference — `docs/product/research/FIGMA_VISUAL_SPECIMEN_2026-09-14.md`;
 * - brand values and their intended roles — `docs/product/HEYBASE_BRAND_SYSTEM.md`;
 * - shell geometry and hierarchy — `docs/product/HEY32_PRODUCT_DESIGN_BRIEF.md`;
 * - the rule that these exist as tokens at all — `docs/engineering/DESIGN_SYSTEM.md`.
 *
 * Three rules hold for every value here.
 *
 * **Nothing on the inline axis is physical.** There is no `--gap-left`. Sides
 * are inline-start and inline-end, resolved by the browser from the document's
 * direction, so Hebrew mirrors rather than being patched.
 *
 * **A value that appears twice is a token.** The alternative — a literal
 * `#FE3265` in six components — is how a design system becomes advisory.
 * `tests/unit/design-tokens.test.ts` fails the build if a shell stylesheet
 * writes a raw brand hex or a raw geometry pixel instead of the token.
 *
 * **A token names a job, not a look.** `--surface-floating` says what sits on
 * it; `--panel-radius-lg` said only that it was bigger than the other one. V4
 * renames the second kind, because the names are what keep five depth levels
 * from collapsing back into one rounded rectangle.
 * ========================================================================= */

:root {
  /* ---------------------------------------------------------------------
   * Brand. Supplied by the Product Owner; not derived, not sampled, not
   * substituted (HEYBASE_BRAND_SYSTEM.md §1, §7).
   * ------------------------------------------------------------------ */
  --brand-primary: #fe3265;
  --brand-ink: #252525;
  --brand-tint: #ffdce5;
  --brand-white: #ffffff;
  /*
   * A provider's own mark, used only on the tile that says "this Channel is
   * WhatsApp". Not a HeyBase colour and never used for anything else.
   */
  /*
    WhatsApp's green, exactly as `whatsapp-glyph-green.svg` carries it. Drawn
    in one place: the disc behind the white glyph on the channel card, on the
    Product Owner's decision of 2026-09-26 (`qa-7`), which overrules
    `DEC-2026-09-22-306` for that surface. Nothing else draws with it.
  */
  --provider-whatsapp: #25d366;
  /*
    WhatsApp's own UI accent, used only inside the channel profile preview,
    which imitates WhatsApp's business-profile screen (`qa-9`). Nothing else
    draws with it.
  */
  --whatsapp-accent: #008069;
  /*
    The Instagram disc on the "Add a channel" card: the Product Owner's
    decision of 2026-09-26 extends the WhatsApp ruling to this card (`qa-8`).
    The official 10.9 MB gradient file stays unused; the glyph on it is the
    supplied white file.
  */
  --provider-instagram: linear-gradient(45deg, #f9ce34, #ee2a7b, #6228d7);
  /*
    Messenger's disc under the white glyph on the Inbox row's corner badge
    (HEY-93, the Brief §2.1). Meta's brand resources give the Messenger icon
    no gradient: "The primary Messenger icon appears in Facebook Blue"
    (https://www.meta.com/brand/resources/facebook/messenger-icon/, read
    2026-10-11); that blue is the fill of the supplied
    `public/brand/providers/messenger-icon-blue.svg`. Nothing else draws with it.
  */
  --provider-messenger: #0866ff;
  /* The SMS disc on the same card (`qa-8`). Not anybody's brand colour. */
  --channel-sms: #34c759;
  /*
   * The pressed and hovered states of the brand pink, for the Entry family's
   * primary action (`HEY44…UI_SPEC` §3.8). Darkened rather than tinted: a
   * hovered pink that moves toward white reads as the control becoming
   * *disabled*, which is the opposite of what a hover says.
   *
   * These are supplied values, not sampled ones — they are the brand pink with
   * its lightness stepped down, which is what the brand system permits for
   * interaction states of an approved colour (HEYBASE_BRAND_SYSTEM.md §7).
   */
  --brand-primary-hover: #ec2058;
  --brand-primary-active: #d3134a;

  /* ---------------------------------------------------------------------
   * The high-emphasis call to action.
   *
   * Brand ink, not brand pink. This is a resolved Product Owner decision
   * (`HEYBASE_BRAND_SYSTEM.md` §3), and the reason is arithmetic: white on
   * `#FE3265` measures 3.6:1 and ink on the same pink 4.3:1, so at the
   * approved compact label sizes neither label colour reaches the 4.5:1 the
   * text needs. Recolouring the label does not fix a fill that is simply too
   * light. Ink with white text measures 15.33:1.
   *
   * Pink remains the brand accent everywhere it does not have to carry small
   * text: the focus ring, the active destination marker, selected tints and
   * brand moments. The signal is unchanged; only the fill that had to be
   * readable moved.
   * ------------------------------------------------------------------ */
  --cta-surface: #252525;
  --cta-surface-hover: #3a3a3a;
  --cta-surface-active: #101010;
  --text-on-cta: #ffffff;

  /* ---------------------------------------------------------------------
   * The material model — the Product Owner-approved Figma package
   * (`HEY32_V5_FIGMA_VISUAL_SOURCE_OF_TRUTH.md` §4, §6).
   *
   * V5 is not another calibration. The approved package is the visual source of
   * truth, and where it states a value and HeyBase has no behaviour, RTL or
   * accessibility reason to differ, the value here IS that value.
   *
   *   canvas     #EFF0EB   the ground. Visible as the 6px gutter between every
   *                        region and as the strip the global rail sits on. The
   *                        rail belongs to it and is never a card.
   *   module     #FFFFFF   the main work surface.
   *   module-    #FBFBF9   a quieter module — local navigation, a context pane
   *     subtle              whose density wants to sit back a step.
   *   control              rows and controls inside a module: mostly flat,
   *                        hierarchy from spacing, type and quiet fills.
   *   floating             popover, menu, the nav overlay — clearly stronger.
   *   modal                dialog and side sheet, over a scrim.
   * ------------------------------------------------------------------ */
  --canvas: #eff0eb;

  --surface: #ffffff;
  --surface-subtle: #fbfbf9;

  /*
   * Control and hover fills come from the source's own dense chrome: the search
   * field's `#F3F3F0`, the neutral row hovers `#F5F5F5` and `#F9FAFB`.
   *
   * V4.1 tuned these warm, to sit against a warm text system. The text system
   * is now the source's cooler hierarchy, so a warm fill under cool type reads
   * as two design systems in one row.
   */
  --surface-control: #f3f3f0;
  --surface-hover: #f5f5f5;
  --surface-hover-cool: #f9fafb;
  /*
   * Pressed is a neutral step below hover, and it is deliberately NOT the
   * selected token: "I am pushing this" and "this is where you are" are two
   * different statements, and the moment they share a colour neither reads.
   */
  --surface-pressed: #eceded;

  /*
   * Selected is the source's restrained pink tint, not a neutral fill.
   *
   * `#FFF5F7` marks a selected conversation or an active local row;
   * `rgba(254,50,101,0.10)` marks the selected item in the global navigation,
   * where the surface underneath is the canvas rather than a white module.
   * Pink stays sparse — it says "here", and nothing else in the product uses it
   * to mean focus, success or validation.
   */
  --surface-selected: #fff5f7;
  --surface-selected-nav: rgb(254 50 101 / 10%);

  --surface-floating: #ffffff;
  --surface-modal: #ffffff;
  /*
   * The dialog backdrop: a white veil, blurred by the scrim rule, like the
   * reference (`qa-8`, the Product Owner). The page behind recedes rather than
   * darkening. Entry has no scrim, so nothing there moves.
   */
  --surface-backdrop: rgb(255 255 255 / 72%);
  /*
   * The side sheet's backdrop is the other way round (the Product Owner, after
   * the live panels): the ink's hue at 45%, unblurred, so the page behind stays
   * legible but clearly set back. Dialogs keep the white veil above.
   */
  --surface-backdrop-sheet: rgb(17 24 39 / 45%);

  /*
   * WhatsApp's own chat surface, for the template preview (`qa-15e`, the
   * Product Owner: "exactly like WhatsApp"). The values are WhatsApp Web's
   * light theme — chat background, message ink, secondary and tertiary grey,
   * the hairline between a message's buttons, and the link blue its buttons
   * use — so a template looks here as its recipient will see it. The doodle
   * tint is ours: the pattern it colours is drawn by us, never WhatsApp's
   * wallpaper file.
   */
  --wa-bg: #efeae2;
  --wa-bubble: #ffffff;
  --wa-ink: #111b21;
  --wa-secondary: #8696a0;
  --wa-tertiary: #667781;
  --wa-divider: #e9edef;
  --wa-link: #027eb5;
  --wa-doodle: rgb(17 27 33 / 7%);
  /* WhatsApp Web's message shadow, under the bubble and its buttons. */
  --wa-shadow: 0 1px 0.5px rgb(11 20 26 / 13%);
  --wa-text: 14.2px;
  --wa-text-footer: 13px;
  --wa-text-time: 11px;
  /* A template button's label, as WhatsApp Web sets it. */
  --wa-text-button: 14px;
  --wa-radius: 7.5px;
  /* The bubble's own padding, WhatsApp Web's: 6px 7px 8px 9px. */
  --wa-pad-block-start: 6px;
  --wa-pad-block-end: 8px;
  --wa-pad-inline-start: 9px;
  --wa-pad-inline-end: 7px;
  --surface-disabled: #f3f4f6;

  /*
   * Two hairlines, both from the source: `#E5E7EB` around cards, inputs and
   * major dividers, and `#F3F4F6` for the internal dividers between rows inside
   * a module. Having two is the point — a list of rows separated by the same
   * line that draws the card around them is a list that reads as a table.
   */
  --border: #e5e7eb;
  --border-light: #f3f4f6;
  --border-strong: #d1d5db;

  /* ---------------------------------------------------------------------
   * Text — the source's cool five-step hierarchy (§4).
   *
   * This replaces the warm grey system V4/V4.1 shipped. It is the single
   * change most visible side by side: the approved result is crisp and cool,
   * and a warm `#63635E` secondary against it reads as a different product.
   *
   *   text       #111827   titles, values that matter
   *   secondary  #374151   body, labels — the workhorse
   *   muted      #6B7280   supporting text, placeholders
   *   faint      #9CA3AF   timestamps, meta, muted icons
   *   nav        #1F2937   global navigation labels
   * ------------------------------------------------------------------ */
  --text: #111827;
  --text-secondary: #374151;
  --text-muted: #6b7280;
  /*
   * The label above a field is the app's black, like a title (the Product
   * Owner, `qa-4`). Entry rebinds it to `--text-muted`.
   */
  --text-field-label: var(--text);
  --text-faint: #9ca3af;
  --text-nav: #1f2937;
  --text-disabled: #9ca3af;
  /*
   * White on ink. There is no `--text-on-brand`: nothing puts small text on a
   * pink fill any more, and leaving the token would invite someone to.
   */
  --text-on-ink: #ffffff;

  /* ---------------------------------------------------------------------
   * Semantic state.
   *
   * Independent of the brand on purpose: a destructive action must never be
   * confusable with ordinary HeyBase branding (BRAND_SYSTEM §3). Pink is not
   * the danger colour, and danger is not pink-adjacent.
   * ------------------------------------------------------------------ */
  --success: #15803d;
  --success-surface: #eff8f3;
  --warning: #b45309;
  --warning-surface: #fdf5e8;
  --danger: #dc2626;
  --danger-hover: #b91c1c;
  --danger-surface: #fef5f5;
  --info: #2563eb;
  --info-surface: #edf3fd;

  /* ---------------------------------------------------------------------
   * Select-option palette (`DEC-2026-09-22-364`, research §7).
   *
   * The nine colours a workspace can give a select option, and the only ones.
   * Free-text colour was refused deliberately: a workspace picking its own hex
   * values drifts into unreadable contrast within a week, and it does so
   * differently in Hebrew and English because the two read at different
   * weights. A closed palette is the only version of this that stays legible
   * without anybody auditing it later.
   *
   * Every pair is measured, not assumed. Text on its own chip, WCAG AA needs
   * 4.5:1 — grey 8.51, red 5.76, orange 5.37, yellow 5.73, green 5.58, teal
   * 5.22, blue 5.86, purple 6.22, pink 5.42. Green started at 4.51 and its
   * foreground was darkened to carry a margin rather than sit on the line.
   * A colour added here is not added until its ratio is computed and written
   * down beside it.
   *
   * These are deliberately NOT the semantic tokens above. An option called
   * "Lost" may be red, but it is red because a person chose red, and binding
   * it to `--danger` would make every future change to the danger colour
   * silently rewrite a customer's data.
   * ------------------------------------------------------------------ */
  --option-grey-surface: #f1f2f4;
  --option-grey-text: #3f4650;
  --option-red-surface: #fdecec;
  --option-red-text: #b42318;
  --option-orange-surface: #fdf0e6;
  --option-orange-text: #9a4f10;
  --option-yellow-surface: #fbf4dc;
  --option-yellow-text: #7a5b0a;
  --option-green-surface: #e9f6ee;
  --option-green-text: #12702f;
  --option-teal-surface: #e4f4f5;
  --option-teal-text: #0f6f76;
  --option-blue-surface: #e9f0fd;
  --option-blue-text: #1d4ed8;
  --option-purple-surface: #f1ecfb;
  --option-purple-text: #6b34c4;
  --option-pink-surface: #fdecf2;
  --option-pink-text: #b32a5c;

  /*
   * The Label chip's pairs (`R3-78`, `DEC-2026-09-27-682`): the pill's tint
   * and the ink its name is written in. A Label's colour is one of the same
   * nine (`LABEL_COLORS = OPTION_COLORS`), so these are the option pairs
   * above under the Label's own names rather than a second palette — the
   * ratios quoted there are the ratios here, and
   * `tests/unit/label-chip.test.ts` checks every pair for AA.
   */
  --label-grey-tint: var(--option-grey-surface);
  --label-grey-ink: var(--option-grey-text);
  --label-red-tint: var(--option-red-surface);
  --label-red-ink: var(--option-red-text);
  --label-orange-tint: var(--option-orange-surface);
  --label-orange-ink: var(--option-orange-text);
  --label-yellow-tint: var(--option-yellow-surface);
  --label-yellow-ink: var(--option-yellow-text);
  --label-green-tint: var(--option-green-surface);
  --label-green-ink: var(--option-green-text);
  --label-teal-tint: var(--option-teal-surface);
  --label-teal-ink: var(--option-teal-text);
  --label-blue-tint: var(--option-blue-surface);
  --label-blue-ink: var(--option-blue-text);
  --label-purple-tint: var(--option-purple-surface);
  --label-purple-ink: var(--option-purple-text);
  --label-pink-tint: var(--option-pink-surface);
  --label-pink-ink: var(--option-pink-text);

  /* ---------------------------------------------------------------------
   * Focus (CRAFT §4).
   *
   * **Neutral ink, not brand pink.** V4 used the accent, which conflated four
   * different things: a pink ring said "focused", a pink marker said
   * "selected", a pink check said "saved" and a pink border said "invalid" —
   * four meanings, one colour, and none of them legible as a signal.
   *
   * Focus is now 2px of `#252525` at a 2px offset: crisp, no blur, no glow, and
   * unmistakable against every surface in the palette at better than 12:1. On a
   * dark control the ring would vanish into its own fill, so `--focus-ring-inset`
   * puts a light separation between the control and the ring.
   *
   * Pink keeps exactly one job in the state system: the narrow marker that says
   * where you are.
   * ------------------------------------------------------------------ */
  --focus-ring: var(--brand-ink);
  --focus-ring-inset: var(--brand-white);
  /*
   * Two widths, because a control with an edge and a control without one need
   * different amounts of ring to be equally visible
   * (`HEY32_V5_FINAL_POLISH_ADDENDUM.md` §4).
   *
   * A BORDERLESS control has nothing to strengthen, so the whole indicator is
   * the ring: 2px, held one pixel clear so it does not merge with the glyph
   * inside.
   *
   * A BORDERED control already draws its own perimeter. Darkening that edge to
   * the focus ink and laying a single pixel of outline directly on it gives a
   * 2px perimeter of high-contrast ink — which is exactly what WCAG 2.2 SC
   * 2.4.11 asks for — in half the visual area the old recipe used. The previous
   * 2px outline at zero offset ON TOP of a darkened border drew three pixels of
   * black around a 28px field, and the Product Owner rejected it by name as a
   * thick black box. The accessibility maths did not require it; the difference
   * is entirely how heavy the screen reads.
   */
  --focus-ring-width: 2px;
  --focus-ring-width-edge: 1px;
  --focus-ring-offset: 1px;

  /* ---------------------------------------------------------------------
   * Geometry. Targets from HEY32_PRODUCT_DESIGN_BRIEF.md §2, densities from
   * ART DIRECTION V4 §6.
   * ------------------------------------------------------------------ */
  --rail-width: 48px;
  --secondary-nav-width: 200px;
  --context-panel-width: 290px;
  --side-sheet-width: 38vw;
  --side-sheet-min-width: 360px;
  --side-sheet-max-width: 720px;
  /*
   * The macro gap is the canvas showing through, and the specimen's is 6px.
   * Ours is 6px too: at 8px the modules read as separate cards sitting near
   * each other; at 6px they read as one workbench with seams.
   */
  --macro-gap: 6px;
  --toolbar-height: 44px;
  /*
   * Every header's height (`PO-64`, the Product Owner, 2026-10-09): the open
   * conversation's header — two lines, the contact's name and then the
   * channel with the reply window — is the reference, and every header across
   * the application reserves that height whether or not a second line exists.
   * Its rule, where it has one, is inside it, so the rule sits at this height
   * on every screen and content starts below it, rule or not.
   *
   * 64px is that header measured: the 16px title on its 20px line, 2px, the
   * 13px second line on its ~18px, and ~12px above and below, with the 1px
   * rule. A one-line header lays its title, its actions and its controls in
   * the first `--toolbar-height` of it (the band every header had before) and
   * keeps `--header-reserve` under them for the second line it does not have,
   * so its title sits where the conversation's name sits.
   */
  --header-height: 64px;
  --header-reserve: calc(var(--header-height) - var(--toolbar-height));
  --row-height: 28px;
  /*
   * Three control heights, because the source draws three.
   *
   * `sm` is its list filter — `padding:"3px 8px", fontSize:"12px"` — which is
   * the smallest interactive thing in the approved package and the reason a
   * filter row does not read as a row of buttons. `md` is its search and
   * secondary controls, `lg` its send button. Anything below 24px would fall
   * under WCAG 2.2 SC 2.5.8's minimum target size, which is where this stops.
   */
  --control-height-sm: 24px;
  --control-height: 28px;
  /* 32px: the Product Owner's button height (`qa-3`), which is what `.btn` reads. */
  --control-height-lg: 32px;
  /* The one checkbox (`qa-17`): a quiet 20px box on every list. */
  --checkbox-size: 20px;
  /* The SelectionBar's height (`R3-80`): the reference's 56px pill. */
  --selection-bar-height: 56px;
  /* An on/off setting (`R3-75c`): a 36 × 20 pill. */
  --switch-width: 36px;
  --switch-height: 20px;
  /*
   * Form fields — `.input`, `.select`, `.select-trigger`, the search field —
   * have their own height and surface (`qa-3`), so raising them to 32px moves
   * no chip, row control or icon button that shares `--control-height`. The
   * surface is the one around the field: it rests on the page, held by its
   * hairline, rather than on a grey fill of its own.
   */
  /* The existing 32px step, not a new size in the scale. */
  --field-height: var(--control-height-lg);
  --field-surface: transparent;
  --icon-button-size: 26px;

  /* ---------------------------------------------------------------------
   * Icon sizes — the source's optical posture (§10).
   *
   * The approved package renders ordinary row and navigation glyphs at 13-14px,
   * utility glyphs at 11-12px, header actions at 15-16px and the top waffle at
   * 18px. V4.1 ran 17px everywhere with a 20px rail, which is a size larger
   * across the board and reads as a roomier product than the approved one.
   * ------------------------------------------------------------------ */
  /*
   * The rail's glyph is an ORDINARY navigation glyph, not the waffle.
   *
   * The source draws every destination in its 166px column at its default 14px
   * and reserves 18px for the single waffle at the top. Reading "the rail is
   * the global navigation, so it gets the global size" the other way round put
   * a size-larger glyph on every row — the roomier posture §10 rules out.
   */
  --icon-size-rail: 14px;
  --icon-size-mark: 18px;
  --icon-size-header: 15px;
  --icon-size: 14px;
  --icon-size-sm: 13px;
  --icon-size-xs: 11px;

  /* ---------------------------------------------------------------------
   * Global navigation widths (ART DIRECTION V4.1 §3).
   * ------------------------------------------------------------------ */
  --rail-pinned-width: 166px;
  /*
   * The glyph box inside a rail item.
   *
   * Named here because `.rail-item` centres its icon on the compact rail's
   * 48px column by insetting it half the difference between the item's control
   * size and this box — arithmetic that silently stops landing on the centre
   * line if the two values are set in different files.
   */
  --rail-icon-box: 20px;

  /* ---------------------------------------------------------------------
   * Resizable panes (ART DIRECTION V4.1 §4.1).
   *
   * Defaults are the starting widths; the min and max are enforced
   * continuously during a drag and re-clamped when a stored width meets a
   * smaller viewport. The workspace minimum wins over both.
   * ------------------------------------------------------------------ */
  --secondary-nav-min: 176px;
  --secondary-nav-max: 320px;
  --context-panel-min: 248px;
  --context-panel-max: 420px;
  --workspace-min: 460px;
  /*
   * The Inbox's list column (HEY-37). Wider than the Views navigation beside
   * it because a row carries a name, a preview and its state rather than one
   * label, and narrower than the conversation it feeds, which is where the
   * reading happens.
   */
  --inbox-list-width: 320px;
  /*
   * The connection bar's nominal height (A3), used to clear the list beneath
   * it. A nominal value rather than a measurement because the bar carries one
   * short sentence and a small button; where it wraps, the panes have already
   * stacked.
   */
  --inbox-notice-height: 40px;
  --resize-hit: 9px;

  /*
   * The operational content column. Wide enough for a definition row to
   * breathe, narrow enough that a label and its control stay in one eye
   * movement — an unbounded settings column is how a settings page starts
   * reading as a form builder (ART DIRECTION V4 §7).
   */
  --content-column: 940px;

  /* ---------------------------------------------------------------------
   * The Entry family — sign-in, sign-up, verification, recovery and
   * workspace onboarding (`HEY44_ENTRY_AUTH_DETAILED_UI_SPEC_2026-09-17.md`).
   *
   * A second geometry scale, not a second design system.
   *
   * The authenticated product is dense on purpose: 28px controls, 13px type,
   * rows that fit a working day. Entry is the opposite register — it is seen
   * once, by somebody who has not yet decided to trust the product, often on a
   * phone, and the approved specification sizes it accordingly at 44px
   * controls and 15px fields. Those two scales cannot both be `--control-height`.
   *
   * So the Entry card *rebinds* the shared control tokens locally rather than
   * forking the primitives: `.entry-card` sets `--control-height` to the value
   * below, and `Field`, `TextInput` and `Button` become Entry-sized without a
   * single duplicated component or a parallel `.entry-input` rule. The numbers
   * live here so a guard can assert the approved specification directly.
   * ------------------------------------------------------------------ */
  --entry-card-width: 416px;
  --entry-card-inset: 32px;
  /* §19.3 — 20px at the 360-390px class, rising to 24px when width permits. */
  --entry-card-inset-phone: 20px;
  --entry-card-inset-block-phone: 26px;
  /* §3.5 — rendered width of the full-colour lockup inside the card. */
  --entry-lockup-width: 128px;
  --entry-lockup-width-phone: 116px;
  --entry-control-height: 44px;
  /* §8 — the verification code input is taller than an ordinary field. */
  --entry-code-height: 50px;
  /* §19.1 — the page gutter at a 360px-class width, and where space permits. */
  --entry-gutter: 16px;
  --entry-gutter-wide: 20px;
  /*
   * §3.2 — top-biased, not centred. `clamp` rather than a fixed inset because
   * a rigid centre breaks on a short viewport and a fixed top wastes a tall
   * one; the card stays in normal flow either way so the page can scroll when
   * a virtual keyboard takes half the screen (§19.6).
   */
  --entry-top-bias: clamp(72px, 10vh, 104px);
  --entry-bottom-bias: 48px;
  /* §3.3 — the signed-out language control, outside the card. */
  --entry-language-inset: 24px;
  --entry-language-inset-phone: 8px;
  --entry-touch-target: 44px;
  /*
   * §19.2 — "must not overlap card/logo".
   *
   * Derived rather than picked, because the two numbers it sits between are
   * already tokens and a hand-tuned third one drifts the moment either moves:
   * the control starts at its inset, is a full touch target tall, and the card
   * begins one step below where it ends.
   *
   * On desktop the clearance is free — the control ends at 68px and the card's
   * top bias starts at 72px — which is exactly why the phone case was missed
   * until a phone screenshot showed the control sitting on the card's corner.
   */
  --entry-top-bias-phone: calc(
    var(--entry-language-inset-phone) + var(--entry-touch-target) + var(--space-2)
  );

  /* ---------------------------------------------------------------------
   * Radius, by semantic level rather than by size — at the approved source's
   * values (`HEY32_V5_FIGMA_VISUAL_SOURCE_OF_TRUTH.md` §8).
   *
   * Every object at the same radius is the tell of a design system applied
   * uniformly instead of deliberately. But the *family* matters as much as the
   * variety: the approved result is a tight 4-10px family, and V4.1's 6/9/12/14
   * family plus a universal pill for every action is visibly rounder than it.
   *
   *   row       4px   a dense list row inside a module
   *   control   6px   a button, filter, input, select or search field
   *   floating  8px   a menu, a popover, a composer
   *   region   10px   a permanent operational module
   *   modal    12px   a dialog or side sheet
   * ------------------------------------------------------------------ */
  --radius-row: 4px;
  /* The source's own filter radius; `--radius-control` is its button radius. */
  --radius-control-sm: 5px;
  /* A highlight behind a run of text: a template variable (`R3-70`). */
  --radius-highlight: 4px;
  /* The one checkbox's corner (`qa-17`): the filter radius, so a box and a chip agree. */
  --radius-checkbox: var(--radius-control-sm);
  --radius-control: 6px;
  --radius-floating: 8px;
  --radius-region: 10px;
  --radius-modal: 12px;
  /* A choice card inside a dialog, such as the channel chooser's (`qa-8`). */
  --radius-card: 12px;
  /*
   * Pill is a **semantic** shape now, not the default action shape.
   *
   * V4.1 made every authenticated action a pill. The approved source is not a
   * pill-everywhere interface: its buttons and filters are 5-6px rectangles,
   * and the only full-radius shapes are badges, chips, avatars and the one
   * marketing banner CTA. `HEY32_V5_FIGMA_VISUAL_SOURCE_OF_TRUTH.md` §8
   * supersedes that rule, so this token now means "a chip or a badge", and an
   * ordinary button reaches for `--radius-control` like everything else.
   */
  --radius-pill: 999px;
  /*
   * Every workspace button is fully rounded (`qa-3`, the Product Owner's
   * instruction, superseding the 6px-rectangle rule above for buttons).
   * Entry keeps `--radius-control` — `.entry-card` rebinds this token.
   */
  --radius-button: var(--radius-pill);

  /* ---------------------------------------------------------------------
   * Insets — how far content sits from the edge of the thing it lives in.
   *
   * Spacing steps say how far apart two things are. These say something
   * different and more opinionated: where the product's edges are. The Product
   * Owner rejected the V5 result specifically on this
   * (`HEY32_V5_FINAL_POLISH_ADDENDUM.md` §3) — titles were sitting close enough
   * to their module edges that the modules read as cramped rather than calm,
   * and the same decision was spelled out separately in five places as `14px`,
   * `16px`, `12px` and `24px`, so it could not be changed once.
   *
   * Now there are two, and they mean different things:
   *
   *   module   18px   a module's own content edge — its header, its body, its
   *                   sections. This is the line every title in the product
   *                   starts on.
   *   row       8px   how far a DENSE ROW's surface is inset from that edge, so
   *                   a selected tint floats inside the module instead of
   *                   running into its corner.
   *
   * A dense row's own padding is then the difference between them, which is why
   * it is `calc` and not a third number: a row's label lands on the module's
   * edge line while its surface stays inset, and moving the module inset moves
   * both together. That is the source's own grammar — its navigation rows sit
   * in an 8px gutter with 11px of their own padding — expressed once.
   *
   * Dense rows deliberately do NOT inherit the module inset. `18px` of padding
   * on every row of an operational list is how a workbench turns into a
   * settings page, which §3 rules out by name.
   * ------------------------------------------------------------------ */
  --module-inset-inline: 18px;
  --module-content-inset-block: 16px;
  --row-inset-inline: 8px;
  --row-inset-block: 4px;
  --row-padding-inline: calc(var(--module-inset-inline) - var(--row-inset-inline));

  /* ---------------------------------------------------------------------
   * Spacing — the approved 4/8/12/16/24/32 rhythm, applied more tightly in V4
   * (§6). Shell-level components use these and not arbitrary one-off values.
   * ------------------------------------------------------------------ */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;

  /* ---------------------------------------------------------------------
   * Typography (ART DIRECTION V4 §5). The family is chosen in `base.css` and
   * explained there.
   *
   * 13px is the desktop baseline, not 14px. The difference sounds trivial and
   * is not: at 14px an operational row cannot hold a label, a value and a
   * control without either wrapping or growing, and the page starts to look
   * like a settings form rather than a workbench.
   * ------------------------------------------------------------------ */
  /*
   * The locale stacks (`HEY32_V5_FINAL_POLISH_ADDENDUM.md` §1).
   *
   * Each language leads with the family drawn for its own script and keeps the
   * other as the fallback for the other script. Correctness does not actually
   * depend on the order — the Hebrew faces declare a `unicode-range` limited to
   * the Hebrew block, so each character is answered by its own family whichever
   * stack is active — but the order states the intent, and it decides which
   * family answers for anything unusual that both happen to cover.
   *
   * `--font-ui` is the one families are read from. Nothing else in the
   * repository may declare a family: the guard in
   * `tests/unit/shell-structure.test.ts` fails on a `font-family` outside this
   * file and the `@font-face` block.
   */
  --font-inter: Inter;
  --font-heebo: Heebo;
  --font-system: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-ui: var(--font-inter), var(--font-heebo), var(--font-system);
  --font-mono: ui-monospace, 'SFMono-Regular', 'Cascadia Mono', Menlo, monospace;

  /*
   * Module and pane titles.
   *
   * V4 set both of these at 18px, and the browser evidence read as an admin
   * page: an 18px/700 heading over 13px body is a jump of five points and two
   * weights, which is a document's hierarchy rather than a workbench's. The
   * approved Figma specimen runs 13px body against ~14px module headers
   * (`FIGMA_VISUAL_SPECIMEN_2026-09-14.md` §2, and the calibration addendum §3
   * makes it binding), and the hierarchy there comes from weight and colour
   * rather than from size. This is the recalibration, not a shrink for its own
   * sake: 14px names a module, 15px names the pane that contains it.
   */
  /* ---------------------------------------------------------------------
   * The scale, read off the approved source
   * (`HEY32_V5_FIGMA_VISUAL_SOURCE_OF_TRUTH.md` §7).
   *
   * The source runs 13px body against 14px module titles and puts almost
   * everything else *below* the body: 12.5px for a list's channel label, 12px
   * for details labels and previews, 11.5px for timestamps. That downward
   * spread is most of what makes it read as an operational product rather than
   * as a settings screen, and it is the part a size-up-everything scale loses.
   * ------------------------------------------------------------------ */
  /*
   * A pane's own heading, where that pane names a whole screen.
   *
   * Added for the Inbox menu (`INBOX-SPEC-20260923-01` §3.1 item 1). The
   * Product Owner asked for a **bigger** title there — *"צריך להיות כותרת
   * גדולה יותר"* — and the scale had nothing between the 14px module
   * heading and the 23px entry title, which belongs to the signed-out screens
   * and would shout here. 17px is the missing step: clearly a heading beside
   * 13px rows, and still a working surface rather than a landing page.
   */
  --text-pane-title: 17px;
  --text-module: 14px;
  /* 16px: the Product Owner's page-title size (`qa-3`). */
  --text-workspace-title: 16px;
  /*
   * Sign-in and the Organization chooser are the brand moment rather than
   * operational chrome, so their heading keeps a larger step. Applying a
   * workbench density rule to the one screen it was not written for is how a
   * first impression gets flattened.
   */
  /*
   * The Entry family's own type scale (§3.6, §3.7, §8).
   *
   * Larger than the product's, for the reason the geometry is: this is read
   * once, cold, often on a phone. `--text-entry-title` predates the Entry
   * specification at 20px and the specification asks for 22-24px, so it moves
   * to the middle of that band rather than a second title token appearing
   * beside it.
   */
  --text-entry-title: 23px;
  --text-entry-support: 14px;
  --text-entry-field: 15px;
  --text-entry-label: 13px;
  --text-entry-code: 21px;
  /*
   * §19.5 — iOS zooms a page when a focused input's text is under 16px, which
   * on a form is indistinguishable from the layout breaking. This is the floor
   * on phone, not a preference.
   */
  --text-entry-field-phone: 16px;
  --text-section: 13px;
  --text-body: 13px;
  --text-list: 12.5px;
  --text-meta: 12px;
  --text-micro: 11.5px;
  --text-badge: 11px;

  /*
   * Two leadings, as the source uses them: 1.65 where prose is genuinely being
   * read, and 1.4 for compact chrome. Its titles sit tight at 1.25.
   */
  --leading-title: 1.25;
  --leading-prose: 1.65;
  --leading-body: 1.4;
  --leading-meta: 1.35;

  /*
   * Four real faces per script, which is the point of the V5 final-polish
   * change (`HEY32_V5_FINAL_POLISH_ADDENDUM.md` §1, `DEC-2026-09-14-088`).
   *
   * The approved Figma source uses 500 and 600 constantly — a list-row title at
   * 500, a module header at 600, a selected navigation row at 600, a filter and
   * a send button at 500 — and reserves 700 for one marketing call to action.
   * Ping HL had no faces between Regular and Bold, so every one of those
   * resolved to 400 or 700 and the product lost a whole level of hierarchy: it
   * could say "ordinary" and "loud" and nothing in between. Inter and Heebo
   * both supply the intermediate faces, so the ladder below is the source's own
   * rather than the nearest thing a two-weight family could reach.
   *
   * Where each one is used:
   *
   *   regular   400   body, prose, an unselected row, a field's value
   *   medium    500   a list-row title, a button label, a filter, a badge
   *   semibold  600   a module, pane or section title, a selected nav row
   *   bold      700   the entry-surface brand moment, and nothing operational
   *
   * `font-synthesis: none` in `base.css` makes this contract enforceable rather
   * than aspirational, and `tests/unit/design-tokens.test.ts` fails the build if
   * a stylesheet asks for a weight no shipped face provides.
   */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  /*
   * The label of a workspace button: bold (the Product Owner, `qa-4` — 900 was
   * too heavy). Entry rebinds it to `--weight-medium`.
   */
  --weight-button: var(--weight-bold);

  /* ---------------------------------------------------------------------
   * Elevation (ART DIRECTION V4.1 §2.3).
   *
   * **Permanent modules float.** This supersedes V4's rule that levels 0-2
   * carry no shadow, and it is the single change that turns a white page on a
   * grey background into modules resting on a canvas. The lift is deliberately
   * tiny — two very faint layers — because a module should feel native to the
   * canvas, not like a marketing card dropped onto it.
   *
   * Every shadow here has a zero horizontal offset, because a non-zero one is
   * physical geometry that would not mirror — the defect this stylesheet
   * shipped once and now guards against.
   * ------------------------------------------------------------------ */
  /*
   * The approved source's exact two-layer recipe
   * (`HEY32_V5_FIGMA_VISUAL_SOURCE_OF_TRUTH.md` §6).
   *
   * The first layer is the lift. The second is a half-pixel outline that
   * sharpens the edge — it is what makes a module read as a crisp card rather
   * than as a soft shape fading into the canvas, and it is why the source needs
   * no hairline border on its panels at all.
   *
   * V4.1 arrived at a softer recipe of its own. The source states this one, and
   * "our previous token was different" is not a reason to keep a difference.
   */
  --shadow-module: 0 1px 4px rgb(0 0 0 / 13%), 0 0 0 0.5px rgb(0 0 0 / 6%);
  /*
   * One row lifted off the column it sits in — the selected view in both of
   * the Product Owner's references, which draw it as a **raised white card**
   * rather than as a tinted fill.
   *
   * A lighter sibling of `--shadow-module`, not the same value: a module is a
   * region of the page and a row is one line inside one, so the row's lift has
   * to read as *above the column* without competing with the column's own
   * elevation.
   */
  --shadow-row-raised: 0 1px 3px rgb(0 0 0 / 10%), 0 0 0 0.5px rgb(0 0 0 / 5%);
  --shadow-floating: 0 8px 30px rgb(37 37 37 / 10%), 0 2px 8px rgb(37 37 37 / 6%);
  --shadow-modal: 0 20px 60px rgb(37 37 37 / 18%);

  /* ---------------------------------------------------------------------
   * Motion (ART DIRECTION V4 §10).
   *
   * Four durations, one curve. Motion here is feedback — it says a thing
   * opened, closed, moved or accepted a press — so every value is short enough
   * that a person who is working rather than watching never waits for it.
   *
   * The curve leaves quickly and arrives slowly, which is what makes a panel
   * feel placed rather than played.
   * ------------------------------------------------------------------ */
  --motion-instant: 80ms;
  --motion-fast: 120ms;
  --motion-standard: 180ms;
  --motion-panel: 220ms;
  /*
   * The side sheet's entrance and exit, slower than the context panel's
   * `--motion-panel` (the Product Owner found 220ms abrupt for a surface that
   * covers most of the page). Separate so the shell's panel keeps its timing.
   */
  --motion-sheet: 360ms;
  /*
   * How long a label waits before fading in behind a surface that is still
   * opening (`DEC-2026-09-15-109` §G2).
   *
   * A token rather than a literal in `shell.css` for the ordinary reason — the
   * stylesheet should not hold timing values — and for one specific to this
   * one: under `prefers-reduced-motion` a delay is exactly as unwelcome as a
   * duration, and only a token gets zeroed with the rest of the system below.
   * It is deliberately shorter than `--motion-panel`, so the reveal overlaps
   * the movement rather than queueing behind it.
   */
  --motion-reveal-delay: 90ms;
  --ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);

  /*
   * The distance a floating surface travels on entry. Block-axis only: a
   * translation on the inline axis would have to know which way Hebrew runs.
   */
  --motion-rise: 4px;

  /*
   * The one duration that is not feedback: the skeleton's breathing loop. It is
   * a token so the guard can tell "a loop nobody is waiting on" apart from "a
   * transition somebody hand-timed", and it is disabled outright under reduced
   * motion by the skeleton's own rule rather than being shortened to nothing.
   */
  --motion-pulse: 1.4s;

  /* One rotation of a pending spinner. Also a loop, also not feedback timing. */
  --motion-spin: 700ms;
}

/*
 * The workspace's type scale, one step above the base (`R3-152`).
 *
 * The values on `:root` above are the base the Entry screens read — sign-in,
 * the Organization chooser, invitations and the public pages — and they stay
 * there, so Entry's computed styles do not move (PO-19). Only a document that
 * contains the workspace shell (`.app`, its root in `app-shell.tsx`) takes the
 * larger steps.
 *
 * On `:root:has(.app)` rather than on `.app` itself: dialogs, menus, popovers
 * and toasts are portalled to `body`, outside the shell's subtree, and they
 * are part of the workspace too. Scoping on the document keeps them on the
 * same scale as the screen that opened them.
 *
 * The titles (`--text-pane-title`, `--text-workspace-title`) and the message
 * size (`--text-message`, on `.inbox-conversation`) do not move: they already
 * sit above the body, and a message reads at WhatsApp's own size.
 */
:root:has(.app) {
  --text-module: 15px;
  --text-section: 14px;
  --text-body: 14px;
  --text-list: 13.5px;
  --text-meta: 13px;
  --text-micro: 12px;
  --text-badge: 11.5px;
}

/*
 * Reduced motion is a preference, not a nicety: an overlay that slides is an
 * overlay that some people cannot use comfortably. Durations collapse to zero
 * rather than being re-tuned per component, so every transition in the product
 * becomes an instant state change and nothing else about it changes.
 */
/*
 * Surface-scoped tokens (`R3-103` d): WhatsApp's colours, the Inbox
 * conversation card only. They live in the token layer, like every colour,
 * but on the card's root rather than on `:root`: a custom property on `:root`
 * is inherited by every element of every page, and nothing outside the
 * conversation is these colours (PO-19 keeps the Entry dump byte-identical).
 *
 * `--bubble-out` is WhatsApp's outgoing green, so a reply reads here as it
 * reads on the customer's phone. `--wa-read` is its read-receipt blue, the one
 * delivery state with a colour; on the green the brand's pink read as an error.
 *
 * `--text-message` is what a message reads at, in the thread and in the
 * composer (`R3-140`): a pixel above the product's 13px body, because the
 * words of a conversation are what this card is for — and what is typed reads
 * the same as what is read.
 */
.inbox-conversation {
  --bubble-out: #d9fdd3;
  --wa-read: #53bdeb;
  --text-message: 14px;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-instant: 0ms;
    --motion-fast: 0ms;
    --motion-standard: 0ms;
    --motion-panel: 0ms;
    --motion-sheet: 0ms;
    --motion-reveal-delay: 0ms;
    --motion-rise: 0px;
  }
}

/*
 * The letters a search matched (`R3-157`): a light marker yellow, as Heyy
 * marks them, and not the brand tint — which stays the template variable's.
 * `--text` on it is 15.7:1 and `--text-secondary` 9.1:1 (AA and AAA).
 *
 * Surface-scoped, like `--bubble-out`: it is drawn only inside the search
 * dialogs and, as the wash on a message a search opened (`R3-158`), in the
 * conversation card — so it lives on those two rather than `:root`, and no
 * surface that does not draw it — the Entry screens (PO-19) above all —
 * inherits it.
 */
.dialog,
.inbox-conversation {
  --highlight-search: #fff3a3;
}

/*
 * How long a message a search opened keeps its wash (`R3-158`): long enough
 * to find with the eye after the jump. Scoped with the colour it fades. No
 * fade under reduced motion; the mark stays its two seconds, flat
 * (`primitives.css`), because reduced motion is about movement, not colour.
 */
.inbox-conversation {
  --motion-mark-fade: 2s;
}

/* ===========================================================================
 * The phone's size step (`MOBILE_PRODUCT_DESIGN_BRIEF.md` §11, `R3-315`;
 * `DESIGN_SYSTEM.md` "The phone shell"; `DEC-2026-10-18-917`).
 *
 * The one phone block of this stylesheet. Everything in it applies only under
 * the phone boundary (`src/app/_lib/phone-shell.ts`: at most 767 px), so no
 * desk value moves (`PO-101` item 7; `tests/e2e/desktop-isolation.spec.ts`
 * proves it). The colours, the families, the glyphs and the components stay
 * the desk's (`PO-104`): the phone adds sizes, nothing else.
 *
 * Two kinds of token:
 *
 * - **The step itself, `--phone-*`**, on `:root`: every row of §11, named
 *   once, for the phone's own primitives (the bar, the header, the rows, the
 *   sheets) that the later stages of the pass build.
 * - **Where the desk already has a token for the same thing**, that token
 *   takes the phone's value here, so a desk component reading it is sized for
 *   the phone with no edit of its own. Rebound on `:root:has(.app)` — the
 *   workspace, portalled overlays included — and never on `:root`: the Entry
 *   screens have their own approved phone composition (`entry.css` §19) and
 *   keep it.
 * ======================================================================== */

@media (max-width: 767px) {
  :root {
    /* Text (§11). */
    --phone-text-field: 16px;
    --phone-text-row-name: 17px;
    --phone-text-secondary: 13px;
    --phone-text-title: 24px;
    --phone-text-min: 12px;
    /* Targets, rows, fields and bars (§11). */
    --phone-target: 44px;
    --phone-row-height: 76px;
    --phone-avatar: 50px;
    --phone-field-height: 44px;
    --phone-filter-height: 36px;
    --phone-header-height: 44px;
    --phone-bar-height: 49px;
    /* Gutters and gaps (§11). */
    --phone-gutter: 16px;
    --phone-gap-tight: 8px;
    --phone-gap: 12px;
    --phone-section-gap: 24px;
    /* Corners (§11); a bubble keeps the desk's. */
    --phone-radius-control: 10px;
    --phone-radius-card: 12px;
    --phone-radius-sheet: 16px;
    /* Glyphs (§11): the desk's own, larger. */
    --phone-icon-header: 24px;
    --phone-icon-bar: 26px;
    --phone-icon-composer: 22px;
    /* A message bubble's widest (§11). */
    --phone-bubble-max: 80%;
    /*
     * How tall the conversation's composer row stands — what a toast rises
     * above inside a conversation. The row writes its measured height here
     * while it is mounted (`composer.tsx`); this is its height at rest.
     */
    --phone-composer-block-size: 61px;
    /*
     * The safe areas (`viewport-fit=cover` in `src/app/layout.tsx`): what the
     * bar and the composer pad by at the bottom, and the header at the top.
     * Zero on a phone with no notch and no home indicator.
     */
    --safe-area-inset-top: env(safe-area-inset-top, 0px);
    --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px);
  }

  :root:has(.app) {
    /* Every control and row control a 44 px target; fields and primary buttons 44 px tall. */
    --control-height: var(--phone-target);
    --control-height-lg: var(--phone-field-height);
    --field-height: var(--phone-field-height);
    --icon-button-size: var(--phone-target);
    /* The filter buttons: 36 px drawn (their 44 px target is the filter row's, stage 3). */
    --control-height-sm: var(--phone-filter-height);
    --radius-control: var(--phone-radius-control);
    --radius-card: var(--phone-radius-card);
    /* Secondary text at 13 px; nothing anywhere under 12 px. */
    --text-meta: var(--phone-text-secondary);
    --text-micro: var(--phone-text-min);
    --text-badge: var(--phone-text-min);
  }

  /*
   * Every field's text at 16 px, the size under which iOS zooms the page on
   * focus (§1 principle 8, `PO-107`). A field reads one of the text tokens;
   * inside a field every one of them is 16 px, so whichever it reads, it is.
   */
  :root:has(.app) :is(input, textarea, select, .select-trigger, [contenteditable='true']) {
    --text-body: var(--phone-text-field);
    --text-list: var(--phone-text-field);
    --text-meta: var(--phone-text-field);
    --text-micro: var(--phone-text-field);
    --text-section: var(--phone-text-field);
  }

  /* What a message reads at, in the thread and in the composer: 16 px (§11). */
  .inbox-conversation {
    --text-message: var(--phone-text-field);
  }
}

/*
 * The live chat widget's window (C2-S1): the business's face, not the design
 * system's, so its neutrals are its own. Declared on `.lcw-panel`, **not on
 * `:root`**: the desk's isolation probe hashes every computed property of every
 * element, inherited custom properties included, so a variable on `:root`
 * changes every desk screen's hash without moving a pixel
 * (QUALITY_AND_RELEASE_GATES.md §18.9). Kept in this layer because no other
 * stylesheet may write a literal colour (`shell-structure.test.ts`); slice 2's
 * frame loads this file with `live-chat-widget.css`.
 */
.lcw-panel {
  --lcw-accent: #2563eb;
  --lcw-on-accent-white: #ffffff;
  --lcw-on-accent-ink: #111827;
  --lcw-on-accent: var(--lcw-on-accent-white);
  --lcw-surface: #ffffff;
  --lcw-ink: #111827;
  --lcw-body: #f6f7f9;
  --lcw-bubble-team: #e9ebef;
  --lcw-divider: #e5e7eb;
  --lcw-muted: #6b7280;
  --lcw-faint: #9ca3af;
  --lcw-shadow: 0 12px 32px rgb(17 24 39 / 18%);
  /* Slice 3: a refusal line (the pre-chat step, a file the widget refuses). */
  --lcw-danger: #b91c1c;
}

/*
 * The live chat widget's window (C2-S1; `src/app/_components/live-chat/widget-panel.tsx`).
 *
 * The business's face, not HeyBase's (the live chat brief §1 principle 1):
 * the colour comes from the Channel (`--lcw-accent`, `--lcw-on-accent` on the
 * panel), and the panel carries its own `dir`, so every rule here is logical.
 * The same rules serve the Settings preview and, from slice 2, the frame on
 * the business's site. Sizes are the brief's §3.2b: 380px wide, up to 600px
 * tall.
 */

.lcw-panel {
  /*
   * The panel's `--lcw-*` values are declared on `.lcw-panel` in `tokens.css`,
   * never on `:root` (see there); `--lcw-accent` and `--lcw-on-accent` are
   * set again inline by `widget-panel.tsx` from the Channel's colour.
   */
  display: flex;
  flex-direction: column;
  inline-size: 100%;
  max-inline-size: 380px;
  block-size: 480px;
  max-block-size: 600px;
  overflow: hidden;
  border-radius: 16px;
  background: var(--lcw-surface);
  box-shadow: var(--lcw-shadow);
  color: var(--lcw-ink);
  font-size: 14px;
  line-height: 1.45;
}

.lcw-header {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px;
  background: var(--lcw-accent);
  color: var(--lcw-on-accent);
}

.lcw-header-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0;
}

.lcw-business {
  font-size: 16px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.lcw-reply-time {
  font-size: 13px;
  opacity: 0.85;
}

.lcw-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 28px;
  block-size: 28px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.lcw-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  overflow-y: auto;
  background: var(--lcw-body);
}

.lcw-bubble {
  position: relative;
  max-inline-size: 80%;
  margin: 0;
  padding: 8px 12px;
  border-radius: 14px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.lcw-bubble-team {
  align-self: flex-start;
  border-end-start-radius: 4px;
  background: var(--lcw-bubble-team);
}

.lcw-bubble-visitor {
  align-self: flex-end;
  border-end-end-radius: 4px;
  background: var(--lcw-accent);
  color: var(--lcw-on-accent);
}

.lcw-composer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-block-start: 1px solid var(--lcw-divider);
}

.lcw-composer-field {
  flex: 1;
  color: var(--lcw-muted);
}

.lcw-send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 32px;
  block-size: 32px;
  border-radius: 50%;
  background: var(--lcw-accent);
  color: var(--lcw-on-accent);
}


.lcw-foot {
  padding: 6px 16px 10px;
  color: var(--lcw-faint);
  font-size: 11px;
  text-align: center;
}

/*
 * The frame (C2-S2 part B, A13): the same panel filling its own document,
 * which the loader sizes — 380 × up to 600 on the desk, the whole screen on
 * a phone (the brief §3.2b, §3.2c). Only the frame's `body.lcw-frame` and the
 * interactive pieces the preview draws inert; nothing here touches `:root`.
 */
body.lcw-frame {
  margin: 0;
  block-size: 100vh;
  background: transparent;
  /*
   * The app's own faces (`R3-361`), declared for the frame alone in
   * `live-chat-frame-fonts.css` (the frame's stylesheet serves it first): the
   * app's stack, Heebo for Hebrew and Inter for the rest, then the system's.
   */
  font-family: Heebo, Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

.lcw-frame .lcw-panel {
  max-inline-size: none;
  block-size: 100vh;
  max-block-size: none;
  box-shadow: none;
}

.lcw-frame .lcw-panel[data-phone='true'] {
  border-radius: 0;
}

.lcw-panel [hidden] {
  display: none;
}

.lcw-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 28px;
  block-size: 28px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.lcw-responder {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.lcw-typing {
  font-size: 12px;
  opacity: 0.85;
}

.lcw-responder-disc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 20px;
  block-size: 20px;
  border-radius: 50%;
  background: var(--lcw-on-accent);
  color: var(--lcw-accent);
  font-size: 11px;
  font-weight: 600;
}

.lcw-connection {
  margin: 0;
  padding: 6px 16px;
  background: var(--lcw-bubble-team);
  color: var(--lcw-ink);
  font-size: 12px;
}

.lcw-input {
  flex: 1;
  min-inline-size: 0;
  max-block-size: 112px;
  padding: 6px 0;
  border: 0;
  outline: none;
  resize: none;
  background: transparent;
  color: var(--lcw-ink);
  font: inherit;
  line-height: 20px;
  block-size: 32px;
}

/* The field's focus is the composer's top line; the panel's ring is for controls. */
.lcw-panel .lcw-input:focus-visible {
  outline: none;
}

/* The composer's focus is one line in the Channel's colour along its top edge. */
.lcw-composer:focus-within {
  box-shadow: inset 0 2px 0 var(--lcw-accent);
}

.lcw-input::placeholder {
  color: var(--lcw-muted);
}

button.lcw-send {
  flex: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.lcw-panel :focus-visible {
  outline: 2px solid var(--lcw-ink);
  outline-offset: 2px;
}

.lcw-bubble.lcw-pending {
  opacity: 0.65;
}

/*
 * The bubble's meta (C2-QA2, `R3-389`; WhatsApp's grammar): the time at the
 * bottom end corner, small and muted, and on the visitor's own bubble its
 * state after it — the faint clock while sending, one tick once the server
 * accepted the message, two once a member read it. `.lcw-meta-room` is an
 * invisible copy in the text's flow that takes the meta's room on the last
 * line, or a line of its own when the last line is full; the visible meta
 * sits over it at the corner, so a short bubble is never raised. The words
 * after the slash are the screen reader's.
 */
.lcw-meta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11px;
  line-height: 1;
  white-space: nowrap;
  opacity: 0.7;
}

.lcw-meta::before {
  content: attr(data-time);
}

.lcw-meta-room {
  visibility: hidden;
  margin-inline-start: 8px;
  vertical-align: baseline;
}

.lcw-meta:not(.lcw-meta-room) {
  position: absolute;
  inset-block-end: 7px;
  inset-inline-end: 12px;
}

.lcw-bubble-file > .lcw-meta-room {
  align-self: flex-end;
}

.lcw-state {
  display: inline-block;
  inline-size: 14px;
  text-align: center;
}

.lcw-state::after {
  content: '✓' / attr(data-sent-label);
  font-size: 11px;
}

.lcw-pending .lcw-state::after {
  content: '';
  display: inline-block;
  inline-size: 9px;
  block-size: 9px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
}

.lcw-read .lcw-state::after {
  content: '✓✓' / attr(data-read-label);
  letter-spacing: -2px;
}

.lcw-failed .lcw-state {
  visibility: hidden;
}

.lcw-bubble.lcw-failed {
  opacity: 0.8;
}

.lcw-retry {
  display: block;
  margin-block-start: 4px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}


/*
 * Slice 3 (C2-S3 part B; the brief §3.2b, §3.2d, §3.3c; `ADR-020` §6 A15,
 * A16): the attach clip, the pre-chat step, the file bubbles, and the member's
 * picture at the first bubble of a run.
 */
.lcw-attach {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: 32px;
  block-size: 32px;
  border: 0;
  border-radius: 50%;
  padding: 0;
  background: transparent;
  color: var(--lcw-muted);
}

button.lcw-attach {
  cursor: pointer;
}

button.lcw-attach:hover {
  color: var(--lcw-ink);
}

.lcw-composer-error {
  margin: 0;
  padding: 6px 16px 0;
  color: var(--lcw-danger);
  font-size: 12px;
}

.lcw-prechat {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 16px 16px;
  border-block-start: 1px solid var(--lcw-divider);
}

.lcw-prechat-title {
  margin: 0;
  font-weight: 600;
}

.lcw-prechat-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
}

.lcw-prechat-optional {
  color: var(--lcw-muted);
  font-weight: 400;
}

.lcw-prechat-input {
  padding: 8px 10px;
  border: 1px solid var(--lcw-divider);
  border-radius: 8px;
  background: var(--lcw-surface);
  color: var(--lcw-ink);
  font: inherit;
}

.lcw-prechat-input:focus-visible {
  outline: 2px solid var(--lcw-accent);
  outline-offset: 0;
}

.lcw-prechat-input[aria-invalid='true'] {
  border-color: var(--lcw-danger);
}

.lcw-prechat-error {
  color: var(--lcw-danger);
  font-size: 12px;
}

.lcw-prechat-continue {
  align-self: stretch;
  padding: 9px 12px;
  border: 0;
  border-radius: 8px;
  background: var(--lcw-accent);
  color: var(--lcw-on-accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.lcw-bubble-file {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.lcw-file-image {
  display: block;
  min-inline-size: 64px;
  min-block-size: 64px;
  max-inline-size: 100%;
  max-block-size: 220px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--lcw-surface);
}

/*
 * A bubble holding an image is the image alone (`R3-363`, WhatsApp's image
 * bubble): no padding, no background, no border; the image carries the
 * bubble's corners, the tail's included. Anything under it (a caption, the
 * retry line) reads in the window's ink, since there is no colour behind it.
 */
.lcw-bubble-file:has(> .lcw-file-image) {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--lcw-ink);
}

.lcw-bubble-file:has(> .lcw-file-image) > .lcw-file-image {
  border-radius: inherit;
}

/* An image alone carries its meta over its corner, on a scrim, as WhatsApp's does. */
.lcw-bubble-file:has(> .lcw-file-image):not(:has(> bdi)) > .lcw-meta-room {
  display: none;
}

.lcw-bubble-file:has(> .lcw-file-image):not(:has(> bdi)) > .lcw-meta:not(.lcw-meta-room) {
  inset-block-end: 6px;
  inset-inline-end: 6px;
  padding: 3px 6px;
  border-radius: 8px;
  background: rgb(0 0 0 / 45%);
  color: var(--lcw-on-accent-white);
  opacity: 1;
}

.lcw-file-doc {
  display: flex;
  align-items: center;
  gap: 10px;
  min-inline-size: 180px;
}

.lcw-file-doc-glyph {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: 32px;
  block-size: 32px;
  border-radius: 8px;
  background: var(--lcw-surface);
  color: var(--lcw-ink);
  font-size: 10px;
  font-weight: 700;
}

.lcw-file-doc-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-inline-size: 0;
}

.lcw-file-doc-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lcw-file-doc-size {
  font-size: 12px;
  opacity: 0.75;
}

.lcw-file-download {
  color: inherit;
  font-size: 12px;
  text-decoration: underline;
}

/* The member's picture at the first bubble of a team run (the brief §3.2b). */
.lcw-run {
  display: flex;
  align-items: flex-start;
  align-self: flex-start;
  gap: 6px;
  max-inline-size: 88%;
}

.lcw-run-bubbles {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-inline-size: 0;
}

.lcw-run-bubbles > .lcw-bubble {
  max-inline-size: 100%;
}

.lcw-avatar {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: 24px;
  block-size: 24px;
  overflow: hidden;
  border-radius: 50%;
  background: var(--lcw-bubble-team);
  color: var(--lcw-ink);
  font-size: 11px;
  font-weight: 600;
}

.lcw-avatar img,
.lcw-responder-disc img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.lcw-responder-disc {
  overflow: hidden;
}

/*
 * No zoom on a phone (`R3-364`): iOS zooms the page when a field under 16 px
 * takes focus, so every field in the window is 16 px where there is no hover.
 * The desk keeps its sizes; labels and helper lines keep theirs.
 */
@media (hover: none) {
  .lcw-panel .lcw-input,
  .lcw-panel .lcw-prechat-input,
  .lcw-panel input,
  .lcw-panel select,
  .lcw-panel textarea {
    font-size: 16px;
  }
}
