// ========================================================================== // CONFIG — non-colour tunables (colours live in `theme.ts`). // // Centralises the magic numbers that were previously scattered through // SkillTree.svelte so behaviour can be tuned in one place. // ========================================================================== /** * Viewport width (px) below which the mobile layout/behaviour kicks in. * NOTE: JS treats `< 768` as mobile; CSS media queries use `max-width: 767px`. * Both make 768px the first desktop width — keep that pairing if you change this. */ export const MOBILE_BREAKPOINT = 768; /** Pan/zoom limits and step sizes. */ export const ZOOM = { min: 0.3, max: 2.5, keyStep: 0.1, wheelIn: 1.1, wheelOut: 0.9 } as const; /** Background parallax lag factors (fraction of tree movement). */ export const PARALLAX = { star: 0.1, nebula: 0.07, gradient: 0.05 } as const; /** Node radii by size (desktop px); mobile multiplies by MOBILE_RADIUS_FACTOR. */ export const NODE_RADII: Record = { 'x-large': 75, large: 30, medium: 25, small: 20, default: 14 }; export const MOBILE_RADIUS_FACTOR = 0.8; /** Keyboard pan distance per arrow-key press (px). */ export const PAN_SPEED = 50; /** Background particle counts (desktop / mobile). */ export const PARTICLE_COUNTS = { distantStars: { desktop: 200, mobile: 100 }, mediumStars: { desktop: 60, mobile: 30 }, brightStars: { desktop: 15, mobile: 8 }, dust: { desktop: 30, mobile: 15 } } as const; /** Entrance-animation timings (seconds), split by device. */ export const TIMINGS = { titleDuration: { desktop: 2.5, mobile: 1.5 }, zoomDuration: { desktop: 4.5, mobile: 2.5 }, nodeStagger: { desktop: 0.08, mobile: 0.05 } } as const; /** * Whether the visitor has asked for reduced motion. * * This site is unusually motion-heavy — a multi-second forced zoom, a staggered * entrance, ~480 looping tweens and a continuous dust ticker — and none of it * was previously conditional. Read at call time, not module load, so it is * correct under SSR/prerender and picks up an OS-level change on reload. */ export function prefersReducedMotion(): boolean { if (typeof window === 'undefined' || !window.matchMedia) return false; return window.matchMedia('(prefers-reduced-motion: reduce)').matches; } /** Helper: pick a desktop/mobile value. */ export function byDevice(pair: { desktop: T; mobile: T }, isMobile: boolean): T { return isMobile ? pair.mobile : pair.desktop; }