+ {/* Signing in picks up exactly where the login panel left off: the same dot
+ map, except now every route departs at once. The network finishing is
+ the moment — no separate imagery, no plane flying off alone. */}
+
+ {/* The colour rises with the departures rather than sitting there from the start. */}
+
@@ -389,7 +322,7 @@ export default function LoginPage(): React.ReactElement {
style={{
display: 'none',
width: '55%',
- background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
+ background: '#070c1a',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
@@ -401,240 +334,87 @@ export default function LoginPage(): React.ReactElement {
>
- {/* Stars */}
+ {/* Aurora — three drifting colour fields. They are the whole background:
+ deep indigo, a cold cyan and a warm violet, blurred past recognition so
+ what is left is the gradient, not the shapes. */}
+
+
+ {/* Depth: a handful of pinpricks, far enough back to read as distance. */}
- {Array.from({ length: 40 }, (_, i) => (
+ {STARFIELD.map((s, i) => (
0.7 ? 2 : 1,
- height: Math.random() > 0.7 ? 2 : 1,
+ width: s.size,
+ height: s.size,
borderRadius: '50%',
background: 'white',
- opacity: 0.15 + Math.random() * 0.25,
- top: `${Math.random() * 70}%`,
- left: `${Math.random() * 100}%`,
- animationDelay: `${Math.random() * 4}s`,
+ opacity: s.opacity,
+ top: `${s.top}%`,
+ left: `${s.left}%`,
+ animationDelay: `${s.delay}s`,
}}
/>
))}
- {/* Animated glow orbs */}
-
-
+ {/* Coastlines as a dot map, with routes lighting up between cities across
+ it. The geometry is TREK's own Atlas bundle, baked in at build time
+ because this screen is unauthenticated. */}
+
- {/* Animated planes — realistic silhouettes at different sizes/speeds */}
-
- {/* Plane 1 — large, slow, foreground */}
-
-
-
-
-
-
-
-
-
-
-
- {/* Plane 2 — small, faster, higher */}
-
-
-
-
-
-
-
-
-
- {/* Plane 3 — medium, mid-speed */}
-
-
-
-
-
-
-
-
-
-
-
- {/* Plane 4 — tiny, fast, high */}
-
-
-
-
-
-
-
-
-
- {/* Plane 5 — medium, right to left, lower */}
-
-
-
-
-
-
-
-
-
-
-
- {/* Plane 6 — tiny distant */}
-
-
-
-
-
-
-
-
-
-
-
- {/* Logo */}
-
-
+ {/* No max-width: the tagline stays on one line, so the block is allowed to
+ use the whole panel rather than wrapping inside it. */}
+
+ {/* Logo. The soft dark halo is what separates it from the dot map behind —
+ wide and low-opacity rather than a hard drop shadow, so it reads as the
+ map dimming around the mark instead of an outline. */}
+
+
{t('login.tagline')}
{t('login.description')}
-
-
- {[
- { Icon: Map, label: t('login.features.maps'), desc: t('login.features.mapsDesc') },
- { Icon: Zap, label: t('login.features.realtime'), desc: t('login.features.realtimeDesc') },
- { Icon: Wallet, label: t('login.features.budget'), desc: t('login.features.budgetDesc') },
- { Icon: Users, label: t('login.features.collab'), desc: t('login.features.collabDesc') },
- { Icon: CheckSquare, label: t('login.features.packing'), desc: t('login.features.packingDesc') },
- { Icon: BookMarked, label: t('login.features.bookings'), desc: t('login.features.bookingsDesc') },
- { Icon: FolderOpen, label: t('login.features.files'), desc: t('login.features.filesDesc') },
- { Icon: Route, label: t('login.features.routes'), desc: t('login.features.routesDesc') },
- ].map(({ Icon, label, desc }) => (
-
) => {
- e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
- e.currentTarget.style.borderColor = 'rgba(255,255,255,0.12)';
- }}
- onMouseLeave={(e: React.MouseEvent
) => {
- e.currentTarget.style.background = 'rgba(255,255,255,0.04)';
- e.currentTarget.style.borderColor = 'rgba(255,255,255,0.06)';
- }}
- >
-
-
- {label}
-
-
- {desc}
-
-
- ))}
-
-
-
- {t('login.selfHosted')}
-
@@ -1441,20 +1221,45 @@ export default function LoginPage(): React.ReactElement {
diff --git a/client/src/pages/login/LoginWorld.tsx b/client/src/pages/login/LoginWorld.tsx
new file mode 100644
index 000000000..8a684f1a3
--- /dev/null
+++ b/client/src/pages/login/LoginWorld.tsx
@@ -0,0 +1,287 @@
+import React from 'react';
+import { WORLD_GRID_H, WORLD_GRID_W, WORLD_LAT_BOT, WORLD_LAT_TOP, worldDots } from './worldDots';
+
+/**
+ * The login backdrop: TREK's own coastlines as a dot map, with routes lighting up
+ * between cities across it.
+ *
+ * The dots come from the Atlas admin-0 bundle, baked into worldDots.ts — the login
+ * screen is unauthenticated, so it cannot fetch Atlas at runtime.
+ *
+ * Perf: the map itself is drawn once into an offscreen canvas and blitted each
+ * frame; only the handful of live arcs and their city glows are drawn per frame.
+ * No shadowBlur in the frame path.
+ */
+
+/** Landmark cities, the anchors every route runs between. */
+const CITIES: [number, number][] = [
+ [52.52, 13.4], [48.85, 2.35], [51.5, -0.12], [40.4, -3.7], [41.9, 12.5], [59.33, 18.07],
+ [41.0, 28.98], [55.75, 37.62], [52.37, 4.9], [64.15, -21.9], [38.72, -9.14], [50.08, 14.44],
+ [30.04, 31.24], [6.52, 3.38], [-1.29, 36.82], [-33.92, 18.42], [33.57, -7.59],
+ [25.2, 55.27], [19.08, 72.88], [28.61, 77.21], [13.76, 100.5], [1.35, 103.82],
+ [-6.21, 106.85], [35.68, 139.69], [37.57, 126.98], [39.9, 116.4], [31.23, 121.47],
+ [22.32, 114.17], [-33.87, 151.21], [-36.85, 174.76], [-27.47, 153.03],
+ [49.28, -123.12], [37.77, -122.42], [40.71, -74.01], [41.88, -87.63], [43.65, -79.38],
+ [19.43, -99.13], [25.76, -80.19], [4.71, -74.07], [-12.05, -77.04], [-23.55, -46.63],
+ [-34.6, -58.38], [-33.45, -70.67],
+];
+
+interface Arc {
+ from: number;
+ to: number;
+ /** 0 → 1 progress of the light travelling the arc. */
+ t: number;
+ speed: number;
+ /** Stays lit briefly after arrival, then fades. */
+ hold: number;
+ lift: number;
+ /** Milliseconds still to wait before this one starts (takeoff cascade). */
+ delay: number;
+}
+
+const MAX_ARCS = 7;
+const DOT_COLOUR = 'rgba(148, 163, 214, 0.55)';
+
+interface LoginWorldProps {
+ /**
+ * `ambient` is the login backdrop: a few routes at a time, forever.
+ * `takeoff` is the sign-in moment: the whole network fires at once and stays lit.
+ */
+ variant?: 'ambient' | 'takeoff';
+}
+
+export default function LoginWorld({ variant = 'ambient' }: LoginWorldProps): React.ReactElement {
+ const canvasRef = React.useRef
(null);
+ const takeoff = variant === 'takeoff';
+
+ React.useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) return;
+
+ const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
+ const dots = worldDots();
+
+ let raf = 0;
+ let width = 0;
+ let height = 0;
+ let dpr = 1;
+ let mapLayer: HTMLCanvasElement | null = null;
+ /** Map placement in CSS pixels, shared by the layer and the city projection. */
+ let originX = 0;
+ let originY = 0;
+ let cell = 1;
+
+ const project = (lat: number, lon: number) => ({
+ x: originX + ((lon + 180) / 360) * (WORLD_GRID_W - 1) * cell,
+ y: originY + ((WORLD_LAT_TOP - lat) / (WORLD_LAT_TOP - WORLD_LAT_BOT)) * (WORLD_GRID_H - 1) * cell,
+ });
+
+ /** Redraw the static dot map. Called on mount and on resize only. */
+ function buildMap() {
+ // Cover the panel, biased up a little so the busier northern half sits behind
+ // the copy rather than under it.
+ cell = Math.max(width / (WORLD_GRID_W - 1), height / (WORLD_GRID_H - 1)) * 1.08;
+ originX = (width - (WORLD_GRID_W - 1) * cell) / 2;
+ originY = (height - (WORLD_GRID_H - 1) * cell) / 2;
+
+ const layer = document.createElement('canvas');
+ layer.width = Math.max(1, Math.floor(width * dpr));
+ layer.height = Math.max(1, Math.floor(height * dpr));
+ const lc = layer.getContext('2d');
+ if (!lc) return;
+ lc.scale(dpr, dpr);
+ lc.fillStyle = DOT_COLOUR;
+ const r = Math.max(0.6, cell * 0.19);
+ for (const d of dots) {
+ const x = originX + d.x * cell;
+ const y = originY + d.y * cell;
+ if (x < -4 || x > width + 4 || y < -4 || y > height + 4) continue;
+ lc.beginPath();
+ lc.arc(x, y, r, 0, Math.PI * 2);
+ lc.fill();
+ }
+ mapLayer = layer;
+ }
+
+ function resize() {
+ const rect = canvas!.getBoundingClientRect();
+ dpr = Math.min(window.devicePixelRatio || 1, 2);
+ width = rect.width;
+ height = rect.height;
+ canvas!.width = Math.max(1, Math.floor(width * dpr));
+ canvas!.height = Math.max(1, Math.floor(height * dpr));
+ ctx!.setTransform(dpr, 0, 0, dpr, 0, 0);
+ buildMap();
+ }
+
+ const arcs: Arc[] = [];
+
+ function spawnArc(opts?: { delay?: number; speed?: number }) {
+ if (!takeoff && arcs.length >= MAX_ARCS) return;
+ const from = Math.floor(Math.random() * CITIES.length);
+ let to = Math.floor(Math.random() * CITIES.length);
+ if (to === from) to = (to + 1 + Math.floor(Math.random() * 3)) % CITIES.length;
+ arcs.push({
+ from,
+ to,
+ t: 0,
+ // Per millisecond, so 1/speed is the flight time: roughly 3–5s. Long enough
+ // to read as a journey, short enough that the map keeps moving.
+ speed: opts?.speed ?? 0.0002 + Math.random() * 0.00013,
+ hold: 0,
+ // Longer hops bow higher, the way a route reads on a map.
+ lift: 0.16 + Math.random() * 0.16,
+ delay: opts?.delay ?? 0,
+ });
+ }
+
+ /**
+ * Sign-in: the whole network lights up at once. Routes leave in a staggered
+ * cascade so it reads as departures rather than one flash, and nothing fades —
+ * the finished web is the last thing on screen before the app takes over.
+ */
+ function burst() {
+ for (let i = 0; i < 11; i++) {
+ spawnArc({ delay: i * 105 + Math.random() * 70, speed: 0.0013 + Math.random() * 0.0009 });
+ }
+ }
+
+ /** Quadratic arc between two projected cities, bowed away from the straight line. */
+ function arcPoints(a: Arc) {
+ const p1 = project(CITIES[a.from][0], CITIES[a.from][1]);
+ const p2 = project(CITIES[a.to][0], CITIES[a.to][1]);
+ const mx = (p1.x + p2.x) / 2;
+ const my = (p1.y + p2.y) / 2;
+ const dx = p2.x - p1.x;
+ const dy = p2.y - p1.y;
+ const len = Math.hypot(dx, dy) || 1;
+ // Perpendicular offset, always bowing "up" so arcs never dive off-screen.
+ const cx = mx - (dy / len) * len * a.lift * Math.sign(dx || 1);
+ const cy = my + (dx / len) * len * a.lift * Math.sign(dx || 1) - len * 0.08;
+ return { p1, p2, cx, cy };
+ }
+
+ function pointOn(p1: { x: number; y: number }, cx: number, cy: number, p2: { x: number; y: number }, t: number) {
+ const u = 1 - t;
+ return {
+ x: u * u * p1.x + 2 * u * t * cx + t * t * p2.x,
+ y: u * u * p1.y + 2 * u * t * cy + t * t * p2.y,
+ };
+ }
+
+ function cityGlow(x: number, y: number, alpha: number, radius: number) {
+ const g = ctx!.createRadialGradient(x, y, 0, x, y, radius);
+ g.addColorStop(0, `rgba(199, 210, 254, ${alpha})`);
+ g.addColorStop(0.35, `rgba(129, 140, 248, ${alpha * 0.5})`);
+ g.addColorStop(1, 'rgba(99, 102, 241, 0)');
+ ctx!.fillStyle = g;
+ ctx!.beginPath();
+ ctx!.arc(x, y, radius, 0, Math.PI * 2);
+ ctx!.fill();
+ }
+
+ let last = 0;
+ let spawnTimer = 0;
+
+ function frame(now: number) {
+ const dt = last ? Math.min(now - last, 48) : 16;
+ last = now;
+
+ ctx!.clearRect(0, 0, width, height);
+ if (mapLayer) ctx!.drawImage(mapLayer, 0, 0, width, height);
+
+ if (!takeoff) {
+ spawnTimer -= dt;
+ if (spawnTimer <= 0) {
+ spawnArc();
+ spawnTimer = 1600 + Math.random() * 2200;
+ }
+ }
+
+ for (let i = arcs.length - 1; i >= 0; i--) {
+ const a = arcs[i];
+
+ if (a.delay > 0) {
+ a.delay -= dt;
+ continue;
+ }
+
+ const { p1, p2, cx, cy } = arcPoints(a);
+
+ if (a.t < 1) a.t = Math.min(1, a.t + a.speed * dt);
+ else a.hold += dt;
+
+ // Ambient routes retire to make room for the next; the takeoff web stays lit.
+ const fade = takeoff ? 1 : a.hold > 2200 ? Math.max(0, 1 - (a.hold - 2200) / 2400) : 1;
+ if (fade <= 0) {
+ arcs.splice(i, 1);
+ continue;
+ }
+
+ // The drawn portion, with the leading edge brightest.
+ ctx!.strokeStyle = `rgba(129, 140, 248, ${0.5 * fade})`;
+ ctx!.lineWidth = 1.4;
+ ctx!.beginPath();
+ const segs = Math.max(2, Math.round(34 * a.t));
+ for (let s = 0; s <= segs; s++) {
+ const pt = pointOn(p1, cx, cy, p2, (a.t * s) / segs);
+ if (s === 0) ctx!.moveTo(pt.x, pt.y);
+ else ctx!.lineTo(pt.x, pt.y);
+ }
+ ctx!.stroke();
+
+ // Origin lights the moment the route leaves it, destination on arrival.
+ cityGlow(p1.x, p1.y, 0.55 * fade, Math.max(9, cell * 2.6));
+ if (a.t >= 1) cityGlow(p2.x, p2.y, 0.6 * fade, Math.max(11, cell * 3));
+
+ // The travelling head.
+ if (a.t < 1) {
+ const head = pointOn(p1, cx, cy, p2, a.t);
+ cityGlow(head.x, head.y, 0.75, Math.max(7, cell * 2));
+ ctx!.fillStyle = 'rgba(224, 242, 254, 0.95)';
+ ctx!.beginPath();
+ ctx!.arc(head.x, head.y, 1.8, 0, Math.PI * 2);
+ ctx!.fill();
+ }
+ }
+
+ raf = window.requestAnimationFrame(frame);
+ }
+
+ resize();
+ window.addEventListener('resize', resize);
+
+ if (reduced) {
+ // Still frame: the map, no routes firing.
+ ctx.clearRect(0, 0, width, height);
+ if (mapLayer) ctx.drawImage(mapLayer, 0, 0, width, height);
+ } else if (takeoff) {
+ burst();
+ raf = window.requestAnimationFrame(frame);
+ } else {
+ // Open with a few routes already in flight instead of an empty map.
+ for (let i = 0; i < 3; i++) {
+ spawnArc();
+ const a = arcs[arcs.length - 1];
+ if (a) a.t = Math.random() * 0.6;
+ }
+ raf = window.requestAnimationFrame(frame);
+ }
+
+ return () => {
+ window.cancelAnimationFrame(raf);
+ window.removeEventListener('resize', resize);
+ };
+ }, [takeoff]);
+
+ return (
+
+ );
+}
diff --git a/client/src/pages/login/worldDots.ts b/client/src/pages/login/worldDots.ts
new file mode 100644
index 000000000..4e542e7d7
--- /dev/null
+++ b/client/src/pages/login/worldDots.ts
@@ -0,0 +1,23 @@
+// Generated from server/assets/atlas/admin0.geojson.gz — TREK's own Atlas bundle.
+// The login screen is unauthenticated and cannot fetch Atlas, so the coastlines ship
+// as a 300x150 dot grid: sorted cell indices, delta-encoded in base36.
+// Regenerate with the script in the design notes if the bundle ever changes.
+
+export const WORLD_GRID_W = 300;
+export const WORLD_GRID_H = 150;
+export const WORLD_LAT_TOP = 80;
+export const WORLD_LAT_BOT = -58;
+
+const PACKED =
+ '1u.1.1.1.1.1.6.1.1.2.1.1.2.1.1.1.1.1.1.4.1.10.1.1.1.n.1.1.1.1.1.1.1.1.1.1.3.1.j.1.6.1.e.1.b.1.1.1.3.1.1.3g.1.4.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.10.1.1.1.n.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.12.1.b.1.1.1.1.1.1.1.1.1.1.1.1.39.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.2.11.1.1.1.1.o.1.1.1.1.1.1.1.1.1.1.1.1l.5.1.1.1.1.1.1.1.32.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.3.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.1.1.10.1.1.1.1.q.1.1.1.1.2.1.1.1.y.1.1.1.b.5.1.5.1.3.1.1.1.1.1.1.1.1.1.w.21.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.v.1.1.1.t.1.6.1.r.1.1.1.1.1.1.1.1.1.a.1.1.8.1.1.1.1.1.1.1.4.1.1.1.1.1.1.i.2.1.1.1.1.1.4.1.23.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.g.1.1.t.1.1.1.1.1p.1.1.1.1.1.1.1.1.d.1.3.1.1.1.1.1.1.2.1.d.1.i.1.1.1.1.1.1.1.1.1.1.1.1.1.1z.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.i.1.s.1.1.1.v.1.t.1.1.1.1.1.f.1.2.1.1.1.1.i.1.1.1.1.2.g.1.1.1.1.1.1.1.3.1.1.1.1z.1.5.1.1.1.5.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.g.1.1.m.1.1.1.1.1.1.1.1p.1.1.1.1.a.1.2.1.1.2.1.1.1.1.1.1.1.f.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.5.1.3.1.1.1.24.1.4.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.2.2.1.1.1.2.1.1.1.1.1.g.1.1.m.1.1.1.1.1p.1.1.1.1.b.1.1.1.1.1.1.1.1.1.1.1.j.1.b.1.1.1.4.1.8.1.1.1.1.1.1.1.1.1.q.1.1.h.1.p.1.1.1.1.1.1.1.1.1.5.1.2.1.3.1.1.1.1.1.1.3.1.1.1.1.1.2.1.1.1.1.1.1.1.1.d.1.1.1.1.j.1.1.1.1.1.1.1o.1.1.1.1.9.1.1.2.1.1.1.1.1.1.1.1.1.1.11.1.2.1.1.1.1.1.1.1.4.1.2.1.1.1.m.1.1.1.1.d.1.1.1.1.1.1.1.1.1.1.1.1.g.1.3.1.3.1.1.1.1.1.1.6.1.1.1.3.1.2.1.2.1.1.1.1.2.2.1.3.1.1.1.1.1.1.b.1.1.1.1.h.1.1.1.1.1.1.1.a.1.n.1.1.1.1.1.1.1.1.1.i.1.1.1.1.1.1.6.5.1.2.1.1.3.1.12.1.1.5.b.1.1.1.1.1.1.1.1.1.e.1.f.1.1.1.a.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.4.3.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.a.1.1.1.1.i.1.1.1.1.1.x.1.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.7.1.1.1.1.1.2.1.4.1.7.1.1r.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.a.1.1.1.i.2.1.1.1.1.3.4.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.1.4.1.1.b.1.1.1.e.1.2.1.1.1.1.w.1.1.1.1.1.1.1.1.1.1.2.1.3.1.1.1.1.4.1.1.1.2.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.3.1.1.1.1.1x.1.5.1.1.1.6.1.1.1.1.1.1.7.1.1.j.l.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.3.1.2.1.1.1.1.1.4.1.1.1.1.8.1.1.1.e.1.1.1.1.y.1.1.1.1.1.1.2.1.4.1.7.1.1.1.2.1.1.1.1.1.1.1.1.1.4.7.1.3.1.1.2.1.2c.1.4.1.1.1.6.1.1.1.g.r.1.1.8.1.7.1.1.1.1.1.3.1.1.2.1.3.1.2.1.1.1.6.1.1.1.d.1.12.1.1.1.6.5.2.1.1.1.4.1.1.1.1.1.1.i.1.1.1.1.4.2c.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.g.s.1.d.1.1.1.1.1.1.1.7.1.4.1.1.1.1.1.1.6.1.1.1.9.1.1.1.1.8.1.1.1.1.1.1.1.1.1.l.1.1.1.6.1.1.4.1.2.1.1.1.1.1.1.1.1.1.l.1.1.1.2h.1.1.1.1.1.2.1.1.2.1.1.3.1.g.17.1.1.1.1.1.1.4.1.1.1.5.1.1.1.1.1.7.1.1.1.7.1.1.1.1.a.1.1.1.1.4.1.k.1.1.1.1.5.1.2.1.3.4.1.1.1.1.1.34.1.1.1.1.5.1.1.5.1.1.1.1.1.g.13.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.1.4.1.1.a.1.1.7.1.f.1.3.1.1.1.j.1.1.1.1.6.1.1.1.4.1.3.1.1.1.1.1.34.1.1.1.9.1.1.4.1.1.1.g.15.1.1.2.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.b.1.6.1.1.g.1.1.1.1.j.1.1.1.1.1.4.1.1.1.1.1.5.1.1.5.39.1.d.1.j.14.1.1.6.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.5.1.s.1.a.1.1.1.3.4.1.2.8.1.2z.2.1.1.8.1.1.1.1.c.1.1.b.1.1.1.1.4.13.1.b.1.1.4.1.1.1.2.1.1.c.1.1.1.1.1.1.1.t.9.1.1.1.4.4.3.1.5.1.1.2w.1.1.1.1.1.1.1.6.1.1.1.b.4.1.1.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.11.d.1.6.1.1.2.1.d.1.1.1.1.1.y.1.4.1.1.5.4.1.1.1.1.1.1.1.1.1.1.2w.1.3.1.1.1.1.1.1.1.1.1.1.1.i.1.1.1.1.6.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.x.c.2.6.1.3.1.1.f.1.1.x.1.1.5.1.3.1.1.4.1.1.1.1.1.1.1.1.1.1.1.2l.1.1.1.1.1.1.1.1.1.1.4.1.1.1.1.1.1.3.o.1.1.1.1.2.1.1.1.a.1.1.1.1.1.w.1.c.1.6.1.1.1.1.1.1.1.1a.1.1.1.7.1.1.1.1.1.4.1.1.1.2.1.1.2.1.2l.1.1.3.3.1.1.1.1.3.1.2.1.1.w.1.1.1.1.1.d.1.1.1.1.w.1.1.9.2.1.6.1.4.1.18.1.1.1.1.1.1.7.1.2.1.1.3.1.1.1.1.1.1.1.1.1.1.2k.1.e.1.3.1.n.1.7.1.1.1.1.1.1.1.e.1.1.1.1.v.1.1.1.1.7.1.2.c.1.18.1.1.2.1.8.1.1.1.1.3.1.1.2.1.1.1.2.1.2j.1.e.1.5.t.1.1.1.1.2.h.1.1.1.1.1.y.1.1.5.1.1.1.c.1.19.1.1.1.1.8.1.1.1.1.1.1.1.4.1.1.1.1.1.1.1.1.2g.1.f.5.1.r.1.1.1.1.1.l.1.1.1.1.10.1.1.1.2.1.1.e.1.1.1.14.1.1.1.1.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.3.t.1.1.1.1.1.1h.1.1.f.4.1.3.1.1.m.1.1.1.1.o.1.1.1.1.12.2.1.g.1.1.1.12.1.1.1.1.1.1.2.1.5.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.4.1.o.1.1.1.1.4.1.1.1.1.1.1.1d.1.1.1.2.b.3.1.1.5.j.1.1.1.s.1.1.1.1.12.1.1.1.f.1.2.1.12.1.2.1.1.1.2.1.1.2.1.1.1.1.1.3.1.7.6.1.1.n.1.9.1.3.1.y.1.1.1.1.1.9.1.1.1.1.1.1.a.2.1.b.1.7.1.1.1.1.1.1.1.w.1.1.1.1.11.1.1.1.j.1.11.1.1.1.1.1.1.1.2.1.2.1.1.7.7.1.1.1.3.1.1.1.e.1.6.1.f.g.1.1.g.1.4.1.c.1.1.a.2.c.1.2.1.1.1.2.1.1.14.1.1.1.11.1.g.1.1.1.12.1.1.2.1.1.1.1.1.1.1.1.1.1.6.1.7.1.2.1.1.1.3.1.b.1.1.1.1.1.1.1.1.1.1.1.e.1.1.1.1.7.5.1.1.1.f.6.b.1.1.1.a.1.1.h.1.18.1.1.1.1.1b.1.1.1.1.1.1.1.1.1.15.1.1.1.1.1.1.1.1.1.1.1.5.1.1.1.1.1.4.1.9.1.1.1.7.1.5.1.2.1.1.1.f.1.1.1.1.4.1.1.1.1.1.1.1.3.1.1.1.1.1.5.1.1.2.1.6.b.1.1.1.9.1.1t.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.l.1.1.1.1.1.1.3.1.1.1.1.16.1.1.1.4.1.1.4.1.1.2.1.1.1.1.1.d.1.5.1.1.u.1.1.1.1.6.1.8.1.1.1.1.1.2.1.1.7.1.1.9.1.1.1.1.8.1v.1.1.1.n.1.1.1.1.1.1.e.1.1.1.1.1.4.1.3.1.1.13.1.1.1.8.4.1.1.1.1.1.1.1.1.1.2.1.1.9.1.5.1.w.1.1.m.1.2.a.2.1.4.1.1.1.1.1.7.1.1w.1.1.p.2.2.1.d.1.1.1.1.1.2.1.1.1.1.1.1.1.1.14.1.1.7.1.1.1.1.1.3.1.1.3.1.1.1.1.1.1.7.1.1.6.1.s.1.2.2.1.1.k.1.1.1.1.9.1.1.1.4.2.1.8.1.1x.1.w.1.a.1.1.1.2.1.1.1.1.1.3.1.1.1.16.1.5.1.1.1.1.1.1.1.1.1.1.3.1.5.1.1.1.1.1.1.1.1.1.8.1.1.1.1.o.1.1.1.4.l.1.1.1.c.3.1.2.1.1.5.1.1.1z.x.1.9.1.2.1.1.1.1.1.1.1.1c.1.5.1.1.1.3.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.7.1.2.1.4.1.1.i.1.7.k.1.d.1.1.2.1.3.1.1.3.1.1.1.1z.1.y.5.1.1.1.1.1.1.1.1.1.1.1.1.1.1d.1.6.2.3.1.1.1.1.1.1.1.1.5.1.3.1.1.1.1.1.6.1.2.1.4.1.2.1.g.1.1.7.1.1.1.e.1.1.1.d.1.1.2.1.4.1.1.1.1.1.1.21.z.3.1.1.5.1.1.1.1.1.4.17.1.1.1.1.1.5.1.1.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.9.1.1.4.1.2.1.4.4.1.1.1.1.c.1.b.1.d.g.1.3.5.2.1.1.1.22.z.2.1.7.4.1b.1.3.1.1.1.1.1.1.1.1.1.2.1.1.2.2.1.1.1.1.1.1.3.1.a.1.1.1.1.1.1.3.1.3.2.1.5.4.1.1.1.1.1.1.1.1.1.c.1.1.1.1.1.7.1.f.1.1.1.1.1.4.1.2.1.1.23.y.1.1.1.7.1.1f.1.1.7.1.5.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.3.1.1.1.1.4.1.2.1.1.1.3.1.1.1.1.1.1.1.4.1.2.1.1.1.1.4.1.i.1.1.1.1.1.1.f.1.1.1.7.1.1.1.1.25.16.1.1.1.1f.2.6.1.1.4.1.1.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.2.2.1.1.2.1.1.4.1.1.1.1.1.2.1.1.1.z.1.3.1.1.1.9.1.27.1.14.1.10.i.2.5.1.2.1.3.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.c.1.1.3.1.1.1.7.1.4.1.1.1.1.1.1.1.10.1.1.1.1.1.1.1.1.9.1.28.13.1.1.10.2.1.f.1.1.5.1.1.1.4.1.1.4.1.1.2.1.1.1.1.1.1.e.1.1.1.1.1.3.8.1.2.1.1.1.1.1.1.10.1.1.1.1.1.1.1.1.1.a.1.28.1.11.1.1.13.1.1.1.d.1.1.5.1.9.1.1.1.1.3.1.1.1.1.1.1.e.3.1.4.1.1.1.1.5.1.1.1.2.1.2.1.z.1.2.3.1.1.1.1.7.1.1.1.29.12.1.15.1.d.1.1.4.1.6.1.1.1.1.1.1.1.5.1.1.1.1.1.1.a.1.1.1.1.3.1.1.2.1.3.1.4.1.1.1.1.1.1.1.1.1.10.1.1.1.3.1.2.1.5.1.1.2.29.1.11.1.16.f.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.6.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.5.1.1.5.1.3.3.3.1.1.1.1.10.1.1.3.1.2.3.2.1.1.2.1.2a.11.1.1l.1.1.1.1.1.1.6.1.1.1.2.1.7.1.1.1.1.3.1.1.1.1.4.3.1.c.1.1.1.6.4.1.1.1.x.5.1.1.1.2.1.1.1.1.2.1.1.2a.1.z.1.1m.3.1.8.2.1.a.1.1.5.1.1.1.1.3.1.3.1.c.8.1.2.1.1.1.1.1.w.1.5.1.1.1.1.1.1.1.1.1.1.1.1.2b.1.1.1.w.1.1l.1.1.4.8.2.1.k.1.1.1.1.4.1.c.8.1.2.4.1.x.1.4.1.2.1.1.1.1.1.1.2.2d.1.1.1.1.1.r.1.1.c.13.1.6.6.1.7.3.1.1.1.4.1.1.1.9.1.1.1.1.6.1.b.7.1.3.1.3.1.y.4.2.1.1.1.1.6.2e.1.1.1.1.1.3.1.l.1.d.19.1.4.1.1.1.8.1.1.3.1.3.3.1.1.1.3.1.2.1.1.1.1.1.1.5.b.1.5.1.4.1.3.y.1.1.6.1.1.2m.1.1.1.1.1.1.1.1.1.e.1.1.4.1n.5.1.a.1.5.1.1.1.4.2.1.1.1.1.1.1.1.1.1.3.1.4.1.b.4.1.5.1.3.1.1.x.1.7.1.7.2f.1.1.1.6.1.1.1.6.1.1.1.1.1.1.1.1.1.2.1n.1.2.1.1.b.1.7.1.4.6.1.1.1.1.1.4.1.2.1.1.1.9.1.2.1.1.5.1.5.1.1.1.u.1.1.6.1.2l.3.1.1.1.6.1.1.1.3.1.1.2.1.1.1.4.1.1.1.1g.3.2.1.1.1.1.1.d.c.6.1.1.1.1.6.1.1.1.1.1.1.8.1.1.1.7.6.1.2.1.7.1.1.1.k.1.1f.1g.1.1.1.1.8.1.1.1.1.b.1.1.1f.1.1.1.1.1.1.2.g.c.7.1.1.a.1.2.1.8.1.6.1.1.6.1.1.2.1.1.1.1.1.1.1.3.1.j.1.6.1.a.2e.1.1.1.1.1.8.1.1.c.2.1.1.1e.3.1.1.1.1.1.f.c.7.1.1.b.1.2.1.1.1.1.5.5.1.a.1.1.1.2.1.1.1.3.1.1.1.h.1.6.1.b.11.1e.1.1.1.1.8.1.1.c.1.1.1.1.1.1f.1.2.1.1.1.1.d.1.c.8.1.1.b.1.2.1.1.1.4.1.5.1.c.1.1.1.1.4.1.2.1.h.1.5.1.2.1.9.13.1d.1.1.1.1.8.1.d.1.1.1.1.1f.3.4.1.1.c.c.8.1.1.b.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.f.1.1.1.2.3.h.1.1.1.1.2.4.1.8.2h.1.1.2.1.7.d.1.2.1.1.1.1.1c.1.3.4.2.1.b.1.b.9.1.1.b.1.1.1.1.1.8.1.1.1.1.e.2.1.1.1.3.1.f.1.1.1.1.1.1.1.4.9.a.10.2.16.1.1.2.7.d.1.1.2.1.1.1.1c.3.1.4.3.1.b.1.1.1.1.1.6.9.2.1.a.1.2.1.1.1.1.7.1.1.f.1.1.1.1.1.3.1.5.7.2.1.2.1.1.1u.15.3.1.6.b.1.1.1.1.1.1.2.1.1.1b.3.5.4.1.8.1.1.2.1.2.1.5.6.2.1.1.2.b.1.1.1.3.8.1.2.d.1.1.1.1.5.2.1.1.1.1.5.1.1.1.3.1.1u.1.1.1.16.1.6.1.6.1.1.2.1.1.1.1.1.1.1.2.1.1.19.1.1.1.1.5.5.1.6.1.5.3.1.1.3.1.1.1.1.1.1.1.1.2.2.e.2.1.8.1.1.1.c.1.1.1.1.1.5.1.1.1.3.1.1.1.1.1.1.6.1x.1.16.7.5.1.2.7.1.1.1.1.1.1a.1.8.6.1.4.1.6.1.4.1.2.a.2.1.c.3.a.1.1.b.1.4.1.5.1.1.1.1.2.1.1.1.5.5.j.1e.1.10.5.7.1.4.3.5.1.2.1.1.1.1.1.1.1.18.9.6.1.2.1.8.5.1.1.a.3.1.a.1.2.1.c.a.1.6.1.3.1.1.1.2.1.1.1.1.1.1.8.1.j.1e.1.10.5.1.7.1.1.1.1.2.1.5.2.1.1.1.1.1.1.1.1.1.1.1.1.14.9.7.1.1.8.1.6.b.1.3.6.1.1.1.1.3.1.c.9.1.7.1.2.1.2.1.1.1.1.1.2.1.9.1.j.1.h.1u.9.1.1.6.1.2.1.1.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.12.9.8.9.7.c.1.2.1.1.3.1.3.2.1.1.d.8.1.9.2.1.2.1.1.1.1.1.2.1.8.1.1.k.12.1k.1.1.7.1.1.1.3.h.1.u.1.1.1.5.1.1.7.8.9.7.b.1.1.2.1.1.1.1.1.4.1.1.1.e.8.a.1.1.1.2.1.2.1.1.1.4.6.1.1.k.2o.1.1.1.1.1.1.1.1.1.1.1.1.1.i.1.u.1.1.5.1.1.1.1.2.3.8.8.1.7.b.2.1.2.8.h.1.5.1.1.a.1.1.1.1.4.1.1.1.1.9.1.1.j.1.2t.1.2.1.3.1.i.1.u.1.5.1.3.1.1.1.1.1.1.3.1.1.1.1.1.8.7.1.b.1.1.1.1.1.6.1.1.i.5.b.4.6.2.1.9.1.1.j.1.2u.1.1.1.1.1.1.1.1.i.10.1.1.2.1.7.1.2.4.7.7.c.1.1.1.1.1.4.1.1.k.5.b.4.1.3.1.1.1.1.1.9.1.1.1.1.h.k.2d.1.1.1.2.2.h.1.10.1.1.1.1.6.1.3.1.2.1.1.1.1.1.1.1.1.1.6.c.4.1.1.1.1.1.m.1.4.a.1.1.3.1.1.1.1.4.1.9.1.1.1.1.h.2z.1.2.1.1.8.1.1.1.5.1.11.1.1.1.1.1.1.3.1.5.1.1.4.1.3.1.6.8.1.2.1.4.1.1.1.5.1.1.1.h.4.a.1.4.1.1.1.1.3.1.1.9.1.1.1.1.1.11.2f.1.1.7.1.1.1.1.1.1.1.1.1.1.2.11.1.1.3.1.1.2.2.1.1.1.1.1.1.8.1.6.1.7.1.2.5.1.1.3.1.1.1.h.1.2.1.3.a.1.3.1.1.3.1.1.1.1.1.9.2.1.1.1.u.3.1.1.1.1.1w.j.1.1.7.1.1.1.3.1.1.1.1.1.1.12.1.4.1.1.1.2.1.1.1.2.1.8.1.5.1.1.1.1.2.1.1.1.1.1.1.5.1.1.1.1.1.3.h.1.1.2.2.1.a.5.1.3.1.1.1.1.1.8.1.1.1.1.1.1.x.3.1.2f.1.1.1.1.1.1.1.1.1.2.1.1.4.3.1.13.1.1.1.2.2.1.1.3.1.1.8.1.1.1.3.1.2.1.1.1.1.1.1.1.2.7.1.6.i.1.2.2.1.1.a.4.1.1.2.1.1.1.9.1.1.2.1.1.1.a.j.4.1.1.1.1.2h.1.1.1.1.1.1.3.1.9.1.12.1.2.1.1.4.3.1.1.8.1.1.3.1.3.1.7.1.8.1.1.3.1.i.3.1.1.1.1.a.4.1.1.3.1.1.9.1.4.1.1.1.j.e.1.2.1.2h.1.1.1.1.1.3.1.9.1.1.12.1.1.1.1.4.3.1.1.7.1.2.1.1.1.5.1.6.1.a.1.2.n.2.1.a.4.1.1.e.4.1.1.1.1.6.8.2.2.2.1.2.b.1.2.2i.1.2.5.1.1.7.2.1.11.1.1.1.1.4.3.1.1.1.4.1.1.3.9.7.1.9.1.1.1.j.6.1.1.9.1.4.1.1.c.1.1.2.1.1.1.1.1.6.d.7.9.2.n.1q.8.7.1.1.4.1.2.1.1.1.1.10.1.1.4.2.1.1.1.1.1.3.4.1.9.1.7.8.1.2.k.1.5.1.b.1.3.1.1.1.a.1.1.1.2.1.2.1.1.p.3.6.1.2.o.1x.8.6.1.1.1.2.1.1.10.1.1.1.1.1.1.1.5.1.1.1.5.3.1.1.1.1.1.1.1.1.1.4.1.6.1.1.2.1.k.1.i.1.1.2.2.4.5.1.1.2.1.5.1.4.i.8.5.r.1t.3.1.7.3.1.1.1.1.1.1.2.1.1.1.10.1.a.1.1.1.1.4.2.1.2.1.5.1.1.1.1.1.1.1.1.1.1.1.1.4.l.1.i.1.1.1.1.2.4.1.4.1.1.1.1.6.1.3m.8.1.2.4.2.2.1.1.1.1d.1.1.4.1.1.1.a.1.2.4.2.4.1.l.1.i.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.7.4.z.q.1t.1.7.1.1.2.1.3.2.1.1.1.1.1.1.1e.1.1.1.1.1.1.1.a.1.2.1.5.3.1.m.1.i.1.1.1.1.1.1.1.4.1.1.3.2.1.5.1.1.1.11.a.g.1s.1.1.6.1.1.1.1.1.4.1.1.6.1c.1.1.1.1.1.1.1.1.1.9.1.3.1.5.2.1.n.k.1.1.2.1.1.1.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.11.a.1y.1.9.2.1.1.4.1.2.1.c.1.1.1a.2.1.3.1.2.a.4.6.1.1.o.1.k.1.3.1.1.3.7.1.1.1.1.1.1.2.1.1.1.10.n.1k.1.1.7.1.4.1.3.1.f.1.1.1.1.1a.4.1.2.a.1.1.1.1.6.1.p.l.1.1.2.1.1.3.1.6.2.1.1.1.3.1.1.1.1.1.1.1.1.p.2.4.28.1.7.4.1.1.1.3.g.1.1.1.1.1.15.3.3.1.1.1.1.a.1.3.1.4.1.1b.1.1.3.1.1.2.1.5.1.2.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.3.2.1.3.l.1.b.23.1.2.1.3.1.1.1.g.1.3.1.1.1.1.15.1.1.1.1.2.a.1.1.4.1.1.2.1.1c.1.1.2.1.1.1.2.1.4.2.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.4.1.2.1.1.k.b.23.1.1.1.6.1.k.1.2.1.1.1.14.1.4.b.1.6.2.d.11.1.3.1.1.2.1.1.1.1.1.2.1.1.1.1.2.1.1.1.1.1.1.1.1.5.1.1.1.5.1.1.1.s.1.e.1p.1.1.6.1.r.1.4.10.1.1.1.1.b.1.6.1.1.c.1.11.1.1.2.1.7.1.3.1.1.1.3.2.1.1.1.1.1.1.4.2.1.1.3.1.1.1.1.4.12.1q.2.5.1.t.1.13.1.1.1.c.8.1.b.2.d.p.1.1.1.9.3.1.1.1.7.1.1.2.1.3.3.1.1.1.1.1.1.1.1.4.e.2e.1.6.v.14.1.1.1.b.8.r.r.1.1.1.1.1.1.1.6.1.1.1.1.7.1.1.2.1.2.4.1.1.1.1.3.1.h.1.2e.5.1.v.1.13.3.1.2.1.1.6.1.7.b.3.d.1.r.1.1.1.1.1.1.1.1.5.5.1.2.1.2.3.1.1.1.2.1.2.6.1.1.1.g.1.z.1e.1.4.1.v.1.h.m.1.3.1.1.2.6.1.7.1.1k.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.5.1.1.1.1.1.1.1.1.1.2.4.1.1.1.1.2.d.8.1.b.f.1.1e.5.v.14.1.7.5.1.1.1.1.5.6.4.1f.1.1.1.1.1.1.1.1.1.1.1.1.b.1.1.1.2.1.1.1.1.1.1.3.1.1.1.1.e.1.h.9.8.1.1e.1.4.1.1.3.1.o.1.14.1.7.5.4.1.4.1.5.1.3.1k.1.1.2.1.1.e.4.1.1.1.7.1.1.4.b.d.4.i.1f.6.1.1.1.1.o.16.7.1.1.1.2.4.1.4.1.d.1.14.d.1.1.6.1.1.3.5.5.1.1.1.1.6.1.3.1.o.b.1q.1.6.3.1.m.1.16.7.1.1.1.1.1.1.3.1.1.1.1.1.1.2.1.4.14.r.1.1.1.1.1.1.4.1.9.1.4.1.5.2q.6.4.1.1.j.1.16.1.7.1.3.1.1.3.1.5.3.1.3.1.13.m.6.1.1.1.1.1.4.1.1.j.9.6.3.1.27.6.6.1.i.17.8.5.1.3.1.1.4.6.1.1.1r.1.2.1.4.1.4.2.j.1.d.5.1.1.1.g.1.1.1.3.1i.1.5.7.i.17.8.7.1.1.1.1.4.6.2.1q.1.1.1.1.5.1.4.2.1.i.1.f.h.5.1.1.1.3.1.1i.1.4.7.i.16.1.8.6.1.3.1.4.4.1.1.2.1q.1.2.1.5.1.4.3.i.1.b.m.1.1.2.2.1.1.1.2.1j.1.3.8.1.g.s.e.9.5.1.1.1.1.1.1.3.1.3.1.1.3.8.1g.1.1.a.1.1.1.1.3.1.3.e.1.8.1.1.1.1.l.2.1.4.1.1.1.1.2.1i.1.2.9.1.f.16.1.1.1.1.1.1.3.1.1.2.1.4.2.1.1.1.3.1.5.8.1g.1.d.1.1.4.i.1.7.1.1.2.4.9.b.1.5.1.1.3.1i.1.1.a.e.1.16.1.5.1.1.1.1.1.1.1.5.3.6.4.1.1o.f.5.i.1.7.1.1.1.1.3.1.3.9.1.e.1.1.1l.1.1.6.1.1.1.e.1.17.7.4.6.2.1.6.4.1o.1.k.1.d.5.1.6.1.1.2.3.1.d.1.a.1.3.1m.1.1.5.1.2.f.9.1.y.1.6.4.1.5.1.1.7.4.7.1.4.19.1.1.1.m.1.c.1.2.2.1.a.3.d.h.2.1j.1.1.5.3.e.1.9.10.6.5.1.3.1.1.1.3.4.4.5.1.1.1c.1.1.q.1.c.1.1.1.f.e.f.1.2.1.1i.2.1.1.1.1.1.3.1.1.c.1a.5.1.6.1.1.1.3.3.1.2.1.3.1.1i.1.s.1.1.c.1.1.3.v.1.d.1i.2.1.2.2.5.9.1.1.1.1a.1.4.6.1.1.1.1.3.4.2.1.3.1j.1.t.1.d.1.1.2.x.2.1s.2.1.4.1.1.3.1.7.1.1.1c.1.4.6.4.3.7.3.1j.v.1.1t.2.3.19.2.7.1.3.6.1.1f.4.5.1.4.2.1.7.2.1.1i.1.w.1.36.2.8.1.2.1.4.1.1g.4.1.1.1.1.1.4.1.1.9.1.1.1j.1.w.1.2d.l.8.2.8.2.1.1.4.1h.4.1.1.7.1.1.1u.1.x.1h.t.v.1.1.1.8.1.1.1.1.4.1h.1.3.9.1.1.1v.x.35.2.b.1.5.1i.1.1.1.7.1.2.1.1v.1.w.p.2g.1.1.a.1.5.1.1i.1.1.1.6.1.1.1.1.1x.w.c.2t.1.1.9.1.1.4.1.1k.8.1.1.1.1y.w.o.2h.1.a.2.1.2.1.1.1k.1.8.1.1.1y.1.b.1.1.h.1.5.30.1.a.3.1.1.1.1m.8.1.20.8.1.1.1.1.1.1.1.3.c.36.1.1.9.4.1.1m.1.7.1.20.1.6.1.1.7.1.2.b.1.30.6.1.1.9.4.1o.2.1.1.1.1.1.21.4.1.1.1.9.1.1.1.b.h.2p.1.1.a.1.1.1.1.1o.1.1.1.25.1.1.1.1.d.1.1.1.9.1.i.1.2n.2.b.4h.1.1.1.8.j.1.1.2l.1.1.1.b.11.3i.1.8.k.1.1.2k.1.1.1.b.11.23.1g.1.2.1.2.1.1.l.1.1.1.2i.1.1.1.7.1.1.1.1.34.1h.1.1.1.1.1.m.1.2.1.2i.1.1.8.4r.2.1.1.l.1.1.1.2j.1.1.5.1.2.17.3k.1.2.1.j.1.1.1.1.2k.1.1.5.1.1.1.4s.1.1.1.j.1.1.1.1.2k.1.1.5.1.1.4t.2.1.j.2.2l.1.1.1.5.1.1.4t.1.1.1.h.1.1.1.1.9.2c.1.1.1.5.4w.i.1.2.1.2m.1.1.1.4.1.5d.1.2.1.2n.1.1.1.3.1.2p.1.1.2n.1.2.2o.1.2.3.1.1.2e.b.1.2n.1.1.1.2o.1.1.1.4.1.5d.a.2h.1.1.1.3.1.34.1.1.4z.1.1.3.1.36.1.2i.2h.1.1.3.1.5e.1.2r.1.1.1.2.7.1.1.1.7x.1.1.1.1.1.1.6.1.1.59.2q.1.1.1.1.1.39.1.4x.1.1.1.1.1.1.1.1.1.l.1.1.w.3l.1.30.1.1.1.1.1.1.1.m.1.4h.34.1.x.8c.8c.1';
+
+/** Decoded grid cells as [gx, gy] pairs. */
+export function worldDots(): { x: number; y: number }[] {
+ const out: { x: number; y: number }[] = [];
+ let cursor = 0;
+ for (const part of PACKED.split('.')) {
+ cursor += parseInt(part, 36);
+ out.push({ x: cursor % WORLD_GRID_W, y: Math.floor(cursor / WORLD_GRID_W) });
+ }
+ return out;
+}
diff --git a/shared/src/i18n/ar/login.ts b/shared/src/i18n/ar/login.ts
index 83808f2e9..38655a091 100644
--- a/shared/src/i18n/ar/login.ts
+++ b/shared/src/i18n/ar/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'فشل تسجيل الدخول. يرجى التحقق من بياناتك.',
'login.tagline': 'رحلاتك.\nخطتك.',
'login.description': 'خطط لرحلاتك بشكل تعاوني مع خرائط تفاعلية وميزانيات ومزامنة لحظية.',
- 'login.features.maps': 'خرائط تفاعلية',
- 'login.features.mapsDesc': 'Google Places ومسارات وتجميع',
- 'login.features.realtime': 'مزامنة فورية',
- 'login.features.realtimeDesc': 'خططوا معًا عبر WebSocket',
- 'login.features.budget': 'تتبع التكاليف',
- 'login.features.budgetDesc': 'فئات ورسوم وتقسيم لكل شخص',
- 'login.features.collab': 'تعاون',
- 'login.features.collabDesc': 'عدة مستخدمين مع رحلات مشتركة',
- 'login.features.packing': 'قوائم تجهيز',
- 'login.features.packingDesc': 'فئات وتقدم واقتراحات',
- 'login.features.bookings': 'الحجوزات',
- 'login.features.bookingsDesc': 'رحلات وفنادق ومطاعم وغير ذلك',
- 'login.features.files': 'المستندات',
- 'login.features.filesDesc': 'رفع الملفات وإدارتها',
- 'login.features.routes': 'مسارات ذكية',
- 'login.features.routesDesc': 'تحسين تلقائي وتصدير إلى Google Maps',
- 'login.selfHosted': 'استضافة ذاتية · مفتوح المصدر · بياناتك تبقى ملكك',
'login.title': 'تسجيل الدخول',
'login.subtitle': 'مرحبًا بعودتك',
'login.signingIn': 'جارٍ تسجيل الدخول…',
diff --git a/shared/src/i18n/ar/mobileVacay.ts b/shared/src/i18n/ar/mobileVacay.ts
index d232bd0c3..2ac769367 100644
--- a/shared/src/i18n/ar/mobileVacay.ts
+++ b/shared/src/i18n/ar/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'الشهر التالي',
'mobileVacay.usedCount': '{count} مستخدَمة',
'mobileVacay.leftCount': '{count} متبقية',
- 'mobileVacay.daysPerYear': 'يوم / سنة',
+ 'mobileVacay.left': 'متبقية',
+ 'mobileVacay.modeCompany': 'الشركة',
'mobileVacay.decreaseAllowance': 'تقليل الرصيد',
'mobileVacay.increaseAllowance': 'زيادة الرصيد',
'mobileVacay.companyLegend': 'أيام الشركة',
diff --git a/shared/src/i18n/br/login.ts b/shared/src/i18n/br/login.ts
index 2a61c454b..194cac1ed 100644
--- a/shared/src/i18n/br/login.ts
+++ b/shared/src/i18n/br/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Falha no login. Verifique suas credenciais.',
'login.tagline': 'Suas viagens.\nSeu plano.',
'login.description': 'Planeje viagens em equipe com mapas interativos, orçamento e sincronização em tempo real.',
- 'login.features.maps': 'Mapas interativos',
- 'login.features.mapsDesc': 'Google Places, rotas e agrupamento',
- 'login.features.realtime': 'Sincronização em tempo real',
- 'login.features.realtimeDesc': 'Planejem juntos via WebSocket',
- 'login.features.budget': 'Controle de custos',
- 'login.features.budgetDesc': 'Categorias, gráficos e custo por pessoa',
- 'login.features.collab': 'Colaboração',
- 'login.features.collabDesc': 'Vários usuários com viagens compartilhadas',
- 'login.features.packing': 'Listas de malas',
- 'login.features.packingDesc': 'Categorias, progresso e sugestões',
- 'login.features.bookings': 'Reservas',
- 'login.features.bookingsDesc': 'Voos, hotéis, restaurantes e mais',
- 'login.features.files': 'Documentos',
- 'login.features.filesDesc': 'Envie e gerencie documentos',
- 'login.features.routes': 'Rotas inteligentes',
- 'login.features.routesDesc': 'Otimize e exporte para o Google Maps',
- 'login.selfHosted': 'Auto-hospedado · Código aberto · Seus dados são seus',
'login.title': 'Entrar',
'login.subtitle': 'Bem-vindo de volta',
'login.signingIn': 'Entrando…',
diff --git a/shared/src/i18n/br/mobileVacay.ts b/shared/src/i18n/br/mobileVacay.ts
index c97acf641..e35bdd9fa 100644
--- a/shared/src/i18n/br/mobileVacay.ts
+++ b/shared/src/i18n/br/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Próximo mês',
'mobileVacay.usedCount': '{count} usados',
'mobileVacay.leftCount': '{count} restantes',
- 'mobileVacay.daysPerYear': 'dias / ano',
+ 'mobileVacay.left': 'restantes',
+ 'mobileVacay.modeCompany': 'Empresa',
'mobileVacay.decreaseAllowance': 'Diminuir cota',
'mobileVacay.increaseAllowance': 'Aumentar cota',
'mobileVacay.companyLegend': 'Férias coletivas',
diff --git a/shared/src/i18n/ca/login.ts b/shared/src/i18n/ca/login.ts
index 3aabfa8b4..78bda4729 100644
--- a/shared/src/i18n/ca/login.ts
+++ b/shared/src/i18n/ca/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Els teus viatges.\nEl teu pla.',
'login.description':
'Planifica viatges en col·laboració amb mapes interactius, pressupostos i sincronització en temps real.',
- 'login.features.maps': 'Mapes interactius',
- 'login.features.mapsDesc': 'Google Places, rutes i agrupació',
- 'login.features.realtime': 'Sincronització en temps real',
- 'login.features.realtimeDesc': 'Planifiqueu junts mitjançant WebSocket',
- 'login.features.budget': 'Control de pressupost',
- 'login.features.budgetDesc': 'Categories, gràfics i costos per persona',
- 'login.features.collab': 'Col·laboració',
- 'login.features.collabDesc': 'Multiusuari amb viatges compartits',
- 'login.features.packing': "Llistes d'equipatge",
- 'login.features.packingDesc': 'Categories, progrés i suggeriments',
- 'login.features.bookings': 'Reserves',
- 'login.features.bookingsDesc': 'Vols, hotels, restaurants i més',
- 'login.features.files': 'Documents',
- 'login.features.filesDesc': 'Puja i gestiona documents',
- 'login.features.routes': 'Rutes intel·ligents',
- 'login.features.routesDesc': 'Optimització automàtica i exportació a Google Maps',
- 'login.selfHosted': 'Autoallotjat · Codi obert · Les teves dades segueixen sent teves',
'login.title': 'Inicia la sessió',
'login.subtitle': 'Benvingut de nou',
'login.signingIn': 'Iniciant la sessió…',
diff --git a/shared/src/i18n/ca/mobileVacay.ts b/shared/src/i18n/ca/mobileVacay.ts
index f8d6eda3c..8b11c010e 100644
--- a/shared/src/i18n/ca/mobileVacay.ts
+++ b/shared/src/i18n/ca/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Mes següent',
'mobileVacay.usedCount': '{count} usats',
'mobileVacay.leftCount': '{count} restants',
- 'mobileVacay.daysPerYear': 'dies / any',
+ 'mobileVacay.left': 'restants',
+ 'mobileVacay.modeCompany': 'Empresa',
'mobileVacay.decreaseAllowance': "Redueix l'assignació",
'mobileVacay.increaseAllowance': "Augmenta l'assignació",
'mobileVacay.companyLegend': "Dies d'empresa",
diff --git a/shared/src/i18n/cs/login.ts b/shared/src/i18n/cs/login.ts
index 97518ac52..e7c8a5fa8 100644
--- a/shared/src/i18n/cs/login.ts
+++ b/shared/src/i18n/cs/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Přihlášení se nezdařilo. Zkontrolujte prosím své údaje.',
'login.tagline': 'Vaše cesty.\nVáš plán.',
'login.description': 'Plánujte cesty společně s interaktivními mapami, rozpočty a synchronizací v reálném čase.',
- 'login.features.maps': 'Interaktivní mapy',
- 'login.features.mapsDesc': 'Google Places, trasy a shlukování bodů',
- 'login.features.realtime': 'Synchronizace v reálném čase',
- 'login.features.realtimeDesc': 'Plánujte společně přes WebSocket',
- 'login.features.budget': 'Sledování nákladů',
- 'login.features.budgetDesc': 'Kategorie, grafy a náklady na osobu',
- 'login.features.collab': 'Spolupráce',
- 'login.features.collabDesc': 'Více uživatelů se sdílenými cestami',
- 'login.features.packing': 'Seznamy věcí',
- 'login.features.packingDesc': 'Kategorie, pokrok v balení a návrhy',
- 'login.features.bookings': 'Rezervace',
- 'login.features.bookingsDesc': 'Lety, hotely, restaurace a další',
- 'login.features.files': 'Dokumenty',
- 'login.features.filesDesc': 'Nahrávejte a spravujte dokumenty',
- 'login.features.routes': 'Chytré trasy',
- 'login.features.routesDesc': 'Automatická optimalizace a export do Google Maps',
- 'login.selfHosted': 'Self-hosted · Open Source · Vaše data zůstávají u vás',
'login.title': 'Přihlásit se',
'login.subtitle': 'Vítejte zpět',
'login.signingIn': 'Přihlašování…',
diff --git a/shared/src/i18n/cs/mobileVacay.ts b/shared/src/i18n/cs/mobileVacay.ts
index f2e377d41..d1d94ec9e 100644
--- a/shared/src/i18n/cs/mobileVacay.ts
+++ b/shared/src/i18n/cs/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Další měsíc',
'mobileVacay.usedCount': '{count} využito',
'mobileVacay.leftCount': 'Zbývá {count}',
- 'mobileVacay.daysPerYear': 'dní / rok',
+ 'mobileVacay.left': 'zbývá',
+ 'mobileVacay.modeCompany': 'Firma',
'mobileVacay.decreaseAllowance': 'Snížit nárok',
'mobileVacay.increaseAllowance': 'Zvýšit nárok',
'mobileVacay.companyLegend': 'Firemní dny',
diff --git a/shared/src/i18n/de/login.ts b/shared/src/i18n/de/login.ts
index 4c43c8551..1ca952f97 100644
--- a/shared/src/i18n/de/login.ts
+++ b/shared/src/i18n/de/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen.',
'login.tagline': 'Deine Reisen.\nDein Plan.',
'login.description': 'Plane Reisen gemeinsam mit interaktiven Karten, Budgets und Echtzeit-Sync.',
- 'login.features.maps': 'Interaktive Karten',
- 'login.features.mapsDesc': 'Google Places, Routen & Clustering',
- 'login.features.realtime': 'Echtzeit-Sync',
- 'login.features.realtimeDesc': 'Gemeinsam planen via WebSocket',
- 'login.features.budget': 'Kosten-Tracking',
- 'login.features.budgetDesc': 'Kategorien, Diagramme & Pro-Kopf',
- 'login.features.collab': 'Zusammenarbeit',
- 'login.features.collabDesc': 'Multi-User mit geteilten Reisen',
- 'login.features.packing': 'Packlisten',
- 'login.features.packingDesc': 'Kategorien & Fortschritt',
- 'login.features.bookings': 'Buchungen',
- 'login.features.bookingsDesc': 'Flüge, Hotels, Restaurants & mehr',
- 'login.features.files': 'Dokumente',
- 'login.features.filesDesc': 'Dateien hochladen & verwalten',
- 'login.features.routes': 'Routenoptimierung',
- 'login.features.routesDesc': 'Auto-Optimierung & Google Maps Export',
- 'login.selfHosted': 'Self-hosted · Open Source · Deine Daten bleiben bei dir',
'login.title': 'Anmelden',
'login.subtitle': 'Willkommen zurück',
'login.signingIn': 'Anmelden…',
diff --git a/shared/src/i18n/de/mobileVacay.ts b/shared/src/i18n/de/mobileVacay.ts
index 3b37b3349..c0aaabe40 100644
--- a/shared/src/i18n/de/mobileVacay.ts
+++ b/shared/src/i18n/de/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Nächster Monat',
'mobileVacay.usedCount': '{count} genommen',
'mobileVacay.leftCount': '{count} übrig',
- 'mobileVacay.daysPerYear': 'Tage / Jahr',
+ 'mobileVacay.left': 'übrig',
+ 'mobileVacay.modeCompany': 'Betrieb',
'mobileVacay.decreaseAllowance': 'Anspruch verringern',
'mobileVacay.increaseAllowance': 'Anspruch erhöhen',
'mobileVacay.companyLegend': 'Betriebsferien',
diff --git a/shared/src/i18n/en/login.ts b/shared/src/i18n/en/login.ts
index f3631ea40..926eb1eb8 100644
--- a/shared/src/i18n/en/login.ts
+++ b/shared/src/i18n/en/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Login failed. Please check your credentials.',
'login.tagline': 'Your Trips.\nYour Plan.',
'login.description': 'Plan trips collaboratively with interactive maps, budgets, and real-time sync.',
- 'login.features.maps': 'Interactive Maps',
- 'login.features.mapsDesc': 'Google Places, routes & clustering',
- 'login.features.realtime': 'Real-Time Sync',
- 'login.features.realtimeDesc': 'Plan together via WebSocket',
- 'login.features.budget': 'Cost Tracking',
- 'login.features.budgetDesc': 'Categories, charts & per-person costs',
- 'login.features.collab': 'Collaboration',
- 'login.features.collabDesc': 'Multi-user with shared trips',
- 'login.features.packing': 'Packing Lists',
- 'login.features.packingDesc': 'Categories, progress & suggestions',
- 'login.features.bookings': 'Reservations',
- 'login.features.bookingsDesc': 'Flights, hotels, restaurants & more',
- 'login.features.files': 'Documents',
- 'login.features.filesDesc': 'Upload & manage documents',
- 'login.features.routes': 'Smart Routes',
- 'login.features.routesDesc': 'Auto-optimize & Google Maps export',
- 'login.selfHosted': 'Self-hosted · Open Source · Your data stays yours',
'login.title': 'Sign In',
'login.subtitle': 'Welcome back',
'login.signingIn': 'Signing in…',
diff --git a/shared/src/i18n/en/mobileVacay.ts b/shared/src/i18n/en/mobileVacay.ts
index 797300603..5702daf5f 100644
--- a/shared/src/i18n/en/mobileVacay.ts
+++ b/shared/src/i18n/en/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Next month',
'mobileVacay.usedCount': '{count} used',
'mobileVacay.leftCount': '{count} left',
- 'mobileVacay.daysPerYear': 'days / year',
+ 'mobileVacay.left': 'left',
+ 'mobileVacay.modeCompany': 'Company',
'mobileVacay.decreaseAllowance': 'Decrease allowance',
'mobileVacay.increaseAllowance': 'Increase allowance',
'mobileVacay.companyLegend': 'Company days',
diff --git a/shared/src/i18n/es/login.ts b/shared/src/i18n/es/login.ts
index f6992f17d..de96659c6 100644
--- a/shared/src/i18n/es/login.ts
+++ b/shared/src/i18n/es/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Tus viajes.\nTu plan.',
'login.description':
'Planifica viajes en colaboración con mapas interactivos, presupuestos y sincronización en tiempo real.',
- 'login.features.maps': 'Mapas interactivos',
- 'login.features.mapsDesc': 'Google Places, rutas y agrupación',
- 'login.features.realtime': 'Sincronización en tiempo real',
- 'login.features.realtimeDesc': 'Planificad juntos mediante WebSocket',
- 'login.features.budget': 'Control de costes',
- 'login.features.budgetDesc': 'Categorías, gráficos y costes por persona',
- 'login.features.collab': 'Colaboración',
- 'login.features.collabDesc': 'Multiusuario con viajes compartidos',
- 'login.features.packing': 'Listas de equipaje',
- 'login.features.packingDesc': 'Categorías, progreso y sugerencias',
- 'login.features.bookings': 'Reservas',
- 'login.features.bookingsDesc': 'Vuelos, hoteles, restaurantes y más',
- 'login.features.files': 'Documentos',
- 'login.features.filesDesc': 'Sube y gestiona documentos',
- 'login.features.routes': 'Rutas inteligentes',
- 'login.features.routesDesc': 'Optimización automática y exportación a Google Maps',
- 'login.selfHosted': 'Autoalojado · Código abierto · Tus datos siguen siendo tuyos',
'login.title': 'Iniciar sesión',
'login.subtitle': 'Bienvenido de nuevo',
'login.signingIn': 'Iniciando sesión…',
diff --git a/shared/src/i18n/es/mobileVacay.ts b/shared/src/i18n/es/mobileVacay.ts
index 3a331b4da..e0a4f3343 100644
--- a/shared/src/i18n/es/mobileVacay.ts
+++ b/shared/src/i18n/es/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Mes siguiente',
'mobileVacay.usedCount': '{count} usados',
'mobileVacay.leftCount': '{count} restantes',
- 'mobileVacay.daysPerYear': 'días / año',
+ 'mobileVacay.left': 'restantes',
+ 'mobileVacay.modeCompany': 'Empresa',
'mobileVacay.decreaseAllowance': 'Reducir asignación',
'mobileVacay.increaseAllowance': 'Aumentar asignación',
'mobileVacay.companyLegend': 'Días de empresa',
diff --git a/shared/src/i18n/fr/login.ts b/shared/src/i18n/fr/login.ts
index c8a907089..90f43a243 100644
--- a/shared/src/i18n/fr/login.ts
+++ b/shared/src/i18n/fr/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Vos voyages.\nVotre organisation.',
'login.description':
'Planifiez vos voyages en collaboration avec des cartes interactives, des budgets et la synchronisation en temps réel.',
- 'login.features.maps': 'Cartes interactives',
- 'login.features.mapsDesc': 'Google Places, itinéraires et regroupement',
- 'login.features.realtime': 'Synchronisation en temps réel',
- 'login.features.realtimeDesc': 'Planifiez ensemble via WebSocket',
- 'login.features.budget': 'Suivi des coûts',
- 'login.features.budgetDesc': 'Catégories, graphiques et coûts par personne',
- 'login.features.collab': 'Collaboration',
- 'login.features.collabDesc': 'Multi-utilisateurs avec voyages partagés',
- 'login.features.packing': 'Listes de bagages',
- 'login.features.packingDesc': 'Catégories, progression et suggestions',
- 'login.features.bookings': 'Réservations',
- 'login.features.bookingsDesc': 'Vols, hôtels, restaurants et plus',
- 'login.features.files': 'Documents',
- 'login.features.filesDesc': 'Importez et gérez vos documents',
- 'login.features.routes': 'Itinéraires intelligents',
- 'login.features.routesDesc': 'Optimisation automatique et export Google Maps',
- 'login.selfHosted': 'Auto-hébergé · Open Source · Vos données restent les vôtres',
'login.title': 'Connexion',
'login.subtitle': 'Bon retour',
'login.signingIn': 'Connexion en cours…',
diff --git a/shared/src/i18n/fr/mobileVacay.ts b/shared/src/i18n/fr/mobileVacay.ts
index ab38484a5..ade2173c5 100644
--- a/shared/src/i18n/fr/mobileVacay.ts
+++ b/shared/src/i18n/fr/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Mois suivant',
'mobileVacay.usedCount': '{count} pris',
'mobileVacay.leftCount': '{count} restants',
- 'mobileVacay.daysPerYear': 'jours / an',
+ 'mobileVacay.left': 'restants',
+ 'mobileVacay.modeCompany': 'Entreprise',
'mobileVacay.decreaseAllowance': 'Diminuer le quota',
'mobileVacay.increaseAllowance': 'Augmenter le quota',
'mobileVacay.companyLegend': 'Congés d\'entreprise',
diff --git a/shared/src/i18n/gr/login.ts b/shared/src/i18n/gr/login.ts
index 0f003c3cd..129f33be1 100644
--- a/shared/src/i18n/gr/login.ts
+++ b/shared/src/i18n/gr/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Τα Ταξίδια σας.\nΤο Σχέδιό σας.',
'login.description':
'Σχεδιάστε ταξίδια συνεργατικά με διαδραστικούς χάρτες, προϋπολογισμούς και συγχρονισμό σε πραγματικό χρόνο.',
- 'login.features.maps': 'Διαδραστικοί Χάρτες',
- 'login.features.mapsDesc': 'Google Places, διαδρομές & ομαδοποίηση',
- 'login.features.realtime': 'Συγχρονισμός Πραγματικού Χρόνου',
- 'login.features.realtimeDesc': 'Σχεδιάστε μαζί μέσω WebSocket',
- 'login.features.budget': 'Παρακολούθηση Κόστους',
- 'login.features.budgetDesc': 'Κατηγορίες, γραφήματα & κόστος ανά άτομο',
- 'login.features.collab': 'Συνεργασία',
- 'login.features.collabDesc': 'Πολλαπλοί χρήστες με κοινόχρηστα ταξίδια',
- 'login.features.packing': 'Λίστες Πακεταρίσματος',
- 'login.features.packingDesc': 'Κατηγορίες, πρόοδος & προτάσεις',
- 'login.features.bookings': 'Κρατήσεις',
- 'login.features.bookingsDesc': 'Πτήσεις, ξενοδοχεία, εστιατόρια & άλλα',
- 'login.features.files': 'Έγγραφα',
- 'login.features.filesDesc': 'Μεταφόρτωση & διαχείριση εγγράφων',
- 'login.features.routes': 'Έξυπνες Διαδρομές',
- 'login.features.routesDesc': 'Αυτόματη βελτιστοποίηση & εξαγωγή στο Google Maps',
- 'login.selfHosted': 'Self-hosted \u00B7 Open Source \u00B7 Τα δεδομένα σας παραμένουν δικά σας',
'login.title': 'Σύνδεση',
'login.subtitle': 'Καλώς ήρθατε πίσω',
'login.signingIn': 'Σύνδεση…',
diff --git a/shared/src/i18n/gr/mobileVacay.ts b/shared/src/i18n/gr/mobileVacay.ts
index 2f08844f1..f806593b2 100644
--- a/shared/src/i18n/gr/mobileVacay.ts
+++ b/shared/src/i18n/gr/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Επόμενος μήνας',
'mobileVacay.usedCount': '{count} σε χρήση',
'mobileVacay.leftCount': '{count} απομένουν',
- 'mobileVacay.daysPerYear': 'ημέρες / έτος',
+ 'mobileVacay.left': 'απομένουν',
+ 'mobileVacay.modeCompany': 'Εταιρεία',
'mobileVacay.decreaseAllowance': 'Μείωση δικαιώματος',
'mobileVacay.increaseAllowance': 'Αύξηση δικαιώματος',
'mobileVacay.companyLegend': 'Άδεια εταιρείας',
diff --git a/shared/src/i18n/hu/login.ts b/shared/src/i18n/hu/login.ts
index 7075050f4..1a69db77f 100644
--- a/shared/src/i18n/hu/login.ts
+++ b/shared/src/i18n/hu/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Az utazásaid.\nA terved.',
'login.description':
'Tervezz utazásokat közösen interaktív térképekkel, költségvetéssel és valós idejű szinkronizálással.',
- 'login.features.maps': 'Interaktív térképek',
- 'login.features.mapsDesc': 'Google Places, útvonalak és csoportosítás',
- 'login.features.realtime': 'Valós idejű szinkron',
- 'login.features.realtimeDesc': 'Közös tervezés WebSocket-en keresztül',
- 'login.features.budget': 'Költségkövetés',
- 'login.features.budgetDesc': 'Kategóriák, diagramok és személyenkénti költségek',
- 'login.features.collab': 'Együttműködés',
- 'login.features.collabDesc': 'Többfelhasználós, megosztott utazásokkal',
- 'login.features.packing': 'Csomagolási listák',
- 'login.features.packingDesc': 'Kategóriák és haladás',
- 'login.features.bookings': 'Foglalások',
- 'login.features.bookingsDesc': 'Repülők, szállodák, éttermek és még több',
- 'login.features.files': 'Dokumentumok',
- 'login.features.filesDesc': 'Fájlok feltöltése és kezelése',
- 'login.features.routes': 'Útvonal-optimalizálás',
- 'login.features.routesDesc': 'Automatikus optimalizálás és Google Maps export',
- 'login.selfHosted': 'Saját üzemeltetés · Nyílt forráskód · Az adataid nálad maradnak',
'login.title': 'Bejelentkezés',
'login.subtitle': 'Üdv újra',
'login.signingIn': 'Bejelentkezés…',
diff --git a/shared/src/i18n/hu/mobileVacay.ts b/shared/src/i18n/hu/mobileVacay.ts
index 09038cc6e..7d4d044d0 100644
--- a/shared/src/i18n/hu/mobileVacay.ts
+++ b/shared/src/i18n/hu/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Következő hónap',
'mobileVacay.usedCount': '{count} felhasználva',
'mobileVacay.leftCount': '{count} maradt',
- 'mobileVacay.daysPerYear': 'nap / év',
+ 'mobileVacay.left': 'maradt',
+ 'mobileVacay.modeCompany': 'Céges',
'mobileVacay.decreaseAllowance': 'Keret csökkentése',
'mobileVacay.increaseAllowance': 'Keret növelése',
'mobileVacay.companyLegend': 'Céges szabadság',
diff --git a/shared/src/i18n/id/login.ts b/shared/src/i18n/id/login.ts
index 692eda8d4..86ca7ce42 100644
--- a/shared/src/i18n/id/login.ts
+++ b/shared/src/i18n/id/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Login gagal. Periksa kembali kredensial kamu.',
'login.tagline': 'Perjalananmu.\nRencanamu.',
'login.description': 'Rencanakan perjalanan bersama dengan peta interaktif, anggaran, dan sinkronisasi real-time.',
- 'login.features.maps': 'Peta Interaktif',
- 'login.features.mapsDesc': 'Google Places, rute & pengelompokan',
- 'login.features.realtime': 'Sinkronisasi Real-Time',
- 'login.features.realtimeDesc': 'Rencanakan bersama via WebSocket',
- 'login.features.budget': 'Pelacak Biaya',
- 'login.features.budgetDesc': 'Kategori, grafik & biaya per orang',
- 'login.features.collab': 'Kolaborasi',
- 'login.features.collabDesc': 'Multi-pengguna dengan perjalanan bersama',
- 'login.features.packing': 'Daftar Packing',
- 'login.features.packingDesc': 'Kategori, progres & saran',
- 'login.features.bookings': 'Reservasi',
- 'login.features.bookingsDesc': 'Penerbangan, hotel, restoran & lainnya',
- 'login.features.files': 'Dokumen',
- 'login.features.filesDesc': 'Unggah & kelola dokumen',
- 'login.features.routes': 'Rute Cerdas',
- 'login.features.routesDesc': 'Optimasi otomatis & ekspor ke Google Maps',
- 'login.selfHosted': 'Self-hosted · Open Source · Datamu tetap milikmu',
'login.title': 'Masuk',
'login.subtitle': 'Selamat datang kembali',
'login.signingIn': 'Sedang masuk…',
diff --git a/shared/src/i18n/id/mobileVacay.ts b/shared/src/i18n/id/mobileVacay.ts
index 1acb914ed..42e2bc676 100644
--- a/shared/src/i18n/id/mobileVacay.ts
+++ b/shared/src/i18n/id/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Bulan berikutnya',
'mobileVacay.usedCount': '{count} terpakai',
'mobileVacay.leftCount': '{count} tersisa',
- 'mobileVacay.daysPerYear': 'hari / tahun',
+ 'mobileVacay.left': 'tersisa',
+ 'mobileVacay.modeCompany': 'Perusahaan',
'mobileVacay.decreaseAllowance': 'Kurangi jatah',
'mobileVacay.increaseAllowance': 'Tambah jatah',
'mobileVacay.companyLegend': 'Libur perusahaan',
diff --git a/shared/src/i18n/it/login.ts b/shared/src/i18n/it/login.ts
index 8a6ce0bd5..1804993f9 100644
--- a/shared/src/i18n/it/login.ts
+++ b/shared/src/i18n/it/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'I tuoi viaggi.\nIl tuo programma.',
'login.description':
'Programma viaggi in collaborazione con mappe interattive, budget e sincronizzazione in tempo reale.',
- 'login.features.maps': 'Mappe Interattive',
- 'login.features.mapsDesc': 'Google Places, percorsi e clustering',
- 'login.features.realtime': 'Sincronizzazione in tempo reale',
- 'login.features.realtimeDesc': 'Programmate insieme tramite WebSocket',
- 'login.features.budget': 'Tracciamento costi',
- 'login.features.budgetDesc': 'Categorie, grafici e costi per persona',
- 'login.features.collab': 'Collaborazione',
- 'login.features.collabDesc': 'Multi-utente con viaggi condivisi',
- 'login.features.packing': 'Lista Valigia',
- 'login.features.packingDesc': 'Categorie, progressi e suggerimenti',
- 'login.features.bookings': 'Prenotazioni',
- 'login.features.bookingsDesc': 'Voli, alloggi, ristoranti e altro',
- 'login.features.files': 'Documenti',
- 'login.features.filesDesc': 'Carica e gestisci i documenti',
- 'login.features.routes': 'Percorsi Intelligenti',
- 'login.features.routesDesc': 'Ottimizzazione automatica ed esportazione su Google Maps',
- 'login.selfHosted': 'Self-hosted · Open Source · Your data stays yours',
'login.title': 'Accedi',
'login.subtitle': 'Bentornato',
'login.signingIn': 'Accesso in corso…',
diff --git a/shared/src/i18n/it/mobileVacay.ts b/shared/src/i18n/it/mobileVacay.ts
index 720500eaa..d1dda1431 100644
--- a/shared/src/i18n/it/mobileVacay.ts
+++ b/shared/src/i18n/it/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Mese successivo',
'mobileVacay.usedCount': '{count} usati',
'mobileVacay.leftCount': '{count} rimanenti',
- 'mobileVacay.daysPerYear': 'giorni / anno',
+ 'mobileVacay.left': 'rimanenti',
+ 'mobileVacay.modeCompany': 'Azienda',
'mobileVacay.decreaseAllowance': 'Riduci giorni disponibili',
'mobileVacay.increaseAllowance': 'Aumenta giorni disponibili',
'mobileVacay.companyLegend': 'Ferie aziendali',
diff --git a/shared/src/i18n/ja/login.ts b/shared/src/i18n/ja/login.ts
index 238050912..6db123a19 100644
--- a/shared/src/i18n/ja/login.ts
+++ b/shared/src/i18n/ja/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'ログインに失敗しました。認証情報を確認してください。',
'login.tagline': 'あなたの旅行。\nあなたの計画。',
'login.description': 'インタラクティブなマップ、予算、リアルタイム同期で、みんなで旅行を計画。',
- 'login.features.maps': 'インタラクティブマップ',
- 'login.features.mapsDesc': 'Google Places、経路、クラスタリング',
- 'login.features.realtime': 'リアルタイム同期',
- 'login.features.realtimeDesc': 'WebSocketで共同計画',
- 'login.features.budget': '費用管理',
- 'login.features.budgetDesc': 'カテゴリ、グラフ、人数別費用',
- 'login.features.collab': 'コラボレーション',
- 'login.features.collabDesc': '複数ユーザーで旅行を共有',
- 'login.features.packing': '持ち物リスト',
- 'login.features.packingDesc': 'カテゴリ、進捗、提案',
- 'login.features.bookings': '予約',
- 'login.features.bookingsDesc': '航空券、ホテル、レストランなど',
- 'login.features.files': 'ドキュメント',
- 'login.features.filesDesc': '書類のアップロードと管理',
- 'login.features.routes': 'スマート経路',
- 'login.features.routesDesc': '自動最適化&Google Maps書き出し',
- 'login.selfHosted': 'セルフホスト · オープンソース · データはあなたのもの',
'login.title': 'サインイン',
'login.subtitle': 'おかえりなさい',
'login.signingIn': 'サインイン中…',
diff --git a/shared/src/i18n/ja/mobileVacay.ts b/shared/src/i18n/ja/mobileVacay.ts
index 634081423..62d5d1732 100644
--- a/shared/src/i18n/ja/mobileVacay.ts
+++ b/shared/src/i18n/ja/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': '次の月',
'mobileVacay.usedCount': '{count} 日取得',
'mobileVacay.leftCount': '残り {count} 日',
- 'mobileVacay.daysPerYear': '日 / 年',
+ 'mobileVacay.left': '残り',
+ 'mobileVacay.modeCompany': '会社',
'mobileVacay.decreaseAllowance': '付与日数を減らす',
'mobileVacay.increaseAllowance': '付与日数を増やす',
'mobileVacay.companyLegend': '会社の休業日',
diff --git a/shared/src/i18n/ko/login.ts b/shared/src/i18n/ko/login.ts
index e2ed8cf42..45cf3685a 100644
--- a/shared/src/i18n/ko/login.ts
+++ b/shared/src/i18n/ko/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': '로그인 실패. 자격 증명을 확인하세요.',
'login.tagline': '나의 여행.\n나의 계획.',
'login.description': '인터랙티브 지도, 예산, 실시간 동기화로 함께 여행을 계획하세요.',
- 'login.features.maps': '인터랙티브 지도',
- 'login.features.mapsDesc': 'Google Places, 경로 및 클러스터링',
- 'login.features.realtime': '실시간 동기화',
- 'login.features.realtimeDesc': 'WebSocket으로 함께 계획',
- 'login.features.budget': '비용 추적',
- 'login.features.budgetDesc': '카테고리, 차트 및 1인당 비용',
- 'login.features.collab': '협업',
- 'login.features.collabDesc': '공유 여행으로 다중 사용자 지원',
- 'login.features.packing': '짐 목록',
- 'login.features.packingDesc': '카테고리, 진행 상황 및 제안',
- 'login.features.bookings': '예약',
- 'login.features.bookingsDesc': '항공, 호텔, 레스토랑 등',
- 'login.features.files': '문서',
- 'login.features.filesDesc': '문서 업로드 및 관리',
- 'login.features.routes': '스마트 경로',
- 'login.features.routesDesc': '자동 최적화 및 Google Maps 내보내기',
- 'login.selfHosted': '자체 호스팅 · 오픈 소스 · 내 데이터는 내 것',
'login.title': '로그인',
'login.subtitle': '다시 오신 것을 환영합니다',
'login.signingIn': '로그인 중…',
diff --git a/shared/src/i18n/ko/mobileVacay.ts b/shared/src/i18n/ko/mobileVacay.ts
index b4ccb2008..6a48063a0 100644
--- a/shared/src/i18n/ko/mobileVacay.ts
+++ b/shared/src/i18n/ko/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': '다음 달',
'mobileVacay.usedCount': '{count}일 사용',
'mobileVacay.leftCount': '{count}일 남음',
- 'mobileVacay.daysPerYear': '일 / 년',
+ 'mobileVacay.left': '남음',
+ 'mobileVacay.modeCompany': '회사',
'mobileVacay.decreaseAllowance': '할당 줄이기',
'mobileVacay.increaseAllowance': '할당 늘리기',
'mobileVacay.companyLegend': '회사 지정 휴무',
diff --git a/shared/src/i18n/nl/login.ts b/shared/src/i18n/nl/login.ts
index 00ac863eb..083106fa0 100644
--- a/shared/src/i18n/nl/login.ts
+++ b/shared/src/i18n/nl/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Inloggen mislukt. Controleer je inloggegevens.',
'login.tagline': 'Jouw reizen.\nJouw plan.',
'login.description': 'Plan reizen samen met interactieve kaarten, budgetten en realtime synchronisatie.',
- 'login.features.maps': 'Interactieve kaarten',
- 'login.features.mapsDesc': 'Google Places, routes en clustering',
- 'login.features.realtime': 'Realtime synchronisatie',
- 'login.features.realtimeDesc': 'Plan samen via WebSocket',
- 'login.features.budget': 'Onkostenbeheer',
- 'login.features.budgetDesc': 'Categorieën, grafieken en kosten per persoon',
- 'login.features.collab': 'Samenwerking',
- 'login.features.collabDesc': 'Meerdere gebruikers met gedeelde reizen',
- 'login.features.packing': 'Paklijsten',
- 'login.features.packingDesc': 'Categorieën, voortgang en suggesties',
- 'login.features.bookings': 'Reserveringen',
- 'login.features.bookingsDesc': 'Vluchten, hotels, restaurants en meer',
- 'login.features.files': 'Documenten',
- 'login.features.filesDesc': 'Upload en beheer documenten',
- 'login.features.routes': 'Slimme routes',
- 'login.features.routesDesc': 'Automatisch optimaliseren en exporteren naar Google Maps',
- 'login.selfHosted': 'Zelf gehost · Open Source · Jouw gegevens blijven van jou',
'login.title': 'Inloggen',
'login.subtitle': 'Welkom terug',
'login.signingIn': 'Inloggen…',
diff --git a/shared/src/i18n/nl/mobileVacay.ts b/shared/src/i18n/nl/mobileVacay.ts
index 03ae86de9..b3bca586c 100644
--- a/shared/src/i18n/nl/mobileVacay.ts
+++ b/shared/src/i18n/nl/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Volgende maand',
'mobileVacay.usedCount': '{count} gebruikt',
'mobileVacay.leftCount': '{count} over',
- 'mobileVacay.daysPerYear': 'dagen / jaar',
+ 'mobileVacay.left': 'over',
+ 'mobileVacay.modeCompany': 'Bedrijf',
'mobileVacay.decreaseAllowance': 'Tegoed verlagen',
'mobileVacay.increaseAllowance': 'Tegoed verhogen',
'mobileVacay.companyLegend': 'Bedrijfsverlof',
diff --git a/shared/src/i18n/pl/login.ts b/shared/src/i18n/pl/login.ts
index e98b9d106..a26d647ad 100644
--- a/shared/src/i18n/pl/login.ts
+++ b/shared/src/i18n/pl/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Twoje podróże.\nTwój plan.',
'login.description':
'Planuj wspólnie podróże z interaktywnymi mapami, budżetami i synchronizacją w czasie rzeczywistym.',
- 'login.features.maps': 'Interaktywne mapy',
- 'login.features.mapsDesc': 'Google Places, trasy i grupowanie',
- 'login.features.realtime': 'Synchronizacja w czasie rzeczywistym',
- 'login.features.realtimeDesc': 'Planuj wspólnie przez WebSocket',
- 'login.features.budget': 'Śledzenie kosztów',
- 'login.features.budgetDesc': 'Kategorie, wykresy i koszty w przeliczeniu na osobę',
- 'login.features.collab': 'Współpraca',
- 'login.features.collabDesc': 'Wielu użytkowników i wspólne podróże',
- 'login.features.packing': 'Listy pakowania',
- 'login.features.packingDesc': 'Kategorie, postęp i sugestie',
- 'login.features.bookings': 'Rezerwacje',
- 'login.features.bookingsDesc': 'Loty, hotele, restauracje i więcej',
- 'login.features.files': 'Dokumenty',
- 'login.features.filesDesc': 'Przesyłaj i zarządzaj dokumentami',
- 'login.features.routes': 'Inteligentne trasy',
- 'login.features.routesDesc': 'Automatyczna optymalizacja i eksport do Google Maps',
- 'login.selfHosted': 'Własny hosting · Otwarty kod źródłowy · Twoje dane pozostają Twoje',
'login.title': 'Zaloguj się',
'login.subtitle': 'Witaj ponownie',
'login.signingIn': 'Logowanie...',
diff --git a/shared/src/i18n/pl/mobileVacay.ts b/shared/src/i18n/pl/mobileVacay.ts
index 9e252d784..77720119c 100644
--- a/shared/src/i18n/pl/mobileVacay.ts
+++ b/shared/src/i18n/pl/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Następny miesiąc',
'mobileVacay.usedCount': 'Wykorzystano: {count}',
'mobileVacay.leftCount': 'Pozostało: {count}',
- 'mobileVacay.daysPerYear': 'dni / rok',
+ 'mobileVacay.left': 'pozostało',
+ 'mobileVacay.modeCompany': 'Firma',
'mobileVacay.decreaseAllowance': 'Zmniejsz limit',
'mobileVacay.increaseAllowance': 'Zwiększ limit',
'mobileVacay.companyLegend': 'Dni firmowe',
diff --git a/shared/src/i18n/ru/login.ts b/shared/src/i18n/ru/login.ts
index cff0b25e2..48e7a1066 100644
--- a/shared/src/i18n/ru/login.ts
+++ b/shared/src/i18n/ru/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Ваши поездки.\nВаш план.',
'login.description':
'Планируйте поездки совместно с интерактивными картами, бюджетами и синхронизацией в реальном времени.',
- 'login.features.maps': 'Интерактивные карты',
- 'login.features.mapsDesc': 'Google Places, маршруты и кластеризация',
- 'login.features.realtime': 'Синхронизация в реальном времени',
- 'login.features.realtimeDesc': 'Планируйте вместе через WebSocket',
- 'login.features.budget': 'Контроль расходов',
- 'login.features.budgetDesc': 'Категории, графики и расходы на человека',
- 'login.features.collab': 'Совместная работа',
- 'login.features.collabDesc': 'Многопользовательский режим с общими поездками',
- 'login.features.packing': 'Списки вещей',
- 'login.features.packingDesc': 'Категории, прогресс и подсказки',
- 'login.features.bookings': 'Бронирования',
- 'login.features.bookingsDesc': 'Авиабилеты, отели, рестораны и многое другое',
- 'login.features.files': 'Документы',
- 'login.features.filesDesc': 'Загружайте и управляйте документами',
- 'login.features.routes': 'Умные маршруты',
- 'login.features.routesDesc': 'Автооптимизация и экспорт в Google Maps',
- 'login.selfHosted': 'Самостоятельный хостинг · Открытый код · Ваши данные остаются у вас',
'login.title': 'Вход',
'login.subtitle': 'С возвращением',
'login.signingIn': 'Вход…',
diff --git a/shared/src/i18n/ru/mobileVacay.ts b/shared/src/i18n/ru/mobileVacay.ts
index cf5228129..6c5395ff3 100644
--- a/shared/src/i18n/ru/mobileVacay.ts
+++ b/shared/src/i18n/ru/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Следующий месяц',
'mobileVacay.usedCount': 'Использовано: {count}',
'mobileVacay.leftCount': 'Осталось: {count}',
- 'mobileVacay.daysPerYear': 'дней / год',
+ 'mobileVacay.left': 'осталось',
+ 'mobileVacay.modeCompany': 'Компания',
'mobileVacay.decreaseAllowance': 'Уменьшить лимит',
'mobileVacay.increaseAllowance': 'Увеличить лимит',
'mobileVacay.companyLegend': 'Корпоративные дни',
diff --git a/shared/src/i18n/sv/login.ts b/shared/src/i18n/sv/login.ts
index 09045dde8..05e0ec172 100644
--- a/shared/src/i18n/sv/login.ts
+++ b/shared/src/i18n/sv/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Dina resor.\nDin plan.',
'login.description':
'Planera resor tillsammans med hjälp av interaktiva kartor, budgetar och synkronisering i realtid.',
- 'login.features.maps': 'Interaktiva kartor',
- 'login.features.mapsDesc': 'Google Places, rutter och klusterbildning',
- 'login.features.realtime': 'Synkronisering i realtid',
- 'login.features.realtimeDesc': 'Planera tillsammans via WebSocket',
- 'login.features.budget': 'Kostnadsuppföljning',
- 'login.features.budgetDesc': 'Kategorier, diagram och kostnader per person',
- 'login.features.collab': 'Samarbete',
- 'login.features.collabDesc': 'Flera användare med delade resor',
- 'login.features.packing': 'Packlistor',
- 'login.features.packingDesc': 'Kategorier, framsteg och förslag',
- 'login.features.bookings': 'Bokningar',
- 'login.features.bookingsDesc': 'Flyg, hotell, restauranger och mycket mer',
- 'login.features.files': 'Dokument',
- 'login.features.filesDesc': 'Ladda upp och hantera dokument',
- 'login.features.routes': 'Smarta rutter',
- 'login.features.routesDesc': 'Automatisk optimering och export till Google Maps',
- 'login.selfHosted': 'Egenhostad · Öppen källkod · Dina data förblir dina',
'login.title': 'Logga in',
'login.subtitle': 'Välkommen tillbaka',
'login.signingIn': 'Loggar in…',
diff --git a/shared/src/i18n/sv/mobileVacay.ts b/shared/src/i18n/sv/mobileVacay.ts
index dd210c106..f5a607f52 100644
--- a/shared/src/i18n/sv/mobileVacay.ts
+++ b/shared/src/i18n/sv/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Nästa månad',
'mobileVacay.usedCount': '{count} använda',
'mobileVacay.leftCount': '{count} kvar',
- 'mobileVacay.daysPerYear': 'dagar / år',
+ 'mobileVacay.left': 'kvar',
+ 'mobileVacay.modeCompany': 'Företag',
'mobileVacay.decreaseAllowance': 'Minska kvot',
'mobileVacay.increaseAllowance': 'Öka kvot',
'mobileVacay.companyLegend': 'Företagsledighet',
diff --git a/shared/src/i18n/tr/login.ts b/shared/src/i18n/tr/login.ts
index b6b11ec76..d851c51b9 100644
--- a/shared/src/i18n/tr/login.ts
+++ b/shared/src/i18n/tr/login.ts
@@ -5,23 +5,6 @@ const login: TranslationStrings = {
'login.tagline': 'Gezileriniz.\\nPlanınız.',
'login.description':
'Etkileşimli haritalar, bütçeler ve gerçek zamanlı senkronizasyonla gezileri işbirliği içinde planlayın.',
- 'login.features.maps': 'İnteraktif Haritalar',
- 'login.features.mapsDesc': 'Google Rehber, rotalar ve kümeleme',
- 'login.features.realtime': 'Gerçek Zamanlı Senkronizasyon',
- 'login.features.realtimeDesc': 'WebSocket aracılığıyla birlikte plan yapın',
- 'login.features.budget': 'Maliyet Takibi',
- 'login.features.budgetDesc': 'Kategoriler, grafikler ve kişi başı maliyetler',
- 'login.features.collab': 'İşbirliği',
- 'login.features.collabDesc': 'Paylaşılan gezilerle çok kullanıcılı',
- 'login.features.packing': 'Paketleme Listeleri',
- 'login.features.packingDesc': 'Kategoriler, ilerleme ve öneriler',
- 'login.features.bookings': 'Rezervasyonlar',
- 'login.features.bookingsDesc': 'Uçuşlar, oteller, restoranlar ve daha fazlası',
- 'login.features.files': 'Belgeler',
- 'login.features.filesDesc': 'Belgeleri yükleyin ve yönetin',
- 'login.features.routes': 'Akıllı Rotalar',
- 'login.features.routesDesc': "Otomatik optimize etme ve Google Haritalar'ı dışa aktarma",
- 'login.selfHosted': 'Kendi kendine barındırılan \\u00B7 Açık Kaynak \\u00B7 Verileriniz size ait kalır',
'login.title': 'Oturum Aç',
'login.subtitle': 'Tekrar hoş geldiniz',
'login.signingIn': 'Oturum açılıyor…',
diff --git a/shared/src/i18n/tr/mobileVacay.ts b/shared/src/i18n/tr/mobileVacay.ts
index c566a3adb..dc1bac367 100644
--- a/shared/src/i18n/tr/mobileVacay.ts
+++ b/shared/src/i18n/tr/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Sonraki ay',
'mobileVacay.usedCount': '{count} kullanıldı',
'mobileVacay.leftCount': '{count} kaldı',
- 'mobileVacay.daysPerYear': 'gün / yıl',
+ 'mobileVacay.left': 'kaldı',
+ 'mobileVacay.modeCompany': 'Şirket',
'mobileVacay.decreaseAllowance': 'Hakkı azalt',
'mobileVacay.increaseAllowance': 'Hakkı artır',
'mobileVacay.companyLegend': 'Şirket tatili',
diff --git a/shared/src/i18n/uk/login.ts b/shared/src/i18n/uk/login.ts
index 741c5920f..ba502d03b 100644
--- a/shared/src/i18n/uk/login.ts
+++ b/shared/src/i18n/uk/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Помилка входу. Перевірте свої облікові дані.',
'login.tagline': 'Ваші поїздки.\nВаш план.',
'login.description': 'Плануйте поїздки разом з інтерактивними картами, бюджетами та синхронізацією в реальному часі.',
- 'login.features.maps': 'Інтерактивні карти',
- 'login.features.mapsDesc': 'Google Places, маршрути та кластеризація',
- 'login.features.realtime': 'Синхронізація в реальному часі',
- 'login.features.realtimeDesc': 'Плануйте разом через WebSocket',
- 'login.features.budget': 'Контроль витрат',
- 'login.features.budgetDesc': 'Категорії, графіки та витрати на особу',
- 'login.features.collab': 'Спільна робота',
- 'login.features.collabDesc': 'Багатокористувацький режим зі спільними поїздками',
- 'login.features.packing': 'Списки речей',
- 'login.features.packingDesc': 'Категорії, прогрес та підказки',
- 'login.features.bookings': 'Бронювання',
- 'login.features.bookingsDesc': 'Авіаквитки, готелі, ресторани та багато іншого',
- 'login.features.files': 'Документи',
- 'login.features.filesDesc': 'Завантажуйте та керуйте документами',
- 'login.features.routes': 'Розумні маршрути',
- 'login.features.routesDesc': 'Автооптимізація та експорт з Google Maps',
- 'login.selfHosted': 'Self-hosted · Відкритий код · Ваші дані залишаються у вас',
'login.title': 'Вхід',
'login.subtitle': 'З поверненням',
'login.signingIn': 'Вхід…',
diff --git a/shared/src/i18n/uk/mobileVacay.ts b/shared/src/i18n/uk/mobileVacay.ts
index 9f423eb42..57a95f9bb 100644
--- a/shared/src/i18n/uk/mobileVacay.ts
+++ b/shared/src/i18n/uk/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Наступний місяць',
'mobileVacay.usedCount': 'Використано: {count}',
'mobileVacay.leftCount': 'Залишилось: {count}',
- 'mobileVacay.daysPerYear': 'днів / рік',
+ 'mobileVacay.left': 'залишилось',
+ 'mobileVacay.modeCompany': 'Компанія',
'mobileVacay.decreaseAllowance': 'Зменшити ліміт',
'mobileVacay.increaseAllowance': 'Збільшити ліміт',
'mobileVacay.companyLegend': 'Корпоративні дні',
diff --git a/shared/src/i18n/vi/login.ts b/shared/src/i18n/vi/login.ts
index 2fbd29e7f..495906834 100644
--- a/shared/src/i18n/vi/login.ts
+++ b/shared/src/i18n/vi/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': 'Đăng nhập không thành công. Vui lòng kiểm tra thông tin đăng nhập của bạn.',
'login.tagline': 'Chuyến đi của bạn.\nKế hoạch của bạn.',
'login.description': 'Lập kế hoạch chuyến đi cộng tác với bản đồ tương tác, ngân sách và đồng bộ hóa thời gian thực.',
- 'login.features.maps': 'Bản đồ tương tác',
- 'login.features.mapsDesc': 'Google Địa điểm, tuyến đường và phân cụm',
- 'login.features.realtime': 'Đồng bộ hóa thời gian thực',
- 'login.features.realtimeDesc': 'Lập kế hoạch cùng nhau thông qua WebSocket',
- 'login.features.budget': 'Theo dõi chi phí',
- 'login.features.budgetDesc': 'Danh mục, biểu đồ và chi phí mỗi người',
- 'login.features.collab': 'Sự hợp tác',
- 'login.features.collabDesc': 'Nhiều người dùng với các chuyến đi chung',
- 'login.features.packing': 'Danh sách đóng gói',
- 'login.features.packingDesc': 'Danh mục, tiến độ và đề xuất',
- 'login.features.bookings': 'Đặt chỗ',
- 'login.features.bookingsDesc': 'Chuyến bay, khách sạn, nhà hàng và hơn thế nữa',
- 'login.features.files': 'Tài liệu',
- 'login.features.filesDesc': 'Tải lên và quản lý tài liệu',
- 'login.features.routes': 'Tuyến đường thông minh',
- 'login.features.routesDesc': 'Tự động tối ưu hóa & xuất Google Maps',
- 'login.selfHosted': 'Tự lưu trữ · Mã nguồn mở · Dữ liệu của bạn vẫn là của bạn',
'login.title': 'Đăng nhập',
'login.subtitle': 'Chào mừng trở lại',
'login.signingIn': 'Đang đăng nhập…',
diff --git a/shared/src/i18n/vi/mobileVacay.ts b/shared/src/i18n/vi/mobileVacay.ts
index 0b3c39faf..bde6b7f24 100644
--- a/shared/src/i18n/vi/mobileVacay.ts
+++ b/shared/src/i18n/vi/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': 'Tháng sau',
'mobileVacay.usedCount': 'Đã dùng {count}',
'mobileVacay.leftCount': 'Còn {count}',
- 'mobileVacay.daysPerYear': 'ngày / năm',
+ 'mobileVacay.left': 'còn',
+ 'mobileVacay.modeCompany': 'Công ty',
'mobileVacay.decreaseAllowance': 'Giảm hạn mức',
'mobileVacay.increaseAllowance': 'Tăng hạn mức',
'mobileVacay.companyLegend': 'Ngày nghỉ công ty',
diff --git a/shared/src/i18n/zh-TW/login.ts b/shared/src/i18n/zh-TW/login.ts
index 099dbfe9c..22451f2cf 100644
--- a/shared/src/i18n/zh-TW/login.ts
+++ b/shared/src/i18n/zh-TW/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': '登入失敗,請檢查你的憑據。',
'login.tagline': '你的旅行。\n你的計劃。',
'login.description': '透過互動地圖、預算管理和即時同步,協同規劃旅行。',
- 'login.features.maps': '互動地圖',
- 'login.features.mapsDesc': 'Google Places、路線和聚類',
- 'login.features.realtime': '即時同步',
- 'login.features.realtimeDesc': '透過 WebSocket 協同規劃',
- 'login.features.budget': '費用追蹤',
- 'login.features.budgetDesc': '分類、圖表和人均費用',
- 'login.features.collab': '協作',
- 'login.features.collabDesc': '多使用者共享旅行',
- 'login.features.packing': '行李清單',
- 'login.features.packingDesc': '分類、進度和建議',
- 'login.features.bookings': '預訂',
- 'login.features.bookingsDesc': '航班、酒店、餐廳等',
- 'login.features.files': '文件',
- 'login.features.filesDesc': '上傳和管理文件',
- 'login.features.routes': '智慧路線',
- 'login.features.routesDesc': '自動最佳化和匯出到 Google Maps',
- 'login.selfHosted': '自託管 · 開源 · 資料由你掌控',
'login.title': '登入',
'login.subtitle': '歡迎回來',
'login.signingIn': '登入中…',
diff --git a/shared/src/i18n/zh-TW/mobileVacay.ts b/shared/src/i18n/zh-TW/mobileVacay.ts
index 5b98eb94e..ed6327a9c 100644
--- a/shared/src/i18n/zh-TW/mobileVacay.ts
+++ b/shared/src/i18n/zh-TW/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': '下個月',
'mobileVacay.usedCount': '已用 {count}',
'mobileVacay.leftCount': '剩餘 {count}',
- 'mobileVacay.daysPerYear': '天 / 年',
+ 'mobileVacay.left': '剩餘',
+ 'mobileVacay.modeCompany': '公司',
'mobileVacay.decreaseAllowance': '減少額度',
'mobileVacay.increaseAllowance': '增加額度',
'mobileVacay.companyLegend': '公司假期',
diff --git a/shared/src/i18n/zh/login.ts b/shared/src/i18n/zh/login.ts
index aacb4a1c7..02d7226ed 100644
--- a/shared/src/i18n/zh/login.ts
+++ b/shared/src/i18n/zh/login.ts
@@ -4,23 +4,6 @@ const login: TranslationStrings = {
'login.error': '登录失败,请检查你的凭据。',
'login.tagline': '你的旅行。\n你的计划。',
'login.description': '通过互动地图、预算管理和实时同步,协同规划旅行。',
- 'login.features.maps': '互动地图',
- 'login.features.mapsDesc': 'Google Places、路线和聚类',
- 'login.features.realtime': '实时同步',
- 'login.features.realtimeDesc': '通过 WebSocket 协同规划',
- 'login.features.budget': '费用跟踪',
- 'login.features.budgetDesc': '分类、图表和人均费用',
- 'login.features.collab': '协作',
- 'login.features.collabDesc': '多用户共享旅行',
- 'login.features.packing': '行李清单',
- 'login.features.packingDesc': '分类、进度和建议',
- 'login.features.bookings': '预订',
- 'login.features.bookingsDesc': '航班、酒店、餐厅等',
- 'login.features.files': '文档',
- 'login.features.filesDesc': '上传和管理文档',
- 'login.features.routes': '智能路线',
- 'login.features.routesDesc': '自动优化和导出到 Google Maps',
- 'login.selfHosted': '自托管 · 开源 · 数据由你掌控',
'login.title': '登录',
'login.subtitle': '欢迎回来',
'login.signingIn': '登录中…',
diff --git a/shared/src/i18n/zh/mobileVacay.ts b/shared/src/i18n/zh/mobileVacay.ts
index dd1a9645c..a8defcdbd 100644
--- a/shared/src/i18n/zh/mobileVacay.ts
+++ b/shared/src/i18n/zh/mobileVacay.ts
@@ -7,7 +7,8 @@ const mobileVacay: TranslationStrings = {
'mobileVacay.nextMonth': '下个月',
'mobileVacay.usedCount': '已用 {count}',
'mobileVacay.leftCount': '剩余 {count}',
- 'mobileVacay.daysPerYear': '天 / 年',
+ 'mobileVacay.left': '剩余',
+ 'mobileVacay.modeCompany': '公司',
'mobileVacay.decreaseAllowance': '减少额度',
'mobileVacay.increaseAllowance': '增加额度',
'mobileVacay.companyLegend': '公司假期',