Files
marketing-app/client/src/index.css
T
fahed ce4d6025d7 feat: post composition redesign + budget allocation + brand identity (Rawaj)
Post Workflow:
- PostDetail full page (/posts/:id) replaces slide panel approach
- Post = 1 Caption Copy + 1 Body Copy + 1 Design + 0-1 Video
- copy_type field on Translations (caption/body)
- Composition endpoint returns rich data (content preview, languages, thumbnails)
- Stage auto-advances on translation/artefact changes (both link and unlink)
- "Translations" renamed to "Copy" in navigation
- GET /api/posts/:id, /api/translations/:id, /api/artefacts/:id routes added
- PostProduction: "New Post" creates → navigates to full page
- CampaignDetail: click post → navigates to full page
- Inline link picker (no modals) with search + rich item display
- PostComposition sub-components for caption, copy, designs, video, formats, readiness

Budget Allocation:
- Single source of truth: BudgetEntries (Campaign.budget deprecated)
- Budget mutex for race conditions
- Validation at all levels (main → campaign → track, expenses)
- CEO approval workflow: BudgetRequests table, public approval page
- Finance page: request budget UI, budget requests section
- Settings: CEO email field
- All emails branded with "Rawaj —" prefix

Brand Identity:
- Name: Rawaj (رواج) — trending/virality
- Deep teal palette (#0d9488), forest-tinted dark mode
- DM Sans font, custom SVG logo
- Consistent across login, sidebar, emails, public pages

Approval Workflow:
- Single reviewer per artefact (not multi-select)
- Reviewer redirect on public review page
- Server blocks submit-review without reviewer
- Review URLs use APP_URL (not server URL)

UI/UX:
- Scroll clipping fix: Modal, TabbedModal, SlidePanel restructured
  to avoid overflow-y-auto clipping native select dropdowns
- section-card overflow-hidden → overflow-clip
- All page titles via Header.jsx (removed duplicate h1s)
- CampaignDetail redesigned: prominent budget card, compact team

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:02:29 +03:00

706 lines
20 KiB
CSS

@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: 'DM Sans', 'IBM Plex Sans Arabic', system-ui, -apple-system, sans-serif;
--color-sidebar: #0a1f1c;
--color-sidebar-hover: #123b35;
--color-sidebar-active: #061411;
--color-brand-primary: #0d9488;
--color-brand-primary-light: #14b8a6;
--color-brand-secondary: #db2777;
--color-brand-tertiary: #f59e0b;
--color-brand-quaternary: #0d9488;
--color-surface: #ffffff;
--color-surface-secondary: #f9fafb;
--color-surface-tertiary: #f3f4f6;
--color-border: #e5e7eb;
--color-border-light: #f3f4f6;
--color-text-primary: #0f172a;
--color-text-secondary: #475569;
--color-text-tertiary: #94a3b8;
--color-text-on-dark: #f8fafc;
--color-text-on-dark-muted: #94a3b8;
--color-status-draft: #9ca3af;
--color-status-in-review: #f59e0b;
--color-status-approved: #3b82f6;
--color-status-scheduled: #8b5cf6;
--color-status-published: #059669;
--color-status-rejected: #dc2626;
--color-status-todo: #9ca3af;
--color-status-in-progress: #3b82f6;
--color-status-done: #059669;
--color-status-active: #059669;
--color-status-paused: #f59e0b;
--color-status-completed: #3b82f6;
--color-status-cancelled: #dc2626;
}
/* ═══════════════════════════════════════════════
DARK MODE — Forest teal tinted surfaces
═══════════════════════════════════════════════ */
.dark {
/* Layered depth: deep forest → surface → elevated */
--color-surface: #0f1a18;
--color-surface-secondary: #162220;
--color-surface-tertiary: #1e2e2b;
--color-border: rgba(255, 255, 255, 0.08);
--color-border-light: rgba(255, 255, 255, 0.04);
/* Text — warm neutrals, teal-tinted */
--color-text-primary: #e8f0ee;
--color-text-secondary: #9db5b0;
--color-text-tertiary: #637e78;
/* Sidebar */
--color-sidebar: #0a1412;
--color-sidebar-hover: #0f1a18;
--color-sidebar-active: #060e0c;
/* Brand — brighter on dark */
--color-brand-primary: #14b8a6;
--color-brand-primary-light: #2dd4bf;
color-scheme: dark;
background-color: #0f1a18;
color: #e8f0ee;
}
/* ─── Ambient background glow ────────────────── */
.dark .bg-mesh {
background-color: #0f1a18 !important;
background-image: none !important;
}
.dark .bg-mesh::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 70% 50% at 20% 50%, rgba(13, 148, 136, 0.04) 0%, transparent 60%),
radial-gradient(ellipse 50% 40% at 80% 30%, rgba(20, 184, 166, 0.025) 0%, transparent 60%);
pointer-events: none;
z-index: 0;
}
/* ─── Every white surface → elevated dark ────── */
.dark .bg-white,
.dark .bg-\[\#fff\],
.dark .bg-\[\#ffffff\] {
background-color: #1a2a28 !important;
}
.dark .bg-gray-50 { background-color: #0f1a18 !important; }
.dark .bg-gray-100 { background-color: #162220 !important; }
.dark .bg-gray-200 { background-color: #1e2e2b !important; }
/* ─── Borders ────────────────────────────────── */
.dark .border-gray-100,
.dark .border-gray-200,
.dark .border-gray-300 { border-color: rgba(255, 255, 255, 0.08) !important; }
.dark .divide-gray-100 > :not(:first-child),
.dark .divide-gray-200 > :not(:first-child),
.dark .divide-border-light > :not(:first-child) { border-color: rgba(255, 255, 255, 0.05) !important; }
/* ─── Text ───────────────────────────────────── */
.dark .text-gray-900 { color: #e8f0ee !important; }
.dark .text-gray-800 { color: #d0ddd9 !important; }
.dark .text-gray-700 { color: #b5cac5 !important; }
.dark .text-gray-600 { color: #9db5b0 !important; }
.dark .text-gray-500 { color: #7e9a94 !important; }
.dark .text-gray-400 { color: #637e78 !important; }
/* ─── Status badges — translucent glass ──────── */
.dark .bg-emerald-100, .dark .bg-emerald-50 { background-color: rgba(74, 222, 128, 0.12) !important; }
.dark .bg-blue-100, .dark .bg-blue-50 { background-color: rgba(96, 165, 250, 0.12) !important; }
.dark .bg-amber-100, .dark .bg-amber-50 { background-color: rgba(251, 191, 36, 0.12) !important; }
.dark .bg-red-100, .dark .bg-red-50 { background-color: rgba(251, 113, 133, 0.12) !important; }
.dark .bg-purple-100, .dark .bg-purple-50 { background-color: rgba(167, 139, 250, 0.12) !important; }
.dark .bg-orange-100 { background-color: rgba(251, 146, 60, 0.12) !important; }
.dark .bg-indigo-100, .dark .bg-indigo-50 { background-color: rgba(129, 140, 248, 0.12) !important; }
.dark .bg-pink-100 { background-color: rgba(244, 114, 182, 0.12) !important; }
.dark .bg-cyan-100 { background-color: rgba(34, 211, 238, 0.12) !important; }
.dark .bg-teal-100 { background-color: rgba(45, 212, 191, 0.12) !important; }
/* Status text colors — brighter on dark */
.dark .text-emerald-700 { color: #4ade80 !important; }
.dark .text-emerald-600 { color: #4ade80 !important; }
.dark .text-blue-700 { color: #60a5fa !important; }
.dark .text-amber-700 { color: #fbbf24 !important; }
.dark .text-amber-900 { color: #fbbf24 !important; }
.dark .text-red-700, .dark .text-red-600, .dark .text-red-500 { color: #fb7185 !important; }
.dark .text-purple-700 { color: #a78bfa !important; }
.dark .text-orange-700 { color: #fb923c !important; }
.dark .text-indigo-700 { color: #818cf8 !important; }
/* Gradient backgrounds for team sections etc. */
.dark .from-blue-50 { --tw-gradient-from: rgba(96, 165, 250, 0.06) !important; }
.dark .to-indigo-50 { --tw-gradient-to: rgba(129, 140, 248, 0.06) !important; }
.dark .bg-gradient-to-r.from-blue-50 { background: rgba(96, 165, 250, 0.06) !important; }
/* ─── Form elements ──────────────────────────── */
.dark input,
.dark select,
.dark textarea {
background-color: rgba(255, 255, 255, 0.04);
color: #eeecf5;
border-color: rgba(255, 255, 255, 0.08);
}
.dark input:focus,
.dark select:focus,
.dark textarea:focus {
border-color: rgba(20, 184, 166, 0.5);
box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.1);
}
.dark input::placeholder,
.dark textarea::placeholder {
color: #637e78;
}
.dark input:disabled,
.dark select:disabled,
.dark textarea:disabled {
background-color: rgba(255, 255, 255, 0.02) !important;
color: #637e78 !important;
opacity: 0.6;
}
/* Dark select arrow */
.dark select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23637e78' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
}
/* ─── Cards — glass edges ────────────────────── */
.dark .card-hover {
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.dark .card-hover:hover {
box-shadow: 0 0 0 1px rgba(20, 184, 166, 0.12), 0 4px 16px -4px rgba(0, 0, 0, 0.4);
}
.dark .section-card {
background: #162220;
border-color: rgba(255, 255, 255, 0.06);
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.dark .section-card:hover {
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 4px 16px -4px rgba(0, 0, 0, 0.3);
}
.dark .section-card-header {
background: rgba(30, 46, 43, 0.3);
}
/* ─── Sidebar ────────────────────────────────── */
.dark .sidebar {
background: linear-gradient(180deg, #0a1412 0%, #060e0c 100%);
box-shadow: 2px 0 24px rgba(0, 0, 0, 0.5);
}
/* ─── Scrollbar ──────────────────────────────── */
.dark ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.08); }
.dark ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.15); }
/* ─── Shadows ────────────────────────────────── */
.dark .shadow-sm { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4) !important; }
.dark .shadow { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4) !important; }
.dark .shadow-lg { box-shadow: 0 12px 40px -10px rgba(0, 0, 0, 0.6) !important; }
.dark .shadow-2xl { box-shadow: 0 20px 60px -15px rgba(0, 0, 0, 0.7) !important; }
/* ─── Modal backdrop ─────────────────────────── */
.dark .bg-black\/40 { background-color: rgba(0, 0, 0, 0.7) !important; }
/* ─── Hover states ───────────────────────────── */
.dark .hover\:bg-surface-secondary:hover,
.dark .hover\:bg-gray-50:hover,
.dark .hover\:bg-gray-100:hover { background-color: rgba(255, 255, 255, 0.04) !important; }
.dark .hover\:bg-red-50:hover { background-color: rgba(251, 113, 133, 0.08) !important; }
.dark .hover\:bg-blue-100:hover { background-color: rgba(96, 165, 250, 0.08) !important; }
/* ─── Brand accent ────────────────────────────── */
.dark .bg-brand-primary {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.dark .bg-brand-primary:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* ─── White/light text overrides on colored badges ── */
.dark .bg-white\/90 { background-color: rgba(22, 34, 32, 0.9) !important; }
/* ─── Toasts — solid backgrounds ────────────────── */
.dark .bg-emerald-50.border-emerald-200 { background-color: #0f2a1e !important; border-color: #154a2e !important; }
.dark .bg-red-50.border-red-200 { background-color: #2a1315 !important; border-color: #4a1a20 !important; }
.dark .bg-blue-50.border-blue-200 { background-color: #0f1d2a !important; border-color: #152e4a !important; }
.dark .bg-amber-50.border-amber-200 { background-color: #2a2210 !important; border-color: #4a3a15 !important; }
.dark .text-emerald-800 { color: #6ee7b7 !important; }
.dark .text-red-800 { color: #fca5a5 !important; }
.dark .text-blue-800 { color: #93c5fd !important; }
.dark .text-amber-800 { color: #fcd34d !important; }
/* ─── Selection ──────────────────────────────── */
.dark ::selection {
background: rgba(20, 184, 166, 0.4);
color: white;
}
/* Reduced motion — disable animations for accessibility */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Smooth transitions — scoped to interactive elements only.
Do NOT use * selector — it causes every element to re-animate
on any React state change (e.g. drag-and-drop). Components should
use Tailwind transition-colors / transition-all where needed. */
/* Arabic text support */
[dir="rtl"] {
font-family: 'IBM Plex Sans Arabic', 'Inter', system-ui, sans-serif;
}
/* Auto-detect text direction in inputs for mixed Arabic/English content */
input[type="text"],
input[type="search"],
input[type="url"],
input[type="email"],
textarea {
unicode-bidi: plaintext;
}
/* Ensure text content areas handle Arabic properly */
.line-clamp-2, .truncate, h1, h2, h3, h4, h5, p, span, label {
unicode-bidi: plaintext;
}
/* RTL-aware sidebar positioning */
[dir="rtl"] .sidebar {
right: 0;
left: auto;
}
[dir="ltr"] .sidebar {
left: 0;
right: auto;
}
/* RTL-aware main content margin */
[dir="rtl"] .main-content-margin {
margin-right: 260px;
margin-left: 0;
}
[dir="ltr"] .main-content-margin {
margin-left: 260px;
margin-right: 0;
}
[dir="rtl"] .main-content-margin-collapsed {
margin-right: 68px;
margin-left: 0;
}
[dir="ltr"] .main-content-margin-collapsed {
margin-left: 68px;
margin-right: 0;
}
/* Enhanced sidebar */
.sidebar {
background: linear-gradient(180deg, #0a1f1c 0%, #061411 100%);
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.08);
}
/* Animation keyframes */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(-12px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes pulse-subtle {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
.animate-slide-in {
animation: slideIn 0.3s ease-out forwards;
}
.animate-scale-in {
animation: scaleIn 0.2s ease-out forwards;
}
.animate-pulse-subtle {
animation: pulse-subtle 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.animate-spin {
animation: spin 1s linear infinite;
}
@keyframes slide-in-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
.animate-slide-in-right {
animation: slide-in-right 0.3s ease-out;
}
/* Backdrop fade-in */
@keyframes backdropFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.animate-backdrop-in {
animation: backdropFadeIn 0.2s ease-out forwards;
}
/* Slide out (reverse of slideIn) */
@keyframes slideOut {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(-12px); }
}
.animate-slide-out {
animation: slideOut 0.3s ease-in forwards;
}
/* Collapsible section (CSS grid height transition) */
.collapsible-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.collapsible-content.is-open {
grid-template-rows: 1fr;
}
.collapsible-inner {
overflow: hidden;
}
.collapsible-content.is-open > .collapsible-inner {
overflow: visible;
}
/* Stagger children — short, max 4 items */
.stagger-children > * {
opacity: 0;
animation: fadeIn 0.2s ease-out forwards;
}
.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
.stagger-children > *:nth-child(2) { animation-delay: 40ms; }
.stagger-children > *:nth-child(3) { animation-delay: 80ms; }
.stagger-children > *:nth-child(n+4) { animation-delay: 120ms; }
/* Card hover effect - refined, no lift */
.card-hover {
position: relative;
transition: box-shadow 0.2s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.card-hover:hover {
box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.08);
}
/* Stat card accents - subtle colored top borders */
.stat-card {
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--color-brand-primary), var(--color-brand-primary-light));
opacity: 0;
transition: opacity 0.3s ease;
}
.stat-card:hover::before {
opacity: 1;
}
/* Mesh background — flat, no gradients */
.bg-mesh {
background-color: #f8fafc;
}
/* Stat card accent — subtle top border, no gradient */
.stat-card-premium {
position: relative;
overflow: hidden;
}
.stat-card-premium::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
opacity: 0.6;
}
.stat-card-premium.accent-primary::before {
background: #0d9488;
}
.stat-card-premium.accent-secondary::before {
background: #db2777;
}
.stat-card-premium.accent-tertiary::before {
background: #f59e0b;
}
.stat-card-premium.accent-quaternary::before {
background: #059669;
}
/* Section card - premium container */
.section-card {
background: white;
border: 1px solid var(--color-border);
border-radius: 1rem;
overflow: clip;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
transition: box-shadow 0.3s ease;
}
.section-card:hover {
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.06);
}
.section-card-header {
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--color-border);
}
/* Sidebar active glow */
.sidebar-active-glow {
box-shadow: inset 3px 0 0 rgba(20, 184, 166, 0.8);
}
[dir="rtl"] .sidebar-active-glow {
box-shadow: inset -3px 0 0 rgba(20, 184, 166, 0.8);
}
/* Refined button styles */
button {
border-radius: 0.625rem;
transition: background-color 0.2s, border-color 0.2s, color 0.2s, box-shadow 0.2s;
}
button:disabled {
cursor: not-allowed;
opacity: 0.6;
}
/* Focus states for accessibility */
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
outline: 2px solid var(--color-brand-primary);
outline-offset: 2px;
}
/* Input hover states */
input:not(:disabled):hover,
textarea:not(:disabled):hover,
select:not(:disabled):hover {
border-color: var(--color-brand-primary-light);
}
/* Loading button state */
.btn-loading {
position: relative;
pointer-events: none;
color: transparent !important;
}
.btn-loading::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 16px;
height: 16px;
margin-left: -8px;
margin-top: -8px;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spin 0.6s linear infinite;
color: white;
}
/* Calendar grid */
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
/* Smooth height transitions */
.transition-height {
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Better table row hover */
tbody tr:hover {
background-color: var(--color-surface-secondary);
}
/* Better select styling */
select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 12px;
appearance: none;
padding-right: 2.5rem;
}
[dir="rtl"] select {
background-position: left 0.75rem center;
padding-right: 0.75rem;
padding-left: 2.5rem;
}
/* Checkbox and radio improvements */
input[type="checkbox"],
input[type="radio"] {
cursor: pointer;
transition: all 0.2s ease;
}
input[type="checkbox"]:checked,
input[type="radio"]:checked {
background-color: var(--color-brand-primary);
border-color: var(--color-brand-primary);
}
/* Disabled state improvements */
input:disabled,
textarea:disabled,
select:disabled {
background-color: var(--color-surface-tertiary);
cursor: not-allowed;
opacity: 0.6;
}
/* Success/error input states */
.input-error {
border-color: #ef4444 !important;
}
.input-error:focus {
border-color: #ef4444 !important;
ring-color: rgba(239, 68, 68, 0.2) !important;
}
.input-success {
border-color: #10b981 !important;
}
.input-success:focus {
border-color: #10b981 !important;
ring-color: rgba(16, 185, 129, 0.2) !important;
}
/* Tooltip (if needed) */
.tooltip {
position: relative;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-8px);
padding: 0.5rem 0.75rem;
background: var(--color-sidebar);
color: white;
font-size: 0.75rem;
border-radius: 0.5rem;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
}
.tooltip:hover::after {
opacity: 1;
transform: translateX(-50%) translateY(-4px);
}
/* Loading spinner for inline use */
.spinner {
display: inline-block;
width: 1em;
height: 1em;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
/* Skeleton shimmer effect */
@keyframes shimmer-animation {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
.skeleton-shimmer {
background: linear-gradient(
90deg,
var(--color-surface-tertiary) 0%,
var(--color-surface-secondary) 50%,
var(--color-surface-tertiary) 100%
);
background-size: 468px 100%;
animation: shimmer-animation 1.5s ease-in-out infinite;
}