omnira-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/cli/omnira-init.mjs +260 -0
  4. package/cli/presets.mjs +386 -0
  5. package/components/ui/AppStoreButton/AppStoreButton.module.css +87 -0
  6. package/components/ui/AppStoreButton/AppStoreButton.tsx +114 -0
  7. package/components/ui/AppStoreButton/index.ts +2 -0
  8. package/components/ui/Avatar/Avatar.module.css +76 -0
  9. package/components/ui/Avatar/Avatar.tsx +102 -0
  10. package/components/ui/Avatar/index.ts +2 -0
  11. package/components/ui/Badge/Badge.module.css +120 -0
  12. package/components/ui/Badge/Badge.tsx +25 -0
  13. package/components/ui/Badge/index.ts +2 -0
  14. package/components/ui/BadgeGroup/BadgeGroup.module.css +214 -0
  15. package/components/ui/BadgeGroup/BadgeGroup.tsx +88 -0
  16. package/components/ui/BadgeGroup/index.ts +2 -0
  17. package/components/ui/Browser/Browser.module.css +105 -0
  18. package/components/ui/Browser/Browser.tsx +54 -0
  19. package/components/ui/Browser/index.ts +2 -0
  20. package/components/ui/Button/Button.module.css +188 -0
  21. package/components/ui/Button/Button.tsx +74 -0
  22. package/components/ui/Button/index.ts +2 -0
  23. package/components/ui/ButtonUtility/ButtonUtility.module.css +154 -0
  24. package/components/ui/ButtonUtility/ButtonUtility.tsx +65 -0
  25. package/components/ui/ButtonUtility/index.ts +2 -0
  26. package/components/ui/Card/Card.module.css +80 -0
  27. package/components/ui/Card/Card.tsx +39 -0
  28. package/components/ui/Card/index.ts +2 -0
  29. package/components/ui/Checkbox/Checkbox.module.css +92 -0
  30. package/components/ui/Checkbox/Checkbox.tsx +95 -0
  31. package/components/ui/Checkbox/index.ts +2 -0
  32. package/components/ui/Collapse/Collapse.module.css +60 -0
  33. package/components/ui/Collapse/Collapse.tsx +100 -0
  34. package/components/ui/Collapse/index.ts +2 -0
  35. package/components/ui/CreditCard/CreditCard.module.css +117 -0
  36. package/components/ui/CreditCard/CreditCard.tsx +86 -0
  37. package/components/ui/CreditCard/index.ts +2 -0
  38. package/components/ui/Dropdown/Dropdown.module.css +269 -0
  39. package/components/ui/Dropdown/Dropdown.tsx +419 -0
  40. package/components/ui/Dropdown/index.ts +31 -0
  41. package/components/ui/GridLines/GridLines.module.css +95 -0
  42. package/components/ui/GridLines/GridLines.tsx +10 -0
  43. package/components/ui/GridLines/index.ts +1 -0
  44. package/components/ui/Illustration/Illustration.module.css +10 -0
  45. package/components/ui/Illustration/Illustration.tsx +96 -0
  46. package/components/ui/Illustration/index.ts +2 -0
  47. package/components/ui/Input/Input.module.css +217 -0
  48. package/components/ui/Input/Input.tsx +93 -0
  49. package/components/ui/Input/index.ts +2 -0
  50. package/components/ui/Phone/Phone.module.css +104 -0
  51. package/components/ui/Phone/Phone.tsx +54 -0
  52. package/components/ui/Phone/index.ts +2 -0
  53. package/components/ui/PinInput/PinInput.module.css +104 -0
  54. package/components/ui/PinInput/PinInput.tsx +210 -0
  55. package/components/ui/PinInput/index.ts +1 -0
  56. package/components/ui/ProgressBar/ProgressBar.module.css +180 -0
  57. package/components/ui/ProgressBar/ProgressBar.tsx +206 -0
  58. package/components/ui/ProgressBar/index.ts +2 -0
  59. package/components/ui/QRCode/QRCode.module.css +13 -0
  60. package/components/ui/QRCode/QRCode.tsx +128 -0
  61. package/components/ui/QRCode/index.ts +2 -0
  62. package/components/ui/RadioButton/RadioButton.module.css +92 -0
  63. package/components/ui/RadioButton/RadioButton.tsx +134 -0
  64. package/components/ui/RadioButton/index.ts +2 -0
  65. package/components/ui/RadioGroup/RadioGroup.module.css +382 -0
  66. package/components/ui/RadioGroup/RadioGroup.tsx +292 -0
  67. package/components/ui/RadioGroup/index.ts +1 -0
  68. package/components/ui/Rating/Rating.module.css +99 -0
  69. package/components/ui/Rating/Rating.tsx +123 -0
  70. package/components/ui/Rating/index.ts +2 -0
  71. package/components/ui/Select/Select.module.css +371 -0
  72. package/components/ui/Select/Select.tsx +507 -0
  73. package/components/ui/Select/index.ts +2 -0
  74. package/components/ui/SidebarNavigation/SidebarDual.tsx +71 -0
  75. package/components/ui/SidebarNavigation/SidebarFeatureCard.module.css +351 -0
  76. package/components/ui/SidebarNavigation/SidebarFeatureCard.tsx +388 -0
  77. package/components/ui/SidebarNavigation/SidebarNavigation.module.css +610 -0
  78. package/components/ui/SidebarNavigation/SidebarParts.tsx +215 -0
  79. package/components/ui/SidebarNavigation/SidebarSectionDividers.tsx +46 -0
  80. package/components/ui/SidebarNavigation/SidebarSectionHeadings.tsx +52 -0
  81. package/components/ui/SidebarNavigation/SidebarSimple.tsx +46 -0
  82. package/components/ui/SidebarNavigation/SidebarSlim.tsx +48 -0
  83. package/components/ui/SidebarNavigation/index.ts +29 -0
  84. package/components/ui/SidebarNavigation/types.ts +27 -0
  85. package/components/ui/Slider/Slider.module.css +93 -0
  86. package/components/ui/Slider/Slider.tsx +118 -0
  87. package/components/ui/Slider/index.ts +2 -0
  88. package/components/ui/SocialButton/SocialButton.module.css +223 -0
  89. package/components/ui/SocialButton/SocialButton.tsx +185 -0
  90. package/components/ui/SocialButton/index.ts +2 -0
  91. package/components/ui/Tag/Tag.module.css +203 -0
  92. package/components/ui/Tag/Tag.tsx +161 -0
  93. package/components/ui/Tag/index.ts +2 -0
  94. package/components/ui/TextEditor/TextEditor.module.css +182 -0
  95. package/components/ui/TextEditor/TextEditor.tsx +323 -0
  96. package/components/ui/TextEditor/index.ts +1 -0
  97. package/components/ui/Textarea/Textarea.module.css +75 -0
  98. package/components/ui/Textarea/Textarea.tsx +59 -0
  99. package/components/ui/Textarea/index.ts +2 -0
  100. package/components/ui/Toggle/Toggle.module.css +118 -0
  101. package/components/ui/Toggle/Toggle.tsx +77 -0
  102. package/components/ui/Toggle/index.ts +2 -0
  103. package/components/ui/Tooltip/Tooltip.module.css +95 -0
  104. package/components/ui/Tooltip/Tooltip.tsx +55 -0
  105. package/components/ui/Tooltip/index.ts +2 -0
  106. package/components/ui/VideoPlayer/VideoPlayer.module.css +120 -0
  107. package/components/ui/VideoPlayer/VideoPlayer.tsx +229 -0
  108. package/components/ui/VideoPlayer/index.ts +2 -0
  109. package/lib/cn.ts +3 -0
  110. package/lib/copy-to-clipboard.ts +21 -0
  111. package/package.json +61 -0
