/* app/globals.css */ @import 'tailwindcss'; @import '@heroui/styles'; /*@custom-variant dark (&:is(.dark *));*/ @keyframes slide-down { from { opacity: 0; transform: translateY(-100%); } to { opacity: 1; transform: translateY(0); } } /* * Consumer bottom sheets use HeroUI's `data-entering` / `data-exiting` states. * Keeping this on the Modal container lets React Aria preserve the dialog while * the exit animation completes and keeps backdrop clicks managed by HeroUI. */ @keyframes consumer-modal-slide-in { from { transform: translateY(100%); } to { transform: translateY(0); } } @keyframes consumer-modal-slide-out { from { transform: translateY(0); } to { transform: translateY(100%); } } @keyframes consumer-modal-backdrop-in { from { opacity: 0; } to { opacity: 1; } } @keyframes consumer-modal-backdrop-out { from { opacity: 1; } to { opacity: 0; } } /* * HeroUI sizes the backdrop/container to `--visual-viewport-height`. On mobile, * focusing an input can shrink/scroll the visual viewport while the layout * viewport stays tall — leaving a hole where page content shows through the * sheet. Keep the backdrop pinned to the dynamic viewport (`dvh`, not `lvh`: * large viewport is taller than the visible area when browser chrome is shown * and clips the bottom sheet) and lift the sheet with `--keyboard-inset` * instead of relying on the visual-viewport height. */ .consumer-modal-backdrop { top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; width: 100% !important; /* Override `h-(--visual-viewport-height)` so top/bottom stretch covers the layout. */ height: auto !important; min-height: 100%; min-height: 100dvh; max-height: 100dvh; align-items: stretch !important; overscroll-behavior: none; } .consumer-modal-container { /* Fill the dynamic-viewport backdrop; do not track `--visual-viewport-height`. */ height: 100% !important; min-height: 100%; min-height: 100dvh; max-height: 100dvh; flex: 1 1 auto; /* Keyboard open: inset lifts the sheet; keyboard closed: inset is 0. */ padding-bottom: var(--keyboard-inset, 0px); box-sizing: border-box; } .consumer-modal-container[data-entering='true'] { animation: consumer-modal-slide-in 300ms var(--ease-out-fluid, cubic-bezier(0.32, 0.72, 0, 1)) both; will-change: transform; } .consumer-modal-container[data-exiting='true'] { animation: consumer-modal-slide-out 220ms var(--ease-out-fluid, cubic-bezier(0.32, 0.72, 0, 1)) both; will-change: transform; } .consumer-modal-backdrop[data-entering='true'] { animation: consumer-modal-backdrop-in 240ms ease-out both; will-change: opacity; } .consumer-modal-backdrop[data-exiting='true'] { animation: consumer-modal-backdrop-out 220ms ease-out both; will-change: opacity; } @media (prefers-reduced-motion: reduce) { .consumer-modal-backdrop[data-entering='true'], .consumer-modal-backdrop[data-exiting='true'], .consumer-modal-container[data-entering='true'], .consumer-modal-container[data-exiting='true'] { animation: none; } } /* HeroUI v3 semantic tokens: preserve the brand orange with AA contrast. */ :root, .light, .default, [data-theme='light'], [data-theme='default'] { --accent: var(--color-primary); --accent-foreground: var(--color-secondary-100); --focus: var(--accent); } /* * Admin theme contract * * Keep HeroUI's semantic variables scoped to the admin shell. This lets * compound components (fields, overlays, tables, toasts) share the same * brand, focus, border, radius, and elevation language without leaking * admin presentation into the consumer application. */ [data-theme='ghabilee-admin'] { color-scheme: light; --background: var(--color-secondary-50); --foreground: var(--color-text); --background-inverse: var(--color-tertiary); --surface: var(--color-surface); --surface-foreground: var(--foreground); --surface-secondary: var(--color-secondary-50); --surface-secondary-foreground: var(--foreground); --surface-tertiary: var(--color-divider); --surface-tertiary-foreground: var(--foreground); --overlay: var(--color-surface); --overlay-foreground: var(--foreground); --muted: var(--color-secondary-30); --default: var(--color-divider); --default-foreground: var(--color-secondary-20); --accent: var(--color-primary); --accent-foreground: var(--color-secondary-100); --success: var(--color-fifth); --success-foreground: var(--color-tertiary); --warning: var(--color-warning); --warning-foreground: var(--color-tertiary); --danger: var(--color-fourth); --danger-foreground: var(--color-secondary-100); --border: var(--color-divider); --separator: var(--color-divider); --focus: var(--accent); --link: var(--color-tertiary); --field-background: var(--color-surface); --field-foreground: var(--foreground); --field-placeholder: var(--color-secondary-30); --field-border: var(--color-divider); --field-border-width: 1px; --field-radius: 0.75rem; --radius: 0.75rem; --surface-shadow: 0 1px 2px rgb(51 58 68 / 3%), 0 10px 30px rgb(51 58 68 / 4%); --overlay-shadow: 0 16px 48px rgb(51 58 68 / 14%); --field-shadow: 0 1px 2px rgb(51 58 68 / 4%); --scrollbar-thumb: rgb(128 128 128 / 55%); --scrollbar-track: transparent; --scrollbar-width: thin; --admin-page-bg: var(--background); --admin-surface: var(--surface); --admin-border: var(--separator); --admin-radius: 1.125rem; --admin-shadow: var(--surface-shadow); } @theme { --font-sans: var(--font-pinar), ui-sans-serif, system-ui, sans-serif; --font-mono: ui-monospace, 'SFMono-Regular', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; --font-family-sans: var(--font-sans); --color-text: var(--color-secondary); --color-text-main: var(--color-text); --color-surface: var(--color-secondary-100); --color-divider: var(--color-secondary-40); --color-accent-light: var(--color-primary-50); --color-primary-hover: var(--color-primary-700); --color-primary: var(--color-primary-600); --color-primary-25: #fefaf6; --color-primary-50: #fcf5ed; --color-primary-100: #f9eadb; --color-primary-200: #f4d6b8; --color-primary-300: #eec194; --color-primary-400: #f1af6d; --color-primary-500: #f39d46; --color-primary-600: #f68b1f; --color-primary-700: #dd8022; --color-primary-800: #c47625; --color-primary-900: #ab6b28; --color-primary-1000: #74481c; /* Secondary: neutral / gray — base #333A44 */ --color-secondary: var(--color-secondary-10); --color-secondary-10: #333a44; --color-secondary-20: #585858; --color-secondary-30: #808080; --color-secondary-40: #e2e2e2; --color-secondary-45: #ebeced; --color-secondary-50: #f5f5f5; --color-secondary-100: #ffffff; /* Numeric aliases keep existing secondary-* utilities available. */ --color-secondary-200: var(--color-secondary-45); --color-secondary-300: var(--color-secondary-40); --color-secondary-400: var(--color-secondary-30); --color-secondary-500: var(--color-secondary-30); --color-secondary-600: var(--color-secondary-20); --color-secondary-700: var(--color-secondary-20); --color-secondary-800: var(--color-secondary-10); --color-secondary-900: var(--color-secondary-10); /* Tertiary: blue — base #1C2473 */ --color-tertiary: var(--color-tertiary-900); --color-tertiary-50: #eeeef4; --color-tertiary-100: #ddddea; --color-tertiary-200: #bbbbd4; --color-tertiary-300: #9898bf; --color-tertiary-400: #7676a9; --color-tertiary-500: #545494; --color-tertiary-600: #3c4089; --color-tertiary-700: #34377a; --color-tertiary-800: #282e76; --color-tertiary-900: #1c2473; /* Fourth: red — base #ED1C24 */ --color-fourth: var(--color-fourth-500); --color-fourth-100: #f8aa98; --color-fourth-300: #f37160; --color-fourth-500: #ed1c24; --color-fourth-700: #c8161d; --color-fourth-900: #9a061a; /* Fifth: green — base #007839 */ --color-fifth: var(--color-fifth-900); --color-fifth-50: var(--color-fifth-100); --color-fifth-100: #b9e0d2; --color-fifth-200: var(--color-fifth-100); --color-fifth-500: #38a77f; --color-fifth-600: var(--color-fifth-500); --color-fifth-700: var(--color-fifth-500); --color-fifth-900: #007839; --color-warning: #ffcc00; --color-warning-dark: #b88d0a; /* Consumer theme aliases → text-consumer-text, bg-consumer-surface, … */ --color-consumer-canvas: var(--consumer-canvas); --color-consumer-surface: var(--consumer-surface); --color-consumer-surface-muted: var(--consumer-surface-muted); --color-consumer-event-card: var(--consumer-event-card-bg); --color-consumer-text: var(--consumer-text); --color-consumer-text-muted: var(--consumer-text-muted); --color-consumer-border: var(--consumer-border); --color-consumer-focus: var(--consumer-focus); --radius-consumer-control: var(--consumer-radius-control); --radius-consumer-card: var(--consumer-radius-card); --radius-consumer-modal: var(--consumer-radius-modal); --radius-consumer-surface: var(--consumer-radius-surface); --spacing-consumer-bottom-nav: var(--consumer-bottom-nav-height); --container-center: true; --container-padding: 1rem; --container-padding-sm: 2rem; --container-padding-lg: 3rem; --container-padding-xl: 4rem; --container-padding-2xl: 5rem; --container-sm: 100%; --container-md: 100%; --container-lg: 1024px; --container-xl: 1280px; --container-2xl: 1440px; } @utility type-label { font-size: var(--text-sm); font-weight: 500; line-height: 1.4; letter-spacing: normal; } @utility text-2xs { font-size: 10px; } @utility type-display { font-weight: 800; line-height: 1.35; letter-spacing: normal; } @layer base { :root { --admin-page-bg: var(--color-secondary-50); --admin-surface: var(--color-surface); --admin-border: var(--color-divider); --admin-radius: 18px; --admin-shadow: 0 1px 2px rgb(51 58 68 / 3%), 0 10px 30px rgb(51 58 68 / 4%); --consumer-canvas: var(--color-secondary-45); --consumer-surface: var(--color-surface); --consumer-surface-muted: var(--color-secondary-50); --consumer-event-card-bg: var(--color-secondary-50); --consumer-text: var(--color-text); --consumer-text-muted: var(--color-secondary-30); --consumer-border: var(--color-divider); --consumer-focus: var(--color-primary); --consumer-radius-control: 12px; --consumer-radius-card: 16px; --consumer-radius-modal: 22px; --consumer-radius-surface: 22px; /* * Consumer surfaces fill the available viewport on phones. The desktop * breakpoint below turns the same token into the centred device preview, * which keeps pages, sheets, bottom navigation, and sticky actions aligned. */ --consumer-app-max-width: 100%; --consumer-bottom-nav-height: 80px; --keyboard-inset: 0px; } html { font-family: var(--font-sans); } @media (min-width: 768px) { :root { --consumer-app-max-width: 390px; } } body { --pinar-dsty: 0; --pinar-kshd: 100; color: var(--color-tertiary); font-family: var(--font-sans); font-variation-settings: 'DSTY' var(--pinar-dsty), 'KSHD' var(--pinar-kshd); direction: rtl; background: var(--color-primary-25); overscroll-behavior-y: none; } input, textarea, select, button { font: inherit; text-align: right; } input[type='email'], input[type='url'], input[type='number'] { direction: ltr; text-align: right; } /* tel: LTR digits; placeholder on the right, typed value from the left */ input[type='tel'] { direction: ltr !important; text-align: left !important; } input[type='tel']:placeholder-shown { text-align: right !important; } [dir='ltr']:not(input):not(textarea) { direction: ltr; text-align: left; } tr[aria-hidden='true'] { display: none !important; } [data-selected='true']::before, [data-selected='true']::after { opacity: 0 !important; background-color: transparent !important; } canvas { font-family: var(--font-sans); } button[role='tab']:not([data-selected]), button[role='tab']:not([data-selected]) * { color: var(--color-primary-600) !important; } button[role='tab'][data-selected], button[role='tab'][data-selected] * { color: var(--color-primary-700) !important; } } @layer components { .button--xl { height: 54px; padding: 5px 12px; font-size: 14px; font-weight: 500; } .button--xl svg:not([data-slot='spinner'] svg, [data-slot='link-icon'] svg) { width: 16px; height: 16px; } .consumer-wheel-picker > div:last-child { display: none; } .admin-surface { border: 1px solid var(--admin-border); border-radius: var(--admin-radius); background: var(--admin-surface); box-shadow: var(--admin-shadow); } .admin-page-container { @apply mx-auto w-full max-w-[1600px] p-3 sm:p-5 lg:p-6 xl:p-7; } .admin-page-header { box-shadow: 0 1px 0 rgb(51 58 68 / 2%); } .admin-sidebar-scroll { scrollbar-color: rgb(226 226 226 / 70%) transparent; scrollbar-width: thin; } .admin-sidebar-scroll::-webkit-scrollbar { width: 4px; } .admin-sidebar-scroll::-webkit-scrollbar-thumb { border-radius: 999px; background: rgb(226 226 226 / 70%); } .consumer-mobile-viewport, .consumer-app { position: relative; width: 100%; max-width: var(--consumer-app-max-width); margin-inline: auto; } .consumer-app { --consumer-chrome-bottom: var(--keyboard-inset, 0px); --consumer-sticky-pad-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); background: var(--consumer-canvas); color: var(--consumer-text); overscroll-behavior-y: contain; } .consumer-content { overscroll-behavior-y: contain; /* Tab shell remounts tall ↔ short content; anchoring would fight restore-to-top. */ overflow-anchor: none; } /* * Consumer scrollbars are hidden by default (scroll still works). * Opt in with `.consumer-scrollbar` on the scroll container when a visible * bar is needed. */ .consumer-app, .consumer-app *, .consumer-mobile-viewport, .consumer-mobile-viewport *, .consumer-overlay, .consumer-overlay * { scrollbar-width: none; -ms-overflow-style: none; } .consumer-app::-webkit-scrollbar, .consumer-app *::-webkit-scrollbar, .consumer-mobile-viewport::-webkit-scrollbar, .consumer-mobile-viewport *::-webkit-scrollbar, .consumer-overlay::-webkit-scrollbar, .consumer-overlay *::-webkit-scrollbar { display: none; width: 0; height: 0; } .consumer-app .consumer-scrollbar, .consumer-mobile-viewport .consumer-scrollbar, .consumer-overlay .consumer-scrollbar { scrollbar-width: thin; scrollbar-color: rgb(128 128 128 / 40%) transparent; -ms-overflow-style: auto; } .consumer-app .consumer-scrollbar::-webkit-scrollbar, .consumer-mobile-viewport .consumer-scrollbar::-webkit-scrollbar, .consumer-overlay .consumer-scrollbar::-webkit-scrollbar { display: block; width: 4px; height: 4px; } .consumer-app .consumer-scrollbar::-webkit-scrollbar-thumb, .consumer-mobile-viewport .consumer-scrollbar::-webkit-scrollbar-thumb, .consumer-overlay .consumer-scrollbar::-webkit-scrollbar-thumb { border-radius: 999px; background: rgb(128 128 128 / 40%); } /* * HeroUI modal dialog/body use overflow clip. Full-width rounded fields sit flush * on the content edge; extend the clip margin so 1px borders are not shaved at corners. */ .consumer-overlay[data-slot='modal-dialog'], .consumer-overlay [data-slot='modal-body'] { overflow-clip-margin: 1px; } .consumer-app-with-bottom-nav { --consumer-chrome-bottom: calc(var(--consumer-bottom-nav-height) + env(safe-area-inset-bottom, 0px) + var(--keyboard-inset, 0px)); --consumer-sticky-pad-bottom: 12px; } .consumer-page-container { @apply mx-auto w-full px-2; max-width: var(--consumer-app-max-width); } .consumer-content-with-bottom-nav { /* background-color: white; */ padding-bottom: calc(var(--consumer-bottom-nav-height) + env(safe-area-inset-bottom, 0px) + var(--keyboard-inset, 0px)); } .consumer-bottom-nav { inset-inline: 0; bottom: var(--keyboard-inset, 0px); width: 100%; max-width: var(--consumer-app-max-width); margin-inline: auto; padding-bottom: env(safe-area-inset-bottom, 0px); } /* * شیشهٔ frosted مثل منوی macOS. blur روی خودِ nav (همان stacking context) است؛ * این کلاس فقط ته‌رنگ نیمه‌شفاف و لبهٔ براق را می‌سازد. */ .consumer-bottom-nav-glass { border: 1px solid color-mix(in srgb, white 70%, var(--consumer-border)); border-radius: inherit; background: linear-gradient(180deg, rgb(255 255 255 / 58%) 0%, rgb(255 255 255 / 22%) 100%), color-mix(in srgb, var(--consumer-surface) 62%, transparent); box-shadow: 0 10px 32px rgb(51 58 68 / 14%), 0 1px 0 rgb(255 255 255 / 78%) inset; } .consumer-form-action-bar { inset-inline: 0; bottom: var(--keyboard-inset, 0px); width: 100%; max-width: var(--consumer-app-max-width); margin-inline: auto; padding-bottom: env(safe-area-inset-bottom, 0px); } .consumer-chat-thread { inset-inline: 0; width: 100%; max-width: var(--consumer-app-max-width); margin-inline: auto; } .consumer-sticky-action { @apply sticky z-20 -mx-4 mt-1 border-t px-4 pt-3; bottom: var(--consumer-chrome-bottom); /* Keeps bottom actions above the iOS home indicator when no BottomNav exists. */ padding-bottom: var(--consumer-sticky-pad-bottom); border-color: var(--consumer-border); background: color-mix(in srgb, var(--consumer-surface) 94%, transparent); backdrop-filter: blur(16px); } /* Fixed booking/reserve bar — same viewport slot as BottomNav on event detail. */ .consumer-booking-action-bar { @apply fixed inset-x-0 z-50 p-4; bottom: calc(var(--keyboard-inset, 0px)); width: 100%; max-width: var(--consumer-app-max-width); margin-inline: auto; padding-bottom: max(15px, env(safe-area-inset-bottom, 0px)); background: transparent; backdrop-filter: blur(16px); } .consumer-with-booking-action { padding-bottom: calc(var(--consumer-bottom-nav-height) + env(safe-area-inset-bottom, 0px) + var(--keyboard-inset, 0px) + 20px); } .consumer-chat-composer { padding-bottom: max(var(--consumer-sticky-pad-bottom), 12px); border-top: 0; background: color-mix(in srgb, var(--consumer-surface) 78%, transparent); backdrop-filter: blur(16px); } .consumer-chat-composer-control { min-height: 60px; } .consumer-chat-composer-input { height: 44px !important; min-height: 44px !important; border: 0 !important; border-radius: 14px !important; background: transparent !important; } .consumer-chat-composer-input:hover, .consumer-chat-composer-input:focus-within, .consumer-chat-composer-input[data-focused='true'] { border: 0 !important; background: var(--consumer-surface-muted) !important; } .consumer-chat-composer-send { height: 42px !important; width: 42px !important; min-height: 42px !important; min-width: 42px !important; border-radius: 14px !important; } .consumer-chat-composer-icon { height: 40px !important; width: 32px !important; min-height: 40px !important; min-width: 32px !important; padding-inline: 0 !important; color: var(--consumer-text-muted); } .consumer-chat-message-highlight .consumer-chat-message-bubble { animation: consumer-chat-message-flash 1.4s ease-out; } @keyframes consumer-chat-message-flash { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--consumer-primary, #f97316) 45%, transparent); } 35% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--consumer-primary, #f97316) 35%, transparent); } 100% { box-shadow: 0 0 0 0 transparent; } } .consumer-chat-reply-preview { border: 1px solid var(--consumer-border); border-radius: 16px; background: var(--consumer-surface); } .consumer-deep-header { border-bottom: 1px solid var(--consumer-border); background: color-mix(in srgb, var(--consumer-surface) 92%, transparent); } .consumer-surface { border-radius: var(--consumer-radius-surface); background: var(--consumer-surface); } .consumer-list-card { @apply m-2 p-4 transition-[border-color,background-color] hover:border-primary-300; border: 1px solid var(--consumer-border); border-radius: var(--consumer-radius-card); background: var(--consumer-surface); } .consumer-event-card { transition-property: border-color, transform; transition-duration: 300ms; } .consumer-event-card-image { transition: transform 500ms; } .consumer-empty-state { @apply mx-auto flex min-h-64 max-w-md flex-col items-center justify-center gap-3 px-6 py-12 text-center; background: color-mix(in srgb, var(--consumer-surface) 74%, transparent); } .consumer-focus-ring { outline: none; } .consumer-focus-ring:focus-visible { outline: 2px solid var(--consumer-focus); outline-offset: 2px; } .consumer-content > section > :first-child:not(header), .consumer-content > div > :first-child:not(header) { text-align: right; } .consumer-content input, .consumer-content textarea, .consumer-content button, .consumer-content [role='tab'] { text-align: right; } .consumer-content input[type='tel'] { text-align: left !important; } .consumer-content input[type='tel']:placeholder-shown { text-align: right !important; } } @media (hover: hover) and (pointer: fine) { .consumer-event-card:hover { transform: translateY(-4px); border-color: var(--color-primary-200); } .consumer-event-card:hover .consumer-event-card-image { transform: scale(1.02); } } @media (prefers-reduced-motion: reduce) { .consumer-app *, .consumer-app *::before, .consumer-app *::after { scroll-behavior: auto !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } @keyframes loading-slide { 0% { transform: translateX(220%); } 50% { transform: translateX(-20%); } 100% { transform: translateX(-220%); } } /* * ToastRegion can portal with dir=ltr when @react-aria/i18n is duplicated. * Force physical RTL packing so icon+title sit on the right: [empty][text][icon]. * z-index 70: above modal overlays (z-60) so toasts stay visible during and after * HeroUI view-transition enter/exit (top layer only applies while animating). */ [data-slot='toast-region'] { z-index: 70; } [data-slot='toast-region'], [data-slot='toast'] { direction: rtl !important; text-align: right !important; } [data-slot='toast'] { flex-direction: row !important; justify-content: right !important; } [data-slot='toast-content'] { align-items: flex-start !important; text-align: right !important; } [data-slot='toast-title'], [data-slot='toast-description'] { width: 100%; text-align: right !important; }