.root { display: flex; flex-direction: column; align-items: center; /* Side pads ride the shared clearance (figma Input_Bottom drew L32/R32/B8; the sides narrow with the shared width axis); the bottom gradient mask is owned by the chat scroller. No top pad: the composer stack's gap owns the space above; the status strip still carries its own margin. */ padding: 0 var(--dsh-composer-side-clearance) 4px; } .dock { display: flex; align-items: center; justify-content: center; gap: 12px; max-width: 100%; padding-top: 4px; } .hero .dock:empty { display: none; } .hero { /* No bottom pad in the centered hero, but the side clearance must survive: the hero wrapper is full-width on narrow viewports, so this padding is the only thing keeping the card off the edges there. */ padding: 0 var(--dsh-composer-side-clearance); } .notice { width: 100%; max-width: var(--dsh-composer-card-max-width); margin-bottom: 6px; padding: 4px 8px; border-radius: var(--dsw-radius-md); background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; } .card { box-sizing: border-box; position: relative; /* overlay anchor positioning context */ display: flex; flex-direction: column; /* figma Input 75:8208: 12px between the text area and the button row; the card's top pad before .InputText tightened from the drawn 10 to 8. */ gap: 12px; width: 100%; max-width: var(--dsh-composer-card-max-width); padding-top: 8px; /* Input stroke: the light neutral (l2, one step above the menus' l1), drawn as the elevation hairline so it costs no layout; the shadow is the soft elevation tier. Fill: the input surface token (elevated in dark). */ border: 0; --dsw-elevation-stroke-color: var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-panel); background: var(--dsw-specific-input-major); box-shadow: var(--dsw-elevation-soft); /* The draft text follows the Settings font-size preference (the textarea, mirror, and backdrop all inherit from here, so the three layers stay in step); the line height keeps the default 10px leading via the shared px delta. */ font-size: var(--dsh-content-font-size, 14px); line-height: calc(24px + var(--dsh-content-font-delta, 0px)); /* Elevated surface in dark, same as the menus: the draft scrollport inside scrolls once the composer hits its height cap, so the thumb takes the l2 pair. Declared on the card because the elevation belongs to the surface, and the custom properties inherit down to the box that actually scrolls (see ui-theme styles/scrollbar.css for the rebinding contract). */ --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2); --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2); } @media (pointer: coarse) { .card { font-size: max(16px, var(--dsh-content-font-size, 14px)); } } /* No-workspace trigger state: an inset 1px dashed outline marks the card as a workspace picker rather than a live composer. Keeping the complete stroke inside the rounded box prevents clipped corner pixels from reading heavier. */ .cardWorkspaceTrigger { --dsw-elevation-stroke-color: transparent; cursor: pointer; } .cardWorkspaceTrigger::after { content: ''; position: absolute; inset: 0; box-sizing: border-box; border: 1px dashed currentColor; border-radius: var(--dsw-radius-panel); color: var(--dsw-alias-border-l4); transition: color 100ms ease; pointer-events: none; } /* Disabled toolbar controls neither receive nor swallow clicks in the trigger state: pointer events fall through to the card's own click handler, making the whole card one pick target. */ .cardWorkspaceTrigger :disabled { pointer-events: none; } @media (hover: hover) { .cardWorkspaceTrigger:hover::after { color: var(--dsw-alias-state-business-primary); } } .accessory { display: flex; align-items: center; gap: 8px; padding: 10px 12px 0; } /* Floating overlay anchor (menu / popupSelect shell): entries position themselves against the card (bottom: 100% + gap); closed entries render null. */ .overlayAnchor { position: absolute; inset: 0 0 auto; height: 0; } /* The draft's scrollport, and the ONLY scrolling box in the composer. The contenteditable grows with its content inside; the 14-line cap lives here because this is the box the cap describes. */ .scroll { max-height: var(--dsh-composer-text-max-height); overflow-y: auto; /* Keeps the bar off the card's right border; .input's right pad gives the 4px back so the text column is unchanged. */ margin-right: 4px; } /* The card's corner arc still overlaps the scrollport's top (8px top pad reaches only y=8): start the thumb's travel below the arc so it never pokes outside the capsule. WebKit-path only; Firefox's thin bar has no track margin, an accepted remainder. */ .scroll::-webkit-scrollbar-track { margin-top: 8px; } /* Auto-grow anchor: the contenteditable is in normal flow and sets the draft's height; the placeholder rides it absolutely. */ .grow { position: relative; } /* Machine pending dot (adjudicating / submitting). */ .pending { width: 8px; height: 8px; border-radius: 50%; corner-shape: round; background: var(--dsw-alias-state-business-primary); animation: input-pending 1s ease-in-out infinite alternate; } @keyframes input-pending { from { opacity: 0.35; } to { opacity: 1; } } /* The contenteditable draft surface (grows with its content; .scroll caps and scrolls it). figma .InputText 34:10434 drew pl 16 / pr 12 / pt 4; the left pad tightened to 14, and the right pad is 8 here plus .scroll's 4px bar inset. */ .input { box-sizing: border-box; /* Docked floor: one 24px line + 4px top pad = 28 content height; 36 leaves an 8px breath below the line (the hero keeps its own 52px floor below). */ min-height: 36px; padding: 4px 8px 0 14px; font-family: var(--dsw-font-family); font-size: inherit; line-height: inherit; white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; outline: none; color: var(--dsw-alias-label-primary); /* Business blue, not brand-primary: that token resolves to ink in this sheet. */ caret-color: var(--dsw-alias-state-business-primary); } /* Lexical paragraphs are
blocks: strip the UA margins so the surface keeps the textarea's line rhythm. */ .input p { margin: 0; } /* Claim ghost hint as generated content after the last paragraph: the bar sets --dsh-composer-hint (a quoted string) while the claim's args are blank; without the variable the declaration is invalid and nothing shows. */ .input p:last-child::after { content: var(--dsh-composer-hint); color: var(--dsw-alias-label-caption); } .input[data-composer-composing] p:last-child::after { content: none; } /* A selected command chip stands in for the caret until the next key. */ .input[data-composer-chip-selected] { caret-color: transparent; } .input[data-composer-composing] + .placeholder { visibility: hidden; } /* figma 34:10434: #ADB2B8 light / #81858C dark — the caption pair exactly. Insets mirror .input's pads (the other right 4px sits on .scroll). One ellipsized line: a wrapped second line would clip against the docked 36px floor. */ .placeholder { position: absolute; inset: 4px 8px auto 14px; color: var(--dsw-alias-label-caption); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; user-select: none; } .inputDisabled { color: var(--dsw-alias-label-tertiary); cursor: not-allowed; } .input[aria-haspopup='menu'] { cursor: pointer; } /* Hero (centered empty-state) keeps the 2-line floor (figma min-h 52 = ~2 × 24 line + 4pt); the docked composer collapses to the content height. */ .hero .input { min-height: 52px; } /* The hero's 52px floor fits two lines, so its placeholder clamps at two instead of the docked single line. */ .hero .placeholder { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; } /* Toolbar: attach + Plan + Read-only on the left; model + send on the right (figma Input_Bottom chrome). */ .row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; /* 2px moved from the bottom pad to the top: the whole control row sits 2px lower in the card (it read too high against the textarea) while the controls' own centering stays untouched. */ padding: 2px 8px 6px; min-width: 0; /* Size container so the chips inside can collapse to icon-only when the card runs out of row width (PermissionSelect @container rule). Anonymous on purpose: CSS modules hash container-name per module, so a name declared here can never match a query in another module's sheet. */ container-type: inline-size; } .row[data-model-compact] { --dsh-composer-model-text-display: none; --dsh-composer-model-icon-display: block; } .tools, .modes, .trailing { display: flex; align-items: center; min-width: 0; } /* figma 75:8208 drew 16 between + and the mode chips and 4 between Plan / Read-only; both gaps settled on 12 so the pill chips read as separate controls without drifting from the + control. */ .tools { gap: 12px; } .modes { gap: 12px; } .trailing { flex: none; /* Wrap keeps the left mode chips and the right controls apart when the card runs out of row width: the trailing group (model + send) moves to its own line instead of the left group shrinking until its chip overlaps the model trigger (external:107). The auto margin re-anchors it right on the wrapped line; on a single line space-between already pins it right. */ margin-left: auto; gap: 12px; } /* Compact spacing preserves room for the fixed-width controls in narrow cards. */ @container (max-width: 560px) { .tools, .modes, .trailing { gap: 8px; } } /* Attach circle (figma + control): 28px, selector fill, primary glyph. */ .add { display: grid; place-items: center; flex: none; width: 28px; height: 28px; border: none; border-radius: 999px; corner-shape: round; background: var(--dsw-specific-selector); color: var(--dsw-alias-label-primary); cursor: pointer; } @media (hover: hover) { .add:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover-solid); } } .add:disabled { opacity: 0.5; cursor: default; } /* Plan / Read-only / model — native