admin/styles/globals.css
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

840 lines
23 KiB
CSS

/* 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 px-4 py-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;
}