File size: 9,421 Bytes
d44ff09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* ============================================================================
   App shell + screen layout (Section 2.0 global nav, 2.1–2.5 screens).
   MOBILE-FIRST responsive system: base styles target the smallest screens,
   then min-width breakpoints progressively enhance toward desktop.
   Covers: phones (320-480px), tablets (768-1024px), laptops, wide desktops.
   ========================================================================== */

/* ===== Base (mobile-first): designed for 320px+ ===== */
* { -webkit-tap-highlight-color: transparent; }
html { -webkit-text-size-adjust: 100%; }

.app-shell { display: flex; min-height: 100vh; min-height: 100dvh; /* dynamic viewport: accounts for mobile browser chrome */ }

/* Sidebar: drawer by default (mobile), persistent on >= md */
.sidebar {
  width: min(280px, 84vw); background: var(--color-surface); border-right: 1px solid var(--color-border);
  padding: var(--space-6) var(--space-4); display: flex; flex-direction: column; gap: var(--space-2);
  flex-shrink: 0; height: 100vh; height: 100dvh;
  /* mobile drawer state */
  position: fixed; left: 0; top: 0; z-index: 900; transform: translateX(-100%);
  transition: transform .25s ease; box-shadow: 2px 0 16px rgba(0,0,0,.18); overflow-y: auto;
}
.sidebar.open { transform: translateX(0); }
.sidebar .logo { font-size: var(--fs-fluid-md); font-weight: 800; color: var(--color-primary); margin-bottom: var(--space-6); display: flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.nav-link { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius); color: var(--color-text-secondary); text-decoration: none; font-size: var(--fs-14); font-weight: 600; min-height: 44px; /* touch target a11y */ }
.nav-link:hover { background: var(--color-surface-muted); color: var(--color-text-primary); }
.nav-link.active { background: rgba(13,125,107,.1); color: var(--color-primary); }
.sidebar .spacer { flex: 1; }

.main {
  flex: 1; padding: var(--space-4); width: 100%; min-width: 0;
  max-width: 1200px; margin: 0 auto;
}
.page-title { font-size: var(--fs-fluid-xl); margin-bottom: var(--space-2); line-height: 1.15; word-break: break-word; }
.page-sub { color: var(--color-text-secondary); margin-bottom: var(--space-6); font-size: var(--fs-fluid-sm); }

/* Mobile menu button: visible by default */
.menu-toggle { display: inline-flex; }
.scrim { display: none; }
.scrim.show { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 800; }

/* Auth screen */
.auth-wrap { min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: var(--space-4); }
.auth-card { width: 100%; max-width: 420px; }

/* Dashboard widgets */
.stat-card { display: flex; flex-direction: column; gap: var(--space-1); }
.stat-card .num { font-size: var(--fs-fluid-2xl); font-weight: 800; color: var(--color-primary); line-height: 1; }

/* Editor: single column by default (mobile), dual-pane on >= md */
.editor-wrap { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
.editor-pane { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.preview-frame { border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-4); background: var(--color-on-primary); min-height: 300px; overflow: auto; max-height: 70vh; /* review HTML can wrap */ word-break: break-word; }
.json-preview { font-family: var(--font-mono); font-size: var(--fs-12); background: var(--color-code-bg); color: var(--color-code-fg); padding: var(--space-3); border-radius: var(--radius); white-space: pre-wrap; word-break: break-word; max-height: 70vh; overflow: auto; }
.code-editor { font-family: var(--font-mono); font-size: var(--fs-12); min-height: 260px; }

/* Tables: horizontally scrollable wrapper on small screens */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.tbl { width: 100%; border-collapse: collapse; min-width: 560px; /* keeps columns usable inside scroll */ }
table.tbl th, table.tbl td { text-align: left; padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border); font-size: var(--fs-14); white-space: nowrap; }
table.tbl td:first-child { white-space: normal; }

/* Export screen */
.export-row { display: grid; grid-template-columns: 1fr; gap: var(--space-2); padding: var(--space-4) 0; border-bottom: 1px solid var(--color-border); }

