/* * Motion tokens (D220). One curve and three durations, on :root so portals and * the view-transition pseudo-elements can read them. Enters take --v2-dur, * exits and closing take --v2-dur-fast, anything that travels takes --v2-dur-slow. * Animate transform and opacity only; folds are the one height exception. */ :root { --v2-dur-fast: 120ms; --v2-dur: 200ms; --v2-dur-slow: 320ms; --v2-ease: cubic-bezier(0.2, 0, 0, 1); } /* * V2 design tokens + base styles. All v2 styles are scoped under .v2-root * so the existing app's global CSS does not bleed in (and vice versa). */ /* The ONE deliberate exception to the .v2-root scope: the page canvas. App.tsx unconditionally stamps `modern-ui` (V1's dark gradient) on
; .v2-root paints only its own box, so the V1 dark showed through on load flash, overscroll rubber-banding, and every gap. V2App stamps `v2-canvas` while mounted; the compound selector out-ranks body.modern-ui (0-2-1 > 0-1-1) so bundle order cannot decide this. Literal color: body sits outside .v2-root, so tokens don't reach it — keep in lockstep with --v2-page-bg below. */ body.v2-canvas, body.modern-ui.v2-canvas { background: #f8f8fb; } .v2-root { /* Palette */ --v2-bg: #ffffff; --v2-page-bg: #eef0f4; --v2-bg-subtle: #f9fafb; --v2-surface: #ffffff; --v2-surface-tint: #f2f4f7; --v2-surface-hover: #f2f4f7; --v2-border: #d0d5dd; --v2-border-soft: #e4e7ec; --v2-border-strong: #98a2b3; --v2-text-primary: #101828; --v2-message-text: #1f2937; /* TASK-701: inactive rail glyphs, join-radio outlines (3:1 on white and #f2f4f7) and the neutral selected pod row. Dark values sit in both dark sets below. */ /* D223 ruling (Cofounder, 11-01): the artboard's --ph (#98a2b3) is under 3:1 on the rail's own surface, --v2-shell-bg #eef0f4 (2.26:1), so this is the nearest grey that passes there: 3.05:1, and 3.47:1 on the white tab bar. Dark #5f6979 is 3.48:1 on the dark shell and 3.17:1 on its surface. */ --v2-rail-icon: #828a99; --v2-control-outline: #667085; /* D223 (TASK-735): the artboard's .pod.on in both themes, a neutral tint row whose mark alone turns cobalt. */ --v2-row-active: #f2f4f7; --v2-row-active-text: #101828; --v2-row-active-mute: #667085; --v2-row-active-mark: #1d3fd1; --v2-text-secondary: #475467; --v2-text-tertiary: #667085; --v2-text-muted: #667085; --v2-text-placeholder: #98a2b3; /* The tint step (TASK-122) in the Signal identity: shell ground #eef0f4 behind rail, pods and inspector; the content pane is an inset white card with a 1px #d0d5dd ring and a 6px radius — hard edges, no shadow. Mirrors --c-shell-bg / --c-content-radius in design-system/tokens.css. */ --v2-shell-bg: #eef0f4; --v2-content-radius: 6px; /* Character-avatar grounds (TASK-679, Lit Signal). The fill behind each face is generated in utils/avatars.ts CHARACTER_GROUNDS, which must match these values; avatarGrounds.test.ts pins the two together. Agents are cool, people warm, so the family still tells the species apart. Mirrors --c-avatar-* in design-system/tokens.css. */ --v2-avatar-agent-1: #dbe5f1; --v2-avatar-agent-2: #e1e6ee; --v2-avatar-agent-3: #d6e7e4; --v2-avatar-human-1: #f1e7df; --v2-avatar-human-2: #f2e8d8; --v2-avatar-human-3: #efe1dc; --v2-avatar-human-4: #f4e3d7; --v2-avatar-frame: rgba(16, 24, 40, 0.06); /* Dark ring (TASK-679 dark note): the tile stays light on dark, so the ring flips to a light hairline. No dark theme on main yet; TASK-680 points --v2-avatar-frame at this value under its dark scope. */ --v2-avatar-frame-dark: rgba(255, 255, 255, 0.10); /* Ink primary — filled buttons are ink; cobalt (#1d3fd1) is a MARK inside the app: links, the live dot, the agent name, the one card that needs you. On marketing surfaces cobalt is used as blocks. Mirrors --c-ink. */ --v2-ink: #101828; --v2-ink-hover: #1d2939; --v2-on-ink: #ffffff; --v2-on-accent: #ffffff; --v2-accent-fill: #1d3fd1; /* BEND-1 (spec 2026-08-30): the featured-tier name needs a step between 16 and 20. Mirrors --c-fs-feature in design-system/tokens.css; the two move together. */ --v2-fs-feature: 17px; --v2-accent: #1d3fd1; --v2-accent-strong: #1633a8; --v2-accent-soft: #e9edfb; --v2-accent-text: #1d3fd1; --v2-accent-deep: #0f2378; --v2-focus-ring: 0 0 0 3px rgba(29, 63, 209, 0.18); /* Platform brand + tint tokens (connectors) — mirror of --c-platform-* in design-system/tokens.css; the two move together. */ --v2-platform-telegram: #2aabee; --v2-platform-telegram-soft: #e4f3fb; --v2-platform-slack: #611f69; --v2-platform-slack-soft: #f3e9f4; --v2-platform-discord: #5865f2; --v2-platform-discord-soft: #eaecfe; --v2-platform-whatsapp: #1fa855; --v2-platform-whatsapp-soft: #e5f6ec; --v2-platform-x: #111827; --v2-platform-x-soft: #eef0f3; --v2-platform-instagram: #d62976; --v2-platform-instagram-soft: #fbe9f1; --v2-platform-messenger: #0084ff; --v2-platform-messenger-soft: #e5f2ff; --v2-platform-groupme: #00aff0; --v2-platform-groupme-soft: #e3f5fd; --v2-success: #10b981; /* Text-on-soft success — one canonical value (#1368: a second ad-hoc success-text hex is how a third one happens). Mirrors --c-success-text in design-system/tokens.css. */ --v2-success-text: #0f7a53; --v2-success-soft: #e3f6ec; --v2-success-ring: rgba(16, 185, 129, 0.40); --v2-success-ring-strong: rgba(16, 185, 129, 0.50); --v2-warning: #f4a23a; --v2-warning-soft: #fdefdc; --v2-info: #0891b2; --v2-info-soft: #dff4f8; --v2-danger: #ef4444; --v2-danger-text: #b91c1c; --v2-danger-soft: rgba(239, 68, 68, 0.10); /* TASK-687/698: board status and priority colour, the D215 B-lite choice (Sam, 10-31). Blocked and P0 are red; in progress stays cobalt; P1, P2, pending and done stay grey, with no green. Each state keeps its shape. Dark values are in both dark blocks below. */ --v2-prio-p0-bg: #b42318; --v2-prio-p0-fg: #ffffff; --v2-prio-ink: var(--v2-text-secondary); --v2-prio-p2-fg: #667085; --v2-prio-edge: var(--v2-border); --v2-status-live: var(--v2-accent); --v2-status-idle: var(--v2-border-strong); --v2-status-pending: #667085; --v2-status-blocked: #d92d20; /* Agent role colors */ --v2-pink: #f472b6; --v2-violet: #a78bfa; --v2-amber: #fbbf24; --v2-emerald: #34d399; --v2-sky: #60a5fa; --v2-rose: #fb7185; /* Typography — the Signal identity (direction C on the Shell Parity canvas, 2026-09-03). Three faces, each with one job: Bricolage Grotesque for display (headings, wordmarks — weight 700/800), IBM Plex Sans for every line of body and control text, IBM Plex Mono for meta (timestamps, ids, status). All self-hosted via @fontsource, imported in V2App.tsx above this sheet; the system stacks are fallbacks only while woff2 loads. Mirrors --c-font-* in design-system/tokens.css. */ --v2-font: "IBM Plex Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; --v2-font-display: "Bricolage Grotesque Variable", "Bricolage Grotesque", "Helvetica Neue", Arial, sans-serif; --v2-fs-body: 14px; --v2-lh-body: 20px; --v2-fs-meta: 12px; --v2-lh-meta: 16px; --v2-fs-label: 11px; --v2-lh-label: 14px; --v2-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; /* Geometry */ --v2-radius-sm: 4px; --v2-radius: 4px; --v2-radius-lg: 6px; --v2-radius-pill: 999px; /* D223 Signal primitives (TASK-730), from the d215 artboard :root. Additive: the old --v2-radius and --v2-shadow values above stay until PR2 moves consumers onto the scale. Pinned by __tests__/signalTokens.test.ts. */ --v2-ink-2: #1d2433; --v2-fill-a: #333c4e; --v2-fill-b: #161d2c; --v2-fill-edge: #0b101b; --v2-on-fill: #ffffff; --v2-accent-fill-a: #2e50e2; --v2-accent-fill-b: #1b3bc6; --v2-key-a: #ffffff; --v2-key-b: #f4f5f7; --v2-key-edge: rgba(15, 23, 42, 0.14); --v2-key-inset: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 1px 2px rgba(16, 24, 40, 0.06); --v2-shadow-pane: 0 0 0 1px rgba(16, 24, 40, 0.07), 0 1px 2px rgba(16, 24, 40, 0.04), 0 12px 28px -20px rgba(16, 24, 40, 0.18); --v2-shadow-pop: 0 0 0 1px rgba(16, 24, 40, 0.08), 0 2px 4px rgba(16, 24, 40, 0.04), 0 18px 36px -14px rgba(16, 24, 40, 0.28); --v2-av-ring: rgba(16, 24, 40, 0.07); --v2-scrim: rgba(16, 24, 40, 0.18); --v2-r-6: 6px; --v2-r-7: 7px; --v2-r-8: 8px; --v2-r-9: 9px; --v2-r-10: 10px; --v2-r-12: 12px; --v2-r-14: 14px; /* Elevation */ --v2-shadow-sm: none; --v2-shadow: none; --v2-shadow-lg: none; --v2-shadow-pending: none; /* the one chrome-card shadow: a PENDING decision or approval only */ /* Layout */ --v2-rail-w: 56px; --v2-pods-w: 260px; --v2-inspector-w: 272px; /* Keep production's scoped V2 layer in step with design-system/tokens.css. The typing cadence belongs to the minimal mark; pulse/spin retain their separate state-affordance semantics. */ --motion-stagger: 0.18s; --motion-breath: 1.2s; --motion-pulse: 1.6s; --motion-spin: 0.8s; --motion-ease-breath: ease-in-out; --motion-ease-state: ease-out; --motion-ease-linear: linear; font-family: var(--v2-font); color: var(--v2-text-primary); background: var(--v2-page-bg); height: 100vh; width: 100vw; overflow: hidden; display: flex; flex-direction: column; font-size: var(--v2-fs-body); line-height: var(--v2-lh-body); min-width: 0; } /* D216 dark set. Values are the artboard's dark :root (index.html, the @media + :root[data-theme="dark"] pair), mapped onto v2 token names. Scoped to .v2-root under the html attribute set by utils/theme.ts: explicit dark, or auto when the OS asks for dark. The light set is the block above. Not from the artboard, pending ux-lead: --v2-focus-ring, the success and danger text lifts, and the soft fills. Avatar and persona tints stay literal. */ :root[data-v2-theme="dark"] .v2-root { /* D223 Signal primitives (TASK-730); dark values from the d215 artboard. */ --v2-ink-2: #d3d8e1; --v2-fill-a: #f4f5f8; --v2-fill-b: #dce0e8; --v2-fill-edge: #c4cad5; --v2-on-fill: #0b0e13; --v2-accent-fill-a: #3d5ef2; --v2-accent-fill-b: #2948d8; --v2-key-a: #1e2531; --v2-key-b: #191f29; --v2-key-edge: rgba(255, 255, 255, 0.10); --v2-key-inset: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 1px 2px rgba(0, 0, 0, 0.3); --v2-shadow-pane: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 12px 28px -20px rgba(0, 0, 0, 0.6); --v2-shadow-pop: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 18px 36px -14px rgba(0, 0, 0, 0.75); --v2-av-ring: rgba(255, 255, 255, 0.10); --v2-scrim: rgba(0, 0, 0, 0.45); --v2-bg: #141922; --v2-page-bg: #0b0e13; --v2-bg-subtle: #181e28; --v2-surface: #141922; --v2-surface-tint: #1e2531; --v2-surface-hover: #1e2531; --v2-border: #364051; --v2-border-soft: #262d3a; --v2-border-strong: #5f6979; --v2-prio-p0-bg: #f97066; --v2-prio-p0-fg: #0b0e13; --v2-prio-p2-fg: #8b94a5; --v2-status-pending: #7c8696; --v2-status-blocked: #f97066; --v2-text-primary: #e8ebf1; --v2-message-text: #e8ebf1; --v2-rail-icon: #5f6979; --v2-control-outline: #737e92; --v2-row-active: #1e2531; --v2-row-active-text: #e8ebf1; --v2-row-active-mute: #8b94a5; --v2-row-active-mark: #6680ff; --v2-text-secondary: #aeb6c4; --v2-text-tertiary: #8b94a5; --v2-text-muted: #8b94a5; --v2-text-placeholder: #5f6979; --v2-shell-bg: #0b0e13; --v2-ink: #f4f5f8; --v2-ink-hover: #dce0e8; --v2-on-ink: #0b0e13; --v2-accent: #6680ff; --v2-accent-strong: #3d5ef2; --v2-accent-soft: #1b2546; --v2-accent-text: #8aa2ff; --v2-accent-deep: #2948d8; --v2-focus-ring: 0 0 0 2px var(--v2-bg), 0 0 0 4px #8aa2ff; --v2-success-text: #3ccf95; --v2-success-soft: rgba(16, 185, 129, 0.16); --v2-danger-text: #ff8a8a; --v2-danger-soft: rgba(239, 68, 68, 0.16); --v2-on-accent: #ffffff; --v2-accent-fill: #3d5ef2; --v2-warning-soft: rgba(244, 162, 58, 0.16); --v2-info-soft: rgba(8, 145, 178, 0.16); --v2-platform-x: #e8ebf1; --v2-platform-x-soft: rgba(17, 24, 39, 0.16); --v2-platform-telegram-soft: rgba(42, 171, 238, 0.16); --v2-platform-slack-soft: rgba(97, 31, 105, 0.16); --v2-platform-discord-soft: rgba(88, 101, 242, 0.16); --v2-platform-whatsapp-soft: rgba(31, 168, 85, 0.16); --v2-platform-instagram-soft: rgba(214, 41, 118, 0.16); --v2-platform-messenger-soft: rgba(0, 132, 255, 0.16); --v2-platform-groupme-soft: rgba(0, 175, 240, 0.16); color-scheme: dark; } @media (prefers-color-scheme: dark) { :root:not([data-v2-theme="light"]) .v2-root { /* D223 Signal primitives (TASK-730); dark values from the d215 artboard. */ --v2-ink-2: #d3d8e1; --v2-fill-a: #f4f5f8; --v2-fill-b: #dce0e8; --v2-fill-edge: #c4cad5; --v2-on-fill: #0b0e13; --v2-accent-fill-a: #3d5ef2; --v2-accent-fill-b: #2948d8; --v2-key-a: #1e2531; --v2-key-b: #191f29; --v2-key-edge: rgba(255, 255, 255, 0.10); --v2-key-inset: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 1px 2px rgba(0, 0, 0, 0.3); --v2-shadow-pane: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 12px 28px -20px rgba(0, 0, 0, 0.6); --v2-shadow-pop: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 18px 36px -14px rgba(0, 0, 0, 0.75); --v2-av-ring: rgba(255, 255, 255, 0.10); --v2-scrim: rgba(0, 0, 0, 0.45); --v2-bg: #141922; --v2-page-bg: #0b0e13; --v2-bg-subtle: #181e28; --v2-surface: #141922; --v2-surface-tint: #1e2531; --v2-surface-hover: #1e2531; --v2-border: #364051; --v2-border-soft: #262d3a; --v2-border-strong: #5f6979; --v2-prio-p0-bg: #f97066; --v2-prio-p0-fg: #0b0e13; --v2-prio-p2-fg: #8b94a5; --v2-status-pending: #7c8696; --v2-status-blocked: #f97066; --v2-text-primary: #e8ebf1; --v2-message-text: #e8ebf1; --v2-rail-icon: #5f6979; --v2-control-outline: #737e92; --v2-row-active: #1e2531; --v2-row-active-text: #e8ebf1; --v2-row-active-mute: #8b94a5; --v2-row-active-mark: #6680ff; --v2-text-secondary: #aeb6c4; --v2-text-tertiary: #8b94a5; --v2-text-muted: #8b94a5; --v2-text-placeholder: #5f6979; --v2-shell-bg: #0b0e13; --v2-ink: #f4f5f8; --v2-ink-hover: #dce0e8; --v2-on-ink: #0b0e13; --v2-accent: #6680ff; --v2-accent-strong: #3d5ef2; --v2-accent-soft: #1b2546; --v2-accent-text: #8aa2ff; --v2-accent-deep: #2948d8; --v2-focus-ring: 0 0 0 2px var(--v2-bg), 0 0 0 4px #8aa2ff; --v2-success-text: #3ccf95; --v2-success-soft: rgba(16, 185, 129, 0.16); --v2-danger-text: #ff8a8a; --v2-danger-soft: rgba(239, 68, 68, 0.16); --v2-on-accent: #ffffff; --v2-accent-fill: #3d5ef2; --v2-warning-soft: rgba(244, 162, 58, 0.16); --v2-info-soft: rgba(8, 145, 178, 0.16); --v2-platform-x: #e8ebf1; --v2-platform-x-soft: rgba(17, 24, 39, 0.16); --v2-platform-telegram-soft: rgba(42, 171, 238, 0.16); --v2-platform-slack-soft: rgba(97, 31, 105, 0.16); --v2-platform-discord-soft: rgba(88, 101, 242, 0.16); --v2-platform-whatsapp-soft: rgba(31, 168, 85, 0.16); --v2-platform-instagram-soft: rgba(214, 41, 118, 0.16); --v2-platform-messenger-soft: rgba(0, 132, 255, 0.16); --v2-platform-groupme-soft: rgba(0, 175, 240, 0.16); color-scheme: dark; } } .v2-root *, .v2-root *::before, .v2-root *::after { box-sizing: border-box; } /* Thin overlay-style scrollbars on every scroll container inside v2. Without this, Linux + Windows Chrome show a chunky default scrollbar with a gray track that clashes with the otherwise-flat surfaces. macOS Chrome paints overlay scrollbars by default and is unaffected. */ .v2-root *::-webkit-scrollbar { width: 8px; height: 8px; } .v2-root *::-webkit-scrollbar-track { background: transparent; } .v2-root *::-webkit-scrollbar-thumb { background: var(--v2-border-strong); border-radius: 4px; border: 2px solid transparent; background-clip: padding-box; } .v2-root *::-webkit-scrollbar-thumb:hover { background: var(--v2-text-tertiary); background-clip: padding-box; border: 2px solid transparent; } .v2-root *::-webkit-scrollbar-corner { background: transparent; } /* Firefox + modern Chrome standard property. */ .v2-root * { scrollbar-width: thin; scrollbar-color: var(--v2-border-strong) transparent; } .v2-root :where(button:not(.MuiButtonBase-root)) { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; padding: 0; } .v2-root input, .v2-root textarea { font-family: inherit; color: inherit; } .v2-root a { color: inherit; text-decoration: none; } /* ---------- Language switch ---------- */ /* Language dropdown. The trigger deliberately reads like a plain nav option (no pill chrome) so it sits naturally among nav links; the menu is a small anchored card. */ .v2-lang-switch { position: relative; display: inline-flex; } .v2-lang-switch__trigger { display: inline-flex; align-items: center; gap: 4px; padding: 4px 6px; border-radius: var(--v2-radius-sm, 8px); color: var(--v2-text-secondary); font-size: 13px; font-weight: 500; line-height: 1; transition: color 80ms ease; } .v2-lang-switch__trigger:hover { color: var(--v2-text-primary); } .v2-lang-switch__menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 132px; margin: 0; padding: 6px; list-style: none; background: var(--v2-surface); border: 1px solid var(--v2-border); border-radius: 10px; box-shadow: var(--v2-shadow-md, 0 8px 24px rgba(16, 24, 40, 0.12)); } .v2-lang-switch__item { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 14px; min-height: 34px; padding: 8px 12px; border-radius: 7px; color: var(--v2-text-secondary); font-size: 13px; text-align: left; transition: background 80ms ease, color 80ms ease; } .v2-lang-switch__item:hover { background: var(--v2-bg-subtle); color: var(--v2-text-primary); } .v2-lang-switch__item--active { color: var(--v2-accent-text); font-weight: 600; } .v2-lang-switch__check { font-size: 11px; } /* ---------- Shared filter grammar ---------- */ .v2-root button:focus-visible, .v2-root input:focus-visible, .v2-root textarea:focus-visible, .v2-root a:focus-visible { outline: none; box-shadow: var(--v2-focus-ring); } /* ---------- Layout shell ---------- */ /* Authenticated v2 routes reserve a compact shell row for the email verification reminder. The inner shell/panes must use the remaining space, not 100vh, or the composer and feature-page footers would clip. */ .v2-authenticated-shell { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); height: 100vh; min-height: 0; overflow: hidden; } .v2-authenticated-shell__content { /* Row 2 always, not by auto-placement. Row 1 is the `auto` row the banner sits in, so a verified user (no banner) has the content as the FIRST grid child and auto-placement put it in that row instead: the inset card then sized to its own content and any page shorter than the viewport ended 178-195px above the bottom gutter instead of 14 (TASK-157, ux-lead at 1200). Row 1 collapses to 0 with nothing in it, which is the same layout the banner variant already produced. */ grid-row: 2; min-width: 0; min-height: 0; overflow: hidden; } .v2-authenticated-shell__content > .v2-shell, .v2-authenticated-shell__content .v2-pane { height: 100%; } .v2-verification-banner { box-sizing: border-box; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "content resend dismiss"; align-items: center; gap: 8px; min-width: 0; min-height: 40px; padding: 4px 16px; border-bottom: 1px solid var(--v2-border-soft); background: var(--v2-warning-soft); color: var(--v2-text-primary); font-size: 14px; line-height: 20px; } .v2-verification-banner__content { grid-area: content; min-width: 0; } .v2-verification-banner__text { display: block; overflow-wrap: anywhere; } /* Resend feedback stays in the visible status reminder so a sighted person receives the same confirmation as assistive technology. */ .v2-verification-banner__notice { display: block; margin-top: 2px; color: var(--v2-text-secondary); font-size: 13px; line-height: 18px; } .v2-root button.v2-verification-banner__resend, .v2-root button.v2-verification-banner__dismiss { flex: none; min-height: 44px; border: 0; background: transparent; color: var(--v2-accent-text); font: inherit; font-weight: 650; cursor: pointer; } .v2-root button.v2-verification-banner__resend { grid-area: resend; padding: 4px 4px; } .v2-root button.v2-verification-banner__dismiss { grid-area: dismiss; display: grid; width: 44px; height: 44px; padding: 0; place-items: center; color: var(--v2-text-secondary); } .v2-root button.v2-verification-banner__resend:disabled { cursor: progress; opacity: 0.7; } .v2-shell { display: grid; grid-template-columns: var(--v2-rail-w) var(--v2-pods-w) 1fr var(--v2-inspector-w); grid-template-rows: 1fr; height: 100vh; width: 100%; background: var(--v2-shell-bg); } .v2-shell--no-inspector { grid-template-columns: var(--v2-rail-w) var(--v2-pods-w) 1fr; } .v2-shell--feature { grid-template-columns: var(--v2-rail-w) var(--v2-pods-w) minmax(0, 1fr); } .v2-shell--feature-wide { grid-template-columns: var(--v2-rail-w) minmax(0, 1fr); } .v2-pane { height: 100vh; overflow: hidden; display: flex; flex-direction: column; border-right: 1px solid transparent; /* the content card's ring replaces the pane hairlines */ background: var(--v2-shell-bg); } .v2-pane--rail { background: var(--v2-shell-bg); /* The 56px rail lets the language menu, attention badge and nav hover labels escape the pane instead of clipping them at its edge. */ overflow: visible; } /* The content pane is the inset card: 8px gutter top/right/bottom, 1px ring, 14px radius (TASK-122). Height subtracts the gutters; the banner variant beats `.v2-authenticated-shell__content .v2-pane { height: 100% }` on specificity. */ .v2-pane--main { margin: 8px 8px 8px 0; height: calc(100vh - 16px); background: var(--v2-surface); border: 1px solid var(--v2-border); border-radius: var(--v2-content-radius); overflow: hidden; } .v2-authenticated-shell .v2-pane--main { height: calc(100% - 16px); } /* Inspector open: the card meets the inspector gutter. --feature-wide has no inspector column, so it keeps its right gutter. */ .v2-shell:not(.v2-shell--no-inspector):not(.v2-shell--feature):not(.v2-shell--feature-wide) .v2-pane--main { margin-right: 0; } .v2-inspector-backdrop { display: none; } /* ---------- Nav rail ---------- */ .v2-rail { height: 100%; /* 56px rail - 1px pane border - 2 x 8px = 39px, room for the 38px nav glyph. At 10px the nav was 35px wide and scrolled sideways by 2px, which the staging smoke's "rail no h-scroll @1440px" check caught after #3139. */ padding: 6px 8px; display: flex; flex-direction: column; gap: 8px; justify-content: space-between; } /* Bottom of the rail: a help/preferences cluster (language · guide · feedback) sits above a divider, then the account cluster (avatar · sign out). */ .v2-rail__foot { display: flex; flex-direction: column; align-items: center; gap: 10px; } .v2-rail__utility { display: flex; flex-direction: column; align-items: center; gap: 8px; } /* The language switch left the rail for Settings › Look (D215, TASK-691). */ .v2-rail__brand { display: flex; align-items: center; justify-content: center; gap: 0; font-weight: 700; font-size: 17px; padding: 0; margin-bottom: 8px; color: var(--v2-text-primary); letter-spacing: -0.02em; height: 36px; } .v2-rail__brand > span:not(.v2-rail__brand-icon) { display: none; } /* D223 (TASK-735): the artboard's .logo, a 36px cobalt key. */ .v2-rail__brand-icon { width: 36px; height: 36px; border-radius: var(--v2-r-10, 10px); background: var(--v2-accent); background: linear-gradient(180deg, var(--v2-accent-fill-a, var(--v2-accent)), var(--v2-accent-fill-b, var(--v2-accent))); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 6px 12px -6px rgba(29, 63, 209, 0.5); color: #fff; display: grid; place-items: center; font-weight: 800; font-size: 14px; } .v2-rail__nav { display: flex; flex: 1; flex-direction: column; gap: 8px; min-height: 0; overflow: visible; padding-right: 0; } /* Below the rail's fixed chrome (245px) plus six nav items (268px), scroll the nav vertically while keeping its horizontal overflow hidden. */ @media (max-height: 460px) { .v2-rail__nav { overflow-y: auto; overflow-x: hidden; } } .v2-root button.v2-rail__item { position: relative; display: flex; align-items: center; justify-content: center; gap: 0; /* D223 (TASK-735): the artboard's .nav, a bare 38px glyph at rest, coloured by --v2-rail-icon: the nearest grey to the artboard's that clears 3:1 on the shell (values and ratios at the token). */ width: 38px; min-height: 38px; height: 38px; align-self: center; padding: 0; border-radius: var(--v2-r-10, 10px); font-size: 14px; font-weight: 600; color: var(--v2-rail-icon); background: transparent; text-align: left; transition: background 80ms ease; } .v2-rail__item:hover::after, .v2-rail__item:focus-visible::after { content: attr(data-label); position: absolute; left: calc(100% + 8px); top: 50%; transform: translateY(-50%); z-index: 10; padding: 5px 8px; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-sm); background: var(--v2-surface); color: var(--v2-text-primary); font-size: 12px; font-weight: 600; white-space: nowrap; } .v2-root button.v2-rail__item:hover { background: var(--v2-surface-hover); color: var(--v2-text-primary); } .v2-root button.v2-rail__item--active { background: var(--v2-ink); background: linear-gradient(180deg, var(--v2-fill-a, var(--v2-ink)), var(--v2-fill-b, var(--v2-ink))); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 4px 10px -3px rgba(16, 24, 40, 0.3); color: var(--v2-on-fill, var(--v2-on-ink)); font-weight: 600; } .v2-rail__item-icon { width: 20px; height: 20px; display: grid; place-items: center; flex-shrink: 0; } .v2-rail__item-label { display: none; } .v2-rail__divider { width: 24px; height: 1px; margin: 6px auto; background: var(--v2-border); } .v2-rail__user { min-height: 48px; padding: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; border-top: none; } .v2-rail__user > div { display: none; } .v2-rail__feedback, .v2-rail__account { width: 24px; height: 24px; display: grid; place-items: center; border-radius: var(--v2-radius-sm); color: var(--v2-text-tertiary); background: transparent; border: none; cursor: pointer; transition: background 80ms ease, color 80ms ease; } .v2-rail__feedback:hover, .v2-rail__feedback[aria-expanded="true"], .v2-rail__account:hover, .v2-rail__account[aria-expanded="true"] { background: var(--v2-surface-hover); color: var(--v2-text-primary); } .v2-rail__feedback svg { width: 18px; height: 18px; } .v2-rail__account { width: 36px; height: 36px; padding: 0; } /* The artboard's .av--36: a 36px square, 9px corners, a hairline ring. */ .v2-rail__account .v2-avatar { width: 36px; height: 36px; border: 0; border-radius: var(--v2-r-9, 9px); box-shadow: 0 0 0 1px var(--v2-av-ring, var(--v2-border-soft)); pointer-events: none; } .v2-root .v2-account-menu__popover.MuiPaper-root { min-width: 190px; margin-left: 8px; overflow: hidden; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-lg); background: var(--v2-surface); color: var(--v2-text-primary); box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12); } .v2-account-menu__list { display: flex; flex-direction: column; gap: 2px; padding: 6px; } .v2-root button.v2-account-menu__item { display: flex; align-items: center; gap: 10px; min-height: 38px; padding: 8px 10px; border: 0; border-radius: var(--v2-radius-sm); background: transparent; color: var(--v2-text-secondary); font-size: 13px; font-weight: 600; text-align: left; cursor: pointer; } .v2-root button.v2-account-menu__item:hover, .v2-root button.v2-account-menu__item:focus-visible { background: var(--v2-surface-hover); color: var(--v2-text-primary); outline: none; } .v2-root button.v2-account-menu__item:focus-visible { box-shadow: var(--v2-focus-ring); } .v2-account-menu__item svg { width: 18px; height: 18px; color: var(--v2-text-tertiary); } .v2-root .v2-feedback-menu__popover.MuiPaper-root { min-width: 210px; margin-left: 8px; overflow: hidden; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-lg); background: var(--v2-surface); color: var(--v2-text-primary); box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12); } .v2-feedback-menu__list { display: flex; flex-direction: column; gap: 2px; padding: 6px; } .v2-feedback-menu__item { display: flex; align-items: center; gap: 10px; min-height: 38px; padding: 8px 10px; border-radius: var(--v2-radius-sm); color: var(--v2-text-secondary); font-size: 13px; font-weight: 600; transition: background 80ms ease, color 80ms ease; } .v2-feedback-menu__item:hover, .v2-feedback-menu__item:focus-visible { background: var(--v2-surface-hover); color: var(--v2-text-primary); outline: none; } .v2-feedback-menu__item:focus-visible { box-shadow: var(--v2-focus-ring); } .v2-feedback-menu__item svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--v2-text-tertiary); } /* ---------- Pods sidebar ---------- */ .v2-pods { display: flex; flex-direction: column; height: 100%; padding: 24px 16px; } .v2-pods__create { display: flex; flex-direction: column; gap: 7px; padding: 10px; border: 1px solid var(--v2-border); border-radius: var(--v2-radius); background: var(--v2-surface); margin-bottom: 14px; } /* Who can join is separate from public visibility. The default remains open; * invite-only is needed before a private pod can hold an outsider-writable * connector grant. These are radios rather than the old option buttons whose * styling lost to the global button reset (#870). */ .v2-pods__join-policy { display: flex; min-width: 0; flex-direction: column; gap: 6px; margin: 3px 0; padding: 0; border: 0; color: var(--v2-text-secondary); font: 400 12px/16px var(--v2-font); } .v2-pods__join-policy legend { margin-bottom: 6px; color: var(--v2-text-primary); font-weight: 600; } .v2-pods__join-policy label { display: flex; align-items: center; gap: 8px; min-height: 40px; cursor: pointer; } /* Native radios ignore border tokens, so draw the outline from one. Checked, the border thickens into the accent dot, so no cobalt background is added. */ .v2-pods__join-policy input { appearance: none; width: 14px; height: 14px; margin: 0; flex-shrink: 0; border: 1.5px solid var(--v2-control-outline); border-radius: 50%; background: var(--v2-surface); cursor: pointer; } .v2-pods__join-policy input:checked { border: 4px solid var(--v2-accent); } .v2-pods__join-policy-hint { margin: 0; color: var(--v2-text-secondary); font: 400 var(--v2-fs-meta)/16px var(--v2-font); } .v2-pods__create-input { width: 100%; padding: 8px 9px; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-sm); background: var(--v2-surface); font-size: 12px; outline: none; } .v2-pods__create-input:focus { border-color: var(--v2-accent); } .v2-pods__create-error { font-size: 11px; color: var(--v2-danger); } .v2-pods__create-actions { display: flex; justify-content: flex-end; gap: 8px; } .v2-root button.v2-pods__create-cancel, .v2-root button.v2-pods__create-submit { padding: 6px 9px; border-radius: var(--v2-radius-sm); font-size: 12px; font-weight: 700; } .v2-root button.v2-pods__create-cancel { color: var(--v2-text-tertiary); } .v2-root button.v2-pods__create-submit { background: var(--v2-ink); color: var(--v2-on-ink); } .v2-root button.v2-pods__create-submit:disabled { background: var(--v2-border-strong); cursor: not-allowed; } @media (max-width: 760px) { .v2-root button.v2-pods__create-cancel, .v2-root button.v2-pods__create-submit { min-height: 40px; } } /* Must sit AFTER the base chip rule: both this and the shared .v2-root button.v2-filter-segment__item--active rule weigh 0-2-1, so source order decides — with the base chip rule later in the file, the selected chip shipped grey-on-blue (measured live 2026-08-23). */ .v2-pods__list { flex: 1; overflow-y: auto; padding: 0; } /* Title-row sibling — sits to the right of the pod name. Slack/iMessage pattern: the timestamp lives on the same line as the title so the second line is reserved entirely for the message preview. */ .v2-pods__empty { padding: 32px 16px; text-align: center; color: var(--v2-text-tertiary); font-size: 13px; } /* First-run is a shell-level dialog. The overlay separates the one-time setup path from pod content, while the dialog itself owns overflow on short mobile viewports so every step and the dismissal control stay reachable. */ .v2-first-run__overlay { position: fixed; inset: 0; z-index: 120; display: grid; place-items: center; overflow-y: auto; padding: 24px; background: var(--v2-scrim); } /* D215 .v2-first-run: the pop surface at 16px, its edge carried by the pop shadow rather than a border. */ .v2-first-run { width: min(720px, 100%); max-height: calc(100dvh - 48px); overflow-y: auto; padding: 28px; border-radius: 16px; background: var(--v2-surface); color: var(--v2-text-primary); box-shadow: var(--v2-shadow-pop); outline: none; } .v2-first-run__eyebrow { margin-bottom: 8px; color: var(--v2-text-muted); font: 500 11.5px/16px var(--v2-font-mono); } .v2-first-run__heading-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; } .v2-first-run__title { margin: 0; color: var(--v2-text-primary); font: 700 28px/1.15 var(--v2-font-display); letter-spacing: -0.025em; /* Two even lines on a phone, not a lone last word. */ text-wrap: balance; } .v2-first-run__lede { max-width: 560px; margin: 10px 0 0; color: var(--v2-text-secondary); font-size: 14px; line-height: 1.55; } .v2-root button.v2-first-run__skip { flex: none; display: inline-flex; align-items: center; height: 32px; padding: 0 10px; border-radius: var(--v2-r-9); color: var(--v2-text-secondary); font: 600 12.5px/1 var(--v2-font); } .v2-root button.v2-first-run__skip:hover { background: var(--v2-surface-tint); color: var(--v2-text-primary); } .v2-first-run__steps { display: grid; gap: 10px; margin: 22px 0 0; padding: 0; list-style: none; } .v2-first-run__step { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 12px; align-items: start; padding: 14px; border-radius: var(--v2-r-12); background: var(--v2-bg-subtle); box-shadow: inset 0 0 0 1px var(--v2-border-soft); } .v2-first-run__step-mark { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--v2-surface); box-shadow: inset 0 0 0 1px var(--v2-border); color: var(--v2-text-secondary); font: 600 12px/1 var(--v2-font-mono); } .v2-first-run__step-mark--done { background: var(--v2-accent-soft); box-shadow: inset 0 0 0 1px var(--v2-accent); color: var(--v2-accent-text); } .v2-first-run__step-body { display: flex; min-width: 0; flex-direction: column; align-items: flex-start; gap: 5px; color: var(--v2-text-secondary); font-size: 13px; line-height: 1.45; } .v2-first-run__step-body strong { color: var(--v2-text-primary); font-size: 14px; font-weight: 600; } /* D215 keys: setup is the key material, hello the one filled action (TASK-647 item 4: the card keeps one ink action). */ .v2-root button.v2-first-run__hello, .v2-root a.v2-first-run__setup { display: inline-flex; align-items: center; gap: 7px; height: 32px; margin-top: 6px; padding: 0 13px; border-radius: var(--v2-r-10); font: 600 13px/1 var(--v2-font); text-decoration: none; } .v2-root a.v2-first-run__setup { border: 1px solid var(--v2-key-edge); background: linear-gradient(180deg, var(--v2-key-a), var(--v2-key-b)); box-shadow: var(--v2-key-inset); color: var(--v2-text-primary); } .v2-root a.v2-first-run__setup:hover { border-color: var(--v2-border-strong); } .v2-root button.v2-first-run__hello { border: 1px solid var(--v2-fill-edge); background: linear-gradient(180deg, var(--v2-fill-a), var(--v2-fill-b)); color: var(--v2-on-fill); } .v2-root button.v2-first-run__hello:hover:not(:disabled) { background: var(--v2-ink-hover); } /* Disabled before the agent connects, as drawn; busy while the room opens. */ .v2-root button.v2-first-run__hello:disabled { cursor: default; opacity: 0.45; } .v2-root button.v2-first-run__hello[aria-busy="true"] { cursor: wait; opacity: 0.65; } .v2-first-run__waiting, .v2-first-run__connected, .v2-first-run__error { display: inline-flex; align-items: center; gap: 8px; } /* The waiting dot is the presence dot: cobalt, with the shared pulse. */ .v2-first-run__dot { position: relative; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--v2-accent); } .v2-first-run__dot::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--v2-accent); opacity: 0; animation: v2-presence-pulse var(--motion-pulse) var(--motion-ease-state) infinite; } @media (prefers-reduced-motion: reduce) { .v2-first-run__dot::after { animation: none; } } .v2-first-run__connected { color: var(--v2-accent-text); font-weight: 600; } .v2-first-run__error { color: var(--v2-text-primary); font-size: 12.5px; } .v2-first-run__error svg { flex: none; color: var(--v2-danger); } @keyframes v2-typing-dot { 0%, 60%, 100% { transform: translateY(0); opacity: 0.35; } 30% { transform: translateY(-3px); opacity: 1; } } /* ---------- Message bubble ---------- */ .v2-msg { display: grid; grid-template-columns: 38px minmax(0, 1fr); align-items: start; gap: 12px; padding: 10px 0; } /* The message action cluster (taste rulings 2026-08-23): Reply, Thread, and the reaction trigger share ONE floating pill at the row's top-right, identical for headed and grouped rows. Absolute + hover means zero layout shift; icon buttons instead of text keep it quiet at rest and compact under the cursor. The reaction picker anchors to the cluster so the trigger sits with its siblings instead of orphaned in an empty band below the message (the old absolute "+"). */ .v2-msg { position: relative; } /* ---------- Inspector ---------- */ .v2-inspector { height: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 0; position: relative; } .v2-inspector { display: flex; flex-direction: column; height: 100%; } /* Danger zone — destructive actions (delete pod, etc.) live in the Manage tab inside this red-accent card with proper warning chrome. */ @keyframes v2-now-pulse { 0% { box-shadow: 0 0 0 0 var(--v2-success-ring); } 70% { box-shadow: 0 0 0 6px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } } /* ---------- Inspector: Members ---------- */ /* ---------- Your Team category tab bar ---------- */ /* ---------- Invite modal + redeem page ---------- */ .v2-modal__overlay { position: fixed; inset: 0; z-index: 100; background: rgba(15, 23, 42, 0.42); display: flex; align-items: center; justify-content: center; padding: 24px; } .v2-modal { width: min(560px, 100%); max-height: calc(100vh - 48px); overflow: auto; background: var(--v2-surface); border: 1px solid var(--v2-border-soft); border-radius: var(--v2-radius-md, 12px); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18); display: flex; flex-direction: column; } .v2-modal__head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 12px; border-bottom: 1px solid var(--v2-border-soft); } .v2-modal__title { font-size: 14px; font-weight: 700; color: var(--v2-text-primary); } .v2-root button.v2-modal__close, .v2-modal__close { width: 28px; height: 28px; border-radius: 6px; border: none; background: transparent; color: var(--v2-text-secondary); font-size: 20px; line-height: 1; cursor: pointer; } .v2-root button.v2-modal__close:hover, .v2-modal__close:hover { background: var(--v2-surface-hover); color: var(--v2-text-primary); } .v2-modal__tabs { display: flex; gap: 0; padding: 0 18px; border-bottom: 1px solid var(--v2-border-soft); } .v2-root button.v2-modal__tab, .v2-modal__tab { height: 36px; padding: 0 14px; border: none; background: transparent; color: var(--v2-text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; } .v2-root button.v2-modal__tab:hover, .v2-modal__tab:hover { color: var(--v2-text-primary); } .v2-root button.v2-modal__tab--active, .v2-modal__tab--active { color: var(--v2-accent); border-bottom-color: var(--v2-accent); } .v2-modal__body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 12px; } .v2-modal__hint { font-size: 12px; color: var(--v2-text-secondary); margin: 0; line-height: 1.5; } .v2-modal__error { font-size: 11px; color: var(--v2-danger); margin-top: 4px; } .v2-modal__section-title { font-size: 12px; font-weight: 700; color: var(--v2-text-primary); } .v2-invite-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; } .v2-invite-options__field { display: flex; flex-direction: column; gap: 5px; min-width: 0; font-size: 11px; font-weight: 600; color: var(--v2-text-secondary); } .v2-invite-options__select { width: 100%; height: 36px; padding: 0 10px; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-sm); background: var(--v2-surface); color: var(--v2-text-primary); font: inherit; font-weight: 500; outline: none; } .v2-invite-options__select:focus { border-color: var(--v2-accent); box-shadow: var(--v2-focus-ring); } .v2-invite-link-row { display: flex; gap: 6px; align-items: stretch; } /* The Copy button inside the invite-link row inherits `width: 100%` from * the base `.v2-invite-card__cta` rule (which is meant for full-width * primary CTAs). Inside the row it needs to stay sized to its content so * the URL input gets the remaining flex space. Without this override the * input collapses to ~20px and the URL is invisible. */ .v2-root .v2-invite-link-row button.v2-invite-card__cta, .v2-invite-link-row .v2-invite-card__cta { width: auto; flex-shrink: 0; margin-top: 0; padding: 0 12px; } .v2-invite-link { flex: 1; min-width: 0; padding: 8px 10px; border: 1px solid var(--v2-border-soft); border-radius: var(--v2-radius-sm); background: var(--v2-bg-subtle); font-size: 11px; color: var(--v2-text-secondary); font-family: ui-monospace, SFMono-Regular, monospace; outline: none; } .v2-invite-link:focus { border-color: var(--v2-accent); } .v2-invite-manage { display: flex; flex-direction: column; gap: 8px; padding-top: 14px; margin-top: 4px; border-top: 1px solid var(--v2-border-soft); } .v2-invite-manage__empty { padding: 12px; border: 1px dashed var(--v2-border); border-radius: var(--v2-radius-sm); color: var(--v2-text-tertiary); font-size: 11px; text-align: center; } .v2-invite-manage__list { display: flex; flex-direction: column; gap: 8px; } .v2-invite-manage__item { min-width: 0; padding: 10px; border: 1px solid var(--v2-border-soft); border-radius: var(--v2-radius-sm); background: var(--v2-surface); } .v2-invite-manage__summary, .v2-invite-manage__meta { display: flex; justify-content: space-between; gap: 8px; min-width: 0; } .v2-invite-manage__summary { align-items: baseline; font-size: 11px; color: var(--v2-text-tertiary); } .v2-invite-manage__summary strong { overflow: hidden; color: var(--v2-text-primary); font-weight: 650; text-overflow: ellipsis; white-space: nowrap; } .v2-invite-manage__meta { flex-wrap: wrap; margin-top: 3px; color: var(--v2-text-secondary); font-size: 10px; } .v2-invite-manage__url { box-sizing: border-box; width: 100%; min-width: 0; margin-top: 8px; padding: 7px 9px; border: 1px solid var(--v2-border-soft); border-radius: 6px; background: var(--v2-bg-subtle); color: var(--v2-text-secondary); font-family: var(--v2-font-mono); font-size: 10px; outline: none; } .v2-invite-manage__url:focus { border-color: var(--v2-accent); } .v2-invite-manage__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; margin-top: 8px; } .v2-root button.v2-invite-manage__action, .v2-invite-manage__action { min-width: 0; height: 30px; border: 1px solid var(--v2-border); border-radius: 6px; background: var(--v2-surface); color: var(--v2-text-primary); font-size: 11px; font-weight: 600; cursor: pointer; } .v2-root button.v2-invite-manage__action:hover, .v2-invite-manage__action:hover { background: var(--v2-surface-hover); } .v2-root button.v2-invite-manage__action--danger, .v2-invite-manage__action--danger { border-color: var(--v2-danger-soft); color: var(--v2-danger); } .v2-root button.v2-invite-manage__action--danger:hover, .v2-invite-manage__action--danger:hover { background: var(--v2-danger-soft); } .v2-root button.v2-invite-manage__action:disabled, .v2-invite-manage__action:disabled { opacity: 0.6; cursor: not-allowed; } @media (max-width: 480px) { .v2-verification-banner { grid-template-columns: minmax(0, 1fr) 44px; grid-template-areas: "content dismiss" "resend resend"; align-items: start; gap: 6px 8px; min-height: 0; padding: 8px 12px; font-size: 16px; } .v2-root button.v2-verification-banner__resend { justify-self: start; } /* Auth is a primary phone flow: 16px prevents Safari's focus zoom and a 44px target keeps signup usable with a thumb. */ .v2-login__input { min-height: 44px; font-size: 16px; } .v2-root button.v2-login__submit { min-height: 44px; font-size: 16px; } .v2-modal__overlay { padding: 12px; } .v2-invite-options { grid-template-columns: minmax(0, 1fr); } .v2-invite-link-row { flex-wrap: wrap; } .v2-root .v2-invite-link-row button.v2-invite-card__cta, .v2-invite-link-row .v2-invite-card__cta { width: 100%; height: 34px; } } .v2-root button.v2-invite-card__cta, .v2-invite-card__cta { width: 100%; height: 38px; border: none; border-radius: var(--v2-radius-sm); background: var(--v2-accent-fill); color: var(--v2-on-accent); font-size: 13px; font-weight: 600; cursor: pointer; margin-top: 8px; } .v2-root button.v2-invite-card__cta:hover, .v2-invite-card__cta:hover { filter: brightness(1.05); } .v2-root button.v2-invite-card__cta:disabled, .v2-invite-card__cta:disabled { opacity: 0.6; cursor: not-allowed; } .v2-root button.v2-invite-card__cta--secondary, .v2-invite-card__cta--secondary { background: var(--v2-surface); color: var(--v2-text-primary); border: 1px solid var(--v2-border-soft); } .v2-root button.v2-invite-card__cta--secondary:hover, .v2-invite-card__cta--secondary:hover { background: var(--v2-bg-subtle); } /* ---------- Avatar ---------- */ .v2-avatar { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; color: #fff; font-weight: 650; font-size: 11px; flex-shrink: 0; border: 2px solid var(--v2-surface); text-transform: uppercase; position: relative; /* Two initials at 700 with default tracking collide at 24px — the pair reads as one glyph. 650 with positive tracking separates them without making the avatar shout; the optical centre then sits ~0.5px right, so the tracking is compensated with an equal indent rather than left as a visible lean. */ letter-spacing: 0.04em; text-indent: 0.04em; /* The seeded gradient is the identity; a flat inner highlight over it reads as a lit sphere rather than a filled circle, and costs nothing. */ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 -1px 0 rgba(0, 0, 0, 0.10); overflow: hidden; } /* A photo is the identity when one exists — the gradient underneath is only a backing plate for transparent PNGs, so the inner highlight must not sit on top of someone's face. */ .v2-avatar:has(img) { box-shadow: none; } /* Character tier gets a hairline ring (TASK-679). The compact transcript frame (--flat) stays unframed; the :has(img) rule above outranks a plain class, so the selector repeats it to win. */ .v2-avatar:has(img).v2-avatar--character:not(.v2-avatar--flat) { box-shadow: 0 0 0 1px var(--v2-avatar-frame); } .v2-avatar--lg { width: 34px; height: 34px; font-size: 12px; } .v2-avatar--md { width: 30px; height: 30px; font-size: 11px; } .v2-avatar--sm { width: 24px; height: 24px; font-size: 10px; } .v2-avatar__online { position: absolute; right: -1px; bottom: -1px; width: 9px; height: 9px; border-radius: 50%; background: var(--v2-success); border: 2px solid var(--v2-surface); } /* ---------- Login ---------- */ /* The auth page is its own scroll container: .v2-root and html hide overflow, so a card taller than the window (a short laptop, a phone with the keyboard up) would otherwise lose its last rows. The card centres with auto margins, which fall to zero when it overflows; place-items: center would push its top out of reach instead. */ .v2-login { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; padding: 24px; background: var(--v2-page-bg); } /* The d215 auth card: the pane surface on the shell ground (every auth page shares it; sign-in, sign-up, reset and forgot frames all draw .authcard). */ .v2-login__card { width: 100%; max-width: 400px; margin: auto; background: var(--v2-surface); border-radius: 14px; padding: 32px; box-shadow: var(--v2-shadow-pane); } /* Each auth card stacks its rows on one 16px rhythm, as drawn. A form inside the card (invite-required has two) lends its rows to that grid. */ .v2-login__card--stack { display: grid; /* minmax(0, 1fr): one long unbroken word (a pod description, a URL) wraps inside the card instead of widening the grid column past it. */ grid-template-columns: minmax(0, 1fr); gap: 16px; } .v2-login__card--stack > form { display: contents; } .v2-login__card--stack .v2-login__brand, .v2-login__card--stack .v2-login__field, .v2-login__card--stack .v2-login__or, .v2-login__card--stack .v2-login__oauth, .v2-login__card--stack .v2-login__notice, .v2-login__card--stack .v2-login__subtitle, .v2-login__card--stack .v2-login__hint, .v2-login__card--stack .v2-login__legal, .v2-login__card--stack .v2-login__field-hint { margin: 0; } /* The one prose line a page keeps (the frames' .sub), and a plain hint under it: no box on a lifted card. */ .v2-login__card--stack .v2-login__subtitle { font-size: 13.5px; line-height: 1.5; } .v2-login__card--stack .v2-login__hint { padding: 0; border-radius: 0; background: none; font-size: 13px; line-height: 1.5; color: var(--v2-text-secondary); } .v2-login__card--stack .v2-login__title { margin: 4px 0 0; } .v2-login__card--stack .v2-login__oauth { gap: 16px; } .v2-root .v2-login__card--stack button.v2-login__submit { margin-top: 0; /* A key can carry a pod name ("Join