@enderfall/ui 0.1.0 → 0.1.4

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 (45) hide show
  1. package/assets/brand/enderfall-lockup.png +0 -0
  2. package/assets/brand/enderfall-lockup.svg +8 -0
  3. package/assets/brand/enderfall-mark.png +0 -0
  4. package/assets/brand/enderfall-mark.svg +8 -0
  5. package/dist/components/Button.d.ts +2 -1
  6. package/dist/components/Button.d.ts.map +1 -1
  7. package/dist/components/Button.js +8 -1
  8. package/dist/components/Dropdown.d.ts.map +1 -1
  9. package/dist/components/Dropdown.js +2 -2
  10. package/package.json +5 -2
  11. package/src/base.css +160 -0
  12. package/src/components/AccessGate.css +24 -0
  13. package/src/components/AccessGate.tsx +61 -0
  14. package/src/components/BookmarkDropdown.css +220 -0
  15. package/src/components/Button.css +183 -0
  16. package/src/components/Button.tsx +20 -0
  17. package/src/components/Dropdown.tsx +570 -0
  18. package/src/components/FloatingFooter.css +49 -0
  19. package/src/components/FloatingFooter.tsx +27 -0
  20. package/src/components/FormField.tsx +29 -0
  21. package/src/components/HeaderMenu.css +280 -0
  22. package/src/components/Input.css +68 -0
  23. package/src/components/Input.tsx +23 -0
  24. package/src/components/MainHeader.css +167 -0
  25. package/src/components/MainHeader.tsx +51 -0
  26. package/src/components/Modal.css +282 -0
  27. package/src/components/Modal.tsx +142 -0
  28. package/src/components/Panel.css +71 -0
  29. package/src/components/Panel.tsx +31 -0
  30. package/src/components/PreferencesModal.tsx +67 -0
  31. package/src/components/SideMenu.tsx +239 -0
  32. package/src/components/Slider.css +114 -0
  33. package/src/components/Slider.tsx +33 -0
  34. package/src/components/StackedCard.css +180 -0
  35. package/src/components/StackedCard.tsx +125 -0
  36. package/src/components/StatDots.css +122 -0
  37. package/src/components/StatDots.tsx +53 -0
  38. package/src/components/Tabs.css +108 -0
  39. package/src/components/Tabs.tsx +68 -0
  40. package/src/components/Toggle.css +161 -0
  41. package/src/components/Toggle.tsx +38 -0
  42. package/src/components/UserMenu.css +273 -0
  43. package/src/index.ts +45 -0
  44. package/src/theme.css +353 -0
  45. package/styles.css +1 -0
@@ -1,7 +1,8 @@
1
1
  import type { ButtonHTMLAttributes } from "react";
2
2
  type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
3
3
  variant?: "primary" | "ghost" | "locked" | "danger" | "delete" | "warning" | "info" | "success";
4
+ subvariant?: "default" | "glow";
4
5
  };
