9db53cb36f
Replace the stacked sign-in card with a responsive public shell, a local monochrome compute field, restrained grain and reduced-motion-safe drift. Share the anonymous header with Learn so public navigation stays consistent, and carry the corrected spacing through registration and profile setup.
263 lines
7.5 KiB
CSS
263 lines
7.5 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
@font-face {
|
|
font-family: 'Manrope Variable';
|
|
font-style: normal;
|
|
font-display: swap;
|
|
font-weight: 200 800;
|
|
src: url('@fontsource-variable/manrope/files/manrope-latin-wght-normal.woff2') format('woff2-variations');
|
|
}
|
|
|
|
/*
|
|
* Surfaces and neutrals.
|
|
*
|
|
* Accent variables are NOT defined here — they are written onto the root
|
|
* element at runtime from the palette in @pig/core, so there is exactly one
|
|
* definition of each colour and the CSS cannot drift from the TypeScript.
|
|
*/
|
|
:root {
|
|
--bg: 0 0% 100%;
|
|
--surface: 0 0% 100%;
|
|
--surface-2: 240 5% 97%;
|
|
--border: 240 6% 90%;
|
|
--fg: 240 10% 4%;
|
|
--muted: 240 4% 46%;
|
|
|
|
--positive: 160 84% 32%;
|
|
--warning: 32 95% 44%;
|
|
--danger: 0 72% 45%;
|
|
--info: 201 90% 40%;
|
|
--shadow: 240 10% 4%;
|
|
|
|
/* Safe-area insets, so layout can reference them even at zero. */
|
|
--safe-top: env(safe-area-inset-top, 0px);
|
|
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
--safe-left: env(safe-area-inset-left, 0px);
|
|
--safe-right: env(safe-area-inset-right, 0px);
|
|
|
|
/*
|
|
* The application header's height, including the notch it sits under. Both
|
|
* side panes are sticky beneath it and subtract this from the viewport, so
|
|
* it has to be one number rather than a magic constant in three files.
|
|
*/
|
|
--app-header-h: calc(3.5rem + var(--safe-top));
|
|
|
|
/*
|
|
* The sidebar primitive's palette.
|
|
*
|
|
* Aliases, not values. shadcn's sidebar block wants its own `--sidebar-*`
|
|
* scale; defining literal colours here would be a second palette to keep in
|
|
* step with the first, and it would not follow the user's accent — which is
|
|
* written onto this same element at runtime by lib/theme.tsx. Because these
|
|
* are HSL channel triples pointing at other HSL channel triples, the dark
|
|
* theme and every accent flow through for free and there is nothing to
|
|
* duplicate under [data-theme='dark'].
|
|
*/
|
|
--sidebar-background: var(--surface);
|
|
--sidebar-foreground: var(--fg);
|
|
--sidebar-primary: var(--accent);
|
|
--sidebar-primary-foreground: var(--accent-on);
|
|
--sidebar-accent: var(--accent-subtle);
|
|
--sidebar-accent-foreground: var(--accent-fg);
|
|
--sidebar-border: var(--border);
|
|
--sidebar-ring: var(--accent);
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
:root {
|
|
--app-header-h: calc(4rem + var(--safe-top));
|
|
}
|
|
}
|
|
|
|
:root[data-theme='dark'] {
|
|
--bg: 240 10% 4%;
|
|
--surface: 240 8% 7%;
|
|
--surface-2: 240 6% 11%;
|
|
--border: 240 5% 18%;
|
|
--fg: 0 0% 98%;
|
|
--muted: 240 5% 60%;
|
|
|
|
--positive: 158 64% 52%;
|
|
--warning: 38 92% 60%;
|
|
--danger: 0 84% 65%;
|
|
--info: 199 89% 60%;
|
|
--shadow: 0 0% 0%;
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
border-color: hsl(var(--border));
|
|
}
|
|
|
|
html {
|
|
/* Prevents iOS from silently enlarging text in landscape. */
|
|
-webkit-text-size-adjust: 100%;
|
|
/* Keeps the background painted behind the rubber-band overscroll area. */
|
|
background-color: hsl(var(--bg));
|
|
}
|
|
|
|
body {
|
|
@apply bg-bg text-fg antialiased;
|
|
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
|
/* Stops the whole page bouncing on iOS while inner panes still scroll. */
|
|
overscroll-behavior-y: none;
|
|
}
|
|
|
|
::selection {
|
|
background: hsl(var(--accent-subtle));
|
|
color: hsl(var(--accent-fg));
|
|
}
|
|
|
|
/*
|
|
* Mobile Safari zooms the viewport when a focused input has a font size
|
|
* below 16px, and never zooms back out. This is the fix — not
|
|
* user-scalable=no, which breaks pinch-zoom for everyone.
|
|
*/
|
|
input,
|
|
select,
|
|
textarea {
|
|
font-size: max(16px, 1rem);
|
|
}
|
|
|
|
/* A visible, consistent focus ring — keyboard users need it, and the
|
|
default varies wildly between browsers. */
|
|
:focus-visible {
|
|
@apply outline-none ring-2 ring-accent ring-offset-2 ring-offset-bg;
|
|
}
|
|
|
|
/* Respect a reduced-motion preference rather than animating regardless. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
/* Horizontal scrollers (pipeline boards, wide tables) get momentum
|
|
scrolling and snap on touch, without hijacking the page. */
|
|
.scroll-x {
|
|
@apply overflow-x-auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
scrollbar-width: thin;
|
|
overscroll-behavior-x: contain;
|
|
}
|
|
|
|
.scroll-x::-webkit-scrollbar {
|
|
height: 6px;
|
|
}
|
|
.scroll-x::-webkit-scrollbar-thumb {
|
|
background-color: hsl(var(--border));
|
|
border-radius: 3px;
|
|
}
|
|
|
|
/* Touch targets. 44px is the documented iOS minimum and the difference
|
|
between a usable and a frustrating phone experience. */
|
|
.tap {
|
|
@apply min-h-[44px] min-w-[44px];
|
|
}
|
|
|
|
.card {
|
|
@apply rounded-2xl border border-border/90 bg-surface;
|
|
box-shadow: 0 1px 2px hsl(var(--shadow) / 0.035), 0 12px 36px hsl(var(--shadow) / 0.025);
|
|
}
|
|
|
|
.app-canvas {
|
|
background-color: hsl(var(--bg));
|
|
background-image:
|
|
radial-gradient(circle at 78% -12%, hsl(var(--accent-subtle) / 0.7), transparent 34rem),
|
|
linear-gradient(to bottom, hsl(var(--surface) / 0.25), transparent 28rem);
|
|
background-attachment: fixed;
|
|
}
|
|
|
|
/*
|
|
* The auth surface is intentionally monochrome even if a signed-in user has
|
|
* selected another accent. It is public brand chrome, and forcing the local
|
|
* dark token set keeps controls legible without changing the stored theme.
|
|
*/
|
|
.auth-shell {
|
|
--bg: 240 10% 3%;
|
|
--surface: 240 7% 6%;
|
|
--surface-2: 240 5% 11%;
|
|
--border: 240 4% 22%;
|
|
--fg: 0 0% 98%;
|
|
--muted: 240 5% 67%;
|
|
--accent: 0 0% 98%;
|
|
--accent-fg: 0 0% 98%;
|
|
--accent-on: 240 6% 10%;
|
|
--accent-subtle: 240 4% 16%;
|
|
color-scheme: dark;
|
|
}
|
|
|
|
.auth-field-motion {
|
|
animation: auth-field-drift 26s ease-in-out infinite alternate;
|
|
will-change: transform;
|
|
}
|
|
|
|
.auth-visual {
|
|
opacity: 0.52;
|
|
}
|
|
|
|
.auth-visual::after {
|
|
position: absolute;
|
|
inset: 0;
|
|
content: '';
|
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.92' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.72'/%3E%3C/svg%3E");
|
|
background-size: 180px 180px;
|
|
mix-blend-mode: soft-light;
|
|
opacity: 0.14;
|
|
}
|
|
|
|
.auth-visual-vignette {
|
|
background:
|
|
linear-gradient(to bottom, hsl(var(--bg) / 0.18), hsl(var(--bg) / 0.72) 34%, hsl(var(--bg)) 100%),
|
|
linear-gradient(to right, hsl(var(--bg) / 0.18), hsl(var(--bg) / 0.42));
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
.auth-visual {
|
|
opacity: 1;
|
|
}
|
|
|
|
.auth-visual-vignette {
|
|
background:
|
|
linear-gradient(to right, transparent 48%, hsl(var(--bg) / 0.28) 78%, hsl(var(--bg) / 0.72)),
|
|
linear-gradient(to bottom, hsl(var(--bg) / 0.06), transparent 18%, hsl(var(--bg) / 0.28));
|
|
}
|
|
}
|
|
|
|
@keyframes auth-field-drift {
|
|
0% {
|
|
transform: translate3d(-1.4%, -0.8%, 0) scale(1.045);
|
|
}
|
|
50% {
|
|
transform: translate3d(0.8%, 0.6%, 0) scale(1.075);
|
|
}
|
|
100% {
|
|
transform: translate3d(-0.3%, 1.1%, 0) scale(1.055);
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.auth-field-motion {
|
|
animation: none;
|
|
transform: scale(1.045);
|
|
will-change: auto;
|
|
}
|
|
}
|
|
|
|
/* Tabular figures keep money and percentages from jittering as they
|
|
update, which matters on a dashboard that refreshes. */
|
|
.nums {
|
|
font-variant-numeric: tabular-nums;
|
|
font-feature-settings: 'tnum';
|
|
}
|
|
}
|