// Screens for Variant 1: Light Liquid Glass (Apple iOS 26 default)
const lightTheme = {
dark: false,
studio: false,
bg: '#f7f8fa',
bgGradient: 'linear-gradient(180deg, #ffffff 0%, #f7f8fa 60%, #f4f5f7 100%)',
pageBg: '#f7f8fa',
card: '#FFFFFF',
cardAlt: '#f4f5f7',
text: '#1a1d2b',
textSec: '#6b7280',
textTer: 'rgba(26,29,43,0.34)',
accent: '#6c8388',
accentSoft: 'rgba(108,131,136,0.12)',
onAccent: '#FFFFFF',
green: '#4f7a5b',
greenSoft: 'rgba(79,122,91,0.14)',
orange: '#b07a3c',
orangeSoft: 'rgba(176,122,60,0.14)',
red: '#b0474b',
redSoft: 'rgba(176,71,75,0.10)',
purple: '#6c6f8a',
sep: 'rgba(26,29,43,0.08)',
// Interaction surfaces
chip: 'rgba(26,29,43,0.06)', // soft panel / icon tile fill
chipStrong: 'rgba(26,29,43,0.09)', // segmented track
solidBg: '#1a1d2b', // primary ink buttons + selected chips
solidFg: '#ffffff', // text on solidBg
sheetBg: '#FFFFFF',
inputBg: '#FFFFFF',
inputBorder: 'rgba(26,29,43,0.14)',
scrim: 'rgba(12,16,20,0.42)',
grabber: 'rgba(26,29,43,0.18)',
shadowCard: '0 1px 2px rgba(26,29,43,0.03), 0 6px 18px rgba(26,29,43,0.05)'
};
const darkTheme = {
dark: true,
bg: '#000000',
bgGradient: 'linear-gradient(180deg, #0a0a0c 0%, #000000 60%, #000000 100%)',
pageBg: '#000000',
card: '#1C1C1E',
cardAlt: '#2C2C2E',
text: '#FFFFFF',
textSec: 'rgba(235,235,245,0.62)',
textTer: 'rgba(235,235,245,0.32)',
accent: '#0A84FF',
accentSoft: 'rgba(10,132,255,0.22)',
onAccent: '#FFFFFF',
green: '#30D158',
greenSoft: 'rgba(48,209,88,0.20)',
orange: '#FF9F0A',
orangeSoft: 'rgba(255,159,10,0.20)',
red: '#FF453A',
redSoft: 'rgba(255,69,58,0.20)',
purple: '#BF5AF2',
sep: 'rgba(84,84,88,0.55)',
chip: 'rgba(120,120,128,0.28)',
chipStrong: 'rgba(120,120,128,0.36)',
solidBg: '#48484A',
solidFg: '#ffffff',
sheetBg: '#1C1C1E',
inputBg: '#2C2C2E',
inputBorder: 'rgba(84,84,88,0.6)',
scrim: 'rgba(0,0,0,0.6)',
grabber: 'rgba(235,235,245,0.3)',
shadowCard: '0 1px 2px rgba(0,0,0,0.5), 0 8px 24px rgba(0,0,0,0.4)'
};
// Theme context — every screen reads its palette via useTheme()
const ThemeContext = React.createContext(lightTheme);
const useTheme = () => React.useContext(ThemeContext);
// ─── Studio design (AudioFlow design system) ────────────────
// Near-monochrome: ink on white/greys, one desaturated teal-grey accent, grey shadows,
// hairline translucent-ink borders, larger card radii. SF/system font (already app default).
const studioTheme = {
dark: false,
studio: true,
bg: '#ffffff',
bgGradient: 'linear-gradient(180deg, #ffffff 0%, #f7f8fa 100%)',
pageBg: '#f7f8fa',
card: '#ffffff',
cardAlt: '#f4f5f7',
text: '#1a1d2b',
textSec: 'rgba(26,29,43,0.60)',
textTer: 'rgba(26,29,43,0.34)',
accent: '#6c8388',
accentSoft: 'rgba(108,131,136,0.12)',
onAccent: '#ffffff',
green: '#4d7c5a', greenSoft: 'rgba(77,124,90,0.14)',
orange: '#b07a3c', orangeSoft: 'rgba(176,122,60,0.14)',
red: '#a8443f', redSoft: 'rgba(168,68,63,0.10)',
purple: '#6c6f88',
sep: 'rgba(26,29,43,0.10)',
chip: 'rgba(26,29,43,0.06)',
chipStrong: 'rgba(26,29,43,0.10)',
solidBg: '#1a1d2b', solidFg: '#ffffff',
sheetBg: '#ffffff',
inputBg: '#ffffff', inputBorder: 'rgba(26,29,43,0.14)',
scrim: 'rgba(12,16,20,0.42)', grabber: 'rgba(26,29,43,0.18)',
shadowCard: '0 1px 2px rgba(26,29,43,0.05), 0 10px 30px rgba(26,29,43,0.07)',
radiusCard: 20, radiusPill: 999,
accents: ['#6c8388', '#4d6266']
};
const studioDarkTheme = {
dark: true,
studio: true,
bg: '#0c1014',
bgGradient: 'linear-gradient(180deg, #0e1216 0%, #0c1014 100%)',
pageBg: '#0c1014',
card: '#151a1f',
cardAlt: '#1c222a',
text: '#f2f4f6',
textSec: 'rgba(242,244,246,0.62)',
textTer: 'rgba(242,244,246,0.34)',
accent: '#8aa1a6',
accentSoft: 'rgba(138,161,166,0.20)',
onAccent: '#0c1014',
green: '#6fae86', greenSoft: 'rgba(111,174,134,0.20)',
orange: '#c79a5e', orangeSoft: 'rgba(199,154,94,0.20)',
red: '#cf6b64', redSoft: 'rgba(207,107,100,0.20)',
purple: '#9a9db8',
sep: 'rgba(242,244,246,0.12)',
chip: 'rgba(242,244,246,0.08)',
chipStrong: 'rgba(242,244,246,0.14)',
solidBg: '#f2f4f6', solidFg: '#0c1014',
sheetBg: '#151a1f',
inputBg: '#1c222a', inputBorder: 'rgba(242,244,246,0.16)',
scrim: 'rgba(0,0,0,0.6)', grabber: 'rgba(242,244,246,0.28)',
shadowCard: '0 1px 2px rgba(0,0,0,0.5), 0 8px 24px rgba(0,0,0,0.4)',
radiusCard: 20, radiusPill: 999,
accents: ['#8aa1a6', '#6c8388']
};
// Accent swatches for the built-in light/dark themes too
lightTheme.accents = ['#6c8388', '#4d6266', '#1a1d2b', '#4f7a5b', '#b07a3c', '#6c6f8a'];
darkTheme.accents = ['#0A84FF', '#5E5CE6', '#30D158', '#FF9F0A', '#FF375F', '#BF5AF2'];
const THEMES = { light: lightTheme, dark: darkTheme, studio: studioTheme, studioDark: studioDarkTheme };
const THEME_META = [
{ id: 'studio', label: 'Studio' },
{ id: 'studioDark', label: 'Studio Dunkel' }];
function rfHexToRgba(hex, a) {
let h = (hex || '').replace('#', '');
if (h.length === 3) h = h.split('').map((c) => c + c).join('');
const n = parseInt(h, 16);
if (isNaN(n)) return hex;
return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${a})`;
}
// Returns a theme with the chosen accent applied (or the base theme unchanged).
function applyAccent(base, accent) {
if (!accent || accent === base.accent) return base;
return { ...base, accent, accentSoft: rfHexToRgba(accent, base.dark ? 0.22 : 0.14) };
}
// Tiny SF-style icon factory
const Icon = ({ d, size = 17, color = 'currentColor', sw = 1.8, fill = false }) =>
;
const icons = {
trailer: 'M3 16h2m14 0h2M5 16a2 2 0 104 0M15 16a2 2 0 104 0M3 16V8a1 1 0 011-1h14a2 2 0 012 2v7M3 16h12',
speaker: 'M8 4h8a1 1 0 011 1v14a1 1 0 01-1 1H8a1 1 0 01-1-1V5a1 1 0 011-1zM12 9.5a2 2 0 100 0.001M12 15.5a3 3 0 100 0.001',
mic: 'M12 2a3 3 0 00-3 3v6a3 3 0 006 0V5a3 3 0 00-3-3zM5 11a7 7 0 0014 0M12 18v3M9 21h6',
cable: 'M4 12a4 4 0 014-4h2v8H8a4 4 0 01-4-4zM20 12a4 4 0 01-4 4h-2V8h2a4 4 0 014 4zM10 12h4',
cal: 'M4 6a2 2 0 012-2h12a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM4 10h16M8 2v4M16 2v4',
euro: 'M18 7a6 6 0 00-9 0M18 17a6 6 0 01-9 0M5 10h10M5 14h10',
doc: 'M6 2h8l6 6v12a2 2 0 01-2 2H6a2 2 0 01-2-2V4a2 2 0 012-2zM14 2v6h6M9 14h6M9 18h4',
bell: 'M12 2a6 6 0 016 6v4l2 3H4l2-3V8a6 6 0 016-6zM10 19a2 2 0 004 0',
plus: 'M12 5v14M5 12h14',
search: 'M11 4a7 7 0 100 14 7 7 0 000-14zM21 21l-5.2-5.2',
filter: 'M4 6h16M7 12h10M10 18h4',
back: 'M15 6l-6 6 6 6',
chev: 'M9 6l6 6-6 6',
more: 'M5 12h.01M12 12h.01M19 12h.01',
pin: 'M12 2v8M12 10l-4 4M12 10l4 4M9 14h6l-2 7h-2l-2-7z',
camera: 'M4 8a2 2 0 012-2h2l2-2h4l2 2h2a2 2 0 012 2v10a2 2 0 01-2 2H6a2 2 0 01-2-2V8zM12 17a4 4 0 100-8 4 4 0 000 8z',
check: 'M5 12l5 5L20 7',
arrow: 'M5 12h14M13 5l7 7-7 7',
sound: 'M3 9v6h4l5 4V5L7 9H3zM16 8a5 5 0 010 8M19 5a9 9 0 010 14',
star: 'M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1L12 2z',
download: 'M12 4v12M6 10l6 6 6-6M4 20h16',
edit: 'M4 20h4l11-11-4-4L4 16v4zM14 5l4 4'
};
// ─────────────────────────────────────────────────────────────
// Equipment glyphs — solid, filled (like the user's reference speaker)
// ─────────────────────────────────────────────────────────────
function EqGlyph({ kind = 'speaker', size = 26, color = '#1c1c1e' }) {
const common = { width: size, height: size, viewBox: '0 0 24 24' };
switch (kind) {
case 'speaker':
return (
);
case 'mic':
return (
);
case 'mixer':
return (
);
case 'trailer':
return (
);
case 'light':
return (
);
case 'cable':
return (
);
case 'case':
return (
);
case 'light2':
case 'par':
return (
);
default:
return ;
}
}
// ─────────────────────────────────────────────────────────────
// Themed PNG icon — swaps between light/dark image based on theme.
// Use for richly-colored / detailed icons that don't fit the stroke pattern.
// ─────────────────────────────────────────────────────────────
function ImgIcon({ light, dark, size = 22, alt = '', style }) {
const t = useTheme();
const src = t && t.dark ? dark : light;
return (
);
}
// Canonical themed icon paths for the new equipment + logistik glyphs.
// Per user request: use the colored/filled (dark) version in BOTH light and dark modes.
const IMG_ICONS = {
equipment: { light: 'assets/icon-equipment-dark.png', dark: 'assets/icon-equipment-dark.png' },
truck: { light: 'assets/icon-truck-dark.png', dark: 'assets/icon-truck-dark.png' },
// Line-art glyphs (black on transparent) — invert in dark mode.
warenhaus: { light: 'assets/icon-warenhaus.png', dark: 'assets/icon-warenhaus.png', invertDark: true },
lager: { light: 'assets/icon-lager.png', dark: 'assets/icon-lager.png', invertDark: true },
lieferwagen: { light: 'assets/icon-lieferwagen.png', dark: 'assets/icon-lieferwagen.png', invertDark: true }
};
const EQ_GLYPHS = ['speaker', 'mic', 'mixer', 'trailer', 'light', 'cable', 'case', 'par'];
const EQ_EMOJIS = ['🔊', '🎤', '🎚️', '🎛️', '🚚', '🚐', '💡', '🔌', '📦', '🎸', '🥁', '⚡'];
// Equipment avatar — photo > emoji > glyph, on a soft tile
function EqAvatar({ item = {}, size = 54, radius = 12 }) {
const t = useTheme();
const glyphInk = t.dark ? 'rgba(235,235,245,0.85)' : '#1c1c1e';
const tile = {
width: size, height: size, borderRadius: radius, flexShrink: 0, background: 'transparent',
display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden'
};
if (item.photo) {
return (