/* Conversation column skeleton: header (breadcrumb row + tabs) over the view area, composer InputBar at the bottom. Column width/squeeze is layout's; this fills its cell. Figma: Header 39:27730 (83px two-row), tabs 13px with a 3px active bar. */ .root { display: flex; flex-direction: column; height: 100%; min-width: 0; background: var(--dsw-alias-bg-base); } .header { flex: none; padding: 12px 28px 0 20px; border-bottom: 1px solid var(--dsw-alias-border-l2); } /* Blank hero/settling: keep the header node mounted (stable Session tree for the wrapActiveBody composer) without taking column space. */ .headerHidden { display: none; } .crumbRow { display: flex; align-items: center; justify-content: space-between; min-height: 32px; } .crumbs { display: flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; white-space: nowrap; } .crumbSeg { display: inline-flex; align-items: center; gap: 4px; min-width: 0; } .crumbSep { /* figma: "/" separators are 14px caption gray (75:7903), one tint lighter than crumb text. */ color: var(--dsw-alias-label-caption); font-size: 14px; line-height: 20px; } .crumb { max-width: 220px; overflow: hidden; padding: 4px 8px; border: none; border-radius: 12px; background: transparent; font-size: 14px; line-height: 20px; color: var(--dsw-alias-label-tertiary); text-overflow: ellipsis; white-space: nowrap; cursor: pointer; } .crumb:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); } .crumbCurrent { font-weight: 500; color: var(--dsw-alias-label-primary); cursor: default; } /* figma Tab_Group 34:11441: 35px strip, gap 36, pad-left 8, tabs bottom-aligned. */ .tabs { display: flex; gap: 36px; margin-top: 4px; padding-left: 8px; } /* figma .Tab 34:11442: 13/16 text (figma wt510, rendered 500), gap 8 to the 3px bar (no bottom rounding). */ .tab { position: relative; padding: 0 0 11px; border: none; background: transparent; font-size: 13px; line-height: 16px; font-weight: 500; color: var(--dsw-alias-label-tertiary); cursor: pointer; } .tab::after { content: ''; position: absolute; right: 0; bottom: 0; left: 0; height: 3px; background: transparent; } /* Selected tab is blue, not ink: brand-primary resolves to neutral black in this token sheet, so the selected state rides the business blue — the nearest semantic token that stays blue in both themes. */ .tabActive { color: var(--dsw-alias-state-business-primary); } .tabActive::after { background: var(--dsw-alias-state-business-primary); } .viewArea { display: flex; flex: 1; flex-direction: column; min-height: 0; } /* Composer context stack (Figma 9:937): standalone dock cards share one rhythm; the terminal queue strip additionally tucks under the input card. */ .composerStack { --dsh-composer-stack-gap: 6px; --dsh-queue-composer-overlap: 5px; display: flex; flex-direction: column; gap: var(--dsh-composer-stack-gap); } /* Common seat for the composer chain (fallback + elected overlay siblings). */ .composerSeat { display: flex; flex: none; flex-direction: column; /* One cap for every scrolling text region a composer seat can hold: the InputBar draft (figma Input 75:8208 max 14 lines × 24px line) and the takeover panels' bodies top out at the same height, so electing a takeover never grows the footer past the card it replaces. Declared on the seat because it is the chain's only shared ancestor — fallback and elected overlay are siblings — and custom properties inherit down to whichever entry is mounted. */ --dsh-composer-text-max-height: 336px; } /* Active phase: header is ordinary column chrome above the scrollport (not sticky). The scroll body holds the transcript and the sticky composer seat so wheel over the footer moves the flow. */ .root[data-phase='active'] { overflow: hidden; } .root[data-phase='active'] .header { flex: none; } .scrollBody { display: flex; flex: 1; flex-direction: column; min-height: 0; overflow-y: auto; } .root[data-phase='active'] .viewArea { flex: 1 0 auto; min-height: auto; } .root[data-phase='active'] .composerSeat { position: sticky; bottom: 0; /* Above markdown CodeBlock sticky banners (z-index 6) so the footer never paints under a sticking code header while scrolling. */ z-index: 7; /* Input mask (figma 1205:27463): transcript fades out under a FIXED 36px band at the seat's top (the figma 24% of the resting ~150px composer), solid below — px stops, not %, so a growing draft only widens the solid region and the fade band never stretches. The 0px stop is bg-base at zero alpha (not white, which the figma export hardcodes) so both themes fade from their own base. */ background: linear-gradient( 180deg, color-mix(in srgb, var(--dsw-alias-bg-base) 0%, transparent) 0px, var(--dsw-alias-bg-base) 36px ); } /* Hero phase: the composer stack (hero chrome + workspace row + card) is flex-centered in the column; composer phase docks it at the bottom. Flex, NOT absolute+transform: a transform would make this box the containing block for position:fixed descendants (pickers/modals), shrinking them. */ .composerHero { position: relative; /* .heroGlow positioning context */ align-self: center; /* figma 75:8208: 12 between hero chrome / workspace row / card. */ gap: 12px; /* Foot inside the centered box floats the stack a bit above true center. */ padding-bottom: 32px; width: min(776px, calc(100% - 48px)); z-index: 1; } /* Blue backdrop ellipse (figma 313:14109), centered on the input card: the card's resting center sits ~92px above the stack bottom (32 foot pad + half of the ~120px two-row card); width tracks the card (glow asset 1051 vs design card 776) so blur scales in userSpace with it. z-index -1 keeps it behind the in-flow hero content inside this stacking context. */ .heroGlow { position: absolute; left: 50%; bottom: 92px; z-index: -1; width: calc(100% * 1051 / 776); aspect-ratio: 1051 / 468; transform: translate(-50%, 50%); pointer-events: none; } .heroWorkspaceRow { display: flex; align-items: center; min-width: 0; padding-left: 8px; } /* Hero: the composer sits inside the session scroll body; center there so the tree seat matches active (sticky footer) without a Root remount. */ .root[data-phase='hero'] .scrollBody { justify-content: center; overflow-y: auto; } /* Settling (session replaying, hero/docked unknown): keep the composer seat mounted but invisible so no wrong layout flashes before the phase lands. */ .root[data-phase='settling'] .composerSeat { visibility: hidden; }