@zoreal/oauth2-react 0.2.20 → 0.2.21
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.
- package/README.md +31 -67
- package/dist/index.cjs +123 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +123 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/wire.ts","../src/PairingModal.tsx","../src/mark.tsx","../src/lockup.tsx","../src/i18n.ts","../src/intent.ts","../src/styles.ts","../src/ZorealLogin.tsx","../src/useZorealLogin.ts","../src/return.ts","../src/jwt.ts","../src/pairing.ts","../src/pkce.ts","../src/ring.tsx","../src/useZorealAutoLogin.ts","../src/logout.ts","../src/scopes.ts"],"sourcesContent":["export { ZorealOAuthProvider, useZorealOAuth } from './context';\nexport type { ZorealOAuthProviderProps, ZorealOAuthContextProps } from './context';\nexport { ZorealLogin } from './ZorealLogin';\nexport { ZorealBusyRing } from './ring';\n// The mark, for a site that renders its own button in the house shape.\nexport { ZorealMark } from './mark';\n// Exported so an integrator on `pairingUI: 'none'` can still mount the real\n// dialog (driven by their own `onPairingStateChange`) rather than rebuild it.\nexport { PairingModal, DEFAULT_PAIRING_TIMEOUT_MS } from './PairingModal';\nexport type { PairingModalProps } from './PairingModal';\nexport { useZorealLogin } from './useZorealLogin';\nexport { useZorealAutoLogin } from './useZorealAutoLogin';\nexport { zorealLogout } from './logout';\nexport { hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal } from './scopes';\nexport { resolveIntent } from './intent';\nexport type {\n AcrValue,\n LoginIntent,\n PairingUI,\n ZorealTheme,\n AuthCodeFlowOptions,\n BrowserDirectFlowOptions,\n ErrorCode,\n NonOAuthError,\n PairingState,\n SelectBy,\n UseZorealAutoLoginOptions,\n ZorealButtonConfiguration,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginProps,\n ZorealLoginRequestOptions,\n} from './types';\n","import { createContext, useContext, useMemo, useState, type ReactNode } from 'react';\nimport { DEFAULT_ISSUER } from './wire';\nimport { PairingModal } from './PairingModal';\nimport type { LoginIntent, PairingState, PairingUI, ZorealTheme } from './types';\n\nexport interface ZorealOAuthProviderProps {\n /** From the ZOREAL dashboard: the asset ID. */\n clientId: string;\n /** Override the provider origin. Sandbox and self-hosted testing only. */\n issuer?: string;\n /**\n * BCP 47. Drives button text, the pairing page, AND the pairing modal's own\n * copy — pass the language your app is currently showing, so the browser and\n * the phone say the same thing.\n */\n locale?: string;\n /** Colour scheme for the pairing modal. Defaults to following the OS. */\n theme?: ZorealTheme;\n /**\n * Who renders the QR. Defaults to 'modal': the SDK draws it. Set 'none' only\n * if you are rendering your own from `onPairingStateChange`.\n */\n pairingUI?: PairingUI;\n /**\n * How long the modal stays open before giving up and cancelling, in ms.\n * Defaults to 120000. The provider's own expiry wins when it is shorter.\n */\n pairingTimeoutMs?: number;\n children: ReactNode;\n}\n\nexport interface ZorealOAuthContextProps {\n clientId: string;\n issuer: string;\n locale?: string;\n}\n\n/** What the flow hands the provider so it can draw the pairing. */\nexport interface HostedPairing {\n state: PairingState;\n qrUrl: string;\n intent: LoginIntent;\n cancel: () => void;\n}\n\nconst ZorealOAuthContext = createContext<ZorealOAuthContextProps | null>(null);\n\n/**\n * Internal channel from the flow to the provider.\n *\n * The modal has to be rendered by the provider rather than by the hook, because\n * `useZorealLogin` returns a function, not an element: there is nowhere for a\n * hook to put a dialog. Publishing up to the provider is what lets an\n * integrator get the whole pairing UI without writing (or importing) anything.\n *\n * Null when `pairingUI` is 'none', which is also how the flow knows to stay out\n * of the way and let the caller render.\n */\nconst PairingHostContext = createContext<((pairing: HostedPairing | null) => void) | null>(null);\n\nexport function useZorealPairingHost() {\n return useContext(PairingHostContext);\n}\n\nexport function ZorealOAuthProvider({\n clientId,\n issuer = DEFAULT_ISSUER,\n locale,\n theme = 'auto',\n pairingUI = 'modal',\n pairingTimeoutMs,\n children,\n}: ZorealOAuthProviderProps) {\n const [pairing, setPairing] = useState<HostedPairing | null>(null);\n\n const value = useMemo(\n () => ({ clientId, issuer: issuer.replace(/\\/$/, ''), locale }),\n [clientId, issuer, locale]\n );\n\n const host = pairingUI === 'modal' ? setPairing : null;\n\n return (\n <ZorealOAuthContext.Provider value={value}>\n <PairingHostContext.Provider value={host}>\n {children}\n {pairing && (\n <PairingModal\n state={pairing.state}\n qrUrl={pairing.qrUrl}\n intent={pairing.intent}\n onCancel={pairing.cancel}\n locale={locale}\n theme={theme}\n timeoutMs={pairingTimeoutMs}\n />\n )}\n </PairingHostContext.Provider>\n </ZorealOAuthContext.Provider>\n );\n}\n\nexport function useZorealOAuth(): ZorealOAuthContextProps {\n const ctx = useContext(ZorealOAuthContext);\n if (!ctx) {\n throw new Error(\n 'useZorealOAuth must be used inside <ZorealOAuthProvider clientId=...>. ' +\n 'Wrap your app (or the part that logs in) in the provider.'\n );\n }\n return ctx;\n}\n","/**\n * The wire protocol between this package and the ZOREAL OpenID Provider.\n *\n * VERSIONED: a shipped version keeps working until the provider explicitly\n * refuses it, and when it does, the reason is surfaced verbatim. Both the wire\n * version and the package version travel on every pairing request so a refusal\n * can be precise.\n *\n * Endpoints, all relative to the issuer and all CORS-gated on the client's\n * authorized JavaScript origins (the dashboard):\n *\n * POST /pair start a pairing request. Body carries the\n * authorize parameters, the PKCE challenge and\n * `display`: \"qr\" or \"link\", the surface this\n * package is about to show, decided before the\n * request. Returns { request_id, pair_url,\n * expires_in, display, qr_refresh_seconds } or,\n * for prompt=none with a live consented\n * session, { code } immediately. The provider\n * binds the pairing to the display it echoes\n * back. A \"link\" pairing's pair_url carries a\n * start token (`?t=<start_token>`) that only\n * the browser it was handed to can claim with,\n * and the provider renders no QR for it, so\n * nobody can turn a same-device link into a\n * code that gets scanned elsewhere. A request\n * with no `display` gets the older static\n * behaviour (\"legacy\").\n * GET /pair/start the same-device sign-in as a NAVIGATION:\n * the /pair parameters as a query, plus\n * request_id (the page's own token) and\n * origin; answered with a redirect to the\n * pairing's universal link, inside the tap\n * GET /pair/:id/status poll: pending | claimed |\n * approved (with code) | denied | expired |\n * enrolling. Over-polling cancels the request\n * rather than throttling it, so the cadence\n * below is not a suggestion.\n * GET /pair/:id/qr.svg the QR image, rendered by the provider so\n * the pairing surface stays changeable at\n * runtime and this package keeps zero\n * dependencies: it generates nothing. For a\n * \"qr\" pairing the image is the CURRENT FRAME,\n * served `Cache-Control: no-store`: a QR of\n * `<pair_url>?f=<time>.<hmac>`, where `time`\n * is whole seconds since the pairing was\n * created on the provider's clock and `hmac`\n * is a truncated HMAC-SHA-256 of that time\n * under a per-pairing secret that never leaves\n * the provider. The frame moves every\n * qr_refresh_seconds and the provider refuses a\n * stale one at claim, so a screenshot of the\n * code is dead on arrival and only a live view\n * of the screen can be relayed. This package\n * re-fetches the image on that cadence with a\n * cache-busting `?t=<Date.now()>` and swaps it\n * in once loaded. 404 once the pairing has left\n * pending, and always for a \"link\" pairing.\n * POST /token the code exchange. Browser-direct mode uses\n * it directly with PKCE and no client secret;\n * auth-code mode leaves it to the RP backend.\n */\n\nexport const WIRE_VERSION = 1;\nexport const SDK_VERSION = '0.2.20';\nexport const DEFAULT_ISSUER = 'https://id.zoreal.com';\n\n/** Pending TTL is short. Poll gently; over-polling cancels the request. */\nexport const POLL_INTERVAL_MS = 2000;\n/** Enrolling extends the window well beyond a normal login; poll slower. */\nexport const POLL_INTERVAL_ENROLLING_MS = 5000;\n/**\n * How often the QR frame is re-fetched when the provider did not say. The\n * provider's `qr_refresh_seconds` wins whenever it is present; this is the\n * floor for an older provider that predates animated frames, where refreshing\n * a static code is merely redundant.\n */\nexport const DEFAULT_QR_REFRESH_SECONDS = 3;\n\nexport type PairDisplay = 'qr' | 'link';\n\nexport interface PairCreated {\n request_id: string;\n /**\n * The pairing page: https://zoreal.com/login/<request_id>. The same URL is\n * what the QR encodes and what the app link opens. For a \"link\" pairing it\n * also carries `?t=<start_token>`, and this package navigates to it verbatim.\n */\n pair_url: string;\n expires_in: number;\n /**\n * The surface the provider bound the pairing to, echoed from the request.\n * \"legacy\" is a provider that got no `display` and kept the static QR.\n * Absent from a provider that predates the field.\n */\n display?: PairDisplay | 'legacy';\n /**\n * QR pairings only: how often, in seconds, to re-fetch the QR image so the\n * code on screen is the provider's current frame. DEFAULT_QR_REFRESH_SECONDS\n * applies when absent.\n */\n qr_refresh_seconds?: number;\n}\n\nexport interface PairImmediate {\n /** prompt=none resolved silently: consented sector, live session. */\n code: string;\n}\n\nexport type PairStartResponse = PairCreated | PairImmediate;\n\nexport interface PairStatusResponse {\n status: 'pending' | 'claimed' | 'approved' | 'denied' | 'expired' | 'cancelled' | 'enrolling';\n code?: string;\n expires_in?: number;\n enrolment_deadline?: number;\n /**\n * The provider's reason on denial or refusal. Surfaced verbatim, never\n * rewritten. A pairing the provider denied because the approving phone was\n * in a different country from the browser arrives here as `access_denied`\n * with the provider's own sentence in error_description.\n */\n error?: string;\n error_description?: string;\n}\n\nexport interface TokenResponse {\n id_token: string;\n access_token?: string;\n token_type?: string;\n expires_in?: number;\n scope?: string;\n error?: string;\n error_description?: string;\n}\n","import { useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ZorealLockup } from './lockup';\nimport { interpolate, isRtl, strings } from './i18n';\nimport { titleFor } from './intent';\nimport { cx, ensureStyles } from './styles';\nimport type { LoginIntent, PairingState, ZorealTheme } from './types';\n\n/**\n * The pairing modal, rendered by the SDK rather than by every integrator.\n *\n * It is a modal and not an inline card because the handshake is blocking,\n * time-boxed and happens on a second device: there is nothing useful to do on\n * the page until it resolves, and an inline panel below a button competes with\n * the rest of a sign-in form for the person's attention at the one moment they\n * need to look at their phone.\n */\n\n/** Our own cap on how long a pairing sits on screen. See `pairingTimeoutMs`. */\nexport const DEFAULT_PAIRING_TIMEOUT_MS = 120_000;\n\n/** Below this the countdown changes colour: it stops being background\n * information and starts being a prompt to hurry. */\nconst URGENT_SECONDS = 20;\n\nfunction mmss(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\n/* Icons are inlined rather than pulled from an icon package: this renders on\n someone else's sign-in page, and a UI dependency here would be inherited by\n every host app that installs the SDK. */\nconst IconClose = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden focusable=\"false\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nconst IconPhone = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden focusable=\"false\">\n <rect x=\"6\" y=\"2\" width=\"12\" height=\"20\" rx=\"2.5\" />\n <path d=\"M11 18.5h2\" />\n </svg>\n);\n\nconst IconShield = () => (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden focusable=\"false\">\n <path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n);\n\nexport interface PairingModalProps {\n state: PairingState;\n qrUrl: string;\n onCancel: () => void;\n locale?: string;\n theme?: ZorealTheme;\n timeoutMs?: number;\n /** Which title the dialog opens with. Defaults to the sign-in wording. */\n intent?: LoginIntent;\n}\n\nexport function PairingModal({\n state,\n qrUrl,\n onCancel,\n locale,\n theme = 'auto',\n timeoutMs = DEFAULT_PAIRING_TIMEOUT_MS,\n intent = 'sign-in',\n}: PairingModalProps) {\n const t = strings(locale);\n const titleId = useId();\n const closeRef = useRef<HTMLButtonElement>(null);\n\n // A deadline, not a decremented counter. Background tabs throttle timers, so\n // a counter that subtracts one per tick drifts and comes back lying about how\n // much time is left; reading the clock each tick self-corrects.\n const deadlineRef = useRef(0);\n const [remaining, setRemaining] = useState(Math.round(timeoutMs / 1000));\n\n // `claimed` = the request is now waiting in the holder's app; `enrolling` =\n // a first-time holder finishing ZOREAL ID setup. In both the QR has done its\n // job and the action has moved to the phone.\n const settled = state.status === 'claimed' || state.status === 'enrolling';\n\n // The code on screen is a frame of a rotating sequence, so `qrUrl` arrives\n // again every few seconds with a different value. Swapping an <img> src\n // straight over blanks the well while the new image downloads, which on a\n // three second cadence is a QR that flickers the whole time someone is\n // trying to aim a camera at it. So the next frame is fetched into an\n // off-document Image first and only becomes the visible src once it has\n // decoded. A frame that fails to load is dropped without touching what is\n // showing: the old code is still valid for a few more seconds, and the next\n // refresh is another attempt.\n const [frameUrl, setFrameUrl] = useState(qrUrl);\n useEffect(() => {\n // Once the phone has claimed the code the sequence is over and the well\n // keeps the spent frame under its overlay.\n if (settled || frameUrl === qrUrl) return;\n let abandoned = false;\n const next = new Image();\n next.onload = () => {\n if (!abandoned) setFrameUrl(qrUrl);\n };\n next.onerror = () => {\n /* keep the frame that is showing; the next refresh retries */\n };\n next.src = qrUrl;\n return () => {\n abandoned = true;\n next.onload = null;\n next.onerror = null;\n };\n }, [qrUrl, settled, frameUrl]);\n\n const onCancelRef = useRef(onCancel);\n onCancelRef.current = onCancel;\n\n useEffect(() => {\n ensureStyles();\n }, []);\n\n useEffect(() => {\n // Never claim more time than the provider will actually honour: if the\n // server's own window is shorter than our cap, the server wins.\n const serverMs = typeof state.expiresIn === 'number' ? state.expiresIn * 1000 : Infinity;\n deadlineRef.current = Date.now() + Math.min(timeoutMs, serverMs);\n setRemaining(Math.round(Math.min(timeoutMs, serverMs) / 1000));\n\n const id = setInterval(() => {\n const left = Math.max(0, Math.ceil((deadlineRef.current - Date.now()) / 1000));\n setRemaining(left);\n if (left === 0) {\n // Stop the tick before cancelling. Unmount clears it anyway, but only\n // after this render commits, and a timer that keeps firing `cancel`\n // once a second in between is a race waiting to be inherited.\n clearInterval(id);\n onCancelRef.current();\n }\n }, 1000);\n return () => clearInterval(id);\n // Deliberately keyed on the FIRST expiresIn only: re-running on every poll\n // would restart the countdown each tick and it would never reach zero.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [timeoutMs]);\n\n // Escape closes, page scroll locks, and focus moves into the dialog, so the\n // panel is reachable and dismissable without a mouse.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancelRef.current();\n };\n document.addEventListener('keydown', onKey);\n const previous = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n closeRef.current?.focus();\n return () => {\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = previous;\n };\n }, []);\n\n if (typeof document === 'undefined') return null;\n\n const body =\n state.status === 'enrolling'\n ? t.bodyEnrolling\n : settled\n ? t.bodyApprove\n : t.bodyScan;\n\n return createPortal(\n <div className={`${cx('root')} ${cx('scrim')}`} data-theme={theme} onClick={onCancel}>\n <div\n className={cx('card')}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n dir={isRtl(locale) ? 'rtl' : 'ltr'}\n onClick={(e) => e.stopPropagation()}\n >\n <button ref={closeRef} type=\"button\" className={cx('close')} aria-label={t.close} onClick={onCancel}>\n <IconClose />\n </button>\n\n <div className={cx('body')}>\n <ZorealLockup height={44} className={cx('lockup')} />\n\n <h2 id={titleId} className={cx('title')}>\n {settled ? t.titleApprove : titleFor(t, intent)}\n </h2>\n <p className={cx('body-text')}>{body}</p>\n\n {/* The light on the well's edge is a set of masked overlays inside\n the well, drawn first so the spent badge, a later sibling, stays\n above them. The well carries the spent flag for them: a\n stylesheet cannot look back from the image to a sibling before\n it. */}\n <div className={cx('qr-well')} data-spent={settled}>\n <span className={cx('qr-beam-glow')} aria-hidden>\n <span className={cx('qr-beam-glow-band')} />\n </span>\n <span className={cx('qr-beam')} aria-hidden />\n <img className={cx('qr')} data-spent={settled} src={frameUrl} alt={t.qrAlt} width={180} height={180} />\n {settled && (\n <span className={cx('qr-overlay')}>\n <span className={cx('qr-badge')}>\n <IconPhone />\n </span>\n </span>\n )}\n </div>\n\n <div className={cx('status')}>\n <span className={cx('dot')}>\n <i />\n <i />\n </span>\n {settled ? t.waitingApproval : t.waiting}\n </div>\n <p className={cx('timer')} data-urgent={remaining <= URGENT_SECONDS}>\n {interpolate(t.expiresIn, mmss(remaining))}\n </p>\n </div>\n\n {/* The QR is on screen because this person is being asked to use a\n phone app, and some of them do not have it yet. Without this the\n panel reads as \"scan this with something I do not have\", and the\n flow dead-ends at the one moment it can still be recovered: the\n same code is also the app's download link. */}\n <div className={cx('help')}>\n <p className={cx('help-title')}>{t.noIdTitle}</p>\n <p className={cx('help-body')}>{t.noIdBody}</p>\n </div>\n\n <div className={cx('footer')}>\n <button type=\"button\" className={cx('cancel')} onClick={onCancel}>\n {t.cancel}\n </button>\n <a className={cx('secured')} href=\"https://zoreal.com\" target=\"_blank\" rel=\"noopener\">\n <IconShield />\n {t.secured}\n </a>\n </div>\n </div>\n </div>,\n document.body\n );\n}\n","/**\n * The ZOREAL mark. Geometry is the tight crop made for small sizes.\n *\n * Two colour modes, because the mark has two jobs. On the button it is\n * `currentColor`, so it inherits whatever the host theme puts on the label. In\n * the pairing modal it is the brand blue, because there it identifies WHOSE\n * request the person is being asked to approve, and an identity check is\n * exactly the wrong place for a mark that changes colour with the page.\n */\nexport const ZOREAL_BLUE = '#00b4d9';\n\nexport function ZorealMark({\n size = 18,\n brand = false,\n className,\n}: {\n size?: number;\n /** Paint the brand blue instead of inheriting currentColor. */\n brand?: boolean;\n className?: string;\n}) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"8.4 7.4 62.2 62.2\"\n fill={brand ? ZOREAL_BLUE : 'currentColor'}\n fillRule=\"evenodd\"\n aria-hidden\n focusable=\"false\"\n className={className}\n >\n <path d=\"M56.1,32.9c.6-3.1-.8-6.4-3.7-8.1l-18-10.4,5.2-3,15.4,8.9c5.4,3.1,7.7,9.6,5.8,15.3-.3.8-.6,1.6-1.1,2.4-.4.7-.9,1.4-1.5,2.1-1.3,1.4-2.9,2.6-4.6,3.3-3.6,1.5-7.9,1.4-11.6-.7l-8.9-5.1c-2.9-1.7-6.5-1.3-8.9.8-.6.6-1.2,1.2-1.7,2-.5.8-.8,1.7-.9,2.5-.6,3.1.9,6.4,3.7,8.1l18,10.4-5.2,3-15.4-8.9c-5.4-3.1-7.7-9.6-5.8-15.3.2-.8.6-1.6,1-2.4.5-.7,1-1.4,1.5-2.1,1.3-1.4,2.9-2.6,4.7-3.3,3.6-1.6,7.8-1.4,11.5.6l8.9,5.2c3,1.7,6.6,1.3,8.9-.8.6-.6,1.2-1.2,1.7-2,.4-.8.7-1.7.9-2.5Z\" />\n <path d=\"M68.7,44.2c-.7,1.2-2.3,1.7-3.5.9-1.3-.7-1.7-2.3-1-3.5.7-1.3,2.3-1.7,3.5-1,1.3.7,1.7,2.3,1,3.6Z\" />\n <path d=\"M25.6,21.3c5.1-.8,10.4,0,15.3,2.9l1.2.7h0c1.2.7,1.6,2.3.9,3.5s-2.3,1.7-3.5.9l-1.2-.7c-4.2-2.4-9.1-3-13.5-1.9-1.6.4-3.1,1.1-4.5,1.9h0c-1.2.7-2.8.3-3.5-1-.7-1.2-.3-2.8.9-3.5,0,0,.1,0,.3-.1.3-.1.6-.4,1-.5,2.1-1.1,4.4-1.7,6.7-2.2Z\" />\n <path d=\"M9.6,31.8c.7-1.2,2.4-1.6,3.5-.8,1.2.7,1.6,2.4.8,3.5-.8,1.2-2.4,1.6-3.6.8-1.2-.8-1.5-2.4-.7-3.5Z\" />\n <path d=\"M46.2,30.3c.7-1.3,2.3-1.7,3.5-1,1.2.7,1.7,2.3.9,3.6-.7,1.2-2.3,1.7-3.5.9s-1.7-2.3-.9-3.5Z\" />\n <path d=\"M52.1,54.5c-5,.9-10.4,0-15.3-2.8l-1.2-.7h0c-1.2-.7-1.7-2.3-.9-3.5s2.3-1.7,3.5-.9l1.2.7c4.3,2.4,9.1,3,13.6,1.9,1.6-.4,3.1-1.1,4.5-1.9h0c1.2-.7,2.8-.3,3.5.9.7,1.3.3,2.9-.9,3.6-.1,0-.2,0-.3,0-.4.2-.7.4-1.1.6-2.1,1-4.3,1.7-6.7,2.1Z\" />\n <path d=\"M31.4,45.6c-.7,1.2-2.3,1.7-3.5.9s-1.7-2.3-.9-3.5,2.3-1.7,3.5-.9,1.7,2.3.9,3.5Z\" />\n </svg>\n );\n}\n","import { ZOREAL_BLUE } from './mark';\n\n/**\n * The full ZOREAL lockup (mark + wordmark), from zoreal-web's\n * `images/logo/zoreal-lockup.svg`, inlined so the modal ships no external asset\n * and renders identically offline and behind a strict CSP.\n *\n * One deliberate change from the source file: the wordmark, `#0e104f` in the\n * master, is `currentColor` here. The lockup sits on the host's page in either\n * theme, and a fixed near-black wordmark disappears against a dark card. The\n * mark keeps the brand blue in both themes — it reads on either ground, and it\n * is the part that identifies whose sign-in this is.\n */\nexport function ZorealLockup({ height = 22, className }: { height?: number; className?: string }) {\n return (\n <svg\n height={height}\n width={height * (240 / 58.5)}\n viewBox=\"0 0 240 58.5\"\n role=\"img\"\n aria-label=\"ZOREAL\"\n focusable=\"false\"\n className={className}\n >\n <path\n fill=\"currentColor\"\n d=\"M205,40.5h15.3v-3.5h-11.5v-18.4h-3.8v21.8ZM157,22.2v5.6h10.9v3.5h-10.9v5.8h12.5v3.5h-16.3v-21.8h16.2v3.5h-12.4ZM141.4,25.8c0,1.1-.4,2-1.2,2.7-.8.7-1.9,1-3.3,1h-5.6v-7.3h5.6c1.4,0,2.6.3,3.4.9.8.6,1.2,1.5,1.2,2.7ZM146,40.5l-5.9-8.3c.8-.2,1.4-.5,2.1-.9s1.2-.9,1.7-1.4c.4-.5.8-1.2,1.1-1.9s.4-1.5.4-2.4-.1-2-.5-2.9c-.4-.9-.9-1.6-1.7-2.2-.6-.6-1.5-1-2.5-1.4-1-.3-2.2-.4-3.4-.4h-9.8v21.8h3.8v-7.6h4.8l5.4,7.6h4.5ZM115.7,29.7c0,1.1-.1,2.1-.5,3s-.9,1.7-1.5,2.4-1.4,1.2-2.4,1.7c-.9.4-1.9.6-3,.6s-2.1-.1-3-.6-1.7-1-2.4-1.7-1.2-1.5-1.5-2.4-.5-1.9-.5-3,.1-2.1.5-3,.9-1.7,1.5-2.4,1.4-1.2,2.4-1.7c.9-.4,1.9-.6,3-.6s2.1.2,3,.6c.9.4,1.7,1,2.3,1.7.6.6,1.2,1.5,1.6,2.4s.5,1.9.5,3ZM119.7,29.6c0-1.5-.3-3-.8-4.4-.6-1.4-1.4-2.5-2.4-3.6-1-1-2.2-1.8-3.6-2.4-1.4-.6-3-.9-4.6-.9s-3.2.4-4.6.9c-1.4.6-2.7,1.4-3.7,2.4s-1.8,2.2-2.4,3.6c-.5,1.4-.8,2.8-.8,4.4s.3,3,.8,4.4c.6,1.4,1.4,2.5,2.4,3.6,1,1,2.2,1.8,3.6,2.4,1.4.6,3,.9,4.6.9s3.2-.4,4.6-.9c1.4-.6,2.6-1.4,3.7-2.4,1-1,1.8-2.2,2.4-3.6.5-1.4.8-2.9.8-4.4ZM86.1,22.1l-13,15.6v2.8h17.9v-3.4h-12.9l12.9-15.6v-2.8h-17.5v3.4h12.5ZM188.5,18.5h-3.5l-9.6,22h4c3.7-8.8,3.4-8,7.4-17.4,3.7,8.8,3.9,9.1,7.4,17.4h4l-9.6-22Z\"\n />\n <g fill={ZOREAL_BLUE} fillRule=\"evenodd\">\n <path d=\"M52,25.7c.4-2-.5-4.2-2.5-5.4l-11.8-6.8,3.4-2,10.1,5.9c3.6,2,5.1,6.3,3.8,10.1-.2.5-.4,1-.7,1.6-.3.5-.6.9-1,1.4-.9.9-1.9,1.7-3,2.2-2.4,1-5.2.9-7.6-.5l-5.9-3.4c-1.9-1.1-4.3-.9-5.9.5-.4.4-.8.8-1.1,1.3-.3.5-.5,1.1-.6,1.7-.4,2,.6,4.2,2.5,5.4l11.8,6.8-3.4,2-10.1-5.9c-3.6-2-5.1-6.3-3.8-10.1.1-.5.4-1,.7-1.6.3-.5.7-.9,1-1.4.9-.9,1.9-1.7,3.1-2.2,2.4-1,5.2-.9,7.6.4l5.9,3.4c1.9,1.1,4.3.9,5.9-.5.4-.4.8-.8,1.1-1.3.3-.5.5-1.1.6-1.7Z\" />\n <path d=\"M60.3,33.1c-.5.8-1.5,1.1-2.3.6-.9-.5-1.1-1.5-.7-2.3.5-.9,1.5-1.1,2.3-.7.9.5,1.1,1.5.7,2.4Z\" />\n <path d=\"M31.9,18c3.4-.5,6.9,0,10,1.9l.8.5h0c.8.5,1,1.5.6,2.3s-1.5,1.1-2.3.6l-.8-.5c-2.8-1.6-6-1.9-8.9-1.2-1,.3-2,.7-3,1.2h0c-.8.5-1.8.2-2.3-.7-.5-.8-.2-1.8.6-2.3,0,0,0,0,.2,0,.2,0,.4-.2.7-.3,1.4-.7,2.9-1.1,4.4-1.4Z\" />\n <path d=\"M21.4,24.9c.5-.8,1.6-1,2.3-.5.8.5,1,1.6.5,2.3-.5.8-1.6,1-2.4.5-.8-.5-1-1.6-.5-2.3Z\" />\n <path d=\"M45.5,23.9c.5-.9,1.5-1.1,2.3-.7.8.5,1.1,1.5.6,2.4-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3Z\" />\n <path d=\"M49.4,39.9c-3.3.6-6.9,0-10-1.8l-.8-.5h0c-.8-.5-1.1-1.5-.6-2.3s1.5-1.1,2.3-.6l.8.5c2.8,1.6,6,1.9,9,1.2,1-.3,2-.7,3-1.2h0c.8-.5,1.8-.2,2.3.6.5.9.2,1.9-.6,2.4,0,0-.1,0-.2,0-.2.1-.5.3-.7.4-1.4.7-2.8,1.1-4.4,1.4Z\" />\n <path d=\"M35.8,34c-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3,1.5-1.1,2.3-.6,1.1,1.5.6,2.3Z\" />\n </g>\n </svg>\n );\n}\n","/**\n * Pairing-modal copy, carried by the SDK.\n *\n * The modal is rendered by this package, so its strings have to ship with it:\n * an integrator cannot translate a component they never write, and asking every\n * one of them to re-supply the same fifteen strings is how a sign-in screen\n * ends up half-English in production.\n *\n * No i18n runtime. A frozen record and one `{time}` substitution is the whole\n * requirement, and a dependency here would be inherited by every host app.\n *\n * Locales match the set ZOREAL's own pairing page serves, so the phone and the\n * browser say the same thing in the same language. `strings()` resolves BCP 47\n * down to that set; anything unknown falls back to English rather than\n * rendering a key.\n */\n\nexport interface PairingStrings {\n /** Dialog title while the code is still unscanned. */\n title: string;\n /** Dialog title when the request is for verified identity attributes. */\n titleIdentify: string;\n /** Dialog title when the request is a presence check and not a login. */\n titlePresence: string;\n /** Dialog title once the request is waiting in the app. */\n titleApprove: string;\n bodyScan: string;\n bodyApprove: string;\n bodyEnrolling: string;\n waiting: string;\n waitingApproval: string;\n /** Carries `{time}`, substituted with mm:ss. */\n expiresIn: string;\n secured: string;\n noIdTitle: string;\n noIdBody: string;\n cancel: string;\n close: string;\n qrAlt: string;\n /** The default label of the sign-in button. */\n buttonContinue: string;\n}\n\nconst en: PairingStrings = {\n title: 'Scan to sign in',\n titleIdentify: 'Scan to verify your identity',\n titlePresence: 'Scan to prove you are a real human',\n titleApprove: 'Approve on your phone',\n bodyScan: 'Scan with your phone camera or the ZOREAL ID app.',\n bodyApprove: 'Approve the login in your ZOREAL ID app.',\n bodyEnrolling: 'Finish setting up ZOREAL ID on your phone, then approve the login.',\n waiting: 'Waiting for scan',\n waitingApproval: 'Waiting for approval',\n expiresIn: 'Expires in {time}',\n secured: 'Proof-of-Human verification by ZOREAL',\n noIdTitle: 'No ZOREAL ID yet?',\n noIdBody: 'Scan the same code to download the app and create one for free. It only takes a minute.',\n cancel: 'Cancel',\n close: 'Close',\n qrAlt: 'QR code to sign in with ZOREAL',\n buttonContinue: 'Continue with ZOREAL',\n};\n\nconst TRANSLATIONS: Record<string, PairingStrings> = {\n en,\n sv: {\n title: 'Skanna för att logga in',\n titleIdentify: 'Skanna för att verifiera din identitet',\n titlePresence: 'Skanna för att bevisa att du är en riktig människa',\n titleApprove: 'Godkänn på telefonen',\n bodyScan: 'Skanna med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkänn inloggningen i ZOREAL ID-appen.',\n bodyEnrolling: 'Slutför konfigurationen av ZOREAL ID på telefonen och godkänn sedan inloggningen.',\n waiting: 'Väntar på skanning',\n waitingApproval: 'Väntar på godkännande',\n expiresIn: 'Upphör om {time}',\n secured: 'Proof-of-Human-verifiering av ZOREAL',\n noIdTitle: 'Har du inget ZOREAL ID?',\n noIdBody: 'Skanna samma kod för att ladda ner appen och skapa ett gratis. Det tar bara en minut.',\n cancel: 'Avbryt',\n close: 'Stäng',\n qrAlt: 'QR-kod för att logga in med ZOREAL',\n buttonContinue: 'Fortsätt med ZOREAL',\n },\n es: {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Apruébalo en tu teléfono',\n bodyScan: 'Escanea con la cámara de tu teléfono o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu teléfono y luego aprueba el inicio de sesión.',\n waiting: 'Esperando el escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Caduca en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Aún no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear una gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n pt: {\n title: 'Digitalize para entrar',\n titleIdentify: 'Digitalize para verificar a sua identidade',\n titlePresence: 'Digitalize para provar que é uma pessoa real',\n titleApprove: 'Aprove no seu telefone',\n bodyScan: 'Digitalize com a câmara do seu telefone ou com a app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu telefone e depois aprove o login.',\n waiting: 'Aguardando digitalização',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem ZOREAL ID?',\n noIdBody: 'Digitalize o mesmo código para baixar o app e criar uma conta grátis. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n fr: {\n title: 'Scannez pour vous connecter',\n titleIdentify: 'Scannez pour vérifier votre identité',\n titlePresence: 'Scannez pour prouver que vous êtes bien un humain',\n titleApprove: 'Approuvez sur votre téléphone',\n bodyScan: \"Scannez avec l'appareil photo de votre téléphone ou l'app ZOREAL ID.\",\n bodyApprove: 'Approuvez la connexion dans votre app ZOREAL ID.',\n bodyEnrolling: 'Terminez la configuration de ZOREAL ID sur votre téléphone, puis approuvez la connexion.',\n waiting: 'En attente du scan',\n waitingApproval: \"En attente d'approbation\",\n expiresIn: 'Expire dans {time}',\n secured: 'Vérification Proof-of-Human par ZOREAL',\n noIdTitle: \"Pas encore de ZOREAL ID ?\",\n noIdBody: \"Scannez le même code pour télécharger l'app et en créer un gratuitement. Cela prend une minute.\",\n cancel: 'Annuler',\n close: 'Fermer',\n qrAlt: 'Code QR pour se connecter avec ZOREAL',\n buttonContinue: 'Continuer avec ZOREAL',\n },\n de: {\n title: 'Zum Anmelden scannen',\n titleIdentify: 'Scannen, um Ihre Identität zu verifizieren',\n titlePresence: 'Scannen, um zu beweisen, dass Sie ein echter Mensch sind',\n titleApprove: 'Auf dem Handy bestätigen',\n bodyScan: 'Mit der Handykamera oder der ZOREAL ID App scannen.',\n bodyApprove: 'Anmeldung in der ZOREAL ID App bestätigen.',\n bodyEnrolling: 'ZOREAL ID auf dem Handy fertig einrichten und dann die Anmeldung bestätigen.',\n waiting: 'Warten auf Scan',\n waitingApproval: 'Warten auf Bestätigung',\n expiresIn: 'Läuft ab in {time}',\n secured: 'Proof-of-Human-Verifizierung von ZOREAL',\n noIdTitle: 'Noch keine ZOREAL ID?',\n noIdBody: 'Denselben Code scannen, um die App zu laden und kostenlos eine zu erstellen. Dauert nur eine Minute.',\n cancel: 'Abbrechen',\n close: 'Schließen',\n qrAlt: 'QR-Code für die Anmeldung mit ZOREAL',\n buttonContinue: 'Weiter mit ZOREAL',\n },\n ru: {\n title: 'Отсканируйте, чтобы войти',\n titleIdentify: 'Отсканируйте, чтобы подтвердить личность',\n titlePresence: 'Отсканируйте, чтобы доказать, что вы реальный человек',\n titleApprove: 'Подтвердите на телефоне',\n bodyScan: 'Отсканируйте камерой телефона или через приложение ZOREAL ID.',\n bodyApprove: 'Подтвердите вход в приложении ZOREAL ID.',\n bodyEnrolling: 'Завершите настройку ZOREAL ID на телефоне, затем подтвердите вход.',\n waiting: 'Ожидание сканирования',\n waitingApproval: 'Ожидание подтверждения',\n expiresIn: 'Истекает через {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Ещё нет ZOREAL ID?',\n noIdBody: 'Отсканируйте тот же код, чтобы скачать приложение и создать его бесплатно. Это займёт минуту.',\n cancel: 'Отмена',\n close: 'Закрыть',\n qrAlt: 'QR-код для входа через ZOREAL',\n buttonContinue: 'Продолжить с ZOREAL',\n },\n ja: {\n title: 'スキャンしてログイン',\n titleIdentify: 'スキャンして本人確認',\n titlePresence: 'スキャンして実在の人物であることを証明',\n titleApprove: 'スマートフォンで承認',\n bodyScan: 'スマートフォンのカメラまたはZOREAL IDアプリでスキャンしてください。',\n bodyApprove: 'ZOREAL IDアプリでログインを承認してください。',\n bodyEnrolling: 'スマートフォンでZOREAL IDの設定を完了し、ログインを承認してください。',\n waiting: 'スキャン待ち',\n waitingApproval: '承認待ち',\n expiresIn: '有効期限まで {time}',\n secured: 'ZOREALによるProof-of-Human認証',\n noIdTitle: 'ZOREAL IDをお持ちでないですか?',\n noIdBody: '同じコードをスキャンしてアプリをダウンロードし、無料で作成できます。1分ほどで完了します。',\n cancel: 'キャンセル',\n close: '閉じる',\n qrAlt: 'ZOREALでログインするためのQRコード',\n buttonContinue: 'ZOREALで続行',\n },\n hi: {\n title: 'साइन इन करने के लिए स्कैन करें',\n titleIdentify: 'अपनी पहचान सत्यापित करने के लिए स्कैन करें',\n titlePresence: 'यह साबित करने के लिए स्कैन करें कि आप एक वास्तविक इंसान हैं',\n titleApprove: 'अपने फोन पर स्वीकृत करें',\n bodyScan: 'अपने फोन के कैमरे या ZOREAL ID ऐप से स्कैन करें।',\n bodyApprove: 'अपने ZOREAL ID ऐप में लॉगिन स्वीकृत करें।',\n bodyEnrolling: 'अपने फोन पर ZOREAL ID सेटअप पूरा करें, फिर लॉगिन स्वीकृत करें।',\n waiting: 'स्कैन की प्रतीक्षा है',\n waitingApproval: 'स्वीकृति की प्रतीक्षा है',\n expiresIn: '{time} में समाप्त',\n secured: 'ZOREAL द्वारा Proof-of-Human सत्यापन',\n noIdTitle: 'अभी तक ZOREAL ID नहीं है?',\n noIdBody: 'ऐप डाउनलोड करने और मुफ्त में एक बनाने के लिए वही कोड स्कैन करें। इसमें बस एक मिनट लगता है।',\n cancel: 'रद्द करें',\n close: 'बंद करें',\n qrAlt: 'ZOREAL से साइन इन करने के लिए QR कोड',\n buttonContinue: 'ZOREAL के साथ जारी रखें',\n },\n zhs: {\n title: '扫码登录',\n titleIdentify: '扫码验证身份',\n titlePresence: '扫码证明您是真人',\n titleApprove: '在手机上批准',\n bodyScan: '使用手机相机或 ZOREAL ID 应用扫描。',\n bodyApprove: '请在 ZOREAL ID 应用中批准登录。',\n bodyEnrolling: '请在手机上完成 ZOREAL ID 设置,然后批准登录。',\n waiting: '等待扫描',\n waitingApproval: '等待批准',\n expiresIn: '{time} 后失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 验证',\n noIdTitle: '还没有 ZOREAL ID?',\n noIdBody: '扫描同一个二维码即可下载应用并免费创建,只需一分钟。',\n cancel: '取消',\n close: '关闭',\n qrAlt: '使用 ZOREAL 登录的二维码',\n buttonContinue: '使用 ZOREAL 继续',\n },\n zht: {\n title: '掃碼登入',\n titleIdentify: '掃碼驗證身分',\n titlePresence: '掃碼證明您是真人',\n titleApprove: '在手機上核准',\n bodyScan: '使用手機相機或 ZOREAL ID 應用程式掃描。',\n bodyApprove: '請在 ZOREAL ID 應用程式中核准登入。',\n bodyEnrolling: '請在手機上完成 ZOREAL ID 設定,然後核准登入。',\n waiting: '等待掃描',\n waitingApproval: '等待核准',\n expiresIn: '{time} 後失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 驗證',\n noIdTitle: '還沒有 ZOREAL ID?',\n noIdBody: '掃描同一個 QR code 即可下載應用程式並免費建立,只需一分鐘。',\n cancel: '取消',\n close: '關閉',\n qrAlt: '使用 ZOREAL 登入的 QR code',\n buttonContinue: '使用 ZOREAL 繼續',\n },\n ar: {\n title: 'امسح لتسجيل الدخول',\n titleIdentify: 'امسح للتحقق من هويتك',\n titlePresence: 'امسح لإثبات أنك إنسان حقيقي',\n titleApprove: 'وافق على هاتفك',\n bodyScan: 'امسح باستخدام كاميرا هاتفك أو تطبيق ZOREAL ID.',\n bodyApprove: 'وافق على تسجيل الدخول في تطبيق ZOREAL ID.',\n bodyEnrolling: 'أكمل إعداد ZOREAL ID على هاتفك، ثم وافق على تسجيل الدخول.',\n waiting: 'في انتظار المسح',\n waitingApproval: 'في انتظار الموافقة',\n expiresIn: 'تنتهي الصلاحية خلال {time}',\n secured: 'التحقق من Proof-of-Human بواسطة ZOREAL',\n noIdTitle: 'ليس لديك ZOREAL ID بعد؟',\n noIdBody: 'امسح الرمز نفسه لتنزيل التطبيق وإنشاء حساب مجاني. يستغرق الأمر دقيقة واحدة فقط.',\n cancel: 'إلغاء',\n close: 'إغلاق',\n qrAlt: 'رمز QR لتسجيل الدخول باستخدام ZOREAL',\n buttonContinue: 'المتابعة باستخدام ZOREAL',\n },\n ko: {\n title: '스캔하여 로그인',\n titleIdentify: '스캔하여 신원 확인',\n titlePresence: '스캔하여 실제 사람임을 증명',\n titleApprove: '휴대폰에서 승인',\n bodyScan: '휴대폰 카메라 또는 ZOREAL ID 앱으로 스캔하세요.',\n bodyApprove: 'ZOREAL ID 앱에서 로그인을 승인하세요.',\n bodyEnrolling: '휴대폰에서 ZOREAL ID 설정을 완료한 후 로그인을 승인하세요.',\n waiting: '스캔 대기 중',\n waitingApproval: '승인 대기 중',\n expiresIn: '{time} 후 만료',\n secured: 'ZOREAL의 Proof-of-Human 인증',\n noIdTitle: '아직 ZOREAL ID가 없으신가요?',\n noIdBody: '같은 코드를 스캔해 앱을 내려받고 무료로 만드세요. 1분이면 됩니다.',\n cancel: '취소',\n close: '닫기',\n qrAlt: 'ZOREAL로 로그인하기 위한 QR 코드',\n buttonContinue: 'ZOREAL로 계속',\n },\n // Български\n bg: {\n title: 'Сканирайте за вход',\n titleIdentify: 'Сканирайте, за да потвърдите самоличността си',\n titlePresence: 'Сканирайте, за да докажете, че сте истински човек',\n titleApprove: 'Потвърдете на телефона си',\n bodyScan: 'Сканирайте с камерата на телефона или с приложението ZOREAL ID.',\n bodyApprove: 'Потвърдете входа в приложението ZOREAL ID.',\n bodyEnrolling: 'Довършете настройката на ZOREAL ID на телефона си, след което потвърдете входа.',\n waiting: 'Изчакване на сканиране',\n waitingApproval: 'Изчакване на потвърждение',\n expiresIn: 'Изтича след {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Все още нямате ZOREAL ID?',\n noIdBody: 'Сканирайте същия код, за да изтеглите приложението и да си създадете безплатен акаунт. Отнема само минута.',\n cancel: 'Отказ',\n close: 'Затвори',\n qrAlt: 'QR код за вход със ZOREAL',\n buttonContinue: 'Продължи със ZOREAL',\n },\n // বাংলা\n bn: {\n title: 'সাইন ইন করতে স্ক্যান করুন',\n titleIdentify: 'আপনার পরিচয় যাচাই করতে স্ক্যান করুন',\n titlePresence: 'আপনি একজন প্রকৃত মানুষ তা প্রমাণ করতে স্ক্যান করুন',\n titleApprove: 'আপনার ফোনে অনুমোদন করুন',\n bodyScan: 'আপনার ফোনের ক্যামেরা বা ZOREAL ID অ্যাপ দিয়ে স্ক্যান করুন।',\n bodyApprove: 'আপনার ZOREAL ID অ্যাপে লগইন অনুমোদন করুন।',\n bodyEnrolling: 'আপনার ফোনে ZOREAL ID সেটআপ সম্পূর্ণ করুন, তারপর লগইন অনুমোদন করুন।',\n waiting: 'স্ক্যানের অপেক্ষায়',\n waitingApproval: 'অনুমোদনের অপেক্ষায়',\n expiresIn: '{time} পরে মেয়াদ শেষ হবে',\n secured: 'ZOREAL দ্বারা Proof-of-Human যাচাইকরণ',\n noIdTitle: 'এখনো ZOREAL ID নেই?',\n noIdBody: 'অ্যাপ ডাউনলোড করে বিনামূল্যে একটি তৈরি করতে একই কোড স্ক্যান করুন। এতে মাত্র এক মিনিট সময় লাগে।',\n cancel: 'বাতিল',\n close: 'বন্ধ',\n qrAlt: 'ZOREAL দিয়ে সাইন ইন করার জন্য QR কোড',\n buttonContinue: 'ZOREAL দিয়ে চালিয়ে যান',\n },\n // Bosanski\n bs: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte da potvrdite svoj identitet',\n titlePresence: 'Skenirajte da dokažete da ste stvarna osoba',\n titleApprove: 'Odobrite na svom telefonu',\n bodyScan: 'Skenirajte kamerom svog telefona ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Završite podešavanje ZOREAL ID-a na svom telefonu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human verifikacija',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga napravite. Traje samo minutu.',\n cancel: 'Otkaži',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Čeština\n cs: {\n title: 'Přihlaste se naskenováním',\n titleIdentify: 'Naskenujte pro ověření totožnosti',\n titlePresence: 'Naskenujte a prokažte, že jste skutečný člověk',\n titleApprove: 'Potvrďte v telefonu',\n bodyScan: 'Naskenujte fotoaparátem telefonu nebo aplikací ZOREAL ID.',\n bodyApprove: 'Potvrďte přihlášení v aplikaci ZOREAL ID.',\n bodyEnrolling: 'Dokončete nastavení ZOREAL ID v telefonu a poté potvrďte přihlášení.',\n waiting: 'Čekání na naskenování',\n waitingApproval: 'Čekání na potvrzení',\n expiresIn: 'Vyprší za {time}',\n secured: 'Ověření Proof-of-Human od ZOREAL',\n noIdTitle: 'Ještě nemáte ZOREAL ID?',\n noIdBody: 'Naskenováním stejného kódu si stáhnete aplikaci a zdarma vytvoříte ZOREAL ID. Zabere to jen minutu.',\n cancel: 'Zrušit',\n close: 'Zavřít',\n qrAlt: 'QR kód pro přihlášení pomocí ZOREAL',\n buttonContinue: 'Pokračovat se ZOREAL',\n },\n // Dansk\n da: {\n title: 'Scan for at logge ind',\n titleIdentify: 'Scan for at bekræfte din identitet',\n titlePresence: 'Scan for at bevise, at du er et rigtigt menneske',\n titleApprove: 'Godkend på din telefon',\n bodyScan: 'Scan med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkend login i din ZOREAL ID-app.',\n bodyEnrolling: 'Færdiggør opsætningen af ZOREAL ID på din telefon, og godkend derefter login.',\n waiting: 'Venter på scanning',\n waitingApproval: 'Venter på godkendelse',\n expiresIn: 'Udløber om {time}',\n secured: 'Proof-of-Human-verificering af ZOREAL',\n noIdTitle: 'Har du ikke et ZOREAL ID endnu?',\n noIdBody: 'Scan den samme kode for at hente appen og oprette et gratis. Det tager kun et minut.',\n cancel: 'Annuller',\n close: 'Luk',\n qrAlt: 'QR-kode til at logge ind med ZOREAL',\n buttonContinue: 'Fortsæt med ZOREAL',\n },\n // Ελληνικά\n el: {\n title: 'Σάρωση για σύνδεση',\n titleIdentify: 'Σάρωση για επαλήθευση ταυτότητας',\n titlePresence: 'Σάρωση για να αποδείξετε ότι είστε πραγματικός άνθρωπος',\n titleApprove: 'Έγκριση από το κινητό σας',\n bodyScan: 'Σαρώστε με την κάμερα του κινητού σας ή την εφαρμογή ZOREAL ID.',\n bodyApprove: 'Εγκρίνετε τη σύνδεση στην εφαρμογή ZOREAL ID.',\n bodyEnrolling: 'Ολοκληρώστε τη ρύθμιση του ZOREAL ID στο κινητό σας και έπειτα εγκρίνετε τη σύνδεση.',\n waiting: 'Αναμονή σάρωσης',\n waitingApproval: 'Αναμονή έγκρισης',\n expiresIn: 'Λήγει σε {time}',\n secured: 'Επαλήθευση Proof-of-Human από τη ZOREAL',\n noIdTitle: 'Δεν έχετε ακόμα ZOREAL ID;',\n noIdBody: 'Σαρώστε τον ίδιο κωδικό για να κατεβάσετε την εφαρμογή και να δημιουργήσετε ένα δωρεάν. Χρειάζεται μόνο ένα λεπτό.',\n cancel: 'Άκυρο',\n close: 'Κλείσιμο',\n qrAlt: 'Κωδικός QR για σύνδεση με ZOREAL',\n buttonContinue: 'Συνέχεια με ZOREAL',\n },\n // Español (LA)\n 'es-419': {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Aprueba desde tu celular',\n bodyScan: 'Escanea con la cámara de tu celular o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu celular y luego aprueba el inicio de sesión.',\n waiting: 'Esperando escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Expira en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Todavía no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear uno gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n // Suomi\n fi: {\n title: 'Kirjaudu sisään skannaamalla',\n titleIdentify: 'Vahvista henkilöllisyytesi skannaamalla',\n titlePresence: 'Todista skannaamalla, että olet oikea ihminen',\n titleApprove: 'Hyväksy puhelimessasi',\n bodyScan: 'Skannaa puhelimesi kameralla tai ZOREAL ID -sovelluksella.',\n bodyApprove: 'Hyväksy kirjautuminen ZOREAL ID -sovelluksessasi.',\n bodyEnrolling: 'Viimeistele ZOREAL ID -sovelluksen käyttöönotto puhelimellasi ja hyväksy sitten kirjautuminen.',\n waiting: 'Odotetaan skannausta',\n waitingApproval: 'Odotetaan hyväksyntää',\n expiresIn: 'Vanhenee {time} kuluttua',\n secured: 'ZOREALin Proof-of-Human-vahvistus',\n noIdTitle: 'Eikö sinulla ole vielä ZOREAL ID:tä?',\n noIdBody: 'Skannaa sama koodi ladataksesi sovelluksen ja luodaksesi tunnuksen ilmaiseksi. Se vie vain minuutin.',\n cancel: 'Peruuta',\n close: 'Sulje',\n qrAlt: 'QR-koodi ZOREAL-kirjautumista varten',\n buttonContinue: 'Jatka ZOREALilla',\n },\n // עברית\n he: {\n title: 'סרוק כדי להתחבר',\n titleIdentify: 'סרוק כדי לאמת את זהותך',\n titlePresence: 'סרוק כדי להוכיח שאתה אדם אמיתי',\n titleApprove: 'אשר בטלפון שלך',\n bodyScan: 'סרוק באמצעות מצלמת הטלפון שלך או אפליקציית ZOREAL ID.',\n bodyApprove: 'אשר את ההתחברות באפליקציית ZOREAL ID שלך.',\n bodyEnrolling: 'סיים להגדיר את ZOREAL ID בטלפון שלך, ואז אשר את ההתחברות.',\n waiting: 'ממתין לסריקה',\n waitingApproval: 'ממתין לאישור',\n expiresIn: 'יפוג בעוד {time}',\n secured: 'אימות Proof-of-Human מבית ZOREAL',\n noIdTitle: 'עדיין אין לך ZOREAL ID?',\n noIdBody: 'סרוק את אותו הקוד כדי להוריד את האפליקציה וליצור אחד בחינם. זה לוקח רק דקה.',\n cancel: 'ביטול',\n close: 'סגור',\n qrAlt: 'קוד QR להתחברות עם ZOREAL',\n buttonContinue: 'המשך עם ZOREAL',\n },\n // Hrvatski\n hr: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte za potvrdu identiteta',\n titlePresence: 'Skenirajte kako biste dokazali da ste stvarna osoba',\n titleApprove: 'Odobrite na svom mobitelu',\n bodyScan: 'Skenirajte kamerom svog mobitela ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Dovršite postavljanje ZOREAL ID-a na svom mobitelu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human provjera',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga izradite. Traje samo minutu.',\n cancel: 'Odustani',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Magyar\n hu: {\n title: 'Bejelentkezés beolvasással',\n titleIdentify: 'Olvassa be a személyazonossága igazolásához',\n titlePresence: 'Olvassa be annak igazolásához, hogy valódi ember',\n titleApprove: 'Jóváhagyás a telefonján',\n bodyScan: 'Olvassa be a telefonja kamerájával, vagy a ZOREAL ID alkalmazással.',\n bodyApprove: 'Hagyja jóvá a bejelentkezést a ZOREAL ID alkalmazásban.',\n bodyEnrolling: 'Fejezze be a ZOREAL ID beállítását a telefonján, majd hagyja jóvá a bejelentkezést.',\n waiting: 'Várakozás beolvasásra',\n waitingApproval: 'Várakozás jóváhagyásra',\n expiresIn: 'Lejár {time} múlva',\n secured: 'Proof-of-Human hitelesítés a ZOREAL-tól',\n noIdTitle: 'Még nincs ZOREAL ID-je?',\n noIdBody: 'Olvassa be ugyanazt a kódot az alkalmazás letöltéséhez, és hozzon létre egyet ingyenesen. Mindössze egy percet vesz igénybe.',\n cancel: 'Mégse',\n close: 'Bezárás',\n qrAlt: 'QR-kód a ZOREAL-lal való bejelentkezéshez',\n buttonContinue: 'Folytatás a ZOREAL-lal',\n },\n // Bahasa Indonesia\n id: {\n title: 'Pindai untuk masuk',\n titleIdentify: 'Pindai untuk memverifikasi identitas Anda',\n titlePresence: 'Pindai untuk membuktikan bahwa Anda manusia sungguhan',\n titleApprove: 'Setujui di ponsel Anda',\n bodyScan: 'Pindai dengan kamera ponsel atau aplikasi ZOREAL ID.',\n bodyApprove: 'Setujui proses masuk di aplikasi ZOREAL ID Anda.',\n bodyEnrolling: 'Selesaikan pengaturan ZOREAL ID di ponsel Anda, lalu setujui proses masuk.',\n waiting: 'Menunggu pemindaian',\n waitingApproval: 'Menunggu persetujuan',\n expiresIn: 'Berakhir dalam {time}',\n secured: 'Verifikasi Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum punya ZOREAL ID?',\n noIdBody: 'Pindai kode yang sama untuk mengunduh aplikasi dan membuat akun secara gratis. Hanya butuh waktu satu menit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kode QR untuk masuk dengan ZOREAL',\n buttonContinue: 'Lanjutkan dengan ZOREAL',\n },\n // Italiano\n it: {\n title: 'Scansiona per accedere',\n titleIdentify: 'Scansiona per verificare la tua identità',\n titlePresence: 'Scansiona per dimostrare di essere una persona reale',\n titleApprove: 'Approva sul tuo telefono',\n bodyScan: 'Scansiona con la fotocamera del telefono o con l\\'app ZOREAL ID.',\n bodyApprove: 'Approva l\\'accesso nell\\'app ZOREAL ID.',\n bodyEnrolling: 'Completa la configurazione di ZOREAL ID sul telefono, poi approva l\\'accesso.',\n waiting: 'In attesa della scansione',\n waitingApproval: 'In attesa di approvazione',\n expiresIn: 'Scade tra {time}',\n secured: 'Verifica Proof-of-Human di ZOREAL',\n noIdTitle: 'Non hai ancora uno ZOREAL ID?',\n noIdBody: 'Scansiona lo stesso codice per scaricare l\\'app e crearne uno gratis. Basta un minuto.',\n cancel: 'Annulla',\n close: 'Chiudi',\n qrAlt: 'Codice QR per accedere con ZOREAL',\n buttonContinue: 'Continua con ZOREAL',\n },\n // Bahasa Melayu\n ms: {\n title: 'Imbas untuk log masuk',\n titleIdentify: 'Imbas untuk mengesahkan identiti anda',\n titlePresence: 'Imbas untuk membuktikan anda manusia sebenar',\n titleApprove: 'Luluskan di telefon anda',\n bodyScan: 'Imbas dengan kamera telefon atau aplikasi ZOREAL ID.',\n bodyApprove: 'Luluskan log masuk dalam aplikasi ZOREAL ID anda.',\n bodyEnrolling: 'Selesaikan persediaan ZOREAL ID di telefon anda, kemudian luluskan log masuk.',\n waiting: 'Menunggu imbasan',\n waitingApproval: 'Menunggu kelulusan',\n expiresIn: 'Tamat tempoh dalam {time}',\n secured: 'Pengesahan Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum ada ZOREAL ID?',\n noIdBody: 'Imbas kod yang sama untuk memuat turun aplikasi dan cipta satu secara percuma. Hanya mengambil masa seminit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kod QR untuk log masuk dengan ZOREAL',\n buttonContinue: 'Teruskan dengan ZOREAL',\n },\n // Nederlands\n nl: {\n title: 'Scan om in te loggen',\n titleIdentify: 'Scan om je identiteit te verifiëren',\n titlePresence: 'Scan om te bewijzen dat je een echt mens bent',\n titleApprove: 'Keur goed op je telefoon',\n bodyScan: 'Scan met de camera van je telefoon of de ZOREAL ID-app.',\n bodyApprove: 'Keur de aanmelding goed in je ZOREAL ID-app.',\n bodyEnrolling: 'Rond het instellen van ZOREAL ID op je telefoon af en keur daarna de aanmelding goed.',\n waiting: 'Wachten op scan',\n waitingApproval: 'Wachten op goedkeuring',\n expiresIn: 'Verloopt over {time}',\n secured: 'Proof-of-Human-verificatie door ZOREAL',\n noIdTitle: 'Nog geen ZOREAL ID?',\n noIdBody: 'Scan dezelfde code om de app te downloaden en gratis een account aan te maken. Dit duurt maar een minuut.',\n cancel: 'Annuleren',\n close: 'Sluiten',\n qrAlt: 'QR-code om in te loggen met ZOREAL',\n buttonContinue: 'Doorgaan met ZOREAL',\n },\n // Norsk\n no: {\n title: 'Skann for å logge inn',\n titleIdentify: 'Skann for å bekrefte identiteten din',\n titlePresence: 'Skann for å bevise at du er et ekte menneske',\n titleApprove: 'Godkjenn på telefonen din',\n bodyScan: 'Skann med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkjenn innloggingen i ZOREAL ID-appen din.',\n bodyEnrolling: 'Fullfør oppsettet av ZOREAL ID på telefonen din, og godkjenn deretter innloggingen.',\n waiting: 'Venter på skanning',\n waitingApproval: 'Venter på godkjenning',\n expiresIn: 'Utløper om {time}',\n secured: 'Proof-of-Human-verifisering av ZOREAL',\n noIdTitle: 'Har du ikke ZOREAL ID ennå?',\n noIdBody: 'Skann den samme koden for å laste ned appen og opprette en gratis. Det tar bare et minutt.',\n cancel: 'Avbryt',\n close: 'Lukk',\n qrAlt: 'QR-kode for å logge inn med ZOREAL',\n buttonContinue: 'Fortsett med ZOREAL',\n },\n // Polski\n pl: {\n title: 'Zeskanuj, aby się zalogować',\n titleIdentify: 'Zeskanuj, aby zweryfikować swoją tożsamość',\n titlePresence: 'Zeskanuj, aby udowodnić, że jesteś prawdziwym człowiekiem',\n titleApprove: 'Zatwierdź w telefonie',\n bodyScan: 'Zeskanuj aparatem telefonu lub aplikacją ZOREAL ID.',\n bodyApprove: 'Zatwierdź logowanie w aplikacji ZOREAL ID.',\n bodyEnrolling: 'Dokończ konfigurację ZOREAL ID w telefonie, a następnie zatwierdź logowanie.',\n waiting: 'Czekanie na skan',\n waitingApproval: 'Czekanie na zatwierdzenie',\n expiresIn: 'Wygasa za {time}',\n secured: 'Weryfikacja Proof-of-Human od ZOREAL',\n noIdTitle: 'Nie masz jeszcze ZOREAL ID?',\n noIdBody: 'Zeskanuj ten sam kod, aby pobrać aplikację i bezpłatnie utworzyć ZOREAL ID. Zajmie to tylko minutę.',\n cancel: 'Anuluj',\n close: 'Zamknij',\n qrAlt: 'Kod QR do logowania za pomocą ZOREAL',\n buttonContinue: 'Kontynuuj z ZOREAL',\n },\n // Português (BR)\n 'pt-br': {\n title: 'Escaneie para entrar',\n titleIdentify: 'Escaneie para verificar sua identidade',\n titlePresence: 'Escaneie para provar que você é uma pessoa real',\n titleApprove: 'Aprove no seu celular',\n bodyScan: 'Escaneie com a câmera do seu celular ou com o app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu celular e depois aprove o login.',\n waiting: 'Aguardando escaneamento',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem um ZOREAL ID?',\n noIdBody: 'Escaneie o mesmo código para baixar o app e criar um de graça. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n // Română\n ro: {\n title: 'Scanați pentru conectare',\n titleIdentify: 'Scanați pentru a vă verifica identitatea',\n titlePresence: 'Scanați pentru a dovedi că sunteți o persoană reală',\n titleApprove: 'Aprobați de pe telefon',\n bodyScan: 'Scanați cu camera telefonului sau cu aplicația ZOREAL ID.',\n bodyApprove: 'Aprobați conectarea în aplicația ZOREAL ID.',\n bodyEnrolling: 'Finalizați configurarea ZOREAL ID pe telefon, apoi aprobați conectarea.',\n waiting: 'Se așteaptă scanarea',\n waitingApproval: 'Se așteaptă aprobarea',\n expiresIn: 'Expiră în {time}',\n secured: 'Verificare Proof-of-Human de la ZOREAL',\n noIdTitle: 'Nu aveți încă un ZOREAL ID?',\n noIdBody: 'Scanați același cod pentru a descărca aplicația și a crea unul gratuit. Durează doar un minut.',\n cancel: 'Anulează',\n close: 'Închide',\n qrAlt: 'Cod QR pentru conectare cu ZOREAL',\n buttonContinue: 'Continuați cu ZOREAL',\n },\n // Српски\n sr: {\n title: 'Скенирајте за пријаву',\n titleIdentify: 'Скенирајте да потврдите свој идентитет',\n titlePresence: 'Скенирајте да докажете да сте права особа',\n titleApprove: 'Одобрите на свом телефону',\n bodyScan: 'Скенирајте камером свог телефона или апликацијом ZOREAL ID.',\n bodyApprove: 'Одобрите пријаву у апликацији ZOREAL ID.',\n bodyEnrolling: 'Довршите подешавање ZOREAL ID-а на свом телефону, па одобрите пријаву.',\n waiting: 'Чека се скенирање',\n waitingApproval: 'Чека се одобрење',\n expiresIn: 'Истиче за {time}',\n secured: 'ZOREAL Proof-of-Human верификација',\n noIdTitle: 'Немате ZOREAL ID?',\n noIdBody: 'Скенирајте исти код да преузмете апликацију и бесплатно га направите. Траје само минут.',\n cancel: 'Откажи',\n close: 'Затвори',\n qrAlt: 'QR код за пријаву преко ZOREAL-а',\n buttonContinue: 'Настави са ZOREAL-ом',\n },\n // ไทย\n th: {\n title: 'สแกนเพื่อเข้าสู่ระบบ',\n titleIdentify: 'สแกนเพื่อยืนยันตัวตนของคุณ',\n titlePresence: 'สแกนเพื่อพิสูจน์ว่าคุณเป็นมนุษย์จริง',\n titleApprove: 'อนุมัติบนโทรศัพท์ของคุณ',\n bodyScan: 'สแกนด้วยกล้องโทรศัพท์หรือแอป ZOREAL ID',\n bodyApprove: 'อนุมัติการเข้าสู่ระบบในแอป ZOREAL ID ของคุณ',\n bodyEnrolling: 'ตั้งค่า ZOREAL ID บนโทรศัพท์ของคุณให้เสร็จสิ้น แล้วอนุมัติการเข้าสู่ระบบ',\n waiting: 'รอการสแกน',\n waitingApproval: 'รอการอนุมัติ',\n expiresIn: 'หมดอายุใน {time}',\n secured: 'การยืนยันตัวตน Proof-of-Human โดย ZOREAL',\n noIdTitle: 'ยังไม่มี ZOREAL ID ใช่ไหม',\n noIdBody: 'สแกนโค้ดเดียวกันเพื่อดาวน์โหลดแอปและสร้างบัญชีฟรี ใช้เวลาเพียงนาทีเดียว',\n cancel: 'ยกเลิก',\n close: 'ปิด',\n qrAlt: 'คิวอาร์โค้ดสำหรับเข้าสู่ระบบด้วย ZOREAL',\n buttonContinue: 'ดำเนินการต่อด้วย ZOREAL',\n },\n // Tagalog\n tl: {\n title: 'I-scan para mag-sign in',\n titleIdentify: 'I-scan para i-verify ang iyong pagkakakilanlan',\n titlePresence: 'I-scan para patunayang tunay kang tao',\n titleApprove: 'I-approve sa iyong telepono',\n bodyScan: 'I-scan gamit ang camera ng iyong telepono o ang ZOREAL ID app.',\n bodyApprove: 'I-approve ang login sa iyong ZOREAL ID app.',\n bodyEnrolling: 'Tapusin muna ang pag-set up ng ZOREAL ID sa iyong telepono, pagkatapos ay i-approve ang login.',\n waiting: 'Naghihintay ng scan',\n waitingApproval: 'Naghihintay ng approval',\n expiresIn: 'Mag-e-expire sa {time}',\n secured: 'Proof-of-Human verification mula sa ZOREAL',\n noIdTitle: 'Wala ka pang ZOREAL ID?',\n noIdBody: 'I-scan ang parehong code para i-download ang app at gumawa ng iyong ZOREAL ID nang libre. Isang minuto lang ito.',\n cancel: 'Kanselahin',\n close: 'Isara',\n qrAlt: 'QR code para mag-sign in gamit ang ZOREAL',\n buttonContinue: 'Magpatuloy gamit ang ZOREAL',\n },\n // Türkçe\n tr: {\n title: 'Giriş için tarayın',\n titleIdentify: 'Kimliğinizi doğrulamak için tarayın',\n titlePresence: 'Gerçek bir insan olduğunuzu kanıtlamak için tarayın',\n titleApprove: 'Telefonunuzdan onaylayın',\n bodyScan: 'Telefonunuzun kamerasıyla veya ZOREAL ID uygulamasıyla tarayın.',\n bodyApprove: 'Girişi ZOREAL ID uygulamanızdan onaylayın.',\n bodyEnrolling: 'Telefonunuzda ZOREAL ID kurulumunu tamamlayın, ardından girişi onaylayın.',\n waiting: 'Tarama bekleniyor',\n waitingApproval: 'Onay bekleniyor',\n expiresIn: '{time} içinde sona erer',\n secured: 'ZOREAL tarafından Proof-of-Human doğrulaması',\n noIdTitle: 'Henüz ZOREAL ID\\'niz yok mu?',\n noIdBody: 'Uygulamayı indirmek ve ücretsiz bir tane oluşturmak için aynı kodu tarayın. Sadece bir dakikanızı alır.',\n cancel: 'İptal',\n close: 'Kapat',\n qrAlt: 'ZOREAL ile giriş yapmak için QR kodu',\n buttonContinue: 'ZOREAL ile devam et',\n },\n // Українська\n uk: {\n title: 'Скануйте для входу',\n titleIdentify: 'Скануйте, щоб підтвердити особу',\n titlePresence: 'Скануйте, щоб довести, що ви справжня людина',\n titleApprove: 'Підтвердьте на телефоні',\n bodyScan: 'Скануйте камерою телефону або додатком ZOREAL ID.',\n bodyApprove: 'Підтвердьте вхід у додатку ZOREAL ID.',\n bodyEnrolling: 'Завершіть налаштування ZOREAL ID на телефоні, а потім підтвердьте вхід.',\n waiting: 'Очікування сканування',\n waitingApproval: 'Очікування підтвердження',\n expiresIn: 'Спливає через {time}',\n secured: 'Перевірка Proof-of-Human від ZOREAL',\n noIdTitle: 'Ще немає ZOREAL ID?',\n noIdBody: 'Скануйте той самий код, щоб завантажити додаток і безкоштовно створити його. Це займе лише хвилину.',\n cancel: 'Скасувати',\n close: 'Закрити',\n qrAlt: 'QR-код для входу через ZOREAL',\n buttonContinue: 'Продовжити з ZOREAL',\n },\n // اردو\n ur: {\n title: 'لاگ اِن کرنے کے لیے اسکین کریں',\n titleIdentify: 'اپنی شناخت کی تصدیق کے لیے اسکین کریں',\n titlePresence: 'یہ ثابت کرنے کے لیے اسکین کریں کہ آپ ایک حقیقی انسان ہیں',\n titleApprove: 'اپنے فون پر منظوری دیں',\n bodyScan: 'اپنے فون کے کیمرے یا ZOREAL ID ایپ سے اسکین کریں۔',\n bodyApprove: 'اپنی ZOREAL ID ایپ میں لاگ اِن کی منظوری دیں۔',\n bodyEnrolling: 'اپنے فون پر ZOREAL ID کی سیٹ اپ مکمل کریں، پھر لاگ اِن کی منظوری دیں۔',\n waiting: 'اسکین کا انتظار',\n waitingApproval: 'منظوری کا انتظار',\n expiresIn: '{time} میں ختم ہوگا',\n secured: 'ZOREAL کی جانب سے Proof-of-Human تصدیق',\n noIdTitle: 'ابھی تک ZOREAL ID نہیں ہے؟',\n noIdBody: 'ایپ ڈاؤن لوڈ کرنے اور مفت میں ایک بنانے کے لیے وہی کوڈ اسکین کریں۔ اس میں صرف ایک منٹ لگتا ہے۔',\n cancel: 'منسوخ کریں',\n close: 'بند کریں',\n qrAlt: 'ZOREAL کے ساتھ لاگ اِن کرنے کے لیے QR کوڈ',\n buttonContinue: 'ZOREAL کے ساتھ جاری رکھیں',\n },\n // Tiếng Việt\n vi: {\n title: 'Quét để đăng nhập',\n titleIdentify: 'Quét để xác minh danh tính của bạn',\n titlePresence: 'Quét để chứng minh bạn là người thật',\n titleApprove: 'Phê duyệt trên điện thoại của bạn',\n bodyScan: 'Quét bằng camera điện thoại hoặc ứng dụng ZOREAL ID.',\n bodyApprove: 'Phê duyệt đăng nhập trong ứng dụng ZOREAL ID của bạn.',\n bodyEnrolling: 'Hoàn tất thiết lập ZOREAL ID trên điện thoại, sau đó phê duyệt đăng nhập.',\n waiting: 'Đang chờ quét mã',\n waitingApproval: 'Đang chờ phê duyệt',\n expiresIn: 'Hết hạn sau {time}',\n secured: 'Xác minh Proof-of-Human bởi ZOREAL',\n noIdTitle: 'Chưa có ZOREAL ID?',\n noIdBody: 'Quét cùng mã này để tải ứng dụng và tạo tài khoản miễn phí. Chỉ mất một phút.',\n cancel: 'Hủy',\n close: 'Đóng',\n qrAlt: 'Mã QR để đăng nhập bằng ZOREAL',\n buttonContinue: 'Tiếp tục với ZOREAL',\n },\n};\n\n/** Locales whose script runs right to left, so the dialog flips with `dir`. */\n// Only languages we actually carry. Listing an RTL language we do not\n// translate would flip the dialog for someone who is then shown the English\n// fallback — LTR text in an RTL container, which is worse than either alone.\nconst RTL = new Set(['ar', 'he', 'iw', 'ur']);\n\n/**\n * One BCP 47 tag to a translation, or undefined if we do not carry it.\n *\n * Chinese is the only case needing more than the primary subtag: `zh-Hans` /\n * `zh-CN` / `zh-SG` are Simplified, everything else `zh` is treated as\n * Traditional, matching how the pairing page splits them.\n */\n/**\n * Primary subtags that reach the same table under another name: superseded ISO\n * codes some platforms still emit, and the written standards we carry one entry\n * for. Without these a Norwegian browser sending `nb` gets English while `no`\n * sits right there in the table.\n */\nconst ALIASES: Record<string, string> = {\n nb: 'no', // Bokmål — what we actually wrote\n nn: 'no', // Nynorsk reader, served Bokmål: closer than English\n fil: 'tl', // Filipino / Tagalog\n iw: 'he', // superseded code for Hebrew, still emitted by some platforms\n in: 'id', // superseded code for Indonesian\n};\n\n/**\n * Spanish and Portuguese ship two variants each, and the split that matters is\n * not the language but the side of the Atlantic. A `es-MX` browser resolving to\n * peninsular Spanish is the kind of near-miss that reads as nobody having\n * thought about it, so the Latin American regions are named explicitly.\n */\nconst LATAM = new Set([\n 'ar', 'bo', 'cl', 'co', 'cr', 'cu', 'do', 'ec', 'gt', 'hn',\n 'mx', 'ni', 'pa', 'pe', 'pr', 'py', 'sv', 'uy', 've', '419',\n]);\n\nfunction lookup(locale: string): PairingStrings | undefined {\n const tag = locale.toLowerCase().replace(/_/g, '-');\n const parts = tag.split('-');\n const primary = ALIASES[parts[0]] ?? parts[0];\n const region = parts[1];\n\n // Script, not region, is what separates these two.\n if (primary === 'zh') {\n const simplified = /(^|-)(hans|cn|sg|my)(-|$)/.test(tag);\n return TRANSLATIONS[simplified ? 'zhs' : 'zht'];\n }\n if (primary === 'es' && region && LATAM.has(region)) return TRANSLATIONS['es-419'];\n if (primary === 'pt' && region === 'br') return TRANSLATIONS['pt-br'];\n\n return TRANSLATIONS[tag] ?? TRANSLATIONS[primary];\n}\n\n/**\n * What the browser says the person reads, best first. `languages` is the whole\n * ordered preference list, which matters: someone whose first choice we do not\n * carry may well have a second we do, and falling straight to English would\n * skip it.\n */\nfunction browserLocales(): string[] {\n if (typeof navigator === 'undefined') return [];\n const nav = navigator as Navigator & { languages?: readonly string[] };\n if (nav.languages && nav.languages.length) return [...nav.languages];\n return nav.language ? [nav.language] : [];\n}\n\n/**\n * The strings to render.\n *\n * An explicit `locale` (from the provider) wins outright: the host app knows\n * which language it is currently showing, and the modal must not disagree with\n * the page it opened on. With none given we follow the browser's own preference\n * list, so an integrator who never sets `locale` still gets a translated modal\n * instead of English-by-default. Anything we do not carry falls back to English\n * rather than rendering a key.\n */\nexport function strings(locale?: string): PairingStrings {\n if (locale) return lookup(locale) ?? en;\n for (const candidate of browserLocales()) {\n const hit = lookup(candidate);\n if (hit) return hit;\n }\n return en;\n}\n\nexport function isRtl(locale?: string): boolean {\n const tag = locale ?? browserLocales()[0];\n if (!tag) return false;\n return RTL.has(tag.toLowerCase().replace(/_/g, '-').split('-')[0]);\n}\n\n/** The one substitution the copy needs. */\nexport function interpolate(template: string, time: string): string {\n return template.replace('{time}', time);\n}\n","import type { PairingStrings } from './i18n';\nimport type { LoginIntent } from './types';\n\n/**\n * The scopes a relying party asks for in order to know who is signing in:\n * the identifier, and how to reach and address the person. Anything beyond\n * these is an attribute read from the identity document, and the dialog\n * should say that it is about to be shared rather than call it a sign-in.\n */\nconst SIGN_IN_SCOPES = new Set(['openid', 'email', 'profile.name']);\n\n/**\n * What the pairing dialog says it is for. An explicit intent wins. Otherwise\n * a request for document attributes is an identification; a request for the\n * identifier alone with a liveness capture is a presence check, since nothing\n * is being logged into; everything else is a sign-in.\n */\nexport function resolveIntent(\n intent: LoginIntent | undefined,\n scope: string | undefined,\n acrValues: string | readonly string[] | undefined\n): LoginIntent {\n if (intent) return intent;\n const scopes = (scope ?? 'openid').split(/\\s+/).filter(Boolean);\n if (scopes.some((s) => !SIGN_IN_SCOPES.has(s))) return 'identify';\n const acr = typeof acrValues === 'string' ? acrValues.split(/\\s+/) : (acrValues ?? []);\n if (scopes.every((s) => s === 'openid') && acr.includes('zoreal.live')) return 'presence';\n return 'sign-in';\n}\n\n/** The unscanned-code title for an intent. */\nexport function titleFor(t: PairingStrings, intent: LoginIntent): string {\n if (intent === 'identify') return t.titleIdentify;\n if (intent === 'presence') return t.titlePresence;\n return t.title;\n}\n","/**\n * The pairing modal's stylesheet, injected once on first mount.\n *\n * Why a stylesheet and not inline styles: the modal needs hover, focus-visible,\n * keyframes, `prefers-color-scheme` and `prefers-reduced-motion`. None of those\n * exist as inline style properties, and a component that silently drops its\n * focus ring and its reduced-motion fallback is not shippable in a sign-in\n * flow.\n *\n * Why injected and not a `.css` file the integrator imports: a required import\n * step is a required support ticket. Plenty of hosts (Next.js app dir, CRA,\n * plain Vite, an app with no CSS pipeline at all) treat package CSS\n * differently, and the modal has to look the same in all of them.\n *\n * Every selector is prefixed `zrl-` and every declaration is scoped under one\n * of those classes, so nothing here can reach the host's markup. Values are\n * literal rather than inherited for the same reason: a host page with an\n * aggressive reset must not be able to break the layout of a dialog the person\n * is being asked to authenticate in. Font family is the one exception — it\n * inherits the host's UI font so the modal belongs to the page it opens on.\n */\n\nconst PREFIX = 'zrl';\nexport const cx = (name: string) => `${PREFIX}-${name}`;\n\nexport const STYLE_ELEMENT_ID = 'zoreal-pairing-styles';\n\n/**\n * Palette. `light`/`dark` force a theme, `auto` follows the OS. The tokens are\n * defined three times rather than once with overrides so a forced theme never\n * depends on media-query specificity to win.\n */\nconst LIGHT = `\n --zrl-scrim: rgba(16, 18, 27, 0.45);\n --zrl-surface: #ffffff;\n --zrl-surface-sunken: #f6f7f9;\n --zrl-ink: #16181c;\n --zrl-ink-soft: #4a4f57;\n --zrl-ink-mute: #6b7078;\n --zrl-line: #e4e6ea;\n --zrl-line-soft: #eef0f3;\n --zrl-accent: #00b4d9;\n --zrl-accent-soft: #dcf3fa;\n --zrl-accent-ink: #04698a;\n --zrl-urgent: #b4761a;\n --zrl-qr-bg: #ffffff;\n --zrl-qr-filter: none;\n --zrl-qr-spent-filter: blur(3px);\n --zrl-qr-blend: normal;\n --zrl-shadow: 0 1px 2px rgba(16, 18, 27, 0.06), 0 20px 50px -12px rgba(16, 18, 27, 0.3);\n --zrl-ring: rgba(16, 18, 27, 0.07);\n /* The light on the QR well's edge. Brand blue on both grounds, a lighter\n tint at the head; only its strength is themed, see the dark block. */\n --zrl-beam: #00b4d9;\n --zrl-beam-head: #7fe0f4;\n --zrl-beam-line: 2px;\n --zrl-glow-core: 4px;\n --zrl-glow-reach: 24px;\n --zrl-glow-blur: 8px;\n --zrl-glow-opacity: 0.6;\n`;\n\nconst DARK = `\n --zrl-scrim: rgba(0, 0, 0, 0.62);\n --zrl-surface: #17191d;\n --zrl-surface-sunken: #1f2226;\n --zrl-ink: #f4f5f7;\n --zrl-ink-soft: #b3b8c0;\n --zrl-ink-mute: #8b9199;\n --zrl-line: #2c3036;\n --zrl-line-soft: #24272c;\n --zrl-accent: #34c9e8;\n --zrl-accent-soft: #0d3b47;\n --zrl-accent-ink: #7fdcf0;\n --zrl-urgent: #e0a952;\n /* The code is drawn light on the dark surface: the panel is transparent\n and the image is inverted and screened, so only the modules and the\n mark show. */\n --zrl-qr-bg: transparent;\n --zrl-qr-filter: invert(1);\n --zrl-qr-spent-filter: invert(1) blur(3px);\n --zrl-qr-blend: screen;\n --zrl-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -12px rgba(0, 0, 0, 0.65);\n --zrl-ring: rgba(255, 255, 255, 0.1);\n /* A glow that reads on a white card disappears on a dark one: the light\n here is brighter and wider, and its halo reaches further out. */\n --zrl-beam: #22c8ec;\n --zrl-beam-head: #c2f3fc;\n --zrl-beam-line: 3px;\n --zrl-glow-core: 6px;\n --zrl-glow-reach: 32px;\n --zrl-glow-blur: 10px;\n --zrl-glow-opacity: 0.85;\n`;\n\nexport const CSS = `\n.${PREFIX}-root { ${LIGHT} }\n.${PREFIX}-root[data-theme=\"dark\"] { ${DARK} }\n@media (prefers-color-scheme: dark) {\n .${PREFIX}-root[data-theme=\"auto\"] { ${DARK} }\n}\n\n.${PREFIX}-scrim {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: grid;\n place-items: center;\n overflow-y: auto;\n padding: 16px;\n background: var(--zrl-scrim);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n font-family: inherit;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-card {\n position: relative;\n box-sizing: border-box;\n width: 100%;\n max-width: 380px;\n border-radius: 16px;\n background: var(--zrl-surface);\n color: var(--zrl-ink);\n box-shadow: var(--zrl-shadow);\n outline: 1px solid var(--zrl-ring);\n outline-offset: -1px;\n text-align: center;\n animation: ${PREFIX}-rise 300ms cubic-bezier(0.23, 1, 0.32, 1) both;\n}\n\n.${PREFIX}-body { padding: 28px 24px 20px; }\n\n.${PREFIX}-lockup { display: block; margin: 0 auto; color: var(--zrl-ink); }\n\n.${PREFIX}-title {\n margin: 18px 0 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n line-height: 1.3;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-body-text {\n margin: 6px auto 0;\n max-width: 30ch;\n font-size: 14px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-qr-well {\n position: relative;\n display: grid;\n place-items: center;\n box-sizing: border-box;\n width: 204px;\n height: 204px;\n margin: 20px auto 0;\n padding: 12px;\n border: 1px solid var(--zrl-line);\n border-radius: var(--zrl-radius);\n background: var(--zrl-qr-bg);\n /* The light on the edge takes its shape from here and its colour and\n strength from the theme tokens above. One lap in 4s on every tier. */\n --zrl-radius: 16px;\n --zrl-beam-time: 4s;\n}\n\n/* The light on the well's edge: a short comet running along the border, with\n a soft glow outside it. Three overlays inside the well, each masked so the\n comet can only ever paint where its mask allows, and the white interior lies\n outside every mask: nothing here can reach the quiet zone a camera needs,\n whatever the comet is doing. The mask is the padding box cut out of the\n border box, a transparent layer clipped to the padding box intersected\n with a solid one clipped to the border box. The prefixed form is for Chrome\n before 120 and Safari before 15.4; the unprefixed one, declared after it,\n wins everywhere else.\n\n qr-beam keeps a thin ring on the border line: the comet itself.\n qr-beam-glow is the glow: a wide band outside the well that blurs whatever\n is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a\n second copy of the comet. The blur has to sit on the parent because a\n filter is applied before a mask: blurred on the band itself, the glow\n would be cut back to the band's own edge. On the parent it runs after the\n band has clipped the comet thin and before the parent's mask cuts away the\n inward half, which is what makes it fade outward and never over the QR.\n All three share one containing block, the well's padding box, so the two\n comets ride the same path; the spent badge is a later sibling and paints\n above them. */\n.${PREFIX}-qr-beam,\n.${PREFIX}-qr-beam-glow,\n.${PREFIX}-qr-beam-glow-band {\n position: absolute;\n inset: calc(0px - var(--zrl-beam-line));\n border: var(--zrl-beam-line) solid transparent;\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-beam-line));\n pointer-events: none;\n -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n -webkit-mask-clip: padding-box, border-box;\n -webkit-mask-composite: source-in;\n mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n mask-clip: padding-box, border-box;\n mask-composite: intersect;\n}\n.${PREFIX}-qr-beam-glow {\n inset: calc(0px - var(--zrl-glow-reach));\n border-width: var(--zrl-glow-reach);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));\n filter: blur(var(--zrl-glow-blur));\n opacity: var(--zrl-glow-opacity);\n will-change: filter;\n}\n.${PREFIX}-qr-beam-glow-band {\n inset: calc(0px - var(--zrl-glow-core));\n border-width: var(--zrl-glow-core);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));\n}\n\n/* At rest the edge holds a dim, even blue: a 1px line on the border and, from\n the glow band, a soft halo outside it. Hidden while the comet runs, so the\n border reads as the well's own line with a light passing over it; shown\n once the light has stopped. Every path below ends here, which is what\n makes them look the same at rest. */\n.${PREFIX}-qr-beam::before,\n.${PREFIX}-qr-beam-glow-band::before {\n content: '';\n position: absolute;\n inset: -50%;\n background: var(--zrl-beam);\n opacity: 0;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* The moving light: an oversized square carrying a conic sweep, rotated\n whole. A transform animation runs on the compositor, so the light keeps\n moving while the page is busy; animating the gradient angle instead\n repaints every frame on the main thread and stutters. */\n.${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-beam-glow-band::after {\n content: '';\n position: absolute;\n inset: -50%;\n background: conic-gradient(\n from 0deg,\n transparent 0deg 220deg,\n var(--zrl-beam) 330deg,\n var(--zrl-beam-head) 348deg,\n transparent 356deg 360deg\n );\n animation: ${PREFIX}-orbit var(--zrl-beam-time) linear infinite;\n will-change: transform;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Spent: the light stops where it is and fades, and the edge settles to the\n dim glow. Paused rather than removed, so it does not jump back to its start\n on the way out. */\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::after {\n animation-play-state: paused;\n opacity: 0;\n}\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::before { opacity: 0.55; }\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }\n\n.${PREFIX}-qr {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n filter: var(--zrl-qr-filter);\n mix-blend-mode: var(--zrl-qr-blend);\n transition: filter 300ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 300ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 300ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Once the code is claimed the QR is spent. Blurring it out rather than\n swapping it keeps one object on screen through the state change, so the eye\n reads a transformation instead of two things trading places. */\n.${PREFIX}-qr[data-spent=\"true\"] { opacity: 0.2; filter: var(--zrl-qr-spent-filter); transform: scale(0.96); }\n\n.${PREFIX}-qr-overlay {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-qr-badge {\n display: grid;\n place-items: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n background: var(--zrl-accent-soft);\n color: var(--zrl-accent-ink);\n}\n\n.${PREFIX}-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 20px;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-dot { position: relative; display: grid; place-items: center; width: 8px; height: 8px; }\n.${PREFIX}-dot i {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 999px;\n background: var(--zrl-accent);\n font-style: normal;\n}\n.${PREFIX}-dot i:first-child { animation: ${PREFIX}-ping 1.8s cubic-bezier(0.23, 1, 0.32, 1) infinite; }\n\n.${PREFIX}-timer {\n margin: 4px 0 0;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--zrl-ink-mute);\n transition: color 200ms ease-out;\n}\n.${PREFIX}-timer[data-urgent=\"true\"] { color: var(--zrl-urgent); }\n\n.${PREFIX}-help {\n padding: 14px 24px;\n border-top: 1px solid var(--zrl-line-soft);\n background: var(--zrl-surface-sunken);\n border-radius: 0;\n}\n.${PREFIX}-help-title { margin: 0; font-size: 12px; font-weight: 600; color: var(--zrl-ink); }\n.${PREFIX}-help-body {\n margin: 4px auto 0;\n max-width: 34ch;\n font-size: 12px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-footer { padding: 12px; border-top: 1px solid var(--zrl-line-soft); }\n\n.${PREFIX}-cancel {\n display: block;\n width: 100%;\n padding: 10px;\n border: 0;\n border-radius: 12px;\n background: transparent;\n font: inherit;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink-soft);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-cancel:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-cancel:active { transform: scale(0.99); }\n\n.${PREFIX}-secured {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n margin: 6px 0 0;\n font-size: 12px;\n color: var(--zrl-ink-mute);\n text-decoration: none;\n border-radius: 6px;\n transition: color 150ms ease-out;\n}\n.${PREFIX}-secured:hover { color: var(--zrl-ink); }\n\n.${PREFIX}-close {\n position: absolute;\n top: 12px;\n inset-inline-end: 12px;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--zrl-ink-mute);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-close:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-close:active { transform: scale(0.95); }\n\n.${PREFIX}-card :focus-visible {\n outline: 2px solid var(--zrl-accent);\n outline-offset: 2px;\n}\n\n@keyframes ${PREFIX}-fade { from { opacity: 0 } to { opacity: 1 } }\n@keyframes ${PREFIX}-rise {\n from { opacity: 0; transform: translateY(10px) scale(0.98) }\n to { opacity: 1; transform: none }\n}\n@keyframes ${PREFIX}-ping {\n 0% { transform: scale(1); opacity: 0.5 }\n 70%, 100% { transform: scale(2.6); opacity: 0 }\n}\n\n@keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }\n/* THE BUSY RING. The light of the QR well, around any control that is\n waiting on the provider: the button on a phone between the tap and the\n hand-over to the app. The well's sweep is a cone from the centre, which is\n even on a square and useless on a wide button: it crawls along the long\n sides and lights two edges at once near the ends. So here the light is a\n dash on an SVG outline, which moves at one speed the whole way round\n whatever the shape, drawn with the well's tokens: its colour and head\n tint, its line width, its halo, its four second lap. The outline's length\n is measured by the component and set as --zrl-ring-len, and every dash\n and offset is a fraction of it, because pathLength does not scale dash\n values given from CSS. A stroke cannot fade along its length, so the tail\n is a stack of dashes sharing one head, each shorter and more opaque than\n the one under it, with opacities chosen so the stack composes to a\n straight fade from the head to nothing three tenths of the way back; the\n component sets each layer's length, offset and opacity. Shown only while\n busy. */\n.${PREFIX}-ring {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n --zrl-beam-time: 4s;\n}\n.${PREFIX}-ring-svg {\n position: absolute;\n inset: -4px;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n overflow: visible;\n pointer-events: none;\n opacity: 0;\n transition: opacity 200ms ease-out;\n}\n.${PREFIX}-ring[data-busy=\"true\"] > .${PREFIX}-ring-svg { opacity: 1; }\n.${PREFIX}-ring-svg rect {\n --zrl-l: var(--zrl-ring-len, 600px);\n x: 2px;\n y: 2px;\n width: calc(100% - 4px);\n height: calc(100% - 4px);\n fill: none;\n stroke: var(--zrl-beam);\n stroke-width: var(--zrl-beam-line);\n stroke-linecap: round;\n stroke-dashoffset: var(--zrl-s, 0px);\n animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;\n}\n.${PREFIX}-ring-head { stroke: var(--zrl-beam-head); }\n.${PREFIX}-ring-halo {\n stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));\n filter: blur(var(--zrl-glow-blur));\n}\n@keyframes ${PREFIX}-dash {\n from { stroke-dashoffset: var(--zrl-s, 0px); }\n to { stroke-dashoffset: calc(var(--zrl-s, 0px) - var(--zrl-l)); }\n}\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }\n .${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }\n}\n\n\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-scrim,\n .${PREFIX}-card,\n .${PREFIX}-qr-overlay { animation: none }\n .${PREFIX}-dot i:first-child { animation: none; opacity: 0.35 }\n .${PREFIX}-qr,\n .${PREFIX}-cancel,\n .${PREFIX}-close,\n .${PREFIX}-timer { transition: none }\n /* No travelling light; the edge keeps its dim static glow instead. */\n .${PREFIX}-qr-beam::after,\n .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }\n .${PREFIX}-qr-beam::before { opacity: 0.55 }\n .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }\n}\n`;\n\n/**\n * Injected at module scope on first import in a DOM, not per render: the tag is\n * idempotent by id, so a host with two provider instances (or a hot reload)\n * still ends up with exactly one.\n */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ELEMENT_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n}\n","import { useMemo, useState, type CSSProperties } from 'react';\nimport { useZorealOAuth } from './context';\nimport { strings } from './i18n';\nimport { useZorealFlow } from './useZorealLogin';\nimport { ZorealMark } from './mark';\nimport { ZorealBusyRing } from './ring';\nimport type {\n NonOAuthError,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginProps,\n} from './types';\n\n/**\n * The drop-in button. In its default browser-direct flow it receives no\n * access token, so it returns the pseudonymous identity only; personal data\n * needs `flow: 'auth-code'`, which hands your backend the code instead\n * (supported here since 0.2.8, same discriminator as useZorealLogin).\n *\n * The copy is neutral: the button asserts nothing about a person who has not\n * yet authenticated. Styling is inline and self-contained; no stylesheet, no\n * font, no external asset, because this renders on a sign-in page.\n *\n * The QR itself is no longer drawn here. `ZorealOAuthProvider` renders the\n * pairing modal for every flow, so the button and `useZorealLogin` get the\n * same dialog and it only had to be designed, translated and made accessible\n * once. Opt out with `pairingUI=\"none\"` on the provider.\n */\n\n// The default label is translated with the modal's own copy; the four\n// alternatives are English, as they were.\nconst TEXTS: Record<NonNullable<ZorealLoginProps['text']>, string | null> = {\n continue_with: null,\n signin_with: 'Sign in with ZOREAL',\n signup_with: 'Sign up with ZOREAL',\n signin: 'Sign in',\n verify_with: 'Verify with ZOREAL ID',\n};\n\n/* The house button: 14px medium text, a 22px mark, 12px between them, 14px\n above and below, 20px at the sides, 12px corners. The smaller sizes scale\n that down; they do not change its proportions. */\nconst SIZES = {\n large: { height: 50, font: 14, pad: 20, mark: 22, gap: 12, radius: 12 },\n medium: { height: 42, font: 14, pad: 16, mark: 20, gap: 10, radius: 10 },\n small: { height: 34, font: 12, pad: 12, mark: 16, gap: 8, radius: 8 },\n} as const;\n\nexport function ZorealLogin(props: ZorealLoginProps) {\n const {\n onSuccess,\n onError,\n containerProps,\n type = 'standard',\n theme = 'outline',\n size = 'large',\n text = 'continue_with',\n shape = 'rectangular',\n logo_alignment = 'center',\n width,\n click_listener,\n flow = 'browser-direct',\n ...request\n } = props;\n\n const { locale } = useZorealOAuth();\n const label = TEXTS[text] ?? strings(locale).buttonContinue;\n\n // Busy from the tap until the flow ends. On a phone the tap creates the\n // pairing and then sends the tab to the app, one round trip later; the\n // button is disabled and a light runs round it for that gap, so the tap is\n // seen to have worked and cannot start a second pairing. On a computer it\n // stays busy while the dialog is open. Never cleared by a navigation away:\n // the page is gone with it.\n const [busy, setBusy] = useState(false);\n\n const { login } = useZorealFlow({\n ...request,\n flow,\n onCredential:\n flow === 'browser-direct'\n ? (r: ZorealCredentialResponse) => {\n setBusy(false);\n (onSuccess as (r: ZorealCredentialResponse) => void)(r);\n }\n : undefined,\n onCode:\n flow === 'auth-code'\n ? (r: ZorealCodeResponse) => {\n setBusy(false);\n (onSuccess as unknown as (r: ZorealCodeResponse) => void)(r);\n }\n : undefined,\n onError: (e) => {\n setBusy(false);\n onError?.({ type: 'unknown', description: e.description ?? e.error });\n },\n onNonOAuthError: (e: NonOAuthError) => {\n setBusy(false);\n onError?.(e);\n },\n });\n\n const s = SIZES[size];\n const radius = shape === 'pill' ? s.height / 2 : shape === 'square' ? 4 : s.radius;\n // The mark keeps the brand blue wherever it can be seen. On the brand-blue\n // filled button it cannot, so there it takes the label's white.\n const brandMark = theme !== 'filled';\n const style: CSSProperties = useMemo(\n () => ({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: logo_alignment === 'center' ? 'center' : 'flex-start',\n gap: s.gap,\n height: s.height,\n padding: `0 ${s.pad}px`,\n width,\n fontSize: s.font,\n fontFamily: 'inherit',\n fontWeight: 500,\n cursor: 'pointer',\n borderRadius: radius,\n ...(theme === 'outline'\n ? { background: '#ffffff', color: '#16181c', border: '1px solid #e2e4de' }\n : theme === 'filled_black'\n ? { background: '#111', color: '#fff', border: '1px solid #111' }\n : { background: '#00b4d9', color: '#fff', border: '1px solid #00b4d9' }),\n }),\n [logo_alignment, s, radius, theme, width]\n );\n\n return (\n <div {...containerProps}>\n <ZorealBusyRing busy={busy} radius={radius} theme={theme === 'outline' ? 'auto' : 'light'}>\n <button\n type=\"button\"\n style={busy ? { ...style, cursor: 'progress' } : style}\n disabled={busy}\n aria-busy={busy}\n onClick={() => {\n if (busy) return;\n click_listener?.();\n setBusy(true);\n login();\n }}\n >\n <ZorealMark size={s.mark} brand={brandMark} />\n {type === 'standard' && label}\n </button>\n </ZorealBusyRing>\n </div>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useZorealOAuth, useZorealPairingHost } from './context';\nimport { resolveIntent } from './intent';\nimport {\n forgetReturnFlow,\n isReturnDone,\n markReturnDone,\n peekReturnFlow,\n pendingReturnId,\n returnToUrl,\n saveReturnFlow,\n} from './return';\nimport { unsafeClaims } from './jwt';\nimport {\n FlowAbandonedError,\n OAuthFlowError,\n exchangeCode,\n pollUntilApproved,\n qrRefreshSecondsOf,\n resolveDisplay,\n sameDeviceStartUrl,\n startPairing,\n} from './pairing';\nimport {\n challengeS256,\n challengeS256Sync,\n generateRequestId,\n generateState,\n generateVerifier,\n} from './pkce';\nimport type {\n AcrValue,\n AuthCodeFlowOptions,\n BrowserDirectFlowOptions,\n ErrorCode,\n NonOAuthError,\n PairingState,\n SelectBy,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginRequestOptions,\n} from './types';\n\nexport interface ActivePairing {\n requestId: string;\n pairUrl: string;\n /**\n * The QR image to show right now. It MOVES: a QR pairing's code is a frame\n * the provider rotates every few seconds, so read this from the latest\n * state rather than holding the first value.\n */\n qrUrl: string;\n state: PairingState;\n /** True when display resolved to the app link rather than the QR. */\n appLink: boolean;\n cancel: () => void;\n}\n\n/** Returns already taken up in this page load, so a second hook instance does not repeat one. */\nconst resumedReturns = new Set<string>();\n\ninterface FlowInternals {\n /** Non-null while a pairing is on screen. ZorealLogin renders from this. */\n pairing: ActivePairing | null;\n}\n\n/**\n * The internal option shape: one flow discriminator, one success callback per\n * mode. The public API keeps Google's single overloaded onSuccess; this type\n * exists because an intersection of those two signatures is uninhabitable, and\n * the mapping from public to internal happens once, in useZorealLogin.\n */\nexport interface InternalFlowOptions extends ZorealLoginRequestOptions {\n flow: 'browser-direct' | 'auth-code';\n redirect_uri?: string;\n onCredential?: (response: ZorealCredentialResponse) => void;\n onCode?: (response: ZorealCodeResponse) => void;\n onError?: (error: Pick<NonOAuthError, 'description'> & { error: ErrorCode }) => void;\n onNonOAuthError?: (error: NonOAuthError) => void;\n}\n\n/**\n * The one flow, shared by the hook and the button. Starts a pairing, exposes\n * it for rendering, polls, and finishes per mode: browser-direct exchanges the\n * code here (public client, PKCE, no secret) and hands over an ID token;\n * auth-code hands the code and the PKCE verifier to the caller, whose backend\n * does the exchange with its client authentication.\n */\nexport function useZorealFlow(options: InternalFlowOptions): {\n login: () => void;\n internals: FlowInternals;\n} {\n const { clientId, issuer, locale } = useZorealOAuth();\n const [pairing, setPairing] = useState<ActivePairing | null>(null);\n // Null when the provider is set to pairingUI: 'none', which is the caller\n // saying they render the QR themselves. Held in a ref so `login` keeps its\n // identity across renders.\n const publish = useZorealPairingHost();\n const publishRef = useRef(publish);\n publishRef.current = publish;\n const abortRef = useRef<AbortController | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n // Set by a cancel the person made (the dialog's close, its Cancel, Escape,\n // a tap outside, its timeout), as opposed to the unmount. The abort that\n // follows is then reported as `popup_closed`, so a button that went busy\n // on the tap has something to recover on.\n const closedByPerson = useRef(false);\n\n // A component unmounting mid-login must stop the poll: the provider cancels\n // over-polled requests, and an orphaned interval is exactly how one happens.\n useEffect(\n () => () => {\n abortRef.current?.abort();\n publishRef.current?.(null);\n },\n []\n );\n\n // THE RETURN. When the ZOREAL ID app reopens the page after a same-device\n // approval, the pairing is named in the fragment and the flow that started\n // it is in local storage. This finishes it where the page stands, once per\n // page load whichever hook instance mounts first, and reports through the\n // same callbacks the tap would have.\n useEffect(() => {\n const id = pendingReturnId();\n if (!id || resumedReturns.has(id)) return;\n const saved = peekReturnFlow(id);\n if (!saved || saved.clientId !== clientId) return;\n forgetReturnFlow(id);\n resumedReturns.add(id);\n const controller = new AbortController();\n abortRef.current = controller;\n void (async () => {\n const opts = optionsRef.current;\n try {\n const code = await pollUntilApproved(issuer, id, undefined, controller.signal, {\n tolerateUnknownUntil: Date.now() + 5_000,\n });\n if (saved.flow === 'auth-code') {\n opts.onCode?.({\n code,\n scope: saved.scope,\n app_state: saved.appState,\n code_verifier: saved.verifier,\n nonce: saved.nonce,\n });\n } else {\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: saved.verifier,\n client_id: clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n opts.onCredential?.({\n credential: tokens.id_token,\n clientId,\n select_by: 'app_link',\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n });\n }\n markReturnDone(id);\n } catch (e) {\n if (e instanceof DOMException && e.name === 'AbortError') return;\n if (e instanceof FlowAbandonedError) {\n opts.onNonOAuthError?.(e.reason);\n return;\n }\n if (e instanceof OAuthFlowError) {\n opts.onError?.({ error: e.error, description: e.description });\n return;\n }\n opts.onNonOAuthError?.({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n })();\n }, [clientId, issuer]);\n\n const login = useCallback(() => {\n const opts = optionsRef.current;\n const run = async () => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n const flow = opts.flow;\n const verifier = generateVerifier();\n const state = generateState();\n const nonce = generateState();\n\n // Which surface this login will use is decided HERE, before the pairing\n // exists, because the provider binds the pairing to it: a QR pairing\n // gets a rotating code, a link pairing gets a start token on its URL and\n // no QR at all. Deciding afterwards would mean asking for one surface\n // and showing another.\n const display = resolveDisplay(opts.display);\n const useAppLink = display === 'link';\n const intent = resolveIntent(opts.intent, opts.scope, opts.acr_values);\n\n try {\n let code: string;\n let selectBy: SelectBy = 'device';\n let returnId: string | null = null;\n\n if (useAppLink) {\n // THE TAP IS THE NAVIGATION. Nothing is awaited between the click\n // and the assignment below: a browser hands a universal link to an\n // app only inside a navigation the person began, and an await here\n // would put the navigation outside it, where the link loads as a\n // web page instead (see sameDeviceStartUrl). The provider creates\n // the pairing and redirects to the link; the page stays and polls\n // the token it chose, tolerating \"no such pairing\" for as long as\n // the provider may still be answering the navigation. No modal:\n // there is no code to scan and the page is the button that was\n // tapped.\n const requestId = generateRequestId();\n // The way back: the app reopens this page once the holder has\n // approved, in a new tab, and the hook there finishes the sign-in\n // from what is saved here. This tab keeps polling too; whichever\n // finishes first marks the flow done and the other stands down.\n saveReturnFlow({\n v: 1,\n issuer,\n clientId,\n flow,\n verifier,\n nonce,\n state,\n scope: opts.scope ?? 'openid',\n appState: opts.app_state,\n requestId,\n createdAt: Date.now(),\n });\n returnId = requestId;\n const startUrl = sameDeviceStartUrl(issuer, {\n client_id: clientId,\n scope: opts.scope ?? 'openid',\n state,\n nonce,\n code_challenge: challengeS256Sync(verifier),\n redirect_uri: flow === 'auth-code' ? opts.redirect_uri : undefined,\n acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(' ') : opts.acr_values,\n max_age: opts.max_age,\n prompt: opts.prompt,\n locale,\n request_id: requestId,\n origin: window.location.origin,\n return_to: returnToUrl(),\n });\n selectBy = 'app_link';\n const cancel = () => {\n closedByPerson.current = true;\n controller.abort();\n setPairing(null);\n };\n const surface = { pairUrl: startUrl, appLink: true, intent, cancel };\n const active: ActivePairing = {\n requestId,\n pairUrl: startUrl,\n qrUrl: '',\n state: { status: 'pending', ...surface },\n appLink: true,\n cancel,\n };\n setPairing(active);\n opts.onPairingStateChange?.(active.state);\n window.location.assign(startUrl);\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => {\n const enriched = { ...s, ...surface };\n setPairing((p) => (p && p.requestId === requestId ? { ...p, state: enriched } : p));\n opts.onPairingStateChange?.(enriched);\n },\n controller.signal,\n { tolerateUnknownUntil: Date.now() + 15_000 }\n );\n if (isReturnDone(requestId)) {\n // The page the app reopened has finished this sign-in. This tab\n // was left behind; it stands down rather than spend a used code.\n throw new DOMException('aborted', 'AbortError');\n }\n } else {\n const started = await startPairing(issuer, {\n client_id: clientId,\n scope: opts.scope ?? 'openid',\n state,\n nonce,\n code_challenge: await challengeS256(verifier),\n redirect_uri: flow === 'auth-code' ? opts.redirect_uri : undefined,\n acr_values: Array.isArray(opts.acr_values)\n ? opts.acr_values.join(' ')\n : opts.acr_values,\n max_age: opts.max_age,\n prompt: opts.prompt,\n locale,\n display: 'qr',\n });\n\n if ('code' in started) {\n // prompt=none resolved silently: consented sector, live session.\n code = started.code;\n selectBy = 'session';\n } else {\n selectBy = 'qr';\n const qrRefreshSeconds = qrRefreshSecondsOf(started);\n\n const cancel = () => {\n closedByPerson.current = true;\n controller.abort();\n setPairing(null);\n publishRef.current?.(null);\n };\n // Everything a caller-rendered pairing UI needs, on every state it\n // sees: the QR flow cannot complete unless SOMETHING renders\n // pairUrl, and for the auth-code flow that something is the caller.\n // qrUrl is deliberately NOT in here: it is the one field that\n // changes during the pairing, and a fixed copy spread over every\n // state would paste the first frame back on top of the current one.\n const surface = {\n pairUrl: started.pair_url,\n appLink: false,\n intent,\n cancel,\n qrRefreshSeconds,\n };\n // The frame on screen. The poll replaces it every qrRefreshSeconds;\n // everything published in between reuses whatever is current, so the\n // three channels below never disagree about which code is showing.\n let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;\n const active: ActivePairing = {\n requestId: started.request_id,\n pairUrl: surface.pairUrl,\n qrUrl,\n state: { status: 'pending', expiresIn: started.expires_in, qrUrl, ...surface },\n appLink: false,\n cancel,\n };\n setPairing(active);\n if (!useAppLink) {\n publishRef.current?.({ state: active.state, qrUrl, intent, cancel });\n }\n // The initial state, immediately: the first poll response is one\n // round-trip away, and a UI that waits for it opens visibly empty.\n opts.onPairingStateChange?.(active.state);\n\n if (useAppLink) {\n // The universal link, in the same tab: the app claims it, and with\n // no app installed the same URL is the real pairing page which can\n // enrol. A popup here would be blocked more often than it would\n // help. The URL carries the start token that binds the claim to\n // this browser, so it is used exactly as the provider gave it.\n window.location.assign(started.pair_url);\n }\n\n code = await pollUntilApproved(\n issuer,\n started.request_id,\n (s) => {\n // A refresh arrives as a state carrying a new qrUrl; a poll\n // arrives without one and keeps the frame already showing.\n if (s.qrUrl) qrUrl = s.qrUrl;\n const enriched = { ...s, ...surface, qrUrl };\n setPairing((p) =>\n p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p\n );\n if (!useAppLink) {\n publishRef.current?.({ state: enriched, qrUrl, intent, cancel });\n }\n opts.onPairingStateChange?.(enriched);\n },\n controller.signal,\n { qrRefreshSeconds }\n );\n }\n\n }\n\n setPairing(null);\n publishRef.current?.(null);\n if (returnId) markReturnDone(returnId);\n\n if (flow === 'auth-code') {\n opts.onCode?.({\n code,\n scope: opts.scope ?? 'openid',\n app_state: opts.app_state,\n code_verifier: verifier,\n nonce,\n });\n return;\n }\n\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: verifier,\n client_id: clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n const response: ZorealCredentialResponse = {\n credential: tokens.id_token,\n clientId,\n select_by: selectBy,\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n };\n opts.onCredential?.(response);\n } catch (e) {\n setPairing(null);\n publishRef.current?.(null);\n if (e instanceof DOMException && e.name === 'AbortError') {\n if (closedByPerson.current) {\n closedByPerson.current = false;\n opts.onNonOAuthError?.({\n type: 'popup_closed',\n description: 'the sign-in dialog was closed before the holder approved',\n });\n }\n return;\n }\n if (e instanceof FlowAbandonedError) {\n opts.onNonOAuthError?.(e.reason);\n return;\n }\n if (e instanceof OAuthFlowError) {\n opts.onError?.({ error: e.error, description: e.description });\n return;\n }\n opts.onNonOAuthError?.({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n };\n void run();\n }, [clientId, issuer, locale]);\n\n return { login, internals: { pairing } };\n}\n\nexport function useZorealLogin(\n options: { flow?: 'browser-direct' } & BrowserDirectFlowOptions\n): () => void;\nexport function useZorealLogin(options: { flow: 'auth-code' } & AuthCodeFlowOptions): () => void;\nexport function useZorealLogin(\n options: ({ flow?: 'browser-direct' | 'auth-code' } & ZorealLoginRequestOptions) &\n Partial<Pick<AuthCodeFlowOptions, 'redirect_uri' | 'ux_mode'>> & {\n onSuccess?: (response: never) => void;\n onError?: (error: Pick<NonOAuthError, 'description'> & { error: ErrorCode }) => void;\n onNonOAuthError?: (error: NonOAuthError) => void;\n }\n): () => void {\n if (options.ux_mode === 'redirect') {\n // v1 supports the popup shape only: the code and PKCE verifier go to your\n // onSuccess and from there to your backend over TLS. A redirect would have\n // to carry the verifier in a URL, which is a credential in every access\n // log on the path. Refused loudly rather than implemented badly.\n throw new Error(\n \"@zoreal/oauth2-react: ux_mode 'redirect' is not supported in v1. Use the default \" +\n \"'popup' shape and post the code and code_verifier from onSuccess to your backend.\"\n );\n }\n const flow = options.flow ?? 'browser-direct';\n return useZorealFlow({\n ...options,\n flow,\n onCredential:\n flow === 'browser-direct'\n ? (options.onSuccess as unknown as (r: ZorealCredentialResponse) => void)\n : undefined,\n onCode:\n flow === 'auth-code'\n ? (options.onSuccess as unknown as (r: ZorealCodeResponse) => void)\n : undefined,\n }).login;\n}\n","/**\n * The way back for the same-device sign-in.\n *\n * The tap navigates away to the provider, the app opens, and once the holder\n * has approved, the app opens this page again with the pairing named in the\n * URL fragment. The page then has to finish a sign-in it did not start in\n * this page load, so the flow is saved here before the navigation and taken\n * back on the return: the verifier, the nonce, the state and which mode the\n * caller wanted. Local storage rather than session storage because the\n * browser opens the return in a new tab, and session storage is per tab.\n *\n * The original tab may still be polling when the returned page completes.\n * Whichever finishes first marks the flow done; the other, on seeing the\n * approval, stands down instead of spending a code that was already used.\n */\n\nexport interface SavedFlow {\n v: 1;\n issuer: string;\n clientId: string;\n flow: 'browser-direct' | 'auth-code';\n verifier: string;\n nonce: string;\n state: string;\n scope: string;\n appState?: string;\n requestId: string;\n createdAt: number;\n}\n\nconst PREFIX = 'zoreal:oauth2:return:';\nconst DONE = 'zoreal:oauth2:done:';\n/** A saved flow older than this is not resumed; the pairing is long expired. */\nconst MAX_AGE_MS = 10 * 60 * 1000;\n\nfunction storage(): Storage | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage;\n } catch {\n return null;\n }\n}\n\nexport function saveReturnFlow(flow: SavedFlow): void {\n try {\n storage()?.setItem(PREFIX + flow.requestId, JSON.stringify(flow));\n } catch {\n // Storage full or blocked: the original tab still polls and completes.\n }\n}\n\n/**\n * The saved flow for a pairing, left in place; null when none, or too old.\n * Left in place because the first reader on a page may not be its owner: a\n * page can carry more than one client, and only the one whose id matches\n * takes it (`forgetReturnFlow`).\n */\nexport function peekReturnFlow(requestId: string): SavedFlow | null {\n const store = storage();\n if (!store) return null;\n const raw = store.getItem(PREFIX + requestId);\n if (!raw) return null;\n try {\n const flow = JSON.parse(raw) as SavedFlow;\n if (flow.v !== 1 || flow.requestId !== requestId) return null;\n if (Date.now() - flow.createdAt > MAX_AGE_MS) return null;\n return flow;\n } catch {\n return null;\n }\n}\n\n/** The owner has taken the flow up; nobody else on this page load should. */\nexport function forgetReturnFlow(requestId: string): void {\n try {\n storage()?.removeItem(PREFIX + requestId);\n } catch {\n // Nothing to do.\n }\n if (pending === requestId) pending = null;\n}\n\nexport function markReturnDone(requestId: string): void {\n try {\n const store = storage();\n store?.setItem(DONE + requestId, String(Date.now()));\n store?.removeItem(PREFIX + requestId);\n } catch {\n // Nothing to do: at worst the other tab attempts a used code and is told so.\n }\n}\n\nexport function isReturnDone(requestId: string): boolean {\n return storage()?.getItem(DONE + requestId) !== null && storage()?.getItem(DONE + requestId) !== undefined;\n}\n\n/** The address the app reopens: this page, without any fragment. */\nexport function returnToUrl(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n const { href } = window.location;\n const hash = href.indexOf('#');\n return hash === -1 ? href : href.slice(0, hash);\n}\n\nconst RETURN_MARK = /(?:^|[#&])zoreal_return=([A-Za-z0-9]{32})(?:&|$)/;\n\n/** The id read from the fragment, held for the rest of this page load. */\nlet pending: string | null = null;\n\n/**\n * The pairing named in this page's fragment by the app's return, if any. The\n * fragment is removed from the address bar as it is read, so a reload does\n * not try to resume a second time, and the id is kept for this page load so\n * every reader on the page sees it until its owner takes the flow up.\n */\nexport function pendingReturnId(): string | null {\n if (pending) return pending;\n if (typeof window === 'undefined') return null;\n const match = RETURN_MARK.exec(window.location.hash);\n if (!match) return null;\n pending = match[1];\n try {\n window.history.replaceState(window.history.state, '', returnToUrl());\n } catch {\n // Some embedded browsers refuse; the id was still read.\n }\n return pending;\n}\n","/**\n * Reads claims OUT of an ID token without verifying it.\n *\n * That is not a shortcut, it is the design: this code runs in a browser the\n * threat model assumes is attacker-controlled (02), so a signature check here\n * proves nothing to anyone. The token is verified where verification means\n * something: server-side against the JWKS. What this parser feeds is\n * convenience fields (acr on the response object) that the types document as\n * convenience, with the token staying the authority.\n */\n\nexport function unsafeClaims(idToken: string): Record<string, unknown> {\n try {\n const payload = idToken.split('.')[1] ?? '';\n const b64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n return JSON.parse(\n new TextDecoder().decode(Uint8Array.from(atob(padded), (c) => c.charCodeAt(0)))\n );\n } catch {\n return {};\n }\n}\n","/**\n * The pairing channel, client side. wire.ts pins the endpoints.\n *\n * The browser polls; the phone never talks to the browser. Everything here is\n * therefore plain fetch against the issuer, CORS-gated on the client's\n * authorized origins, with the poll cadence fixed: the provider cancels an\n * over-polling request rather than throttling it, so a \"retry\n * faster on error\" strategy here would kill the login it is trying to save.\n */\n\nimport {\n DEFAULT_QR_REFRESH_SECONDS,\n POLL_INTERVAL_ENROLLING_MS,\n POLL_INTERVAL_MS,\n SDK_VERSION,\n WIRE_VERSION,\n type PairCreated,\n type PairDisplay,\n type PairStartResponse,\n type PairStatusResponse,\n type TokenResponse,\n} from './wire';\nimport type { ErrorCode, NonOAuthError, PairingState } from './types';\n\nexport class OAuthFlowError extends Error {\n constructor(\n public error: ErrorCode,\n public description?: string\n ) {\n super(description ?? error);\n }\n}\n\nexport class FlowAbandonedError extends Error {\n constructor(public reason: NonOAuthError) {\n super(reason.description ?? reason.type);\n }\n}\n\nexport interface StartPairingParams {\n client_id: string;\n scope: string;\n state: string;\n nonce: string;\n code_challenge: string;\n redirect_uri?: string;\n acr_values?: string;\n max_age?: number;\n prompt?: string;\n locale?: string;\n /**\n * The surface this package is about to show, decided BEFORE the request:\n * the provider binds the pairing to it. \"qr\" gets animated frames, \"link\"\n * gets a start token on pair_url and no QR at all.\n */\n display?: PairDisplay;\n}\n\nasync function parseJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function startPairing(\n issuer: string,\n params: StartPairingParams\n): Promise<PairStartResponse> {\n const response = await fetch(`${issuer}/pair`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `@zoreal/oauth2-react/${SDK_VERSION}`,\n }),\n });\n\n const body = await parseJson(response);\n if (!response.ok) {\n // The provider's words, verbatim. A refused package version arrives here,\n // and rewriting its reason would hide the only signal telling an integrator\n // to upgrade.\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n (body.error_description as string) ?? `The provider refused the request (${response.status})`\n );\n }\n return body as unknown as PairStartResponse;\n}\n\n/**\n * The same-device sign-in, as a URL to NAVIGATE to, not to fetch.\n *\n * A phone's browser hands a universal link to an app only inside a\n * navigation the person began, and a page that sets its location after a\n * network round trip has left that navigation behind: the link then loads\n * as a web page. So on a phone this package fetches nothing on the tap. The\n * tap itself navigates to the provider's start endpoint with what /pair\n * would have been sent, the provider creates the link pairing and answers\n * with a redirect to its universal link, still inside the person's\n * navigation, and the app opens. The page is not unloaded when it does, and\n * polls the pairing by the `request_id` it chose here. With no app installed\n * the same redirect lands on the page that installs it. `return_to` is this\n * page's own address, which the app reopens once the holder has approved,\n * with the pairing named in the fragment (see return.ts).\n */\nexport function sameDeviceStartUrl(\n issuer: string,\n params: StartPairingParams & { request_id: string; origin: string; return_to?: string }\n): string {\n const query = new URLSearchParams();\n const all: Record<string, unknown> = {\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `@zoreal/oauth2-react/${SDK_VERSION}`,\n };\n for (const [key, value] of Object.entries(all)) {\n if (value === undefined || value === null || value === '') continue;\n query.set(key, String(value));\n }\n return `${issuer}/pair/start?${query.toString()}`;\n}\n\n/**\n * The QR refresh cadence for a pairing: the provider's, or the default when\n * it sent none (a provider that predates animated frames, or a legacy\n * pairing). Anything that is not a positive number is treated as absent\n * rather than trusted, because a zero here would spin.\n */\nexport function qrRefreshSecondsOf(started: PairCreated): number {\n const seconds = started.qr_refresh_seconds;\n return typeof seconds === 'number' && seconds > 0 ? seconds : DEFAULT_QR_REFRESH_SECONDS;\n}\n\n/**\n * The URL of the provider's current QR frame. The query is a cache-buster\n * and nothing more: the frame itself is chosen on the provider, this package\n * only asks for it again. A new value every call, so an <img> whose src is\n * set to it fetches rather than reusing what it showed last time.\n */\nexport function qrFrameUrl(issuer: string, requestId: string): string {\n return `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg?t=${Date.now()}`;\n}\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n // An already-aborted signal never fires its abort event, so check first\n // or the sleep runs to term and the poll takes one extra swing.\n if (signal?.aborted) {\n reject(new DOMException('aborted', 'AbortError'));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(new DOMException('aborted', 'AbortError'));\n };\n // Each sleep takes its listener back off when it finishes. One signal\n // lives for the whole login and is slept on once per poll and once per QR\n // frame, so listeners left behind pile up on it for as long as the\n // pairing is open: dozens per login, all of them dead.\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/** How long a same-device navigation is given to begin before the first poll. */\nconst SETTLE_MS = 1500;\n/** Consecutive network failures a poll rides out before it is a failure. */\nconst NETWORK_FAILURES_TOLERATED = 4;\n\nexport interface PollOptions {\n /**\n * Same-device navigation only. The page starts polling while the\n * provider is still answering the navigation that creates the pairing,\n * so a \"no such pairing\" answer before this instant (epoch ms) is the\n * pairing not existing YET, and is read as pending.\n */\n tolerateUnknownUntil?: number;\n /**\n * QR surface only. While the request is pending, hand `onState` a fresh\n * `qrUrl` every this many seconds, merged into the last state seen, so the\n * code on screen keeps up with the provider's moving frame. Omit on the app\n * link: a link pairing has no QR and the provider answers 404 for it.\n */\n qrRefreshSeconds?: number;\n}\n\n/**\n * Polls until the request resolves. Returns the authorization code.\n * Throws FlowAbandonedError for the human outcomes (denied, expired,\n * enrolment abandoned) and OAuthFlowError for protocol ones.\n *\n * With `qrRefreshSeconds` set it also drives the QR animation: between polls\n * it re-issues `qrUrl` on that cadence, through the same `onState`, for as\n * long as the request is pending. The frames stop the moment the status\n * leaves pending (the QR is spent once the phone has claimed it) and when the\n * poll is aborted, so a cancelled login never keeps fetching an image.\n */\nexport async function pollUntilApproved(\n issuer: string,\n requestId: string,\n onState?: (state: PairingState) => void,\n signal?: AbortSignal,\n options: PollOptions = {}\n): Promise<string> {\n let last: PairingState = { status: 'pending' };\n const emit = (state: PairingState) => {\n last = state;\n onState?.(state);\n };\n\n // The frame loop is a setTimeout chain armed from the clock after each\n // frame, never a setInterval: a background tab throttles timers, and an\n // interval that wakes late fires its missed ticks in a burst, which here\n // would be a burst of image fetches for frames the provider has already\n // moved past. Waking late costs one frame's delay, then the cadence resumes\n // from now. The provider always renders the current frame regardless.\n let frames: AbortController | null = null;\n const stopFrames = () => {\n frames?.abort();\n frames = null;\n };\n const startFrames = () => {\n const seconds = options.qrRefreshSeconds;\n if (frames || signal?.aborted || typeof seconds !== 'number' || !(seconds > 0)) return;\n const period = seconds * 1000;\n const controller = new AbortController();\n frames = controller;\n // The caller's abort reaches the frames too, and the listener goes away\n // with them so a long-lived signal does not accumulate one per pairing.\n signal?.addEventListener('abort', stopFrames, { signal: controller.signal });\n void (async () => {\n let due = Date.now() + period;\n for (;;) {\n await sleep(Math.max(0, due - Date.now()), controller.signal);\n emit({ ...last, qrUrl: qrFrameUrl(issuer, requestId) });\n due = Date.now() + period;\n }\n })().catch(() => {\n // Aborted: the frames stopped with the pairing. Nothing to report.\n });\n };\n\n const settlingUntil = options.tolerateUnknownUntil ?? 0;\n let networkFailures = 0;\n try {\n // Let a same-device navigation begin before the first poll, so the poll\n // is not the request the navigation cancels.\n if (settlingUntil > Date.now()) await sleep(SETTLE_MS, signal);\n for (;;) {\n let response: Response;\n try {\n response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {\n signal,\n });\n networkFailures = 0;\n } catch (e) {\n if (e instanceof DOMException && e.name === 'AbortError') throw e;\n // A navigation cancels the page's requests while it is in flight, and\n // the same-device sign-in IS a navigation: the first poll after the tap\n // is killed by it (Safari reports \"Load failed\"), even though the tab\n // stays once the app has taken the link. A network failure while the\n // navigation settles is therefore not an outcome, and one in the\n // background, on a phone that has just switched apps, seldom is either:\n // only a run of them is.\n networkFailures += 1;\n if (settlingUntil > Date.now() || networkFailures <= NETWORK_FAILURES_TOLERATED) {\n emit({ ...last, status: last.status });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n throw e;\n }\n const body = (await parseJson(response)) as unknown as PairStatusResponse;\n\n if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {\n // Same-device navigation: the pairing is being created by the\n // navigation this page is polling ahead of; not there YET is pending.\n emit({ status: 'pending' });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n\n if (!response.ok) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Pairing status failed (${response.status})`\n );\n }\n\n emit({\n status: body.status,\n expiresIn: body.expires_in,\n enrolmentDeadline: body.enrolment_deadline,\n });\n\n // Frames run only while the code is still the thing on screen. Once the\n // phone has claimed it the image is spent, and a frame issued after that\n // would only replace the spent code with a different spent code.\n if (body.status === 'pending') startFrames();\n else stopFrames();\n\n switch (body.status) {\n case 'approved':\n if (!body.code) {\n throw new OAuthFlowError('server_error', 'approved with no authorization code');\n }\n return body.code;\n case 'denied':\n throw new FlowAbandonedError({ type: 'request_denied', description: body.error_description });\n case 'expired':\n throw new FlowAbandonedError({ type: 'request_expired', description: body.error_description });\n case 'cancelled':\n // The provider cancels an over-polled or abandoned request outright\n // (its pairing rows have a real cancelled state). Before 0.1.4 this\n // fell through to the default branch and polled a dead request\n // forever.\n throw new FlowAbandonedError({\n type: 'request_expired',\n description: body.error_description ?? 'the provider cancelled the pairing request',\n });\n case 'enrolling':\n await sleep(POLL_INTERVAL_ENROLLING_MS, signal);\n break;\n default:\n await sleep(POLL_INTERVAL_MS, signal);\n }\n }\n } finally {\n stopFrames();\n }\n}\n\n/**\n * The code exchange, browser-direct mode only: a public client, PKCE and no\n * secret. What comes back can only ever be the pseudonymous tier, by\n * construction rather than by rule: personal data lives at /userinfo behind an\n * access token this mode is never issued, because personal-data scopes are\n * refused for public clients at the pairing step.\n */\nexport async function exchangeCode(\n issuer: string,\n input: { code: string; code_verifier: string; client_id: string }\n): Promise<TokenResponse> {\n const response = await fetch(`${issuer}/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: input.code,\n code_verifier: input.code_verifier,\n client_id: input.client_id,\n }),\n });\n\n const body = (await parseJson(response)) as unknown as TokenResponse;\n if (!response.ok || body.error) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Token exchange failed (${response.status})`\n );\n }\n return body;\n}\n\n/** A mobile user agent gets the app link, not a QR of its own screen. */\nexport function isMobileUserAgent(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /android|iphone|ipad|ipod/i.test(navigator.userAgent);\n}\n\n/**\n * Which surface this login will use, from the caller's preference and the\n * user agent. Decided before the pairing is created, never after: the\n * provider binds the pairing to the surface it is told about, and the two\n * surfaces are claimed differently, so asking for one and showing the other\n * produces a code the phone is right to refuse.\n */\nexport function resolveDisplay(display?: 'auto' | 'qr' | 'link'): PairDisplay {\n if (display === 'link') return 'link';\n if (display === 'qr') return 'qr';\n return isMobileUserAgent() ? 'link' : 'qr';\n}\n","/**\n * PKCE, S256 only: mandatory for every client, confidential ones included.\n * There is no plain fallback and there must never be one; a provider seeing\n * method=plain is seeing a bug or an attack.\n */\n\nconst VERIFIER_BYTES = 32; // 43 base64url chars, the RFC 7636 minimum length\n\nconst base64url = (bytes: Uint8Array): string =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nexport function generateVerifier(): string {\n const bytes = new Uint8Array(VERIFIER_BYTES);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\nexport async function challengeS256(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64url(new Uint8Array(digest));\n}\n\n/**\n * The same challenge, computed synchronously.\n *\n * The same-device sign-in is a navigation the browser must see as the\n * person's own tap, and an `await` between the tap and the navigation is\n * what breaks that: WebCrypto only digests asynchronously, so the digest is\n * done here by hand. SHA-256 as in FIPS 180-4, verified against the RFC\n * 7636 vector and against WebCrypto in the tests.\n */\nexport function challengeS256Sync(verifier: string): string {\n return base64url(sha256(new TextEncoder().encode(verifier)));\n}\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst rotr = (x: number, n: number): number => (x >>> n) | (x << (32 - n));\n\nexport function sha256(message: Uint8Array): Uint8Array {\n const H = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ]);\n const length = message.length;\n const padded = new Uint8Array(((length + 9 + 63) >> 6) << 6);\n padded.set(message);\n padded[length] = 0x80;\n const view = new DataView(padded.buffer);\n const bits = length * 8;\n view.setUint32(padded.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(padded.length - 4, bits >>> 0);\n\n const W = new Uint32Array(64);\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = W[i - 15];\n const w2 = W[i - 2];\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;\n }\n let [a, b, c, d, e, f, g, h] = H;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i] + W[i]) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0] + a) >>> 0;\n H[1] = (H[1] + b) >>> 0;\n H[2] = (H[2] + c) >>> 0;\n H[3] = (H[3] + d) >>> 0;\n H[4] = (H[4] + e) >>> 0;\n H[5] = (H[5] + f) >>> 0;\n H[6] = (H[6] + g) >>> 0;\n H[7] = (H[7] + h) >>> 0;\n }\n const out = new Uint8Array(32);\n const outView = new DataView(out.buffer);\n for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);\n return out;\n}\n\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\n/**\n * A pairing token of this package's own choosing, for the same-device\n * navigation: the provider answers a navigation with nothing the page could\n * read, so the page names the pairing it will poll. Same shape as a token\n * the provider mints, 32 letters and digits from the CSPRNG.\n */\nconst TOKEN_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nexport function generateRequestId(): string {\n let out = '';\n const bytes = new Uint8Array(64);\n while (out.length < 32) {\n crypto.getRandomValues(bytes);\n for (const byte of bytes) {\n // Rejection sampling: 62 does not divide 256, so bytes past the last\n // full multiple are thrown away rather than folded, which would bias.\n if (byte >= 248 || out.length === 32) continue;\n out += TOKEN_ALPHABET[byte % 62];\n }\n }\n return out;\n}\n","import { useEffect, useLayoutEffect, useRef, type CSSProperties, type ReactNode } from 'react';\nimport { cx, ensureStyles } from './styles';\nimport type { ZorealTheme } from './types';\n\n/**\n * The light of the pairing dialog's QR well, run around whatever this wraps\n * while `busy` is true: the well's colours, line, halo and lap, on a dash\n * that travels the outline at one speed whatever the shape.\n *\n * The SDK's own button uses it for the gap on a phone between the tap and the\n * hand-over to the app. A site with its own sign-in button MAY wrap that\n * button the same way: set your busy state on the tap, clear it when\n * `onSuccess` or `onError` fires. `radius` is the wrapped control's corner\n * radius in pixels, so the light follows its shape; `theme` picks the light\n * and dark strengths the dialog uses, following the OS by default.\n *\n * It is a wrapper, and a wrapper is a change to your markup, so know what it\n * does before you use it: it is inline by default and shrinks to the button,\n * so a full-width button needs `block`; the light sits outside the button,\n * so an ancestor with `overflow: hidden` clips it; and a selector that relies\n * on the button's parent (`.card > button`) no longer matches. A site that\n * would rather draw its own busy state needs nothing from here.\n */\n/** How far the tail reaches behind the head, as a fraction of the outline. */\nconst TAIL = 0.3;\n/* Layer n is n/N of the tail long and 1/n opaque. A point k/N of the way\n back is covered by layers n >= k, and the product of their transparencies\n telescopes to (k - 1)/N: a straight fade. Longest first, so the head paints\n on top. The two shortest carry the head tint. */\nconst STACK = Array.from({ length: 12 }, (_, i) => 12 - i);\nconst HALO = [3, 2, 1];\n\nexport function ZorealBusyRing({\n busy,\n radius = 8,\n theme = 'auto',\n block = false,\n className,\n style,\n children,\n}: {\n busy: boolean;\n radius?: number;\n theme?: ZorealTheme;\n /** Lay the wrapper out as a block, for a button that fills its row. */\n block?: boolean;\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n}) {\n useEffect(() => {\n ensureStyles();\n }, []);\n // The dash is sized as a fraction of the outline, so the outline's length\n // is measured once laid out and again whenever the control changes size.\n const wrapRef = useRef<HTMLSpanElement>(null);\n const measureRef = useRef<SVGRectElement>(null);\n useLayoutEffect(() => {\n const wrap = wrapRef.current;\n const rect = measureRef.current;\n if (!wrap || !rect) return;\n const measure = () => {\n const length = rect.getTotalLength();\n if (length > 0) wrap.style.setProperty('--zrl-ring-len', `${length}px`);\n };\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(wrap);\n return () => observer.disconnect();\n }, [radius]);\n // The outline runs 2px outside the control, so its corners are 2px larger.\n const rx = radius + 2;\n // One dash of the stack: `len` of the outline long, ending at the shared\n // head three tenths of the way round, `alpha` opaque.\n const layer = (key: string, name: string, len: number, alpha: string, ref?: typeof measureRef) => (\n <rect\n key={key}\n ref={ref}\n className={cx(name)}\n rx={rx}\n ry={rx}\n style={\n {\n strokeDasharray: `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`,\n '--zrl-s': `calc(var(--zrl-l) * ${-(TAIL - len)})`,\n opacity: alpha,\n } as CSSProperties\n }\n />\n );\n return (\n <span\n ref={wrapRef}\n className={className ? `${cx('root')} ${cx('ring')} ${className}` : `${cx('root')} ${cx('ring')}`}\n data-theme={theme}\n data-busy={busy}\n style={block ? { display: 'flex', ...style } : style}\n >\n {children}\n <svg className={cx('ring-svg')} aria-hidden=\"true\">\n {HALO.map((n, i) =>\n layer(`h${n}`, 'ring-halo', (TAIL * n) / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`, i === 0 ? measureRef : undefined)\n )}\n {STACK.map((n) => layer(`t${n}`, n <= 2 ? 'ring-head' : 'ring-tail', (TAIL * n) / STACK.length, String(1 / n)))}\n </svg>\n </span>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useZorealFlow } from './useZorealLogin';\nimport type { UseZorealAutoLoginOptions } from './types';\n\n/**\n * Silent re-auth with prompt=none. NOT One Tap and never could be: there is no\n * ZOREAL session cookie in the browser to read, the credential is on a phone.\n * This succeeds only for a returning user at a consented sector with a live\n * session, the resulting acr is zoreal.session with an empty amr, and a\n * relying party that needs a live human must not build on this hook.\n *\n * PRIVACY NOTE: mounting this on a page sends a request to ZOREAL on page\n * load, before the user does anything. The hook is therefore conservative: it\n * fires once per mount, never retries, and does nothing when `disabled`.\n */\nexport function useZorealAutoLogin(options: UseZorealAutoLoginOptions): void {\n const { login } = useZorealFlow({\n flow: 'browser-direct',\n scope: options.scope,\n prompt: 'none',\n onCredential: options.onSuccess,\n onError: (e) => {\n // The provider's honest answer when no silent session exists (the\n // common case): unavailable, not an error, and never surfaced.\n const quiet = ['login_required', 'consent_required', 'interaction_required'];\n if (quiet.includes(e.error)) {\n options.onUnavailable?.();\n } else {\n options.onError?.({ type: 'unknown', description: e.description ?? e.error });\n }\n },\n onNonOAuthError: (e) => options.onError?.(e),\n });\n\n const fired = useRef(false);\n useEffect(() => {\n if (options.disabled || fired.current) return;\n fired.current = true;\n login();\n }, [options.disabled, login]);\n}\n","/**\n * Clears SDK-held local state. Named for parity with googleLogout and, like\n * it, LOCAL ONLY: it does not end the holder's ZOREAL session, which lives on\n * their phone and at the provider. A relying party that believes this signs\n * the user out of ZOREAL has a security misunderstanding, not a naming\n * complaint. The relying party's own session is the relying\n * party's to end.\n *\n * The SDK deliberately persists nothing (no localStorage, no cookies), so\n * today this has nothing to clear and exists as the stable API surface for a\n * future that does.\n */\nexport function zorealLogout(): void {\n // Intentionally empty until the SDK holds state worth clearing.\n}\n","import type { ZorealCodeResponse } from './types';\n\n/** Mirrors hasGrantedAllScopesGoogle for name-for-name portability. */\nexport function hasGrantedAllScopesZoreal(\n response: Pick<ZorealCodeResponse, 'scope'>,\n firstScope: string,\n ...restScopes: string[]\n): boolean {\n const granted = new Set((response.scope ?? '').split(/\\s+/).filter(Boolean));\n return [firstScope, ...restScopes].every((s) => granted.has(s));\n}\n\nexport function hasGrantedAnyScopeZoreal(\n response: Pick<ZorealCodeResponse, 'scope'>,\n firstScope: string,\n ...restScopes: string[]\n): boolean {\n const granted = new Set((response.scope ?? '').split(/\\s+/).filter(Boolean));\n return [firstScope, ...restScopes].some((s) => granted.has(s));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAA6E;;;AC+DtE,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,iBAAiB;AAGvB,IAAM,mBAAmB;AAEzB,IAAM,6BAA6B;AAOnC,IAAM,6BAA6B;;;AC7E1C,mBAAmD;AACnD,uBAA6B;;;ACqBzB;AAbG,IAAM,cAAc;AAEpB,SAAS,WAAW;AAAA,EACzB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AACF,GAKG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAQ;AAAA,MACR,MAAM,QAAQ,cAAc;AAAA,MAC5B,UAAS;AAAA,MACT,eAAW;AAAA,MACX,WAAU;AAAA,MACV;AAAA,MAEA;AAAA,oDAAC,UAAK,GAAE,+cAA8c;AAAA,QACtd,4CAAC,UAAK,GAAE,kGAAiG;AAAA,QACzG,4CAAC,UAAK,GAAE,sOAAqO;AAAA,QAC7O,4CAAC,UAAK,GAAE,mGAAkG;AAAA,QAC1G,4CAAC,UAAK,GAAE,6FAA4F;AAAA,QACpG,4CAAC,UAAK,GAAE,uOAAsO;AAAA,QAC9O,4CAAC,UAAK,GAAE,kFAAiF;AAAA;AAAA;AAAA,EAC3F;AAEJ;;;ACjBM,IAAAC,sBAAA;AAXC,SAAS,aAAa,EAAE,SAAS,IAAI,UAAU,GAA4C;AAChG,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,OAAO,UAAU,MAAM;AAAA,MACvB,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAU;AAAA,MACV;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,GAAE;AAAA;AAAA,QACJ;AAAA,QACA,8CAAC,OAAE,MAAM,aAAa,UAAS,WAC7B;AAAA,uDAAC,UAAK,GAAE,waAAua;AAAA,UAC/a,6CAAC,UAAK,GAAE,8FAA6F;AAAA,UACrG,6CAAC,UAAK,GAAE,kNAAiN;AAAA,UACzN,6CAAC,UAAK,GAAE,sFAAqF;AAAA,UAC7F,6CAAC,UAAK,GAAE,yFAAwF;AAAA,UAChG,6CAAC,UAAK,GAAE,mNAAkN;AAAA,UAC1N,6CAAC,UAAK,GAAE,8EAA6E;AAAA,WACvF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACIA,IAAM,KAAqB;AAAA,EACzB,OAAO;AAAA,EACP,eAAe;AAAA,EACf,eAAe;AAAA,EACf,cAAc;AAAA,EACd,UAAU;AAAA,EACV,aAAa;AAAA,EACb,eAAe;AAAA,EACf,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,gBAAgB;AAClB;AAEA,IAAM,eAA+C;AAAA,EACnD;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,KAAK;AAAA,IACH,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,KAAK;AAAA,IACH,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI;AAAA,IACF,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,UAAU;AAAA,IACV,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,gBAAgB;AAAA,EAClB;AACF;AAMA,IAAM,MAAM,oBAAI,IAAI,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC;AAe5C,IAAM,UAAkC;AAAA,EACtC,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,KAAK;AAAA;AAAA,EACL,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AACN;AAQA,IAAM,QAAQ,oBAAI,IAAI;AAAA,EACpB;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EACtD;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AACxD,CAAC;AAED,SAAS,OAAO,QAA4C;AAC1D,QAAM,MAAM,OAAO,YAAY,EAAE,QAAQ,MAAM,GAAG;AAClD,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAM,UAAU,QAAQ,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;AAC5C,QAAM,SAAS,MAAM,CAAC;AAGtB,MAAI,YAAY,MAAM;AACpB,UAAM,aAAa,4BAA4B,KAAK,GAAG;AACvD,WAAO,aAAa,aAAa,QAAQ,KAAK;AAAA,EAChD;AACA,MAAI,YAAY,QAAQ,UAAU,MAAM,IAAI,MAAM,EAAG,QAAO,aAAa,QAAQ;AACjF,MAAI,YAAY,QAAQ,WAAW,KAAM,QAAO,aAAa,OAAO;AAEpE,SAAO,aAAa,GAAG,KAAK,aAAa,OAAO;AAClD;AAQA,SAAS,iBAA2B;AAClC,MAAI,OAAO,cAAc,YAAa,QAAO,CAAC;AAC9C,QAAM,MAAM;AACZ,MAAI,IAAI,aAAa,IAAI,UAAU,OAAQ,QAAO,CAAC,GAAG,IAAI,SAAS;AACnE,SAAO,IAAI,WAAW,CAAC,IAAI,QAAQ,IAAI,CAAC;AAC1C;AAYO,SAAS,QAAQ,QAAiC;AACvD,MAAI,OAAQ,QAAO,OAAO,MAAM,KAAK;AACrC,aAAW,aAAa,eAAe,GAAG;AACxC,UAAM,MAAM,OAAO,SAAS;AAC5B,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEO,SAAS,MAAM,QAA0B;AAC9C,QAAM,MAAM,UAAU,eAAe,EAAE,CAAC;AACxC,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,IAAI,IAAI,IAAI,YAAY,EAAE,QAAQ,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AACnE;AAGO,SAAS,YAAY,UAAkB,MAAsB;AAClE,SAAO,SAAS,QAAQ,UAAU,IAAI;AACxC;;;ACt4BA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,SAAS,cAAc,CAAC;AAQ3D,SAAS,cACd,QACA,OACA,WACa;AACb,MAAI,OAAQ,QAAO;AACnB,QAAM,UAAU,SAAS,UAAU,MAAM,KAAK,EAAE,OAAO,OAAO;AAC9D,MAAI,OAAO,KAAK,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC,EAAG,QAAO;AACvD,QAAM,MAAM,OAAO,cAAc,WAAW,UAAU,MAAM,KAAK,IAAK,aAAa,CAAC;AACpF,MAAI,OAAO,MAAM,CAAC,MAAM,MAAM,QAAQ,KAAK,IAAI,SAAS,aAAa,EAAG,QAAO;AAC/E,SAAO;AACT;AAGO,SAAS,SAAS,GAAmB,QAA6B;AACvE,MAAI,WAAW,WAAY,QAAO,EAAE;AACpC,MAAI,WAAW,WAAY,QAAO,EAAE;AACpC,SAAO,EAAE;AACX;;;ACbA,IAAM,SAAS;AACR,IAAM,KAAK,CAAC,SAAiB,GAAG,MAAM,IAAI,IAAI;AAE9C,IAAM,mBAAmB;AAOhC,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8Bd,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCN,IAAM,MAAM;AAAA,GAChB,MAAM,WAAW,KAAK;AAAA,GACtB,MAAM,8BAA8B,IAAI;AAAA;AAAA,KAEtC,MAAM,8BAA8B,IAAI;AAAA;AAAA;AAAA,GAG1C,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAYM,MAAM;AAAA;AAAA;AAAA,GAGlB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAYM,MAAM;AAAA;AAAA;AAAA,GAGlB,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAuCN,MAAM;AAAA,GACN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAaN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAaN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAWM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQlB,MAAM,gCAAgC,MAAM;AAAA,GAC5C,MAAM,gCAAgC,MAAM;AAAA;AAAA;AAAA;AAAA,GAI5C,MAAM,gCAAgC,MAAM;AAAA,GAC5C,MAAM,gCAAgC,MAAM;AAAA;AAAA,GAE5C,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAeN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,eAKM,MAAM;AAAA;AAAA;AAAA,GAGlB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQN,MAAM,mCAAmC,MAAM;AAAA;AAAA,GAE/C,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAON,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAcN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAgBN,MAAM;AAAA,GACN,MAAM;AAAA;AAAA,GAEN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,aAKI,MAAM;AAAA,aACN,MAAM;AAAA;AAAA;AAAA;AAAA,aAIN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,aAKN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiBhB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAMN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUN,MAAM,8BAA8B,MAAM;AAAA,GAC1C,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAWM,MAAM;AAAA;AAAA,GAElB,MAAM;AAAA,GACN,MAAM;AAAA;AAAA;AAAA;AAAA,aAII,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,KAKd,MAAM;AAAA,KACN,MAAM,gBAAgB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,KAK5B,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA;AAAA,KAEN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA,KACN,MAAM;AAAA;AAAA;AASJ,SAAS,eAAqB;AACnC,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,eAAe,gBAAgB,EAAG;AAC/C,QAAM,KAAK,SAAS,cAAc,OAAO;AACzC,KAAG,KAAK;AACR,KAAG,cAAc;AACjB,WAAS,KAAK,YAAY,EAAE;AAC9B;;;ALvdI,IAAAC,sBAAA;AAjBG,IAAM,6BAA6B;AAI1C,IAAM,iBAAiB;AAEvB,SAAS,KAAK,cAA8B;AAC1C,QAAM,IAAI,KAAK,MAAM,eAAe,EAAE;AACtC,QAAM,IAAI,eAAe;AACzB,SAAO,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAC3C;AAKA,IAAM,YAAY,MAChB,6CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAAC,WAAU,SAC5I,uDAAC,UAAK,GAAE,wBAAuB,GACjC;AAGF,IAAM,YAAY,MAChB,8CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MAAC,WAAU,SACrK;AAAA,+CAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,OAAM;AAAA,EAClD,6CAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAGF,IAAM,aAAa,MACjB,8CAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MAAC,WAAU,SACnK;AAAA,+CAAC,UAAK,GAAE,+CAA8C;AAAA,EACtD,6CAAC,UAAK,GAAE,iBAAgB;AAAA,GAC1B;AAcK,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AACX,GAAsB;AACpB,QAAM,IAAI,QAAQ,MAAM;AACxB,QAAM,cAAU,oBAAM;AACtB,QAAM,eAAW,qBAA0B,IAAI;AAK/C,QAAM,kBAAc,qBAAO,CAAC;AAC5B,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK,MAAM,YAAY,GAAI,CAAC;AAKvE,QAAM,UAAU,MAAM,WAAW,aAAa,MAAM,WAAW;AAW/D,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAC9C,8BAAU,MAAM;AAGd,QAAI,WAAW,aAAa,MAAO;AACnC,QAAI,YAAY;AAChB,UAAM,OAAO,IAAI,MAAM;AACvB,SAAK,SAAS,MAAM;AAClB,UAAI,CAAC,UAAW,aAAY,KAAK;AAAA,IACnC;AACA,SAAK,UAAU,MAAM;AAAA,IAErB;AACA,SAAK,MAAM;AACX,WAAO,MAAM;AACX,kBAAY;AACZ,WAAK,SAAS;AACd,WAAK,UAAU;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,QAAQ,CAAC;AAE7B,QAAM,kBAAc,qBAAO,QAAQ;AACnC,cAAY,UAAU;AAEtB,8BAAU,MAAM;AACd,iBAAa;AAAA,EACf,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AAGd,UAAM,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY,MAAO;AAChF,gBAAY,UAAU,KAAK,IAAI,IAAI,KAAK,IAAI,WAAW,QAAQ;AAC/D,iBAAa,KAAK,MAAM,KAAK,IAAI,WAAW,QAAQ,IAAI,GAAI,CAAC;AAE7D,UAAM,KAAK,YAAY,MAAM;AAC3B,YAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,UAAU,KAAK,IAAI,KAAK,GAAI,CAAC;AAC7E,mBAAa,IAAI;AACjB,UAAI,SAAS,GAAG;AAId,sBAAc,EAAE;AAChB,oBAAY,QAAQ;AAAA,MACtB;AAAA,IACF,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,EAAE;AAAA,EAI/B,GAAG,CAAC,SAAS,CAAC;AAId,8BAAU,MAAM;AACd,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,SAAU,aAAY,QAAQ;AAAA,IAC9C;AACA,aAAS,iBAAiB,WAAW,KAAK;AAC1C,UAAM,WAAW,SAAS,KAAK,MAAM;AACrC,aAAS,KAAK,MAAM,WAAW;AAC/B,aAAS,SAAS,MAAM;AACxB,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,KAAK;AAC7C,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,OACJ,MAAM,WAAW,cACb,EAAE,gBACF,UACE,EAAE,cACF,EAAE;AAEV,aAAO;AAAA,IACL,6CAAC,SAAI,WAAW,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,cAAY,OAAO,SAAS,UAC1E;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,MAAM;AAAA,QACpB,MAAK;AAAA,QACL,cAAW;AAAA,QACX,mBAAiB;AAAA,QACjB,KAAK,MAAM,MAAM,IAAI,QAAQ;AAAA,QAC7B,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,QAElC;AAAA,uDAAC,YAAO,KAAK,UAAU,MAAK,UAAS,WAAW,GAAG,OAAO,GAAG,cAAY,EAAE,OAAO,SAAS,UACzF,uDAAC,aAAU,GACb;AAAA,UAEA,8CAAC,SAAI,WAAW,GAAG,MAAM,GACvB;AAAA,yDAAC,gBAAa,QAAQ,IAAI,WAAW,GAAG,QAAQ,GAAG;AAAA,YAEnD,6CAAC,QAAG,IAAI,SAAS,WAAW,GAAG,OAAO,GACnC,oBAAU,EAAE,eAAe,SAAS,GAAG,MAAM,GAChD;AAAA,YACA,6CAAC,OAAE,WAAW,GAAG,WAAW,GAAI,gBAAK;AAAA,YAOrC,8CAAC,SAAI,WAAW,GAAG,SAAS,GAAG,cAAY,SACzC;AAAA,2DAAC,UAAK,WAAW,GAAG,cAAc,GAAG,eAAW,MAC9C,uDAAC,UAAK,WAAW,GAAG,mBAAmB,GAAG,GAC5C;AAAA,cACA,6CAAC,UAAK,WAAW,GAAG,SAAS,GAAG,eAAW,MAAC;AAAA,cAC5C,6CAAC,SAAI,WAAW,GAAG,IAAI,GAAG,cAAY,SAAS,KAAK,UAAU,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,KAAK;AAAA,cACpG,WACC,6CAAC,UAAK,WAAW,GAAG,YAAY,GAC9B,uDAAC,UAAK,WAAW,GAAG,UAAU,GAC5B,uDAAC,aAAU,GACb,GACF;AAAA,eAEJ;AAAA,YAEA,8CAAC,SAAI,WAAW,GAAG,QAAQ,GACzB;AAAA,4DAAC,UAAK,WAAW,GAAG,KAAK,GACvB;AAAA,6DAAC,OAAE;AAAA,gBACH,6CAAC,OAAE;AAAA,iBACL;AAAA,cACC,UAAU,EAAE,kBAAkB,EAAE;AAAA,eACnC;AAAA,YACA,6CAAC,OAAE,WAAW,GAAG,OAAO,GAAG,eAAa,aAAa,gBAClD,sBAAY,EAAE,WAAW,KAAK,SAAS,CAAC,GAC3C;AAAA,aACF;AAAA,UAOA,8CAAC,SAAI,WAAW,GAAG,MAAM,GACvB;AAAA,yDAAC,OAAE,WAAW,GAAG,YAAY,GAAI,YAAE,WAAU;AAAA,YAC7C,6CAAC,OAAE,WAAW,GAAG,WAAW,GAAI,YAAE,UAAS;AAAA,aAC7C;AAAA,UAEA,8CAAC,SAAI,WAAW,GAAG,QAAQ,GACzB;AAAA,yDAAC,YAAO,MAAK,UAAS,WAAW,GAAG,QAAQ,GAAG,SAAS,UACrD,YAAE,QACL;AAAA,YACA,8CAAC,OAAE,WAAW,GAAG,SAAS,GAAG,MAAK,sBAAqB,QAAO,UAAS,KAAI,YACzE;AAAA,2DAAC,cAAW;AAAA,cACX,EAAE;AAAA,eACL;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;AFxKM,IAAAC,sBAAA;AAvCN,IAAM,yBAAqB,6BAA8C,IAAI;AAa7E,IAAM,yBAAqB,6BAAgE,IAAI;AAExF,SAAS,uBAAuB;AACrC,aAAO,0BAAW,kBAAkB;AACtC;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA+B,IAAI;AAEjE,QAAM,YAAQ;AAAA,IACZ,OAAO,EAAE,UAAU,QAAQ,OAAO,QAAQ,OAAO,EAAE,GAAG,OAAO;AAAA,IAC7D,CAAC,UAAU,QAAQ,MAAM;AAAA,EAC3B;AAEA,QAAM,OAAO,cAAc,UAAU,aAAa;AAElD,SACE,6CAAC,mBAAmB,UAAnB,EAA4B,OAC3B,wDAAC,mBAAmB,UAAnB,EAA4B,OAAO,MACjC;AAAA;AAAA,IACA,WACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,QAAQ;AAAA,QACf,OAAO,QAAQ;AAAA,QACf,QAAQ,QAAQ;AAAA,QAChB,UAAU,QAAQ;AAAA,QAClB;AAAA,QACA;AAAA,QACA,WAAW;AAAA;AAAA,IACb;AAAA,KAEJ,GACF;AAEJ;AAEO,SAAS,iBAA0C;AACxD,QAAM,UAAM,0BAAW,kBAAkB;AACzC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;;;AQ/GA,IAAAC,gBAAsD;;;ACAtD,IAAAC,gBAAyD;;;AC8BzD,IAAMC,UAAS;AACf,IAAM,OAAO;AAEb,IAAM,aAAa,KAAK,KAAK;AAE7B,SAAS,UAA0B;AACjC,MAAI;AACF,WAAO,OAAO,iBAAiB,cAAc,OAAO;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,MAAuB;AACpD,MAAI;AACF,YAAQ,GAAG,QAAQA,UAAS,KAAK,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,EAClE,QAAQ;AAAA,EAER;AACF;AAQO,SAAS,eAAe,WAAqC;AAClE,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,MAAM,QAAQA,UAAS,SAAS;AAC5C,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,KAAK,MAAM,KAAK,KAAK,cAAc,UAAW,QAAO;AACzD,QAAI,KAAK,IAAI,IAAI,KAAK,YAAY,WAAY,QAAO;AACrD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,iBAAiB,WAAyB;AACxD,MAAI;AACF,YAAQ,GAAG,WAAWA,UAAS,SAAS;AAAA,EAC1C,QAAQ;AAAA,EAER;AACA,MAAI,YAAY,UAAW,WAAU;AACvC;AAEO,SAAS,eAAe,WAAyB;AACtD,MAAI;AACF,UAAM,QAAQ,QAAQ;AACtB,WAAO,QAAQ,OAAO,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC;AACnD,WAAO,WAAWA,UAAS,SAAS;AAAA,EACtC,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,aAAa,WAA4B;AACvD,SAAO,QAAQ,GAAG,QAAQ,OAAO,SAAS,MAAM,QAAQ,QAAQ,GAAG,QAAQ,OAAO,SAAS,MAAM;AACnG;AAGO,SAAS,cAAkC;AAChD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,OAAO,KAAK,QAAQ,GAAG;AAC7B,SAAO,SAAS,KAAK,OAAO,KAAK,MAAM,GAAG,IAAI;AAChD;AAEA,IAAM,cAAc;AAGpB,IAAI,UAAyB;AAQtB,SAAS,kBAAiC;AAC/C,MAAI,QAAS,QAAO;AACpB,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,QAAQ,YAAY,KAAK,OAAO,SAAS,IAAI;AACnD,MAAI,CAAC,MAAO,QAAO;AACnB,YAAU,MAAM,CAAC;AACjB,MAAI;AACF,WAAO,QAAQ,aAAa,OAAO,QAAQ,OAAO,IAAI,YAAY,CAAC;AAAA,EACrE,QAAQ;AAAA,EAER;AACA,SAAO;AACT;;;ACpHO,SAAS,aAAa,SAA0C;AACrE,MAAI;AACF,UAAM,UAAU,QAAQ,MAAM,GAAG,EAAE,CAAC,KAAK;AACzC,UAAM,MAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACxD,UAAM,SAAS,MAAM,IAAI,QAAQ,IAAK,IAAI,SAAS,KAAM,CAAC;AAC1D,WAAO,KAAK;AAAA,MACV,IAAI,YAAY,EAAE,OAAO,WAAW,KAAK,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAAA,IAChF;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;ACEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YACS,OACA,aACP;AACA,UAAM,eAAe,KAAK;AAHnB;AACA;AAAA,EAGT;AACF;AAEO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAmB,QAAuB;AACxC,UAAM,OAAO,eAAe,OAAO,IAAI;AADtB;AAAA,EAEnB;AACF;AAqBA,eAAe,UAAU,UAAsD;AAC7E,MAAI;AACF,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,aACpB,QACA,QAC4B;AAC5B,QAAM,WAAW,MAAM,MAAM,GAAG,MAAM,SAAS;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB,GAAG;AAAA,MACH,uBAAuB;AAAA,MACvB,cAAc;AAAA,MACd,KAAK,wBAAwB,WAAW;AAAA,IAC1C,CAAC;AAAA,EACH,CAAC;AAED,QAAM,OAAO,MAAM,UAAU,QAAQ;AACrC,MAAI,CAAC,SAAS,IAAI;AAIhB,UAAM,IAAI;AAAA,MACP,KAAK,SAAuB;AAAA,MAC5B,KAAK,qBAAgC,qCAAqC,SAAS,MAAM;AAAA,IAC5F;AAAA,EACF;AACA,SAAO;AACT;AAkBO,SAAS,mBACd,QACA,QACQ;AACR,QAAM,QAAQ,IAAI,gBAAgB;AAClC,QAAM,MAA+B;AAAA,IACnC,GAAG;AAAA,IACH,uBAAuB;AAAA,IACvB,cAAc;AAAA,IACd,KAAK,wBAAwB,WAAW;AAAA,EAC1C;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI;AAC3D,UAAM,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,EAC9B;AACA,SAAO,GAAG,MAAM,eAAe,MAAM,SAAS,CAAC;AACjD;AAQO,SAAS,mBAAmB,SAA8B;AAC/D,QAAM,UAAU,QAAQ;AACxB,SAAO,OAAO,YAAY,YAAY,UAAU,IAAI,UAAU;AAChE;AAQO,SAAS,WAAW,QAAgB,WAA2B;AACpE,SAAO,GAAG,MAAM,SAAS,mBAAmB,SAAS,CAAC,aAAa,KAAK,IAAI,CAAC;AAC/E;AAEA,IAAM,QAAQ,CAAC,IAAY,WACzB,IAAI,QAAc,CAAC,SAAS,WAAW;AAGrC,MAAI,QAAQ,SAAS;AACnB,WAAO,IAAI,aAAa,WAAW,YAAY,CAAC;AAChD;AAAA,EACF;AACA,QAAM,UAAU,MAAM;AACpB,iBAAa,KAAK;AAClB,WAAO,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,EAClD;AAKA,QAAM,QAAQ,WAAW,MAAM;AAC7B,YAAQ,oBAAoB,SAAS,OAAO;AAC5C,YAAQ;AAAA,EACV,GAAG,EAAE;AACL,UAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAC3D,CAAC;AAGH,IAAM,YAAY;AAElB,IAAM,6BAA6B;AA8BnC,eAAsB,kBACpB,QACA,WACA,SACA,QACA,UAAuB,CAAC,GACP;AACjB,MAAI,OAAqB,EAAE,QAAQ,UAAU;AAC7C,QAAM,OAAO,CAAC,UAAwB;AACpC,WAAO;AACP,cAAU,KAAK;AAAA,EACjB;AAQA,MAAI,SAAiC;AACrC,QAAM,aAAa,MAAM;AACvB,YAAQ,MAAM;AACd,aAAS;AAAA,EACX;AACA,QAAM,cAAc,MAAM;AACxB,UAAM,UAAU,QAAQ;AACxB,QAAI,UAAU,QAAQ,WAAW,OAAO,YAAY,YAAY,EAAE,UAAU,GAAI;AAChF,UAAM,SAAS,UAAU;AACzB,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS;AAGT,YAAQ,iBAAiB,SAAS,YAAY,EAAE,QAAQ,WAAW,OAAO,CAAC;AAC3E,UAAM,YAAY;AAChB,UAAI,MAAM,KAAK,IAAI,IAAI;AACvB,iBAAS;AACP,cAAM,MAAM,KAAK,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,WAAW,MAAM;AAC5D,aAAK,EAAE,GAAG,MAAM,OAAO,WAAW,QAAQ,SAAS,EAAE,CAAC;AACtD,cAAM,KAAK,IAAI,IAAI;AAAA,MACrB;AAAA,IACF,GAAG,EAAE,MAAM,MAAM;AAAA,IAEjB,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,QAAQ,wBAAwB;AACtD,MAAI,kBAAkB;AACtB,MAAI;AAGF,QAAI,gBAAgB,KAAK,IAAI,EAAG,OAAM,MAAM,WAAW,MAAM;AAC7D,eAAS;AACP,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,MAAM,GAAG,MAAM,SAAS,mBAAmB,SAAS,CAAC,WAAW;AAAA,UAC/E;AAAA,QACF,CAAC;AACD,0BAAkB;AAAA,MACpB,SAAS,GAAG;AACV,YAAI,aAAa,gBAAgB,EAAE,SAAS,aAAc,OAAM;AAQhE,2BAAmB;AACnB,YAAI,gBAAgB,KAAK,IAAI,KAAK,mBAAmB,4BAA4B;AAC/E,eAAK,EAAE,GAAG,MAAM,QAAQ,KAAK,OAAO,CAAC;AACrC,gBAAM,MAAM,kBAAkB,MAAM;AACpC;AAAA,QACF;AACA,cAAM;AAAA,MACR;AACA,YAAM,OAAQ,MAAM,UAAU,QAAQ;AAEtC,UAAI,SAAS,WAAW,QAAQ,QAAQ,wBAAwB,KAAK,KAAK,IAAI,GAAG;AAG/E,aAAK,EAAE,QAAQ,UAAU,CAAC;AAC1B,cAAM,MAAM,kBAAkB,MAAM;AACpC;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI;AAAA,UACP,KAAK,SAAuB;AAAA,UAC7B,KAAK,qBAAqB,0BAA0B,SAAS,MAAM;AAAA,QACrE;AAAA,MACF;AAEA,WAAK;AAAA,QACH,QAAQ,KAAK;AAAA,QACb,WAAW,KAAK;AAAA,QAChB,mBAAmB,KAAK;AAAA,MAC1B,CAAC;AAKD,UAAI,KAAK,WAAW,UAAW,aAAY;AAAA,UACtC,YAAW;AAEhB,cAAQ,KAAK,QAAQ;AAAA,QACnB,KAAK;AACH,cAAI,CAAC,KAAK,MAAM;AACd,kBAAM,IAAI,eAAe,gBAAgB,qCAAqC;AAAA,UAChF;AACA,iBAAO,KAAK;AAAA,QACd,KAAK;AACH,gBAAM,IAAI,mBAAmB,EAAE,MAAM,kBAAkB,aAAa,KAAK,kBAAkB,CAAC;AAAA,QAC9F,KAAK;AACH,gBAAM,IAAI,mBAAmB,EAAE,MAAM,mBAAmB,aAAa,KAAK,kBAAkB,CAAC;AAAA,QAC/F,KAAK;AAKH,gBAAM,IAAI,mBAAmB;AAAA,YAC3B,MAAM;AAAA,YACN,aAAa,KAAK,qBAAqB;AAAA,UACzC,CAAC;AAAA,QACH,KAAK;AACH,gBAAM,MAAM,4BAA4B,MAAM;AAC9C;AAAA,QACF;AACE,gBAAM,MAAM,kBAAkB,MAAM;AAAA,MACxC;AAAA,IACF;AAAA,EACF,UAAE;AACA,eAAW;AAAA,EACb;AACF;AASA,eAAsB,aACpB,QACA,OACwB;AACxB,QAAM,WAAW,MAAM,MAAM,GAAG,MAAM,UAAU;AAAA,IAC9C,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,IAAI,gBAAgB;AAAA,MACxB,YAAY;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,eAAe,MAAM;AAAA,MACrB,WAAW,MAAM;AAAA,IACnB,CAAC;AAAA,EACH,CAAC;AAED,QAAM,OAAQ,MAAM,UAAU,QAAQ;AACtC,MAAI,CAAC,SAAS,MAAM,KAAK,OAAO;AAC9B,UAAM,IAAI;AAAA,MACP,KAAK,SAAuB;AAAA,MAC7B,KAAK,qBAAqB,0BAA0B,SAAS,MAAM;AAAA,IACrE;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,oBAA6B;AAC3C,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,SAAO,4BAA4B,KAAK,UAAU,SAAS;AAC7D;AASO,SAAS,eAAe,SAA+C;AAC5E,MAAI,YAAY,OAAQ,QAAO;AAC/B,MAAI,YAAY,KAAM,QAAO;AAC7B,SAAO,kBAAkB,IAAI,SAAS;AACxC;;;AC/XA,IAAM,iBAAiB;AAEvB,IAAM,YAAY,CAAC,UACjB,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC,EAC/B,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAEf,SAAS,mBAA2B;AACzC,QAAM,QAAQ,IAAI,WAAW,cAAc;AAC3C,SAAO,gBAAgB,KAAK;AAC5B,SAAO,UAAU,KAAK;AACxB;AAEA,eAAsB,cAAc,UAAmC;AACrE,QAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,EAAE,OAAO,QAAQ,CAAC;AACvF,SAAO,UAAU,IAAI,WAAW,MAAM,CAAC;AACzC;AAWO,SAAS,kBAAkB,UAA0B;AAC1D,SAAO,UAAU,OAAO,IAAI,YAAY,EAAE,OAAO,QAAQ,CAAC,CAAC;AAC7D;AAEA,IAAM,IAAI,IAAI,YAAY;AAAA,EACxB;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EACpF;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AAAA,EAAY;AACtF,CAAC;AAED,IAAM,OAAO,CAAC,GAAW,MAAuB,MAAM,IAAM,KAAM,KAAK;AAEhE,SAAS,OAAO,SAAiC;AACtD,QAAM,IAAI,IAAI,YAAY;AAAA,IACxB;AAAA,IAAY;AAAA,IAAY;AAAA,IAAY;AAAA,IAAY;AAAA,IAAY;AAAA,IAAY;AAAA,IAAY;AAAA,EACtF,CAAC;AACD,QAAM,SAAS,QAAQ;AACvB,QAAM,SAAS,IAAI,WAAa,SAAS,IAAI,MAAO,KAAM,CAAC;AAC3D,SAAO,IAAI,OAAO;AAClB,SAAO,MAAM,IAAI;AACjB,QAAM,OAAO,IAAI,SAAS,OAAO,MAAM;AACvC,QAAM,OAAO,SAAS;AACtB,OAAK,UAAU,OAAO,SAAS,GAAG,KAAK,MAAM,OAAO,UAAW,CAAC;AAChE,OAAK,UAAU,OAAO,SAAS,GAAG,SAAS,CAAC;AAE5C,QAAM,IAAI,IAAI,YAAY,EAAE;AAC5B,WAAS,SAAS,GAAG,SAAS,OAAO,QAAQ,UAAU,IAAI;AACzD,aAAS,IAAI,GAAG,IAAI,IAAI,IAAK,GAAE,CAAC,IAAI,KAAK,UAAU,SAAS,IAAI,CAAC;AACjE,aAAS,IAAI,IAAI,IAAI,IAAI,KAAK;AAC5B,YAAM,MAAM,EAAE,IAAI,EAAE;AACpB,YAAM,KAAK,EAAE,IAAI,CAAC;AAClB,YAAM,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,IAAK,QAAQ;AACnD,YAAM,KAAK,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAK,OAAO;AACjD,QAAE,CAAC,IAAK,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,IAAI,CAAC,IAAI,OAAQ;AAAA,IAC9C;AACA,QAAI,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI;AAC/B,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,KAAK,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE;AAChD,YAAM,KAAM,IAAI,IAAM,CAAC,IAAI;AAC3B,YAAM,KAAM,IAAI,KAAK,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,MAAO;AAC3C,YAAM,KAAK,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE;AAChD,YAAM,MAAO,IAAI,IAAM,IAAI,IAAM,IAAI;AACrC,YAAM,KAAM,KAAK,QAAS;AAC1B,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAK,IAAI,OAAQ;AACjB,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAK,KAAK,OAAQ;AAAA,IACpB;AACA,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AACtB,MAAE,CAAC,IAAK,EAAE,CAAC,IAAI,MAAO;AAAA,EACxB;AACA,QAAM,MAAM,IAAI,WAAW,EAAE;AAC7B,QAAM,UAAU,IAAI,SAAS,IAAI,MAAM;AACvC,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,SAAQ,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC;AACzD,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,UAAU,KAAK;AACxB;AAQA,IAAM,iBAAiB;AAEhB,SAAS,oBAA4B;AAC1C,MAAI,MAAM;AACV,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,IAAI,SAAS,IAAI;AACtB,WAAO,gBAAgB,KAAK;AAC5B,eAAW,QAAQ,OAAO;AAGxB,UAAI,QAAQ,OAAO,IAAI,WAAW,GAAI;AACtC,aAAO,eAAe,OAAO,EAAE;AAAA,IACjC;AAAA,EACF;AACA,SAAO;AACT;;;AJ1EA,IAAM,iBAAiB,oBAAI,IAAY;AA6BhC,SAAS,cAAc,SAG5B;AACA,QAAM,EAAE,UAAU,QAAQ,OAAO,IAAI,eAAe;AACpD,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA+B,IAAI;AAIjE,QAAM,UAAU,qBAAqB;AACrC,QAAM,iBAAa,sBAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,eAAW,sBAA+B,IAAI;AACpD,QAAM,iBAAa,sBAAO,OAAO;AACjC,aAAW,UAAU;AAKrB,QAAM,qBAAiB,sBAAO,KAAK;AAInC;AAAA,IACE,MAAM,MAAM;AACV,eAAS,SAAS,MAAM;AACxB,iBAAW,UAAU,IAAI;AAAA,IAC3B;AAAA,IACA,CAAC;AAAA,EACH;AAOA,+BAAU,MAAM;AACd,UAAM,KAAK,gBAAgB;AAC3B,QAAI,CAAC,MAAM,eAAe,IAAI,EAAE,EAAG;AACnC,UAAM,QAAQ,eAAe,EAAE;AAC/B,QAAI,CAAC,SAAS,MAAM,aAAa,SAAU;AAC3C,qBAAiB,EAAE;AACnB,mBAAe,IAAI,EAAE;AACrB,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,UAAU;AACnB,UAAM,YAAY;AAChB,YAAM,OAAO,WAAW;AACxB,UAAI;AACF,cAAM,OAAO,MAAM,kBAAkB,QAAQ,IAAI,QAAW,WAAW,QAAQ;AAAA,UAC7E,sBAAsB,KAAK,IAAI,IAAI;AAAA,QACrC,CAAC;AACD,YAAI,MAAM,SAAS,aAAa;AAC9B,eAAK,SAAS;AAAA,YACZ;AAAA,YACA,OAAO,MAAM;AAAA,YACb,WAAW,MAAM;AAAA,YACjB,eAAe,MAAM;AAAA,YACrB,OAAO,MAAM;AAAA,UACf,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,SAAS,MAAM,aAAa,QAAQ;AAAA,YACxC;AAAA,YACA,eAAe,MAAM;AAAA,YACrB,WAAW;AAAA,UACb,CAAC;AACD,gBAAM,SAAS,aAAa,OAAO,QAAQ;AAC3C,eAAK,eAAe;AAAA,YAClB,YAAY,OAAO;AAAA,YACnB;AAAA,YACA,WAAW;AAAA,YACX,KAAM,OAAO,OAAoB;AAAA,UACnC,CAAC;AAAA,QACH;AACA,uBAAe,EAAE;AAAA,MACnB,SAAS,GAAG;AACV,YAAI,aAAa,gBAAgB,EAAE,SAAS,aAAc;AAC1D,YAAI,aAAa,oBAAoB;AACnC,eAAK,kBAAkB,EAAE,MAAM;AAC/B;AAAA,QACF;AACA,YAAI,aAAa,gBAAgB;AAC/B,eAAK,UAAU,EAAE,OAAO,EAAE,OAAO,aAAa,EAAE,YAAY,CAAC;AAC7D;AAAA,QACF;AACA,aAAK,kBAAkB;AAAA,UACrB,MAAM;AAAA,UACN,aAAa,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF,GAAG;AAAA,EACL,GAAG,CAAC,UAAU,MAAM,CAAC;AAErB,QAAM,YAAQ,2BAAY,MAAM;AAC9B,UAAM,OAAO,WAAW;AACxB,UAAM,MAAM,YAAY;AACtB,eAAS,SAAS,MAAM;AACxB,YAAM,aAAa,IAAI,gBAAgB;AACvC,eAAS,UAAU;AAEnB,YAAM,OAAO,KAAK;AAClB,YAAM,WAAW,iBAAiB;AAClC,YAAM,QAAQ,cAAc;AAC5B,YAAM,QAAQ,cAAc;AAO5B,YAAM,UAAU,eAAe,KAAK,OAAO;AAC3C,YAAM,aAAa,YAAY;AAC/B,YAAM,SAAS,cAAc,KAAK,QAAQ,KAAK,OAAO,KAAK,UAAU;AAErE,UAAI;AACF,YAAI;AACJ,YAAI,WAAqB;AACzB,YAAI,WAA0B;AAE9B,YAAI,YAAY;AAWd,gBAAM,YAAY,kBAAkB;AAKpC,yBAAe;AAAA,YACb,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,OAAO,KAAK,SAAS;AAAA,YACrB,UAAU,KAAK;AAAA,YACf;AAAA,YACA,WAAW,KAAK,IAAI;AAAA,UACtB,CAAC;AACD,qBAAW;AACX,gBAAM,WAAW,mBAAmB,QAAQ;AAAA,YAC1C,WAAW;AAAA,YACX,OAAO,KAAK,SAAS;AAAA,YACrB;AAAA,YACA;AAAA,YACA,gBAAgB,kBAAkB,QAAQ;AAAA,YAC1C,cAAc,SAAS,cAAc,KAAK,eAAe;AAAA,YACzD,YAAY,MAAM,QAAQ,KAAK,UAAU,IAAI,KAAK,WAAW,KAAK,GAAG,IAAI,KAAK;AAAA,YAC9E,SAAS,KAAK;AAAA,YACd,QAAQ,KAAK;AAAA,YACb;AAAA,YACA,YAAY;AAAA,YACZ,QAAQ,OAAO,SAAS;AAAA,YACxB,WAAW,YAAY;AAAA,UACzB,CAAC;AACD,qBAAW;AACX,gBAAM,SAAS,MAAM;AACnB,2BAAe,UAAU;AACzB,uBAAW,MAAM;AACjB,uBAAW,IAAI;AAAA,UACjB;AACA,gBAAM,UAAU,EAAE,SAAS,UAAU,SAAS,MAAM,QAAQ,OAAO;AACnE,gBAAM,SAAwB;AAAA,YAC5B;AAAA,YACA,SAAS;AAAA,YACT,OAAO;AAAA,YACP,OAAO,EAAE,QAAQ,WAAW,GAAG,QAAQ;AAAA,YACvC,SAAS;AAAA,YACT;AAAA,UACF;AACA,qBAAW,MAAM;AACjB,eAAK,uBAAuB,OAAO,KAAK;AACxC,iBAAO,SAAS,OAAO,QAAQ;AAE/B,iBAAO,MAAM;AAAA,YACX;AAAA,YACA;AAAA,YACA,CAAC,MAAM;AACL,oBAAM,WAAW,EAAE,GAAG,GAAG,GAAG,QAAQ;AACpC,yBAAW,CAAC,MAAO,KAAK,EAAE,cAAc,YAAY,EAAE,GAAG,GAAG,OAAO,SAAS,IAAI,CAAE;AAClF,mBAAK,uBAAuB,QAAQ;AAAA,YACtC;AAAA,YACA,WAAW;AAAA,YACX,EAAE,sBAAsB,KAAK,IAAI,IAAI,KAAO;AAAA,UAC9C;AACA,cAAI,aAAa,SAAS,GAAG;AAG3B,kBAAM,IAAI,aAAa,WAAW,YAAY;AAAA,UAChD;AAAA,QACF,OAAO;AACP,gBAAM,UAAU,MAAM,aAAa,QAAQ;AAAA,YACzC,WAAW;AAAA,YACX,OAAO,KAAK,SAAS;AAAA,YACrB;AAAA,YACA;AAAA,YACA,gBAAgB,MAAM,cAAc,QAAQ;AAAA,YAC5C,cAAc,SAAS,cAAc,KAAK,eAAe;AAAA,YACzD,YAAY,MAAM,QAAQ,KAAK,UAAU,IACrC,KAAK,WAAW,KAAK,GAAG,IACxB,KAAK;AAAA,YACT,SAAS,KAAK;AAAA,YACd,QAAQ,KAAK;AAAA,YACb;AAAA,YACA,SAAS;AAAA,UACX,CAAC;AAED,cAAI,UAAU,SAAS;AAErB,mBAAO,QAAQ;AACf,uBAAW;AAAA,UACb,OAAO;AACL,uBAAW;AACX,kBAAM,mBAAmB,mBAAmB,OAAO;AAEnD,kBAAM,SAAS,MAAM;AACnB,6BAAe,UAAU;AACzB,yBAAW,MAAM;AACjB,yBAAW,IAAI;AACf,yBAAW,UAAU,IAAI;AAAA,YAC3B;AAOA,kBAAM,UAAU;AAAA,cACd,SAAS,QAAQ;AAAA,cACjB,SAAS;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAIA,gBAAI,QAAQ,GAAG,MAAM,SAAS,mBAAmB,QAAQ,UAAU,CAAC;AACpE,kBAAM,SAAwB;AAAA,cAC5B,WAAW,QAAQ;AAAA,cACnB,SAAS,QAAQ;AAAA,cACjB;AAAA,cACA,OAAO,EAAE,QAAQ,WAAW,WAAW,QAAQ,YAAY,OAAO,GAAG,QAAQ;AAAA,cAC7E,SAAS;AAAA,cACT;AAAA,YACF;AACA,uBAAW,MAAM;AACjB,gBAAI,CAAC,YAAY;AACf,yBAAW,UAAU,EAAE,OAAO,OAAO,OAAO,OAAO,QAAQ,OAAO,CAAC;AAAA,YACrE;AAGA,iBAAK,uBAAuB,OAAO,KAAK;AAExC,gBAAI,YAAY;AAMd,qBAAO,SAAS,OAAO,QAAQ,QAAQ;AAAA,YACzC;AAEA,mBAAO,MAAM;AAAA,cACX;AAAA,cACA,QAAQ;AAAA,cACR,CAAC,MAAM;AAGL,oBAAI,EAAE,MAAO,SAAQ,EAAE;AACvB,sBAAM,WAAW,EAAE,GAAG,GAAG,GAAG,SAAS,MAAM;AAC3C;AAAA,kBAAW,CAAC,MACV,KAAK,EAAE,cAAc,QAAQ,aAAa,EAAE,GAAG,GAAG,OAAO,OAAO,SAAS,IAAI;AAAA,gBAC/E;AACA,oBAAI,CAAC,YAAY;AACf,6BAAW,UAAU,EAAE,OAAO,UAAU,OAAO,QAAQ,OAAO,CAAC;AAAA,gBACjE;AACA,qBAAK,uBAAuB,QAAQ;AAAA,cACtC;AAAA,cACA,WAAW;AAAA,cACX,EAAE,iBAAiB;AAAA,YACrB;AAAA,UACF;AAAA,QAEA;AAEA,mBAAW,IAAI;AACf,mBAAW,UAAU,IAAI;AACzB,YAAI,SAAU,gBAAe,QAAQ;AAErC,YAAI,SAAS,aAAa;AACxB,eAAK,SAAS;AAAA,YACZ;AAAA,YACA,OAAO,KAAK,SAAS;AAAA,YACrB,WAAW,KAAK;AAAA,YAChB,eAAe;AAAA,YACf;AAAA,UACF,CAAC;AACD;AAAA,QACF;AAEA,cAAM,SAAS,MAAM,aAAa,QAAQ;AAAA,UACxC;AAAA,UACA,eAAe;AAAA,UACf,WAAW;AAAA,QACb,CAAC;AACD,cAAM,SAAS,aAAa,OAAO,QAAQ;AAC3C,cAAM,WAAqC;AAAA,UACzC,YAAY,OAAO;AAAA,UACnB;AAAA,UACA,WAAW;AAAA,UACX,KAAM,OAAO,OAAoB;AAAA,QACnC;AACA,aAAK,eAAe,QAAQ;AAAA,MAC9B,SAAS,GAAG;AACV,mBAAW,IAAI;AACf,mBAAW,UAAU,IAAI;AACzB,YAAI,aAAa,gBAAgB,EAAE,SAAS,cAAc;AACxD,cAAI,eAAe,SAAS;AAC1B,2BAAe,UAAU;AACzB,iBAAK,kBAAkB;AAAA,cACrB,MAAM;AAAA,cACN,aAAa;AAAA,YACf,CAAC;AAAA,UACH;AACA;AAAA,QACF;AACA,YAAI,aAAa,oBAAoB;AACnC,eAAK,kBAAkB,EAAE,MAAM;AAC/B;AAAA,QACF;AACA,YAAI,aAAa,gBAAgB;AAC/B,eAAK,UAAU,EAAE,OAAO,EAAE,OAAO,aAAa,EAAE,YAAY,CAAC;AAC7D;AAAA,QACF;AACA,aAAK,kBAAkB;AAAA,UACrB,MAAM;AAAA,UACN,aAAa,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF;AACA,SAAK,IAAI;AAAA,EACX,GAAG,CAAC,UAAU,QAAQ,MAAM,CAAC;AAE7B,SAAO,EAAE,OAAO,WAAW,EAAE,QAAQ,EAAE;AACzC;AAMO,SAAS,eACd,SAMY;AACZ,MAAI,QAAQ,YAAY,YAAY;AAKlC,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,OAAO,QAAQ,QAAQ;AAC7B,SAAO,cAAc;AAAA,IACnB,GAAG;AAAA,IACH;AAAA,IACA,cACE,SAAS,mBACJ,QAAQ,YACT;AAAA,IACN,QACE,SAAS,cACJ,QAAQ,YACT;AAAA,EACR,CAAC,EAAE;AACL;;;AK9dA,IAAAC,gBAAuF;AA4EnF,IAAAC,sBAAA;AApDJ,IAAM,OAAO;AAKb,IAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,MAAM,KAAK,CAAC;AACzD,IAAM,OAAO,CAAC,GAAG,GAAG,CAAC;AAEd,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,+BAAU,MAAM;AACd,iBAAa;AAAA,EACf,GAAG,CAAC,CAAC;AAGL,QAAM,cAAU,sBAAwB,IAAI;AAC5C,QAAM,iBAAa,sBAAuB,IAAI;AAC9C,qCAAgB,MAAM;AACpB,UAAM,OAAO,QAAQ;AACrB,UAAM,OAAO,WAAW;AACxB,QAAI,CAAC,QAAQ,CAAC,KAAM;AACpB,UAAM,UAAU,MAAM;AACpB,YAAM,SAAS,KAAK,eAAe;AACnC,UAAI,SAAS,EAAG,MAAK,MAAM,YAAY,kBAAkB,GAAG,MAAM,IAAI;AAAA,IACxE;AACA,YAAQ;AACR,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,KAAK,SAAS;AAGpB,QAAM,QAAQ,CAAC,KAAa,MAAc,KAAa,OAAe,QACpE;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,WAAW,GAAG,IAAI;AAAA,MAClB;AAAA,MACA,IAAI;AAAA,MACJ,OACE;AAAA,QACE,iBAAiB,uBAAuB,GAAG,yBAAyB,IAAI,GAAG;AAAA,QAC3E,WAAW,uBAAuB,EAAE,OAAO,IAAI;AAAA,QAC/C,SAAS;AAAA,MACX;AAAA;AAAA,IAVG;AAAA,EAYP;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,YAAY,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,SAAS,KAAK,GAAG,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC;AAAA,MAC/F,cAAY;AAAA,MACZ,aAAW;AAAA,MACX,OAAO,QAAQ,EAAE,SAAS,QAAQ,GAAG,MAAM,IAAI;AAAA,MAE9C;AAAA;AAAA,QACD,8CAAC,SAAI,WAAW,GAAG,UAAU,GAAG,eAAY,QACzC;AAAA,eAAK;AAAA,YAAI,CAAC,GAAG,MACZ,MAAM,IAAI,CAAC,IAAI,aAAc,OAAO,IAAK,KAAK,QAAQ,wCAAwC,CAAC,KAAK,MAAM,IAAI,aAAa,MAAS;AAAA,UACtI;AAAA,UACC,MAAM,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,IAAI,KAAK,IAAI,cAAc,aAAc,OAAO,IAAK,MAAM,QAAQ,OAAO,IAAI,CAAC,CAAC,CAAC;AAAA,WAChH;AAAA;AAAA;AAAA,EACF;AAEJ;;;AN0BQ,IAAAC,sBAAA;AAvGR,IAAM,QAAsE;AAAA,EAC1E,eAAe;AAAA,EACf,aAAa;AAAA,EACb,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,aAAa;AACf;AAKA,IAAM,QAAQ;AAAA,EACZ,OAAO,EAAE,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,QAAQ,GAAG;AAAA,EACtE,QAAQ,EAAE,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,QAAQ,GAAG;AAAA,EACvE,OAAO,EAAE,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,GAAG,QAAQ,EAAE;AACtE;AAEO,SAAS,YAAY,OAAyB;AACnD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,EAAE,OAAO,IAAI,eAAe;AAClC,QAAM,QAAQ,MAAM,IAAI,KAAK,QAAQ,MAAM,EAAE;AAQ7C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,KAAK;AAEtC,QAAM,EAAE,MAAM,IAAI,cAAc;AAAA,IAC9B,GAAG;AAAA,IACH;AAAA,IACA,cACE,SAAS,mBACL,CAAC,MAAgC;AAC/B,cAAQ,KAAK;AACb,MAAC,UAAoD,CAAC;AAAA,IACxD,IACA;AAAA,IACN,QACE,SAAS,cACL,CAAC,MAA0B;AACzB,cAAQ,KAAK;AACb,MAAC,UAAyD,CAAC;AAAA,IAC7D,IACA;AAAA,IACN,SAAS,CAAC,MAAM;AACd,cAAQ,KAAK;AACb,gBAAU,EAAE,MAAM,WAAW,aAAa,EAAE,eAAe,EAAE,MAAM,CAAC;AAAA,IACtE;AAAA,IACA,iBAAiB,CAAC,MAAqB;AACrC,cAAQ,KAAK;AACb,gBAAU,CAAC;AAAA,IACb;AAAA,EACF,CAAC;AAED,QAAM,IAAI,MAAM,IAAI;AACpB,QAAM,SAAS,UAAU,SAAS,EAAE,SAAS,IAAI,UAAU,WAAW,IAAI,EAAE;AAG5E,QAAM,YAAY,UAAU;AAC5B,QAAM,YAAuB;AAAA,IAC3B,OAAO;AAAA,MACL,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB,mBAAmB,WAAW,WAAW;AAAA,MACzD,KAAK,EAAE;AAAA,MACP,QAAQ,EAAE;AAAA,MACV,SAAS,KAAK,EAAE,GAAG;AAAA,MACnB;AAAA,MACA,UAAU,EAAE;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,GAAI,UAAU,YACV,EAAE,YAAY,WAAW,OAAO,WAAW,QAAQ,oBAAoB,IACvE,UAAU,iBACR,EAAE,YAAY,QAAQ,OAAO,QAAQ,QAAQ,iBAAiB,IAC9D,EAAE,YAAY,WAAW,OAAO,QAAQ,QAAQ,oBAAoB;AAAA,IAC5E;AAAA,IACA,CAAC,gBAAgB,GAAG,QAAQ,OAAO,KAAK;AAAA,EAC1C;AAEA,SACE,6CAAC,SAAK,GAAG,gBACP,uDAAC,kBAAe,MAAY,QAAgB,OAAO,UAAU,YAAY,SAAS,SAChF;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,OAAO,OAAO,EAAE,GAAG,OAAO,QAAQ,WAAW,IAAI;AAAA,MACjD,UAAU;AAAA,MACV,aAAW;AAAA,MACX,SAAS,MAAM;AACb,YAAI,KAAM;AACV,yBAAiB;AACjB,gBAAQ,IAAI;AACZ,cAAM;AAAA,MACR;AAAA,MAEA;AAAA,qDAAC,cAAW,MAAM,EAAE,MAAM,OAAO,WAAW;AAAA,QAC3C,SAAS,cAAc;AAAA;AAAA;AAAA,EAC1B,GACF,GACF;AAEJ;;;AOxJA,IAAAC,gBAAkC;AAe3B,SAAS,mBAAmB,SAA0C;AAC3E,QAAM,EAAE,MAAM,IAAI,cAAc;AAAA,IAC9B,MAAM;AAAA,IACN,OAAO,QAAQ;AAAA,IACf,QAAQ;AAAA,IACR,cAAc,QAAQ;AAAA,IACtB,SAAS,CAAC,MAAM;AAGd,YAAM,QAAQ,CAAC,kBAAkB,oBAAoB,sBAAsB;AAC3E,UAAI,MAAM,SAAS,EAAE,KAAK,GAAG;AAC3B,gBAAQ,gBAAgB;AAAA,MAC1B,OAAO;AACL,gBAAQ,UAAU,EAAE,MAAM,WAAW,aAAa,EAAE,eAAe,EAAE,MAAM,CAAC;AAAA,MAC9E;AAAA,IACF;AAAA,IACA,iBAAiB,CAAC,MAAM,QAAQ,UAAU,CAAC;AAAA,EAC7C,CAAC;AAED,QAAM,YAAQ,sBAAO,KAAK;AAC1B,+BAAU,MAAM;AACd,QAAI,QAAQ,YAAY,MAAM,QAAS;AACvC,UAAM,UAAU;AAChB,UAAM;AAAA,EACR,GAAG,CAAC,QAAQ,UAAU,KAAK,CAAC;AAC9B;;;AC5BO,SAAS,eAAqB;AAErC;;;ACXO,SAAS,0BACd,UACA,eACG,YACM;AACT,QAAM,UAAU,IAAI,KAAK,SAAS,SAAS,IAAI,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC;AAC3E,SAAO,CAAC,YAAY,GAAG,UAAU,EAAE,MAAM,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC;AAChE;AAEO,SAAS,yBACd,UACA,eACG,YACM;AACT,QAAM,UAAU,IAAI,KAAK,SAAS,SAAS,IAAI,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC;AAC3E,SAAO,CAAC,YAAY,GAAG,UAAU,EAAE,KAAK,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC;AAC/D;","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_react","PREFIX","import_react","import_jsx_runtime","import_jsx_runtime","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.tsx","../src/wire.ts","../src/PairingModal.tsx","../src/mark.tsx","../src/lockup.tsx","../src/i18n.ts","../src/intent.ts","../src/styles.ts","../src/ZorealLogin.tsx","../src/useZorealLogin.ts","../src/busy.ts","../src/return.ts","../src/jwt.ts","../src/pairing.ts","../src/pkce.ts","../src/ring.tsx","../src/useZorealAutoLogin.ts","../src/logout.ts","../src/scopes.ts"],"sourcesContent":["export { ZorealOAuthProvider, useZorealOAuth } from './context';\nexport type { ZorealOAuthProviderProps, ZorealOAuthContextProps } from './context';\nexport { ZorealLogin } from './ZorealLogin';\nexport { ZorealBusyRing } from './ring';\n// The mark, for a site that renders its own button in the house shape.\nexport { ZorealMark } from './mark';\n// Exported so an integrator on `pairingUI: 'none'` can still mount the real\n// dialog (driven by their own `onPairingStateChange`) rather than rebuild it.\nexport { PairingModal, DEFAULT_PAIRING_TIMEOUT_MS } from './PairingModal';\nexport type { PairingModalProps } from './PairingModal';\nexport { useZorealLogin } from './useZorealLogin';\nexport { useZorealAutoLogin } from './useZorealAutoLogin';\nexport { zorealLogout } from './logout';\nexport { hasGrantedAllScopesZoreal, hasGrantedAnyScopeZoreal } from './scopes';\nexport { resolveIntent } from './intent';\nexport type {\n AcrValue,\n LoginIntent,\n PairingUI,\n ZorealTheme,\n AuthCodeFlowOptions,\n BrowserDirectFlowOptions,\n ErrorCode,\n NonOAuthError,\n PairingState,\n SelectBy,\n UseZorealAutoLoginOptions,\n ZorealButtonConfiguration,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginProps,\n ZorealLoginRequestOptions,\n} from './types';\n","import { createContext, useContext, useMemo, useState, type ReactNode } from 'react';\nimport { DEFAULT_ISSUER } from './wire';\nimport { PairingModal } from './PairingModal';\nimport type { LoginIntent, PairingState, PairingUI, ZorealTheme } from './types';\n\nexport interface ZorealOAuthProviderProps {\n /** From the ZOREAL dashboard: the asset ID. */\n clientId: string;\n /** Override the provider origin. Sandbox and self-hosted testing only. */\n issuer?: string;\n /**\n * BCP 47. Drives button text, the pairing page, AND the pairing modal's own\n * copy — pass the language your app is currently showing, so the browser and\n * the phone say the same thing.\n */\n locale?: string;\n /** Colour scheme for the pairing modal. Defaults to following the OS. */\n theme?: ZorealTheme;\n /**\n * Who renders the QR. Defaults to 'modal': the SDK draws it. Set 'none' only\n * if you are rendering your own from `onPairingStateChange`.\n */\n pairingUI?: PairingUI;\n /**\n * How long the modal stays open before giving up and cancelling, in ms.\n * Defaults to 120000. The provider's own expiry wins when it is shorter.\n */\n pairingTimeoutMs?: number;\n children: ReactNode;\n}\n\nexport interface ZorealOAuthContextProps {\n clientId: string;\n issuer: string;\n locale?: string;\n}\n\n/** What the flow hands the provider so it can draw the pairing. */\nexport interface HostedPairing {\n state: PairingState;\n qrUrl: string;\n intent: LoginIntent;\n cancel: () => void;\n}\n\nconst ZorealOAuthContext = createContext<ZorealOAuthContextProps | null>(null);\n\n/**\n * Internal channel from the flow to the provider.\n *\n * The modal has to be rendered by the provider rather than by the hook, because\n * `useZorealLogin` returns a function, not an element: there is nowhere for a\n * hook to put a dialog. Publishing up to the provider is what lets an\n * integrator get the whole pairing UI without writing (or importing) anything.\n *\n * Null when `pairingUI` is 'none', which is also how the flow knows to stay out\n * of the way and let the caller render.\n */\nconst PairingHostContext = createContext<((pairing: HostedPairing | null) => void) | null>(null);\n\nexport function useZorealPairingHost() {\n return useContext(PairingHostContext);\n}\n\nexport function ZorealOAuthProvider({\n clientId,\n issuer = DEFAULT_ISSUER,\n locale,\n theme = 'auto',\n pairingUI = 'modal',\n pairingTimeoutMs,\n children,\n}: ZorealOAuthProviderProps) {\n const [pairing, setPairing] = useState<HostedPairing | null>(null);\n\n const value = useMemo(\n () => ({ clientId, issuer: issuer.replace(/\\/$/, ''), locale }),\n [clientId, issuer, locale]\n );\n\n const host = pairingUI === 'modal' ? setPairing : null;\n\n return (\n <ZorealOAuthContext.Provider value={value}>\n <PairingHostContext.Provider value={host}>\n {children}\n {pairing && (\n <PairingModal\n state={pairing.state}\n qrUrl={pairing.qrUrl}\n intent={pairing.intent}\n onCancel={pairing.cancel}\n locale={locale}\n theme={theme}\n timeoutMs={pairingTimeoutMs}\n />\n )}\n </PairingHostContext.Provider>\n </ZorealOAuthContext.Provider>\n );\n}\n\nexport function useZorealOAuth(): ZorealOAuthContextProps {\n const ctx = useContext(ZorealOAuthContext);\n if (!ctx) {\n throw new Error(\n 'useZorealOAuth must be used inside <ZorealOAuthProvider clientId=...>. ' +\n 'Wrap your app (or the part that logs in) in the provider.'\n );\n }\n return ctx;\n}\n","/**\n * The wire protocol between this package and the ZOREAL OpenID Provider.\n *\n * VERSIONED: a shipped version keeps working until the provider explicitly\n * refuses it, and when it does, the reason is surfaced verbatim. Both the wire\n * version and the package version travel on every pairing request so a refusal\n * can be precise.\n *\n * Endpoints, all relative to the issuer and all CORS-gated on the client's\n * authorized JavaScript origins (the dashboard):\n *\n * POST /pair start a pairing request. Body carries the\n * authorize parameters, the PKCE challenge and\n * `display`: \"qr\" or \"link\", the surface this\n * package is about to show, decided before the\n * request. Returns { request_id, pair_url,\n * expires_in, display, qr_refresh_seconds } or,\n * for prompt=none with a live consented\n * session, { code } immediately. The provider\n * binds the pairing to the display it echoes\n * back. A \"link\" pairing's pair_url carries a\n * start token (`?t=<start_token>`) that only\n * the browser it was handed to can claim with,\n * and the provider renders no QR for it, so\n * nobody can turn a same-device link into a\n * code that gets scanned elsewhere. A request\n * with no `display` gets the older static\n * behaviour (\"legacy\").\n * GET /pair/start the same-device sign-in as a NAVIGATION:\n * the /pair parameters as a query, plus\n * request_id (the page's own token) and\n * origin; answered with a redirect to the\n * pairing's universal link, inside the tap\n * GET /pair/:id/status poll: pending | claimed |\n * approved (with code) | denied | expired |\n * enrolling. Over-polling cancels the request\n * rather than throttling it, so the cadence\n * below is not a suggestion.\n * GET /pair/:id/qr.svg the QR image, rendered by the provider so\n * the pairing surface stays changeable at\n * runtime and this package keeps zero\n * dependencies: it generates nothing. For a\n * \"qr\" pairing the image is the CURRENT FRAME,\n * served `Cache-Control: no-store`: a QR of\n * `<pair_url>?f=<time>.<hmac>`, where `time`\n * is whole seconds since the pairing was\n * created on the provider's clock and `hmac`\n * is a truncated HMAC-SHA-256 of that time\n * under a per-pairing secret that never leaves\n * the provider. The frame moves every\n * qr_refresh_seconds and the provider refuses a\n * stale one at claim, so a screenshot of the\n * code is dead on arrival and only a live view\n * of the screen can be relayed. This package\n * re-fetches the image on that cadence with a\n * cache-busting `?t=<Date.now()>` and swaps it\n * in once loaded. 404 once the pairing has left\n * pending, and always for a \"link\" pairing.\n * POST /token the code exchange. Browser-direct mode uses\n * it directly with PKCE and no client secret;\n * auth-code mode leaves it to the RP backend.\n */\n\nexport const WIRE_VERSION = 1;\nexport const SDK_VERSION = '0.2.21';\nexport const DEFAULT_ISSUER = 'https://id.zoreal.com';\n\n/** Pending TTL is short. Poll gently; over-polling cancels the request. */\nexport const POLL_INTERVAL_MS = 2000;\n/** Enrolling extends the window well beyond a normal login; poll slower. */\nexport const POLL_INTERVAL_ENROLLING_MS = 5000;\n/**\n * How often the QR frame is re-fetched when the provider did not say. The\n * provider's `qr_refresh_seconds` wins whenever it is present; this is the\n * floor for an older provider that predates animated frames, where refreshing\n * a static code is merely redundant.\n */\nexport const DEFAULT_QR_REFRESH_SECONDS = 3;\n\nexport type PairDisplay = 'qr' | 'link';\n\nexport interface PairCreated {\n request_id: string;\n /**\n * The pairing page: https://zoreal.com/login/<request_id>. The same URL is\n * what the QR encodes and what the app link opens. For a \"link\" pairing it\n * also carries `?t=<start_token>`, and this package navigates to it verbatim.\n */\n pair_url: string;\n expires_in: number;\n /**\n * The surface the provider bound the pairing to, echoed from the request.\n * \"legacy\" is a provider that got no `display` and kept the static QR.\n * Absent from a provider that predates the field.\n */\n display?: PairDisplay | 'legacy';\n /**\n * QR pairings only: how often, in seconds, to re-fetch the QR image so the\n * code on screen is the provider's current frame. DEFAULT_QR_REFRESH_SECONDS\n * applies when absent.\n */\n qr_refresh_seconds?: number;\n}\n\nexport interface PairImmediate {\n /** prompt=none resolved silently: consented sector, live session. */\n code: string;\n}\n\nexport type PairStartResponse = PairCreated | PairImmediate;\n\nexport interface PairStatusResponse {\n status: 'pending' | 'claimed' | 'approved' | 'denied' | 'expired' | 'cancelled' | 'enrolling';\n code?: string;\n expires_in?: number;\n enrolment_deadline?: number;\n /**\n * The provider's reason on denial or refusal. Surfaced verbatim, never\n * rewritten. A pairing the provider denied because the approving phone was\n * in a different country from the browser arrives here as `access_denied`\n * with the provider's own sentence in error_description.\n */\n error?: string;\n error_description?: string;\n}\n\nexport interface TokenResponse {\n id_token: string;\n access_token?: string;\n token_type?: string;\n expires_in?: number;\n scope?: string;\n error?: string;\n error_description?: string;\n}\n","import { useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ZorealLockup } from './lockup';\nimport { interpolate, isRtl, strings } from './i18n';\nimport { titleFor } from './intent';\nimport { cx, ensureStyles } from './styles';\nimport type { LoginIntent, PairingState, ZorealTheme } from './types';\n\n/**\n * The pairing modal, rendered by the SDK rather than by every integrator.\n *\n * It is a modal and not an inline card because the handshake is blocking,\n * time-boxed and happens on a second device: there is nothing useful to do on\n * the page until it resolves, and an inline panel below a button competes with\n * the rest of a sign-in form for the person's attention at the one moment they\n * need to look at their phone.\n */\n\n/** Our own cap on how long a pairing sits on screen. See `pairingTimeoutMs`. */\nexport const DEFAULT_PAIRING_TIMEOUT_MS = 120_000;\n\n/** Below this the countdown changes colour: it stops being background\n * information and starts being a prompt to hurry. */\nconst URGENT_SECONDS = 20;\n\nfunction mmss(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\n/* Icons are inlined rather than pulled from an icon package: this renders on\n someone else's sign-in page, and a UI dependency here would be inherited by\n every host app that installs the SDK. */\nconst IconClose = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden focusable=\"false\">\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nconst IconPhone = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden focusable=\"false\">\n <rect x=\"6\" y=\"2\" width=\"12\" height=\"20\" rx=\"2.5\" />\n <path d=\"M11 18.5h2\" />\n </svg>\n);\n\nconst IconShield = () => (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden focusable=\"false\">\n <path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n);\n\nexport interface PairingModalProps {\n state: PairingState;\n qrUrl: string;\n onCancel: () => void;\n locale?: string;\n theme?: ZorealTheme;\n timeoutMs?: number;\n /** Which title the dialog opens with. Defaults to the sign-in wording. */\n intent?: LoginIntent;\n}\n\nexport function PairingModal({\n state,\n qrUrl,\n onCancel,\n locale,\n theme = 'auto',\n timeoutMs = DEFAULT_PAIRING_TIMEOUT_MS,\n intent = 'sign-in',\n}: PairingModalProps) {\n const t = strings(locale);\n const titleId = useId();\n const closeRef = useRef<HTMLButtonElement>(null);\n\n // A deadline, not a decremented counter. Background tabs throttle timers, so\n // a counter that subtracts one per tick drifts and comes back lying about how\n // much time is left; reading the clock each tick self-corrects.\n const deadlineRef = useRef(0);\n const [remaining, setRemaining] = useState(Math.round(timeoutMs / 1000));\n\n // `claimed` = the request is now waiting in the holder's app; `enrolling` =\n // a first-time holder finishing ZOREAL ID setup. In both the QR has done its\n // job and the action has moved to the phone.\n const settled = state.status === 'claimed' || state.status === 'enrolling';\n\n // The code on screen is a frame of a rotating sequence, so `qrUrl` arrives\n // again every few seconds with a different value. Swapping an <img> src\n // straight over blanks the well while the new image downloads, which on a\n // three second cadence is a QR that flickers the whole time someone is\n // trying to aim a camera at it. So the next frame is fetched into an\n // off-document Image first and only becomes the visible src once it has\n // decoded. A frame that fails to load is dropped without touching what is\n // showing: the old code is still valid for a few more seconds, and the next\n // refresh is another attempt.\n const [frameUrl, setFrameUrl] = useState(qrUrl);\n useEffect(() => {\n // Once the phone has claimed the code the sequence is over and the well\n // keeps the spent frame under its overlay.\n if (settled || frameUrl === qrUrl) return;\n let abandoned = false;\n const next = new Image();\n next.onload = () => {\n if (!abandoned) setFrameUrl(qrUrl);\n };\n next.onerror = () => {\n /* keep the frame that is showing; the next refresh retries */\n };\n next.src = qrUrl;\n return () => {\n abandoned = true;\n next.onload = null;\n next.onerror = null;\n };\n }, [qrUrl, settled, frameUrl]);\n\n const onCancelRef = useRef(onCancel);\n onCancelRef.current = onCancel;\n\n useEffect(() => {\n ensureStyles();\n }, []);\n\n useEffect(() => {\n // Never claim more time than the provider will actually honour: if the\n // server's own window is shorter than our cap, the server wins.\n const serverMs = typeof state.expiresIn === 'number' ? state.expiresIn * 1000 : Infinity;\n deadlineRef.current = Date.now() + Math.min(timeoutMs, serverMs);\n setRemaining(Math.round(Math.min(timeoutMs, serverMs) / 1000));\n\n const id = setInterval(() => {\n const left = Math.max(0, Math.ceil((deadlineRef.current - Date.now()) / 1000));\n setRemaining(left);\n if (left === 0) {\n // Stop the tick before cancelling. Unmount clears it anyway, but only\n // after this render commits, and a timer that keeps firing `cancel`\n // once a second in between is a race waiting to be inherited.\n clearInterval(id);\n onCancelRef.current();\n }\n }, 1000);\n return () => clearInterval(id);\n // Deliberately keyed on the FIRST expiresIn only: re-running on every poll\n // would restart the countdown each tick and it would never reach zero.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [timeoutMs]);\n\n // Escape closes, page scroll locks, and focus moves into the dialog, so the\n // panel is reachable and dismissable without a mouse.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onCancelRef.current();\n };\n document.addEventListener('keydown', onKey);\n const previous = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n closeRef.current?.focus();\n return () => {\n document.removeEventListener('keydown', onKey);\n document.body.style.overflow = previous;\n };\n }, []);\n\n if (typeof document === 'undefined') return null;\n\n const body =\n state.status === 'enrolling'\n ? t.bodyEnrolling\n : settled\n ? t.bodyApprove\n : t.bodyScan;\n\n return createPortal(\n <div className={`${cx('root')} ${cx('scrim')}`} data-theme={theme} onClick={onCancel}>\n <div\n className={cx('card')}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n dir={isRtl(locale) ? 'rtl' : 'ltr'}\n onClick={(e) => e.stopPropagation()}\n >\n <button ref={closeRef} type=\"button\" className={cx('close')} aria-label={t.close} onClick={onCancel}>\n <IconClose />\n </button>\n\n <div className={cx('body')}>\n <ZorealLockup height={44} className={cx('lockup')} />\n\n <h2 id={titleId} className={cx('title')}>\n {settled ? t.titleApprove : titleFor(t, intent)}\n </h2>\n <p className={cx('body-text')}>{body}</p>\n\n {/* The light on the well's edge is a set of masked overlays inside\n the well, drawn first so the spent badge, a later sibling, stays\n above them. The well carries the spent flag for them: a\n stylesheet cannot look back from the image to a sibling before\n it. */}\n <div className={cx('qr-well')} data-spent={settled}>\n <span className={cx('qr-beam-glow')} aria-hidden>\n <span className={cx('qr-beam-glow-band')} />\n </span>\n <span className={cx('qr-beam')} aria-hidden />\n <img className={cx('qr')} data-spent={settled} src={frameUrl} alt={t.qrAlt} width={180} height={180} />\n {settled && (\n <span className={cx('qr-overlay')}>\n <span className={cx('qr-badge')}>\n <IconPhone />\n </span>\n </span>\n )}\n </div>\n\n <div className={cx('status')}>\n <span className={cx('dot')}>\n <i />\n <i />\n </span>\n {settled ? t.waitingApproval : t.waiting}\n </div>\n <p className={cx('timer')} data-urgent={remaining <= URGENT_SECONDS}>\n {interpolate(t.expiresIn, mmss(remaining))}\n </p>\n </div>\n\n {/* The QR is on screen because this person is being asked to use a\n phone app, and some of them do not have it yet. Without this the\n panel reads as \"scan this with something I do not have\", and the\n flow dead-ends at the one moment it can still be recovered: the\n same code is also the app's download link. */}\n <div className={cx('help')}>\n <p className={cx('help-title')}>{t.noIdTitle}</p>\n <p className={cx('help-body')}>{t.noIdBody}</p>\n </div>\n\n <div className={cx('footer')}>\n <button type=\"button\" className={cx('cancel')} onClick={onCancel}>\n {t.cancel}\n </button>\n <a className={cx('secured')} href=\"https://zoreal.com\" target=\"_blank\" rel=\"noopener\">\n <IconShield />\n {t.secured}\n </a>\n </div>\n </div>\n </div>,\n document.body\n );\n}\n","/**\n * The ZOREAL mark. Geometry is the tight crop made for small sizes.\n *\n * Two colour modes, because the mark has two jobs. On the button it is\n * `currentColor`, so it inherits whatever the host theme puts on the label. In\n * the pairing modal it is the brand blue, because there it identifies WHOSE\n * request the person is being asked to approve, and an identity check is\n * exactly the wrong place for a mark that changes colour with the page.\n */\nexport const ZOREAL_BLUE = '#00b4d9';\n\nexport function ZorealMark({\n size = 18,\n brand = false,\n className,\n}: {\n size?: number;\n /** Paint the brand blue instead of inheriting currentColor. */\n brand?: boolean;\n className?: string;\n}) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"8.4 7.4 62.2 62.2\"\n fill={brand ? ZOREAL_BLUE : 'currentColor'}\n fillRule=\"evenodd\"\n aria-hidden\n focusable=\"false\"\n className={className}\n >\n <path d=\"M56.1,32.9c.6-3.1-.8-6.4-3.7-8.1l-18-10.4,5.2-3,15.4,8.9c5.4,3.1,7.7,9.6,5.8,15.3-.3.8-.6,1.6-1.1,2.4-.4.7-.9,1.4-1.5,2.1-1.3,1.4-2.9,2.6-4.6,3.3-3.6,1.5-7.9,1.4-11.6-.7l-8.9-5.1c-2.9-1.7-6.5-1.3-8.9.8-.6.6-1.2,1.2-1.7,2-.5.8-.8,1.7-.9,2.5-.6,3.1.9,6.4,3.7,8.1l18,10.4-5.2,3-15.4-8.9c-5.4-3.1-7.7-9.6-5.8-15.3.2-.8.6-1.6,1-2.4.5-.7,1-1.4,1.5-2.1,1.3-1.4,2.9-2.6,4.7-3.3,3.6-1.6,7.8-1.4,11.5.6l8.9,5.2c3,1.7,6.6,1.3,8.9-.8.6-.6,1.2-1.2,1.7-2,.4-.8.7-1.7.9-2.5Z\" />\n <path d=\"M68.7,44.2c-.7,1.2-2.3,1.7-3.5.9-1.3-.7-1.7-2.3-1-3.5.7-1.3,2.3-1.7,3.5-1,1.3.7,1.7,2.3,1,3.6Z\" />\n <path d=\"M25.6,21.3c5.1-.8,10.4,0,15.3,2.9l1.2.7h0c1.2.7,1.6,2.3.9,3.5s-2.3,1.7-3.5.9l-1.2-.7c-4.2-2.4-9.1-3-13.5-1.9-1.6.4-3.1,1.1-4.5,1.9h0c-1.2.7-2.8.3-3.5-1-.7-1.2-.3-2.8.9-3.5,0,0,.1,0,.3-.1.3-.1.6-.4,1-.5,2.1-1.1,4.4-1.7,6.7-2.2Z\" />\n <path d=\"M9.6,31.8c.7-1.2,2.4-1.6,3.5-.8,1.2.7,1.6,2.4.8,3.5-.8,1.2-2.4,1.6-3.6.8-1.2-.8-1.5-2.4-.7-3.5Z\" />\n <path d=\"M46.2,30.3c.7-1.3,2.3-1.7,3.5-1,1.2.7,1.7,2.3.9,3.6-.7,1.2-2.3,1.7-3.5.9s-1.7-2.3-.9-3.5Z\" />\n <path d=\"M52.1,54.5c-5,.9-10.4,0-15.3-2.8l-1.2-.7h0c-1.2-.7-1.7-2.3-.9-3.5s2.3-1.7,3.5-.9l1.2.7c4.3,2.4,9.1,3,13.6,1.9,1.6-.4,3.1-1.1,4.5-1.9h0c1.2-.7,2.8-.3,3.5.9.7,1.3.3,2.9-.9,3.6-.1,0-.2,0-.3,0-.4.2-.7.4-1.1.6-2.1,1-4.3,1.7-6.7,2.1Z\" />\n <path d=\"M31.4,45.6c-.7,1.2-2.3,1.7-3.5.9s-1.7-2.3-.9-3.5,2.3-1.7,3.5-.9,1.7,2.3.9,3.5Z\" />\n </svg>\n );\n}\n","import { ZOREAL_BLUE } from './mark';\n\n/**\n * The full ZOREAL lockup (mark + wordmark), from zoreal-web's\n * `images/logo/zoreal-lockup.svg`, inlined so the modal ships no external asset\n * and renders identically offline and behind a strict CSP.\n *\n * One deliberate change from the source file: the wordmark, `#0e104f` in the\n * master, is `currentColor` here. The lockup sits on the host's page in either\n * theme, and a fixed near-black wordmark disappears against a dark card. The\n * mark keeps the brand blue in both themes — it reads on either ground, and it\n * is the part that identifies whose sign-in this is.\n */\nexport function ZorealLockup({ height = 22, className }: { height?: number; className?: string }) {\n return (\n <svg\n height={height}\n width={height * (240 / 58.5)}\n viewBox=\"0 0 240 58.5\"\n role=\"img\"\n aria-label=\"ZOREAL\"\n focusable=\"false\"\n className={className}\n >\n <path\n fill=\"currentColor\"\n d=\"M205,40.5h15.3v-3.5h-11.5v-18.4h-3.8v21.8ZM157,22.2v5.6h10.9v3.5h-10.9v5.8h12.5v3.5h-16.3v-21.8h16.2v3.5h-12.4ZM141.4,25.8c0,1.1-.4,2-1.2,2.7-.8.7-1.9,1-3.3,1h-5.6v-7.3h5.6c1.4,0,2.6.3,3.4.9.8.6,1.2,1.5,1.2,2.7ZM146,40.5l-5.9-8.3c.8-.2,1.4-.5,2.1-.9s1.2-.9,1.7-1.4c.4-.5.8-1.2,1.1-1.9s.4-1.5.4-2.4-.1-2-.5-2.9c-.4-.9-.9-1.6-1.7-2.2-.6-.6-1.5-1-2.5-1.4-1-.3-2.2-.4-3.4-.4h-9.8v21.8h3.8v-7.6h4.8l5.4,7.6h4.5ZM115.7,29.7c0,1.1-.1,2.1-.5,3s-.9,1.7-1.5,2.4-1.4,1.2-2.4,1.7c-.9.4-1.9.6-3,.6s-2.1-.1-3-.6-1.7-1-2.4-1.7-1.2-1.5-1.5-2.4-.5-1.9-.5-3,.1-2.1.5-3,.9-1.7,1.5-2.4,1.4-1.2,2.4-1.7c.9-.4,1.9-.6,3-.6s2.1.2,3,.6c.9.4,1.7,1,2.3,1.7.6.6,1.2,1.5,1.6,2.4s.5,1.9.5,3ZM119.7,29.6c0-1.5-.3-3-.8-4.4-.6-1.4-1.4-2.5-2.4-3.6-1-1-2.2-1.8-3.6-2.4-1.4-.6-3-.9-4.6-.9s-3.2.4-4.6.9c-1.4.6-2.7,1.4-3.7,2.4s-1.8,2.2-2.4,3.6c-.5,1.4-.8,2.8-.8,4.4s.3,3,.8,4.4c.6,1.4,1.4,2.5,2.4,3.6,1,1,2.2,1.8,3.6,2.4,1.4.6,3,.9,4.6.9s3.2-.4,4.6-.9c1.4-.6,2.6-1.4,3.7-2.4,1-1,1.8-2.2,2.4-3.6.5-1.4.8-2.9.8-4.4ZM86.1,22.1l-13,15.6v2.8h17.9v-3.4h-12.9l12.9-15.6v-2.8h-17.5v3.4h12.5ZM188.5,18.5h-3.5l-9.6,22h4c3.7-8.8,3.4-8,7.4-17.4,3.7,8.8,3.9,9.1,7.4,17.4h4l-9.6-22Z\"\n />\n <g fill={ZOREAL_BLUE} fillRule=\"evenodd\">\n <path d=\"M52,25.7c.4-2-.5-4.2-2.5-5.4l-11.8-6.8,3.4-2,10.1,5.9c3.6,2,5.1,6.3,3.8,10.1-.2.5-.4,1-.7,1.6-.3.5-.6.9-1,1.4-.9.9-1.9,1.7-3,2.2-2.4,1-5.2.9-7.6-.5l-5.9-3.4c-1.9-1.1-4.3-.9-5.9.5-.4.4-.8.8-1.1,1.3-.3.5-.5,1.1-.6,1.7-.4,2,.6,4.2,2.5,5.4l11.8,6.8-3.4,2-10.1-5.9c-3.6-2-5.1-6.3-3.8-10.1.1-.5.4-1,.7-1.6.3-.5.7-.9,1-1.4.9-.9,1.9-1.7,3.1-2.2,2.4-1,5.2-.9,7.6.4l5.9,3.4c1.9,1.1,4.3.9,5.9-.5.4-.4.8-.8,1.1-1.3.3-.5.5-1.1.6-1.7Z\" />\n <path d=\"M60.3,33.1c-.5.8-1.5,1.1-2.3.6-.9-.5-1.1-1.5-.7-2.3.5-.9,1.5-1.1,2.3-.7.9.5,1.1,1.5.7,2.4Z\" />\n <path d=\"M31.9,18c3.4-.5,6.9,0,10,1.9l.8.5h0c.8.5,1,1.5.6,2.3s-1.5,1.1-2.3.6l-.8-.5c-2.8-1.6-6-1.9-8.9-1.2-1,.3-2,.7-3,1.2h0c-.8.5-1.8.2-2.3-.7-.5-.8-.2-1.8.6-2.3,0,0,0,0,.2,0,.2,0,.4-.2.7-.3,1.4-.7,2.9-1.1,4.4-1.4Z\" />\n <path d=\"M21.4,24.9c.5-.8,1.6-1,2.3-.5.8.5,1,1.6.5,2.3-.5.8-1.6,1-2.4.5-.8-.5-1-1.6-.5-2.3Z\" />\n <path d=\"M45.5,23.9c.5-.9,1.5-1.1,2.3-.7.8.5,1.1,1.5.6,2.4-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3Z\" />\n <path d=\"M49.4,39.9c-3.3.6-6.9,0-10-1.8l-.8-.5h0c-.8-.5-1.1-1.5-.6-2.3s1.5-1.1,2.3-.6l.8.5c2.8,1.6,6,1.9,9,1.2,1-.3,2-.7,3-1.2h0c.8-.5,1.8-.2,2.3.6.5.9.2,1.9-.6,2.4,0,0-.1,0-.2,0-.2.1-.5.3-.7.4-1.4.7-2.8,1.1-4.4,1.4Z\" />\n <path d=\"M35.8,34c-.5.8-1.5,1.1-2.3.6s-1.1-1.5-.6-2.3,1.5-1.1,2.3-.6,1.1,1.5.6,2.3Z\" />\n </g>\n </svg>\n );\n}\n","/**\n * Pairing-modal copy, carried by the SDK.\n *\n * The modal is rendered by this package, so its strings have to ship with it:\n * an integrator cannot translate a component they never write, and asking every\n * one of them to re-supply the same fifteen strings is how a sign-in screen\n * ends up half-English in production.\n *\n * No i18n runtime. A frozen record and one `{time}` substitution is the whole\n * requirement, and a dependency here would be inherited by every host app.\n *\n * Locales match the set ZOREAL's own pairing page serves, so the phone and the\n * browser say the same thing in the same language. `strings()` resolves BCP 47\n * down to that set; anything unknown falls back to English rather than\n * rendering a key.\n */\n\nexport interface PairingStrings {\n /** Dialog title while the code is still unscanned. */\n title: string;\n /** Dialog title when the request is for verified identity attributes. */\n titleIdentify: string;\n /** Dialog title when the request is a presence check and not a login. */\n titlePresence: string;\n /** Dialog title once the request is waiting in the app. */\n titleApprove: string;\n bodyScan: string;\n bodyApprove: string;\n bodyEnrolling: string;\n waiting: string;\n waitingApproval: string;\n /** Carries `{time}`, substituted with mm:ss. */\n expiresIn: string;\n secured: string;\n noIdTitle: string;\n noIdBody: string;\n cancel: string;\n close: string;\n qrAlt: string;\n /** The default label of the sign-in button. */\n buttonContinue: string;\n}\n\nconst en: PairingStrings = {\n title: 'Scan to sign in',\n titleIdentify: 'Scan to verify your identity',\n titlePresence: 'Scan to prove you are a real human',\n titleApprove: 'Approve on your phone',\n bodyScan: 'Scan with your phone camera or the ZOREAL ID app.',\n bodyApprove: 'Approve the login in your ZOREAL ID app.',\n bodyEnrolling: 'Finish setting up ZOREAL ID on your phone, then approve the login.',\n waiting: 'Waiting for scan',\n waitingApproval: 'Waiting for approval',\n expiresIn: 'Expires in {time}',\n secured: 'Proof-of-Human verification by ZOREAL',\n noIdTitle: 'No ZOREAL ID yet?',\n noIdBody: 'Scan the same code to download the app and create one for free. It only takes a minute.',\n cancel: 'Cancel',\n close: 'Close',\n qrAlt: 'QR code to sign in with ZOREAL',\n buttonContinue: 'Continue with ZOREAL',\n};\n\nconst TRANSLATIONS: Record<string, PairingStrings> = {\n en,\n sv: {\n title: 'Skanna för att logga in',\n titleIdentify: 'Skanna för att verifiera din identitet',\n titlePresence: 'Skanna för att bevisa att du är en riktig människa',\n titleApprove: 'Godkänn på telefonen',\n bodyScan: 'Skanna med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkänn inloggningen i ZOREAL ID-appen.',\n bodyEnrolling: 'Slutför konfigurationen av ZOREAL ID på telefonen och godkänn sedan inloggningen.',\n waiting: 'Väntar på skanning',\n waitingApproval: 'Väntar på godkännande',\n expiresIn: 'Upphör om {time}',\n secured: 'Proof-of-Human-verifiering av ZOREAL',\n noIdTitle: 'Har du inget ZOREAL ID?',\n noIdBody: 'Skanna samma kod för att ladda ner appen och skapa ett gratis. Det tar bara en minut.',\n cancel: 'Avbryt',\n close: 'Stäng',\n qrAlt: 'QR-kod för att logga in med ZOREAL',\n buttonContinue: 'Fortsätt med ZOREAL',\n },\n es: {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Apruébalo en tu teléfono',\n bodyScan: 'Escanea con la cámara de tu teléfono o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu teléfono y luego aprueba el inicio de sesión.',\n waiting: 'Esperando el escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Caduca en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Aún no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear una gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n pt: {\n title: 'Digitalize para entrar',\n titleIdentify: 'Digitalize para verificar a sua identidade',\n titlePresence: 'Digitalize para provar que é uma pessoa real',\n titleApprove: 'Aprove no seu telefone',\n bodyScan: 'Digitalize com a câmara do seu telefone ou com a app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu telefone e depois aprove o login.',\n waiting: 'Aguardando digitalização',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem ZOREAL ID?',\n noIdBody: 'Digitalize o mesmo código para baixar o app e criar uma conta grátis. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n fr: {\n title: 'Scannez pour vous connecter',\n titleIdentify: 'Scannez pour vérifier votre identité',\n titlePresence: 'Scannez pour prouver que vous êtes bien un humain',\n titleApprove: 'Approuvez sur votre téléphone',\n bodyScan: \"Scannez avec l'appareil photo de votre téléphone ou l'app ZOREAL ID.\",\n bodyApprove: 'Approuvez la connexion dans votre app ZOREAL ID.',\n bodyEnrolling: 'Terminez la configuration de ZOREAL ID sur votre téléphone, puis approuvez la connexion.',\n waiting: 'En attente du scan',\n waitingApproval: \"En attente d'approbation\",\n expiresIn: 'Expire dans {time}',\n secured: 'Vérification Proof-of-Human par ZOREAL',\n noIdTitle: \"Pas encore de ZOREAL ID ?\",\n noIdBody: \"Scannez le même code pour télécharger l'app et en créer un gratuitement. Cela prend une minute.\",\n cancel: 'Annuler',\n close: 'Fermer',\n qrAlt: 'Code QR pour se connecter avec ZOREAL',\n buttonContinue: 'Continuer avec ZOREAL',\n },\n de: {\n title: 'Zum Anmelden scannen',\n titleIdentify: 'Scannen, um Ihre Identität zu verifizieren',\n titlePresence: 'Scannen, um zu beweisen, dass Sie ein echter Mensch sind',\n titleApprove: 'Auf dem Handy bestätigen',\n bodyScan: 'Mit der Handykamera oder der ZOREAL ID App scannen.',\n bodyApprove: 'Anmeldung in der ZOREAL ID App bestätigen.',\n bodyEnrolling: 'ZOREAL ID auf dem Handy fertig einrichten und dann die Anmeldung bestätigen.',\n waiting: 'Warten auf Scan',\n waitingApproval: 'Warten auf Bestätigung',\n expiresIn: 'Läuft ab in {time}',\n secured: 'Proof-of-Human-Verifizierung von ZOREAL',\n noIdTitle: 'Noch keine ZOREAL ID?',\n noIdBody: 'Denselben Code scannen, um die App zu laden und kostenlos eine zu erstellen. Dauert nur eine Minute.',\n cancel: 'Abbrechen',\n close: 'Schließen',\n qrAlt: 'QR-Code für die Anmeldung mit ZOREAL',\n buttonContinue: 'Weiter mit ZOREAL',\n },\n ru: {\n title: 'Отсканируйте, чтобы войти',\n titleIdentify: 'Отсканируйте, чтобы подтвердить личность',\n titlePresence: 'Отсканируйте, чтобы доказать, что вы реальный человек',\n titleApprove: 'Подтвердите на телефоне',\n bodyScan: 'Отсканируйте камерой телефона или через приложение ZOREAL ID.',\n bodyApprove: 'Подтвердите вход в приложении ZOREAL ID.',\n bodyEnrolling: 'Завершите настройку ZOREAL ID на телефоне, затем подтвердите вход.',\n waiting: 'Ожидание сканирования',\n waitingApproval: 'Ожидание подтверждения',\n expiresIn: 'Истекает через {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Ещё нет ZOREAL ID?',\n noIdBody: 'Отсканируйте тот же код, чтобы скачать приложение и создать его бесплатно. Это займёт минуту.',\n cancel: 'Отмена',\n close: 'Закрыть',\n qrAlt: 'QR-код для входа через ZOREAL',\n buttonContinue: 'Продолжить с ZOREAL',\n },\n ja: {\n title: 'スキャンしてログイン',\n titleIdentify: 'スキャンして本人確認',\n titlePresence: 'スキャンして実在の人物であることを証明',\n titleApprove: 'スマートフォンで承認',\n bodyScan: 'スマートフォンのカメラまたはZOREAL IDアプリでスキャンしてください。',\n bodyApprove: 'ZOREAL IDアプリでログインを承認してください。',\n bodyEnrolling: 'スマートフォンでZOREAL IDの設定を完了し、ログインを承認してください。',\n waiting: 'スキャン待ち',\n waitingApproval: '承認待ち',\n expiresIn: '有効期限まで {time}',\n secured: 'ZOREALによるProof-of-Human認証',\n noIdTitle: 'ZOREAL IDをお持ちでないですか?',\n noIdBody: '同じコードをスキャンしてアプリをダウンロードし、無料で作成できます。1分ほどで完了します。',\n cancel: 'キャンセル',\n close: '閉じる',\n qrAlt: 'ZOREALでログインするためのQRコード',\n buttonContinue: 'ZOREALで続行',\n },\n hi: {\n title: 'साइन इन करने के लिए स्कैन करें',\n titleIdentify: 'अपनी पहचान सत्यापित करने के लिए स्कैन करें',\n titlePresence: 'यह साबित करने के लिए स्कैन करें कि आप एक वास्तविक इंसान हैं',\n titleApprove: 'अपने फोन पर स्वीकृत करें',\n bodyScan: 'अपने फोन के कैमरे या ZOREAL ID ऐप से स्कैन करें।',\n bodyApprove: 'अपने ZOREAL ID ऐप में लॉगिन स्वीकृत करें।',\n bodyEnrolling: 'अपने फोन पर ZOREAL ID सेटअप पूरा करें, फिर लॉगिन स्वीकृत करें।',\n waiting: 'स्कैन की प्रतीक्षा है',\n waitingApproval: 'स्वीकृति की प्रतीक्षा है',\n expiresIn: '{time} में समाप्त',\n secured: 'ZOREAL द्वारा Proof-of-Human सत्यापन',\n noIdTitle: 'अभी तक ZOREAL ID नहीं है?',\n noIdBody: 'ऐप डाउनलोड करने और मुफ्त में एक बनाने के लिए वही कोड स्कैन करें। इसमें बस एक मिनट लगता है।',\n cancel: 'रद्द करें',\n close: 'बंद करें',\n qrAlt: 'ZOREAL से साइन इन करने के लिए QR कोड',\n buttonContinue: 'ZOREAL के साथ जारी रखें',\n },\n zhs: {\n title: '扫码登录',\n titleIdentify: '扫码验证身份',\n titlePresence: '扫码证明您是真人',\n titleApprove: '在手机上批准',\n bodyScan: '使用手机相机或 ZOREAL ID 应用扫描。',\n bodyApprove: '请在 ZOREAL ID 应用中批准登录。',\n bodyEnrolling: '请在手机上完成 ZOREAL ID 设置,然后批准登录。',\n waiting: '等待扫描',\n waitingApproval: '等待批准',\n expiresIn: '{time} 后失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 验证',\n noIdTitle: '还没有 ZOREAL ID?',\n noIdBody: '扫描同一个二维码即可下载应用并免费创建,只需一分钟。',\n cancel: '取消',\n close: '关闭',\n qrAlt: '使用 ZOREAL 登录的二维码',\n buttonContinue: '使用 ZOREAL 继续',\n },\n zht: {\n title: '掃碼登入',\n titleIdentify: '掃碼驗證身分',\n titlePresence: '掃碼證明您是真人',\n titleApprove: '在手機上核准',\n bodyScan: '使用手機相機或 ZOREAL ID 應用程式掃描。',\n bodyApprove: '請在 ZOREAL ID 應用程式中核准登入。',\n bodyEnrolling: '請在手機上完成 ZOREAL ID 設定,然後核准登入。',\n waiting: '等待掃描',\n waitingApproval: '等待核准',\n expiresIn: '{time} 後失效',\n secured: '由 ZOREAL 提供的 Proof-of-Human 驗證',\n noIdTitle: '還沒有 ZOREAL ID?',\n noIdBody: '掃描同一個 QR code 即可下載應用程式並免費建立,只需一分鐘。',\n cancel: '取消',\n close: '關閉',\n qrAlt: '使用 ZOREAL 登入的 QR code',\n buttonContinue: '使用 ZOREAL 繼續',\n },\n ar: {\n title: 'امسح لتسجيل الدخول',\n titleIdentify: 'امسح للتحقق من هويتك',\n titlePresence: 'امسح لإثبات أنك إنسان حقيقي',\n titleApprove: 'وافق على هاتفك',\n bodyScan: 'امسح باستخدام كاميرا هاتفك أو تطبيق ZOREAL ID.',\n bodyApprove: 'وافق على تسجيل الدخول في تطبيق ZOREAL ID.',\n bodyEnrolling: 'أكمل إعداد ZOREAL ID على هاتفك، ثم وافق على تسجيل الدخول.',\n waiting: 'في انتظار المسح',\n waitingApproval: 'في انتظار الموافقة',\n expiresIn: 'تنتهي الصلاحية خلال {time}',\n secured: 'التحقق من Proof-of-Human بواسطة ZOREAL',\n noIdTitle: 'ليس لديك ZOREAL ID بعد؟',\n noIdBody: 'امسح الرمز نفسه لتنزيل التطبيق وإنشاء حساب مجاني. يستغرق الأمر دقيقة واحدة فقط.',\n cancel: 'إلغاء',\n close: 'إغلاق',\n qrAlt: 'رمز QR لتسجيل الدخول باستخدام ZOREAL',\n buttonContinue: 'المتابعة باستخدام ZOREAL',\n },\n ko: {\n title: '스캔하여 로그인',\n titleIdentify: '스캔하여 신원 확인',\n titlePresence: '스캔하여 실제 사람임을 증명',\n titleApprove: '휴대폰에서 승인',\n bodyScan: '휴대폰 카메라 또는 ZOREAL ID 앱으로 스캔하세요.',\n bodyApprove: 'ZOREAL ID 앱에서 로그인을 승인하세요.',\n bodyEnrolling: '휴대폰에서 ZOREAL ID 설정을 완료한 후 로그인을 승인하세요.',\n waiting: '스캔 대기 중',\n waitingApproval: '승인 대기 중',\n expiresIn: '{time} 후 만료',\n secured: 'ZOREAL의 Proof-of-Human 인증',\n noIdTitle: '아직 ZOREAL ID가 없으신가요?',\n noIdBody: '같은 코드를 스캔해 앱을 내려받고 무료로 만드세요. 1분이면 됩니다.',\n cancel: '취소',\n close: '닫기',\n qrAlt: 'ZOREAL로 로그인하기 위한 QR 코드',\n buttonContinue: 'ZOREAL로 계속',\n },\n // Български\n bg: {\n title: 'Сканирайте за вход',\n titleIdentify: 'Сканирайте, за да потвърдите самоличността си',\n titlePresence: 'Сканирайте, за да докажете, че сте истински човек',\n titleApprove: 'Потвърдете на телефона си',\n bodyScan: 'Сканирайте с камерата на телефона или с приложението ZOREAL ID.',\n bodyApprove: 'Потвърдете входа в приложението ZOREAL ID.',\n bodyEnrolling: 'Довършете настройката на ZOREAL ID на телефона си, след което потвърдете входа.',\n waiting: 'Изчакване на сканиране',\n waitingApproval: 'Изчакване на потвърждение',\n expiresIn: 'Изтича след {time}',\n secured: 'Проверка Proof-of-Human от ZOREAL',\n noIdTitle: 'Все още нямате ZOREAL ID?',\n noIdBody: 'Сканирайте същия код, за да изтеглите приложението и да си създадете безплатен акаунт. Отнема само минута.',\n cancel: 'Отказ',\n close: 'Затвори',\n qrAlt: 'QR код за вход със ZOREAL',\n buttonContinue: 'Продължи със ZOREAL',\n },\n // বাংলা\n bn: {\n title: 'সাইন ইন করতে স্ক্যান করুন',\n titleIdentify: 'আপনার পরিচয় যাচাই করতে স্ক্যান করুন',\n titlePresence: 'আপনি একজন প্রকৃত মানুষ তা প্রমাণ করতে স্ক্যান করুন',\n titleApprove: 'আপনার ফোনে অনুমোদন করুন',\n bodyScan: 'আপনার ফোনের ক্যামেরা বা ZOREAL ID অ্যাপ দিয়ে স্ক্যান করুন।',\n bodyApprove: 'আপনার ZOREAL ID অ্যাপে লগইন অনুমোদন করুন।',\n bodyEnrolling: 'আপনার ফোনে ZOREAL ID সেটআপ সম্পূর্ণ করুন, তারপর লগইন অনুমোদন করুন।',\n waiting: 'স্ক্যানের অপেক্ষায়',\n waitingApproval: 'অনুমোদনের অপেক্ষায়',\n expiresIn: '{time} পরে মেয়াদ শেষ হবে',\n secured: 'ZOREAL দ্বারা Proof-of-Human যাচাইকরণ',\n noIdTitle: 'এখনো ZOREAL ID নেই?',\n noIdBody: 'অ্যাপ ডাউনলোড করে বিনামূল্যে একটি তৈরি করতে একই কোড স্ক্যান করুন। এতে মাত্র এক মিনিট সময় লাগে।',\n cancel: 'বাতিল',\n close: 'বন্ধ',\n qrAlt: 'ZOREAL দিয়ে সাইন ইন করার জন্য QR কোড',\n buttonContinue: 'ZOREAL দিয়ে চালিয়ে যান',\n },\n // Bosanski\n bs: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte da potvrdite svoj identitet',\n titlePresence: 'Skenirajte da dokažete da ste stvarna osoba',\n titleApprove: 'Odobrite na svom telefonu',\n bodyScan: 'Skenirajte kamerom svog telefona ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Završite podešavanje ZOREAL ID-a na svom telefonu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human verifikacija',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga napravite. Traje samo minutu.',\n cancel: 'Otkaži',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Čeština\n cs: {\n title: 'Přihlaste se naskenováním',\n titleIdentify: 'Naskenujte pro ověření totožnosti',\n titlePresence: 'Naskenujte a prokažte, že jste skutečný člověk',\n titleApprove: 'Potvrďte v telefonu',\n bodyScan: 'Naskenujte fotoaparátem telefonu nebo aplikací ZOREAL ID.',\n bodyApprove: 'Potvrďte přihlášení v aplikaci ZOREAL ID.',\n bodyEnrolling: 'Dokončete nastavení ZOREAL ID v telefonu a poté potvrďte přihlášení.',\n waiting: 'Čekání na naskenování',\n waitingApproval: 'Čekání na potvrzení',\n expiresIn: 'Vyprší za {time}',\n secured: 'Ověření Proof-of-Human od ZOREAL',\n noIdTitle: 'Ještě nemáte ZOREAL ID?',\n noIdBody: 'Naskenováním stejného kódu si stáhnete aplikaci a zdarma vytvoříte ZOREAL ID. Zabere to jen minutu.',\n cancel: 'Zrušit',\n close: 'Zavřít',\n qrAlt: 'QR kód pro přihlášení pomocí ZOREAL',\n buttonContinue: 'Pokračovat se ZOREAL',\n },\n // Dansk\n da: {\n title: 'Scan for at logge ind',\n titleIdentify: 'Scan for at bekræfte din identitet',\n titlePresence: 'Scan for at bevise, at du er et rigtigt menneske',\n titleApprove: 'Godkend på din telefon',\n bodyScan: 'Scan med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkend login i din ZOREAL ID-app.',\n bodyEnrolling: 'Færdiggør opsætningen af ZOREAL ID på din telefon, og godkend derefter login.',\n waiting: 'Venter på scanning',\n waitingApproval: 'Venter på godkendelse',\n expiresIn: 'Udløber om {time}',\n secured: 'Proof-of-Human-verificering af ZOREAL',\n noIdTitle: 'Har du ikke et ZOREAL ID endnu?',\n noIdBody: 'Scan den samme kode for at hente appen og oprette et gratis. Det tager kun et minut.',\n cancel: 'Annuller',\n close: 'Luk',\n qrAlt: 'QR-kode til at logge ind med ZOREAL',\n buttonContinue: 'Fortsæt med ZOREAL',\n },\n // Ελληνικά\n el: {\n title: 'Σάρωση για σύνδεση',\n titleIdentify: 'Σάρωση για επαλήθευση ταυτότητας',\n titlePresence: 'Σάρωση για να αποδείξετε ότι είστε πραγματικός άνθρωπος',\n titleApprove: 'Έγκριση από το κινητό σας',\n bodyScan: 'Σαρώστε με την κάμερα του κινητού σας ή την εφαρμογή ZOREAL ID.',\n bodyApprove: 'Εγκρίνετε τη σύνδεση στην εφαρμογή ZOREAL ID.',\n bodyEnrolling: 'Ολοκληρώστε τη ρύθμιση του ZOREAL ID στο κινητό σας και έπειτα εγκρίνετε τη σύνδεση.',\n waiting: 'Αναμονή σάρωσης',\n waitingApproval: 'Αναμονή έγκρισης',\n expiresIn: 'Λήγει σε {time}',\n secured: 'Επαλήθευση Proof-of-Human από τη ZOREAL',\n noIdTitle: 'Δεν έχετε ακόμα ZOREAL ID;',\n noIdBody: 'Σαρώστε τον ίδιο κωδικό για να κατεβάσετε την εφαρμογή και να δημιουργήσετε ένα δωρεάν. Χρειάζεται μόνο ένα λεπτό.',\n cancel: 'Άκυρο',\n close: 'Κλείσιμο',\n qrAlt: 'Κωδικός QR για σύνδεση με ZOREAL',\n buttonContinue: 'Συνέχεια με ZOREAL',\n },\n // Español (LA)\n 'es-419': {\n title: 'Escanea para iniciar sesión',\n titleIdentify: 'Escanea para verificar tu identidad',\n titlePresence: 'Escanea para demostrar que eres una persona real',\n titleApprove: 'Aprueba desde tu celular',\n bodyScan: 'Escanea con la cámara de tu celular o con la app ZOREAL ID.',\n bodyApprove: 'Aprueba el inicio de sesión en tu app ZOREAL ID.',\n bodyEnrolling: 'Termina de configurar ZOREAL ID en tu celular y luego aprueba el inicio de sesión.',\n waiting: 'Esperando escaneo',\n waitingApproval: 'Esperando aprobación',\n expiresIn: 'Expira en {time}',\n secured: 'Verificación Proof-of-Human de ZOREAL',\n noIdTitle: '¿Todavía no tienes ZOREAL ID?',\n noIdBody: 'Escanea el mismo código para descargar la app y crear uno gratis. Solo toma un minuto.',\n cancel: 'Cancelar',\n close: 'Cerrar',\n qrAlt: 'Código QR para iniciar sesión con ZOREAL',\n buttonContinue: 'Continuar con ZOREAL',\n },\n // Suomi\n fi: {\n title: 'Kirjaudu sisään skannaamalla',\n titleIdentify: 'Vahvista henkilöllisyytesi skannaamalla',\n titlePresence: 'Todista skannaamalla, että olet oikea ihminen',\n titleApprove: 'Hyväksy puhelimessasi',\n bodyScan: 'Skannaa puhelimesi kameralla tai ZOREAL ID -sovelluksella.',\n bodyApprove: 'Hyväksy kirjautuminen ZOREAL ID -sovelluksessasi.',\n bodyEnrolling: 'Viimeistele ZOREAL ID -sovelluksen käyttöönotto puhelimellasi ja hyväksy sitten kirjautuminen.',\n waiting: 'Odotetaan skannausta',\n waitingApproval: 'Odotetaan hyväksyntää',\n expiresIn: 'Vanhenee {time} kuluttua',\n secured: 'ZOREALin Proof-of-Human-vahvistus',\n noIdTitle: 'Eikö sinulla ole vielä ZOREAL ID:tä?',\n noIdBody: 'Skannaa sama koodi ladataksesi sovelluksen ja luodaksesi tunnuksen ilmaiseksi. Se vie vain minuutin.',\n cancel: 'Peruuta',\n close: 'Sulje',\n qrAlt: 'QR-koodi ZOREAL-kirjautumista varten',\n buttonContinue: 'Jatka ZOREALilla',\n },\n // עברית\n he: {\n title: 'סרוק כדי להתחבר',\n titleIdentify: 'סרוק כדי לאמת את זהותך',\n titlePresence: 'סרוק כדי להוכיח שאתה אדם אמיתי',\n titleApprove: 'אשר בטלפון שלך',\n bodyScan: 'סרוק באמצעות מצלמת הטלפון שלך או אפליקציית ZOREAL ID.',\n bodyApprove: 'אשר את ההתחברות באפליקציית ZOREAL ID שלך.',\n bodyEnrolling: 'סיים להגדיר את ZOREAL ID בטלפון שלך, ואז אשר את ההתחברות.',\n waiting: 'ממתין לסריקה',\n waitingApproval: 'ממתין לאישור',\n expiresIn: 'יפוג בעוד {time}',\n secured: 'אימות Proof-of-Human מבית ZOREAL',\n noIdTitle: 'עדיין אין לך ZOREAL ID?',\n noIdBody: 'סרוק את אותו הקוד כדי להוריד את האפליקציה וליצור אחד בחינם. זה לוקח רק דקה.',\n cancel: 'ביטול',\n close: 'סגור',\n qrAlt: 'קוד QR להתחברות עם ZOREAL',\n buttonContinue: 'המשך עם ZOREAL',\n },\n // Hrvatski\n hr: {\n title: 'Skenirajte za prijavu',\n titleIdentify: 'Skenirajte za potvrdu identiteta',\n titlePresence: 'Skenirajte kako biste dokazali da ste stvarna osoba',\n titleApprove: 'Odobrite na svom mobitelu',\n bodyScan: 'Skenirajte kamerom svog mobitela ili aplikacijom ZOREAL ID.',\n bodyApprove: 'Odobrite prijavu u aplikaciji ZOREAL ID.',\n bodyEnrolling: 'Dovršite postavljanje ZOREAL ID-a na svom mobitelu, a zatim odobrite prijavu.',\n waiting: 'Čeka se skeniranje',\n waitingApproval: 'Čeka se odobrenje',\n expiresIn: 'Ističe za {time}',\n secured: 'ZOREAL Proof-of-Human provjera',\n noIdTitle: 'Nemate ZOREAL ID?',\n noIdBody: 'Skenirajte isti kod da preuzmete aplikaciju i besplatno ga izradite. Traje samo minutu.',\n cancel: 'Odustani',\n close: 'Zatvori',\n qrAlt: 'QR kod za prijavu putem ZOREAL-a',\n buttonContinue: 'Nastavi sa ZOREAL-om',\n },\n // Magyar\n hu: {\n title: 'Bejelentkezés beolvasással',\n titleIdentify: 'Olvassa be a személyazonossága igazolásához',\n titlePresence: 'Olvassa be annak igazolásához, hogy valódi ember',\n titleApprove: 'Jóváhagyás a telefonján',\n bodyScan: 'Olvassa be a telefonja kamerájával, vagy a ZOREAL ID alkalmazással.',\n bodyApprove: 'Hagyja jóvá a bejelentkezést a ZOREAL ID alkalmazásban.',\n bodyEnrolling: 'Fejezze be a ZOREAL ID beállítását a telefonján, majd hagyja jóvá a bejelentkezést.',\n waiting: 'Várakozás beolvasásra',\n waitingApproval: 'Várakozás jóváhagyásra',\n expiresIn: 'Lejár {time} múlva',\n secured: 'Proof-of-Human hitelesítés a ZOREAL-tól',\n noIdTitle: 'Még nincs ZOREAL ID-je?',\n noIdBody: 'Olvassa be ugyanazt a kódot az alkalmazás letöltéséhez, és hozzon létre egyet ingyenesen. Mindössze egy percet vesz igénybe.',\n cancel: 'Mégse',\n close: 'Bezárás',\n qrAlt: 'QR-kód a ZOREAL-lal való bejelentkezéshez',\n buttonContinue: 'Folytatás a ZOREAL-lal',\n },\n // Bahasa Indonesia\n id: {\n title: 'Pindai untuk masuk',\n titleIdentify: 'Pindai untuk memverifikasi identitas Anda',\n titlePresence: 'Pindai untuk membuktikan bahwa Anda manusia sungguhan',\n titleApprove: 'Setujui di ponsel Anda',\n bodyScan: 'Pindai dengan kamera ponsel atau aplikasi ZOREAL ID.',\n bodyApprove: 'Setujui proses masuk di aplikasi ZOREAL ID Anda.',\n bodyEnrolling: 'Selesaikan pengaturan ZOREAL ID di ponsel Anda, lalu setujui proses masuk.',\n waiting: 'Menunggu pemindaian',\n waitingApproval: 'Menunggu persetujuan',\n expiresIn: 'Berakhir dalam {time}',\n secured: 'Verifikasi Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum punya ZOREAL ID?',\n noIdBody: 'Pindai kode yang sama untuk mengunduh aplikasi dan membuat akun secara gratis. Hanya butuh waktu satu menit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kode QR untuk masuk dengan ZOREAL',\n buttonContinue: 'Lanjutkan dengan ZOREAL',\n },\n // Italiano\n it: {\n title: 'Scansiona per accedere',\n titleIdentify: 'Scansiona per verificare la tua identità',\n titlePresence: 'Scansiona per dimostrare di essere una persona reale',\n titleApprove: 'Approva sul tuo telefono',\n bodyScan: 'Scansiona con la fotocamera del telefono o con l\\'app ZOREAL ID.',\n bodyApprove: 'Approva l\\'accesso nell\\'app ZOREAL ID.',\n bodyEnrolling: 'Completa la configurazione di ZOREAL ID sul telefono, poi approva l\\'accesso.',\n waiting: 'In attesa della scansione',\n waitingApproval: 'In attesa di approvazione',\n expiresIn: 'Scade tra {time}',\n secured: 'Verifica Proof-of-Human di ZOREAL',\n noIdTitle: 'Non hai ancora uno ZOREAL ID?',\n noIdBody: 'Scansiona lo stesso codice per scaricare l\\'app e crearne uno gratis. Basta un minuto.',\n cancel: 'Annulla',\n close: 'Chiudi',\n qrAlt: 'Codice QR per accedere con ZOREAL',\n buttonContinue: 'Continua con ZOREAL',\n },\n // Bahasa Melayu\n ms: {\n title: 'Imbas untuk log masuk',\n titleIdentify: 'Imbas untuk mengesahkan identiti anda',\n titlePresence: 'Imbas untuk membuktikan anda manusia sebenar',\n titleApprove: 'Luluskan di telefon anda',\n bodyScan: 'Imbas dengan kamera telefon atau aplikasi ZOREAL ID.',\n bodyApprove: 'Luluskan log masuk dalam aplikasi ZOREAL ID anda.',\n bodyEnrolling: 'Selesaikan persediaan ZOREAL ID di telefon anda, kemudian luluskan log masuk.',\n waiting: 'Menunggu imbasan',\n waitingApproval: 'Menunggu kelulusan',\n expiresIn: 'Tamat tempoh dalam {time}',\n secured: 'Pengesahan Proof-of-Human oleh ZOREAL',\n noIdTitle: 'Belum ada ZOREAL ID?',\n noIdBody: 'Imbas kod yang sama untuk memuat turun aplikasi dan cipta satu secara percuma. Hanya mengambil masa seminit.',\n cancel: 'Batal',\n close: 'Tutup',\n qrAlt: 'Kod QR untuk log masuk dengan ZOREAL',\n buttonContinue: 'Teruskan dengan ZOREAL',\n },\n // Nederlands\n nl: {\n title: 'Scan om in te loggen',\n titleIdentify: 'Scan om je identiteit te verifiëren',\n titlePresence: 'Scan om te bewijzen dat je een echt mens bent',\n titleApprove: 'Keur goed op je telefoon',\n bodyScan: 'Scan met de camera van je telefoon of de ZOREAL ID-app.',\n bodyApprove: 'Keur de aanmelding goed in je ZOREAL ID-app.',\n bodyEnrolling: 'Rond het instellen van ZOREAL ID op je telefoon af en keur daarna de aanmelding goed.',\n waiting: 'Wachten op scan',\n waitingApproval: 'Wachten op goedkeuring',\n expiresIn: 'Verloopt over {time}',\n secured: 'Proof-of-Human-verificatie door ZOREAL',\n noIdTitle: 'Nog geen ZOREAL ID?',\n noIdBody: 'Scan dezelfde code om de app te downloaden en gratis een account aan te maken. Dit duurt maar een minuut.',\n cancel: 'Annuleren',\n close: 'Sluiten',\n qrAlt: 'QR-code om in te loggen met ZOREAL',\n buttonContinue: 'Doorgaan met ZOREAL',\n },\n // Norsk\n no: {\n title: 'Skann for å logge inn',\n titleIdentify: 'Skann for å bekrefte identiteten din',\n titlePresence: 'Skann for å bevise at du er et ekte menneske',\n titleApprove: 'Godkjenn på telefonen din',\n bodyScan: 'Skann med telefonens kamera eller ZOREAL ID-appen.',\n bodyApprove: 'Godkjenn innloggingen i ZOREAL ID-appen din.',\n bodyEnrolling: 'Fullfør oppsettet av ZOREAL ID på telefonen din, og godkjenn deretter innloggingen.',\n waiting: 'Venter på skanning',\n waitingApproval: 'Venter på godkjenning',\n expiresIn: 'Utløper om {time}',\n secured: 'Proof-of-Human-verifisering av ZOREAL',\n noIdTitle: 'Har du ikke ZOREAL ID ennå?',\n noIdBody: 'Skann den samme koden for å laste ned appen og opprette en gratis. Det tar bare et minutt.',\n cancel: 'Avbryt',\n close: 'Lukk',\n qrAlt: 'QR-kode for å logge inn med ZOREAL',\n buttonContinue: 'Fortsett med ZOREAL',\n },\n // Polski\n pl: {\n title: 'Zeskanuj, aby się zalogować',\n titleIdentify: 'Zeskanuj, aby zweryfikować swoją tożsamość',\n titlePresence: 'Zeskanuj, aby udowodnić, że jesteś prawdziwym człowiekiem',\n titleApprove: 'Zatwierdź w telefonie',\n bodyScan: 'Zeskanuj aparatem telefonu lub aplikacją ZOREAL ID.',\n bodyApprove: 'Zatwierdź logowanie w aplikacji ZOREAL ID.',\n bodyEnrolling: 'Dokończ konfigurację ZOREAL ID w telefonie, a następnie zatwierdź logowanie.',\n waiting: 'Czekanie na skan',\n waitingApproval: 'Czekanie na zatwierdzenie',\n expiresIn: 'Wygasa za {time}',\n secured: 'Weryfikacja Proof-of-Human od ZOREAL',\n noIdTitle: 'Nie masz jeszcze ZOREAL ID?',\n noIdBody: 'Zeskanuj ten sam kod, aby pobrać aplikację i bezpłatnie utworzyć ZOREAL ID. Zajmie to tylko minutę.',\n cancel: 'Anuluj',\n close: 'Zamknij',\n qrAlt: 'Kod QR do logowania za pomocą ZOREAL',\n buttonContinue: 'Kontynuuj z ZOREAL',\n },\n // Português (BR)\n 'pt-br': {\n title: 'Escaneie para entrar',\n titleIdentify: 'Escaneie para verificar sua identidade',\n titlePresence: 'Escaneie para provar que você é uma pessoa real',\n titleApprove: 'Aprove no seu celular',\n bodyScan: 'Escaneie com a câmera do seu celular ou com o app ZOREAL ID.',\n bodyApprove: 'Aprove o login no app ZOREAL ID.',\n bodyEnrolling: 'Termine de configurar o ZOREAL ID no seu celular e depois aprove o login.',\n waiting: 'Aguardando escaneamento',\n waitingApproval: 'Aguardando aprovação',\n expiresIn: 'Expira em {time}',\n secured: 'Verificação Proof-of-Human da ZOREAL',\n noIdTitle: 'Ainda não tem um ZOREAL ID?',\n noIdBody: 'Escaneie o mesmo código para baixar o app e criar um de graça. Leva só um minuto.',\n cancel: 'Cancelar',\n close: 'Fechar',\n qrAlt: 'Código QR para entrar com ZOREAL',\n buttonContinue: 'Continuar com ZOREAL',\n },\n // Română\n ro: {\n title: 'Scanați pentru conectare',\n titleIdentify: 'Scanați pentru a vă verifica identitatea',\n titlePresence: 'Scanați pentru a dovedi că sunteți o persoană reală',\n titleApprove: 'Aprobați de pe telefon',\n bodyScan: 'Scanați cu camera telefonului sau cu aplicația ZOREAL ID.',\n bodyApprove: 'Aprobați conectarea în aplicația ZOREAL ID.',\n bodyEnrolling: 'Finalizați configurarea ZOREAL ID pe telefon, apoi aprobați conectarea.',\n waiting: 'Se așteaptă scanarea',\n waitingApproval: 'Se așteaptă aprobarea',\n expiresIn: 'Expiră în {time}',\n secured: 'Verificare Proof-of-Human de la ZOREAL',\n noIdTitle: 'Nu aveți încă un ZOREAL ID?',\n noIdBody: 'Scanați același cod pentru a descărca aplicația și a crea unul gratuit. Durează doar un minut.',\n cancel: 'Anulează',\n close: 'Închide',\n qrAlt: 'Cod QR pentru conectare cu ZOREAL',\n buttonContinue: 'Continuați cu ZOREAL',\n },\n // Српски\n sr: {\n title: 'Скенирајте за пријаву',\n titleIdentify: 'Скенирајте да потврдите свој идентитет',\n titlePresence: 'Скенирајте да докажете да сте права особа',\n titleApprove: 'Одобрите на свом телефону',\n bodyScan: 'Скенирајте камером свог телефона или апликацијом ZOREAL ID.',\n bodyApprove: 'Одобрите пријаву у апликацији ZOREAL ID.',\n bodyEnrolling: 'Довршите подешавање ZOREAL ID-а на свом телефону, па одобрите пријаву.',\n waiting: 'Чека се скенирање',\n waitingApproval: 'Чека се одобрење',\n expiresIn: 'Истиче за {time}',\n secured: 'ZOREAL Proof-of-Human верификација',\n noIdTitle: 'Немате ZOREAL ID?',\n noIdBody: 'Скенирајте исти код да преузмете апликацију и бесплатно га направите. Траје само минут.',\n cancel: 'Откажи',\n close: 'Затвори',\n qrAlt: 'QR код за пријаву преко ZOREAL-а',\n buttonContinue: 'Настави са ZOREAL-ом',\n },\n // ไทย\n th: {\n title: 'สแกนเพื่อเข้าสู่ระบบ',\n titleIdentify: 'สแกนเพื่อยืนยันตัวตนของคุณ',\n titlePresence: 'สแกนเพื่อพิสูจน์ว่าคุณเป็นมนุษย์จริง',\n titleApprove: 'อนุมัติบนโทรศัพท์ของคุณ',\n bodyScan: 'สแกนด้วยกล้องโทรศัพท์หรือแอป ZOREAL ID',\n bodyApprove: 'อนุมัติการเข้าสู่ระบบในแอป ZOREAL ID ของคุณ',\n bodyEnrolling: 'ตั้งค่า ZOREAL ID บนโทรศัพท์ของคุณให้เสร็จสิ้น แล้วอนุมัติการเข้าสู่ระบบ',\n waiting: 'รอการสแกน',\n waitingApproval: 'รอการอนุมัติ',\n expiresIn: 'หมดอายุใน {time}',\n secured: 'การยืนยันตัวตน Proof-of-Human โดย ZOREAL',\n noIdTitle: 'ยังไม่มี ZOREAL ID ใช่ไหม',\n noIdBody: 'สแกนโค้ดเดียวกันเพื่อดาวน์โหลดแอปและสร้างบัญชีฟรี ใช้เวลาเพียงนาทีเดียว',\n cancel: 'ยกเลิก',\n close: 'ปิด',\n qrAlt: 'คิวอาร์โค้ดสำหรับเข้าสู่ระบบด้วย ZOREAL',\n buttonContinue: 'ดำเนินการต่อด้วย ZOREAL',\n },\n // Tagalog\n tl: {\n title: 'I-scan para mag-sign in',\n titleIdentify: 'I-scan para i-verify ang iyong pagkakakilanlan',\n titlePresence: 'I-scan para patunayang tunay kang tao',\n titleApprove: 'I-approve sa iyong telepono',\n bodyScan: 'I-scan gamit ang camera ng iyong telepono o ang ZOREAL ID app.',\n bodyApprove: 'I-approve ang login sa iyong ZOREAL ID app.',\n bodyEnrolling: 'Tapusin muna ang pag-set up ng ZOREAL ID sa iyong telepono, pagkatapos ay i-approve ang login.',\n waiting: 'Naghihintay ng scan',\n waitingApproval: 'Naghihintay ng approval',\n expiresIn: 'Mag-e-expire sa {time}',\n secured: 'Proof-of-Human verification mula sa ZOREAL',\n noIdTitle: 'Wala ka pang ZOREAL ID?',\n noIdBody: 'I-scan ang parehong code para i-download ang app at gumawa ng iyong ZOREAL ID nang libre. Isang minuto lang ito.',\n cancel: 'Kanselahin',\n close: 'Isara',\n qrAlt: 'QR code para mag-sign in gamit ang ZOREAL',\n buttonContinue: 'Magpatuloy gamit ang ZOREAL',\n },\n // Türkçe\n tr: {\n title: 'Giriş için tarayın',\n titleIdentify: 'Kimliğinizi doğrulamak için tarayın',\n titlePresence: 'Gerçek bir insan olduğunuzu kanıtlamak için tarayın',\n titleApprove: 'Telefonunuzdan onaylayın',\n bodyScan: 'Telefonunuzun kamerasıyla veya ZOREAL ID uygulamasıyla tarayın.',\n bodyApprove: 'Girişi ZOREAL ID uygulamanızdan onaylayın.',\n bodyEnrolling: 'Telefonunuzda ZOREAL ID kurulumunu tamamlayın, ardından girişi onaylayın.',\n waiting: 'Tarama bekleniyor',\n waitingApproval: 'Onay bekleniyor',\n expiresIn: '{time} içinde sona erer',\n secured: 'ZOREAL tarafından Proof-of-Human doğrulaması',\n noIdTitle: 'Henüz ZOREAL ID\\'niz yok mu?',\n noIdBody: 'Uygulamayı indirmek ve ücretsiz bir tane oluşturmak için aynı kodu tarayın. Sadece bir dakikanızı alır.',\n cancel: 'İptal',\n close: 'Kapat',\n qrAlt: 'ZOREAL ile giriş yapmak için QR kodu',\n buttonContinue: 'ZOREAL ile devam et',\n },\n // Українська\n uk: {\n title: 'Скануйте для входу',\n titleIdentify: 'Скануйте, щоб підтвердити особу',\n titlePresence: 'Скануйте, щоб довести, що ви справжня людина',\n titleApprove: 'Підтвердьте на телефоні',\n bodyScan: 'Скануйте камерою телефону або додатком ZOREAL ID.',\n bodyApprove: 'Підтвердьте вхід у додатку ZOREAL ID.',\n bodyEnrolling: 'Завершіть налаштування ZOREAL ID на телефоні, а потім підтвердьте вхід.',\n waiting: 'Очікування сканування',\n waitingApproval: 'Очікування підтвердження',\n expiresIn: 'Спливає через {time}',\n secured: 'Перевірка Proof-of-Human від ZOREAL',\n noIdTitle: 'Ще немає ZOREAL ID?',\n noIdBody: 'Скануйте той самий код, щоб завантажити додаток і безкоштовно створити його. Це займе лише хвилину.',\n cancel: 'Скасувати',\n close: 'Закрити',\n qrAlt: 'QR-код для входу через ZOREAL',\n buttonContinue: 'Продовжити з ZOREAL',\n },\n // اردو\n ur: {\n title: 'لاگ اِن کرنے کے لیے اسکین کریں',\n titleIdentify: 'اپنی شناخت کی تصدیق کے لیے اسکین کریں',\n titlePresence: 'یہ ثابت کرنے کے لیے اسکین کریں کہ آپ ایک حقیقی انسان ہیں',\n titleApprove: 'اپنے فون پر منظوری دیں',\n bodyScan: 'اپنے فون کے کیمرے یا ZOREAL ID ایپ سے اسکین کریں۔',\n bodyApprove: 'اپنی ZOREAL ID ایپ میں لاگ اِن کی منظوری دیں۔',\n bodyEnrolling: 'اپنے فون پر ZOREAL ID کی سیٹ اپ مکمل کریں، پھر لاگ اِن کی منظوری دیں۔',\n waiting: 'اسکین کا انتظار',\n waitingApproval: 'منظوری کا انتظار',\n expiresIn: '{time} میں ختم ہوگا',\n secured: 'ZOREAL کی جانب سے Proof-of-Human تصدیق',\n noIdTitle: 'ابھی تک ZOREAL ID نہیں ہے؟',\n noIdBody: 'ایپ ڈاؤن لوڈ کرنے اور مفت میں ایک بنانے کے لیے وہی کوڈ اسکین کریں۔ اس میں صرف ایک منٹ لگتا ہے۔',\n cancel: 'منسوخ کریں',\n close: 'بند کریں',\n qrAlt: 'ZOREAL کے ساتھ لاگ اِن کرنے کے لیے QR کوڈ',\n buttonContinue: 'ZOREAL کے ساتھ جاری رکھیں',\n },\n // Tiếng Việt\n vi: {\n title: 'Quét để đăng nhập',\n titleIdentify: 'Quét để xác minh danh tính của bạn',\n titlePresence: 'Quét để chứng minh bạn là người thật',\n titleApprove: 'Phê duyệt trên điện thoại của bạn',\n bodyScan: 'Quét bằng camera điện thoại hoặc ứng dụng ZOREAL ID.',\n bodyApprove: 'Phê duyệt đăng nhập trong ứng dụng ZOREAL ID của bạn.',\n bodyEnrolling: 'Hoàn tất thiết lập ZOREAL ID trên điện thoại, sau đó phê duyệt đăng nhập.',\n waiting: 'Đang chờ quét mã',\n waitingApproval: 'Đang chờ phê duyệt',\n expiresIn: 'Hết hạn sau {time}',\n secured: 'Xác minh Proof-of-Human bởi ZOREAL',\n noIdTitle: 'Chưa có ZOREAL ID?',\n noIdBody: 'Quét cùng mã này để tải ứng dụng và tạo tài khoản miễn phí. Chỉ mất một phút.',\n cancel: 'Hủy',\n close: 'Đóng',\n qrAlt: 'Mã QR để đăng nhập bằng ZOREAL',\n buttonContinue: 'Tiếp tục với ZOREAL',\n },\n};\n\n/** Locales whose script runs right to left, so the dialog flips with `dir`. */\n// Only languages we actually carry. Listing an RTL language we do not\n// translate would flip the dialog for someone who is then shown the English\n// fallback — LTR text in an RTL container, which is worse than either alone.\nconst RTL = new Set(['ar', 'he', 'iw', 'ur']);\n\n/**\n * One BCP 47 tag to a translation, or undefined if we do not carry it.\n *\n * Chinese is the only case needing more than the primary subtag: `zh-Hans` /\n * `zh-CN` / `zh-SG` are Simplified, everything else `zh` is treated as\n * Traditional, matching how the pairing page splits them.\n */\n/**\n * Primary subtags that reach the same table under another name: superseded ISO\n * codes some platforms still emit, and the written standards we carry one entry\n * for. Without these a Norwegian browser sending `nb` gets English while `no`\n * sits right there in the table.\n */\nconst ALIASES: Record<string, string> = {\n nb: 'no', // Bokmål — what we actually wrote\n nn: 'no', // Nynorsk reader, served Bokmål: closer than English\n fil: 'tl', // Filipino / Tagalog\n iw: 'he', // superseded code for Hebrew, still emitted by some platforms\n in: 'id', // superseded code for Indonesian\n};\n\n/**\n * Spanish and Portuguese ship two variants each, and the split that matters is\n * not the language but the side of the Atlantic. A `es-MX` browser resolving to\n * peninsular Spanish is the kind of near-miss that reads as nobody having\n * thought about it, so the Latin American regions are named explicitly.\n */\nconst LATAM = new Set([\n 'ar', 'bo', 'cl', 'co', 'cr', 'cu', 'do', 'ec', 'gt', 'hn',\n 'mx', 'ni', 'pa', 'pe', 'pr', 'py', 'sv', 'uy', 've', '419',\n]);\n\nfunction lookup(locale: string): PairingStrings | undefined {\n const tag = locale.toLowerCase().replace(/_/g, '-');\n const parts = tag.split('-');\n const primary = ALIASES[parts[0]] ?? parts[0];\n const region = parts[1];\n\n // Script, not region, is what separates these two.\n if (primary === 'zh') {\n const simplified = /(^|-)(hans|cn|sg|my)(-|$)/.test(tag);\n return TRANSLATIONS[simplified ? 'zhs' : 'zht'];\n }\n if (primary === 'es' && region && LATAM.has(region)) return TRANSLATIONS['es-419'];\n if (primary === 'pt' && region === 'br') return TRANSLATIONS['pt-br'];\n\n return TRANSLATIONS[tag] ?? TRANSLATIONS[primary];\n}\n\n/**\n * What the browser says the person reads, best first. `languages` is the whole\n * ordered preference list, which matters: someone whose first choice we do not\n * carry may well have a second we do, and falling straight to English would\n * skip it.\n */\nfunction browserLocales(): string[] {\n if (typeof navigator === 'undefined') return [];\n const nav = navigator as Navigator & { languages?: readonly string[] };\n if (nav.languages && nav.languages.length) return [...nav.languages];\n return nav.language ? [nav.language] : [];\n}\n\n/**\n * The strings to render.\n *\n * An explicit `locale` (from the provider) wins outright: the host app knows\n * which language it is currently showing, and the modal must not disagree with\n * the page it opened on. With none given we follow the browser's own preference\n * list, so an integrator who never sets `locale` still gets a translated modal\n * instead of English-by-default. Anything we do not carry falls back to English\n * rather than rendering a key.\n */\nexport function strings(locale?: string): PairingStrings {\n if (locale) return lookup(locale) ?? en;\n for (const candidate of browserLocales()) {\n const hit = lookup(candidate);\n if (hit) return hit;\n }\n return en;\n}\n\nexport function isRtl(locale?: string): boolean {\n const tag = locale ?? browserLocales()[0];\n if (!tag) return false;\n return RTL.has(tag.toLowerCase().replace(/_/g, '-').split('-')[0]);\n}\n\n/** The one substitution the copy needs. */\nexport function interpolate(template: string, time: string): string {\n return template.replace('{time}', time);\n}\n","import type { PairingStrings } from './i18n';\nimport type { LoginIntent } from './types';\n\n/**\n * The scopes a relying party asks for in order to know who is signing in:\n * the identifier, and how to reach and address the person. Anything beyond\n * these is an attribute read from the identity document, and the dialog\n * should say that it is about to be shared rather than call it a sign-in.\n */\nconst SIGN_IN_SCOPES = new Set(['openid', 'email', 'profile.name']);\n\n/**\n * What the pairing dialog says it is for. An explicit intent wins. Otherwise\n * a request for document attributes is an identification; a request for the\n * identifier alone with a liveness capture is a presence check, since nothing\n * is being logged into; everything else is a sign-in.\n */\nexport function resolveIntent(\n intent: LoginIntent | undefined,\n scope: string | undefined,\n acrValues: string | readonly string[] | undefined\n): LoginIntent {\n if (intent) return intent;\n const scopes = (scope ?? 'openid').split(/\\s+/).filter(Boolean);\n if (scopes.some((s) => !SIGN_IN_SCOPES.has(s))) return 'identify';\n const acr = typeof acrValues === 'string' ? acrValues.split(/\\s+/) : (acrValues ?? []);\n if (scopes.every((s) => s === 'openid') && acr.includes('zoreal.live')) return 'presence';\n return 'sign-in';\n}\n\n/** The unscanned-code title for an intent. */\nexport function titleFor(t: PairingStrings, intent: LoginIntent): string {\n if (intent === 'identify') return t.titleIdentify;\n if (intent === 'presence') return t.titlePresence;\n return t.title;\n}\n","/**\n * The pairing modal's stylesheet, injected once on first mount.\n *\n * Why a stylesheet and not inline styles: the modal needs hover, focus-visible,\n * keyframes, `prefers-color-scheme` and `prefers-reduced-motion`. None of those\n * exist as inline style properties, and a component that silently drops its\n * focus ring and its reduced-motion fallback is not shippable in a sign-in\n * flow.\n *\n * Why injected and not a `.css` file the integrator imports: a required import\n * step is a required support ticket. Plenty of hosts (Next.js app dir, CRA,\n * plain Vite, an app with no CSS pipeline at all) treat package CSS\n * differently, and the modal has to look the same in all of them.\n *\n * Every selector is prefixed `zrl-` and every declaration is scoped under one\n * of those classes, so nothing here can reach the host's markup. Values are\n * literal rather than inherited for the same reason: a host page with an\n * aggressive reset must not be able to break the layout of a dialog the person\n * is being asked to authenticate in. Font family is the one exception — it\n * inherits the host's UI font so the modal belongs to the page it opens on.\n */\n\nconst PREFIX = 'zrl';\nexport const cx = (name: string) => `${PREFIX}-${name}`;\n\nexport const STYLE_ELEMENT_ID = 'zoreal-pairing-styles';\n\n/**\n * Palette. `light`/`dark` force a theme, `auto` follows the OS. The tokens are\n * defined three times rather than once with overrides so a forced theme never\n * depends on media-query specificity to win.\n */\nconst LIGHT = `\n --zrl-scrim: rgba(16, 18, 27, 0.45);\n --zrl-surface: #ffffff;\n --zrl-surface-sunken: #f6f7f9;\n --zrl-ink: #16181c;\n --zrl-ink-soft: #4a4f57;\n --zrl-ink-mute: #6b7078;\n --zrl-line: #e4e6ea;\n --zrl-line-soft: #eef0f3;\n --zrl-accent: #00b4d9;\n --zrl-accent-soft: #dcf3fa;\n --zrl-accent-ink: #04698a;\n --zrl-urgent: #b4761a;\n --zrl-qr-bg: #ffffff;\n --zrl-qr-filter: none;\n --zrl-qr-spent-filter: blur(3px);\n --zrl-qr-blend: normal;\n --zrl-shadow: 0 1px 2px rgba(16, 18, 27, 0.06), 0 20px 50px -12px rgba(16, 18, 27, 0.3);\n --zrl-ring: rgba(16, 18, 27, 0.07);\n /* The light on the QR well's edge. Brand blue on both grounds, a lighter\n tint at the head; only its strength is themed, see the dark block. */\n --zrl-beam: #00b4d9;\n --zrl-beam-head: #7fe0f4;\n --zrl-beam-line: 2px;\n --zrl-glow-core: 4px;\n --zrl-glow-reach: 24px;\n --zrl-glow-blur: 8px;\n --zrl-glow-opacity: 0.6;\n`;\n\nconst DARK = `\n --zrl-scrim: rgba(0, 0, 0, 0.62);\n --zrl-surface: #17191d;\n --zrl-surface-sunken: #1f2226;\n --zrl-ink: #f4f5f7;\n --zrl-ink-soft: #b3b8c0;\n --zrl-ink-mute: #8b9199;\n --zrl-line: #2c3036;\n --zrl-line-soft: #24272c;\n --zrl-accent: #34c9e8;\n --zrl-accent-soft: #0d3b47;\n --zrl-accent-ink: #7fdcf0;\n --zrl-urgent: #e0a952;\n /* The code is drawn light on the dark surface: the panel is transparent\n and the image is inverted and screened, so only the modules and the\n mark show. */\n --zrl-qr-bg: transparent;\n --zrl-qr-filter: invert(1);\n --zrl-qr-spent-filter: invert(1) blur(3px);\n --zrl-qr-blend: screen;\n --zrl-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 20px 50px -12px rgba(0, 0, 0, 0.65);\n --zrl-ring: rgba(255, 255, 255, 0.1);\n /* A glow that reads on a white card disappears on a dark one: the light\n here is brighter and wider, and its halo reaches further out. */\n --zrl-beam: #22c8ec;\n --zrl-beam-head: #c2f3fc;\n --zrl-beam-line: 3px;\n --zrl-glow-core: 6px;\n --zrl-glow-reach: 32px;\n --zrl-glow-blur: 10px;\n --zrl-glow-opacity: 0.85;\n`;\n\nexport const CSS = `\n.${PREFIX}-root { ${LIGHT} }\n.${PREFIX}-root[data-theme=\"dark\"] { ${DARK} }\n@media (prefers-color-scheme: dark) {\n .${PREFIX}-root[data-theme=\"auto\"] { ${DARK} }\n}\n\n.${PREFIX}-scrim {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: grid;\n place-items: center;\n overflow-y: auto;\n padding: 16px;\n background: var(--zrl-scrim);\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n font-family: inherit;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-card {\n position: relative;\n box-sizing: border-box;\n width: 100%;\n max-width: 380px;\n border-radius: 16px;\n background: var(--zrl-surface);\n color: var(--zrl-ink);\n box-shadow: var(--zrl-shadow);\n outline: 1px solid var(--zrl-ring);\n outline-offset: -1px;\n text-align: center;\n animation: ${PREFIX}-rise 300ms cubic-bezier(0.23, 1, 0.32, 1) both;\n}\n\n.${PREFIX}-body { padding: 28px 24px 20px; }\n\n.${PREFIX}-lockup { display: block; margin: 0 auto; color: var(--zrl-ink); }\n\n.${PREFIX}-title {\n margin: 18px 0 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n line-height: 1.3;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-body-text {\n margin: 6px auto 0;\n max-width: 30ch;\n font-size: 14px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-qr-well {\n position: relative;\n display: grid;\n place-items: center;\n box-sizing: border-box;\n width: 204px;\n height: 204px;\n margin: 20px auto 0;\n padding: 12px;\n border: 1px solid var(--zrl-line);\n border-radius: var(--zrl-radius);\n background: var(--zrl-qr-bg);\n /* The light on the edge takes its shape from here and its colour and\n strength from the theme tokens above. One lap in 4s on every tier. */\n --zrl-radius: 16px;\n --zrl-beam-time: 4s;\n}\n\n/* The light on the well's edge: a short comet running along the border, with\n a soft glow outside it. Three overlays inside the well, each masked so the\n comet can only ever paint where its mask allows, and the white interior lies\n outside every mask: nothing here can reach the quiet zone a camera needs,\n whatever the comet is doing. The mask is the padding box cut out of the\n border box, a transparent layer clipped to the padding box intersected\n with a solid one clipped to the border box. The prefixed form is for Chrome\n before 120 and Safari before 15.4; the unprefixed one, declared after it,\n wins everywhere else.\n\n qr-beam keeps a thin ring on the border line: the comet itself.\n qr-beam-glow is the glow: a wide band outside the well that blurs whatever\n is inside it, and inside it qr-beam-glow-band keeps a 3px ring with a\n second copy of the comet. The blur has to sit on the parent because a\n filter is applied before a mask: blurred on the band itself, the glow\n would be cut back to the band's own edge. On the parent it runs after the\n band has clipped the comet thin and before the parent's mask cuts away the\n inward half, which is what makes it fade outward and never over the QR.\n All three share one containing block, the well's padding box, so the two\n comets ride the same path; the spent badge is a later sibling and paints\n above them. */\n.${PREFIX}-qr-beam,\n.${PREFIX}-qr-beam-glow,\n.${PREFIX}-qr-beam-glow-band {\n position: absolute;\n inset: calc(0px - var(--zrl-beam-line));\n border: var(--zrl-beam-line) solid transparent;\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-beam-line));\n pointer-events: none;\n -webkit-mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n -webkit-mask-clip: padding-box, border-box;\n -webkit-mask-composite: source-in;\n mask: linear-gradient(transparent, transparent), linear-gradient(#fff, #fff);\n mask-clip: padding-box, border-box;\n mask-composite: intersect;\n}\n.${PREFIX}-qr-beam-glow {\n inset: calc(0px - var(--zrl-glow-reach));\n border-width: var(--zrl-glow-reach);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-reach));\n filter: blur(var(--zrl-glow-blur));\n opacity: var(--zrl-glow-opacity);\n will-change: filter;\n}\n.${PREFIX}-qr-beam-glow-band {\n inset: calc(0px - var(--zrl-glow-core));\n border-width: var(--zrl-glow-core);\n border-radius: calc(var(--zrl-radius) - 1px + var(--zrl-glow-core));\n}\n\n/* At rest the edge holds a dim, even blue: a 1px line on the border and, from\n the glow band, a soft halo outside it. Hidden while the comet runs, so the\n border reads as the well's own line with a light passing over it; shown\n once the light has stopped. Every path below ends here, which is what\n makes them look the same at rest. */\n.${PREFIX}-qr-beam::before,\n.${PREFIX}-qr-beam-glow-band::before {\n content: '';\n position: absolute;\n inset: -50%;\n background: var(--zrl-beam);\n opacity: 0;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* The moving light: an oversized square carrying a conic sweep, rotated\n whole. A transform animation runs on the compositor, so the light keeps\n moving while the page is busy; animating the gradient angle instead\n repaints every frame on the main thread and stutters. */\n.${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-beam-glow-band::after {\n content: '';\n position: absolute;\n inset: -50%;\n background: conic-gradient(\n from 0deg,\n transparent 0deg 220deg,\n var(--zrl-beam) 330deg,\n var(--zrl-beam-head) 348deg,\n transparent 356deg 360deg\n );\n animation: ${PREFIX}-orbit var(--zrl-beam-time) linear infinite;\n will-change: transform;\n transition: opacity 400ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Spent: the light stops where it is and fades, and the edge settles to the\n dim glow. Paused rather than removed, so it does not jump back to its start\n on the way out. */\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::after,\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::after {\n animation-play-state: paused;\n opacity: 0;\n}\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam::before { opacity: 0.55; }\n.${PREFIX}-qr-well[data-spent=\"true\"] .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7; }\n\n.${PREFIX}-qr {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: 8px;\n filter: var(--zrl-qr-filter);\n mix-blend-mode: var(--zrl-qr-blend);\n transition: filter 300ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 300ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 300ms cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n/* Once the code is claimed the QR is spent. Blurring it out rather than\n swapping it keeps one object on screen through the state change, so the eye\n reads a transformation instead of two things trading places. */\n.${PREFIX}-qr[data-spent=\"true\"] { opacity: 0.2; filter: var(--zrl-qr-spent-filter); transform: scale(0.96); }\n\n.${PREFIX}-qr-overlay {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n animation: ${PREFIX}-fade 200ms ease-out both;\n}\n\n.${PREFIX}-qr-badge {\n display: grid;\n place-items: center;\n width: 56px;\n height: 56px;\n border-radius: 999px;\n background: var(--zrl-accent-soft);\n color: var(--zrl-accent-ink);\n}\n\n.${PREFIX}-status {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 20px;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink);\n}\n\n.${PREFIX}-dot { position: relative; display: grid; place-items: center; width: 8px; height: 8px; }\n.${PREFIX}-dot i {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 999px;\n background: var(--zrl-accent);\n font-style: normal;\n}\n.${PREFIX}-dot i:first-child { animation: ${PREFIX}-ping 1.8s cubic-bezier(0.23, 1, 0.32, 1) infinite; }\n\n.${PREFIX}-timer {\n margin: 4px 0 0;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--zrl-ink-mute);\n transition: color 200ms ease-out;\n}\n.${PREFIX}-timer[data-urgent=\"true\"] { color: var(--zrl-urgent); }\n\n.${PREFIX}-help {\n padding: 14px 24px;\n border-top: 1px solid var(--zrl-line-soft);\n background: var(--zrl-surface-sunken);\n border-radius: 0;\n}\n.${PREFIX}-help-title { margin: 0; font-size: 12px; font-weight: 600; color: var(--zrl-ink); }\n.${PREFIX}-help-body {\n margin: 4px auto 0;\n max-width: 34ch;\n font-size: 12px;\n line-height: 1.55;\n color: var(--zrl-ink-soft);\n}\n\n.${PREFIX}-footer { padding: 12px; border-top: 1px solid var(--zrl-line-soft); }\n\n.${PREFIX}-cancel {\n display: block;\n width: 100%;\n padding: 10px;\n border: 0;\n border-radius: 12px;\n background: transparent;\n font: inherit;\n font-size: 14px;\n font-weight: 500;\n color: var(--zrl-ink-soft);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-cancel:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-cancel:active { transform: scale(0.99); }\n\n.${PREFIX}-secured {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n margin: 6px 0 0;\n font-size: 12px;\n color: var(--zrl-ink-mute);\n text-decoration: none;\n border-radius: 6px;\n transition: color 150ms ease-out;\n}\n.${PREFIX}-secured:hover { color: var(--zrl-ink); }\n\n.${PREFIX}-close {\n position: absolute;\n top: 12px;\n inset-inline-end: 12px;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--zrl-ink-mute);\n cursor: pointer;\n transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;\n}\n.${PREFIX}-close:hover { background: var(--zrl-surface-sunken); color: var(--zrl-ink); }\n.${PREFIX}-close:active { transform: scale(0.95); }\n\n.${PREFIX}-card :focus-visible {\n outline: 2px solid var(--zrl-accent);\n outline-offset: 2px;\n}\n\n@keyframes ${PREFIX}-fade { from { opacity: 0 } to { opacity: 1 } }\n@keyframes ${PREFIX}-rise {\n from { opacity: 0; transform: translateY(10px) scale(0.98) }\n to { opacity: 1; transform: none }\n}\n@keyframes ${PREFIX}-ping {\n 0% { transform: scale(1); opacity: 0.5 }\n 70%, 100% { transform: scale(2.6); opacity: 0 }\n}\n\n@keyframes ${PREFIX}-orbit { to { transform: rotate(360deg) } }\n/* THE BUSY RING. The light of the QR well, around any control that is\n waiting on the provider: the button on a phone between the tap and the\n hand-over to the app. The well's sweep is a cone from the centre, which is\n even on a square and useless on a wide button: it crawls along the long\n sides and lights two edges at once near the ends. So here the light is a\n dash on an SVG outline, which moves at one speed the whole way round\n whatever the shape, drawn with the well's tokens: its colour and head\n tint, its line width, its halo, its four second lap. The outline's length\n is measured by the component and set as --zrl-ring-len, and every dash\n and offset is a fraction of it, because pathLength does not scale dash\n values given from CSS. A stroke cannot fade along its length, so the tail\n is a stack of dashes sharing one head, each shorter and more opaque than\n the one under it, with opacities chosen so the stack composes to a\n straight fade from the head to nothing three tenths of the way back; the\n component sets each layer's length, offset and opacity. Shown only while\n busy. */\n.${PREFIX}-ring {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n --zrl-beam-time: 4s;\n}\n.${PREFIX}-ring-svg {\n position: absolute;\n inset: -4px;\n width: calc(100% + 8px);\n height: calc(100% + 8px);\n overflow: visible;\n pointer-events: none;\n opacity: 0;\n transition: opacity 200ms ease-out;\n}\n.${PREFIX}-ring[data-busy=\"true\"] > .${PREFIX}-ring-svg { opacity: 1; }\n/* The same light as an overlay in the document body, placed over a site's\n own control by the package itself (busy.ts): nothing of the site's\n markup or CSS is touched, and neither an ancestor's overflow nor a\n selector on the control's parent is affected. */\n.${PREFIX}-ring-overlay {\n position: fixed;\n display: block;\n z-index: 2147483000;\n pointer-events: none;\n}\n.${PREFIX}-ring-svg rect {\n --zrl-l: var(--zrl-ring-len, 600px);\n x: 2px;\n y: 2px;\n width: calc(100% - 4px);\n height: calc(100% - 4px);\n fill: none;\n stroke: var(--zrl-beam);\n stroke-width: var(--zrl-beam-line);\n stroke-linecap: round;\n stroke-dashoffset: var(--zrl-s, 0px);\n animation: ${PREFIX}-dash var(--zrl-beam-time) linear infinite;\n}\n.${PREFIX}-ring-head { stroke: var(--zrl-beam-head); }\n.${PREFIX}-ring-halo {\n stroke-width: calc(var(--zrl-glow-core) * 2 + var(--zrl-beam-line));\n filter: blur(var(--zrl-glow-blur));\n}\n@keyframes ${PREFIX}-dash {\n from { stroke-dashoffset: var(--zrl-s, 0px); }\n to { stroke-dashoffset: calc(var(--zrl-s, 0px) - var(--zrl-l)); }\n}\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-ring-svg rect { animation: none; stroke-dasharray: none; opacity: 0.45; }\n .${PREFIX}-ring-halo, .${PREFIX}-ring-head { display: none; }\n}\n\n\n@media (prefers-reduced-motion: reduce) {\n .${PREFIX}-scrim,\n .${PREFIX}-card,\n .${PREFIX}-qr-overlay { animation: none }\n .${PREFIX}-dot i:first-child { animation: none; opacity: 0.35 }\n .${PREFIX}-qr,\n .${PREFIX}-cancel,\n .${PREFIX}-close,\n .${PREFIX}-timer { transition: none }\n /* No travelling light; the edge keeps its dim static glow instead. */\n .${PREFIX}-qr-beam::after,\n .${PREFIX}-qr-beam-glow-band::after { animation: none; opacity: 0 }\n .${PREFIX}-qr-beam::before { opacity: 0.55 }\n .${PREFIX}-qr-beam-glow-band::before { opacity: 0.7 }\n}\n`;\n\n/**\n * Injected at module scope on first import in a DOM, not per render: the tag is\n * idempotent by id, so a host with two provider instances (or a hot reload)\n * still ends up with exactly one.\n */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ELEMENT_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n}\n","import { useMemo, useState, type CSSProperties } from 'react';\nimport { useZorealOAuth } from './context';\nimport { strings } from './i18n';\nimport { useZorealFlow } from './useZorealLogin';\nimport { ZorealMark } from './mark';\nimport { ZorealBusyRing } from './ring';\nimport type {\n NonOAuthError,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginProps,\n} from './types';\n\n/**\n * The drop-in button. In its default browser-direct flow it receives no\n * access token, so it returns the pseudonymous identity only; personal data\n * needs `flow: 'auth-code'`, which hands your backend the code instead\n * (supported here since 0.2.8, same discriminator as useZorealLogin).\n *\n * The copy is neutral: the button asserts nothing about a person who has not\n * yet authenticated. Styling is inline and self-contained; no stylesheet, no\n * font, no external asset, because this renders on a sign-in page.\n *\n * The QR itself is no longer drawn here. `ZorealOAuthProvider` renders the\n * pairing modal for every flow, so the button and `useZorealLogin` get the\n * same dialog and it only had to be designed, translated and made accessible\n * once. Opt out with `pairingUI=\"none\"` on the provider.\n */\n\n// The default label is translated with the modal's own copy; the four\n// alternatives are English, as they were.\nconst TEXTS: Record<NonNullable<ZorealLoginProps['text']>, string | null> = {\n continue_with: null,\n signin_with: 'Sign in with ZOREAL',\n signup_with: 'Sign up with ZOREAL',\n signin: 'Sign in',\n verify_with: 'Verify with ZOREAL ID',\n};\n\n/* The house button: 14px medium text, a 22px mark, 12px between them, 14px\n above and below, 20px at the sides, 12px corners. The smaller sizes scale\n that down; they do not change its proportions. */\nconst SIZES = {\n large: { height: 50, font: 14, pad: 20, mark: 22, gap: 12, radius: 12 },\n medium: { height: 42, font: 14, pad: 16, mark: 20, gap: 10, radius: 10 },\n small: { height: 34, font: 12, pad: 12, mark: 16, gap: 8, radius: 8 },\n} as const;\n\nexport function ZorealLogin(props: ZorealLoginProps) {\n const {\n onSuccess,\n onError,\n containerProps,\n type = 'standard',\n theme = 'outline',\n size = 'large',\n text = 'continue_with',\n shape = 'rectangular',\n logo_alignment = 'center',\n width,\n click_listener,\n flow = 'browser-direct',\n ...request\n } = props;\n\n const { locale } = useZorealOAuth();\n const label = TEXTS[text] ?? strings(locale).buttonContinue;\n\n // Busy from the tap until the flow ends. On a phone the tap creates the\n // pairing and then sends the tab to the app, one round trip later; the\n // button is disabled and a light runs round it for that gap, so the tap is\n // seen to have worked and cannot start a second pairing. On a computer it\n // stays busy while the dialog is open. Never cleared by a navigation away:\n // the page is gone with it.\n const [busy, setBusy] = useState(false);\n\n const { login } = useZorealFlow({\n ...request,\n flow,\n onCredential:\n flow === 'browser-direct'\n ? (r: ZorealCredentialResponse) => {\n setBusy(false);\n (onSuccess as (r: ZorealCredentialResponse) => void)(r);\n }\n : undefined,\n onCode:\n flow === 'auth-code'\n ? (r: ZorealCodeResponse) => {\n setBusy(false);\n (onSuccess as unknown as (r: ZorealCodeResponse) => void)(r);\n }\n : undefined,\n onError: (e) => {\n setBusy(false);\n onError?.({ type: 'unknown', description: e.description ?? e.error });\n },\n onNonOAuthError: (e: NonOAuthError) => {\n setBusy(false);\n onError?.(e);\n },\n });\n\n const s = SIZES[size];\n const radius = shape === 'pill' ? s.height / 2 : shape === 'square' ? 4 : s.radius;\n // The mark keeps the brand blue wherever it can be seen. On the brand-blue\n // filled button it cannot, so there it takes the label's white.\n const brandMark = theme !== 'filled';\n const style: CSSProperties = useMemo(\n () => ({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: logo_alignment === 'center' ? 'center' : 'flex-start',\n gap: s.gap,\n height: s.height,\n padding: `0 ${s.pad}px`,\n width,\n fontSize: s.font,\n fontFamily: 'inherit',\n fontWeight: 500,\n cursor: 'pointer',\n borderRadius: radius,\n ...(theme === 'outline'\n ? { background: '#ffffff', color: '#16181c', border: '1px solid #e2e4de' }\n : theme === 'filled_black'\n ? { background: '#111', color: '#fff', border: '1px solid #111' }\n : { background: '#00b4d9', color: '#fff', border: '1px solid #00b4d9' }),\n }),\n [logo_alignment, s, radius, theme, width]\n );\n\n return (\n <div {...containerProps}>\n <ZorealBusyRing busy={busy} radius={radius} theme={theme === 'outline' ? 'auto' : 'light'}>\n <button\n type=\"button\"\n style={busy ? { ...style, cursor: 'progress' } : style}\n disabled={busy}\n aria-busy={busy}\n onClick={() => {\n if (busy) return;\n click_listener?.();\n setBusy(true);\n login();\n }}\n >\n <ZorealMark size={s.mark} brand={brandMark} />\n {type === 'standard' && label}\n </button>\n </ZorealBusyRing>\n </div>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useZorealOAuth, useZorealPairingHost } from './context';\nimport { controlFrom, holdBusy } from './busy';\nimport { resolveIntent } from './intent';\nimport {\n forgetReturnFlow,\n isReturnDone,\n markReturnDone,\n peekReturnFlow,\n pendingReturnId,\n returnToUrl,\n saveReturnFlow,\n} from './return';\nimport { unsafeClaims } from './jwt';\nimport {\n FlowAbandonedError,\n OAuthFlowError,\n exchangeCode,\n pollUntilApproved,\n qrRefreshSecondsOf,\n resolveDisplay,\n sameDeviceStartUrl,\n startPairing,\n} from './pairing';\nimport {\n challengeS256,\n challengeS256Sync,\n generateRequestId,\n generateState,\n generateVerifier,\n} from './pkce';\nimport type {\n AcrValue,\n AuthCodeFlowOptions,\n BrowserDirectFlowOptions,\n ErrorCode,\n NonOAuthError,\n PairingState,\n SelectBy,\n ZorealCodeResponse,\n ZorealCredentialResponse,\n ZorealLoginRequestOptions,\n} from './types';\n\nexport interface ActivePairing {\n requestId: string;\n pairUrl: string;\n /**\n * The QR image to show right now. It MOVES: a QR pairing's code is a frame\n * the provider rotates every few seconds, so read this from the latest\n * state rather than holding the first value.\n */\n qrUrl: string;\n state: PairingState;\n /** True when display resolved to the app link rather than the QR. */\n appLink: boolean;\n cancel: () => void;\n}\n\n/** Returns already taken up in this page load, so a second hook instance does not repeat one. */\nconst resumedReturns = new Set<string>();\n\ninterface FlowInternals {\n /** Non-null while a pairing is on screen. ZorealLogin renders from this. */\n pairing: ActivePairing | null;\n}\n\n/**\n * The internal option shape: one flow discriminator, one success callback per\n * mode. The public API keeps Google's single overloaded onSuccess; this type\n * exists because an intersection of those two signatures is uninhabitable, and\n * the mapping from public to internal happens once, in useZorealLogin.\n */\nexport interface InternalFlowOptions extends ZorealLoginRequestOptions {\n flow: 'browser-direct' | 'auth-code';\n redirect_uri?: string;\n onCredential?: (response: ZorealCredentialResponse) => void;\n onCode?: (response: ZorealCodeResponse) => void;\n onError?: (error: Pick<NonOAuthError, 'description'> & { error: ErrorCode }) => void;\n onNonOAuthError?: (error: NonOAuthError) => void;\n}\n\n/**\n * The one flow, shared by the hook and the button. Starts a pairing, exposes\n * it for rendering, polls, and finishes per mode: browser-direct exchanges the\n * code here (public client, PKCE, no secret) and hands over an ID token;\n * auth-code hands the code and the PKCE verifier to the caller, whose backend\n * does the exchange with its client authentication.\n */\nexport function useZorealFlow(options: InternalFlowOptions): {\n login: (event?: unknown) => void;\n internals: FlowInternals;\n} {\n const { clientId, issuer, locale } = useZorealOAuth();\n const [pairing, setPairing] = useState<ActivePairing | null>(null);\n // Null when the provider is set to pairingUI: 'none', which is the caller\n // saying they render the QR themselves. Held in a ref so `login` keeps its\n // identity across renders.\n const publish = useZorealPairingHost();\n const publishRef = useRef(publish);\n publishRef.current = publish;\n const abortRef = useRef<AbortController | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n // Set by a cancel the person made (the dialog's close, its Cancel, Escape,\n // a tap outside, its timeout), as opposed to the unmount. The abort that\n // follows is then reported as `popup_closed`, so a button that went busy\n // on the tap has something to recover on.\n const closedByPerson = useRef(false);\n\n // A component unmounting mid-login must stop the poll: the provider cancels\n // over-polled requests, and an orphaned interval is exactly how one happens.\n useEffect(\n () => () => {\n abortRef.current?.abort();\n publishRef.current?.(null);\n releaseRef.current();\n },\n []\n );\n\n // THE RETURN. When the ZOREAL ID app reopens the page after a same-device\n // approval, the pairing is named in the fragment and the flow that started\n // it is in local storage. This finishes it where the page stands, once per\n // page load whichever hook instance mounts first, and reports through the\n // same callbacks the tap would have.\n useEffect(() => {\n const id = pendingReturnId();\n if (!id || resumedReturns.has(id)) return;\n const saved = peekReturnFlow(id);\n if (!saved || saved.clientId !== clientId) return;\n forgetReturnFlow(id);\n resumedReturns.add(id);\n const controller = new AbortController();\n abortRef.current = controller;\n void (async () => {\n const opts = optionsRef.current;\n try {\n const code = await pollUntilApproved(issuer, id, undefined, controller.signal, {\n tolerateUnknownUntil: Date.now() + 5_000,\n });\n if (saved.flow === 'auth-code') {\n opts.onCode?.({\n code,\n scope: saved.scope,\n app_state: saved.appState,\n code_verifier: saved.verifier,\n nonce: saved.nonce,\n });\n } else {\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: saved.verifier,\n client_id: clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n opts.onCredential?.({\n credential: tokens.id_token,\n clientId,\n select_by: 'app_link',\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n });\n }\n markReturnDone(id);\n } catch (e) {\n if (e instanceof DOMException && e.name === 'AbortError') return;\n if (e instanceof FlowAbandonedError) {\n opts.onNonOAuthError?.(e.reason);\n return;\n }\n if (e instanceof OAuthFlowError) {\n opts.onError?.({ error: e.error, description: e.description });\n return;\n }\n opts.onNonOAuthError?.({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n })();\n }, [clientId, issuer]);\n\n // The site's control, held busy for the whole login: taken from the click\n // event `login` is called with, let go on every exit and on unmount.\n const releaseRef = useRef<() => void>(() => {});\n\n const login = useCallback((event?: unknown) => {\n const opts = optionsRef.current;\n const control = controlFrom(event);\n const run = async () => {\n abortRef.current?.abort();\n releaseRef.current();\n releaseRef.current = control ? holdBusy(control) : () => {};\n const release = () => {\n releaseRef.current();\n releaseRef.current = () => {};\n };\n const controller = new AbortController();\n abortRef.current = controller;\n\n const flow = opts.flow;\n const verifier = generateVerifier();\n const state = generateState();\n const nonce = generateState();\n\n // Which surface this login will use is decided HERE, before the pairing\n // exists, because the provider binds the pairing to it: a QR pairing\n // gets a rotating code, a link pairing gets a start token on its URL and\n // no QR at all. Deciding afterwards would mean asking for one surface\n // and showing another.\n const display = resolveDisplay(opts.display);\n const useAppLink = display === 'link';\n const intent = resolveIntent(opts.intent, opts.scope, opts.acr_values);\n\n try {\n let code: string;\n let selectBy: SelectBy = 'device';\n let returnId: string | null = null;\n\n if (useAppLink) {\n // THE TAP IS THE NAVIGATION. Nothing is awaited between the click\n // and the assignment below: a browser hands a universal link to an\n // app only inside a navigation the person began, and an await here\n // would put the navigation outside it, where the link loads as a\n // web page instead (see sameDeviceStartUrl). The provider creates\n // the pairing and redirects to the link; the page stays and polls\n // the token it chose, tolerating \"no such pairing\" for as long as\n // the provider may still be answering the navigation. No modal:\n // there is no code to scan and the page is the button that was\n // tapped.\n const requestId = generateRequestId();\n // The way back: the app reopens this page once the holder has\n // approved, in a new tab, and the hook there finishes the sign-in\n // from what is saved here. This tab keeps polling too; whichever\n // finishes first marks the flow done and the other stands down.\n saveReturnFlow({\n v: 1,\n issuer,\n clientId,\n flow,\n verifier,\n nonce,\n state,\n scope: opts.scope ?? 'openid',\n appState: opts.app_state,\n requestId,\n createdAt: Date.now(),\n });\n returnId = requestId;\n const startUrl = sameDeviceStartUrl(issuer, {\n client_id: clientId,\n scope: opts.scope ?? 'openid',\n state,\n nonce,\n code_challenge: challengeS256Sync(verifier),\n redirect_uri: flow === 'auth-code' ? opts.redirect_uri : undefined,\n acr_values: Array.isArray(opts.acr_values) ? opts.acr_values.join(' ') : opts.acr_values,\n max_age: opts.max_age,\n prompt: opts.prompt,\n locale,\n request_id: requestId,\n origin: window.location.origin,\n return_to: returnToUrl(),\n });\n selectBy = 'app_link';\n const cancel = () => {\n closedByPerson.current = true;\n controller.abort();\n setPairing(null);\n };\n const surface = { pairUrl: startUrl, appLink: true, intent, cancel };\n const active: ActivePairing = {\n requestId,\n pairUrl: startUrl,\n qrUrl: '',\n state: { status: 'pending', ...surface },\n appLink: true,\n cancel,\n };\n setPairing(active);\n opts.onPairingStateChange?.(active.state);\n window.location.assign(startUrl);\n\n code = await pollUntilApproved(\n issuer,\n requestId,\n (s) => {\n const enriched = { ...s, ...surface };\n setPairing((p) => (p && p.requestId === requestId ? { ...p, state: enriched } : p));\n opts.onPairingStateChange?.(enriched);\n },\n controller.signal,\n { tolerateUnknownUntil: Date.now() + 15_000 }\n );\n if (isReturnDone(requestId)) {\n // The page the app reopened has finished this sign-in. This tab\n // was left behind; it stands down rather than spend a used code.\n throw new DOMException('aborted', 'AbortError');\n }\n } else {\n const started = await startPairing(issuer, {\n client_id: clientId,\n scope: opts.scope ?? 'openid',\n state,\n nonce,\n code_challenge: await challengeS256(verifier),\n redirect_uri: flow === 'auth-code' ? opts.redirect_uri : undefined,\n acr_values: Array.isArray(opts.acr_values)\n ? opts.acr_values.join(' ')\n : opts.acr_values,\n max_age: opts.max_age,\n prompt: opts.prompt,\n locale,\n display: 'qr',\n });\n\n if ('code' in started) {\n // prompt=none resolved silently: consented sector, live session.\n code = started.code;\n selectBy = 'session';\n } else {\n selectBy = 'qr';\n const qrRefreshSeconds = qrRefreshSecondsOf(started);\n\n const cancel = () => {\n closedByPerson.current = true;\n controller.abort();\n setPairing(null);\n publishRef.current?.(null);\n };\n // Everything a caller-rendered pairing UI needs, on every state it\n // sees: the QR flow cannot complete unless SOMETHING renders\n // pairUrl, and for the auth-code flow that something is the caller.\n // qrUrl is deliberately NOT in here: it is the one field that\n // changes during the pairing, and a fixed copy spread over every\n // state would paste the first frame back on top of the current one.\n const surface = {\n pairUrl: started.pair_url,\n appLink: false,\n intent,\n cancel,\n qrRefreshSeconds,\n };\n // The frame on screen. The poll replaces it every qrRefreshSeconds;\n // everything published in between reuses whatever is current, so the\n // three channels below never disagree about which code is showing.\n let qrUrl = `${issuer}/pair/${encodeURIComponent(started.request_id)}/qr.svg`;\n const active: ActivePairing = {\n requestId: started.request_id,\n pairUrl: surface.pairUrl,\n qrUrl,\n state: { status: 'pending', expiresIn: started.expires_in, qrUrl, ...surface },\n appLink: false,\n cancel,\n };\n setPairing(active);\n if (!useAppLink) {\n publishRef.current?.({ state: active.state, qrUrl, intent, cancel });\n }\n // The initial state, immediately: the first poll response is one\n // round-trip away, and a UI that waits for it opens visibly empty.\n opts.onPairingStateChange?.(active.state);\n\n if (useAppLink) {\n // The universal link, in the same tab: the app claims it, and with\n // no app installed the same URL is the real pairing page which can\n // enrol. A popup here would be blocked more often than it would\n // help. The URL carries the start token that binds the claim to\n // this browser, so it is used exactly as the provider gave it.\n window.location.assign(started.pair_url);\n }\n\n code = await pollUntilApproved(\n issuer,\n started.request_id,\n (s) => {\n // A refresh arrives as a state carrying a new qrUrl; a poll\n // arrives without one and keeps the frame already showing.\n if (s.qrUrl) qrUrl = s.qrUrl;\n const enriched = { ...s, ...surface, qrUrl };\n setPairing((p) =>\n p && p.requestId === started.request_id ? { ...p, qrUrl, state: enriched } : p\n );\n if (!useAppLink) {\n publishRef.current?.({ state: enriched, qrUrl, intent, cancel });\n }\n opts.onPairingStateChange?.(enriched);\n },\n controller.signal,\n { qrRefreshSeconds }\n );\n }\n\n }\n\n setPairing(null);\n publishRef.current?.(null);\n if (returnId) markReturnDone(returnId);\n\n release();\n if (flow === 'auth-code') {\n opts.onCode?.({\n code,\n scope: opts.scope ?? 'openid',\n app_state: opts.app_state,\n code_verifier: verifier,\n nonce,\n });\n return;\n }\n\n const tokens = await exchangeCode(issuer, {\n code,\n code_verifier: verifier,\n client_id: clientId,\n });\n const claims = unsafeClaims(tokens.id_token);\n const response: ZorealCredentialResponse = {\n credential: tokens.id_token,\n clientId,\n select_by: selectBy,\n acr: (claims.acr as AcrValue) ?? 'zoreal.device',\n };\n opts.onCredential?.(response);\n } catch (e) {\n release();\n setPairing(null);\n publishRef.current?.(null);\n if (e instanceof DOMException && e.name === 'AbortError') {\n if (closedByPerson.current) {\n closedByPerson.current = false;\n opts.onNonOAuthError?.({\n type: 'popup_closed',\n description: 'the sign-in dialog was closed before the holder approved',\n });\n }\n return;\n }\n if (e instanceof FlowAbandonedError) {\n opts.onNonOAuthError?.(e.reason);\n return;\n }\n if (e instanceof OAuthFlowError) {\n opts.onError?.({ error: e.error, description: e.description });\n return;\n }\n opts.onNonOAuthError?.({\n type: 'unknown',\n description: e instanceof Error ? e.message : String(e),\n });\n }\n };\n void run();\n }, [clientId, issuer, locale]);\n\n return { login, internals: { pairing } };\n}\n\nexport function useZorealLogin(\n options: { flow?: 'browser-direct' } & BrowserDirectFlowOptions\n): (event?: unknown) => void;\nexport function useZorealLogin(\n options: { flow: 'auth-code' } & AuthCodeFlowOptions\n): (event?: unknown) => void;\nexport function useZorealLogin(\n options: ({ flow?: 'browser-direct' | 'auth-code' } & ZorealLoginRequestOptions) &\n Partial<Pick<AuthCodeFlowOptions, 'redirect_uri' | 'ux_mode'>> & {\n onSuccess?: (response: never) => void;\n onError?: (error: Pick<NonOAuthError, 'description'> & { error: ErrorCode }) => void;\n onNonOAuthError?: (error: NonOAuthError) => void;\n }\n): (event?: unknown) => void {\n if (options.ux_mode === 'redirect') {\n // v1 supports the popup shape only: the code and PKCE verifier go to your\n // onSuccess and from there to your backend over TLS. A redirect would have\n // to carry the verifier in a URL, which is a credential in every access\n // log on the path. Refused loudly rather than implemented badly.\n throw new Error(\n \"@zoreal/oauth2-react: ux_mode 'redirect' is not supported in v1. Use the default \" +\n \"'popup' shape and post the code and code_verifier from onSuccess to your backend.\"\n );\n }\n const flow = options.flow ?? 'browser-direct';\n return useZorealFlow({\n ...options,\n flow,\n onCredential:\n flow === 'browser-direct'\n ? (options.onSuccess as unknown as (r: ZorealCredentialResponse) => void)\n : undefined,\n onCode:\n flow === 'auth-code'\n ? (options.onSuccess as unknown as (r: ZorealCodeResponse) => void)\n : undefined,\n }).login;\n}\n","import { cx, ensureStyles } from './styles';\n\n/**\n * Holds a site's own sign-in control busy while a login runs, with the\n * pairing modal's light round it, and lets it go when the login ends.\n *\n * The control is whatever the person tapped: the React hook takes it from\n * the click event handed to `login`, and `startLogin` takes it as `control`.\n * Nothing is asked of the site: no wrapper round its button, no busy state\n * of its own, no CSS. The light is an overlay in the document body, placed\n * over the control's box and kept there through scrolling and resizing, so\n * neither an ancestor's overflow nor a selector that counts on the button's\n * parent is affected. The control is disabled for the duration, and whatever\n * `disabled` and `aria-busy` it had before are put back.\n */\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst TAIL = 0.3;\nconst STACK = Array.from({ length: 12 }, (_, i) => 12 - i);\nconst HALO = [3, 2, 1];\n\nfunction radiusOf(control: HTMLElement): number {\n const value = parseFloat(getComputedStyle(control).borderTopLeftRadius);\n return Number.isFinite(value) ? value : 8;\n}\n\nexport function holdBusy(control: HTMLElement): () => void {\n if (typeof document === 'undefined') return () => {};\n ensureStyles();\n\n const hadDisabled = control.hasAttribute('disabled');\n const hadBusy = control.getAttribute('aria-busy');\n if (control instanceof HTMLButtonElement || control instanceof HTMLInputElement) {\n control.disabled = true;\n } else {\n control.setAttribute('aria-disabled', 'true');\n }\n control.setAttribute('aria-busy', 'true');\n\n const overlay = document.createElement('div');\n overlay.className = `${cx('root')} ${cx('ring')} ${cx('ring-overlay')}`;\n overlay.dataset.theme = 'auto';\n overlay.dataset.busy = 'true';\n overlay.setAttribute('aria-hidden', 'true');\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('class', cx('ring-svg'));\n const rx = radiusOf(control) + 2;\n const layer = (name: string, len: number, alpha: string) => {\n const rect = document.createElementNS(SVG_NS, 'rect');\n rect.setAttribute('class', cx(name));\n rect.setAttribute('rx', String(rx));\n rect.setAttribute('ry', String(rx));\n rect.style.strokeDasharray = `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`;\n rect.style.setProperty('--zrl-s', `calc(var(--zrl-l) * ${-(TAIL - len)})`);\n rect.style.opacity = alpha;\n svg.appendChild(rect);\n return rect;\n };\n let measured: SVGRectElement | null = null;\n for (const n of HALO) {\n const rect = layer('ring-halo', (TAIL * n) / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`);\n measured ??= rect;\n }\n for (const n of STACK) layer(n <= 2 ? 'ring-head' : 'ring-tail', (TAIL * n) / STACK.length, String(1 / n));\n overlay.appendChild(svg);\n document.body.appendChild(overlay);\n\n // The overlay follows the control's box. Fixed positioning against the\n // viewport rect, re-read on every frame the page may have moved it.\n let frame = 0;\n const place = () => {\n frame = 0;\n const box = control.getBoundingClientRect();\n overlay.style.left = `${box.left}px`;\n overlay.style.top = `${box.top}px`;\n overlay.style.width = `${box.width}px`;\n overlay.style.height = `${box.height}px`;\n const length = typeof measured?.getTotalLength === 'function' ? measured.getTotalLength() : 0;\n if (length > 0) overlay.style.setProperty('--zrl-ring-len', `${length}px`);\n };\n const schedule = () => {\n if (!frame) frame = requestAnimationFrame(place);\n };\n place();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(schedule);\n observer?.observe(control);\n window.addEventListener('scroll', schedule, true);\n window.addEventListener('resize', schedule);\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (frame) cancelAnimationFrame(frame);\n observer?.disconnect();\n window.removeEventListener('scroll', schedule, true);\n window.removeEventListener('resize', schedule);\n overlay.remove();\n if (control instanceof HTMLButtonElement || control instanceof HTMLInputElement) {\n control.disabled = hadDisabled;\n } else {\n control.removeAttribute('aria-disabled');\n }\n if (hadBusy === null) control.removeAttribute('aria-busy');\n else control.setAttribute('aria-busy', hadBusy);\n };\n}\n\n/** The element a click event was bound to, if it is one this can hold. */\nexport function controlFrom(event: unknown): HTMLElement | null {\n const target = (event as { currentTarget?: unknown } | null)?.currentTarget;\n return typeof HTMLElement !== 'undefined' && target instanceof HTMLElement ? target : null;\n}\n","/**\n * The way back for the same-device sign-in.\n *\n * The tap navigates away to the provider, the app opens, and once the holder\n * has approved, the app opens this page again with the pairing named in the\n * URL fragment. The page then has to finish a sign-in it did not start in\n * this page load, so the flow is saved here before the navigation and taken\n * back on the return: the verifier, the nonce, the state and which mode the\n * caller wanted. Local storage rather than session storage because the\n * browser opens the return in a new tab, and session storage is per tab.\n *\n * The original tab may still be polling when the returned page completes.\n * Whichever finishes first marks the flow done; the other, on seeing the\n * approval, stands down instead of spending a code that was already used.\n */\n\nexport interface SavedFlow {\n v: 1;\n issuer: string;\n clientId: string;\n flow: 'browser-direct' | 'auth-code';\n verifier: string;\n nonce: string;\n state: string;\n scope: string;\n appState?: string;\n requestId: string;\n createdAt: number;\n}\n\nconst PREFIX = 'zoreal:oauth2:return:';\nconst DONE = 'zoreal:oauth2:done:';\n/** A saved flow older than this is not resumed; the pairing is long expired. */\nconst MAX_AGE_MS = 10 * 60 * 1000;\n\nfunction storage(): Storage | null {\n try {\n return typeof localStorage === 'undefined' ? null : localStorage;\n } catch {\n return null;\n }\n}\n\nexport function saveReturnFlow(flow: SavedFlow): void {\n try {\n storage()?.setItem(PREFIX + flow.requestId, JSON.stringify(flow));\n } catch {\n // Storage full or blocked: the original tab still polls and completes.\n }\n}\n\n/**\n * The saved flow for a pairing, left in place; null when none, or too old.\n * Left in place because the first reader on a page may not be its owner: a\n * page can carry more than one client, and only the one whose id matches\n * takes it (`forgetReturnFlow`).\n */\nexport function peekReturnFlow(requestId: string): SavedFlow | null {\n const store = storage();\n if (!store) return null;\n const raw = store.getItem(PREFIX + requestId);\n if (!raw) return null;\n try {\n const flow = JSON.parse(raw) as SavedFlow;\n if (flow.v !== 1 || flow.requestId !== requestId) return null;\n if (Date.now() - flow.createdAt > MAX_AGE_MS) return null;\n return flow;\n } catch {\n return null;\n }\n}\n\n/** The owner has taken the flow up; nobody else on this page load should. */\nexport function forgetReturnFlow(requestId: string): void {\n try {\n storage()?.removeItem(PREFIX + requestId);\n } catch {\n // Nothing to do.\n }\n if (pending === requestId) pending = null;\n}\n\nexport function markReturnDone(requestId: string): void {\n try {\n const store = storage();\n store?.setItem(DONE + requestId, String(Date.now()));\n store?.removeItem(PREFIX + requestId);\n } catch {\n // Nothing to do: at worst the other tab attempts a used code and is told so.\n }\n}\n\nexport function isReturnDone(requestId: string): boolean {\n return storage()?.getItem(DONE + requestId) !== null && storage()?.getItem(DONE + requestId) !== undefined;\n}\n\n/** The address the app reopens: this page, without any fragment. */\nexport function returnToUrl(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n const { href } = window.location;\n const hash = href.indexOf('#');\n return hash === -1 ? href : href.slice(0, hash);\n}\n\nconst RETURN_MARK = /(?:^|[#&])zoreal_return=([A-Za-z0-9]{32})(?:&|$)/;\n\n/** The id read from the fragment, held for the rest of this page load. */\nlet pending: string | null = null;\n\n/**\n * The pairing named in this page's fragment by the app's return, if any. The\n * fragment is removed from the address bar as it is read, so a reload does\n * not try to resume a second time, and the id is kept for this page load so\n * every reader on the page sees it until its owner takes the flow up.\n */\nexport function pendingReturnId(): string | null {\n if (pending) return pending;\n if (typeof window === 'undefined') return null;\n const match = RETURN_MARK.exec(window.location.hash);\n if (!match) return null;\n pending = match[1];\n try {\n window.history.replaceState(window.history.state, '', returnToUrl());\n } catch {\n // Some embedded browsers refuse; the id was still read.\n }\n return pending;\n}\n","/**\n * Reads claims OUT of an ID token without verifying it.\n *\n * That is not a shortcut, it is the design: this code runs in a browser the\n * threat model assumes is attacker-controlled (02), so a signature check here\n * proves nothing to anyone. The token is verified where verification means\n * something: server-side against the JWKS. What this parser feeds is\n * convenience fields (acr on the response object) that the types document as\n * convenience, with the token staying the authority.\n */\n\nexport function unsafeClaims(idToken: string): Record<string, unknown> {\n try {\n const payload = idToken.split('.')[1] ?? '';\n const b64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n return JSON.parse(\n new TextDecoder().decode(Uint8Array.from(atob(padded), (c) => c.charCodeAt(0)))\n );\n } catch {\n return {};\n }\n}\n","/**\n * The pairing channel, client side. wire.ts pins the endpoints.\n *\n * The browser polls; the phone never talks to the browser. Everything here is\n * therefore plain fetch against the issuer, CORS-gated on the client's\n * authorized origins, with the poll cadence fixed: the provider cancels an\n * over-polling request rather than throttling it, so a \"retry\n * faster on error\" strategy here would kill the login it is trying to save.\n */\n\nimport {\n DEFAULT_QR_REFRESH_SECONDS,\n POLL_INTERVAL_ENROLLING_MS,\n POLL_INTERVAL_MS,\n SDK_VERSION,\n WIRE_VERSION,\n type PairCreated,\n type PairDisplay,\n type PairStartResponse,\n type PairStatusResponse,\n type TokenResponse,\n} from './wire';\nimport type { ErrorCode, NonOAuthError, PairingState } from './types';\n\nexport class OAuthFlowError extends Error {\n constructor(\n public error: ErrorCode,\n public description?: string\n ) {\n super(description ?? error);\n }\n}\n\nexport class FlowAbandonedError extends Error {\n constructor(public reason: NonOAuthError) {\n super(reason.description ?? reason.type);\n }\n}\n\nexport interface StartPairingParams {\n client_id: string;\n scope: string;\n state: string;\n nonce: string;\n code_challenge: string;\n redirect_uri?: string;\n acr_values?: string;\n max_age?: number;\n prompt?: string;\n locale?: string;\n /**\n * The surface this package is about to show, decided BEFORE the request:\n * the provider binds the pairing to it. \"qr\" gets animated frames, \"link\"\n * gets a start token on pair_url and no QR at all.\n */\n display?: PairDisplay;\n}\n\nasync function parseJson(response: Response): Promise<Record<string, unknown>> {\n try {\n return (await response.json()) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function startPairing(\n issuer: string,\n params: StartPairingParams\n): Promise<PairStartResponse> {\n const response = await fetch(`${issuer}/pair`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `@zoreal/oauth2-react/${SDK_VERSION}`,\n }),\n });\n\n const body = await parseJson(response);\n if (!response.ok) {\n // The provider's words, verbatim. A refused package version arrives here,\n // and rewriting its reason would hide the only signal telling an integrator\n // to upgrade.\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n (body.error_description as string) ?? `The provider refused the request (${response.status})`\n );\n }\n return body as unknown as PairStartResponse;\n}\n\n/**\n * The same-device sign-in, as a URL to NAVIGATE to, not to fetch.\n *\n * A phone's browser hands a universal link to an app only inside a\n * navigation the person began, and a page that sets its location after a\n * network round trip has left that navigation behind: the link then loads\n * as a web page. So on a phone this package fetches nothing on the tap. The\n * tap itself navigates to the provider's start endpoint with what /pair\n * would have been sent, the provider creates the link pairing and answers\n * with a redirect to its universal link, still inside the person's\n * navigation, and the app opens. The page is not unloaded when it does, and\n * polls the pairing by the `request_id` it chose here. With no app installed\n * the same redirect lands on the page that installs it. `return_to` is this\n * page's own address, which the app reopens once the holder has approved,\n * with the pairing named in the fragment (see return.ts).\n */\nexport function sameDeviceStartUrl(\n issuer: string,\n params: StartPairingParams & { request_id: string; origin: string; return_to?: string }\n): string {\n const query = new URLSearchParams();\n const all: Record<string, unknown> = {\n ...params,\n code_challenge_method: 'S256',\n wire_version: WIRE_VERSION,\n sdk: `@zoreal/oauth2-react/${SDK_VERSION}`,\n };\n for (const [key, value] of Object.entries(all)) {\n if (value === undefined || value === null || value === '') continue;\n query.set(key, String(value));\n }\n return `${issuer}/pair/start?${query.toString()}`;\n}\n\n/**\n * The QR refresh cadence for a pairing: the provider's, or the default when\n * it sent none (a provider that predates animated frames, or a legacy\n * pairing). Anything that is not a positive number is treated as absent\n * rather than trusted, because a zero here would spin.\n */\nexport function qrRefreshSecondsOf(started: PairCreated): number {\n const seconds = started.qr_refresh_seconds;\n return typeof seconds === 'number' && seconds > 0 ? seconds : DEFAULT_QR_REFRESH_SECONDS;\n}\n\n/**\n * The URL of the provider's current QR frame. The query is a cache-buster\n * and nothing more: the frame itself is chosen on the provider, this package\n * only asks for it again. A new value every call, so an <img> whose src is\n * set to it fetches rather than reusing what it showed last time.\n */\nexport function qrFrameUrl(issuer: string, requestId: string): string {\n return `${issuer}/pair/${encodeURIComponent(requestId)}/qr.svg?t=${Date.now()}`;\n}\n\nconst sleep = (ms: number, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n // An already-aborted signal never fires its abort event, so check first\n // or the sleep runs to term and the poll takes one extra swing.\n if (signal?.aborted) {\n reject(new DOMException('aborted', 'AbortError'));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(new DOMException('aborted', 'AbortError'));\n };\n // Each sleep takes its listener back off when it finishes. One signal\n // lives for the whole login and is slept on once per poll and once per QR\n // frame, so listeners left behind pile up on it for as long as the\n // pairing is open: dozens per login, all of them dead.\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n\n/** How long a same-device navigation is given to begin before the first poll. */\nconst SETTLE_MS = 1500;\n/** Consecutive network failures a poll rides out before it is a failure. */\nconst NETWORK_FAILURES_TOLERATED = 4;\n\nexport interface PollOptions {\n /**\n * Same-device navigation only. The page starts polling while the\n * provider is still answering the navigation that creates the pairing,\n * so a \"no such pairing\" answer before this instant (epoch ms) is the\n * pairing not existing YET, and is read as pending.\n */\n tolerateUnknownUntil?: number;\n /**\n * QR surface only. While the request is pending, hand `onState` a fresh\n * `qrUrl` every this many seconds, merged into the last state seen, so the\n * code on screen keeps up with the provider's moving frame. Omit on the app\n * link: a link pairing has no QR and the provider answers 404 for it.\n */\n qrRefreshSeconds?: number;\n}\n\n/**\n * Polls until the request resolves. Returns the authorization code.\n * Throws FlowAbandonedError for the human outcomes (denied, expired,\n * enrolment abandoned) and OAuthFlowError for protocol ones.\n *\n * With `qrRefreshSeconds` set it also drives the QR animation: between polls\n * it re-issues `qrUrl` on that cadence, through the same `onState`, for as\n * long as the request is pending. The frames stop the moment the status\n * leaves pending (the QR is spent once the phone has claimed it) and when the\n * poll is aborted, so a cancelled login never keeps fetching an image.\n */\nexport async function pollUntilApproved(\n issuer: string,\n requestId: string,\n onState?: (state: PairingState) => void,\n signal?: AbortSignal,\n options: PollOptions = {}\n): Promise<string> {\n let last: PairingState = { status: 'pending' };\n const emit = (state: PairingState) => {\n last = state;\n onState?.(state);\n };\n\n // The frame loop is a setTimeout chain armed from the clock after each\n // frame, never a setInterval: a background tab throttles timers, and an\n // interval that wakes late fires its missed ticks in a burst, which here\n // would be a burst of image fetches for frames the provider has already\n // moved past. Waking late costs one frame's delay, then the cadence resumes\n // from now. The provider always renders the current frame regardless.\n let frames: AbortController | null = null;\n const stopFrames = () => {\n frames?.abort();\n frames = null;\n };\n const startFrames = () => {\n const seconds = options.qrRefreshSeconds;\n if (frames || signal?.aborted || typeof seconds !== 'number' || !(seconds > 0)) return;\n const period = seconds * 1000;\n const controller = new AbortController();\n frames = controller;\n // The caller's abort reaches the frames too, and the listener goes away\n // with them so a long-lived signal does not accumulate one per pairing.\n signal?.addEventListener('abort', stopFrames, { signal: controller.signal });\n void (async () => {\n let due = Date.now() + period;\n for (;;) {\n await sleep(Math.max(0, due - Date.now()), controller.signal);\n emit({ ...last, qrUrl: qrFrameUrl(issuer, requestId) });\n due = Date.now() + period;\n }\n })().catch(() => {\n // Aborted: the frames stopped with the pairing. Nothing to report.\n });\n };\n\n const settlingUntil = options.tolerateUnknownUntil ?? 0;\n let networkFailures = 0;\n try {\n // Let a same-device navigation begin before the first poll, so the poll\n // is not the request the navigation cancels.\n if (settlingUntil > Date.now()) await sleep(SETTLE_MS, signal);\n for (;;) {\n let response: Response;\n try {\n response = await fetch(`${issuer}/pair/${encodeURIComponent(requestId)}/status`, {\n signal,\n });\n networkFailures = 0;\n } catch (e) {\n if (e instanceof DOMException && e.name === 'AbortError') throw e;\n // A navigation cancels the page's requests while it is in flight, and\n // the same-device sign-in IS a navigation: the first poll after the tap\n // is killed by it (Safari reports \"Load failed\"), even though the tab\n // stays once the app has taken the link. A network failure while the\n // navigation settles is therefore not an outcome, and one in the\n // background, on a phone that has just switched apps, seldom is either:\n // only a run of them is.\n networkFailures += 1;\n if (settlingUntil > Date.now() || networkFailures <= NETWORK_FAILURES_TOLERATED) {\n emit({ ...last, status: last.status });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n throw e;\n }\n const body = (await parseJson(response)) as unknown as PairStatusResponse;\n\n if (response.status === 404 && (options.tolerateUnknownUntil ?? 0) > Date.now()) {\n // Same-device navigation: the pairing is being created by the\n // navigation this page is polling ahead of; not there YET is pending.\n emit({ status: 'pending' });\n await sleep(POLL_INTERVAL_MS, signal);\n continue;\n }\n\n if (!response.ok) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Pairing status failed (${response.status})`\n );\n }\n\n emit({\n status: body.status,\n expiresIn: body.expires_in,\n enrolmentDeadline: body.enrolment_deadline,\n });\n\n // Frames run only while the code is still the thing on screen. Once the\n // phone has claimed it the image is spent, and a frame issued after that\n // would only replace the spent code with a different spent code.\n if (body.status === 'pending') startFrames();\n else stopFrames();\n\n switch (body.status) {\n case 'approved':\n if (!body.code) {\n throw new OAuthFlowError('server_error', 'approved with no authorization code');\n }\n return body.code;\n case 'denied':\n throw new FlowAbandonedError({ type: 'request_denied', description: body.error_description });\n case 'expired':\n throw new FlowAbandonedError({ type: 'request_expired', description: body.error_description });\n case 'cancelled':\n // The provider cancels an over-polled or abandoned request outright\n // (its pairing rows have a real cancelled state). Before 0.1.4 this\n // fell through to the default branch and polled a dead request\n // forever.\n throw new FlowAbandonedError({\n type: 'request_expired',\n description: body.error_description ?? 'the provider cancelled the pairing request',\n });\n case 'enrolling':\n await sleep(POLL_INTERVAL_ENROLLING_MS, signal);\n break;\n default:\n await sleep(POLL_INTERVAL_MS, signal);\n }\n }\n } finally {\n stopFrames();\n }\n}\n\n/**\n * The code exchange, browser-direct mode only: a public client, PKCE and no\n * secret. What comes back can only ever be the pseudonymous tier, by\n * construction rather than by rule: personal data lives at /userinfo behind an\n * access token this mode is never issued, because personal-data scopes are\n * refused for public clients at the pairing step.\n */\nexport async function exchangeCode(\n issuer: string,\n input: { code: string; code_verifier: string; client_id: string }\n): Promise<TokenResponse> {\n const response = await fetch(`${issuer}/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: input.code,\n code_verifier: input.code_verifier,\n client_id: input.client_id,\n }),\n });\n\n const body = (await parseJson(response)) as unknown as TokenResponse;\n if (!response.ok || body.error) {\n throw new OAuthFlowError(\n (body.error as ErrorCode) ?? 'server_error',\n body.error_description ?? `Token exchange failed (${response.status})`\n );\n }\n return body;\n}\n\n/** A mobile user agent gets the app link, not a QR of its own screen. */\nexport function isMobileUserAgent(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /android|iphone|ipad|ipod/i.test(navigator.userAgent);\n}\n\n/**\n * Which surface this login will use, from the caller's preference and the\n * user agent. Decided before the pairing is created, never after: the\n * provider binds the pairing to the surface it is told about, and the two\n * surfaces are claimed differently, so asking for one and showing the other\n * produces a code the phone is right to refuse.\n */\nexport function resolveDisplay(display?: 'auto' | 'qr' | 'link'): PairDisplay {\n if (display === 'link') return 'link';\n if (display === 'qr') return 'qr';\n return isMobileUserAgent() ? 'link' : 'qr';\n}\n","/**\n * PKCE, S256 only: mandatory for every client, confidential ones included.\n * There is no plain fallback and there must never be one; a provider seeing\n * method=plain is seeing a bug or an attack.\n */\n\nconst VERIFIER_BYTES = 32; // 43 base64url chars, the RFC 7636 minimum length\n\nconst base64url = (bytes: Uint8Array): string =>\n btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\nexport function generateVerifier(): string {\n const bytes = new Uint8Array(VERIFIER_BYTES);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\nexport async function challengeS256(verifier: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));\n return base64url(new Uint8Array(digest));\n}\n\n/**\n * The same challenge, computed synchronously.\n *\n * The same-device sign-in is a navigation the browser must see as the\n * person's own tap, and an `await` between the tap and the navigation is\n * what breaks that: WebCrypto only digests asynchronously, so the digest is\n * done here by hand. SHA-256 as in FIPS 180-4, verified against the RFC\n * 7636 vector and against WebCrypto in the tests.\n */\nexport function challengeS256Sync(verifier: string): string {\n return base64url(sha256(new TextEncoder().encode(verifier)));\n}\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst rotr = (x: number, n: number): number => (x >>> n) | (x << (32 - n));\n\nexport function sha256(message: Uint8Array): Uint8Array {\n const H = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ]);\n const length = message.length;\n const padded = new Uint8Array(((length + 9 + 63) >> 6) << 6);\n padded.set(message);\n padded[length] = 0x80;\n const view = new DataView(padded.buffer);\n const bits = length * 8;\n view.setUint32(padded.length - 8, Math.floor(bits / 0x100000000));\n view.setUint32(padded.length - 4, bits >>> 0);\n\n const W = new Uint32Array(64);\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i++) W[i] = view.getUint32(offset + i * 4);\n for (let i = 16; i < 64; i++) {\n const w15 = W[i - 15];\n const w2 = W[i - 2];\n const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);\n const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);\n W[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;\n }\n let [a, b, c, d, e, f, g, h] = H;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i] + W[i]) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0] + a) >>> 0;\n H[1] = (H[1] + b) >>> 0;\n H[2] = (H[2] + c) >>> 0;\n H[3] = (H[3] + d) >>> 0;\n H[4] = (H[4] + e) >>> 0;\n H[5] = (H[5] + f) >>> 0;\n H[6] = (H[6] + g) >>> 0;\n H[7] = (H[7] + h) >>> 0;\n }\n const out = new Uint8Array(32);\n const outView = new DataView(out.buffer);\n for (let i = 0; i < 8; i++) outView.setUint32(i * 4, H[i]);\n return out;\n}\n\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64url(bytes);\n}\n\n/**\n * A pairing token of this package's own choosing, for the same-device\n * navigation: the provider answers a navigation with nothing the page could\n * read, so the page names the pairing it will poll. Same shape as a token\n * the provider mints, 32 letters and digits from the CSPRNG.\n */\nconst TOKEN_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nexport function generateRequestId(): string {\n let out = '';\n const bytes = new Uint8Array(64);\n while (out.length < 32) {\n crypto.getRandomValues(bytes);\n for (const byte of bytes) {\n // Rejection sampling: 62 does not divide 256, so bytes past the last\n // full multiple are thrown away rather than folded, which would bias.\n if (byte >= 248 || out.length === 32) continue;\n out += TOKEN_ALPHABET[byte % 62];\n }\n }\n return out;\n}\n","import { useEffect, useLayoutEffect, useRef, type CSSProperties, type ReactNode } from 'react';\nimport { cx, ensureStyles } from './styles';\nimport type { ZorealTheme } from './types';\n\n/**\n * The light of the pairing dialog's QR well, run around whatever this wraps\n * while `busy` is true: the well's colours, line, halo and lap, on a dash\n * that travels the outline at one speed whatever the shape.\n *\n * The SDK's own button uses it for the gap on a phone between the tap and the\n * hand-over to the app. A site with its own sign-in button MAY wrap that\n * button the same way: set your busy state on the tap, clear it when\n * `onSuccess` or `onError` fires. `radius` is the wrapped control's corner\n * radius in pixels, so the light follows its shape; `theme` picks the light\n * and dark strengths the dialog uses, following the OS by default.\n *\n * It is a wrapper, and a wrapper is a change to your markup, so know what it\n * does before you use it: it is inline by default and shrinks to the button,\n * so a full-width button needs `block`; the light sits outside the button,\n * so an ancestor with `overflow: hidden` clips it; and a selector that relies\n * on the button's parent (`.card > button`) no longer matches. A site that\n * would rather draw its own busy state needs nothing from here.\n */\n/** How far the tail reaches behind the head, as a fraction of the outline. */\nconst TAIL = 0.3;\n/* Layer n is n/N of the tail long and 1/n opaque. A point k/N of the way\n back is covered by layers n >= k, and the product of their transparencies\n telescopes to (k - 1)/N: a straight fade. Longest first, so the head paints\n on top. The two shortest carry the head tint. */\nconst STACK = Array.from({ length: 12 }, (_, i) => 12 - i);\nconst HALO = [3, 2, 1];\n\nexport function ZorealBusyRing({\n busy,\n radius = 8,\n theme = 'auto',\n block = false,\n className,\n style,\n children,\n}: {\n busy: boolean;\n radius?: number;\n theme?: ZorealTheme;\n /** Lay the wrapper out as a block, for a button that fills its row. */\n block?: boolean;\n className?: string;\n style?: CSSProperties;\n children: ReactNode;\n}) {\n useEffect(() => {\n ensureStyles();\n }, []);\n // The dash is sized as a fraction of the outline, so the outline's length\n // is measured once laid out and again whenever the control changes size.\n const wrapRef = useRef<HTMLSpanElement>(null);\n const measureRef = useRef<SVGRectElement>(null);\n useLayoutEffect(() => {\n const wrap = wrapRef.current;\n const rect = measureRef.current;\n if (!wrap || !rect) return;\n const measure = () => {\n const length = rect.getTotalLength();\n if (length > 0) wrap.style.setProperty('--zrl-ring-len', `${length}px`);\n };\n measure();\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(measure);\n observer.observe(wrap);\n return () => observer.disconnect();\n }, [radius]);\n // The outline runs 2px outside the control, so its corners are 2px larger.\n const rx = radius + 2;\n // One dash of the stack: `len` of the outline long, ending at the shared\n // head three tenths of the way round, `alpha` opaque.\n const layer = (key: string, name: string, len: number, alpha: string, ref?: typeof measureRef) => (\n <rect\n key={key}\n ref={ref}\n className={cx(name)}\n rx={rx}\n ry={rx}\n style={\n {\n strokeDasharray: `calc(var(--zrl-l) * ${len}) calc(var(--zrl-l) * ${1 - len})`,\n '--zrl-s': `calc(var(--zrl-l) * ${-(TAIL - len)})`,\n opacity: alpha,\n } as CSSProperties\n }\n />\n );\n return (\n <span\n ref={wrapRef}\n className={className ? `${cx('root')} ${cx('ring')} ${className}` : `${cx('root')} ${cx('ring')}`}\n data-theme={theme}\n data-busy={busy}\n style={block ? { display: 'flex', ...style } : style}\n >\n {children}\n <svg className={cx('ring-svg')} aria-hidden=\"true\">\n {HALO.map((n, i) =>\n layer(`h${n}`, 'ring-halo', (TAIL * n) / HALO.length, `calc(var(--zrl-glow-opacity) * 0.6 / ${n})`, i === 0 ? measureRef : undefined)\n )}\n {STACK.map((n) => layer(`t${n}`, n <= 2 ? 'ring-head' : 'ring-tail', (TAIL * n) / STACK.length, String(1 / n)))}\n </svg>\n </span>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useZorealFlow } from './useZorealLogin';\nimport type { UseZorealAutoLoginOptions } from './types';\n\n/**\n * Silent re-auth with prompt=none. NOT One Tap and never could be: there is no\n * ZOREAL session cookie in the browser to read, the credential is on a phone.\n * This succeeds only for a returning user at a consented sector with a live\n * session, the resulting acr is zoreal.session with an empty amr, and a\n * relying party that needs a live human must not build on this hook.\n *\n * PRIVACY NOTE: mounting this on a page sends a request to ZOREAL on page\n * load, before the user does anything. The hook is therefore conservative: it\n * fires once per mount, never retries, and does nothing when `disabled`.\n */\nexport function useZorealAutoLogin(options: UseZorealAutoLoginOptions): void {\n const { login } = useZorealFlow({\n flow: 'browser-direct',\n scope: options.scope,\n prompt: 'none',\n onCredential: options.onSuccess,\n onError: (e) => {\n // The provider's honest answer when no silent session exists (the\n // common case): unavailable, not an error, and never surfaced.\n const quiet = ['login_required', 'consent_required', 'interaction_required'];\n if (quiet.includes(e.error)) {\n options.onUnavailable?.();\n } else {\n options.onError?.({ type: 'unknown', description: e.description ?? e.error });\n }\n },\n onNonOAuthError: (e) => options.onError?.(e),\n });\n\n const fired = useRef(false);\n useEffect(() => {\n if (options.disabled || fired.current) return;\n fired.current = true;\n login();\n }, [options.disabled, login]);\n}\n","/**\n * Clears SDK-held local state. Named for parity with googleLogout and, like\n * it, LOCAL ONLY: it does not end the holder's ZOREAL session, which lives on\n * their phone and at the provider. A relying party that believes this signs\n * the user out of ZOREAL has a security misunderstanding, not a naming\n * complaint. The relying party's own session is the relying\n * party's to end.\n *\n * The SDK deliberately persists nothing (no localStorage, no cookies), so\n * today this has nothing to clear and exists as the stable API surface for a\n * future that does.\n */\nexport function zorealLogout(): void {\n // Intentionally empty until the SDK holds state worth clearing.\n}\n","import type { ZorealCodeResponse } from './types';\n\n/** Mirrors hasGrantedAllScopesGoogle for name-for-name portability. */\nexport function hasGrantedAllScopesZoreal(\n response: Pick<ZorealCodeResponse, 'scope'>,\n firstScope: string,\n ...restScopes: string[]\n): boolean {\n const granted = new Set((response.scope ?? '').split(/\\s+/).filter(Boolean));\n return [firstScope, ...restScopes].every((s) => granted.has(s));\n}\n\nexport function hasGrantedAnyScopeZoreal(\n response: Pick<ZorealCodeResponse, 'scope'>,\n firstScope: string,\n ...restScopes: string[]\n): boolean {\n const granted = new Set((response.scope ?? '').split(/\\s+/).filter(Boolean));\n return [firstScope, ...restScopes].some((s) => granted.has(s));\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_react","PREFIX","import_react","import_jsx_runtime","TAIL","STACK","HALO","import_jsx_runtime","import_react"]}
|