Files
ApiCore/ApiCore-Frontend/src/app/globals.css
T
Nick SzaunigandClaude Opus 5.5 6e13236d49 feat: holographic 3D hive scene and Futtertasche (Beute L3, first cut)
- 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]>
2026-10-16 09:12:03 +02:00

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;
}
}