@@ -0,0 +1,75 @@
1
+ /* ── Wrapper ── */
2
+ .wrapper {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 6px;
6
+ width: 100%;
7
+ }
8
+
9
+ /* ── Label ── */
10
+ .label {
11
+ font-size: 12px;
12
+ font-weight: 600;
13
+ text-transform: uppercase;
14
+ letter-spacing: 0.06em;
15
+ color: var(--color-text-tertiary);
16
+ }
17
+
18
+ .required {
19
+ color: var(--color-error);
20
+ margin-left: 2px;
21
+ }
22
+
23
+ /* ── Textarea ── */
24
+ .textarea {
25
+ width: 100%;
26
+ padding: 12px 14px;
27
+ border-radius: var(--radius-md);
28
+ background: var(--color-bg-input);
29
+ border: 1px solid var(--color-border-standard);
30
+ font-family: inherit;
31
+ font-size: 14px;
32
+ line-height: 1.6;
33
+ color: var(--color-text-primary);
34
+ transition: all 0.25s;
35
+ outline: none;
36
+ resize: vertical;
37
+ min-height: 80px;
38
+ }
39
+
40
+ .textarea:focus {
41
+ border-color: var(--color-border-lime-strong);
42
+ background: var(--color-bg-elevated);
43
+ }
44
+
45
+ .textarea::placeholder {
46
+ color: var(--color-text-tertiary);
47
+ opacity: 0.5;
48
+ }
49
+
50
+ /* ── Disabled ── */
51
+ .disabled {
52
+ opacity: 0.5;
53
+ cursor: not-allowed;
54
+ resize: none;
55
+ }
56
+
57
+ /* ── Error ── */
58
+ .error {
59
+ border-color: var(--color-error-border);
60
+ }
61
+
62
+ .error:focus {
63
+ border-color: var(--color-error);
64
+ }
65
+
66
+ .errorText {
67
+ font-size: 12px;
68
+ color: var(--color-error);
69
+ }
70
+
71
+ /* ── Helper / Hint ── */
72
+ .helperText {
73
+ font-size: 12px;
74
+ color: var(--color-text-tertiary);
75
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+
3
+ import { forwardRef } from "react";
4
+ import { cn } from "@/lib/cn";
5
+ import styles from "./Textarea.module.css";
6
+
7
+ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
8
+ label?: string;
9
+ error?: string;
10
+ helperText?: string;
11
+ hint?: string;
12
+ isRequired?: boolean;
13
+ wrapperClassName?: string;
14
+ }
15
+
16
+ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
17
+ (
18
+ {
19
+ label,
20
+ error,
21
+ helperText,
22
+ hint,
23
+ isRequired = false,
24
+ wrapperClassName,
25
+ className,
26
+ disabled,
27
+ ...props
28
+ },
29
+ ref,
30
+ ) => {
31
+ return (
32
+ <div className={cn(styles.wrapper, wrapperClassName)}>
33
+ {label && (
34
+ <label className={styles.label}>
35
+ {label}
36
+ {isRequired && <span className={styles.required}>*</span>}
37
+ </label>
38
+ )}
39
+ <textarea
40
+ ref={ref}
41
+ className={cn(
42
+ styles.textarea,
43
+ error ? styles.error : undefined,
44
+ disabled ? styles.disabled : undefined,
45
+ className,
46
+ )}
47
+ disabled={disabled}
48
+ {...props}
49
+ />
50
+ {error && <span className={styles.errorText}>{error}</span>}
51
+ {(hint || helperText) && !error && (
52
+ <span className={styles.helperText}>{hint || helperText}</span>
53
+ )}
54
+ </div>
55
+ );
56
+ },
57
+ );
58
+
59
+ Textarea.displayName = "Textarea";
@@ -0,0 +1,2 @@
1
+ export { Textarea } from "./Textarea";
2
+ export type { TextareaProps } from "./Textarea";
@@ -0,0 +1,118 @@
1
+ /* ── Wrapper ── */
2
+ .wrapper {
3
+ display: flex;
4
+ align-items: flex-start;
5
+ gap: 10px;
6
+ cursor: pointer;
7
+ }
8
+
9
+ .wrapperDisabled {
10
+ opacity: 0.5;
11
+ cursor: not-allowed;
12
+ pointer-events: none;
13
+ }
14
+
15
+ /* ── Track ── */
16
+ .track {
17
+ position: relative;
18
+ flex-shrink: 0;
19
+ border: none;
20
+ border-radius: 999px;
21
+ background: var(--color-bg-elevated);
22
+ border: 1px solid var(--color-border-standard);
23
+ cursor: pointer;
24
+ transition: all 0.25s;
25
+ padding: 0;
26
+ }
27
+
28
+ .track:hover {
29
+ border-color: var(--color-border-lime-strong);
30
+ }
31
+
32
+ .trackChecked {
33
+ background: var(--color-lime);
34
+ border-color: var(--color-lime);
35
+ }
36
+
37
+ /* Track sizes */
38
+ .track_sm {
39
+ width: 36px;
40
+ height: 20px;
41
+ }
42
+
43
+ .track_md {
44
+ width: 44px;
45
+ height: 24px;
46
+ }
47
+
48
+ .track_lg {
49
+ width: 52px;
50
+ height: 28px;
51
+ }
52
+
53
+ /* ── Thumb ── */
54
+ .thumb {
55
+ position: absolute;
56
+ top: 50%;
57
+ border-radius: 50%;
58
+ background: var(--color-text-tertiary);
59
+ transition: all 0.25s;
60
+ transform: translateY(-50%);
61
+ }
62
+
63
+ .trackChecked .thumb {
64
+ background: var(--color-bg-primary);
65
+ }
66
+
67
+ /* Thumb sizes */
68
+ .thumb_sm {
69
+ width: 14px;
70
+ height: 14px;
71
+ left: 2px;
72
+ }
73
+
74
+ .thumb_md {
75
+ width: 18px;
76
+ height: 18px;
77
+ left: 2px;
78
+ }
79
+
80
+ .thumb_lg {
81
+ width: 22px;
82
+ height: 22px;
83
+ left: 2px;
84
+ }
85
+
86
+ /* Thumb checked positions */
87
+ .thumbChecked.thumb_sm {
88
+ left: 18px;
89
+ }
90
+
91
+ .thumbChecked.thumb_md {
92
+ left: 22px;
93
+ }
94
+
95
+ .thumbChecked.thumb_lg {
96
+ left: 26px;
97
+ }
98
+
99
+ /* ── Text ── */
100
+ .textWrap {
101
+ display: flex;
102
+ flex-direction: column;
103
+ gap: 2px;
104
+ padding-top: 1px;
105
+ }
106
+
107
+ .label {
108
+ font-size: 14px;
109
+ font-weight: 500;
110
+ color: var(--color-text-primary);
111
+ line-height: 1.3;
112
+ }
113
+
114
+ .hint {
115
+ font-size: 12px;
116
+ color: var(--color-text-tertiary);
117
+ line-height: 1.4;
118
+ }
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import { forwardRef, useState, useCallback } from "react";
4
+ import { cn } from "@/lib/cn";
5
+ import styles from "./Toggle.module.css";
6
+
7
+ export interface ToggleProps {
8
+ label?: string;
9
+ hint?: string;
10
+ size?: "sm" | "md" | "lg";
11
+ disabled?: boolean;
12
+ checked?: boolean;
13
+ defaultChecked?: boolean;
14
+ onChange?: (checked: boolean) => void;
15
+ className?: string;
16
+ }
17
+
18
+ export const Toggle = forwardRef<HTMLButtonElement, ToggleProps>(
19
+ (
20
+ {
21
+ label,
22
+ hint,
23
+ size = "sm",
24
+ disabled = false,
25
+ checked: controlledChecked,
26
+ defaultChecked = false,
27
+ onChange,
28
+ className,
29
+ },
30
+ ref,
31
+ ) => {
32
+ const [internalChecked, setInternalChecked] = useState(defaultChecked);
33
+ const isControlled = controlledChecked !== undefined;
34
+ const checked = isControlled ? controlledChecked : internalChecked;
35
+
36
+ const handleToggle = useCallback(() => {
37
+ if (disabled) return;
38
+ const next = !checked;
39
+ if (!isControlled) setInternalChecked(next);
40
+ onChange?.(next);
41
+ }, [checked, disabled, isControlled, onChange]);
42
+
43
+ return (
44
+ <div className={cn(styles.wrapper, disabled && styles.wrapperDisabled, className)}>
45
+ <button
46
+ ref={ref}
47
+ type="button"
48
+ role="switch"
49
+ aria-checked={checked}
50
+ disabled={disabled}
51
+ onClick={handleToggle}
52
+ className={cn(
53
+ styles.track,
54
+ styles[`track_${size}`],
55
+ checked && styles.trackChecked,
56
+ )}
57
+ >
58
+ <span
59
+ className={cn(
60
+ styles.thumb,
61
+ styles[`thumb_${size}`],
62
+ checked && styles.thumbChecked,
63
+ )}
64
+ />
65
+ </button>
66
+ {(label || hint) && (
67
+ <div className={styles.textWrap}>
68
+ {label && <span className={styles.label}>{label}</span>}
69
+ {hint && <span className={styles.hint}>{hint}</span>}
70
+ </div>
71
+ )}
72
+ </div>
73
+ );
74
+ },
75
+ );
76
+
77
+ Toggle.displayName = "Toggle";
@@ -0,0 +1,2 @@
1
+ export { Toggle } from "./Toggle";
2
+ export type { ToggleProps } from "./Toggle";
@@ -0,0 +1,95 @@
1
+ .wrapper {
2
+ position: relative;
3
+ display: inline-flex;
4
+ }
5
+
6
+ .tooltip {
7
+ position: absolute;
8
+ z-index: 50;
9
+ pointer-events: none;
10
+ animation: fadeIn 0.15s ease;
11
+ }
12
+
13
+ @keyframes fadeIn {
14
+ from { opacity: 0; transform: scale(0.96); }
15
+ to { opacity: 1; transform: scale(1); }
16
+ }
17
+
18
+ .content {
19
+ background: var(--color-bg-card);
20
+ border: 1px solid var(--color-border-standard);
21
+ border-radius: var(--radius-md);
22
+ padding: 6px 12px;
23
+ font-size: 12px;
24
+ font-weight: 500;
25
+ color: var(--color-text-primary);
26
+ white-space: nowrap;
27
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
28
+ backdrop-filter: blur(12px);
29
+ }
30
+
31
+ /* ── Positions ── */
32
+ .tooltip_top {
33
+ bottom: calc(100% + 8px);
34
+ left: 50%;
35
+ transform: translateX(-50%);
36
+ }
37
+
38
+ .tooltip_bottom {
39
+ top: calc(100% + 8px);
40
+ left: 50%;
41
+ transform: translateX(-50%);
42
+ }
43
+
44
+ .tooltip_left {
45
+ right: calc(100% + 8px);
46
+ top: 50%;
47
+ transform: translateY(-50%);
48
+ }
49
+
50
+ .tooltip_right {
51
+ left: calc(100% + 8px);
52
+ top: 50%;
53
+ transform: translateY(-50%);
54
+ }
55
+
56
+ /* ── Arrow ── */
57
+ .arrow {
58
+ position: absolute;
59
+ width: 8px;
60
+ height: 8px;
61
+ background: var(--color-bg-card);
62
+ border: 1px solid var(--color-border-standard);
63
+ }
64
+
65
+ .arrow_top {
66
+ bottom: -5px;
67
+ left: 50%;
68
+ transform: translateX(-50%) rotate(45deg);
69
+ border-top: none;
70
+ border-left: none;
71
+ }
72
+
73
+ .arrow_bottom {
74
+ top: -5px;
75
+ left: 50%;
76
+ transform: translateX(-50%) rotate(45deg);
77
+ border-bottom: none;
78
+ border-right: none;
79
+ }
80
+
81
+ .arrow_left {
82
+ right: -5px;
83
+ top: 50%;
84
+ transform: translateY(-50%) rotate(45deg);
85
+ border-bottom: none;
86
+ border-left: none;
87
+ }
88
+
89
+ .arrow_right {
90
+ left: -5px;
91
+ top: 50%;
92
+ transform: translateY(-50%) rotate(45deg);
93
+ border-top: none;
94
+ border-right: none;
95
+ }
@@ -0,0 +1,55 @@
1
+ "use client";
2
+
3
+ import { useState, useRef, useCallback, useEffect } from "react";
4
+ import { cn } from "@/lib/cn";
5
+ import styles from "./Tooltip.module.css";
6
+
7
+ export interface TooltipProps {
8
+ content: React.ReactNode;
9
+ children: React.ReactElement;
10
+ position?: "top" | "bottom" | "left" | "right";
11
+ delay?: number;
12
+ className?: string;
13
+ }
14
+
15
+ export function Tooltip({
16
+ content,
17
+ children,
18
+ position = "top",
19
+ delay = 200,
20
+ className,
21
+ }: TooltipProps) {
22
+ const [visible, setVisible] = useState(false);
23
+ const timeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);
24
+
25
+ const show = useCallback(() => {
26
+ timeoutRef.current = setTimeout(() => setVisible(true), delay);
27
+ }, [delay]);
28
+
29
+ const hide = useCallback(() => {
30
+ clearTimeout(timeoutRef.current);
31
+ setVisible(false);
32
+ }, []);
33
+
34
+ useEffect(() => {
35
+ return () => clearTimeout(timeoutRef.current);
36
+ }, []);
37
+
38
+ return (
39
+ <div
40
+ className={cn(styles.wrapper, className)}
41
+ onMouseEnter={show}
42
+ onMouseLeave={hide}
43
+ onFocus={show}
44
+ onBlur={hide}
45
+ >
46
+ {children}
47
+ {visible && (
48
+ <div className={cn(styles.tooltip, styles[`tooltip_${position}`])}>
49
+ <div className={styles.content}>{content}</div>
50
+ <div className={cn(styles.arrow, styles[`arrow_${position}`])} />
51
+ </div>
52
+ )}
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,2 @@
1
+ export { Tooltip } from "./Tooltip";
2
+ export type { TooltipProps } from "./Tooltip";
@@ -0,0 +1,120 @@
1
+ .player {
2
+ position: relative;
3
+ border-radius: var(--radius-lg);
4
+ overflow: hidden;
5
+ background: #000;
6
+ aspect-ratio: 16 / 9;
7
+ cursor: pointer;
8
+ }
9
+
10
+ .player_sm { max-width: 400px; }
11
+ .player_md { max-width: 640px; }
12
+ .player_lg { max-width: 960px; }
13
+
14
+ .video {
15
+ width: 100%;
16
+ height: 100%;
17
+ object-fit: cover;
18
+ display: block;
19
+ }
20
+
21
+ /* ── Thumbnail ── */
22
+ .thumbnail {
23
+ position: absolute;
24
+ inset: 0;
25
+ z-index: 2;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ cursor: pointer;
30
+ }
31
+
32
+ .thumbnailImg {
33
+ width: 100%;
34
+ height: 100%;
35
+ object-fit: cover;
36
+ }
37
+
38
+ .playBtnLarge {
39
+ position: absolute;
40
+ background: none;
41
+ border: none;
42
+ cursor: pointer;
43
+ transition: transform 0.2s;
44
+ }
45
+
46
+ .playBtnLarge:hover {
47
+ transform: scale(1.1);
48
+ }
49
+
50
+ /* ── Controls ── */
51
+ .controls {
52
+ position: absolute;
53
+ bottom: 0;
54
+ left: 0;
55
+ right: 0;
56
+ display: flex;
57
+ align-items: center;
58
+ gap: 8px;
59
+ padding: 10px 14px;
60
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
61
+ opacity: 0;
62
+ transition: opacity 0.25s;
63
+ z-index: 3;
64
+ }
65
+
66
+ .controlsVisible {
67
+ opacity: 1;
68
+ }
69
+
70
+ .controlBtn {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ width: 28px;
75
+ height: 28px;
76
+ border: none;
77
+ background: none;
78
+ color: #fff;
79
+ cursor: pointer;
80
+ border-radius: var(--radius-sm);
81
+ transition: background 0.15s;
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .controlBtn:hover {
86
+ background: rgba(255, 255, 255, 0.15);
87
+ }
88
+
89
+ .time {
90
+ font-size: 11px;
91
+ font-weight: 600;
92
+ color: rgba(255, 255, 255, 0.8);
93
+ white-space: nowrap;
94
+ min-width: 32px;
95
+ text-align: center;
96
+ font-variant-numeric: tabular-nums;
97
+ }
98
+
99
+ /* ── Progress ── */
100
+ .progressTrack {
101
+ flex: 1;
102
+ height: 4px;
103
+ border-radius: 999px;
104
+ background: rgba(255, 255, 255, 0.25);
105
+ cursor: pointer;
106
+ position: relative;
107
+ transition: height 0.15s;
108
+ }
109
+
110
+ .progressTrack:hover {
111
+ height: 6px;
112
+ }
113
+
114
+ .progressFill {
115
+ height: 100%;
116
+ border-radius: 999px;
117
+ background: var(--color-lime);
118
+ transition: width 0.1s linear;
119
+ pointer-events: none;
120
+ }