@stacksjs/defaults 0.75.219 → 0.75.221

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.
@@ -233,6 +233,12 @@ buddy env:check # validate env configuration
233
233
  buddy env:check --file .env.production # check a specific environment's file
234
234
  ```
235
235
 
236
+ `getEnv()` fails a whole-file read if any encrypted entry cannot be decrypted.
237
+ Restore the environment's matching private key through `.env.keys` or its
238
+ `DOTENV_PRIVATE_KEY_<ENVIRONMENT>` process secret. A request for one plaintext
239
+ key remains readable independently. `buddy deploy` stops on an incomplete or
240
+ unreadable environment instead of deploying a partial map.
241
+
236
242
  ## Tenant isolation on a shared box
237
243
 
238
244
  When several projects share one server - one owns it, the rest attach with
@@ -83,6 +83,28 @@ Based on `cz-git` UserConfig:
83
83
 
84
84
  ## Git Utilities
85
85
 
86
+ `gitCredentialEnvironment({ url, username, password })` from `@stacksjs/git`
87
+ returns environment variables for a child Git process. Pass the caller's base
88
+ environment as its second argument, then merge the result after that base.
89
+ Existing runtime Git configuration is preserved. The HTTP(S) URL must identify one repository without
90
+ userinfo, query or fragment. The helper supplies credentials only for that
91
+ protocol, host and repository path, refuses redirects for that URL, disables
92
+ interactive prompts, and writes no credential or Git config file. Never log
93
+ the returned environment. Use a credential scoped to the repository and the
94
+ operation; this helper does not grant server permissions.
95
+
96
+ ```typescript
97
+ import { gitCredentialEnvironment } from '@stacksjs/git'
98
+
99
+ const child = Bun.spawn(['git', 'ls-remote', 'origin'], {
100
+ cwd: workspace,
101
+ env: { ...process.env, ...gitCredentialEnvironment({ url: repositoryUrl, username: 'x-access-token', password: jobToken }, process.env) },
102
+ })
103
+ ```
104
+
105
+ The child and its descendants receive the credential. Use this for trusted
106
+ job steps, never to transfer a host credential into an isolated guest.
107
+
86
108
  `buddy release` atomically pushes its commit and version tag to the branch's
87
109
  configured upstream remote and branch. A worktree on `codex/example` tracking
88
110
  `origin/main` releases to `origin/main`; it does not create a remote feature
@@ -21,7 +21,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
21
21
  | `@stacksjs/auth` | [`stacks-auth`](../stacks-auth/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/auth/src/index.ts) | 115 |
22
22
  | `@stacksjs/browser` | [`stacks-browser`](../stacks-browser/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser/src/index.ts) | 8 |
23
23
  | `@stacksjs/browser-extension` | [`stacks-browser-extension`](../stacks-browser-extension/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser-extension/src/index.ts) | 7 |
24
- | `@stacksjs/buddy` | [`stacks-buddy`](../stacks-buddy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/index.ts) | 149 |
24
+ | `@stacksjs/buddy` | [`stacks-buddy`](../stacks-buddy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/index.ts) | 150 |
25
25
  | `@stacksjs/build` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/build/src/index.ts) | 2 |
26
26
  | `@stacksjs/cache` | [`stacks-cache`](../stacks-cache/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/index.ts) | 11 |
27
27
  | `@stacksjs/calendar-api` | [`stacks-calendar`](../stacks-calendar/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/index.ts) | 2 |
@@ -45,14 +45,14 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
45
45
  | `@stacksjs/docs` | [`stacks-docs`](../stacks-docs/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/docs/src/index.ts) | 2 |
46
46
  | `@stacksjs/email` | [`stacks-email`](../stacks-email/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/index.ts) | 21 |
47
47
  | `@stacksjs/enums` | [`stacks-enums`](../stacks-enums/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/enums/src/index.ts) | 3 |
48
- | `@stacksjs/env` | [`stacks-env`](../stacks-env/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/index.ts) | 27 |
48
+ | `@stacksjs/env` | [`stacks-env`](../stacks-env/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/index.ts) | 28 |
49
49
  | `@stacksjs/error-handling` | [`stacks-error-handling`](../stacks-error-handling/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/error-handling/src/index.ts) | 10 |
50
50
  | `@stacksjs/events` | [`stacks-events`](../stacks-events/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/events/src/index.ts) | 12 |
51
51
  | `@stacksjs/faker` | [`stacks-faker`](../stacks-faker/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/faker/src/index.ts) | 1 |
52
52
  | `@stacksjs/feature-flags` | [`stacks-feature-flags`](../stacks-feature-flags/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/feature-flags/src/index.ts) | 7 |
53
53
  | `@stacksjs/features` | [`stacks-features`](../stacks-features/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/features/src/index.ts) | 1 |
54
54
  | `@stacksjs/forms` | [`stacks-forms`](../stacks-forms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/forms/src/index.ts) | 4 |
55
- | `@stacksjs/git` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/index.ts) | 1 |
55
+ | `@stacksjs/git` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/index.ts) | 2 |
56
56
  | `@stacksjs/github` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/github/src/index.ts) | 7 |
57
57
  | `@stacksjs/health` | [`stacks-health`](../stacks-health/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/health/src/index.ts) | 2 |
58
58
  | `@stacksjs/http` | [`stacks-http`](../stacks-http/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/http/src/index.ts) | 2 |
@@ -92,7 +92,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`.
92
92
  | `@stacksjs/tinker` | [`stacks-repl`](../stacks-repl/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tinker/src/index.ts) | 1 |
