1pub mod breakpoints {
59 pub const XS: &str = "480px";
61 pub const SM: &str = "640px";
63 pub const MD: &str = "768px";
65 pub const LG: &str = "1024px";
67 pub const XL: &str = "1280px";
69}
70
71pub mod media {
73 pub const MOBILE: &str = "@media (max-width: 768px)";
74 pub const TABLET: &str = "@media (min-width: 769px) and (max-width: 1024px)";
75 pub const DESKTOP: &str = "@media (min-width: 1025px)";
76 pub const MOBILE_LANDSCAPE: &str = "@media (max-height: 500px) and (orientation: landscape)";
77 pub const TOUCH_DEVICE: &str = "@media (hover: none) and (pointer: coarse)";
78 pub const REDUCED_MOTION: &str = "@media (prefers-reduced-motion: reduce)";
79}
80
81pub mod touch {
87 pub const MIN_TARGET: &str = "44px";
89 pub const COMFORTABLE_TARGET: &str = "48px";
91 pub const LARGE_TARGET: &str = "56px";
93}
94
95pub const MOBILE_BASE_STYLES: &str = r#"
101/* ============================================ */
102/* MOBILE CSS VARIABLES */
103/* ============================================ */
104:root {
105 /* Touch targets */
106 --touch-min: 44px;
107 --touch-comfortable: 48px;
108 --touch-large: 56px;
109
110 /* Mobile spacing */
111 --mobile-padding: 12px;
112 --mobile-gap: 8px;
113
114 /* Safe area insets for notched devices */
115 --safe-top: env(safe-area-inset-top, 0px);
116 --safe-bottom: env(safe-area-inset-bottom, 0px);
117 --safe-left: env(safe-area-inset-left, 0px);
118 --safe-right: env(safe-area-inset-right, 0px);
119
120 /* Layout heights - standardized across app */
121 --header-height: 72px;
122 --footer-height-desktop: 280px;
123 --footer-height-tablet: 360px;
124 --footer-height-mobile: auto;
125
126 /* Standardized breakpoints (as CSS custom properties for reference) */
127 --breakpoint-mobile: 768px;
128 --breakpoint-tablet: 1024px;
129}
130
131/* ============================================ */
132/* MOBILE UTILITY CLASSES */
133/* ============================================ */
134
135/* Hide on mobile, show on desktop */
136.desktop-only {
137 display: block;
138}
139
140/* Show on mobile, hide on desktop */
141.mobile-only {
142 display: none;
143}
144
145@media (max-width: 768px) {
146 .desktop-only {
147 display: none !important;
148 }
149 .mobile-only {
150 display: block !important;
151 }
152 .mobile-only-flex {
153 display: flex !important;
154 }
155}
156
157/* Touch-friendly button base */
158.touch-target {
159 min-width: var(--touch-min);
160 min-height: var(--touch-min);
161 display: flex;
162 align-items: center;
163 justify-content: center;
164 cursor: pointer;
165 -webkit-tap-highlight-color: transparent;
166 touch-action: manipulation;
167}
168
169/* Safe area padding utilities */
170.safe-top {
171 padding-top: max(var(--mobile-padding), var(--safe-top));
172}
173
174.safe-bottom {
175 padding-bottom: max(var(--mobile-padding), var(--safe-bottom));
176}
177
178.safe-horizontal {
179 padding-left: max(var(--mobile-padding), var(--safe-left));
180 padding-right: max(var(--mobile-padding), var(--safe-right));
181}
182
183/* Smooth scrolling with momentum on iOS */
184.scroll-smooth {
185 -webkit-overflow-scrolling: touch;
186 scroll-behavior: smooth;
187}
188
189/* Prevent text selection on interactive elements */
190.no-select {
191 -webkit-user-select: none;
192 user-select: none;
193}
194
195/* Reduced motion support */
196@media (prefers-reduced-motion: reduce) {
197 *,
198 *::before,
199 *::after {
200 animation-duration: 0.01ms !important;
201 animation-iteration-count: 1 !important;
202 transition-duration: 0.01ms !important;
203 }
204}
205
206/* ============================================ */
207/* BODY SCROLL LOCK */
208/* ============================================ */
209
210/* Applied to body when mobile menu is open */
211body.menu-open {
212 overflow: hidden;
213 position: fixed;
214 width: 100%;
215 height: 100%;
216}
217
218/* ============================================ */
219/* HAMBURGER MENU & OVERLAY */
220/* ============================================ */
221
222/* Hamburger button */
223.hamburger-btn {
224 display: none;
225 width: var(--touch-comfortable);
226 height: var(--touch-comfortable);
227 padding: 0;
228 border: none;
229 background: transparent;
230 cursor: pointer;
231 -webkit-tap-highlight-color: transparent;
232 position: relative;
233 z-index: 1001;
234}
235
236.hamburger-icon {
237 width: 24px;
238 height: 18px;
239 position: relative;
240 display: flex;
241 flex-direction: column;
242 justify-content: space-between;
243}
244
245.hamburger-line {
246 width: 100%;
247 height: 2px;
248 background: var(--text-primary, #f0f0f0);
249 border-radius: 1px;
250 transition: all 0.3s ease;
251 transform-origin: center;
252}
253
254/* Hamburger to X animation */
255.hamburger-btn.open .hamburger-line:nth-child(1) {
256 transform: translateY(8px) rotate(45deg);
257}
258
259.hamburger-btn.open .hamburger-line:nth-child(2) {
260 opacity: 0;
261 transform: scaleX(0);
262}
263
264.hamburger-btn.open .hamburger-line:nth-child(3) {
265 transform: translateY(-8px) rotate(-45deg);
266}
267
268/* Mobile menu overlay */
269.mobile-menu-overlay {
270 position: fixed;
271 top: 0;
272 left: 0;
273 right: 0;
274 bottom: 0;
275 background: rgba(6, 8, 20, 0.98);
276 backdrop-filter: blur(20px);
277 -webkit-backdrop-filter: blur(20px);
278 z-index: 1000;
279 opacity: 0;
280 visibility: hidden;
281 transition: opacity 0.3s ease, visibility 0.3s ease;
282 display: flex;
283 flex-direction: column;
284 padding: calc(var(--header-height) + 20px) var(--mobile-padding) var(--mobile-padding);
285 overflow-y: auto;
286}
287
288.mobile-menu-overlay.open {
289 opacity: 1;
290 visibility: visible;
291}
292
293/* Mobile menu navigation links */
294.mobile-menu-nav {
295 display: flex;
296 flex-direction: column;
297 gap: 8px;
298 margin-bottom: 32px;
299}
300
301.mobile-menu-link {
302 display: flex;
303 align-items: center;
304 gap: 12px;
305 padding: 16px 20px;
306 border-radius: 12px;
307 background: rgba(255, 255, 255, 0.03);
308 color: var(--text-secondary, #b0b0b0);
309 text-decoration: none;
310 font-size: 18px;
311 font-weight: 500;
312 transition: all 0.2s ease;
313 border: 1px solid transparent;
314}
315
316.mobile-menu-link:active {
317 transform: scale(0.98);
318 background: rgba(255, 255, 255, 0.08);
319}
320
321.mobile-menu-link.active {
322 background: rgba(102, 126, 234, 0.15);
323 color: var(--text-primary, #f0f0f0);
324 border-color: rgba(102, 126, 234, 0.3);
325}
326
327.mobile-menu-link-icon {
328 font-size: 20px;
329 width: 24px;
330 text-align: center;
331}
332
333/* Mobile menu CTAs */
334.mobile-menu-ctas {
335 display: flex;
336 flex-direction: column;
337 gap: 12px;
338 margin-top: auto;
339 padding-top: 24px;
340 border-top: 1px solid rgba(255, 255, 255, 0.08);
341}
342
343.mobile-menu-cta {
344 display: flex;
345 align-items: center;
346 justify-content: center;
347 padding: 16px 24px;
348 border-radius: 12px;
349 font-size: 16px;
350 font-weight: 600;
351 text-decoration: none;
352 transition: all 0.2s ease;
353}
354
355.mobile-menu-cta.primary {
356 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
357 color: white;
358}
359
360.mobile-menu-cta.secondary {
361 background: rgba(255, 255, 255, 0.05);
362 border: 1px solid rgba(255, 255, 255, 0.1);
363 color: var(--text-primary, #f0f0f0);
364}
365
366/* Show hamburger on mobile/tablet */
367@media (max-width: 1024px) {
368 .hamburger-btn {
369 display: flex;
370 align-items: center;
371 justify-content: center;
372 }
373
374 .nav-links-desktop {
375 display: none !important;
376 }
377
378 .nav-ctas-desktop {
379 display: none !important;
380 }
381}
382
383/* ============================================ */
384/* TABLET-SPECIFIC ADJUSTMENTS */
385/* ============================================ */
386
387@media (min-width: 769px) and (max-width: 1024px) {
388 .mobile-menu-overlay {
389 padding: calc(var(--header-height) + 40px) 40px 40px;
390 }
391
392 .mobile-menu-link {
393 padding: 18px 24px;
394 font-size: 20px;
395 }
396
397 .mobile-menu-nav {
398 max-width: 400px;
399 margin: 0 auto 40px;
400 }
401
402 .mobile-menu-ctas {
403 max-width: 400px;
404 margin: auto auto 0;
405 }
406}
407"#;
408
409pub const MOBILE_TAB_BAR_STYLES: &str = r#"
415/* Mobile Tab Bar Container */
416.mobile-tabs {
417 display: none;
418}
419
420@media (max-width: 768px) {
421 .mobile-tabs {
422 display: flex;
423 gap: 4px;
424 padding: 8px var(--mobile-padding, 12px);
425 background: rgba(0, 0, 0, 0.4);
426 border-bottom: 1px solid rgba(255, 255, 255, 0.08);
427 overflow-x: auto;
428 -webkit-overflow-scrolling: touch;
429 flex-shrink: 0;
430 /* Hide scrollbar but keep functionality */
431 scrollbar-width: none;
432 -ms-overflow-style: none;
433 }
434
435 .mobile-tabs::-webkit-scrollbar {
436 display: none;
437 }
438
439 /* Individual Tab Button */
440 .mobile-tab {
441 flex: 1;
442 min-width: 0;
443 padding: 10px 8px;
444 border: none;
445 border-radius: 10px;
446 background: rgba(255, 255, 255, 0.05);
447 color: #888;
448 font-size: 13px;
449 font-weight: 500;
450 cursor: pointer;
451 transition: all 0.2s ease;
452 display: flex;
453 flex-direction: column;
454 align-items: center;
455 gap: 4px;
456 min-height: var(--touch-min, 44px);
457 -webkit-tap-highlight-color: transparent;
458 }
459
460 .mobile-tab-icon {
461 font-size: 18px;
462 line-height: 1;
463 }
464
465 .mobile-tab-label {
466 font-size: 11px;
467 white-space: nowrap;
468 overflow: hidden;
469 text-overflow: ellipsis;
470 max-width: 100%;
471 }
472
473 .mobile-tab:active {
474 background: rgba(255, 255, 255, 0.15);
475 transform: scale(0.97);
476 }
477
478 .mobile-tab.active {
479 background: rgba(102, 126, 234, 0.25);
480 color: #e8e8e8;
481 border: 1px solid rgba(102, 126, 234, 0.4);
482 }
483
484 /* Tab indicator dots (optional, for swipe hint) */
485 .mobile-tab-indicator {
486 display: flex;
487 justify-content: center;
488 gap: 6px;
489 padding: 6px;
490 background: rgba(0, 0, 0, 0.2);
491 }
492
493 .mobile-tab-dot {
494 width: 6px;
495 height: 6px;
496 border-radius: 50%;
497 background: rgba(255, 255, 255, 0.2);
498 transition: all 0.2s ease;
499 }
500
501 .mobile-tab-dot.active {
502 background: #667eea;
503 width: 18px;
504 border-radius: 3px;
505 }
506}
507
508/* Landscape mobile - horizontal tab layout */
509@media (max-height: 500px) and (orientation: landscape) {
510 .mobile-tabs {
511 padding: 4px 8px;
512 }
513
514 .mobile-tab {
515 padding: 6px 12px;
516 flex-direction: row;
517 gap: 6px;
518 min-height: 36px;
519 }
520
521 .mobile-tab-icon {
522 font-size: 16px;
523 }
524}
525"#;
526
527pub const MOBILE_PANEL_STYLES: &str = r#"
533/* Desktop: show all panels side by side */
534.panel-container {
535 display: flex;
536 flex: 1;
537 overflow: hidden;
538}
539
540.panel {
541 display: flex;
542 flex-direction: column;
543 overflow: hidden;
544 background: rgba(0, 0, 0, 0.3);
545}
546
547.panel-header {
548 padding: 12px 16px;
549 border-bottom: 1px solid rgba(255, 255, 255, 0.08);
550 font-size: 12px;
551 font-weight: 600;
552 text-transform: uppercase;
553 letter-spacing: 0.5px;
554 color: #888;
555 display: flex;
556 justify-content: space-between;
557 align-items: center;
558 flex-shrink: 0;
559}
560
561.panel-content {
562 flex: 1;
563 overflow: auto;
564 -webkit-overflow-scrolling: touch;
565}
566
567@media (max-width: 768px) {
568 .panel-container {
569 flex-direction: column;
570 position: relative;
571 }
572
573 /* On mobile, panels stack and only active one shows */
574 .panel {
575 position: absolute;
576 top: 0;
577 left: 0;
578 right: 0;
579 bottom: 0;
580 opacity: 0;
581 pointer-events: none;
582 transition: opacity 0.15s ease;
583 min-width: unset;
584 }
585
586 .panel.panel-active {
587 position: relative;
588 flex: 1;
589 opacity: 1;
590 pointer-events: auto;
591 }
592
593 /* Panel headers hidden on mobile (tabs replace them) */
594 .panel .panel-header {
595 display: none;
596 }
597
598 /* But show header for active panel if it has controls */
599 .panel.panel-active .panel-header.has-controls {
600 display: flex;
601 padding: 8px 12px;
602 background: rgba(0, 0, 0, 0.2);
603 }
604}
605"#;
606
607pub const MOBILE_BUTTON_STYLES: &str = r#"
613@media (max-width: 768px) {
614 /* Ensure all buttons meet touch target requirements */
615 button,
616 .btn,
617 [role="button"] {
618 min-height: var(--touch-min, 44px);
619 min-width: var(--touch-min, 44px);
620 padding: 10px 16px;
621 font-size: 14px;
622 }
623
624 /* Toggle button groups */
625 .toggle-group {
626 gap: 6px;
627 padding: 4px;
628 border-radius: 8px;
629 }
630
631 .toggle-btn {
632 padding: 10px 16px;
633 font-size: 14px;
634 border-radius: 6px;
635 min-height: var(--touch-min, 44px);
636 min-width: var(--touch-min, 44px);
637 display: flex;
638 align-items: center;
639 justify-content: center;
640 }
641
642 /* Small icon buttons */
643 .icon-btn {
644 width: var(--touch-min, 44px);
645 height: var(--touch-min, 44px);
646 padding: 0;
647 display: flex;
648 align-items: center;
649 justify-content: center;
650 }
651
652 .icon-btn svg,
653 .icon-btn .icon {
654 width: 20px;
655 height: 20px;
656 }
657}
658"#;
659
660pub const MOBILE_INPUT_STYLES: &str = r#"
666@media (max-width: 768px) {
667 /* Text inputs and textareas */
668 input[type="text"],
669 input[type="email"],
670 input[type="password"],
671 input[type="search"],
672 textarea {
673 font-size: 16px; /* Prevents iOS zoom on focus */
674 min-height: var(--touch-min, 44px);
675 padding: 12px 16px;
676 border-radius: 10px;
677 }
678
679 textarea {
680 min-height: 120px;
681 resize: vertical;
682 }
683
684 /* Labels above inputs */
685 label {
686 font-size: 14px;
687 margin-bottom: 6px;
688 }
689
690 /* Form groups */
691 .form-group {
692 margin-bottom: 16px;
693 }
694}
695"#;
696
697pub const MOBILE_RESIZER_STYLES: &str = r#"
703.panel-resizer {
704 width: 6px;
705 background: rgba(255, 255, 255, 0.05);
706 cursor: col-resize;
707 transition: background 0.2s ease;
708 flex-shrink: 0;
709}
710
711.panel-resizer:hover,
712.panel-resizer.active {
713 background: rgba(102, 126, 234, 0.5);
714}
715
716@media (max-width: 768px) {
717 .panel-resizer {
718 display: none;
719 }
720}
721"#;
722
723pub fn all_mobile_styles() -> String {
729 format!(
730 "{}\n{}\n{}\n{}\n{}\n{}",
731 MOBILE_BASE_STYLES,
732 MOBILE_TAB_BAR_STYLES,
733 MOBILE_PANEL_STYLES,
734 MOBILE_BUTTON_STYLES,
735 MOBILE_INPUT_STYLES,
736 MOBILE_RESIZER_STYLES,
737 )
738}
739
740pub fn with_mobile_styles(page_styles: &str) -> String {
743 format!("{}\n{}", MOBILE_BASE_STYLES, page_styles)
744}