MEGAINFRA-WEBAPP/src/app/globals.css
DEls 0a7138f60c fix: harden workforce, plant and financials
Improve workforce scanning, site transfers and timesheet exports.
Audit plant imports, duplicate identities, rates and dashboard totals.
Repair delivery PO selection, periodic rechecks and receiving guards.
Align financial ledgers, supplier periods and mobile layouts.
Add the site map preview, shared theme controls and audit evidence.

Verify 3,455 application tests, six fuel export tests, TypeScript and
an optimized production build. Clean up a leaked test timer and keep
local worktrees and generated reports out of ESLint traversal.

Changed-file lint passes; repository-wide lint retains 33 existing
errors in unchanged files.
2026-10-19 13:18:31 +02:00

387 lines
12 KiB
CSS

@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
:root {
--background: #e8ebed;
--foreground: #333333;
--card: #ffffff;
--card-foreground: #333333;
--popover: #ffffff;
--popover-foreground: #333333;
--primary: #fdb13c;
--primary-ink: #925e0b;
--brand-soft: #f2b165;
--brand-strong: #e08a3c;
--brand-on-dark: #fdb13c;
--primary-foreground: #ffffff;
--secondary: #f3f4f6;
--secondary-foreground: #4b5563;
--muted: #f9fafb;
--muted-foreground: #6b7280;
--accent: #d6e4f0;
--accent-foreground: #1e3a8a;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
--border: #dcdfe2;
--input: #f4f5f7;
--ring: #e05d38;
--chart-1: #fdb13c;
--chart-2: #a1ae3b;
--chart-3: #4f9c5b;
--chart-4: #008372;
--chart-5: #006672;
--chart-6: #2f4858;
--sidebar: #e8ebed;
--sidebar-foreground: #333333;
--sidebar-primary: #fdb13c;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #d6e4f0;
--sidebar-accent-foreground: #1e3a8a;
--sidebar-border: #dcdfe2;
--sidebar-ring: #e05d38;
--font-sans: Inter, ui-sans-serif, system-ui, sans-serif;
--font-serif: "Source Serif 4", ui-serif, Georgia, serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
--radius: 0.75rem;
--shadow-x: 0px;
--shadow-y: 1px;
--shadow-blur: 3px;
--shadow-spread: 0px;
--shadow-opacity: 0.1;
--shadow-color: hsl(0 0% 0%);
--shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10);
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10);
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 2px 4px -1px hsl(0 0% 0% / 0.10);
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 4px 6px -1px hsl(0 0% 0% / 0.10);
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 8px 10px -1px hsl(0 0% 0% / 0.10);
--shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25);
--tracking-normal: 0em;
--spacing: 0.25rem;
}
.dark {
--background: #1c2433;
--foreground: #e5e5e5;
--card: #2a3040;
--card-foreground: #e5e5e5;
--popover: #262b38;
--popover-foreground: #e5e5e5;
--primary: #fdb13c;
--primary-ink: #fdb13c;
--primary-foreground: #ffffff;
--secondary: #2a303e;
--secondary-foreground: #e5e5e5;
--muted: #2a303e;
--muted-foreground: #a3a3a3;
--accent: #2a3656;
--accent-foreground: #bfdbfe;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
--border: #3d4354;
--input: #3d4354;
--ring: #e05d38;
--chart-1: #fdb13c;
--chart-2: #a1ae3b;
--chart-3: #4f9c5b;
--chart-4: #008372;
--chart-5: #006672;
--chart-6: #2f4858;
--sidebar: #2a303f;
--sidebar-foreground: #e5e5e5;
--sidebar-primary: #fdb13c;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #2a3656;
--sidebar-accent-foreground: #bfdbfe;
--sidebar-border: #3d4354;
--sidebar-ring: #fdb13c;
--font-sans: Inter, ui-sans-serif, system-ui, sans-serif;
--font-serif: "Source Serif 4", ui-serif, Georgia, serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
--radius: 0.75rem;
--shadow-x: 0px;
--shadow-y: 1px;
--shadow-blur: 3px;
--shadow-spread: 0px;
--shadow-opacity: 0.1;
--shadow-color: hsl(0 0% 0%);
--shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10);
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10);
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 2px 4px -1px hsl(0 0% 0% / 0.10);
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 4px 6px -1px hsl(0 0% 0% / 0.10);
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.10), 0px 8px 10px -1px hsl(0 0% 0% / 0.10);
--shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25);
}
/* Standalone accent text needs contrast against the surface, while button
fills retain the exact selected colour. Keep foreground labels separate. */
@layer utilities {
.text-primary {
color: var(--primary-ink);
}
}
@theme inline {
--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-destructive-foreground: var(--destructive-foreground);
--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);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-serif: var(--font-serif);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--shadow-2xs: var(--shadow-2xs);
--shadow-xs: var(--shadow-xs);
--shadow-sm: var(--shadow-sm);
--shadow: var(--shadow);
--shadow-md: var(--shadow-md);
--shadow-lg: var(--shadow-lg);
--shadow-xl: var(--shadow-xl);
--shadow-2xl: var(--shadow-2xl);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
html,
body {
height: 100%;
}
body {
@apply bg-background text-foreground antialiased;
font-family: var(--font-sans);
font-feature-settings: "rlig" 1, "calt" 1;
}
::selection {
background: color-mix(in oklab, var(--primary) 35%, transparent);
}
}
/* Rain outlook: a slow drift on the icons of days heavy enough to stop
work, so the eye lands on them without the card feeling busy. */
@keyframes rain-drift {
0%, 100% { transform: translateY(0); opacity: 1; }
50% { transform: translateY(1.5px); opacity: 0.72; }
}
@media (prefers-reduced-motion: no-preference) {
.animate-rain-drift {
animation: rain-drift 2.6s ease-in-out infinite;
}
}
@keyframes wiggle {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-6px); }
40% { transform: translateX(6px); }
60% { transform: translateX(-4px); }
80% { transform: translateX(4px); }
}
/* Subtle scrollbars that match the theme */
@layer utilities {
.scrollbar-thin {
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
.scrollbar-thin::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background-color: var(--border);
border-radius: 9999px;
}
.scrollbar-thin::-webkit-scrollbar-track {
background: transparent;
}
.grid-bg {
background-image:
linear-gradient(to right, color-mix(in oklab, var(--border) 60%, transparent) 1px, transparent 1px),
linear-gradient(to bottom, color-mix(in oklab, var(--border) 60%, transparent) 1px, transparent 1px);
background-size: 32px 32px;
}
}
/* Ask report charts. Categorical slots per the dataviz method, slot 1
mapped to the dashboard accent; validated with validate_palette.js
against the card surface in each mode (light #ffffff, dark #2a3040).
Dark is its own selected steps, not an automatic flip. Light slots 1,
3 and 5 sit below 3:1 on white, so every chart ships a table view. */
.report-chart {
--series-1: var(--brand-strong);
--series-2: #2a78d6;
--series-3: #1baf7a;
--series-4: #4a3aa7;
--series-5: #e87ba4;
--series-6: #008300;
--chart-grid: #e1e0d9;
--chart-axis: #898781;
}
.dark .report-chart {
--series-1: var(--brand-strong);
--series-2: #3987e5;
--series-3: #199e70;
--series-4: #9085e9;
--series-5: #d55181;
--series-6: #c98500;
--chart-grid: rgba(255, 255, 255, 0.08);
--chart-axis: #a3a8b3;
}
/* Printing an answer: hide the app chrome and let the answer fill the page. Triggered by adding .printing-answer to <body>. */
@media print {
body.printing-answer * {
visibility: hidden;
}
body.printing-answer .print-target,
body.printing-answer .print-target * {
visibility: visible;
}
body.printing-answer .print-target {
position: absolute;
inset: 0;
padding: 0;
border: 0;
max-width: none;
}
body.printing-answer .print-hide {
display: none !important;
}
/* Keep table rows intact across page breaks. */
body.printing-answer table {
page-break-inside: auto;
}
body.printing-answer tr {
page-break-inside: avoid;
}
/* Report charts: Recharts measures its container, which print layout
does not re-run, so pin the size. */
body.printing-answer .recharts-responsive-container {
width: 100% !important;
height: 260px !important;
}
body.printing-answer .report-chart {
page-break-inside: avoid;
}
}
/* Worker QR badge cards — rendered on-screen on a worker's detail page
(next to the single "Print badge" button). Sized to standard
ID-badge/credit-card dimensions so the printed card laminates cleanly.
The batch export on the Workers list no longer uses these classes —
it builds a real PDF via pdf-lib instead. */
.worker-qr-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
width: 85mm;
height: 54mm;
padding: 6mm;
border: 1px solid hsl(var(--border));
border-radius: 10px;
background: hsl(var(--card));
box-sizing: border-box;
text-align: center;
break-inside: avoid;
}
.worker-qr-card__brand {
font-size: 9px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: hsl(var(--muted-foreground));
}
.worker-qr-card__qr {
width: 26mm;
height: 26mm;
object-fit: contain;
}
.worker-qr-card__name {
font-size: 13px;
font-weight: 700;
}
.worker-qr-card__number {
font-size: 11px;
font-family: ui-monospace, monospace;
color: #E08A3C;
}
/* MEGA Clock's footer tab bar sits flush with the bottom edge in iOS
standalone mode, where the home-indicator area needs real padding —
nothing in this file already accounts for env(safe-area-inset-*). */
.clock-footer {
padding-bottom: env(safe-area-inset-bottom);
}
/* MEGA Clock scanner viewfinder — an orange line that sweeps top to
bottom inside the QR alignment frame. transform-based for hardware
acceleration, paused when the user prefers reduced motion. */
@keyframes clock-scan-line {
0% { transform: translateY(0); }
50% { transform: translateY(calc(100% + 0px)); }
100% { transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
.clock-scan-line {
animation: clock-scan-line 2.4s ease-in-out infinite;
}
}
/* Fleet popups share the dashboard palette; Leaflet's default is always white. */
.plant-map-popup .leaflet-popup-content-wrapper,
.plant-map-popup .leaflet-popup-tip {
background: var(--popover);
color: var(--popover-foreground);
}
.plant-map-popup .leaflet-popup-content-wrapper {
border: 1px solid var(--border);
}
.plant-map-popup .leaflet-popup-content {
margin: 12px;
}
.plant-map-popup a.leaflet-popup-close-button {
color: var(--muted-foreground);
}