@import "tailwindcss"; /* * Sibling packages that build on these utilities. * * They are reached through workspace symlinks, which Tailwind's automatic * content detection skips — without these the classes in the editor and the * questionnaire views resolve to nothing at all, silently. * `.ts` is included because shared class lists can live outside components. */ @source "../../../packages/editor/src/**/*.{ts,tsx}"; @source "../../../packages/question/src/**/*.{ts,tsx}"; /* The complete light-only system. `static` keeps tokens read from plain CSS. */ @theme static { --color-*: initial; --color-transparent: transparent; --color-current: currentColor; --color-white: oklch(1 0 0); /* Warm olive neutrals. The lightest steps are deliberately close, not aliases. */ --color-gray-50: oklch(0.99051 0.00263 95); /* #fcfcfa */ --color-gray-100: oklch(0.97857 0.00264 95); /* #f8f8f6 */ --color-gray-150: oklch(0.96623 0.0039 95); /* #f4f4f1 */ --color-gray-200: oklch(0.94216 0.00393 95); /* #ecece9 */ --color-gray-300: oklch(0.89108 0.00552 95); /* #dcdbd7 */ --color-gray-400: oklch(0.74029 0.00856 95); /* #acaba5 */ --color-gray-500: oklch(0.56514 0.0123 95); /* #78766e */ --color-gray-600: oklch(0.48124 0.01278 95); /* #605e56 */ --color-gray-700: oklch(0.39036 0.01026 95); /* #47453f */ --color-gray-750: oklch(0.32036 0.00877 95); /* #34332e */ --color-gray-800: oklch(0.25993 0.00745 95); /* #252420 */ --color-gray-850: oklch(0.20017 0.00605 95); /* #171613 */ --color-gray-900: oklch(0.15908 0.00637 95); /* #0e0d0a */ --color-gray-950: oklch(0.09862 0.00629 95); /* #040302 */ /* Surfaces: white is reserved for the document and things lifted above it. */ --color-page: oklch(1 0 0); --color-ground: var(--color-gray-150); --color-hover: var(--color-gray-100); --color-inset: var(--color-gray-100); --color-selected: var(--color-gray-200); --color-control: var(--color-gray-200); --color-control-hover: var(--color-gray-300); --color-text-primary: var(--color-gray-900); --color-text-secondary: var(--color-gray-700); --color-text-tertiary: var(--color-gray-600); --color-text-quaternary: var(--color-gray-500); --color-conversation-body: #3e453b; --color-conversation-pane: color-mix(in srgb, var(--color-ground) 30%, var(--color-page)); /* Petrol is the only accent: action, focus, presence and links all agree. */ --color-brand: oklch(0.50006 0.08514 210.06); /* #06707e */ --color-brand-hover: oklch(0.42914 0.07315 210.634); /* #045a66 */ --color-brand-active: oklch(0.37546 0.06382 210.435); /* #034a54 */ --color-brand-wash: oklch(0.95547 0.0277 207.843); /* #dcf6fa */ --color-brand-ink: oklch(0.43882 0.07476 210.193); /* #045d69 */ --focus-ring-color: var(--color-brand); --focus-ring-width: 2px; --focus-ring-offset: 2px; --color-success: oklch(0.51958 0.10966 145.072); /* #3b793f */ --color-success-wash: oklch(0.95503 0.03552 145.29); /* #e2f7e2 */ --color-success-ink: oklch(0.44098 0.11068 144.754); /* #236228 */ --color-warning: oklch(0.59898 0.12586 74.986); /* #ab7302 */ --color-warning-wash: oklch(0.95386 0.03439 74.416); /* #feedd7 */ --color-warning-ink: oklch(0.4409 0.0921 74.93); /* #704a02 */ --color-destructive: oklch(0.60513 0.17178 24.175); /* #d54d4c */ --color-destructive-hover: oklch(0.56972 0.16045 24.136); /* #c44746 */ --color-destructive-active: oklch(0.53376 0.1489 24.091); /* #b34140 */ --color-destructive-wash: oklch(0.94841 0.02583 29.416); /* #ffe8e4 */ --color-destructive-ink: oklch(0.4941 0.19046 25.161); /* #b51421 */ /* Passive structure and controls are the only two edge weights. */ --color-edge: rgb(0 0 0 / 7%); --color-control-edge: rgb(0 0 0 / 20%); --color-control-boundary: var(--color-gray-500); --edge-width: 1px; /* * Chrome, conversation, prose, section and title. Colour carries hierarchy * inside the chrome rather than two more sizes below the legibility floor. */ --text-*: initial; --text-sm: 0.8125rem; /* 13px — all chrome, chat and comment bodies */ --text-sm--line-height: 1.25rem; --text-base: 0.9375rem; /* 15px — document prose */ --text-base--line-height: 1.375rem; --text-lg: 1.0625rem; /* 17px — document h3 subheadings */ --text-lg--line-height: 1.6875rem; --text-xl: 1.5rem; /* 24px — section headings and dialogs */ --text-xl--line-height: 1.875rem; --text-2xl: 2rem; /* 32px — plan title */ --text-2xl--line-height: 2.375rem; /* Tailwind's multiplier keeps 4px steps and admits 2, 6, 10 and 22px. */ --spacing: 0.25rem; /* * Tailwind's own values, declared here so the scale has one owner: a change * lands in one place, and a misspelt rung is caught rather than waved through. */ --radius-*: initial; --radius-sm: 0.25rem; /* 4px — buttons, grips, toolbar cells */ --radius-md: 0.375rem; /* 6px — cards, inputs, callouts, code blocks */ --radius-lg: 0.5rem; /* 8px — popovers and panels */ --radius-xl: 0.625rem; /* 10px — the document page */ /* * Warm-tinted depth, copied from the settled Figma shadow table. * * The tint is declared *after* the reset, and the order is load-bearing: * `--shadow-*: initial` clears the whole namespace, and `--shadow-color` is * in it. Declared above the reset it is wiped before it is ever emitted, so * every `rgb(var(--shadow-color) / n%)` below becomes an invalid * declaration and all three shadows compute to `none`. Nothing errors and * nothing is undefined at the source — the file still reads correctly, and * `check-tokens.ts` now refuses this rather than reading the declaration * that is textually there. */ --shadow-*: initial; --shadow-color: 14 13 10; --shadow-resting: 0 0 1px rgb(var(--shadow-color) / 3%), 0 1px 1px rgb(var(--shadow-color) / 3%), 0 3px 2px rgb(var(--shadow-color) / 2%); --shadow-raised: 0 1px 2px rgb(var(--shadow-color) / 6%), 0 3px 3px rgb(var(--shadow-color) / 5%), 0 8px 5px rgb(var(--shadow-color) / 3%), 0 14px 5px rgb(var(--shadow-color) / 1%); --shadow-overlay: 0 2px 3px rgb(var(--shadow-color) / 10%), 0 7px 6px rgb(var(--shadow-color) / 9%), 0 15px 8px rgb(var(--shadow-color) / 5%), 0 26px 10px rgb(var(--shadow-color) / 1%); /* * Leaves immediately and settles slowly, so a change reads as a response to * whatever caused it. Durations are named for the job: `fast` is feedback * under the pointer, `linger` is the agent's change marks fading unwatched. */ --ease-*: initial; --ease-out: cubic-bezier(0.2, 0, 0, 1); --duration-fast: 120ms; --duration-base: 200ms; --duration-linger: 600ms; /* Surface motion uses semantic names so 120ms control feedback stays fast. */ --motion-smooth-out: cubic-bezier(0.22, 1, 0.36, 1); --motion-move: cubic-bezier(0.65, 0, 0.35, 1); --dropdown-open-dur: 250ms; --dropdown-close-dur: 150ms; --dropdown-pre-scale: 0.97; --dropdown-closing-scale: 0.99; --dropdown-ease: var(--motion-smooth-out); --modal-open-dur: 250ms; --modal-close-dur: 150ms; --modal-scale: 0.96; --modal-scale-close: 0.96; --modal-ease: var(--motion-smooth-out); --sidebar-open-dur: 250ms; --sidebar-close-dur: 180ms; --drawer-open-dur: 250ms; --drawer-close-dur: 180ms; --drawer-distance: 8px; --panel-open-dur: 280ms; --panel-close-dur: 220ms; --panel-distance: 12px; --panel-blur: 2px; --page-slide-dur: 250ms; --page-slide-distance: 8px; --page-blur: 3px; --icon-swap-dur: 250ms; --icon-swap-blur: 2px; --badge-pop-dur: 500ms; --badge-close-dur: 180ms; --acc-expand: 250ms; --acc-collapse: 250ms; --toast-open: 280ms; --toast-close: 200ms; /* Retunes all fifteen `transition-*` utilities, none of which names a curve. */ --default-transition-duration: var(--duration-fast); --default-transition-timing-function: var(--ease-out); /* Inter is loaded in `main.tsx`; the editor reads this token rather than repeating the stack. */ --font-sans: "Inter Variable", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; } @utility ring-hairline { outline: var(--edge-width) solid var(--color-edge); outline-offset: calc(-1 * var(--edge-width)); } @utility control-edge { border: var(--edge-width) solid var(--color-control-edge); &:disabled { border-color: var(--color-edge); } } @utility btn { display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: var(--radius-md); font-size: var(--text-sm); font-weight: 500; &:disabled { background-color: var(--color-gray-200); color: var(--color-gray-600); cursor: default; } } @utility btn-md { height: 2rem; padding-inline: calc(var(--spacing) * 2); } @utility btn-sm { height: 1.5rem; padding-inline: var(--spacing); } @utility btn-icon { width: 1.75rem; height: 1.75rem; padding: 0; } @utility btn-primary { background-color: var(--color-brand); color: var(--color-page); &:hover:not(:disabled) { background-color: var(--color-brand-hover); } &:active:not(:disabled) { background-color: var(--color-brand-active); } } @utility btn-secondary { background-color: var(--color-gray-200); color: var(--color-text-primary); &:hover:not(:disabled) { background-color: var(--color-gray-300); } &:active:not(:disabled) { background-color: var(--color-gray-400); } } @utility btn-ghost { background-color: transparent; color: var(--color-text-secondary); &:hover:not(:disabled) { background-color: var(--color-gray-200); } &:active:not(:disabled) { background-color: var(--color-gray-300); } } @utility btn-destructive { background-color: var(--color-destructive); color: var(--color-page); &:hover:not(:disabled) { background-color: var(--color-destructive-hover); } &:active:not(:disabled) { background-color: var(--color-destructive-active); } } @utility field { border: var(--edge-width) solid var(--color-control-edge); border-radius: var(--radius-md); background-color: var(--color-page); color: var(--color-text-primary); &:disabled { border-color: var(--color-edge); background-color: var(--color-gray-200); color: var(--color-gray-600); } &[aria-invalid="true"] { border-color: var(--color-destructive); outline: 2px solid var(--color-destructive); outline-offset: 2px; } } @utility field-ghost { border: var(--edge-width) solid transparent; border-radius: var(--radius-md); background-color: transparent; color: var(--color-text-primary); &:hover:not(:disabled) { border-color: var(--color-control-edge); } &:disabled { border-color: var(--color-edge); background-color: var(--color-gray-200); color: var(--color-gray-600); } } @utility choice-control { appearance: none; border: var(--edge-width) solid var(--color-control-edge); background-color: var(--color-page); color: var(--color-text-primary); &[type="radio"] { border-radius: 9999px; } &[type="checkbox"] { border-radius: var(--radius-sm); } &:checked { border-color: var(--color-brand); background-color: var(--color-brand); } &[type="radio"]:checked { box-shadow: inset 0 0 0 3px var(--color-page); } &[type="checkbox"]:checked { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m3 7 2.5 2.5L11 4'/%3E%3C/svg%3E"); } &:disabled { border-color: var(--color-edge); background-color: var(--color-gray-200); color: var(--color-gray-600); } &:disabled:checked { background-color: var(--color-gray-600); } &[type="radio"]:disabled:checked { box-shadow: inset 0 0 0 3px var(--color-gray-200); } &[type="checkbox"]:disabled:checked { background-color: var(--color-gray-200); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='%23605e56' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m3 7 2.5 2.5L11 4'/%3E%3C/svg%3E"); } } /* Extra contrast is an opt-in accommodation, not the normal control edge. */ @media (prefers-contrast: more) { :where(.control-edge, .field, .choice-control):not([aria-invalid="true"]) { outline: var(--edge-width) solid var(--color-control-boundary); outline-offset: calc(-1 * var(--edge-width)); } :where(.control-edge, .field, .choice-control):disabled { outline-color: var(--color-edge); } } @utility hairline-t { border-top: var(--edge-width) solid var(--color-edge); } @utility hairline-r { border-right: var(--edge-width) solid var(--color-edge); } @utility hairline-b { border-bottom: var(--edge-width) solid var(--color-edge); } @utility hairline-l { border-left: var(--edge-width) solid var(--color-edge); } @media (min-resolution: 2dppx) { :root { --edge-width: 0.5px; } } html, body { height: 100%; overflow-x: hidden; } #root { position: fixed; left: var(--app-left, 0px); top: var(--app-top, 0px); width: var(--app-width, 100%); height: 100%; height: var(--app-height, 100dvh); } .motion-dropdown { opacity: 0; transform: scale(var(--dropdown-pre-scale)); transition: opacity var(--dropdown-open-dur) var(--dropdown-ease), transform var(--dropdown-open-dur) var(--dropdown-ease); will-change: opacity, transform; } .motion-dropdown.is-open { opacity: 1; transform: scale(1); } .motion-dropdown.is-closing { pointer-events: none; opacity: 0; transform: scale(var(--dropdown-closing-scale)); transition-duration: var(--dropdown-close-dur); } .motion-popover { opacity: 0; transform: scale(var(--dropdown-pre-scale)); transform-origin: var(--motion-origin-x, 50%) var(--motion-origin-y, 50%); transition: opacity var(--dropdown-open-dur) var(--dropdown-ease), transform var(--dropdown-open-dur) var(--dropdown-ease); will-change: opacity, transform; } .motion-popover.is-open { opacity: 1; transform: scale(1); } .motion-popover.is-closing { pointer-events: none; opacity: 0; transform: scale(var(--dropdown-closing-scale)); transition-duration: var(--dropdown-close-dur); } .motion-popover[data-motion-immediate] { transition: none; } .motion-collapse { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--acc-expand) var(--motion-move); } .motion-collapse-content { min-height: 0; overflow: hidden; opacity: 0; transform: translateY(-4px); transition: opacity var(--acc-expand) var(--motion-move), transform var(--acc-expand) var(--motion-move); } .motion-collapse.is-open { grid-template-rows: 1fr; } .motion-collapse.is-open > .motion-collapse-content { opacity: 1; transform: translateY(0); } .motion-collapse.is-closing { grid-template-rows: 0fr; transition-duration: var(--acc-collapse); } .motion-collapse.is-closing > .motion-collapse-content { opacity: 0; transform: translateY(-4px); transition-duration: var(--acc-collapse); } .motion-disclosure-icon { display: inline-flex; transform: rotate(0deg); } :root[data-motion-input="pointer"] .motion-disclosure-icon { transition: transform var(--icon-swap-dur) var(--motion-smooth-out); } .motion-disclosure-icon[data-open] { transform: rotate(90deg); } body { margin: 0; background: var(--color-ground); color: var(--color-text-primary); font-family: var(--font-sans); /* macOS subpixel smoothing thickens Inter past the weight asked for. */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* One owner per edge: top chrome owns the top inset, compact navigation the bottom one. */ .room-header { padding-right: calc(0.5rem + env(safe-area-inset-right)); padding-left: calc(0.5rem + env(safe-area-inset-left)); padding-top: calc(0.5rem + env(safe-area-inset-top)); } .hosted-header { height: calc(3.5rem + env(safe-area-inset-top)); padding-right: calc(0.75rem + env(safe-area-inset-right)); padding-left: calc(0.75rem + env(safe-area-inset-left)); padding-top: env(safe-area-inset-top); } .workspace-navigation { padding-right: calc(0.25rem + env(safe-area-inset-right)); padding-bottom: calc(0.25rem + env(safe-area-inset-bottom)); padding-left: calc(0.25rem + env(safe-area-inset-left)); } /* The split workspace is one elevated surface; its panes only own the divider. */ .workspace-frame .plan-document { box-shadow: none; } .workspace-frame #pane-chat { border-color: rgb(0 0 0 / 9%); } .motion-panel { opacity: 0; transform: translateX(var(--panel-distance)); transition: opacity var(--panel-open-dur) var(--motion-smooth-out), transform var(--panel-open-dur) var(--motion-smooth-out); will-change: opacity, transform; } .motion-panel.is-open { opacity: 1; transform: translateX(0); } .motion-panel.is-closing { pointer-events: none; opacity: 0; transform: translateX(var(--panel-distance)); transition-duration: var(--panel-close-dur); } .content-swap-stack { display: grid; } .content-swap-stack > .motion-content-swap { grid-area: 1 / 1; min-width: 0; } .motion-content-swap { opacity: 0; transform: translateX(var(--page-slide-distance)); transition: opacity var(--page-slide-dur) var(--motion-smooth-out), transform var(--page-slide-dur) var(--motion-smooth-out); will-change: opacity, transform; } .motion-content-swap.is-open { opacity: 1; transform: translateX(0); } .motion-content-swap.is-closing { pointer-events: none; opacity: 0; transform: translateX(calc(var(--page-slide-distance) * -1)); } .workspace-root:not([data-workspace-mode="split"]) .workspace-conversation-panel { position: absolute; inset: 0; z-index: 10; } .conversation-composer .field { height: 98px; border-color: var(--color-edge); border-radius: var(--radius-lg); box-shadow: var(--shadow-resting); } .conversation-composer .field:focus-within { outline: 2px solid var(--color-brand); outline-offset: 2px; } .conversation-composer textarea:focus { outline: none; } .conversation-header-control:hover:not(:disabled), .conversation-header-control:active:not(:disabled) { background-color: transparent; } .conversation-send-button:disabled { background-color: var(--color-brand); color: var(--color-page); } .conversation-send-button { width: 1.5rem; height: 1.5rem; } .conversation-send-icon { filter: brightness(0) invert(1); } .task-progress-tab:disabled { background-color: transparent; } @media (min-width: 640px) { .room-header { padding-right: calc(1.25rem + env(safe-area-inset-right)); padding-left: calc(1.25rem + env(safe-area-inset-left)); padding-top: env(safe-area-inset-top); } .hosted-header { padding-right: calc(1.5rem + env(safe-area-inset-right)); padding-left: calc(1.5rem + env(safe-area-inset-left)); } } @media (min-width: 1200px) { /* The desktop composer keeps the same clearance on each exposed edge. */ .conversation-composer { padding-inline: 1rem; padding-bottom: calc(1rem + env(safe-area-inset-bottom)); } } @media (max-width: 639px) { /* Touch-sized header controls and their text labels share a vertical center. */ .room-header { align-items: center; } .room-member-face:nth-child(n+4) { display: none; } .room-member-overflow { display: inline; } .repository-picker-trigger { max-width: min(8rem, 36vw); } } :root[data-plan-coarse-pointer] .room-header :where(button, summary), :root[data-plan-coarse-pointer] .conversation-composer button, :root[data-plan-coarse-pointer] .hosted-header button, :root[data-plan-coarse-pointer] .repository-picker-search, :root[data-plan-coarse-pointer] .channel-create-input, :root[data-plan-coarse-pointer] [data-hosted] .btn, :root[data-plan-coarse-pointer] .question-tab, :root[data-plan-coarse-pointer] .question-choice-row, :root[data-plan-coarse-pointer] .question-actions button, :root[data-plan-coarse-pointer] :where(button, [role="button"])[data-press="wide"] { min-width: 44px; min-height: 44px; } :root[data-plan-coarse-pointer] .question-choice-row { padding-block: 0.5rem; } .question-custom-answer { scroll-margin-block-end: calc(var(--keyboard-inset, 0px) + 1rem); } /* * What focus looks like, once, for everything that can take it. An outline * rather than a ring: it follows `border-radius` and takes no space in the * layout. Components may still own their focused fill or other state; the * indicator's colour, width, and offset stay here. */ [data-focus-boundary] { --focus-ring-offset: -2px; } :where(a, button, input, select, summary, textarea, [role="button"], [tabindex]):focus-visible { outline: var(--focus-ring-width) solid var(--focus-ring-color); outline-offset: var(--focus-ring-offset); } /* The editable plan has a live caret, so a second focus marker is redundant. */ :where(.focus-caret):focus-visible { outline: none; } @keyframes item-enter { from { opacity: 0; translate: 0 0.25rem; } } @utility animate-enter { animation: item-enter var(--duration-base) var(--ease-out) both; } @keyframes chat-working-shimmer { from { background-position: 125% 0; } to { background-position: -25% 0; } } @keyframes workspace-working-spin { to { rotate: 1turn; } } .workspace-working-indicator { width: 0.875rem; height: 0.875rem; border: 2px solid currentColor; border-right-color: transparent; border-radius: 9999px; } .chat-working { color: var(--color-text-secondary); } @media (prefers-reduced-motion: no-preference) { .conversation-tool-loader { animation: workspace-working-spin 0.8s linear infinite; } .workspace-working-indicator { animation: workspace-working-spin 0.8s linear infinite; } .chat-working { background: linear-gradient( 90deg, var(--color-text-secondary) 35%, var(--color-text-tertiary) 50%, var(--color-text-secondary) 65% ); background-clip: text; background-size: 200% 100%; color: transparent; animation: chat-working-shimmer 1.8s ease-in-out infinite; } } @media (prefers-reduced-motion: reduce) { .motion-dropdown, .motion-popover, .motion-panel, .motion-content-swap, .motion-collapse, .motion-collapse-content, .motion-disclosure-icon { transition: none; } .animate-enter { animation: none; } } /* * What a press feels like. Zero specificity so a `transition` utility on the * element wins instead — Tailwind's covers `scale` along with the colours. * Disabled controls are excluded: a press that animates and then does nothing * says the opposite of what disabled is trying to say. */ :where(button, [role="button"]) { transition: scale var(--duration-fast) var(--ease-out); } @media (prefers-reduced-motion: no-preference) { :where(button, [role="button"]):not(:disabled):active { scale: 0.96; } /* * A proportion, so a row needs a smaller one: 4% of a full-width row is * several pixels at each edge and reads as jumping, where 4% of a 28px * toolbar cell is the one pixel that is wanted. */ :where(button, [role="button"])[data-press="wide"]:not(:disabled):active { scale: 0.99; } }