diff --git a/resources/css/app.css b/resources/css/app.css
index 783e69c..97767d0 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -2,6 +2,8 @@
@import 'tw-animate-css';
+@import './components.css';
+
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';
diff --git a/resources/css/components.css b/resources/css/components.css
new file mode 100644
index 0000000..a6906da
--- /dev/null
+++ b/resources/css/components.css
@@ -0,0 +1,16 @@
+/* ==========================================================================
+ BEM UI Components Index Manifest (Tailwind CSS v4)
+ ========================================================================== */
+
+@import './components/heading.css';
+@import './components/input-error.css';
+@import './components/text-link.css';
+@import './components/app-logo.css';
+@import './components/user-info.css';
+@import './components/sidebar-header.css';
+@import './components/app-content.css';
+@import './components/appearance-tabs.css';
+@import './components/password-input.css';
+@import './components/auth-layout.css';
+@import './components/chat.css';
+@import './components/lightbox.css';
diff --git a/resources/css/components/app-content.css b/resources/css/components/app-content.css
new file mode 100644
index 0000000..f19c554
--- /dev/null
+++ b/resources/css/components/app-content.css
@@ -0,0 +1,5 @@
+/* 7. AppContent Layout Component */
+.app-content {
+ @apply mx-auto flex h-full w-full max-w-7xl flex-1 flex-col gap-4;
+ border-radius: var(--radius);
+}
diff --git a/resources/css/components/app-logo.css b/resources/css/components/app-logo.css
new file mode 100644
index 0000000..fc7e468
--- /dev/null
+++ b/resources/css/components/app-logo.css
@@ -0,0 +1,20 @@
+/* 4. AppLogo Component */
+.app-logo__icon-container {
+ @apply flex aspect-square size-8 items-center justify-center;
+ border-radius: var(--radius);
+ background-color: var(--sidebar-primary);
+ color: var(--sidebar-primary-foreground);
+
+ .app-logo__icon {
+ @apply size-5 fill-current;
+ }
+}
+
+.app-logo__text-container {
+ @apply ml-1 grid flex-1 text-left text-sm;
+
+ .app-logo__text {
+ @apply mb-0.5 truncate leading-tight font-semibold;
+ color: var(--foreground);
+ }
+}
diff --git a/resources/css/components/appearance-tabs.css b/resources/css/components/appearance-tabs.css
new file mode 100644
index 0000000..462f7c7
--- /dev/null
+++ b/resources/css/components/appearance-tabs.css
@@ -0,0 +1,35 @@
+/* 8. AppearanceTabs Component */
+.appearance-tabs {
+ @apply inline-flex gap-1 p-1;
+ border-radius: var(--radius);
+ background-color: var(--muted);
+
+ .appearance-tabs__tab {
+ @apply flex cursor-pointer items-center px-3.5 py-1.5 transition-colors;
+ border-radius: calc(var(--radius) - 2px);
+ color: var(--muted-foreground);
+
+ &:hover {
+ color: var(--foreground);
+ background-color: rgba(0, 0, 0, 0.05);
+ }
+
+ &.appearance-tabs__tab--active {
+ background-color: var(--card);
+ color: var(--card-foreground);
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+
+ &:hover {
+ background-color: var(--card);
+ }
+ }
+ }
+
+ .appearance-tabs__icon {
+ @apply -ml-1 h-4 w-4;
+ }
+
+ .appearance-tabs__label {
+ @apply ml-1.5 text-sm;
+ }
+}
diff --git a/resources/css/components/auth-layout.css b/resources/css/components/auth-layout.css
new file mode 100644
index 0000000..9a7e3d7
--- /dev/null
+++ b/resources/css/components/auth-layout.css
@@ -0,0 +1,46 @@
+/* 10. AuthSimpleLayout Component */
+.auth-layout {
+ @apply flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10;
+ background-color: var(--background);
+
+ .auth-layout__container {
+ @apply w-full max-w-sm;
+
+ .auth-layout__inner {
+ @apply flex flex-col gap-8;
+
+ .auth-layout__header {
+ @apply flex flex-col items-center gap-4;
+
+ .auth-layout__logo-link {
+ @apply flex flex-col items-center gap-2 font-medium;
+
+ .auth-layout__logo-box {
+ @apply mb-1 flex h-9 w-9 items-center justify-center;
+ border-radius: var(--radius);
+ background-color: var(--sidebar-primary);
+
+ .auth-layout__logo {
+ @apply size-9 fill-current;
+ color: var(--sidebar-primary-foreground);
+ }
+ }
+ }
+
+ .auth-layout__title-box {
+ @apply space-y-2 text-center;
+
+ .auth-layout__title {
+ @apply text-xl font-medium;
+ color: var(--foreground);
+ }
+
+ .auth-layout__description {
+ @apply text-center text-sm;
+ color: var(--muted-foreground);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/resources/css/components/chat.css b/resources/css/components/chat.css
new file mode 100644
index 0000000..a383573
--- /dev/null
+++ b/resources/css/components/chat.css
@@ -0,0 +1,210 @@
+/* 11. Chat Component */
+.c-chat {
+ @apply mt-8;
+
+ .c-chat__title {
+ @apply text-lg font-medium;
+ color: var(--foreground);
+ }
+
+ .c-chat__list {
+ @apply mt-4 flex flex-col gap-3;
+
+ .c-chat__message {
+ @apply overflow-hidden p-4 shadow-sm sm:rounded-lg;
+ border: 1px solid var(--border);
+
+ &.c-chat__message--system {
+ @apply w-full self-center border-0 bg-transparent p-0 shadow-none;
+ }
+
+ &.c-chat__message--own {
+ @apply self-end rounded-br-none text-right;
+ max-width: 80%;
+ background-color: var(--primary);
+ border-color: var(--primary);
+
+ .c-chat__message-header {
+ @apply flex-row-reverse;
+ }
+ .c-chat__message-author {
+ color: var(--primary-foreground);
+ }
+ .c-chat__message-time {
+ color: var(--primary-foreground);
+ opacity: 0.7;
+ }
+ .c-chat__message-text {
+ color: var(--primary-foreground);
+ }
+ }
+
+ &.c-chat__message--other {
+ @apply self-start rounded-bl-none;
+ max-width: 80%;
+ background-color: var(--muted);
+ border-color: var(--border);
+
+ .c-chat__message-author {
+ color: var(--foreground);
+ }
+ .c-chat__message-time {
+ color: var(--muted-foreground);
+ }
+ .c-chat__message-text {
+ color: var(--foreground);
+ }
+ }
+
+ .c-chat__system-inner {
+ @apply flex items-center gap-2 px-3 py-1.5 text-xs;
+ background-color: var(--muted);
+ border: 1px solid var(--border);
+ border-radius: calc(var(--radius) - 2px);
+ color: var(--muted-foreground);
+
+ .c-chat__system-icon {
+ @apply size-3.5 shrink-0;
+ color: var(--muted-foreground);
+ }
+
+ .c-chat__system-text {
+ @apply flex-1 font-medium;
+ }
+
+ .c-chat__system-time {
+ @apply shrink-0 text-[10px];
+ color: var(--muted-foreground);
+ }
+ }
+
+ .c-chat__message-header {
+ @apply flex justify-between;
+
+ .c-chat__message-author {
+ @apply font-semibold;
+ color: var(--foreground);
+ }
+
+ .c-chat__message-time {
+ @apply text-xs;
+ color: var(--muted-foreground);
+ }
+ }
+
+ .c-chat__message-text {
+ @apply mt-2 text-sm;
+ color: var(--foreground);
+ }
+
+ .c-chat__message-media {
+ @apply mt-3 flex flex-wrap gap-2;
+
+ .c-chat__media-item {
+ @apply relative max-w-[240px] overflow-hidden bg-black;
+ border-radius: calc(var(--radius) - 2px);
+ border: 1px solid var(--border);
+
+ .c-chat__image {
+ @apply h-auto max-h-[180px] w-full object-cover;
+ }
+
+ .c-chat__video {
+ @apply h-auto max-h-[180px] w-full;
+ }
+
+ .c-chat__play-overlay {
+ @apply absolute inset-0 flex items-center justify-center bg-black/40 text-2xl font-bold text-white;
+ }
+ }
+ }
+ }
+ }
+
+ .c-chat__empty {
+ color: var(--muted-foreground);
+ }
+
+ .c-chat__form {
+ @apply mt-6 space-y-6;
+
+ .c-chat__form-group {
+ @apply space-y-2;
+
+ .c-chat__label {
+ @apply block text-sm font-medium;
+ color: var(--foreground);
+ }
+
+ .c-chat__textarea {
+ @apply mt-1 block w-full shadow-sm;
+ border-radius: var(--radius);
+ border: 1px solid var(--border);
+ background-color: var(--background);
+ color: var(--foreground);
+ }
+
+ .c-chat__error {
+ @apply text-sm;
+ color: var(--destructive);
+ }
+
+ .c-chat__attachment-container {
+ @apply mt-2 flex items-center;
+
+ .c-chat__attach-btn {
+ @apply inline-flex cursor-pointer items-center gap-1.5 text-xs transition-colors;
+ color: var(--muted-foreground);
+
+ &:hover {
+ color: var(--foreground);
+ }
+
+ .c-chat__attach-icon {
+ @apply size-3.5;
+ }
+ }
+ }
+
+ .c-chat__preview-list {
+ @apply mt-2 flex flex-wrap gap-2;
+
+ .c-chat__preview-item {
+ @apply relative inline-flex items-center gap-2 p-1.5 pr-8 text-xs;
+ border-radius: calc(var(--radius) - 2px);
+ border: 1px solid var(--border);
+ background-color: var(--muted);
+
+ .c-chat__preview-name {
+ @apply max-w-[150px] truncate;
+ color: var(--foreground);
+ }
+
+ .c-chat__preview-remove {
+ @apply absolute top-1.5 right-1.5 cursor-pointer text-[10px];
+ color: var(--muted-foreground);
+
+ &:hover {
+ color: var(--destructive);
+ }
+ }
+ }
+ }
+ }
+
+ .c-chat__submit-box {
+ @apply flex items-center gap-4;
+
+ .c-chat__button {
+ @apply inline-flex items-center border border-transparent px-4 py-2 text-xs font-semibold tracking-widest uppercase transition duration-150 ease-in-out focus:ring-2 focus:outline-none;
+ border-radius: var(--radius);
+ background-color: var(--primary);
+ color: var(--primary-foreground);
+
+ &:hover {
+ opacity: 0.9;
+ }
+ }
+ }
+ }
+}
diff --git a/resources/css/components/heading.css b/resources/css/components/heading.css
new file mode 100644
index 0000000..ca58613
--- /dev/null
+++ b/resources/css/components/heading.css
@@ -0,0 +1,22 @@
+/* 1. Heading Component */
+.c-heading {
+ @apply mb-8 space-y-0.5;
+
+ &.c-heading--small {
+ @apply mb-0 space-y-0;
+
+ .c-heading__title {
+ @apply mb-0.5 text-base font-medium tracking-normal;
+ }
+ }
+
+ .c-heading__title {
+ @apply text-xl font-semibold tracking-tight;
+ color: var(--foreground);
+ }
+
+ .c-heading__description {
+ @apply text-sm;
+ color: var(--muted-foreground);
+ }
+}
diff --git a/resources/css/components/input-error.css b/resources/css/components/input-error.css
new file mode 100644
index 0000000..fbae00d
--- /dev/null
+++ b/resources/css/components/input-error.css
@@ -0,0 +1,5 @@
+/* 2. InputError Component */
+.c-input-error__message {
+ @apply text-sm;
+ color: var(--destructive);
+}
diff --git a/resources/css/components/lightbox.css b/resources/css/components/lightbox.css
new file mode 100644
index 0000000..e1cee73
--- /dev/null
+++ b/resources/css/components/lightbox.css
@@ -0,0 +1,25 @@
+/* 12. Reusable Lightbox Component */
+.c-lightbox {
+ @apply fixed inset-0 z-50 flex items-center justify-center p-4;
+ background-color: rgba(0, 0, 0, 0.95);
+
+ .c-lightbox__close {
+ @apply absolute top-6 right-6 cursor-pointer text-3xl text-white transition-colors duration-200;
+
+ &:hover {
+ color: var(--destructive);
+ }
+ }
+
+ .c-lightbox__content {
+ @apply max-h-full max-w-full;
+
+ .c-lightbox__image {
+ @apply max-h-[90vh] max-w-full rounded object-contain shadow-lg;
+ }
+
+ .c-lightbox__video {
+ @apply max-h-[90vh] max-w-full rounded shadow-lg;
+ }
+ }
+}
diff --git a/resources/css/components/password-input.css b/resources/css/components/password-input.css
new file mode 100644
index 0000000..fa3cc35
--- /dev/null
+++ b/resources/css/components/password-input.css
@@ -0,0 +1,21 @@
+/* 9. PasswordInput Component */
+.password-input {
+ @apply relative;
+
+ .password-input__field {
+ @apply pr-10;
+ }
+
+ .password-input__toggle {
+ @apply absolute inset-y-0 right-0 flex items-center rounded-r-md px-3 focus-visible:outline-none;
+ color: var(--muted-foreground);
+
+ &:hover {
+ color: var(--foreground);
+ }
+ }
+
+ .password-input__icon {
+ @apply size-4;
+ }
+}
diff --git a/resources/css/components/sidebar-header.css b/resources/css/components/sidebar-header.css
new file mode 100644
index 0000000..2d07dcc
--- /dev/null
+++ b/resources/css/components/sidebar-header.css
@@ -0,0 +1,14 @@
+/* 6. AppSidebarHeader Component */
+.sidebar-header {
+ @apply flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear md:px-4;
+ border-bottom: 1px solid var(--sidebar-border);
+ background-color: var(--sidebar-background);
+}
+
+.sidebar-header__inner {
+ @apply flex items-center gap-2;
+}
+
+.group-has-data-[collapsible='icon']/sidebar-wrapper:h-12 .sidebar-header {
+ @apply h-12;
+}
diff --git a/resources/css/components/text-link.css b/resources/css/components/text-link.css
new file mode 100644
index 0000000..415d2da
--- /dev/null
+++ b/resources/css/components/text-link.css
@@ -0,0 +1,10 @@
+/* 3. TextLink Component */
+.c-text-link {
+ @apply underline underline-offset-4 transition-colors duration-300 ease-out;
+ color: var(--foreground);
+ text-decoration-color: var(--border);
+
+ &:hover {
+ text-decoration-color: var(--foreground);
+ }
+}
diff --git a/resources/css/components/user-info.css b/resources/css/components/user-info.css
new file mode 100644
index 0000000..f382ef4
--- /dev/null
+++ b/resources/css/components/user-info.css
@@ -0,0 +1,19 @@
+/* 5. UserInfo Component */
+.user-info__avatar {
+ @apply h-8 w-8 overflow-hidden;
+ border-radius: var(--radius);
+}
+
+.user-info__details {
+ @apply grid flex-1 text-left text-sm leading-tight;
+
+ .user-info__name {
+ @apply truncate font-medium;
+ color: var(--foreground);
+ }
+
+ .user-info__email {
+ @apply truncate text-xs;
+ color: var(--muted-foreground);
+ }
+}
diff --git a/resources/js/app.ts b/resources/js/app.ts
index 46d341c..45e170b 100644
--- a/resources/js/app.ts
+++ b/resources/js/app.ts
@@ -19,7 +19,7 @@ configureEcho({
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
-if (window) {
+if (typeof window !== 'undefined') {
(window as any).echoConfigured = true;
}
diff --git a/resources/js/components/AppContent.vue b/resources/js/components/AppContent.vue
index bc99919..82f1a5e 100644
--- a/resources/js/components/AppContent.vue
+++ b/resources/js/components/AppContent.vue
@@ -22,11 +22,3 @@ const className = computed(() => props.class);
-
-
diff --git a/resources/js/components/AppLogo.vue b/resources/js/components/AppLogo.vue
index 313eff5..b6f7296 100644
--- a/resources/js/components/AppLogo.vue
+++ b/resources/js/components/AppLogo.vue
@@ -10,23 +10,3 @@ import AppLogoIcon from '@/components/AppLogoIcon.vue';
Laravel Starter Kit
-
-
diff --git a/resources/js/components/AppSidebarHeader.vue b/resources/js/components/AppSidebarHeader.vue
index 1af9362..0973fef 100644
--- a/resources/js/components/AppSidebarHeader.vue
+++ b/resources/js/components/AppSidebarHeader.vue
@@ -23,15 +23,3 @@ withDefaults(
-
-
diff --git a/resources/js/components/AppearanceTabs.vue b/resources/js/components/AppearanceTabs.vue
index 8663291..af2a1df 100644
--- a/resources/js/components/AppearanceTabs.vue
+++ b/resources/js/components/AppearanceTabs.vue
@@ -27,27 +27,3 @@ const tabs = [
-
-
diff --git a/resources/js/components/Chat.vue b/resources/js/components/Chat.vue
index 9bea09b..2cb75a3 100644
--- a/resources/js/components/Chat.vue
+++ b/resources/js/components/Chat.vue
@@ -1,6 +1,6 @@