1144 lines
33 KiB
CSS
1144 lines
33 KiB
CSS
@import "tailwindcss";
|
||
/* Subframe design-system theme tokens (generated; see .subframe/sync.json). */
|
||
@import "./ui/theme.css";
|
||
|
||
@custom-variant dark (&:is(.dark *));
|
||
|
||
@config '../../../tailwind.config.ts';
|
||
|
||
/*
|
||
Subframe's theme.css sets every typography token to "Instrument Sans" but the
|
||
Subframe sync never brought the actual font over, so all Subframe-styled text
|
||
fell back to the browser serif. Self-host the font (no runtime third-party
|
||
request — keeps the EU/privacy posture) and make it the default family with a
|
||
clean sans fallback so text renders correctly even before the font loads.
|
||
Variable font (weight 400–700); files in apps/web/public/fonts/.
|
||
*/
|
||
@font-face {
|
||
font-family: "Instrument Sans";
|
||
font-style: normal;
|
||
font-weight: 400 700;
|
||
font-stretch: 100%;
|
||
font-display: swap;
|
||
src: url("/fonts/instrument-sans-latin.woff2") format("woff2");
|
||
unicode-range:
|
||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||
U+FEFF, U+FFFD;
|
||
}
|
||
@font-face {
|
||
font-family: "Instrument Sans";
|
||
font-style: normal;
|
||
font-weight: 400 700;
|
||
font-stretch: 100%;
|
||
font-display: swap;
|
||
src: url("/fonts/instrument-sans-latin-ext.woff2") format("woff2");
|
||
unicode-range:
|
||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
|
||
U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||
}
|
||
|
||
/*
|
||
Some synced Subframe components and legacy theme presets still reference
|
||
Inter directly (`font-['Inter']`) or through `--font-inter`. Keep those
|
||
references metric-compatible with the app font instead of falling back to the
|
||
browser/system stack during navigation.
|
||
*/
|
||
@font-face {
|
||
font-family: "Inter";
|
||
font-style: normal;
|
||
font-weight: 400 700;
|
||
font-stretch: 100%;
|
||
font-display: swap;
|
||
src: url("/fonts/instrument-sans-latin.woff2") format("woff2");
|
||
unicode-range:
|
||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
|
||
U+FEFF, U+FFFD;
|
||
}
|
||
@font-face {
|
||
font-family: "Inter";
|
||
font-style: normal;
|
||
font-weight: 400 700;
|
||
font-stretch: 100%;
|
||
font-display: swap;
|
||
src: url("/fonts/instrument-sans-latin-ext.woff2") format("woff2");
|
||
unicode-range:
|
||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
|
||
U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||
}
|
||
|
||
@theme {
|
||
--default-font-family: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
--font-inter: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
--font-sans: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
}
|
||
|
||
/*
|
||
Product surfaces must never fall back to the browser's serif default when the
|
||
self-hosted font is still loading or unavailable. Subframe typography classes
|
||
reference individual font variables, so bind the complete product type scale
|
||
to one resilient product stack at the document boundary.
|
||
*/
|
||
.font-product {
|
||
--product-font-family: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
--font-inter: var(--product-font-family);
|
||
--font-sans: var(--product-font-family);
|
||
--font-title-hero: var(--product-font-family);
|
||
--font-title-page: var(--product-font-family);
|
||
--font-subtitle: var(--product-font-family);
|
||
--font-heading-: var(--product-font-family);
|
||
--font-body-extra-small-regular-150: var(--product-font-family);
|
||
--font-body-small-medium-150: var(--product-font-family);
|
||
--font-body-default-medium-150: var(--product-font-family);
|
||
--font-body-large: var(--product-font-family);
|
||
--font-body-small-regular-150: var(--product-font-family);
|
||
--font-body-default-regular-150: var(--product-font-family);
|
||
--font-body-extra-small-medium-150: var(--product-font-family);
|
||
--font-body-extra-small-semibold-150: var(--product-font-family);
|
||
--font-body-small-semibold-150: var(--product-font-family);
|
||
--font-body-default-semibold-150: var(--product-font-family);
|
||
--font-body-small-medium-0: var(--product-font-family);
|
||
--font-body-extra-small-semibold-0: var(--product-font-family);
|
||
--font-body-extra-small-medium-0: var(--product-font-family);
|
||
--font-body-extra-small-regular-0: var(--product-font-family);
|
||
--font-body-small-regular-0: var(--product-font-family);
|
||
--font-body-small-semibold-0: var(--product-font-family);
|
||
--font-body-default-regular-0: var(--product-font-family);
|
||
--font-body-default-medium-0: var(--product-font-family);
|
||
--font-body-default-semibold-0: var(--product-font-family);
|
||
--font-body-large-0: var(--product-font-family);
|
||
--font-subtitle-0: var(--product-font-family);
|
||
--font-heading-0: var(--product-font-family);
|
||
font-family: var(--product-font-family);
|
||
}
|
||
|
||
/*
|
||
Solid-neutral surfaces (Button/IconButton variant="neutral", BadgeFeedback/Tag
|
||
solid-neutral) shipped with default grey-800 / active grey-900 — a washed-out
|
||
"almost black" that visibly clashes next to the primary button's grey-900.
|
||
Mirror the primary solid ramp instead (default grey-900, hover grey-700,
|
||
active grey-800) so all solid dark controls share the same black. theme.css is
|
||
overwritten on every Subframe sync, so the override lives here.
|
||
*/
|
||
@theme {
|
||
--color-surface-feedback-solid-neutral: var(--color-neutral-900);
|
||
--color-surface-feedback-solid-neutral-hover: var(--color-neutral-700);
|
||
--color-surface-feedback-solid-neutral-active: var(--color-neutral-800);
|
||
}
|
||
|
||
/*
|
||
Local fix (D-D0a): Subframe's "default" heading style is the token `--text-heading-`
|
||
(trailing dash). Tailwind v4 does NOT emit a `text-heading-`/`font-heading-` utility
|
||
for a token whose name ends in `-`, so every Subframe page that uses `text-heading-`
|
||
rendered unstyled (16px/400 fallback) instead of 24px/500. The token IS registered in
|
||
the twMerge list (app/ui/utils.ts), only the CSS was missing. Re-create both utilities
|
||
from the existing token values. theme.css is overwritten on every Subframe sync, so the
|
||
fix lives here in globals.css.
|
||
*/
|
||
@layer utilities {
|
||
.text-heading- {
|
||
font-size: var(--text-heading-);
|
||
font-weight: var(--text-heading---font-weight);
|
||
line-height: var(--text-heading---line-height);
|
||
letter-spacing: var(--text-heading---letter-spacing);
|
||
}
|
||
.font-heading- {
|
||
font-family: var(--font-heading-);
|
||
}
|
||
}
|
||
|
||
/*
|
||
Tailwind v4 preflight resets buttons to `cursor: default`. Every clickable
|
||
control in the app should signal interactivity like a link does; disabled
|
||
controls keep the default cursor (components may override with not-allowed).
|
||
*/
|
||
@layer base {
|
||
button:not(:disabled),
|
||
[role="button"]:not([aria-disabled="true"]),
|
||
input[type="submit"]:not(:disabled),
|
||
input[type="checkbox"]:not(:disabled),
|
||
input[type="radio"]:not(:disabled),
|
||
select:not(:disabled),
|
||
summary {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
/*
|
||
Subframe's "-0" text tokens used line-height equal to font-size. That clips
|
||
glyphs with ascenders/descenders or umlauts in tight containers (e.g. "Ü", "g",
|
||
"p"). Keep the font sizes/weights but give these tokens a usable line box
|
||
globally instead of patching individual screens.
|
||
*/
|
||
@layer base {
|
||
:root {
|
||
--text-body-extra-small-regular-0--line-height: 16px;
|
||
--text-body-extra-small-medium-0--line-height: 16px;
|
||
--text-body-extra-small-semibold-0--line-height: 16px;
|
||
--text-body-small-regular-0--line-height: 18px;
|
||
--text-body-small-medium-0--line-height: 18px;
|
||
--text-body-small-semibold-0--line-height: 18px;
|
||
--text-body-default-regular-0--line-height: 22px;
|
||
--text-body-default-medium-0--line-height: 22px;
|
||
--text-body-default-semibold-0--line-height: 22px;
|
||
--text-body-large-0--line-height: 26px;
|
||
--text-subtitle-0--line-height: 40px;
|
||
--text-heading-0--line-height: 32px;
|
||
}
|
||
}
|
||
|
||
/*
|
||
@tailwindcss/forms paints a 1px blue ring (box-shadow, not outline) on every
|
||
focused input. The design system shows focus on the surrounding field instead —
|
||
TextInput, SearchInput and the filter bar all carry `focus-within:shadow-focus`
|
||
on their wrapper — so the plugin ring draws a second, differently coloured
|
||
border inside the one that is already there.
|
||
|
||
Removed for the controls that carry the kit marker class, not for inputs in
|
||
general: a bare input outside the design system still needs a visible focus
|
||
indicator, and dropping it there would be an accessibility regression.
|
||
*/
|
||
@layer base {
|
||
.subframe-text-input-control:focus,
|
||
.subframe-text-input-control:focus-visible {
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
|
||
@layer base {
|
||
.subframe-text-input-control:-webkit-autofill,
|
||
.subframe-text-input-control:-webkit-autofill:hover,
|
||
.subframe-text-input-control:-webkit-autofill:focus,
|
||
.subframe-text-input-control:-webkit-autofill:active {
|
||
-webkit-box-shadow: 0 0 0 1000px var(--color-surface-neutral-primary) inset !important;
|
||
-webkit-text-fill-color: var(--color-text-neutral-secondary) !important;
|
||
caret-color: var(--color-text-neutral-secondary);
|
||
transition: background-color 9999s ease-out 0s;
|
||
}
|
||
}
|
||
|
||
/*
|
||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||
so we've added these compatibility styles to make sure everything still
|
||
looks the same as it did with Tailwind CSS v3.
|
||
|
||
If we ever want to remove these styles, we need to add an explicit border
|
||
color utility to any element that depends on these defaults.
|
||
*/
|
||
@layer base {
|
||
*,
|
||
::after,
|
||
::before,
|
||
::backdrop,
|
||
::file-selector-button {
|
||
border-color: var(--color-gray-200, currentColor);
|
||
}
|
||
.typography {
|
||
max-width: 65ch;
|
||
color: var(--color-foreground);
|
||
}
|
||
.typography h1:not(.not-typography h1) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
font-size: var(--text-4xl);
|
||
line-height: var(--text-4xl--line-height);
|
||
font-weight: 800;
|
||
letter-spacing: var(--tracking-tight);
|
||
}
|
||
.typography h2:not(.not-typography h2) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
border-bottom: 1px solid var(--color-border);
|
||
padding-bottom: calc(var(--spacing) * 2);
|
||
font-size: var(--text-3xl);
|
||
line-height: var(--text-3xl--line-height);
|
||
font-weight: 600;
|
||
letter-spacing: var(--tracking-tight);
|
||
margin-top: calc(var(--spacing) * 12);
|
||
}
|
||
.typography h2:not(.not-typography h2):first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography h3:not(.not-typography h3) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
font-size: var(--text-2xl);
|
||
line-height: var(--text-2xl--line-height);
|
||
font-weight: 600;
|
||
letter-spacing: var(--tracking-tight);
|
||
margin-top: calc(var(--spacing) * 12);
|
||
}
|
||
.typography h3:not(.not-typography h3):first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography h4:not(.not-typography h4) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
font-size: var(--text-xl);
|
||
line-height: var(--text-xl--line-height);
|
||
font-weight: 600;
|
||
letter-spacing: var(--tracking-tight);
|
||
margin-top: calc(var(--spacing) * 12);
|
||
}
|
||
.typography h4:not(.not-typography h4):first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography h5:not(.not-typography h5) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
font-size: var(--text-lg);
|
||
line-height: var(--text-lg--line-height);
|
||
font-weight: 600;
|
||
letter-spacing: var(--tracking-tight);
|
||
margin-top: calc(var(--spacing) * 12);
|
||
}
|
||
.typography h5:not(.not-typography h5):first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography h6:not(.not-typography h6) {
|
||
scroll-margin: calc(var(--spacing) * 20);
|
||
font-size: var(--text-base);
|
||
line-height: var(--text-base--line-height);
|
||
font-weight: 600;
|
||
letter-spacing: var(--tracking-tight);
|
||
margin-top: calc(var(--spacing) * 12);
|
||
}
|
||
.typography h6:not(.not-typography h6):first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography h1:not(.not-typography h1) + p,
|
||
.typography h2:not(.not-typography h2) + p,
|
||
.typography h3:not(.not-typography h3) + p,
|
||
.typography h4:not(.not-typography h4) + p,
|
||
.typography h5:not(.not-typography h5) + p,
|
||
.typography h6:not(.not-typography h6) + p {
|
||
margin-top: 0;
|
||
}
|
||
.typography p:not(.not-typography p) {
|
||
line-height: 1.75;
|
||
}
|
||
.typography p:not(.not-typography p):not(:first-child) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
}
|
||
.typography li:not(.not-typography li) {
|
||
line-height: 1.75;
|
||
}
|
||
.typography a:not(.not-typography a) {
|
||
font-weight: 500;
|
||
color: var(--color-primary);
|
||
text-decoration: underline;
|
||
text-underline-offset: var(--spacing);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.typography blockquote:not(.not-typography blockquote) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
border-left: 2px solid var(--color-border);
|
||
padding-left: calc(var(--spacing) * 6);
|
||
font-style: italic;
|
||
}
|
||
.typography table:not(.not-typography table) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-bottom: calc(var(--spacing) * 6);
|
||
width: 100%;
|
||
overflow-y: auto;
|
||
}
|
||
.typography table:not(.not-typography table) thead tr {
|
||
margin: 0;
|
||
border-top: 1px solid var(--color-border);
|
||
padding: 0;
|
||
}
|
||
.typography table:not(.not-typography table) thead tr:nth-child(even) {
|
||
background-color: var(--color-muted);
|
||
}
|
||
.typography table:not(.not-typography table) th {
|
||
border: 1px solid var(--color-border);
|
||
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 4);
|
||
text-align: left;
|
||
font-weight: 700;
|
||
}
|
||
.typography table:not(.not-typography table) th[align="center"] {
|
||
text-align: center;
|
||
}
|
||
.typography table:not(.not-typography table) th[align="right"] {
|
||
text-align: right;
|
||
}
|
||
.typography table:not(.not-typography table) tbody tr {
|
||
margin: 0;
|
||
border-top: 1px solid var(--color-border);
|
||
padding: 0;
|
||
}
|
||
.typography table:not(.not-typography table) tbody tr:nth-child(even) {
|
||
background-color: var(--color-muted);
|
||
}
|
||
.typography table:not(.not-typography table) td {
|
||
border: 1px solid var(--color-border);
|
||
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 4);
|
||
text-align: left;
|
||
}
|
||
.typography table:not(.not-typography table) td[align="center"] {
|
||
text-align: center;
|
||
}
|
||
.typography table:not(.not-typography table) td[align="right"] {
|
||
text-align: right;
|
||
}
|
||
.typography ul:not(.not-typography ul) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-left: calc(var(--spacing) * 6);
|
||
list-style: disc;
|
||
}
|
||
.typography ul:not(.not-typography ul) > li {
|
||
margin-top: calc(var(--spacing) * 2);
|
||
}
|
||
.typography ul:not(.not-typography ul) p {
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
display: inline;
|
||
}
|
||
.typography ol:not(.not-typography ol) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-left: calc(var(--spacing) * 6);
|
||
list-style: decimal;
|
||
}
|
||
.typography ol:not(.not-typography ol) > li {
|
||
margin-top: calc(var(--spacing) * 2);
|
||
}
|
||
.typography ol:not(.not-typography ol) p {
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
display: inline;
|
||
}
|
||
.typography pre:not(.not-typography pre) {
|
||
background-color: var(--color-muted);
|
||
border-radius: calc(var(--radius) - 2px);
|
||
padding: calc(var(--spacing) * 4);
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-bottom: calc(var(--spacing) * 6);
|
||
font-size: var(--text-sm);
|
||
line-height: var(--text-sm--line-height);
|
||
overflow-y: auto;
|
||
}
|
||
.typography code:not(pre code):not(.not-typography code) {
|
||
position: relative;
|
||
border-radius: var(--radius);
|
||
background-color: var(--color-muted);
|
||
padding: calc(var(--spacing) * 0.2) calc(var(--spacing) * 0.3);
|
||
font-family: var(--font-mono);
|
||
font-size: var(--text-sm);
|
||
line-height: var(--text-sm--line-height);
|
||
font-weight: 600;
|
||
}
|
||
.typography .lead:not(.not-typography .lead) {
|
||
font-size: var(--text-xl);
|
||
line-height: var(--text-xl--line-height);
|
||
color: var(--color-muted-foreground);
|
||
}
|
||
.typography .large:not(.not-typography .large) {
|
||
font-size: var(--text-lg);
|
||
line-height: var(--text-lg--line-height);
|
||
font-weight: 600;
|
||
}
|
||
.typography .small:not(.not-typography .small) {
|
||
font-size: var(--text-sm);
|
||
line-height: 1;
|
||
font-weight: 500;
|
||
}
|
||
.typography .muted:not(.not-typography .muted) {
|
||
font-size: var(--text-sm);
|
||
line-height: var(--text-sm--line-height);
|
||
color: var(--color-muted-foreground);
|
||
}
|
||
.typography img:not(.not-typography img),
|
||
.typography picture:not(.not-typography picture),
|
||
.typography video:not(.not-typography video) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-bottom: calc(var(--spacing) * 6);
|
||
}
|
||
.typography picture > img:not(.not-typography picture > img) {
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
}
|
||
.typography kbd:not(.not-typography kbd) {
|
||
border-radius: calc(var(--radius) - 2px);
|
||
background-color: var(--color-muted);
|
||
padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 1.5);
|
||
font-size: var(--text-xs);
|
||
line-height: var(--text-xs--line-height);
|
||
font-weight: 600;
|
||
}
|
||
.typography hr {
|
||
margin-top: calc(var(--spacing) * 10);
|
||
margin-bottom: calc(var(--spacing) * 10);
|
||
}
|
||
.typography dl:not(.not-typography dl) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
margin-bottom: calc(var(--spacing) * 6);
|
||
}
|
||
.typography dl:not(.not-typography dl) dt {
|
||
font-weight: 600;
|
||
margin-top: calc(var(--spacing) * 6);
|
||
letter-spacing: var(--tracking-tight);
|
||
}
|
||
.typography dl:not(.not-typography dl) dt:first-child {
|
||
margin-top: 0;
|
||
}
|
||
.typography details:not(.not-typography details) {
|
||
margin-top: calc(var(--spacing) * 6);
|
||
}
|
||
.typography details:not(.not-typography details) summary {
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
margin-top: calc(var(--spacing) * 6);
|
||
letter-spacing: var(--tracking-tight);
|
||
}
|
||
.typography details:not(.not-typography details) p:first-of-type {
|
||
margin-top: calc(var(--spacing) * 2);
|
||
}
|
||
.typography mark:not(.not-typography mark) {
|
||
background-color: var(--color-yellow-300);
|
||
}
|
||
.typography small:not(.not-typography small) {
|
||
font-size: var(--text-xs);
|
||
line-height: 1;
|
||
}
|
||
/*
|
||
---break---
|
||
*/
|
||
@media (min-width: 1024px) {
|
||
.typography h1:not(.not-typography h1) {
|
||
font-size: var(--text-5xl);
|
||
line-height: var(--text-5xl--line-height);
|
||
}
|
||
}
|
||
}
|
||
|
||
@utility tooltip {
|
||
@apply invisible absolute;
|
||
|
||
.has-tooltip:hover & {
|
||
@apply visible z-50;
|
||
}
|
||
}
|
||
|
||
@utility has-tooltip {
|
||
&:hover .tooltip {
|
||
@apply visible z-50;
|
||
}
|
||
}
|
||
|
||
@utility base-spinner {
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&:before {
|
||
content: "";
|
||
box-sizing: border-box;
|
||
position: absolute;
|
||
background-color: inherit;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
z-index: 1;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
&:after {
|
||
content: "";
|
||
box-sizing: border-box;
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 20px;
|
||
height: 20px;
|
||
margin-top: -10px;
|
||
margin-left: -10px;
|
||
border-radius: 50%;
|
||
border: 2px solid rgba(255, 255, 255, 0.45);
|
||
border-top-color: inherit;
|
||
animation: spinner 0.6s linear infinite;
|
||
z-index: 2;
|
||
}
|
||
}
|
||
|
||
@utility loader {
|
||
border-top-color: #5a67d8;
|
||
-webkit-animation: spinner 1.5s linear infinite;
|
||
animation: spinner 1.5s linear infinite;
|
||
}
|
||
|
||
@utility drop {
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 1rem;
|
||
transition: background-color 0.2s ease-in-out;
|
||
}
|
||
|
||
@utility uploadmyfile {
|
||
display: none;
|
||
}
|
||
|
||
@utility tiptap {
|
||
:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
ul {
|
||
@apply list-disc;
|
||
padding: 0 1rem;
|
||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||
|
||
li p {
|
||
margin-top: 0.25em;
|
||
margin-bottom: 0.25em;
|
||
}
|
||
}
|
||
|
||
ol {
|
||
@apply list-decimal;
|
||
padding: 0 1rem;
|
||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||
|
||
li p {
|
||
margin-top: 0.25em;
|
||
margin-bottom: 0.25em;
|
||
}
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
line-height: 1.1;
|
||
margin-top: 2.5rem;
|
||
text-wrap: pretty;
|
||
}
|
||
|
||
h1,
|
||
h2 {
|
||
margin-top: 3.5rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 1.4rem;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
h3 {
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
font-size: 1rem;
|
||
}
|
||
|
||
code {
|
||
background-color: hsl(var(--muted));
|
||
border-radius: 0.4rem;
|
||
color: hsl(var(--foreground));
|
||
font-size: 0.85rem;
|
||
padding: 0.25em 0.3em;
|
||
}
|
||
|
||
pre {
|
||
background: hsl(var(--foreground));
|
||
border-radius: 0.5rem;
|
||
color: hsl(var(--background));
|
||
font-family: "JetBrainsMono", monospace;
|
||
margin: 1.5rem 0;
|
||
padding: 0.75rem 1rem;
|
||
|
||
code {
|
||
background: none;
|
||
color: inherit;
|
||
font-size: 0.8rem;
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
blockquote {
|
||
border-left: 3px solid hsl(var(--border));
|
||
margin: 1.5rem 0;
|
||
padding-left: 1rem;
|
||
}
|
||
|
||
hr {
|
||
border: none;
|
||
border-top: 1px solid hsl(var(--border));
|
||
margin: 2rem 0;
|
||
}
|
||
}
|
||
|
||
@utility scrollbar-hidden {
|
||
scrollbar-width: none;
|
||
-ms-overflow-style: none;
|
||
|
||
&::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@layer utilities {
|
||
@keyframes spinner {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
@-webkit-keyframes spinner {
|
||
0% {
|
||
-webkit-transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
-webkit-transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
@keyframes spinner {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
}
|
||
|
||
:root {
|
||
--background: hsl(0 0% 100%);
|
||
--foreground: hsl(240 10% 3.9%);
|
||
|
||
--muted: hsl(240 4.8% 95.9%);
|
||
--muted-foreground: hsl(240 3.8% 46.1%);
|
||
|
||
--popover: hsl(0 0% 100%);
|
||
--popover-foreground: hsl(240 10% 3.9%);
|
||
|
||
--card: hsl(0 0% 100%);
|
||
--card-foreground: hsl(240 10% 3.9%);
|
||
|
||
--border: hsl(240 5.9% 90%);
|
||
--input: hsl(240 5.9% 90%);
|
||
|
||
--primary: hsl(240 5.9% 10%);
|
||
--primary-foreground: hsl(0 0% 98%);
|
||
|
||
--secondary: hsl(240 4.8% 95.9%);
|
||
--secondary-foreground: hsl(240 5.9% 10%);
|
||
|
||
--accent: hsl(240 4.8% 95.9%);
|
||
--accent-foreground: hsl(240 5.9% 10%);
|
||
|
||
--destructive: hsl(0 84.2% 60.2%);
|
||
--destructive-foreground: hsl(0 0% 98%);
|
||
|
||
--ring: hsl(240 5.9% 10%);
|
||
|
||
--radius: 0.5rem;
|
||
|
||
/* Subframe SidebarWithSections palette (light): map the shadcn sidebar tokens
|
||
onto the Subframe surface/text/border tokens so the (light) tenant sidebar
|
||
matches the design-system component. Dark `--sidebar-*` (admin + dark mode)
|
||
stays as-is — Subframe's surface tokens are light-only. */
|
||
--sidebar-background: var(--color-surface-neutral-secondary);
|
||
--sidebar-foreground: var(--color-text-neutral-secondary);
|
||
--sidebar-primary: hsl(240 5.9% 10%);
|
||
--sidebar-primary-foreground: hsl(0 0% 98%);
|
||
--sidebar-accent: var(--color-surface-neutral-tertiary);
|
||
--sidebar-accent-foreground: var(--color-text-neutral-primary);
|
||
--sidebar-border: var(--color-border-default);
|
||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||
--sidebar: var(--color-surface-neutral-secondary);
|
||
|
||
--chart-1: 221.2 83.2% 53.3%;
|
||
--chart-2: 212 95% 68%;
|
||
--chart-3: 216 92% 60%;
|
||
--chart-4: 210 98% 78%;
|
||
--chart-5: 212 97% 87%;
|
||
}
|
||
|
||
.dark {
|
||
--background: hsl(240 10% 3.9%);
|
||
--foreground: hsl(0 0% 98%);
|
||
|
||
--muted: hsl(240 3.7% 15.9%);
|
||
--muted-foreground: hsl(240 5% 64.9%);
|
||
|
||
--popover: hsl(240 10% 3.9%);
|
||
--popover-foreground: hsl(0 0% 98%);
|
||
|
||
--card: hsl(240 10% 3.9%);
|
||
--card-foreground: hsl(0 0% 98%);
|
||
|
||
--border: hsl(240 3.7% 15.9%);
|
||
--input: hsl(240 3.7% 15.9%);
|
||
|
||
--primary: hsl(0 0% 98%);
|
||
--primary-foreground: hsl(240 5.9% 10%);
|
||
|
||
--secondary: hsl(240 3.7% 15.9%);
|
||
--secondary-foreground: hsl(0 0% 98%);
|
||
|
||
--accent: hsl(240 3.7% 15.9%);
|
||
--accent-foreground: hsl(0 0% 98%);
|
||
|
||
--destructive: hsl(0 62.8% 30.6%);
|
||
--destructive-foreground: hsl(0 0% 98%);
|
||
|
||
--ring: hsl(240 4.9% 83.9%);
|
||
|
||
--sidebar-background: hsl(240 10% 3.9%);
|
||
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
||
--sidebar-primary: hsl(224.3 76.3% 48%);
|
||
--sidebar-primary-foreground: hsl(0 0% 100%);
|
||
--sidebar-accent: hsl(240 3.7% 15.9%);
|
||
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
||
--sidebar-border: hsl(240 3.7% 15.9%);
|
||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||
--sidebar: hsl(240 5.9% 10%);
|
||
|
||
--chart-1: 221.2 83.2% 53.3%;
|
||
--chart-2: 212 95% 68%;
|
||
--chart-3: 216 92% 60%;
|
||
--chart-4: 210 98% 78%;
|
||
--chart-5: 212 97% 87%;
|
||
}
|
||
|
||
@theme inline {
|
||
--radius-sm: calc(var(--radius) - 4px);
|
||
--radius-md: calc(var(--radius) - 2px);
|
||
--radius-lg: var(--radius);
|
||
--radius-xl: calc(var(--radius) + 4px);
|
||
--color-background: var(--background);
|
||
--color-foreground: var(--foreground);
|
||
--color-card: var(--card);
|
||
--color-card-foreground: var(--card-foreground);
|
||
--color-popover: var(--popover);
|
||
--color-popover-foreground: var(--popover-foreground);
|
||
--color-primary: var(--primary);
|
||
--color-primary-foreground: var(--primary-foreground);
|
||
--color-secondary: var(--secondary);
|
||
--color-secondary-foreground: var(--secondary-foreground);
|
||
--color-muted: var(--muted);
|
||
--color-muted-foreground: var(--muted-foreground);
|
||
--color-accent: var(--accent);
|
||
--color-accent-foreground: var(--accent-foreground);
|
||
--color-destructive: var(--destructive);
|
||
--color-border: var(--border);
|
||
--color-input: var(--input);
|
||
--color-ring: var(--ring);
|
||
--color-chart-1: var(--chart-1);
|
||
--color-chart-2: var(--chart-2);
|
||
--color-chart-3: var(--chart-3);
|
||
--color-chart-4: var(--chart-4);
|
||
--color-chart-5: var(--chart-5);
|
||
--color-sidebar: var(--sidebar);
|
||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||
--color-sidebar-primary: var(--sidebar-primary);
|
||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||
--color-sidebar-accent: var(--sidebar-accent);
|
||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||
--color-sidebar-border: var(--sidebar-border);
|
||
--color-sidebar-ring: var(--sidebar-ring);
|
||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||
|
||
--breakpoint-3xl: 120rem;
|
||
--breakpoint-4xl: 140rem;
|
||
|
||
/*
|
||
---break---
|
||
*/
|
||
@keyframes accordion-down {
|
||
from {
|
||
height: 0;
|
||
}
|
||
to {
|
||
height: var(--radix-accordion-content-height);
|
||
}
|
||
}
|
||
/*
|
||
---break---
|
||
*/
|
||
@keyframes accordion-up {
|
||
from {
|
||
height: var(--radix-accordion-content-height);
|
||
}
|
||
to {
|
||
height: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
@layer base {
|
||
* {
|
||
@apply border-border outline-ring/50;
|
||
}
|
||
body {
|
||
@apply text-foreground overscroll-y-none bg-transparent;
|
||
}
|
||
}
|
||
|
||
/* Browser-local accessibility preference: remove optional motion and blur. */
|
||
html[data-hg-visual-effects="off"] {
|
||
scroll-behavior: auto !important;
|
||
}
|
||
|
||
html[data-hg-visual-effects="off"] *,
|
||
html[data-hg-visual-effects="off"] *::before,
|
||
html[data-hg-visual-effects="off"] *::after {
|
||
animation: none !important;
|
||
transition: none !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
|
||
html[data-hg-visual-effects="off"] [class*="blur"],
|
||
html[data-hg-visual-effects="off"] [style*="blur("] {
|
||
filter: none !important;
|
||
-webkit-backdrop-filter: none !important;
|
||
backdrop-filter: none !important;
|
||
}
|
||
|
||
/* Full-width workflow drawer motion: the panel starts completely beyond the
|
||
right viewport edge and visibly travels left onto the canvas. */
|
||
@keyframes hg-drawer-enter-from-right {
|
||
from {
|
||
transform: translate3d(105%, 0, 0);
|
||
}
|
||
to {
|
||
transform: translate3d(0, 0, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes hg-drawer-exit-to-right {
|
||
from {
|
||
transform: translate3d(0, 0, 0);
|
||
}
|
||
to {
|
||
transform: translate3d(105%, 0, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes hg-drawer-backdrop-enter {
|
||
from {
|
||
background-color: rgb(0 0 0 / 0%);
|
||
-webkit-backdrop-filter: blur(0);
|
||
backdrop-filter: blur(0);
|
||
}
|
||
to {
|
||
background-color: rgb(0 0 0 / 35%);
|
||
-webkit-backdrop-filter: blur(4px);
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
}
|
||
|
||
@keyframes hg-drawer-backdrop-exit {
|
||
from {
|
||
background-color: rgb(0 0 0 / 35%);
|
||
-webkit-backdrop-filter: blur(4px);
|
||
backdrop-filter: blur(4px);
|
||
}
|
||
to {
|
||
background-color: rgb(0 0 0 / 0%);
|
||
-webkit-backdrop-filter: blur(0);
|
||
backdrop-filter: blur(0);
|
||
}
|
||
}
|
||
|
||
.hg-drawer-backdrop[data-state="open"] {
|
||
animation: hg-drawer-backdrop-enter 300ms ease-out both;
|
||
}
|
||
|
||
.hg-drawer-backdrop[data-state="closed"] {
|
||
animation: hg-drawer-backdrop-exit 300ms ease-out both;
|
||
}
|
||
|
||
.hg-drawer-full-from-right[data-state="open"] {
|
||
animation: hg-drawer-enter-from-right 440ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
}
|
||
|
||
.hg-drawer-full-from-right[data-state="closed"] {
|
||
animation: hg-drawer-exit-to-right 440ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hg-drawer-backdrop,
|
||
.hg-drawer-full-from-right {
|
||
animation: none !important;
|
||
}
|
||
}
|
||
|
||
/* Keep centered dialogs stable when third-party widgets inject global styles. */
|
||
[data-slot="dialog-content"][data-positioning="centered"] {
|
||
position: fixed !important;
|
||
top: 50% !important;
|
||
left: 50% !important;
|
||
transform: translate(-50%, -50%) !important;
|
||
}
|
||
|
||
[data-slot="dialog-overlay"] {
|
||
position: fixed !important;
|
||
inset: 0 !important;
|
||
}
|
||
|
||
/*
|
||
* VD-MCP connect pill — idle it stays a plain bordered button. On hover (and on
|
||
* keyboard focus) the brand gold sweeps around the border and the client marks
|
||
* ride past on a 3D conveyor showing every supported client, not just the three
|
||
* that fit. Pure decoration: everything below is inert under reduced motion.
|
||
*/
|
||
|
||
/*
|
||
* The sweep is back to the original form: one conic gradient turning at a
|
||
* constant angular rate. Thickness and the dual glow stay at the tuned values.
|
||
*/
|
||
@property --hg-mcp-angle {
|
||
syntax: "<angle>";
|
||
inherits: false;
|
||
initial-value: 0deg;
|
||
}
|
||
|
||
@keyframes hg-mcp-border-sweep {
|
||
to {
|
||
--hg-mcp-angle: 360deg;
|
||
}
|
||
}
|
||
|
||
.hg-mcp-pill::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: -4.05px;
|
||
z-index: 1;
|
||
border-radius: inherit;
|
||
padding: 4.05px;
|
||
/* Tight hot glow plus a wide soft one — this is what reads as sparkle. */
|
||
filter: drop-shadow(0 0 2px rgb(255 236 179 / 0.9)) drop-shadow(0 0 7px rgb(241 183 41 / 0.6));
|
||
background: conic-gradient(
|
||
from var(--hg-mcp-angle),
|
||
rgb(241 183 41 / 0) 0deg,
|
||
rgb(241 183 41 / 0.15) 40deg,
|
||
rgb(241 183 41 / 1) 80deg,
|
||
rgb(255 236 179 / 1) 100deg,
|
||
rgb(241 183 41 / 1) 120deg,
|
||
rgb(241 183 41 / 0.15) 160deg,
|
||
rgb(241 183 41 / 0) 200deg
|
||
);
|
||
/* Ring-only fill: the mask keeps the padding box and drops the content box. */
|
||
-webkit-mask:
|
||
linear-gradient(#000 0 0) content-box,
|
||
linear-gradient(#000 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask:
|
||
linear-gradient(#000 0 0) content-box,
|
||
linear-gradient(#000 0 0);
|
||
mask-composite: exclude;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 180ms ease;
|
||
}
|
||
|
||
.hg-mcp-pill:hover::before,
|
||
.hg-mcp-pill:focus-visible::before {
|
||
opacity: 1;
|
||
animation: hg-mcp-border-sweep 2.4s linear infinite;
|
||
}
|
||
|
||
/* Two identical mark copies, so -50% -> 0 loops without a seam. */
|
||
@keyframes hg-mcp-marks-ride {
|
||
from {
|
||
transform: translate3d(-50%, 0, 0);
|
||
}
|
||
to {
|
||
transform: translate3d(0, 0, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes hg-mcp-mark-flip {
|
||
0% {
|
||
transform: rotateY(0deg);
|
||
}
|
||
100% {
|
||
transform: rotateY(360deg);
|
||
}
|
||
}
|
||
|
||
.hg-mcp-pill {
|
||
position: relative;
|
||
isolation: isolate;
|
||
}
|
||
|
||
.hg-mcp-marks {
|
||
display: block;
|
||
/* Three marks wide (3 x 14px + 2 x 6px gap); the rest rides in from outside. */
|
||
width: 3.375rem;
|
||
overflow: hidden;
|
||
perspective: 220px;
|
||
}
|
||
|
||
.hg-mcp-pill:hover .hg-mcp-marks,
|
||
.hg-mcp-pill:focus-visible .hg-mcp-marks {
|
||
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%);
|
||
mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%);
|
||
}
|
||
|
||
/*
|
||
* At rest the track is parked on the second copy's first mark, which is where
|
||
* the ride animation starts and ends — so hovering picks up exactly here with
|
||
* no jump. Parking it explicitly rather than relying on a paused animation's
|
||
* frame 0: a paused animation still advances its clock in Chrome and ends up
|
||
* holding an arbitrary offset that slices the marks mid-glyph.
|
||
*/
|
||
.hg-mcp-marks-track {
|
||
display: flex;
|
||
width: max-content;
|
||
align-items: center;
|
||
transform: translate3d(-50%, 0, 0);
|
||
transform-style: preserve-3d;
|
||
/* Lets the marks glide back into place when the pointer leaves mid-ride. */
|
||
transition: transform 260ms ease-out;
|
||
}
|
||
|
||
/* Margin instead of flex gap: gap would leave half a gap unaccounted for in
|
||
the 50% loop period and the conveyor would jump 3px every cycle. */
|
||
.hg-mcp-marks-track > * {
|
||
margin-right: 0.375rem;
|
||
flex: none;
|
||
backface-visibility: visible;
|
||
}
|
||
|
||
.hg-mcp-pill:hover .hg-mcp-marks-track,
|
||
.hg-mcp-pill:focus-visible .hg-mcp-marks-track {
|
||
animation: hg-mcp-marks-ride 3.47s linear infinite;
|
||
transition: none;
|
||
}
|
||
|
||
.hg-mcp-pill:hover .hg-mcp-marks-track > *,
|
||
.hg-mcp-pill:focus-visible .hg-mcp-marks-track > * {
|
||
animation: hg-mcp-mark-flip 6.48s linear infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hg-mcp-pill::before,
|
||
.hg-mcp-marks-track,
|
||
.hg-mcp-marks-track > * {
|
||
animation: none !important;
|
||
}
|
||
.hg-mcp-pill:hover::before,
|
||
.hg-mcp-pill:focus-visible::before {
|
||
/* Keep the affordance, drop the motion. */
|
||
opacity: 1;
|
||
background: rgb(241 183 41 / 0.9);
|
||
}
|
||
}
|
||
|
||
/* Kanban drag & drop: while a card is in hand the pointer leaves the tile it
|
||
started on, so a `cursor-grabbing` class on the card alone would flicker back
|
||
to the default arrow. SortableKanbanBoard sets this class on <html> for the
|
||
duration of the drag and the grabbing fist wins everywhere, including over
|
||
the drop targets and the text inside them. */
|
||
html.hg-kanban-dragging,
|
||
html.hg-kanban-dragging * {
|
||
cursor: grabbing !important;
|
||
}
|