@redot-src/glare 1.0.0 → 1.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 (63) hide show
  1. package/README.md +40 -8
  2. package/dist/core/animation.d.ts +7 -0
  3. package/dist/core/bind.d.ts +8 -0
  4. package/dist/core/dom.d.ts +18 -0
  5. package/dist/core/glare.d.ts +65 -0
  6. package/dist/core/idle.d.ts +9 -0
  7. package/dist/core/interactions.d.ts +3 -0
  8. package/dist/core/loaders.d.ts +5 -0
  9. package/dist/core/options.d.ts +5 -0
  10. package/dist/core/registry.d.ts +9 -0
  11. package/dist/core/zoom.d.ts +36 -0
  12. package/dist/defaults.d.ts +5 -5
  13. package/dist/esm.d.ts +3 -0
  14. package/dist/glare.css +1 -1
  15. package/dist/glare.esm.js +1038 -1171
  16. package/dist/glare.esm.js.map +1 -1
  17. package/dist/glare.js +25 -23
  18. package/dist/glare.js.map +1 -1
  19. package/dist/i18n.d.ts +4 -0
  20. package/dist/icons.d.ts +12 -13
  21. package/dist/index.d.ts +5 -7
  22. package/dist/media/detect.d.ts +4 -0
  23. package/dist/media/items.d.ts +6 -0
  24. package/dist/media/providers.d.ts +6 -0
  25. package/dist/modules/fullscreen.d.ts +15 -0
  26. package/dist/modules/gestures.d.ts +45 -0
  27. package/dist/modules/hash.d.ts +22 -0
  28. package/dist/modules/share.d.ts +17 -0
  29. package/dist/modules/slideshow.d.ts +25 -0
  30. package/dist/modules/thumbs.d.ts +22 -0
  31. package/dist/modules/wheel.d.ts +19 -0
  32. package/dist/templates.d.ts +7 -0
  33. package/dist/types.d.ts +117 -131
  34. package/dist/umd.d.ts +3 -0
  35. package/dist/utils/dom.d.ts +20 -0
  36. package/dist/utils/env.d.ts +5 -0
  37. package/dist/utils/object.d.ts +3 -0
  38. package/dist/utils/template.d.ts +5 -0
  39. package/package.json +18 -24
  40. package/dist/defaults.d.ts.map +0 -1
  41. package/dist/fullscreen.d.ts +0 -14
  42. package/dist/fullscreen.d.ts.map +0 -1
  43. package/dist/gestures.d.ts +0 -24
  44. package/dist/gestures.d.ts.map +0 -1
  45. package/dist/hash.d.ts +0 -20
  46. package/dist/hash.d.ts.map +0 -1
  47. package/dist/icons.d.ts.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/instance.d.ts +0 -103
  50. package/dist/instance.d.ts.map +0 -1
  51. package/dist/media.d.ts +0 -5
  52. package/dist/media.d.ts.map +0 -1
  53. package/dist/share.d.ts +0 -13
  54. package/dist/share.d.ts.map +0 -1
  55. package/dist/slideshow.d.ts +0 -19
  56. package/dist/slideshow.d.ts.map +0 -1
  57. package/dist/thumbs.d.ts +0 -17
  58. package/dist/thumbs.d.ts.map +0 -1
  59. package/dist/types.d.ts.map +0 -1
  60. package/dist/utils.d.ts +0 -31
  61. package/dist/utils.d.ts.map +0 -1
  62. package/dist/wheel.d.ts +0 -11
  63. package/dist/wheel.d.ts.map +0 -1
package/README.md CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  Modern, touch-enabled lightbox for the web. **Zero dependencies.** MIT licensed.
4
4
 
5
- Open images, HTML5 video, YouTube/Vimeo, maps, iframes, inline nodes, AJAX fragments, and custom HTML — with galleries, zoom, thumbnails, slideshow, fullscreen, deep links, and a polished toolbar.
5
+ Open images, HTML5 video, YouTube/Vimeo, maps, iframes, inline nodes, AJAX fragments, and custom HTML, with galleries, zoom, thumbnails, slideshow, fullscreen, deep links, and a polished toolbar.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Vanilla JS / TypeScript** — no jQuery, no framework lock-in
9
+ - **Vanilla JS / TypeScript**, no jQuery, no framework lock-in
10
10
  - **Galleries** with loop, arrows, keyboard, mouse wheel, and swipe
11
11
  - **Content types**: image, video, iframe, inline, AJAX, HTML
12
12
  - **Media helpers** for YouTube, Vimeo, and Google Maps URLs
13
- - **Pinch-zoom & pan**, click-to-zoom, protect mode
14
- - **Thumbnails**, **slideshow**, **fullscreen**, **share**, **hash** deep-linking
13
+ - **Pinch-zoom and pan**, click-to-zoom, protect mode
14
+ - **Thumbnails**, **slideshow**, **fullscreen**, **share**, **hash** deep links
15
15
  - **Accessible** dialog: focus trap, ARIA, Escape, restored focus
16
16
  - **Theming** via CSS custom properties
17
17
  - ESM + UMD builds, full type declarations
