- 3D dependencies (three, react-three-fiber, drei, postprocessing, motion), exact versions, supply-chain checked; scene loaded only on demand on the colony page (283 kB gzip), SVG stays as fallback - glass hologram per owner design 2026-10-16: true Liebig Zander shapes (grips, rebate, Wanderboden foot), cool blue edge glow, click any part -> it flies to the front while the rest stays as a faint ghost; focus shows content (feed levels, Bausperre, pockets); frames rise on hover and take their type look (colour + comb pattern) - full-bleed card with frosted overlays and glass buttons - Futtertasche (FRAME_FEEDER): new part kind occupying frame slots with a fill level, catalog row, validator rules in both ports, outline, sheets, follow-ups, settings Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
431 lines
14 KiB
CSS
431 lines
14 KiB
CSS
@import "tailwindcss";
|
|
@import "tw-animate-css";
|
|
@import "shadcn/tailwind.css";
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@theme inline {
|
|
--color-background: var(--background);
|
|
--color-foreground: var(--foreground);
|
|
--font-sans: var(--font-body);
|
|
--font-mono: var(--font-data);
|
|
--font-heading: var(--font-heading);
|
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
--color-sidebar-border: var(--sidebar-border);
|
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
--color-sidebar-accent: var(--sidebar-accent);
|
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
--color-sidebar-primary: var(--sidebar-primary);
|
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
--color-sidebar: var(--sidebar);
|
|
--color-chart-5: var(--chart-5);
|
|
--color-chart-4: var(--chart-4);
|
|
--color-chart-3: var(--chart-3);
|
|
--color-chart-2: var(--chart-2);
|
|
--color-chart-1: var(--chart-1);
|
|
--color-ring: var(--ring);
|
|
--color-input: var(--input);
|
|
--color-border: var(--border);
|
|
--color-destructive: var(--destructive);
|
|
--color-warning: var(--warning);
|
|
--color-warning-foreground: var(--warning-foreground);
|
|
--color-success: var(--success);
|
|
--color-success-foreground: var(--success-foreground);
|
|
--color-condition-excellent: var(--condition-excellent);
|
|
--color-condition-excellent-foreground: var(--condition-excellent-foreground);
|
|
--color-condition-good: var(--condition-good);
|
|
--color-condition-good-foreground: var(--condition-good-foreground);
|
|
--color-condition-average: var(--condition-average);
|
|
--color-condition-average-foreground: var(--condition-average-foreground);
|
|
--color-condition-weak: var(--condition-weak);
|
|
--color-condition-weak-foreground: var(--condition-weak-foreground);
|
|
--color-condition-critical: var(--condition-critical);
|
|
--color-condition-critical-foreground: var(--condition-critical-foreground);
|
|
--color-holo-bg: var(--holo-bg);
|
|
--color-holo-edge: var(--holo-edge);
|
|
--color-holo-edge-strong: var(--holo-edge-strong);
|
|
--color-holo-fill: var(--holo-fill);
|
|
--color-holo-label-bg: var(--holo-label-bg);
|
|
--color-holo-label-fg: var(--holo-label-fg);
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
--color-accent: var(--accent);
|
|
--color-muted-foreground: var(--muted-foreground);
|
|
--color-muted: var(--muted);
|
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
--color-secondary: var(--secondary);
|
|
--color-primary-foreground: var(--primary-foreground);
|
|
--color-primary: var(--primary);
|
|
--color-popover-foreground: var(--popover-foreground);
|
|
--color-popover: var(--popover);
|
|
--color-card-foreground: var(--card-foreground);
|
|
--color-card: var(--card);
|
|
--radius-sm: calc(var(--radius) * 0.6);
|
|
--radius-md: calc(var(--radius) * 0.8);
|
|
--radius-lg: var(--radius);
|
|
--radius-xl: calc(var(--radius) * 1.4);
|
|
--radius-2xl: calc(var(--radius) * 1.8);
|
|
--radius-3xl: calc(var(--radius) * 2.2);
|
|
--radius-4xl: calc(var(--radius) * 2.6);
|
|
--animate-page-in: page-in 260ms ease-out;
|
|
}
|
|
|
|
@keyframes page-in {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(6px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
:root {
|
|
--background: oklch(0.955 0.017 80);
|
|
--foreground: oklch(0.22 0.028 55);
|
|
--card: oklch(0.995 0.004 90);
|
|
--card-foreground: oklch(0.22 0.028 55);
|
|
--popover: oklch(0.995 0.004 90);
|
|
--popover-foreground: oklch(0.22 0.028 55);
|
|
--primary: oklch(0.6 0.15 48);
|
|
--primary-foreground: oklch(0.99 0.02 85);
|
|
--secondary: oklch(0.44 0.07 145);
|
|
--secondary-foreground: oklch(0.98 0.015 100);
|
|
--muted: oklch(0.9 0.02 82);
|
|
--muted-foreground: oklch(0.48 0.032 65);
|
|
--accent: oklch(0.93 0.032 74);
|
|
--accent-foreground: oklch(0.26 0.045 50);
|
|
--destructive: oklch(0.5 0.16 30);
|
|
--border: oklch(0.85 0.024 78);
|
|
--input: oklch(0.85 0.024 78);
|
|
--ring: oklch(0.6 0.15 48);
|
|
--warning: oklch(0.68 0.14 75);
|
|
--warning-foreground: oklch(0.24 0.04 60);
|
|
--success: oklch(0.46 0.06 145);
|
|
--success-foreground: oklch(0.98 0.015 100);
|
|
--condition-excellent: oklch(0.58 0.13 145);
|
|
--condition-excellent-foreground: oklch(0.16 0.04 145);
|
|
--condition-good: oklch(0.64 0.13 118);
|
|
--condition-good-foreground: oklch(0.26 0.05 118);
|
|
--condition-average: oklch(0.7 0.14 90);
|
|
--condition-average-foreground: oklch(0.28 0.05 90);
|
|
--condition-weak: oklch(0.68 0.15 55);
|
|
--condition-weak-foreground: oklch(0.26 0.05 55);
|
|
--condition-critical: oklch(0.64 0.16 30);
|
|
--condition-critical-foreground: oklch(0.15 0.04 30);
|
|
--chart-1: oklch(0.6 0.135 50);
|
|
--chart-2: oklch(0.46 0.06 145);
|
|
--chart-3: oklch(0.68 0.14 75);
|
|
--chart-4: oklch(0.5 0.16 30);
|
|
--chart-5: oklch(0.55 0.05 250);
|
|
--radius: 0.75rem;
|
|
--sidebar: oklch(0.995 0.004 90);
|
|
--sidebar-foreground: oklch(0.22 0.028 55);
|
|
--sidebar-primary: oklch(0.6 0.15 48);
|
|
--sidebar-primary-foreground: oklch(0.99 0.02 85);
|
|
--sidebar-accent: oklch(0.93 0.032 74);
|
|
--sidebar-accent-foreground: oklch(0.26 0.045 50);
|
|
--sidebar-border: oklch(0.85 0.024 78);
|
|
--sidebar-ring: oklch(0.6 0.15 48);
|
|
--gradient-honey: linear-gradient(135deg, oklch(0.74 0.15 62) 0%, oklch(0.56 0.16 38) 100%);
|
|
--gradient-sage: linear-gradient(135deg, oklch(0.64 0.09 150) 0%, oklch(0.4 0.075 145) 100%);
|
|
--gradient-sky: linear-gradient(135deg, oklch(0.68 0.1 235) 0%, oklch(0.5 0.1 255) 100%);
|
|
--shadow-elevated: 0 1px 2px -1px oklch(0.3 0.05 50 / 8%), 0 8px 24px -8px oklch(0.3 0.05 50 / 16%);
|
|
--shadow-soft: 0 1px 2px -1px oklch(0.3 0.05 50 / 6%), 0 4px 12px -4px oklch(0.3 0.05 50 / 10%);
|
|
/* Hive "screen": always dark, in light and dark mode (spec 9.3). */
|
|
--holo-bg: oklch(0.18 0.03 250);
|
|
--holo-edge: oklch(0.72 0.15 243);
|
|
--holo-edge-strong: oklch(0.87 0.1 238);
|
|
--holo-fill: oklch(0.78 0.14 220 / 10%);
|
|
--holo-label-bg: oklch(0.26 0.04 250);
|
|
--holo-label-fg: oklch(0.95 0.02 230);
|
|
}
|
|
|
|
.dark {
|
|
--background: oklch(0.2 0.018 55);
|
|
--foreground: oklch(0.94 0.02 80);
|
|
--card: oklch(0.25 0.02 55);
|
|
--card-foreground: oklch(0.94 0.02 80);
|
|
--popover: oklch(0.25 0.02 55);
|
|
--popover-foreground: oklch(0.94 0.02 80);
|
|
--primary: oklch(0.72 0.13 55);
|
|
--primary-foreground: oklch(0.2 0.02 55);
|
|
--secondary: oklch(0.62 0.075 145);
|
|
--secondary-foreground: oklch(0.15 0.02 145);
|
|
--muted: oklch(0.29 0.018 55);
|
|
--muted-foreground: oklch(0.7 0.025 75);
|
|
--accent: oklch(0.32 0.03 60);
|
|
--accent-foreground: oklch(0.94 0.02 80);
|
|
--destructive: oklch(0.62 0.16 30);
|
|
--border: oklch(1 0 0 / 12%);
|
|
--input: oklch(1 0 0 / 16%);
|
|
--ring: oklch(0.72 0.13 55);
|
|
--warning: oklch(0.74 0.13 75);
|
|
--warning-foreground: oklch(0.2 0.03 60);
|
|
--success: oklch(0.62 0.075 145);
|
|
--success-foreground: oklch(0.15 0.02 145);
|
|
--condition-excellent: oklch(0.66 0.11 145);
|
|
--condition-excellent-foreground: oklch(0.2 0.04 145);
|
|
--condition-good: oklch(0.72 0.12 118);
|
|
--condition-good-foreground: oklch(0.22 0.04 118);
|
|
--condition-average: oklch(0.76 0.13 90);
|
|
--condition-average-foreground: oklch(0.24 0.04 90);
|
|
--condition-weak: oklch(0.74 0.13 55);
|
|
--condition-weak-foreground: oklch(0.22 0.04 55);
|
|
--condition-critical: oklch(0.62 0.16 30);
|
|
--condition-critical-foreground: oklch(0.2 0.04 30);
|
|
--chart-1: oklch(0.72 0.13 55);
|
|
--chart-2: oklch(0.62 0.075 145);
|
|
--chart-3: oklch(0.74 0.13 75);
|
|
--chart-4: oklch(0.62 0.16 30);
|
|
--chart-5: oklch(0.65 0.06 250);
|
|
--sidebar: oklch(0.22 0.02 55);
|
|
--sidebar-foreground: oklch(0.94 0.02 80);
|
|
--sidebar-primary: oklch(0.72 0.13 55);
|
|
--sidebar-primary-foreground: oklch(0.2 0.02 55);
|
|
--sidebar-accent: oklch(0.32 0.03 60);
|
|
--sidebar-accent-foreground: oklch(0.94 0.02 80);
|
|
--sidebar-border: oklch(1 0 0 / 12%);
|
|
--sidebar-ring: oklch(0.72 0.13 55);
|
|
--gradient-honey: linear-gradient(135deg, oklch(0.62 0.14 55) 0%, oklch(0.38 0.13 40) 100%);
|
|
--gradient-sage: linear-gradient(135deg, oklch(0.5 0.08 148) 0%, oklch(0.3 0.06 145) 100%);
|
|
--gradient-sky: linear-gradient(135deg, oklch(0.55 0.09 235) 0%, oklch(0.34 0.09 255) 100%);
|
|
--shadow-elevated: 0 1px 2px -1px oklch(0 0 0 / 30%), 0 8px 24px -8px oklch(0 0 0 / 45%);
|
|
--shadow-soft: 0 1px 2px -1px oklch(0 0 0 / 25%), 0 4px 12px -4px oklch(0 0 0 / 30%);
|
|
/* Hive "screen": always dark, in light and dark mode (spec 9.3). */
|
|
--holo-bg: oklch(0.18 0.03 250);
|
|
--holo-edge: oklch(0.72 0.15 243);
|
|
--holo-edge-strong: oklch(0.87 0.1 238);
|
|
--holo-fill: oklch(0.78 0.14 220 / 10%);
|
|
--holo-label-bg: oklch(0.26 0.04 250);
|
|
--holo-label-fg: oklch(0.95 0.02 230);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
App chrome (see app/(app)/layout.tsx)
|
|
The page scrolls on the window. On phones a fixed bottom nav sits over its
|
|
lower edge, on iOS above the home indicator (needs viewport-fit=cover).
|
|
Content padding, the sticky save bar, the quick-add button and the focus
|
|
scroll-padding below all derive from these values, so they stay in step.
|
|
------------------------------------------------------------------------ */
|
|
:root {
|
|
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
|
--safe-area-left: env(safe-area-inset-left, 0px);
|
|
--safe-area-right: env(safe-area-inset-right, 0px);
|
|
--bottom-nav-height: calc(4rem + var(--safe-area-bottom));
|
|
/* Sticky header (3.5rem) plus breathing room. */
|
|
--header-clearance: 4.5rem;
|
|
/* StickySubmit with its 48px button (~4.6rem) plus breathing room. */
|
|
--sticky-submit-clearance: 5.5rem;
|
|
/*
|
|
Toasts sit above the sticky save bar. Below sm also above the bottom nav,
|
|
which carries the quick-add button (nav + 1rem + 3rem, less than the bar).
|
|
*/
|
|
--toast-offset-bottom: calc(var(--bottom-nav-height) + var(--sticky-submit-clearance) + 0.5rem);
|
|
|
|
@variant sm {
|
|
--toast-offset-bottom: calc(var(--safe-area-bottom) + var(--sticky-submit-clearance) + 0.5rem);
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
@apply border-border outline-ring/50;
|
|
}
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
}
|
|
html {
|
|
@apply font-sans;
|
|
/* WCAG 2.4.11: a field scrolled into view by focus must not end up under
|
|
the sticky header, the bottom nav / quick-add button or the save bar. */
|
|
scroll-padding-top: var(--header-clearance);
|
|
scroll-padding-bottom: calc(var(--bottom-nav-height) + var(--sticky-submit-clearance));
|
|
|
|
@variant sm {
|
|
scroll-padding-bottom: calc(var(--sticky-submit-clearance) + var(--safe-area-bottom));
|
|
}
|
|
}
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4 {
|
|
font-family: var(--font-heading);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
::selection {
|
|
background: color-mix(in oklch, var(--primary) 28%, transparent);
|
|
color: var(--foreground);
|
|
}
|
|
input,
|
|
textarea {
|
|
caret-color: var(--primary);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
Motion levels (see lib/motion/motion-preference.ts)
|
|
html[data-motion] = full | subtle | off, chosen per device in the profile.
|
|
The OS "reduce motion" setting always wins over "full".
|
|
`motion-full:` = spatial movement allowed (level full AND system allows).
|
|
------------------------------------------------------------------------ */
|
|
@custom-variant motion-full {
|
|
@media (prefers-reduced-motion: no-preference) {
|
|
&:where(:root:not([data-motion="subtle"]):not([data-motion="off"]) *) {
|
|
@slot;
|
|
}
|
|
}
|
|
}
|
|
|
|
:root {
|
|
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
|
|
/* Off: every state change is instant. Colour and icon still carry the state. */
|
|
:root[data-motion="off"] *,
|
|
:root[data-motion="off"] *::before,
|
|
:root[data-motion="off"] *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
transition-delay: 0ms !important;
|
|
}
|
|
|
|
/* Subtle / system reduce: keep fades, drop movement. */
|
|
:root[data-motion="subtle"] .motion-safe\:animate-page-in {
|
|
animation-name: fade-in;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.motion-safe\:animate-page-in {
|
|
animation: fade-in 160ms ease-out;
|
|
}
|
|
}
|
|
:root[data-motion="subtle"] [data-slot="card"] {
|
|
translate: none !important;
|
|
}
|
|
|
|
@keyframes fade-in {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* --- Task completion: the one authored moment ------------------------------
|
|
The honey cell fills, the tick draws itself, then the row folds away. */
|
|
.honey-check {
|
|
position: relative;
|
|
display: inline-grid;
|
|
place-items: center;
|
|
flex-shrink: 0;
|
|
width: 2.75rem;
|
|
height: 2.75rem;
|
|
border-radius: 9999px;
|
|
color: var(--primary-foreground);
|
|
outline: none;
|
|
}
|
|
.honey-check::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0.375rem;
|
|
border-radius: 0.6rem;
|
|
border: 2px solid color-mix(in oklch, var(--primary) 65%, var(--border));
|
|
background: var(--card);
|
|
transition:
|
|
border-color 150ms ease-out,
|
|
background-color 150ms ease-out;
|
|
}
|
|
.honey-check::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0.375rem;
|
|
border-radius: 0.6rem;
|
|
background: var(--gradient-honey);
|
|
clip-path: circle(0% at 50% 60%);
|
|
transition: clip-path 420ms var(--ease-out-expo);
|
|
}
|
|
.honey-check:hover::before {
|
|
border-color: var(--primary);
|
|
background: color-mix(in oklch, var(--primary) 8%, var(--card));
|
|
}
|
|
.honey-check:focus-visible::before {
|
|
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 45%, transparent);
|
|
}
|
|
.honey-check svg {
|
|
position: relative;
|
|
z-index: 1;
|
|
width: 1.1rem;
|
|
height: 1.1rem;
|
|
stroke-dasharray: 24;
|
|
stroke-dashoffset: 24;
|
|
transition: stroke-dashoffset 260ms var(--ease-out-expo) 160ms;
|
|
}
|
|
.honey-check[aria-checked="true"]::after {
|
|
clip-path: circle(75% at 50% 60%);
|
|
}
|
|
.honey-check[aria-checked="true"] svg {
|
|
stroke-dashoffset: 0;
|
|
}
|
|
|
|
.today-row {
|
|
display: grid;
|
|
grid-template-rows: 1fr;
|
|
opacity: 1;
|
|
transition:
|
|
grid-template-rows 320ms var(--ease-out-expo) 380ms,
|
|
opacity 220ms ease-out 360ms;
|
|
}
|
|
.today-row > * {
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
.today-row[data-leaving="true"] {
|
|
grid-template-rows: 0fr;
|
|
opacity: 0;
|
|
}
|
|
|
|
/* The next step changes: bring the new one in with a short focus pull. */
|
|
@keyframes next-step-in {
|
|
from {
|
|
opacity: 0;
|
|
filter: blur(6px);
|
|
transform: translateY(8px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
filter: blur(0);
|
|
transform: none;
|
|
}
|
|
}
|
|
@keyframes next-step-fade {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
.next-step-enter {
|
|
animation: next-step-in 480ms var(--ease-out-expo);
|
|
}
|
|
:root[data-motion="subtle"] .next-step-enter {
|
|
animation: next-step-fade 200ms ease-out;
|
|
}
|
|
:root[data-motion="subtle"] .today-row,
|
|
:root[data-motion="subtle"] .honey-check::after {
|
|
transition-duration: 120ms;
|
|
transition-delay: 0ms;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.next-step-enter {
|
|
animation: next-step-fade 200ms ease-out;
|
|
}
|
|
.today-row,
|
|
.honey-check::after,
|
|
.honey-check svg {
|
|
transition-duration: 120ms;
|
|
transition-delay: 0ms;
|
|
}
|
|
} |