diff --git a/package-lock.json b/package-lock.json
index 33f0acc..6bbce86 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,6 @@
"": {
"devDependencies": {
"@tailwindcss/vite": "^4.3.0",
- "alpinejs": "^3.15.12",
"axios": "^1.11.0",
"chart.js": "^4.5.1",
"concurrently": "^9.0.1",
@@ -1154,23 +1153,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@vue/reactivity": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz",
- "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/shared": "3.1.5"
- }
- },
- "node_modules/@vue/shared": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
- "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -1184,16 +1166,6 @@
"node": ">= 6.0.0"
}
},
- "node_modules/alpinejs": {
- "version": "3.15.12",
- "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz",
- "integrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vue/reactivity": "~3.1.1"
- }
- },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
diff --git a/package.json b/package.json
index 10e9c16..7507236 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,6 @@
},
"devDependencies": {
"@tailwindcss/vite": "^4.3.0",
- "alpinejs": "^3.15.12",
"axios": "^1.11.0",
"chart.js": "^4.5.1",
"concurrently": "^9.0.1",
diff --git a/public/brand/logo-dark.svg b/public/brand/logo-dark.svg
new file mode 100644
index 0000000..d2a1d4f
--- /dev/null
+++ b/public/brand/logo-dark.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/brand/logo-light.svg b/public/brand/logo-light.svg
new file mode 100644
index 0000000..cad075f
--- /dev/null
+++ b/public/brand/logo-light.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/brand/logo-mark.svg b/public/brand/logo-mark.svg
new file mode 100644
index 0000000..f03c1ef
--- /dev/null
+++ b/public/brand/logo-mark.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/brand/logo.svg b/public/brand/logo.svg
new file mode 100644
index 0000000..27920b7
--- /dev/null
+++ b/public/brand/logo.svg
@@ -0,0 +1,14 @@
+
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..a2a6502
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/site.webmanifest b/public/site.webmanifest
new file mode 100644
index 0000000..989d9d9
--- /dev/null
+++ b/public/site.webmanifest
@@ -0,0 +1,13 @@
+{
+ "name": "Nimuli",
+ "short_name": "Nimuli",
+ "description": "Short links, QR codes, and bio pages — all in one place.",
+ "start_url": "/dashboard",
+ "display": "standalone",
+ "background_color": "#0a0a0f",
+ "theme_color": "#7c3aed",
+ "icons": [
+ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
+ { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
+ ]
+}
diff --git a/resources/css/app.css b/resources/css/app.css
index af0bf80..ea31b03 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -6,19 +6,26 @@
@source '../**/*.js';
@theme {
+ /* Dark mode (default) */
--color-bg: #0a0a0f;
--color-s1: #111117;
--color-s2: #16161e;
--color-s3: #1c1c26;
- --color-b1: rgb(255 255 255 / 6%);
- --color-b2: rgb(255 255 255 / 10%);
+ --color-b1: rgb(255 255 255 / 8%);
+ --color-b2: rgb(255 255 255 / 14%);
--color-t1: #f0f0f5;
--color-t2: #8888a0;
--color-t3: #44445a;
- --color-blue: #4f8ef7;
+
+ /* Accent — purple-pink gradient, consistent across both themes */
+ --color-blue: #7c3aed;
+ --color-accent: #7c3aed;
+ --color-accent-2: #ec4899;
+
--color-green: #3ecf8e;
--color-amber: #f5a623;
--color-red: #f05252;
+
--font-mono: 'JetBrains Mono', monospace;
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
@@ -33,8 +40,30 @@
--color-t1: #111827;
--color-t2: #6b7280;
--color-t3: #9ca3af;
+
--color-blue: #7c3aed;
+ --color-accent: #7c3aed;
+ --color-accent-2: #ec4899;
+
--color-green: #10b981;
--color-amber: #f59e0b;
--color-red: #ef4444;
}
+
+/* Visible borders on all themes */
+*, ::before, ::after { border-color: var(--color-b1); }
+
+@layer utilities {
+ .bg-accent-gradient {
+ background: linear-gradient(135deg, var(--color-accent), var(--color-accent-2));
+ }
+ .text-accent-gradient {
+ background: linear-gradient(135deg, var(--color-accent), var(--color-accent-2));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ }
+ .border-accent-gradient {
+ border-image: linear-gradient(135deg, var(--color-accent), var(--color-accent-2)) 1;
+ }
+}
diff --git a/resources/js/app.js b/resources/js/app.js
index 2cf1501..3ade3e0 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -1,34 +1,23 @@
import '../css/app.css';
import './bootstrap';
-import Alpine from 'alpinejs';
import Chart from 'chart.js/auto';
window.Chart = Chart;
-// Theme store — reads localStorage, applies to
-Alpine.store('theme', {
- current: localStorage.getItem('nimuli_theme') || document.documentElement.dataset.theme || 'dark',
+// Alpine stores registered via alpine:init — Livewire v3 owns Alpine lifecycle
+document.addEventListener('alpine:init', () => {
+ Alpine.store('theme', {
+ current: localStorage.getItem('nimuli_theme') || document.documentElement.dataset.theme || 'dark',
+ init() { this.apply(this.current); },
+ apply(theme) {
+ this.current = theme;
+ document.documentElement.dataset.theme = theme;
+ localStorage.setItem('nimuli_theme', theme);
+ },
+ cycle() {
+ const order = ['dark', 'light', 'system'];
+ this.apply(order[(order.indexOf(this.current) + 1) % order.length]);
+ },
+ });
- init() {
- this.apply(this.current);
- },
-
- apply(theme) {
- this.current = theme;
- document.documentElement.dataset.theme = theme;
- localStorage.setItem('nimuli_theme', theme);
- },
-
- cycle() {
- const order = ['dark', 'light', 'system'];
- const next = order[(order.indexOf(this.current) + 1) % order.length];
- this.apply(next);
- },
+ Alpine.store('sidebar', { open: false });
});
-
-// Sidebar drawer store — mobile open/close
-Alpine.store('sidebar', {
- open: false,
-});
-
-window.Alpine = Alpine;
-Alpine.start();
diff --git a/resources/views/components/sidebar.blade.php b/resources/views/components/sidebar.blade.php
index 7cb42a3..e74ba6a 100644
--- a/resources/views/components/sidebar.blade.php
+++ b/resources/views/components/sidebar.blade.php
@@ -100,8 +100,9 @@
@click="wsOpen = !wsOpen"
class="w-full flex items-center gap-3 px-4 py-4 hover:bg-s2 transition-colors border-b border-white/[.06]"
>
+