@@ -65,14 +65,46 @@ Glare.open(
65
65
 
66
66
  - Docs: [redot-src.github.io/glare](https://redot-src.github.io/glare/)
67
67
  - Live demo: [redot-src.github.io/glare/demo](https://redot-src.github.io/glare/demo/)
68
- - Repository: [github.com/redot-src/glare](https://github.com/redot-src/glare)
68
+
69
+ ## Development
69
70
 
70
71
  ```bash
71
72
  npm install
72
- npm run dev # interactive demo
73
+ npm run dev # interactive demo with live reload
74
+ npm run build # typecheck + library build → dist/
73
75
  npm run docs:dev # VitePress documentation
74
- npm run build # library build → dist/
75
- npm run pages:build # docs + demo for GitHub Pages
76
+ npm run pages:build # docs + demo, as deployed to GitHub Pages
77
+ ```
78
+
79
+ ### Project structure
80
+
81
+ ```
82
+ src/
83
+ index.ts public API and type exports
84
+ esm.ts ESM build entry: the API plus the stylesheet
85
+ umd.ts UMD/CDN build entry: exposes the class as window.Glare
86
+ types.ts public types
87
+ defaults.ts default options
88
+ i18n.ts built-in strings
89
+ icons.ts inline SVG icons
90
+ templates.ts HTML templates for the dialog and buttons
91
+ core/ the Glare class and its collaborators
92
+ glare.ts lifecycle, navigation, public API
93
+ dom.ts building and updating the dialog markup
94
+ loaders.ts one loader per content type
95
+ interactions.ts clicks, keyboard, focus trap
96
+ animation.ts open animation and slide-transition settings
97
+ zoom.ts image zoom and pan maths
98
+ idle.ts idle timer that hides the controls
99
+ options.ts merges defaults, mobile overrides, and module shorthands
100
+ registry.ts stack of open instances
101
+ bind.ts declarative `Glare.bind()` and hash restore
102
+ media/ URL type detection, providers (YouTube, Vimeo, Maps), item normalization
103
+ modules/ optional features: fullscreen, gestures, hash, share, slideshow, thumbs, wheel
104
+ styles/ stylesheet split by concern; tokens.css holds the public CSS variables
105
+ utils/ small DOM, object, environment, and template helpers
106
+ demo/ Vite demo site (npm run dev)
107
+ docs/ VitePress documentation
76
108
  ```
77
109
 
78
110
  ## License
@@ -0,0 +1,7 @@
1
+ import type { ResolvedOptions, SlideItem } from '../types';
2
+ /** Writes the effect and duration settings onto the container for the stylesheet to use. */
3
+ export declare function applyMotionSettings(container: HTMLElement, opts: ResolvedOptions): void;
4
+ /** Marks the container so slide-change keyframes apply from now on. */
5
+ export declare function enableTransitions(container: HTMLElement, opts: ResolvedOptions): void;
6
+ /** Flies the opened image in from its trigger element. */
7
+ export declare function animateOpen(container: HTMLElement, item: SlideItem, opts: ResolvedOptions): void;
@@ -0,0 +1,8 @@
1
+ import type { BoundGroup, GlareOptions } from '../types';
2
+ export type BindTarget = string | HTMLElement | HTMLElement[] | NodeListOf<HTMLElement>;
3
+ /** Opens `elements` as a gallery named `gallery`, starting at `index`. */
4
+ type Opener = (elements: HTMLElement[], index: number, gallery: string) => void;
5
+ /** Attaches click handlers that open a lightbox for the clicked element's gallery. */
6
+ export declare function bind(target: BindTarget, options: GlareOptions, open: Opener): BoundGroup;
7
+ export declare function unbindAll(): void;
8
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { GlareRefs, I18nDict, ResolvedOptions, SlideItem } from '../types';
2
+ export declare function buildContainer(opts: ResolvedOptions, dict: I18nDict, id: number, count: number): GlareRefs;
3
+ /** Creates a hidden slide with an empty content box and appends it to the stage. */
4
+ export declare function mountSlide(stage: HTMLElement, item: SlideItem, opts: ResolvedOptions): HTMLElement;
5
+ /** Makes a mounted slide the current one, fading it in unless `instant`. */
6
+ export declare function revealSlide(slide: HTMLElement, instant: boolean): void;
7
+ /** Fades out every slide except `keep` and removes it after `delay`. */
8
+ export declare function retireSlides(stage: HTMLElement, keep: HTMLElement, delay: number): void;
9
+ export declare function addSmallButton(slide: HTMLElement, opts: ResolvedOptions, dict: I18nDict): void;
10
+ export declare function showSpinner(stage: HTMLElement, opts: ResolvedOptions, dict: I18nDict): void;
11
+ export declare function hideSpinner(stage: HTMLElement): void;
12
+ export declare function showError(item: SlideItem, error: unknown, opts: ResolvedOptions, dict: I18nDict): void;
13
+ export declare function usesSmallButton(opts: ResolvedOptions, item: SlideItem): boolean;
14
+ /** Updates every piece of chrome that depends on the current slide. */
15
+ export declare function syncChrome(refs: GlareRefs, opts: ResolvedOptions, dict: I18nDict, item: SlideItem, total: number, caption: string): void;
16
+ export declare function syncZoomButton(container: HTMLElement, dict: I18nDict, zoomed: boolean, hasImage: boolean): void;
17
+ /** Swaps a toolbar toggle's label and icon and marks it active. */
18
+ export declare function setToggleState(button: HTMLElement, active: boolean, label: string, icon: string): void;
@@ -0,0 +1,65 @@
1
+ import type { BoundGroup, GlareInstance, GlareOptions, GlareRefs, I18nDict, ResolvedOptions, SlideItem, SlideSource } from '../types';
2
+ import { Fullscreen } from '../modules/fullscreen';
3
+ import { Share } from '../modules/share';
4
+ import { Slideshow } from '../modules/slideshow';
5
+ import { Thumbs } from '../modules/thumbs';
6
+ import { type BindTarget } from './bind';
7
+ import { Zoom, type Point } from './zoom';
8
+ export declare class Glare implements GlareInstance {
9
+ readonly id: number;
10
+ readonly opts: ResolvedOptions;
11
+ readonly group: SlideItem[];
12
+ readonly dict: I18nDict;
13
+ readonly zoom: Zoom;
14
+ current: SlideItem | null;
15
+ currIndex: number;
16
+ prevIndex: number;
17
+ isActive: boolean;
18
+ isClosing: boolean;
19
+ isIdle: boolean;
20
+ $refs: GlareRefs | null;
21
+ slideshow?: Slideshow;
22
+ thumbs?: Thumbs;
23
+ fullscreen?: Fullscreen;
24
+ share?: Share;
25
+ private readonly galleryName;
26
+ private readonly idle;
27
+ private hash;
28
+ private gestures;
29
+ private wheel;
30
+ private cleanups;
31
+ private previouslyFocused;
32
+ constructor(items: Array<SlideSource | string> | HTMLElement[], options?: GlareOptions, index?: number, galleryName?: string);
33
+ open(index?: number): void;
34
+ close(): void;
35
+ next(): void;
36
+ prev(): void;
37
+ jumpTo(index: number): void;
38
+ toggleZoom(focus?: Point): void;
39
+ update(): void;
40
+ focus(): void;
41
+ toggleControls(force?: boolean): void;
42
+ /** True once per drag gesture, so the click that follows it can be ignored. */
43
+ consumeGesture(): boolean;
44
+ private showSlide;
45
+ private syncChrome;
46
+ private captionFor;
47
+ private syncZoomState;
48
+ private resolveParent;
49
+ private startModules;
50
+ private onActivity;
51
+ private teardown;
52
+ private emit;
53
+ private emitSlide;
54
+ private clampIndex;
55
+ static defaults: ResolvedOptions;
56
+ static open(items: Array<SlideSource | string> | HTMLElement[], options?: GlareOptions, index?: number): Glare;
57
+ static bind(target: BindTarget, options?: GlareOptions): BoundGroup;
58
+ static close(all?: boolean): void;
59
+ static getInstance(id?: number): Glare | null;
60
+ static getInstances(): Glare[];
61
+ /** Closes every instance and removes all `bind()` handlers. */
62
+ static destroy(): void;
63
+ /** Binds every `[data-glare]` element once the DOM is ready. */
64
+ static autoBind(options?: GlareOptions): void;
65
+ }
@@ -0,0 +1,9 @@
1
+ /** Calls `onIdle` after `seconds` of no activity; `false` disables it. */
2
+ export declare class IdleTimer {
3
+ private readonly seconds;
4
+ private readonly onIdle;
5
+ private timer;
6
+ constructor(seconds: number | false, onIdle: () => void);
7
+ reset(): void;
8
+ stop(): void;
9
+ }
@@ -0,0 +1,3 @@
1
+ import type { Glare } from './glare';
2
+ /** Wires clicks, keyboard, focus trapping and idle tracking. Returns a cleanup function. */
3
+ export declare function bindInteractions(glare: Glare, container: HTMLElement, onActivity: () => void): () => void;
@@ -0,0 +1,5 @@
1
+ import type { I18nDict, ResolvedOptions, SlideItem } from '../types';
2
+ /** Renders the slide's content into `content` and resolves once it can be shown. */
3
+ export declare function loadContent(item: SlideItem, content: HTMLElement, opts: ResolvedOptions, dict: I18nDict): Promise<void>;
4
+ /** Warms the browser cache for neighbouring images. */
5
+ export declare function preloadNeighbours(group: SlideItem[], index: number): void;
@@ -0,0 +1,5 @@
1
+ import type { GlareOptions, ResolvedOptions } from '../types';
2
+ /** Merges user options over the defaults, then applies `mobile` overrides on touch devices. */
3
+ export declare function resolveOptions(options: GlareOptions): ResolvedOptions;
4
+ /** Normalizes a `T | boolean` module option: `true` yields the defaults, falsy disables it. */
5
+ export declare function moduleOptions<T extends object>(value: Partial<T> | boolean | undefined, fallback: T): T | null;
@@ -0,0 +1,9 @@
1
+ import type { Glare } from './glare';
2
+ export declare const registry: {
3
+ add(instance: Glare): void;
4
+ remove(instance: Glare): void;
5
+ top(): Glare | null;
6
+ get(id: number): Glare | null;
7
+ all(): Glare[];
8
+ readonly size: number;
9
+ };
@@ -0,0 +1,36 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface ZoomState extends Point {
6
+ scale: number;
7
+ }
8
+ export interface ZoomTarget {
9
+ image(): HTMLImageElement | null;
10
+ stage(): HTMLElement | null;
11
+ onChange(zoomed: boolean): void;
12
+ }
13
+ export declare const ZOOM_MIN = 1;
14
+ export declare const ZOOM_MAX = 5;
15
+ /**
16
+ * Controls the transform of the current image.
17
+ * The image is scaled around its centre and then translated by (x, y).
18
+ */
19
+ export declare class Zoom {
20
+ private readonly target;
21
+ scale: number;
22
+ x: number;
23
+ y: number;
24
+ constructor(target: ZoomTarget);
25
+ get isZoomed(): boolean;
26
+ get state(): ZoomState;
27
+ /** Centre of the image in viewport coordinates, ignoring the current translation. */
28
+ center(): Point | null;
29
+ apply(scale: number, x: number, y: number, duration?: number): void;
30
+ toFit(duration?: number): void;
31
+ /** Zooms to the image's natural size (at least 1.5x), keeping `focus` under the pointer. */
32
+ toActual(focus?: Partial<Point>, duration?: number): void;
33
+ reset(): void;
34
+ /** Keeps the scaled image from being dragged fully out of the stage. */
35
+ private bounded;
36
+ }
@@ -1,5 +1,5 @@
1
- import type { I18nDict, MediaProvider, GlareOptions } from './types';
2
- export declare const i18nEn: I18nDict;
3
- export declare const defaultMedia: Record<string, MediaProvider>;
4
- export declare const defaults: GlareOptions;
5
- //# sourceMappingURL=defaults.d.ts.map
1
+ import type { FullscreenOptions, ResolvedOptions, SlideshowOptions, ThumbsOptions } from './types';
2
+ export declare const thumbsDefaults: Required<Omit<ThumbsOptions, 'parentEl'>> & ThumbsOptions;
3
+ export declare const slideshowDefaults: Required<SlideshowOptions>;
4
+ export declare const fullscreenDefaults: Required<FullscreenOptions>;
5
+ export declare const defaults: ResolvedOptions;
package/dist/esm.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import './styles/index.css';
2
+ export * from './index';
3
+ export { default } from './index';
package/dist/glare.css CHANGED
@@ -1 +1 @@
1
- :root{--glare-bg: rgba(12, 14, 18, .92);--glare-bg-solid: #0c0e12;--glare-fg: #f4f6f8;--glare-muted: rgba(244, 246, 248, .62);--glare-accent: #ff4d4d;--glare-accent-soft: rgba(255, 77, 77, .18);--glare-toolbar-bg: rgba(12, 14, 18, .55);--glare-button-bg: rgba(255, 255, 255, .08);--glare-button-hover: rgba(255, 255, 255, .16);--glare-button-size: 44px;--glare-radius: 14px;--glare-shadow: 0 24px 80px rgba(0, 0, 0, .45);--glare-ease: cubic-bezier(.22, 1, .36, 1);--glare-duration: 366ms;--glare-transition-duration: 366ms;--glare-caption-bg: linear-gradient(180deg, transparent, rgba(0, 0, 0, .72));--glare-z: 100000;--glare-font: "Segoe UI", system-ui, -apple-system, sans-serif}.glare-lock{overflow:hidden!important}.glare-container{position:fixed;inset:0;z-index:var(--glare-z);display:flex;color:var(--glare-fg);font-family:var(--glare-font);outline:none;opacity:0;visibility:hidden;transition:opacity var(--glare-duration) var(--glare-ease),visibility var(--glare-duration);-webkit-tap-highlight-color:transparent}.glare-container.glare-is-open{opacity:1;visibility:visible}.glare-container.glare-is-closing{opacity:0}.glare-bg{position:absolute;inset:0;background:var(--glare-bg);backdrop-filter:blur(18px) saturate(1.2);-webkit-backdrop-filter:blur(18px) saturate(1.2)}.glare-inner{position:relative;z-index:1;display:grid;grid-template-rows:auto 1fr auto;width:100%;height:100%;min-height:0}.glare-toolbar,.glare-infobar,.glare-navigation,.glare-caption{transition:opacity .22s var(--glare-ease),transform .22s var(--glare-ease)}.glare-container.glare-is-idle .glare-toolbar,.glare-container.glare-is-idle .glare-infobar,.glare-container.glare-is-idle .glare-navigation,.glare-container.glare-is-idle .glare-caption:not(:hover){opacity:0;pointer-events:none}.glare-toolbar{position:absolute;top:12px;right:12px;z-index:5;display:flex;gap:6px;padding:6px;border-radius:999px;background:var(--glare-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:var(--glare-shadow)}.glare-infobar{position:absolute;top:18px;left:18px;z-index:5;padding:8px 14px;border-radius:999px;background:var(--glare-toolbar-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);font-size:13px;font-variant-numeric:tabular-nums;letter-spacing:.02em;color:var(--glare-muted)}.glare-button{appearance:none;border:0;margin:0;padding:0;width:var(--glare-button-size);height:var(--glare-button-size);display:inline-flex;align-items:center;justify-content:center;border-radius:50%;background:var(--glare-button-bg);color:var(--glare-fg);cursor:pointer;text-decoration:none;transition:background .16s ease,transform .16s ease,color .16s ease}.glare-button svg{width:20px;height:20px;display:block}.glare-button:hover,.glare-button:focus-visible{background:var(--glare-button-hover);outline:none}.glare-button:focus-visible{box-shadow:0 0 0 2px var(--glare-accent)}.glare-button.is-active{background:var(--glare-accent-soft);color:var(--glare-accent)}.glare-button.glare-disabled,.glare-button:disabled{opacity:.35;pointer-events:none}.glare-navigation{position:absolute;inset:0;z-index:4;pointer-events:none}.glare-button--prev,.glare-button--next{position:absolute;top:50%;transform:translateY(-50%);pointer-events:auto;width:52px;height:52px;background:var(--glare-toolbar-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.glare-button--prev{left:16px}.glare-button--next{right:16px}.glare-button--prev:hover,.glare-button--next:hover{transform:translateY(-50%) scale(1.04)}.glare-stage{position:relative;grid-row:1 / -1;width:100%;height:100%;overflow:hidden;touch-action:none}.glare-slide{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity var(--glare-transition-duration) var(--glare-ease)}.glare-slide--current{opacity:1;z-index:2}.glare-slide--out{opacity:0;z-index:1;pointer-events:none}.glare-space{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:64px 72px 88px;box-sizing:border-box}.glare-content{position:relative;max-width:100%;max-height:100%;display:flex;align-items:center;justify-content:center;border-radius:var(--glare-radius);box-shadow:var(--glare-shadow)}.glare-content--image{background:transparent;box-shadow:none;overflow:visible}.glare-image{display:block;max-width:min(100vw - 48px,100%);max-height:min(100vh - 160px,100%);width:auto;height:auto;object-fit:contain;-webkit-user-select:none;user-select:none;-webkit-user-drag:none;transform-origin:center center;transition:transform var(--glare-duration) var(--glare-ease),opacity var(--glare-duration) var(--glare-ease);border-radius:8px}.glare-content--html,.glare-content--inline,.glare-content--ajax{background:#11151c;color:var(--glare-fg);padding:28px;max-width:min(720px,calc(100vw - 48px));max-height:min(80vh,calc(100vh - 160px));overflow:auto;border:1px solid rgba(255,255,255,.08)}.glare-content--video,.glare-content--iframe{background:#000;overflow:hidden}.glare-video{display:block;max-width:min(960px,calc(100vw - 48px));max-height:min(80vh,calc(100vh - 160px));width:100%;background:#000}.glare-iframe-wrap{width:min(960px,calc(100vw - 48px));height:min(70vh,calc(100vh - 180px));max-width:100%}.glare-iframe{width:100%;height:100%;border:0;display:block;background:#000}.glare-caption{position:absolute;left:0;right:0;bottom:0;z-index:5;padding:48px 24px 24px;background:var(--glare-caption-bg);text-align:center;font-size:15px;line-height:1.5;pointer-events:none}.glare-caption a{color:var(--glare-accent);pointer-events:auto}.glare-close-small{position:absolute;top:12px;right:12px;z-index:6}.glare-spinner{position:absolute;top:50%;left:50%;width:42px;height:42px;margin:-21px 0 0 -21px;border-radius:50%;border:3px solid rgba(255,255,255,.18);border-top-color:var(--glare-accent);animation:glare-spin .8s linear infinite;z-index:3}@keyframes glare-spin{to{transform:rotate(360deg)}}.glare-error{padding:28px 32px;border-radius:var(--glare-radius);background:#161a22;border:1px solid rgba(255,255,255,.08);text-align:center;max-width:420px;color:var(--glare-muted)}.glare-hidden{display:none!important}.glare-thumbs{position:absolute;z-index:6;opacity:0;pointer-events:none;transition:opacity .22s ease,transform .22s ease}.glare-thumbs--active{opacity:1;pointer-events:auto}.glare-thumbs--y{top:72px;right:12px;bottom:88px;width:88px;transform:translate(12px)}.glare-thumbs--y.glare-thumbs--active{transform:translate(0)}.glare-thumbs--x{left:12px;right:12px;bottom:72px;height:78px;transform:translateY(12px)}.glare-thumbs--x.glare-thumbs--active{transform:translateY(0)}.glare-thumbs-list{display:flex;gap:8px;height:100%;padding:8px;border-radius:16px;background:var(--glare-toolbar-bg);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);overflow:auto;scrollbar-width:thin}.glare-thumbs--y .glare-thumbs-list{flex-direction:column}.glare-thumbs--x .glare-thumbs-list{flex-direction:row}.glare-thumbs-item{appearance:none;border:2px solid transparent;padding:0;margin:0;width:64px;height:64px;border-radius:10px;overflow:hidden;background:#ffffff0f;cursor:pointer;flex:0 0 auto;color:var(--glare-muted);font-size:12px}.glare-thumbs-item img{width:100%;height:100%;object-fit:cover;display:block}.glare-thumbs-item.is-active{border-color:var(--glare-accent)}.glare-container.glare-show-thumbs.glare-thumbs-axis-y .glare-space,.glare-show-thumbs .glare-space{padding-right:110px}.glare-share-overlay{position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;background:#0000008c;padding:24px}.glare-share{width:min(420px,100%);background:#12161e;border:1px solid rgba(255,255,255,.08);border-radius:18px;padding:24px;box-shadow:var(--glare-shadow)}.glare-share-title{margin:0 0 16px;font-size:18px}.glare-share p{margin:0 0 10px}.glare-share-link{color:var(--glare-fg);text-decoration:none;display:inline-flex;padding:10px 12px;border-radius:10px;background:#ffffff0f;width:100%;box-sizing:border-box}.glare-share-link:hover{background:#ffffff1f}.glare-share-input{width:100%;box-sizing:border-box;border-radius:10px;border:1px solid rgba(255,255,255,.12);background:#00000059;color:var(--glare-fg);padding:10px 12px;font:inherit}.glare-container[data-transition=slide] .glare-slide--out{transform:translate3d(-40px,0,0)}.glare-container[data-transition=circular] .glare-slide--current{animation:glare-circular var(--glare-transition-duration) var(--glare-ease)}.glare-container[data-transition=tube] .glare-slide--current{animation:glare-tube var(--glare-transition-duration) var(--glare-ease)}.glare-container[data-transition=rotate] .glare-slide--current{animation:glare-rotate var(--glare-transition-duration) var(--glare-ease)}.glare-container[data-transition=zoom-in-out] .glare-slide--current{animation:glare-zoom-inout var(--glare-transition-duration) var(--glare-ease)}@keyframes glare-circular{0%{transform:scale(.85) rotate(-8deg);opacity:0}to{transform:none;opacity:1}}@keyframes glare-tube{0%{transform:scaleX(.2);opacity:0}to{transform:none;opacity:1}}@keyframes glare-rotate{0%{transform:rotate(12deg) scale(.92);opacity:0}to{transform:none;opacity:1}}@keyframes glare-zoom-inout{0%{transform:scale(1.18);opacity:0}to{transform:none;opacity:1}}.glare-container.glare-is-slideshow .glare-button--slideshow{color:var(--glare-accent)}.glare-container.glare-is-modal .glare-bg{pointer-events:none}@media(max-width:720px){.glare-space{padding:56px 16px 96px}.glare-button--prev,.glare-button--next{width:44px;height:44px}.glare-button--prev{left:8px}.glare-button--next{right:8px}.glare-toolbar{top:8px;right:8px;gap:4px;padding:4px}.glare-button{width:40px;height:40px}.glare-thumbs--y{display:none}.glare-infobar{top:12px;left:12px;font-size:12px}}@media(prefers-reduced-motion:reduce){.glare-container,.glare-slide,.glare-image,.glare-toolbar,.glare-infobar,.glare-navigation,.glare-caption,.glare-thumbs{transition:none!important;animation:none!important}}
1
+ :root{--glare-z: 100000;--glare-font: system-ui, -apple-system, "Segoe UI", sans-serif;--glare-bg: rgba(12, 14, 18, .92);--glare-fg: #f4f6f8;--glare-muted: rgba(244, 246, 248, .62);--glare-surface: #11151c;--glare-border: rgba(255, 255, 255, .08);--glare-accent: #ff4d4d;--glare-accent-soft: rgba(255, 77, 77, .18);--glare-toolbar-bg: rgba(12, 14, 18, .55);--glare-toolbar-bg-hover: rgba(12, 14, 18, .8);--glare-button-bg: rgba(255, 255, 255, .08);--glare-button-hover: rgba(255, 255, 255, .16);--glare-button-size: 44px;--glare-caption-bg: linear-gradient(180deg, transparent, rgba(0, 0, 0, .72));--glare-radius: 14px;--glare-shadow: 0 24px 80px rgba(0, 0, 0, .45);--glare-backdrop: blur(14px);--glare-blur: blur(12px);--glare-ease: cubic-bezier(.22, 1, .36, 1);--glare-iframe-bg: #fff;--glare-iframe-radius: 5px;--glare-thumbs-size: 95px}.glare-lock{overflow:hidden!important}.glare-container{position:fixed;inset:0;z-index:var(--glare-z);color:var(--glare-fg);font-family:var(--glare-font);outline:none;visibility:hidden;transition:visibility var(--glare-duration);-webkit-tap-highlight-color:transparent}.glare-container.glare-is-open{visibility:visible}.glare-bg,.glare-inner{opacity:0;transition:opacity var(--glare-duration) var(--glare-ease);will-change:opacity}.glare-is-open .glare-bg,.glare-is-open .glare-inner{opacity:1}.glare-is-closing .glare-bg,.glare-is-closing .glare-inner,.glare-is-closing .glare-thumbs,.glare-is-closing .glare-share-overlay{opacity:0;pointer-events:none}.glare-bg{position:absolute;inset:0;background:var(--glare-bg);-webkit-backdrop-filter:var(--glare-backdrop);backdrop-filter:var(--glare-backdrop)}.glare-inner{position:relative;z-index:1;width:100%;height:100%}.glare-hidden{display:none!important}.glare-sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}.glare-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--glare-button-size);height:var(--glare-button-size);margin:0;padding:0;border:0;border-radius:50%;background:var(--glare-button-bg);color:var(--glare-fg);cursor:pointer;text-decoration:none;transition:background .16s ease,color .16s ease,transform .16s ease}.glare-button svg{display:block;width:20px;height:20px}.glare-toolbar .glare-button{background:transparent}.glare-button:hover,.glare-button:focus-visible{background:var(--glare-button-hover);outline:none}.glare-button:focus-visible{box-shadow:0 0 0 2px var(--glare-accent)}.glare-button.glare-is-active{background:var(--glare-accent-soft);color:var(--glare-accent)}.glare-button.glare-is-disabled{opacity:.35;pointer-events:none}.glare-toolbar,.glare-infobar,.glare-button--prev,.glare-button--next,.glare-close-small{border:1px solid var(--glare-border);background:var(--glare-toolbar-bg);-webkit-backdrop-filter:var(--glare-blur);backdrop-filter:var(--glare-blur)}.glare-button--prev:hover,.glare-button--next:hover,.glare-close-small:hover,.glare-button--prev:focus-visible,.glare-button--next:focus-visible,.glare-close-small:focus-visible{background:var(--glare-toolbar-bg-hover)}.glare-toolbar,.glare-infobar{position:absolute;top:12px;z-index:5;border-radius:999px}.glare-toolbar{right:12px;display:flex;gap:6px;padding:5px}.glare-infobar{top:18px;left:18px;padding:8px 14px;color:var(--glare-muted);font-size:13px;font-variant-numeric:tabular-nums;letter-spacing:.02em}.glare-navigation{position:absolute;inset:0;z-index:4;pointer-events:none}.glare-button--prev,.glare-button--next{position:absolute;top:50%;width:52px;height:52px;transform:translateY(-50%);pointer-events:auto}.glare-button--prev{left:16px}.glare-button--next{right:16px}.glare-close-small{position:absolute;top:12px;right:12px;z-index:6}.glare-caption{position:absolute;inset:auto 0 0;z-index:5;padding:48px 24px 24px;background:var(--glare-caption-bg);font-size:15px;line-height:1.5;text-align:center;pointer-events:none}.glare-caption a{color:var(--glare-accent);pointer-events:auto}.glare-infobar,.glare-navigation,.glare-caption{transition:opacity .22s var(--glare-ease),visibility .22s}.glare-is-idle .glare-infobar,.glare-is-idle .glare-navigation,.glare-is-idle .glare-caption:not(:hover){opacity:0;visibility:hidden;pointer-events:none}.glare-progress{position:absolute;top:0;left:0;right:0;z-index:30;height:3px;background:var(--glare-accent);transform:scaleX(0);transform-origin:left center;transition:transform 0s linear;pointer-events:none}.glare-spinner{position:absolute;top:50%;left:50%;z-index:3;pointer-events:none;width:42px;height:42px;margin:-21px 0 0 -21px;border:3px solid rgba(255,255,255,.18);border-top-color:var(--glare-accent);border-radius:50%;animation:glare-spin .8s linear infinite}@keyframes glare-spin{to{transform:rotate(360deg)}}.glare-error{max-width:420px;padding:28px 32px;border:1px solid var(--glare-border);border-radius:var(--glare-radius);background:var(--glare-surface);color:var(--glare-muted);text-align:center}.glare-container{--glare-thumbs-inset: 0px;--glare-inset-top: 64px;--glare-inset-x: 72px;--glare-inset-bottom: max(88px, var(--glare-thumbs-inset))}.glare-show-thumbs.glare-thumbs-axis-x{--glare-thumbs-inset: calc(var(--glare-thumbs-size) + 16px)}.glare-show-thumbs.glare-thumbs-axis-y{--glare-inset-x: 110px}.glare-slide:where(:not(.glare-slide--image)){--glare-inset-top: 30px;--glare-inset-x: 30px;--glare-inset-bottom: max(30px, var(--glare-thumbs-inset))}.glare-stage{position:absolute;inset:0;overflow:hidden;touch-action:none;transition:transform .2s var(--glare-ease)}.glare-slide{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding:var(--glare-inset-top) var(--glare-inset-x) var(--glare-inset-bottom);opacity:0;transition:opacity var(--glare-transition-duration) var(--glare-ease),transform var(--glare-transition-duration) var(--glare-ease)}.glare-slide--current{z-index:2;opacity:1}.glare-slide--out{z-index:1;opacity:0;pointer-events:none}.glare-content{position:relative;display:flex;align-items:center;justify-content:center;max-width:100%;max-height:100%}.glare-image{display:block;width:auto;height:auto;max-width:calc(100vw - 2 * var(--glare-inset-x));max-height:calc(100vh - var(--glare-inset-top) - var(--glare-inset-bottom));max-height:calc(100dvh - var(--glare-inset-top) - var(--glare-inset-bottom));border-radius:8px;object-fit:contain;-webkit-user-select:none;user-select:none;-webkit-user-drag:none;transform-origin:center;will-change:transform;transition:transform var(--glare-duration) var(--glare-ease),opacity var(--glare-duration) var(--glare-ease)}.glare-can-zoom-in .glare-image{cursor:zoom-in}.glare-can-zoom-out .glare-image{cursor:zoom-out}.glare-video{display:block;width:min(960px,100vw - 2 * var(--glare-inset-x));max-width:100%;max-height:calc(100vh - var(--glare-inset-top) - var(--glare-inset-bottom));max-height:calc(100dvh - var(--glare-inset-top) - var(--glare-inset-bottom));border-radius:var(--glare-iframe-radius);background:#000;box-shadow:var(--glare-shadow);object-fit:contain}.glare-content--iframe{width:100%;height:100%}.glare-iframe-wrap{width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;border-radius:var(--glare-iframe-radius);background:var(--glare-iframe-bg);box-shadow:var(--glare-shadow)}.glare-iframe{display:block;width:100%;height:100%;border:0}.glare-content--html,.glare-content--inline,.glare-content--ajax{display:block;box-sizing:border-box;max-width:min(720px,100%);padding:28px;overflow:auto;border:1px solid var(--glare-border);border-radius:var(--glare-radius);background:var(--glare-surface);box-shadow:var(--glare-shadow)}.glare-content--html>:first-child,.glare-content--inline>:first-child,.glare-content--ajax>:first-child{margin-top:0}.glare-content--html>:last-child,.glare-content--inline>:last-child,.glare-content--ajax>:last-child{margin-bottom:0}.glare-thumbs{position:absolute;z-index:6;opacity:0;pointer-events:none;transition:opacity .22s var(--glare-ease),transform .22s var(--glare-ease)}.glare-thumbs--active{opacity:1;pointer-events:auto;transform:none}.glare-thumbs--x{left:0;right:0;bottom:0;height:var(--glare-thumbs-size);transform:translateY(8px)}.glare-thumbs--y{top:72px;right:12px;bottom:88px;width:88px;transform:translate(12px)}.glare-thumbs-list{display:flex;gap:8px;height:100%;box-sizing:border-box;overflow:auto;scrollbar-width:thin;-webkit-backdrop-filter:var(--glare-blur);backdrop-filter:var(--glare-blur)}.glare-thumbs--x .glare-thumbs-list{padding:10px 10px 5px;background:#0000004d}.glare-thumbs--y .glare-thumbs-list{flex-direction:column;padding:8px;border:1px solid var(--glare-border);border-radius:16px;background:var(--glare-toolbar-bg)}.glare-thumbs-item{appearance:none;flex:0 0 auto;box-sizing:border-box;width:70px;height:70px;margin:0;padding:0;border:4px solid transparent;border-radius:10px;overflow:hidden;background:var(--glare-button-bg);color:var(--glare-muted);font-size:12px;cursor:pointer}.glare-thumbs-item img{display:block;width:100%;height:100%;object-fit:cover}.glare-thumbs-item.glare-is-active{border-color:var(--glare-accent)}.glare-thumbs-item:focus-visible{outline:2px solid var(--glare-accent);outline-offset:-4px}.glare-show-thumbs.glare-thumbs-axis-x .glare-caption{bottom:var(--glare-thumbs-size)}.glare-share-overlay{position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;padding:24px;background:#0000008c}.glare-share{width:min(420px,100%);padding:24px;border:1px solid var(--glare-border);border-radius:var(--glare-radius);background:var(--glare-surface);box-shadow:var(--glare-shadow)}.glare-share-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}.glare-share-title{margin:0;font-size:18px}.glare-share-close{width:36px;height:36px}.glare-share p{margin:0 0 10px}.glare-share-row{display:flex;gap:8px}.glare-share-link,.glare-share-input,.glare-share-copy{display:block;box-sizing:border-box;padding:10px 12px;border-radius:10px;color:var(--glare-fg);font:inherit}.glare-share-link{width:100%;background:var(--glare-button-bg);text-decoration:none}.glare-share-link:hover,.glare-share-copy:hover{background:var(--glare-button-hover)}.glare-share-input{flex:1;min-width:0;border:1px solid var(--glare-border);background:#00000059}.glare-share-copy{flex:0 0 auto;border:0;background:var(--glare-button-bg);cursor:pointer}[data-transition=slide] .glare-slide--out{transform:translate3d(-40px,0,0)}[data-transition=circular] .glare-slide--current{animation:glare-circular var(--glare-transition-duration) var(--glare-ease)}[data-transition=tube] .glare-slide--current{animation:glare-tube var(--glare-transition-duration) var(--glare-ease)}[data-transition=rotate] .glare-slide--current{animation:glare-rotate var(--glare-transition-duration) var(--glare-ease)}[data-transition=zoom-in-out] .glare-slide--current{animation:glare-zoom-in-out var(--glare-transition-duration) var(--glare-ease)}@keyframes glare-circular{0%{transform:scale(.85) rotate(-8deg);opacity:0}}@keyframes glare-tube{0%{transform:scaleX(.2);opacity:0}}@keyframes glare-rotate{0%{transform:rotate(12deg) scale(.92);opacity:0}}@keyframes glare-zoom-in-out{0%{transform:scale(1.18);opacity:0}}@media(prefers-reduced-motion:reduce){.glare-container,.glare-container *{transition:none!important;animation:none!important}}@media(max-width:720px){.glare-container{--glare-button-size: 40px;--glare-inset-top: 56px;--glare-inset-x: 16px;--glare-inset-bottom: max(96px, var(--glare-thumbs-inset))}.glare-slide:where(:not(.glare-slide--image)){--glare-inset-top: 15px;--glare-inset-x: 15px;--glare-inset-bottom: max(15px, var(--glare-thumbs-inset))}.glare-toolbar{top:8px;right:8px;gap:4px;padding:3px}.glare-close-small{top:8px;right:8px}.glare-infobar{top:12px;left:12px;font-size:12px}.glare-button--prev,.glare-button--next{width:44px;height:44px}.glare-button--prev{left:8px}.glare-button--next{right:8px}.glare-thumbs--y,.glare-thumbs-axis-y [data-glare-thumbs]{display:none}.glare-show-thumbs.glare-thumbs-axis-y{--glare-inset-x: 16px}}