93
93
  | `@stacksjs/tunnel` | [`stacks-tunnel`](../stacks-tunnel/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tunnel/src/index.ts) | 1 |
94
94
  | `@stacksjs/types` | [`stacks-types`](../stacks-types/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/types/src/index.ts) | 2 |
95
- | `@stacksjs/ui` | [`stacks-ui`](../stacks-ui/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/index.ts) | 5 |
95
+ | `@stacksjs/ui` | [`stacks-ui`](../stacks-ui/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/index.ts) | 6 |
96
96
  | `@stacksjs/utils` | [`stacks-utils`](../stacks-utils/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/utils/src/index.ts) | 15 |
97
97
  | `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 17 |
98
98
  | `@stacksjs/video` | [`stacks-video`](../stacks-video/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/video/src/index.ts) | 3 |
@@ -215,7 +215,7 @@ Read the entrypoint and package export map for re-exported or type-only surfaces
215
215
 
216
216
  Local source modules: [src/commands/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/commands/index.ts).
217
217
 
218
- Evidence directory: [149 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
218
+ Evidence directory: [150 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
219
219
 
220
220
  ### @stacksjs/build
221
221
 
@@ -443,7 +443,7 @@ Read the entrypoint and package export map for re-exported or type-only surfaces
443
443
 
444
444
  Local source modules: [src/cli.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/cli.ts), [src/crypto.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/crypto.ts), [src/deployment.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/deployment.ts), [src/integrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/integrations.ts), [src/numeric-keys.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/numeric-keys.ts), [src/parser.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/parser.ts), [src/plaintext.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/plaintext.ts), [src/plugin.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/plugin.ts), [src/runtime.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/runtime.ts), [src/tenants.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/tenants.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/types.ts), [src/utils.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/utils.ts).
445
445
 
446
- Evidence directory: [27 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
446
+ Evidence directory: [28 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
447
447
 
448
448
  ### @stacksjs/error-handling
449
449
 
@@ -509,7 +509,9 @@ Guidance: [`stacks-git`](../stacks-git/SKILL.md). Source: [storage/framework/cor
509
509
 
510
510
  Read the entrypoint and package export map for re-exported or type-only surfaces.
511
511
 
512
- Evidence directory: [1 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
512
+ Local source modules: [src/credentials.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/credentials.ts).
513
+
514
+ Evidence directory: [2 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
513
515
 
514
516
  ### @stacksjs/github
515
517
 
@@ -887,7 +889,7 @@ Declared runtime exports: `CssEngine`, `buildPageSequence`, `paginatorVariant`,
887
889
 
888
890
  Local source modules: [src/fonts.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/fonts.ts), [src/pagination.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/pagination.ts).
889
891
 
890
- Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
892
+ Evidence directory: [6 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/tests). These files are evidence pointers, not a claim that all tests were run for this catalog.
891
893
 
892
894
  ### @stacksjs/utils
893
895
 
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.219",
5
+ "version": "0.75.221",
6
6
  "description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
7
7
  "license": "MIT",
8
8
  "funding": "https://github.com/sponsors/chrisbbreuer",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/defaults",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.219",
5
+ "version": "0.75.221",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@iconify-json/f7": "^1.2.2",
46
46
  "@iconify-json/hugeicons": "^1.2.35",
47
- "@stacksjs/mobile": "0.75.219",
47
+ "@stacksjs/mobile": "0.75.221",
48
48
  "@stacksjs/sanitizer": "^0.2.534",
49
49
  "@xterm/addon-fit": "^0.11.0",
50
50
  "@xterm/xterm": "^6.0.0",
@@ -12,40 +12,93 @@
12
12
  const mcReplay = useReactiveProp<number>('replay', 0)
13
13
  const mcReveals = derived(() => [mcReplay()])
14
14
  const mcCelebrate = useReactiveProp<boolean>('celebrate', true)
15
+ const mcSurface = useRef('membershipSurface')
16
+ const mcCanHover = useMediaQuery('(hover: hover) and (pointer: fine)')
17
+ const mcReduceMotion = usePreferredReducedMotion()
18
+ let mcStopHover: (() => void) | undefined
19
+
20
+ onMount(() => {
21
+ const surface = mcSurface.current as HTMLElement | null
22
+ if (!surface) return
23
+
24
+ // Measure the stationary hit area, never the rotating card, to avoid feedback jitter.
25
+ const reset = () => {
26
+ surface.style.setProperty('--mc-rotate-x', '0deg')
27
+ surface.style.setProperty('--mc-rotate-y', '0deg')
28
+ surface.style.setProperty('--mc-light-x', '50%')
29
+ surface.style.setProperty('--mc-light-y', '50%')
30
+ surface.style.setProperty('--mc-hover', '0')
31
+ }
32
+ mcStopHover = effect(() => {
33
+ reset()
34
+ if (!mcCanHover() || mcReduceMotion()) return
35
+ const mouse = useMouse({ target: surface, touch: false, resetOnLeave: true })
36
+ const unsubscribe = mouse.subscribe(({ clientX, clientY, isInside }) => {
37
+ if (!isInside) return reset()
38
+ const bounds = surface.getBoundingClientRect()
39
+ if (!bounds.width || !bounds.height) return reset()
40
+ const x = Math.max(0, Math.min(1, (clientX - bounds.left) / bounds.width))
41
+ const y = Math.max(0, Math.min(1, (clientY - bounds.top) / bounds.height))
42
+ surface.style.setProperty('--mc-rotate-x', `${(0.5 - y) * 12}deg`)
43
+ surface.style.setProperty('--mc-rotate-y', `${(x - 0.5) * 16}deg`)
44
+ surface.style.setProperty('--mc-light-x', `${x * 100}%`)
45
+ surface.style.setProperty('--mc-light-y', `${y * 100}%`)
46
+ surface.style.setProperty('--mc-hover', '1')
47
+ })
48
+ return () => {
49
+ unsubscribe()
50
+ mouse.stop()
51
+ reset()
52
+ }
53
+ })
54
+ })
55
+ onDestroy(() => mcStopHover?.())
56
+
15
57
  const mcPieces = [...Array(32).keys()].map(id => ({
16
58
  id,
17
59
  style: `--mc-x:${(id * 37 + 11) % 100}%;--mc-drift:${(id * 29 % 160) - 80}px;--mc-delay:${id % 8 * 65}ms;--mc-turn:${180 + id * 47}deg;--mc-color:var(--membership-card-confetti-${id % 4 + 1},${['#99f6e4', '#e2e8f0', '#5eead4', '#94a3b8'][id % 4]})`,
18
60
  }))
19
61
  </script>
20
62
 
21
- <div :for="reveal in mcReveals" :key="reveal" class="relative mx-auto w-full mc-stage" :class="mcCelebrate ? 'mc-celebrate' : ''" data-membership-reveal>
22
- <div :if="mcCelebrate" class="absolute inset-0 overflow-hidden pointer-events-none mc-confetti" aria-hidden="true">
23
- <span :for="piece in mcPieces" :key="piece.id" class="mc-piece" :style="piece.style"></span>
24
- </div>
25
- <article class="overflow-hidden relative mx-auto w-full mc-metal" :aria-label="mcLabel" data-membership-card>
26
- <div class="absolute inset-0 pointer-events-none mc-grain" aria-hidden="true"></div>
27
- <div class="absolute inset-0 pointer-events-none mc-shine" aria-hidden="true"></div>
28
- <div class="flex relative gap-4 items-start justify-between">
29
- <img :if="mcLogo" class="mc-logo" :src="mcLogo" :alt="mcBrand" width="96" height="38" />
30
- <span :if="!mcLogo" class="font-bold mc-wordmark" :text="mcBrand"></span>
31
- </div>
32
- <p class="relative mt-3 text-xs mc-label" :text="mcLabel"></p>
33
- <div class="relative mc-holder">
34
- <div class="mc-person">
35
- <p class="mb-2 text-xs mc-label" :text="mcNameLabel"></p>
36
- <p class="mc-engraving" :style="{ fontFamily: mcNameFont }" :text="mcName" data-membership-name></p>
63
+ <div class="relative mx-auto w-full mc-stage" data-membership-reveal>
64
+ <div ref="membershipSurface" class="mc-surface">
65
+ <div :for="reveal in mcReveals" :key="reveal" :class="mcCelebrate ? 'mc-celebrate' : ''">
66
+ <div :if="mcCelebrate" class="absolute inset-0 overflow-hidden pointer-events-none mc-confetti" aria-hidden="true">
67
+ <span :for="piece in mcPieces" :key="piece.id" class="mc-piece" :style="piece.style"></span>
37
68
  </div>
38
- <div :if="mcMemberId" class="mc-identity">
39
- <p class="text-xs mc-label" :text="mcMemberIdLabel"></p>
40
- <p class="font-semibold mc-serial" :text="mcMemberId" data-membership-id></p>
69
+ <div class="mc-tilt">
70
+ <article class="overflow-hidden relative mx-auto w-full mc-metal" :aria-label="mcLabel" data-membership-card>
71
+ <div class="absolute inset-0 pointer-events-none mc-grain" aria-hidden="true"></div>
72
+ <div class="absolute inset-0 pointer-events-none mc-reflection" aria-hidden="true"></div>
73
+ <div class="absolute inset-0 pointer-events-none mc-shine" aria-hidden="true"></div>
74
+ <div class="flex relative gap-4 items-start justify-between">
75
+ <img :if="mcLogo" class="mc-logo" :src="mcLogo" :alt="mcBrand" width="96" height="38" />
76
+ <span :if="!mcLogo" class="font-bold mc-wordmark" :text="mcBrand"></span>
77
+ </div>
78
+ <p class="relative mt-3 text-xs mc-label" :text="mcLabel"></p>
79
+ <div class="relative mc-holder">
80
+ <div class="mc-person">
81
+ <p class="mb-2 text-xs mc-label" :text="mcNameLabel"></p>
82
+ <p class="mc-engraving" :style="{ fontFamily: mcNameFont }" :text="mcName" data-membership-name></p>
83
+ </div>
84
+ <div :if="mcMemberId" class="mc-identity">
85
+ <p class="text-xs mc-label" :text="mcMemberIdLabel"></p>
86
+ <p class="font-semibold mc-serial" :text="mcMemberId" data-membership-id></p>
87
+ </div>
88
+ </div>
89
+ <p :if="mcDetail" class="relative mt-5 text-xs mc-detail" :text="mcDetail"></p>
90
+ </article>
41
91
  </div>
42
92
  </div>
43
- <p :if="mcDetail" class="relative mt-5 text-xs mc-detail" :text="mcDetail"></p>
44
- </article>
93
+ </div>
45
94
  </div>
46
95
 
47
96
  <style>
48
97
  .mc-stage { max-width: 28rem; padding: 2rem 0 2.5rem; perspective: 1000px; }
98
+ .mc-surface { isolation: isolate; }
99
+ .mc-tilt { transform: perspective(1000px) rotateX(var(--mc-rotate-x, 0deg)) rotateY(var(--mc-rotate-y, 0deg)) translateY(calc(var(--mc-hover, 0) * -3px)); transition: transform 450ms cubic-bezier(0.2, 0.75, 0.25, 1); }
100
+ .mc-surface:hover .mc-tilt { transition-duration: 160ms; }
101
+ .mc-reflection { opacity: calc(var(--mc-hover, 0) * 0.65); background: radial-gradient(ellipse at var(--mc-light-x, 50%) var(--mc-light-y, 50%), #ffffff38, #ffffff0a 42%, transparent 72%); transition: opacity 350ms ease; }
49
102
  .mc-metal {
50
103
  box-sizing: border-box; min-height: 15.5rem; padding: clamp(1.25rem, 5vw, 2rem); border-radius: 1.25rem;
51
104
  color: var(--membership-card-color, #e2e8f0); border: 1px solid var(--membership-card-border, #87939d);
@@ -79,10 +132,14 @@
79
132
  .mc-celebrate .mc-metal { animation: mc-arrive 1100ms cubic-bezier(0.16, 1, 0.3, 1) both; }
80
133
  .mc-celebrate .mc-shine { animation: mc-polish 1700ms ease-in-out 650ms both; }
81
134
  .mc-celebrate .mc-piece { animation: mc-fall 2400ms cubic-bezier(0.2, 0.65, 0.45, 1) var(--mc-delay) both; }
82
- .mc-metal:hover .mc-shine { transform: translateX(150%) skewX(-20deg); transition: transform 1400ms ease; }
135
+ }
136
+ @media (hover: none), (pointer: coarse) {
137
+ .mc-tilt { transform: none; transition: none; }
138
+ .mc-reflection { display: none; }
83
139
  }
84
140
  @media (prefers-reduced-motion: reduce) {
85
- .mc-confetti, .mc-shine { display: none; }
141
+ .mc-confetti, .mc-shine, .mc-reflection { display: none; }
142
+ .mc-tilt { transform: none; transition: none; }
86
143
  }
87
144
  @keyframes mc-arrive { from { opacity: 0; transform: translateY(32px) rotateX(16deg) rotateY(-12deg) scale(0.92); } to { opacity: 1; transform: none; } }
88
145
  @keyframes mc-polish { from { transform: translateX(-150%) skewX(-20deg); } to { transform: translateX(150%) skewX(-20deg); } }