/* Progress bar */
.progress { height: 8px; background: var(--color-surface-muted); border-radius: 999px; overflow: hidden; }
.progress > div { height: 100%; background: var(--color-primary); transition: width .3s; }

.error-banner { background: var(--color-banner-error-bg); border: 1px solid var(--color-banner-error-border); color: var(--color-banner-error-fg); padding: var(--space-3) var(--space-4); border-radius: var(--radius); font-size: var(--fs-fluid-sm); display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.empty-state { text-align: center; padding: var(--space-8) var(--space-4); color: var(--color-text-secondary); }
.empty-state .ill { font-size: 48px; margin-bottom: var(--space-4); }

.pagination { display: flex; gap: var(--space-2); justify-content: center; align-items: center; margin-top: var(--space-6); flex-wrap: wrap; }

/* Row components stack vertically by default */
.row { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.grid { display: grid; gap: var(--space-4); }

/* Responsive auto-fit grid β€” collapses columns based on available width
   without needing breakpoints (used for stat cards, checkbox groups). */
.grid-auto { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
/* Two-column grid that collapses to one column under the tablet breakpoint. */
.grid-2 { display: grid; gap: var(--space-4); grid-template-columns: 1fr; }
@media (min-width: 480px) { .grid-2 { grid-template-columns: 1fr 1fr; } }

/* Campaign list row: card-like on mobile, full row on tablet+. */
.campaign-row { display: grid; grid-template-columns: 1fr; gap: var(--space-2); align-items: center; padding: var(--space-4) 0; border-bottom: 1px solid var(--color-border); }
.campaign-row:last-child { border-bottom: none; }
@media (min-width: 600px) {
  .campaign-row { grid-template-columns: 1fr auto auto auto; gap: var(--space-4); }
}

/* Mobile editor pane toggle (Edit/Preview switch) β€” shown only on small screens */
.pane-toggle { display: inline-flex; }

/* ============================================================================
   BREAKPOINT: large phones & up (>= 480px)
   ========================================================================== */
@media (min-width: 480px) {
  .main { padding: var(--space-6); }
  .export-row { grid-template-columns: 1fr auto auto auto; gap: var(--space-4); align-items: center; }
}

/* ============================================================================
   BREAKPOINT: tablets & up (>= 768px) β€” sidebar becomes persistent
   ========================================================================== */
@media (min-width: 768px) {
  .sidebar { position: sticky; transform: none; box-shadow: none; width: 240px; }
  .menu-toggle { display: none; }
  .scrim { display: none !important; }
  .main { padding: var(--space-8); }
  .empty-state { padding: var(--space-12) var(--space-6); }
  /* Editor: dual-pane (editor + preview side by side) */
  .editor-wrap { grid-template-columns: 1fr 1fr; gap: var(--space-6); }
  /* On tablet+ we have both panes visible, so the mobile toggle is hidden */
  .pane-toggle { display: none; }
  .editor-pane.is-mobile-hidden { display: flex; }
}

/* ============================================================================
   BREAKPOINT: laptops & up (>= 1024px)
   ========================================================================== */
@media (min-width: 1024px) {
  .main { max-width: 1200px; }
}

/* ============================================================================
   BREAKPOINT: wide desktops (>= 1440px)
   ========================================================================== */
@media (min-width: 1440px) {
  .main { max-width: 1320px; padding: var(--space-12); }
}

/* ============================================================================
   SMALL PHONES (< 360px) β€” last-ditch safety: prevent overflow
   ========================================================================== */
@media (max-width: 359px) {
  .auth-card { padding: var(--space-3); }
  .btn { padding: 0 var(--space-3); }
  .page-title { font-size: 1.25rem; }
}

/* ============================================================================
   MOBILE editor pane switching: on < md, show only the active pane (edit OR
   preview) to avoid endless scrolling; a toggle switches between them.
   ========================================================================== */
@media (max-width: 767px) {
  .editor-pane.is-mobile-hidden { display: none; }
}

/* Reduced motion preference (a11y) */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* Print: clean export-friendly */
@media print {
  .sidebar, .menu-toggle, .toast-container, .pane-toggle, .btn { display: none !important; }
  .main { padding: 0; max-width: none; }
}