5
- export declare const Button: ({ variant, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const Button: ({ variant, subvariant, className, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
6
7
  export {};
7
8
  //# sourceMappingURL=Button.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD,KAAK,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAC3D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;CACjG,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,kCAA8C,WAAW,4CAK/E,CAAC"}
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElD,KAAK,WAAW,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAC3D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IAChG,UAAU,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,8CAAsE,WAAW,4CAYvG,CAAC"}
@@ -1,2 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- export const Button = ({ variant = "primary", className, ...props }) => (_jsx("button", { ...props, className: ["ef-button", variant, className].filter(Boolean).join(" ") }));
2
+ export const Button = ({ variant = "primary", subvariant = "default", className, ...props }) => (_jsx("button", { ...props, className: [
3
+ "ef-button",
4
+ variant,
5
+ subvariant === "glow" ? "ef-button--glow" : "",
6
+ className,
7
+ ]
8
+ .filter(Boolean)
9
+ .join(" ") }));
@@ -1 +1 @@
1
- {"version":3,"file":"Dropdown.d.ts","sourceRoot":"","sources":["../../src/components/Dropdown.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Dropdown.d.ts","sourceRoot":"","sources":["../../src/components/Dropdown.tsx"],"names":[],"mappings":"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"}
@@ -28,10 +28,10 @@ export const Dropdown = (props) => {
28
28
  props.onCloseMenu();
29
29
  }, closeDelayMs);
30
30
  };
31
- return (_jsx("div", { className: "ef-menu-bar", children: props.menus.map((menu) => (_jsxs("div", { className: "ef-menu-group", onMouseEnter: () => {
31
+ return (_jsx("div", { className: "ef-menu-bar", children: props.menus.map((menu) => (_jsxs("div", { className: "ef-menu-group", "data-open": props.menuOpen === menu.id ? "true" : "false", onMouseEnter: () => {
32
32
  cancelScheduledClose();
33
33
  props.onOpenMenu(menu.id);
34
- }, onMouseLeave: scheduleClose, children: [_jsx("button", { className: "ef-menu-button", type: "button", children: menu.label }), props.menuOpen === menu.id ? (_jsx("div", { className: "ef-menu-popover", "data-open": "true", onMouseEnter: () => {
34
+ }, onMouseLeave: scheduleClose, children: [_jsx("button", { className: "ef-menu-button", type: "button", "data-open": props.menuOpen === menu.id ? "true" : "false", children: menu.label }), props.menuOpen === menu.id ? (_jsx("div", { className: "ef-menu-popover", "data-open": "true", onMouseEnter: () => {
35
35
  cancelScheduledClose();
36
36
  props.onOpenMenu(menu.id);
37
37
  }, onMouseLeave: scheduleClose, children: menu.content })) : null] }, menu.id))) }));
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@enderfall/ui",
3
3
  "private": false,
4
- "version": "0.1.0",
4
+ "version": "0.1.4",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
+ "src",
11
+ "assets",
10
12
  "styles.css"
11
13
  ],
12
14
  "scripts": {
@@ -18,7 +20,8 @@
18
20
  "types": "./dist/index.d.ts",
19
21
  "import": "./dist/index.js"
20
22
  },
21
- "./styles.css": "./styles.css"
23
+ "./styles.css": "./styles.css",
24
+ "./assets/*": "./assets/*"
22
25
  },
23
26
  "peerDependencies": {
24
27
  "react": "^18.2.0"
package/src/base.css ADDED
@@ -0,0 +1,160 @@
1
+ .page {
2
+ min-height: 100vh;
3
+ padding: 42px clamp(20px, 6vw, 96px) 140px;
4
+ position: relative;
5
+ overflow: hidden;
6
+ text-align: left;
7
+ }
8
+
9
+ body {
10
+ margin: 0;
11
+ min-height: 100vh;
12
+ font-family: var(--font-main);
13
+ color: var(--text-strong);
14
+ background: var(--bg);
15
+ }
16
+
17
+ :root[data-reduce-motion="true"] *,
18
+ :root[data-reduce-motion="true"] *::before,
19
+ :root[data-reduce-motion="true"] *::after {
20
+ animation-duration: 0.001ms !important;
21
+ animation-iteration-count: 1 !important;
22
+ transition-duration: 0.001ms !important;
23
+ transition-delay: 0ms !important;
24
+ scroll-behavior: auto !important;
25
+ }
26
+
27
+ :root[data-reduce-motion="true"] body.ef-galaxy::after,
28
+ :root[data-reduce-motion="true"] body.ef-galaxy-light::after {
29
+ animation: none !important;
30
+ }
31
+
32
+ :root[data-theme="light"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action),
33
+ :root[data-theme="plain-light"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action),
34
+ :root[data-theme="galaxy"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action),
35
+ :root[data-theme="plain-dark"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action),
36
+ :root[data-theme="system"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action) {
37
+ border: 2px solid transparent;
38
+ border-radius: 8px;
39
+ padding: 10px 18px;
40
+ font-weight: 600;
41
+ cursor: pointer;
42
+ background:
43
+ linear-gradient(var(--ef-surface), var(--ef-surface)) padding-box,
44
+ var(--ef-border-gradient) border-box;
45
+ color: var(--text-strong);
46
+ box-shadow: var(--shadow);
47
+ }
48
+
49
+ :root[data-theme="light"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action):disabled,
50
+ :root[data-theme="plain-light"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action):disabled,
51
+ :root[data-theme="galaxy"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action):disabled,
52
+ :root[data-theme="plain-dark"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action):disabled,
53
+ :root[data-theme="system"] button:not(.ef-button):not(.ef-menu-button):not(.ef-menu-item):not(.user-button):not(.icon-action):disabled {
54
+ opacity: 0.6;
55
+ cursor: not-allowed;
56
+ box-shadow: none;
57
+ background:
58
+ linear-gradient(var(--ef-surface), var(--ef-surface)) padding-box,
59
+ var(--ef-border-gradient-soft) border-box;
60
+ }
61
+
62
+ body.ef-galaxy {
63
+ background:
64
+ radial-gradient(1600px 900px at 70% -15%, rgba(124, 77, 255, 0.25), transparent 70%),
65
+ radial-gradient(1200px 900px at 10% 20%, rgba(0, 229, 255, 0.18), transparent 70%),
66
+ radial-gradient(1300px 1000px at 85% 80%, rgba(255, 77, 210, 0.22), transparent 70%),
67
+ radial-gradient(1000px 800px at 20% 75%, rgba(255, 183, 77, 0.12), transparent 75%),
68
+ radial-gradient(900px 800px at 60% 40%, rgba(76, 255, 200, 0.1), transparent 70%),
69
+ linear-gradient(180deg, #000001 0%, #02020a 100%);
70
+ }
71
+
72
+ body.ef-galaxy::before {
73
+ content: "";
74
+ position: fixed;
75
+ inset: 0;
76
+ background:
77
+ radial-gradient(1800px 1000px at 50% 0%, rgba(88, 0, 255, 0.18), transparent 75%),
78
+ radial-gradient(1200px 900px at 15% 55%, rgba(0, 168, 255, 0.14), transparent 75%),
79
+ radial-gradient(1400px 1000px at 85% 45%, rgba(255, 0, 170, 0.18), transparent 75%),
80
+ radial-gradient(900px 700px at 60% 85%, rgba(255, 148, 60, 0.1), transparent 75%),
81
+ radial-gradient(1000px 900px at 40% 30%, rgba(0, 255, 150, 0.08), transparent 75%);
82
+ mix-blend-mode: screen;
83
+ opacity: 0.45;
84
+ pointer-events: none;
85
+ z-index: -1;
86
+ }
87
+
88
+ body.ef-galaxy::after {
89
+ content: "";
90
+ position: fixed;
91
+ inset: -20%;
92
+ background:
93
+ radial-gradient(2px 2px at 15% 20%, rgba(255, 255, 255, 0.85), transparent),
94
+ radial-gradient(2px 2px at 35% 45%, rgba(255, 255, 255, 0.75), transparent),
95
+ radial-gradient(2px 2px at 65% 30%, rgba(255, 255, 255, 0.7), transparent),
96
+ radial-gradient(2px 2px at 85% 60%, rgba(255, 255, 255, 0.7), transparent),
97
+ radial-gradient(1px 1px at 25% 85%, rgba(255, 255, 255, 0.6), transparent),
98
+ radial-gradient(1px 1px at 55% 10%, rgba(255, 255, 255, 0.6), transparent),
99
+ radial-gradient(1px 1px at 75% 90%, rgba(255, 255, 255, 0.6), transparent);
100
+ opacity: 0.35;
101
+ mix-blend-mode: screen;
102
+ pointer-events: none;
103
+ z-index: -1;
104
+ background-size: 420px 420px;
105
+ animation: star-drift 120s linear infinite;
106
+ }
107
+
108
+ body.ef-galaxy-light {
109
+ background:
110
+ radial-gradient(1600px 900px at 70% -15%, rgba(124, 77, 255, 0.25), transparent 70%),
111
+ radial-gradient(1200px 900px at 10% 20%, rgba(0, 229, 255, 0.18), transparent 70%),
112
+ radial-gradient(1300px 1000px at 85% 80%, rgba(255, 77, 210, 0.22), transparent 70%),
113
+ radial-gradient(1000px 800px at 20% 75%, rgba(255, 183, 77, 0.12), transparent 75%),
114
+ radial-gradient(900px 800px at 60% 40%, rgba(76, 255, 200, 0.1), transparent 70%),
115
+ linear-gradient(180deg, #f7f4ee 0%, #f2ece2 100%);
116
+ }
117
+
118
+ body.ef-galaxy-light::before {
119
+ content: "";
120
+ position: fixed;
121
+ inset: 0;
122
+ background:
123
+ radial-gradient(1800px 1000px at 50% 0%, rgba(88, 0, 255, 0.18), transparent 75%),
124
+ radial-gradient(1200px 900px at 15% 55%, rgba(0, 168, 255, 0.14), transparent 75%),
125
+ radial-gradient(1400px 1000px at 85% 45%, rgba(255, 0, 170, 0.18), transparent 75%),
126
+ radial-gradient(900px 700px at 60% 85%, rgba(255, 148, 60, 0.1), transparent 75%),
127
+ radial-gradient(1000px 900px at 40% 30%, rgba(0, 255, 150, 0.08), transparent 75%);
128
+ opacity: 0.45;
129
+ pointer-events: none;
130
+ z-index: -1;
131
+ }
132
+
133
+ body.ef-galaxy-light::after {
134
+ content: "";
135
+ position: fixed;
136
+ inset: -20%;
137
+ background:
138
+ radial-gradient(2px 2px at 15% 20%, rgba(0, 0, 0, 0.95), transparent),
139
+ radial-gradient(2px 2px at 35% 45%, rgba(0, 0, 0, 0.9), transparent),
140
+ radial-gradient(2px 2px at 65% 30%, rgba(0, 0, 0, 0.85), transparent),
141
+ radial-gradient(2px 2px at 85% 60%, rgba(0, 0, 0, 0.85), transparent),
142
+ radial-gradient(1px 1px at 25% 85%, rgba(0, 0, 0, 0.75), transparent),
143
+ radial-gradient(1px 1px at 55% 10%, rgba(0, 0, 0, 0.75), transparent),
144
+ radial-gradient(1px 1px at 75% 90%, rgba(0, 0, 0, 0.75), transparent);
145
+ opacity: 0.55;
146
+ pointer-events: none;
147
+ z-index: -1;
148
+ background-size: 420px 420px;
149
+ animation: star-drift 120s linear infinite;
150
+ }
151
+
152
+ @keyframes star-drift {
153
+ 0% {
154
+ transform: translate3d(0, 0, 0);
155
+ }
156
+ 100% {
157
+ transform: translate3d(-220px, -160px, 0);
158
+ }
159
+ }
160
+
@@ -0,0 +1,24 @@
1
+ .ef-access-overlay {
2
+ position: fixed;
3
+ inset: 0;
4
+ background: rgba(8, 10, 16, 0.72);
5
+ display: grid;
6
+ place-items: center;
7
+ z-index: 999;
8
+ }
9
+
10
+ .ef-access-card {
11
+ width: min(520px, 90vw);
12
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
13
+ }
14
+
15
+ .ef-access-title {
16
+ font-size: 1.1rem;
17
+ font-weight: 600;
18
+ }
19
+
20
+ .ef-access-actions {
21
+ display: flex;
22
+ gap: 12px;
23
+ flex-wrap: wrap;
24
+ }
@@ -0,0 +1,61 @@
1
+ import { Button } from "./Button";
2
+ import { Panel } from "./Panel";
3
+
4
+ type AccessGateProps = {
5
+ status: "checking" | "locked" | "allowed";
6
+ titleChecking?: string;
7
+ titleLocked?: string;
8
+ messageChecking?: string;
9
+ messageLocked?: string;
10
+ primaryLabel: string;
11
+ secondaryLabel?: string;
12
+ onPrimary: () => void;
13
+ onSecondary?: () => void;
14
+ primaryClassName?: string;
15
+ secondaryClassName?: string;
16
+ };
17
+
18
+ export const AccessGate = ({
19
+ status,
20
+ titleChecking = "Checking access...",
21
+ titleLocked = "App locked",
22
+ messageChecking = "Verifying your access with Enderfall Hub.",
23
+ messageLocked = "Open Enderfall Hub to verify premium or admin access.",
24
+ primaryLabel,
25
+ secondaryLabel,
26
+ onPrimary,
27
+ onSecondary,
28
+ primaryClassName = "primary",
29
+ secondaryClassName = "ghost",
30
+ }: AccessGateProps) => {
31
+ if (status === "allowed") return null;
32
+ const isChecking = status === "checking";
33
+ return (
34
+ <div className="ef-access-overlay">
35
+ <Panel variant="card" borderWidth={2} className="ef-access-card">
36
+ <div className="ef-access-title">{isChecking ? titleChecking : titleLocked}</div>
37
+ <p>{isChecking ? messageChecking : messageLocked}</p>
38
+ <div className="ef-access-actions">
39
+ <Button
40
+ type="button"
41
+ className={primaryClassName}
42
+ onClick={onPrimary}
43
+ disabled={isChecking}
44
+ >
45
+ {primaryLabel}
46
+ </Button>
47
+ {secondaryLabel && onSecondary ? (
48
+ <Button
49
+ type="button"
50
+ className={secondaryClassName}
51
+ onClick={onSecondary}
52
+ disabled={isChecking}
53
+ >
54
+ {secondaryLabel}
55
+ </Button>
56
+ ) : null}
57
+ </div>
58
+ </Panel>
59
+ </div>
60
+ );
61
+ };
@@ -0,0 +1,220 @@
1
+ .bookmark-row {
2
+ display: grid;
3
+ gap: 8px;
4
+ }
5
+
6
+ .bookmark-row label {
7
+ display: grid;
8
+ gap: 8px;
9
+ font-weight: 600;
10
+ color: var(--muted);
11
+ font-size: 0.85rem;
12
+ }
13
+
14
+ .bookmark-label {
15
+ font-size: 0.8rem;
16
+ font-weight: 700;
17
+ color: var(--muted);
18
+ text-transform: uppercase;
19
+ letter-spacing: 0.08em;
20
+ }
21
+
22
+ .bookmark-field {
23
+ position: relative;
24
+ }
25
+
26
+ .bookmark-dropdown {
27
+ position: relative;
28
+ }
29
+
30
+ .bookmark-trigger {
31
+ width: 100%;
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 10px;
35
+ justify-content: space-between;
36
+ padding: 10px 12px;
37
+ min-height: 40px;
38
+ border-radius: 8px;
39
+ border: 2px solid transparent;
40
+ background:
41
+ linear-gradient(var(--ef-input-surface), var(--ef-input-surface)) padding-box,
42
+ var(--ef-input-border) border-box;
43
+ box-shadow: none;
44
+ }
45
+
46
+ .bookmark-trigger:hover {
47
+ transform: none;
48
+ }
49
+
50
+ .bookmark-icon {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ color: var(--ink);
55
+ }
56
+
57
+ .bookmark-item .bookmark-icon {
58
+ width: var(--view-icon-size, 16px);
59
+ height: var(--view-icon-size, 16px);
60
+ flex: 0 0 var(--view-icon-size, 16px);
61
+ }
62
+
63
+ .bookmark-icon svg {
64
+ width: 16px;
65
+ height: 16px;
66
+ }
67
+
68
+ .bookmark-icon .view-icon {
69
+ width: var(--view-icon-size, 16px);
70
+ height: var(--view-icon-size, 16px);
71
+ display: inline-flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ color: var(--ink);
75
+ }
76
+
77
+ .bookmark-icon .view-icon svg {
78
+ width: 100%;
79
+ height: 100%;
80
+ }
81
+
82
+ .bookmark-trigger .bookmark-icon {
83
+ width: var(--view-icon-size, 16px);
84
+ height: var(--view-icon-size, 16px);
85
+ flex: 0 0 var(--view-icon-size, 16px);
86
+ }
87
+
88
+ .bookmark-trigger .bookmark-icon svg {
89
+ width: 100%;
90
+ height: 100%;
91
+ }
92
+
93
+ .bookmark-trigger .bookmark-icon .view-icon {
94
+ width: var(--view-icon-size, 16px);
95
+ height: var(--view-icon-size, 16px);
96
+ }
97
+
98
+ .bookmark-trigger .bookmark-icon .view-icon svg {
99
+ width: 100%;
100
+ height: 100%;
101
+ }
102
+
103
+ .bookmark-text {
104
+ flex: 1;
105
+ text-align: left;
106
+ font-weight: 600;
107
+ color: var(--text);
108
+ min-width: 0;
109
+ overflow: hidden;
110
+ text-overflow: ellipsis;
111
+ white-space: nowrap;
112
+ }
113
+
114
+ .bookmark-caret {
115
+ color: var(--muted);
116
+ display: inline-flex;
117
+ align-items: center;
118
+ }
119
+
120
+ .bookmark-caret svg {
121
+ width: 14px;
122
+ height: 14px;
123
+ }
124
+
125
+ .bookmark-menu {
126
+ position: absolute;
127
+ left: 0;
128
+ right: 0;
129
+ top: calc(100% + 6px);
130
+ background:
131
+ linear-gradient(var(--ef-input-surface), var(--ef-input-surface)) padding-box,
132
+ var(--ef-input-border) border-box;
133
+ border-radius: 8px;
134
+ border: 2px solid transparent;
135
+ box-shadow: var(--shadow);
136
+ max-height: 240px;
137
+ overflow: auto;
138
+ padding: 6px;
139
+ z-index: 20;
140
+ opacity: 0;
141
+ transform: translateY(-4px);
142
+ transition: opacity 0.14s ease, transform 0.14s ease;
143
+ pointer-events: none;
144
+ }
145
+
146
+ .bookmark-dropdown.open .bookmark-menu {
147
+ opacity: 1;
148
+ transform: translateY(0);
149
+ pointer-events: auto;
150
+ }
151
+
152
+ .bookmark-empty {
153
+ padding: 8px 10px;
154
+ color: var(--muted);
155
+ font-weight: 600;
156
+ font-size: 0.85rem;
157
+ }
158
+
159
+ .bookmark-item {
160
+ width: 100%;
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 10px;
164
+ padding: 8px 10px;
165
+ border-radius: 8px;
166
+ background: transparent;
167
+ border: 1px solid transparent;
168
+ box-shadow: none;
169
+ margin-bottom: 6px;
170
+ }
171
+
172
+ .bookmark-item:last-child {
173
+ margin-bottom: 0;
174
+ }
175
+
176
+ .bookmark-item:hover {
177
+ border-color: rgba(31, 122, 140, 0.2);
178
+ transform: none;
179
+ }
180
+
181
+ .bookmark-item.active {
182
+ border-color: var(--accent);
183
+ background: var(--menu-bg-soft);
184
+ }
185
+
186
+ .bookmark-item.active .bookmark-icon {
187
+ color: var(--ink);
188
+ }
189
+
190
+ .bookmark-group {
191
+ padding: 6px 10px 4px;
192
+ font-size: 0.7rem;
193
+ font-weight: 700;
194
+ color: var(--muted);
195
+ text-transform: uppercase;
196
+ letter-spacing: 0.08em;
197
+ }
198
+
199
+ :root[data-theme="light"] .bookmark-trigger,
200
+ :root[data-theme="light"] .bookmark-menu,
201
+ :root[data-theme="plain-light"] .bookmark-trigger,
202
+ :root[data-theme="plain-light"] .bookmark-menu,
203
+ :root[data-theme="galaxy"] .bookmark-trigger,
204
+ :root[data-theme="galaxy"] .bookmark-menu,
205
+ :root[data-theme="plain-dark"] .bookmark-trigger,
206
+ :root[data-theme="plain-dark"] .bookmark-menu,
207
+ :root[data-theme="system"] .bookmark-trigger,
208
+ :root[data-theme="system"] .bookmark-menu {
209
+ background:
210
+ linear-gradient(var(--ef-input-surface), var(--ef-input-surface)) padding-box,
211
+ var(--ef-input-border) border-box;
212
+ border-color: transparent;
213
+ }
214
+
215
+ :root[data-theme="atelier"] .bookmark-trigger,
216
+ :root[data-theme="atelier"] .bookmark-menu,
217
+ :root[data-theme="atelier"] .bookmark-item {
218
+ border-radius: 0;
219
+ }
220
+