diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b28991..9a5ad22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,43 @@ jobs: test: name: Test runs-on: ubuntu-latest + strategy: + matrix: + node: [22, 24] + services: + mongodb: + image: mongo:7 + ports: + - 27017:27017 + options: >- + --health-cmd "mongosh --quiet --eval 'db.adminCommand({ping:1})'" + --health-interval 10s --health-timeout 5s --health-retries 10 + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s --health-timeout 3s --health-retries 10 + env: + TEST_MONGODB_URI: mongodb://127.0.0.1:27017/dependency_test + PERFORMANCE_MONGO_URI: mongodb://127.0.0.1:27017/perf_test_ci + PERFORMANCE_REDIS_PORT: 6379 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + - run: npm ci + - run: npm run build + - run: npm audit --audit-level=low + - run: npm test + + review-consent: + name: Review consent transactions + runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -28,6 +65,36 @@ jobs: node-version: 22 cache: npm - run: npm ci - - run: npm run build - - run: npm audit --omit=dev --audit-level=moderate - - run: npm test + - run: npx tsc --noEmit + - name: Start disposable MongoDB replica set + run: | + docker run -d --name review-consent-test --network host --memory=1g --cpus=1 --tmpfs /data/db:rw,noexec,nosuid,size=512m mongo:7.0@sha256:0e145625e78b94224d16222ff2609c4621ff6e2c390300e4e6bf698305596792 mongod --bind_ip 127.0.0.1 --port 28743 --replSet review-consent-test --oplogSize 16 + for attempt in $(seq 1 40); do + if docker exec review-consent-test mongosh --quiet --port 28743 --eval 'db.adminCommand({ping:1})' > /dev/null 2>&1; then break; fi + sleep 1 + done + docker exec review-consent-test mongosh --quiet --port 28743 --eval 'rs.initiate({_id:"review-consent-test",members:[{_id:0,host:"127.0.0.1:28743"}]})' + for attempt in $(seq 1 40); do + if docker exec review-consent-test mongosh --quiet --port 28743 --eval 'if (!db.hello().isWritablePrimary) quit(1)' > /dev/null 2>&1; then exit 0; fi + sleep 1 + done + exit 1 + - name: Verify consent authority, concurrency and replay cost + env: + TEST_REVIEW_CONSENT_MONGO: mongodb://127.0.0.1:28743/?replicaSet=review-consent-test + TEST_REVIEW_CONSENT_PERF_REPORT: review-consent-perf.json + TEST_REVIEW_COMPLETION_HTTP_PERF: review-completion-http-perf.json + TEST_REVIEW_COMPLETION_PERF_REPORT: review-completion-perf.json + TEST_REVIEW_CONSENT_HTTP_PERF: review-consent-http-perf.json + run: npx mocha test/review-owner-consent.test.js test/review-consent-http.test.js test/review-completion-http.test.js + - uses: actions/upload-artifact@v4 + with: + name: review-consent-performance + path: | + review-consent-perf.json + review-consent-http-perf.json + review-completion-perf.json + review-completion-http-perf.json + - name: Remove disposable database + if: always() + run: docker rm -f review-consent-test || true diff --git a/.impeccable/config.json b/.impeccable/config.json new file mode 100644 index 0000000..61fe34b --- /dev/null +++ b/.impeccable/config.json @@ -0,0 +1,17 @@ +{ + "detector": { + "ignoreRules": [], + "ignoreFiles": [], + "ignoreValues": [ + { + "rule": "broken-image", + "value": "*", + "files": [ + "public/partials/anonymize.htm" + ], + "createdAt": "2026-11-02T06:04:45.274Z", + "reason": "Agent reviewed: existing avatar images use Vue :src bindings to safeUrl, not missing image sources." + } + ] + } +} diff --git a/Dockerfile b/Dockerfile index 217160b..6d82a0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,9 @@ COPY public ./public COPY src ./src RUN npm run build -# Stage 3: prod-only deps. Independent of source so it caches well. -FROM node:22-slim AS prod-deps +# Stage 3: install production deps on the runtime's libc. Native optional +# dependencies such as zstd must select musl binaries for the Alpine runtime. +FROM node:22-alpine AS prod-deps WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,id=npm-prod,target=/root/.npm,sharing=locked \ @@ -25,12 +26,14 @@ RUN --mount=type=cache,id=npm-prod,target=/root/.npm,sharing=locked \ # Stage 4: minimal runtime FROM node:22-alpine AS runtime +LABEL science.4open.owner-alert-protocol="recipient-v2" ENV NODE_ENV=production ENV PORT=5000 EXPOSE 5000 WORKDIR /app COPY --from=prod-deps /app/node_modules ./node_modules +RUN node -e "const assert = require('node:assert/strict'); const zstd = require('@mongodb-js/zstd'); (async () => { const input = Buffer.from('native compression smoke test'); const compressed = await zstd.compress(input, 3); assert.deepEqual(await zstd.decompress(compressed), input); })().catch(error => { console.error(error); process.exit(1); });" COPY --from=build /app/build ./build COPY --from=build /app/public ./public COPY package.json ./package.json diff --git a/README.md b/README.md index bb88c38..d2972b8 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,20 @@ A free public instance is available at **[anonymous.4open.science](https://anony Just open **[anonymous.4open.science](https://anonymous.4open.science/)**, paste your repository URL, add the terms to hide, and share the generated link. +### Git submodules + +Direct public GitHub submodules are included in the file browser and ZIP downloads +at the commit recorded by the parent repository. Their paths and contents use the +parent's anonymization settings. `.gitmodules` files are omitted from shared GitHub +content because they contain source repository URLs. + +The service expands up to 20 direct submodules and 10,000 combined child tree entries. +Private, non-GitHub, nested, missing, oversized, and truncated submodules remain empty +folders with a warning. Child files are fetched anonymously on demand; child tree +lookups use GitHub's unauthenticated API quota. ZIP downloads fetch public child archives. +Existing cached repositories with `.gitmodules` rebuild their file trees on the next +listing or ZIP download. No manual database migration is required. + ### CLI Anonymize a repository locally and produce an anonymized zip: @@ -42,12 +56,39 @@ npm install -g @tdurieux/anonymous_github anonymous_github ``` +### LLM and automation clients + +Use expiring, repository-scoped maintenance tokens to inspect saved status, +edit anonymization settings, refresh snapshots, and extend expiration. +A signed-in owner or coauthor can issue and revoke tokens through +`/api/user/maintenance-tokens`. See the [client guide](public/llms.txt) and +[OpenAPI description](public/maintenance-openapi.json), also served at +`/llms.txt` and `/maintenance-openapi.json` on each instance. +These tokens cannot access GitHub previews, file downloads, or admin routes. + ## Self-hosting Local installation requires Node.js 22.15 or newer in the 22.x series, 24.x, or 26 or newer. Run `npm ci` and `npm run build` to install and build. `npm run dev` uses Node's watch mode; `npm run build:ui` rebuilds browser assets. +For the November 2026 dependency update, install from the committed lockfile +with `npm ci`, rebuild with `npm run build`, and restart the API and streamer +services together. Docker installations should rebuild the application image. +This dependency update does not change MongoDB documents, Redis keys, or queue +formats, so it requires no production data migration. Existing credential +migration requirements still apply when upgrading from an older release. + +The [`glob`](https://github.com/advisories/GHSA-3g3m-2fjr-6pw4) and +[`minimatch`](https://github.com/advisories/GHSA-g3hv-75wg-m249) overrides select security fixes unavailable through +the dependency ranges of archive and development tools. Keep them until those +packages accept patched versions. CI checks the full dependency audit and runs +MongoDB migration, Redis session, and archive compatibility tests on Node 22 +and 24. To run the database tests locally, point `TEST_MONGODB_URI` and +`PERFORMANCE_MONGO_URI` at disposable MongoDB databases and set +`PERFORMANCE_REDIS_PORT` to a disposable Redis instance. The performance database +name must begin with `perf_test_`; these tests delete their test data. +
Run your own instance with Docker @@ -95,6 +136,96 @@ the [MongoDB replication guide](docs/mongodb-replication.md).
+### Owner email alerts + +Set `RESEND_API_KEY` and `EMAIL_FROM` on the web server and download workers to +send access alerts through [Resend](https://resend.com/docs/api-reference/emails/send-email). +Use a sender address on a domain verified in Resend. `APP_HOSTNAME` sets the +host used for the connections link in emails. + +Registration saves a verified GitHub email address, preferring the primary address. +OAuth requests `user:email`; GitHub App sign-in uses the read-only email permission +described in the setup guide. Later sign-ins also fill missing addresses. Saved +addresses and custom alert addresses are preserved. GitHub privacy placeholder +addresses are not imported. + +Signed-in owners without an email address see an email form in a popup. +“Remind me later” dismisses it for the current login session; “Never” saves a +permanent preference on the account. Existing addresses are reused, and owners +can add or change their address on the connections page at any time. +Alerts cover invalid GitHub credentials and repository access errors encountered +while reading or refreshing content. No periodic access scan is performed. +Emails contain no repository names, anonymous URLs, or credentials. + +An atomic recipient claim allows one email attempt per normalized address across +repositories, pull requests, gists and accounts. Suppression has no time expiry. +Sign-in, reconnecting, successful access recovery, resource removal or expiration, +and disabling the original account retain suppression for that address. Inactive +resources cannot trigger new alerts. Changing to an address that has never been +notified permits its first alert. + +Failed deliveries and interrupted sends whose outcome is unknown also stay +blocked to avoid duplicate delivery. Unsent claims can be cancelled by confirmed recovery before dispatch. Bookkeeping failures +release claims when no provider request has started. Pending claims interrupted +before dispatch can be reclaimed after a 60-second lease during startup or a new +notification attempt. Claim identity and state checks prevent a paused sender +from dispatching after another instance reclaims its claim. Cancellation is saved +before cleanup; matching legacy fields are cleared before the guard is released. +Interrupted cleanup resumes on startup or a later notification. Legacy fields +carry an attempt ID so unrelated deliveries and newer generations are preserved. +A resource revision prevents delayed preparation from recreating a reclaimed +claim. All asynchronous preparation stays pending. A sending reservation is +reclaimable until dispatch commits atomically on the owner document, against its +reset revision and lease, immediately before contacting the provider. Cancellation +competes with that same commitment, so a paused sender cannot dispatch afterward. +Owner dispatch records distinguish known-unsent reservations from interrupted +committed sends. Completed records are removed; expired uncommitted records are +pruned on later attempts. Persistent reset revisions also fence attempts created +while a reset is in progress, even when no recipient guard existed at its start. +Recovery recorded during an active reset is retried when reset finishes or its +lease expires, with the original source and revision checks. +Provider idempotency keys +and redacted attempt records support auditing without storing recipient addresses +or repository names in the audit log. Records include owner and resource IDs, +resource kind, generation and a categorized access failure to identify the +trigger without exposing the address. Attempt records are saved before dispatch, +with completion recorded separately. Existing resource claims are migrated to +recipient guards before new application instances serve requests using an indexed +scan with resumable checkpoints. Other instances wait while its lease is healthy, +and take over if the lease expires. +Every existing resource claim is retained behind the recipient guard. Recovery +marks claims recovered without authorizing another email. Completed provider +results are recorded before guard updates, with bounded retries and reconciliation +on startup or a later notification. The upgrade restores missing recipient guards +from immutable provider completion records, including guards previously released +by recovery or resource removal. This history scan uses an index and resumable +checkpoints and does not send email. Its `recipient-alerts-v2` database marker is +independent of the v1 marker, so cold starts also restore missing suppression. +Resource lifecycle mutations advance the owner dispatch fence and block new +attempts until the status write completes. Concurrent mutations keep separate +reservations. Failed or ambiguous status writes retain suppression for that +owner; run the offline reconciliation command below after stopping all writers +to clear interrupted reservations safely. + +Use `scripts/deploy.sh` when upgrading recipient alert suppression. The new image +has the `science.4open.owner-alert-protocol=recipient-v2` label. When running +application containers have an older or missing protocol label, the script builds +the replacement, stops and verifies all old API and streamer containers, then prepares a full claim +reconciliation and starts the new streamers and API. Stopped legacy containers +from a rollback also trigger reconciliation, so claims written after an earlier +migration are included. This path also runs when retrying with an already-built +image and `FORCE=0`. Once writers are stopped, preparation releases all known +unsent pending claims and clears their matching legacy fields before backfill. +This upgrade pauses traffic while claims migrate and allows up to 15 +minutes for startup, configurable with `MIGRATION_HEALTH_TIMEOUT`. Subsequent +deployments keep the usual rolling streamer replacement. Manual upgrades must +stop all application writers, run +`docker compose run --rm --no-deps --entrypoint node anonymous_github ./build/scripts/reconcile-legacy-owner-alerts.js` +using the new image, and then start the new application instances. Run this +preparation after legacy writers have operated, even if their containers were +already removed. Ordinary restarts preserve confirmed recoveries. +Leaving either Resend setting empty disables delivery and the email prompt. + ## Scope of anonymization In double-anonymous review, the boundary of anonymization is **the paper plus its online appendix — and only that**. Googling part of the paper or appendix to reveal authorship is considered a deliberate attempt to break anonymity ([explanation](https://www.monperrus.net/martin/open-science-double-blind)). diff --git a/docs/github-app-setup.md b/docs/github-app-setup.md index 5000a73..0aa41e2 100644 --- a/docs/github-app-setup.md +++ b/docs/github-app-setup.md @@ -19,7 +19,10 @@ Configure: - Repository permissions: **Contents: read-only**, **Metadata: read-only**, **Pull requests: read-only**, **Pages: read-only**. Do not grant write permissions. Pull requests read also permits [reading PR issue comments](https://docs.github.com/en/rest/issues/comments#list-issue-comments); Issues permission is unnecessary. -- Keep user access token expiration enabled. Private email permission is unnecessary. +- User permissions: **Email addresses: read-only** to save a verified address at + sign-in, including addresses hidden from the public GitHub profile. If this + permission is unavailable, owners can enter an address in the email form. +- Keep user access token expiration enabled. - Leave **Request user authorization (OAuth) during installation** unchecked. Anonymous GitHub authorizes the user before opening installation; the setup redirect must remain available afterward. @@ -105,6 +108,14 @@ Public reads use a [repository-scoped App user token](https://docs.github.com/en Each API request renews through its own repository binding, rechecking identity and public visibility while keeping the same user quota. Existing installation bindings retain their installation checks. + +If a previously public source becomes private, its existing public-only binding +continues to reject private reads. Select the repository in the App installation, +then open the anonymization's Edit page and click **Repair GitHub access**. This +verifies installation access to the same GitHub repository ID and published commit +before replacing the binding. It preserves the anonymous URL, snapshot and settings. +Signing in again alone does not change a resource's binding. The Connections page +also provides **Reconnect read-only access** for this purpose. This uses GitHub's documented [public resource access for App user tokens](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app). The explicit **Use existing OAuth access** choice handles repositories not yet diff --git a/docs/markdown-compatibility.md b/docs/markdown-compatibility.md new file mode 100644 index 0000000..6bbe2f4 --- /dev/null +++ b/docs/markdown-compatibility.md @@ -0,0 +1,26 @@ +# Markdown rendering + +Repository previews and Markdown project pages use `src/markdown.ts`. The browser build bundles that module into the lazy-loaded `markdown.min.js`; the project-page route calls it directly. Each document gets fresh parser extensions, references and heading IDs. + +The compatibility suite covers: + +- CommonMark blocks and inline formatting, GFM tables, task lists, strikethrough and autolinks. +- GitHub heading IDs, repeated headings, custom anchors, relative links and images, picture sources, disclosures, keyboard text, subscript and superscript. +- Emoji shortcodes, footnotes with repeated references, and all five GitHub alerts. +- Inline dollar and dollar/backtick math, display math and fenced `math` blocks. +- Syntax highlighting, Mermaid, GeoJSON, TopoJSON and ASCII STL fences. Invalid map/model blocks retain their source. +- Sanitization, escaped syntax, code-block isolation, document-to-document state isolation and browser/server output parity. + +Maps render their supplied geometry with zoom controls. STL models support zoom and rotation. These viewers do not reproduce GitHub's map tiles or 3D material controls. Math uses KaTeX rather than GitHub's MathJax, so MathJax-only commands are not guaranteed. Highlighting uses highlight.js's common language bundle; unrecognized languages remain readable code. Mermaid uses the locally shipped version, which may differ from GitHub's deployed version. + +GitHub service features such as user mentions, issue-status badges, commit references and attachment uploads require GitHub identity or repository context. The anonymous viewer does not resolve those identities. It also does not execute repository HTML, arbitrary CSS or scripts. + +The sanitizer runs on all Markdown-produced HTML. Math and diagram renderers insert their own generated markup through unpredictable, per-document markers after sanitization. TeX runs with `trust: false`; Mermaid runs with `securityLevel: strict`. Source HTML cannot opt into this bypass. Task inputs are always disabled checkboxes. + +Run `npm run build:ui` before `npx mocha test/markdown-compatibility.test.js test/markdown-sanitization.test.js test/markdown-security.test.js test/mermaid-security.test.js test/asset-build.test.js`. The browser tests exercise the built bundle, not a copy of the renderer. + +References: [GitHub basic syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), [math](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/writing-mathematical-expressions), [diagrams](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams), and the [GFM specification](https://github.github.com/gfm/). + +Diagram inputs are limited to one million characters and 60,000 coordinates. Maps also enforce nesting and TopoJSON expansion limits before conversion. Rejected diagrams display as code. + +Mermaid, DOMPurify and KaTeX are npm dependencies so dependency audits cover their versions. After upgrading them, run `npm run vendor:markdown` followed by `npm run build:ui` and commit the refreshed assets. Security tests require the vendored files to match the installed packages. diff --git a/docs/performance-monitoring.md b/docs/performance-monitoring.md index 7bfa57c..625dbfc 100644 --- a/docs/performance-monitoring.md +++ b/docs/performance-monitoring.md @@ -88,3 +88,40 @@ validators avoid fetching and rendering content again; changed terms or content invalidate them. Raw Markdown served after a rendering failure has a separate validator, so later requests retry rendering when the transient failure clears. + +## Production failures and restarts + +Run `node scripts/check-production-logs.js 15` from a machine with Node and the +Docker CLI to summarize recent application logs and container states. For a +remote Docker host, use +`DOCKER_HOST=ssh://ubuntu@host node scripts/check-production-logs.js 15`. +The optional +window is 1–1440 minutes. The report includes completed 5xx responses, interrupted +requests, token-refresh failures, missing-file warnings, native unhandled stream +errors, and Docker restart counts. It prints counts and container metadata; +request URLs, repository IDs, file paths, credentials, and Docker environment +variables are excluded. Restart counts cover the container's lifetime, while log +counts cover the selected window. Compare restart counts with the last deployment +baseline even when every container is currently healthy. + +The API and streamers log `process_fatal_error` through Node's +`uncaughtExceptionMonitor`. This records fatal errors before the default process +exit; it does not resume execution or swallow uncaught exceptions. Native stderr +may describe the same crash as well, so fatal log indicators are not a count of +unique process exits. Use Docker restart counts to confirm exits. + +Legacy OAuth reset requests are coalesced by owner, credential, and OAuth app. +A failed 401/404 reset backs off for 30 minutes; transient failures back off for +one minute. A replaced credential or changed OAuth configuration gets a new key. +GitHub App and personal access tokens do not use the OAuth reset endpoint. Refresh +failure leaves the credential unchanged and continues through normal token +validation and fallback. + +Confirmed GitHub content 404s back off for 30 seconds in a bounded process-local +cache. The key includes repository, path, commit, blob SHA, cache generation, +revision, and credential. Permission failures and transport errors are not cached. +This reduces repeated upstream misses without changing the file tree or treating +an unavailable file as a successful cache fill. Persistent 404s still require +checking the repository's source snapshot. Application rate limits remain in +force; 429 replies now include `rate_limited`, `resetAt`, and `Retry-After` so +clients can wait for the actual store reset. diff --git a/docs/review-callback-handoff.md b/docs/review-callback-handoff.md new file mode 100644 index 0000000..bccba2e --- /dev/null +++ b/docs/review-callback-handoff.md @@ -0,0 +1,13 @@ +# Returning owner consent to review + +An optional callback registry maps the authenticated intent's client and callback IDs to one exact HTTPS URL. Configuration rejects credentials, queries, fragments, noncanonical paths, duplicate IDs and duplicate JSON fields. It permits at most 32 entries and a 4 KiB form-action policy. Callback selection never uses a browser-supplied address. + +After recording consent, the browser can request `POST /api/review-consent/handoff` with its signed ticket. The existing transport requires the current authenticated session, same origin and a session-bound CSRF token. The backend resolves the signed intent's callback before issuing a completion code and checks the current repository authority and saved consent. The transport reloads the session before returning the result. A missing registry leaves this endpoint unavailable. + +The page submits the completion envelope as the sole `completion` field in a native HTML POST form. Codes never enter URL parameters or browser storage. The page's form-action policy allows only the configured destinations and its own origin. It removes the temporary form when CSP blocks submission and shows a retry/status message. A late result after an account change cannot submit a form. The signed ticket stays in a controller closure for a retry; it is not submitted to the callback. + +The initial cross-site POST does not carry a review session cookie configured with SameSite=Strict. The review receiver must serve a non-caching landing page, then use a same-origin authenticated request and saved initiating-browser state before exchanging the code. Receiving the POST alone must not establish a binding. That receiver and startup activation are separate unfinished work. This change does not mount the upstream consent router or enable an integration. + +Validation covers callback configuration, current-session HTTP access, concurrent logout, signed callback scope, consent requirements, browser POST contents, account changes and malformed completions. The disposable MongoDB suite contains 23 passing tests. The local 100-replay binding benchmark measured median 2.20 ms and p95 2.69 ms on a one-CPU, 1 GiB container; this excludes provider traffic. Browser fixture results use synthetic HTTP backend responses and test TLS certificates, so they do not establish provider or certificate-verification behavior. + +The full local suite passed 762 tests, with 74 pending tests for optional fixtures. The targeted browser/page suite passed 17 tests, registry/HTTP tests passed 16 with one optional performance case pending, and TypeScript, targeted lint and the UI build passed. Chromium at 1280 and 320 pixels completed the native cross-site POST with no referrer or Strict cookie, no overflow, no external requests, no script errors and no WCAG A/AA violations. A third case blocked the destination with CSP and verified zero callback deliveries and removal of the code form. The first browser harness waited for a navigation that CSP intentionally blocked; changing the assertion to inspect the current document completed this case without changing application code. diff --git a/docs/review-completion-exchange.md b/docs/review-completion-exchange.md new file mode 100644 index 0000000..6337d5a --- /dev/null +++ b/docs/review-completion-exchange.md @@ -0,0 +1,15 @@ +# Completion codes and binding receipts + +The owner consent backend now saves the authenticated review intent with its immutable consent receipt. Existing consent records that lack this scope cannot issue completion codes. No caller may supply replacement submission, entitlement or policy fields. + +`completion(actor, ticket)` requires the same current browser account and session, a matching confirmed consent and current repository authority. It issues one code per intent, expiring at the earliest of five minutes, intent expiry or retention expiry. Repeating issuance returns the same code without extending its lifetime. The database stores a random nonce and a code hash. A domain-separated HMAC using the private consent signing key makes the code recoverable after response loss without storing it in plaintext. Retain that key for the code lifetime; changing it fails closed for outstanding issuance retries. + +`exchange(authenticatedClientId, command)` requires a separately verified service client. The argument is an internal authority boundary, not authentication by itself. First exchange checks the code, expiry, current account and repository authority, then atomically inserts a pending binding, a client-scoped request key and the original receipt. A later write failure rolls all three back. Competing transactions may report unavailable; callers explicitly retry the original command. The backend never automatically repeats a transaction. + +Exact authenticated exchanges recover the original receipt after response loss, code expiry or later owner changes. They cannot create another binding. Changed codes under the committed request ID conflict, a spent code cannot be used with another request ID, and an existing request ID cannot bind another intent. `receipt(authenticatedClientId, intentId)` provides post-expiry reconciliation without accepting an expired code for first use. Both operations remain client scoped and expose only protocol identifiers and policy. They never return source coordinates, account IDs or repository IDs. + +Bindings remain pending. No snapshot, artifact access, retained bytes or entitlement fulfillment is claimed. Browser completion POSTs, callback registration, service HTTP authentication, private configuration, startup wiring and activation remain separate tasks. The existing browser confirmation response intentionally does not expose the completion code yet. + +Receipt recovery uses majority reads outside transactions. The factory limits active authority transactions and service reads to four each. MongoDB queries have three-second limits, majority writes have a three-second acknowledgment timeout and commits have a five-second limit. The serving connection must also have bounded selection/socket/pool waits. Secrets and encoded completion payloads must never be logged. + +Tests use a disposable local MongoDB replica set and cover current ownership, absent consent, cross-session and cross-client attempts, altered codes, expiry, explicit replay, concurrent exchange, request-key reuse, rollback and persistence without raw tokens or codes. Set `TEST_REVIEW_CONSENT_MONGO` to the disposable replica-set URI and run `npx mocha test/review-owner-consent.test.js test/review-consent-http.test.js`. `TEST_REVIEW_COMPLETION_PERF_REPORT` records 100 immutable receipt replays; it excludes browser, TLS and provider latency. diff --git a/docs/review-completion-http.md b/docs/review-completion-http.md new file mode 100644 index 0000000..c6c108b --- /dev/null +++ b/docs/review-completion-http.md @@ -0,0 +1,15 @@ +# Completion service transport + +`createReviewCompletionService` serves the exact `POST /service/v1/completions/exchange` and `GET /service/v1/intents/{intentId}/receipt` routes when explicitly mounted before body parsing and browser authentication. Startup does not mount it. An absent scoped registry returns typed 404 responses without touching the backend. + +The private registry has exactly `version: 1` and `keys`. Each key contains `clientId`, `keyId`, `tokenSHA256`, `notBefore`, `notAfter` and `scopes`. Identifiers, ASCII-token hashing and whole-second validity windows follow the capabilities registry. Each key must explicitly authorize `completion.exchange`, `receipt.read`, or both. The existing capabilities-only configuration is rejected because it has no operation scopes. Limits are 64 KiB, 64 keys, 32 clients and 16 keys per client. Duplicate fields, key identities, digests and scope entries are rejected. + +Requests use `X-4open-Artifact-Client-Id`, `X-4open-Artifact-Service-Key-Id` and `Authorization: Bearer `. TLS is required, directly or through explicitly trusted proxy configuration. Browser cookies, origin/referrer/fetch metadata, duplicate headers, encoded bodies, query strings and route aliases are rejected. Authorization precedes body reads. Constant-time digest comparison and the key window are checked, with validity rechecked before backend execution and before returning a result. + +The transport accepts at most 64 KiB of strict JSON. The shared decoder rejects invalid UTF-8, duplicate decoded keys and excessive nesting. Commands have exactly the v1 exchange fields. A successful receipt must contain exactly the expected client/intent scope and v1 receipt fields. Errors contain a fixed protocol code and fresh random correlation ID, never provider or database diagnostics. Responses disable caching, referrers and CORS. Rejections close the connection. + +A client is limited to 60 requests per minute across its rotation keys. At most four operations run concurrently. Each request has a 15-second deadline. A disconnected or timed-out caller does not release an in-progress backend slot until the backend settles. A write may commit after response loss; callers must retain and explicitly retry their original request. No automatic retry occurs here. + +Tests use real loopback HTTP, synthetic trusted-proxy TLS metadata and a simulated backend. They do not measure incoming TLS, MongoDB or provider latency. Set `TEST_REVIEW_COMPLETION_HTTP_PERF` to capture 50 authenticated receipt reads. The opt-in review-repository interoperability test also passes against this actual TypeScript handler over certificate-verified HTTPS. It covers explicit replay, changed-request conflicts, post-expiry receipt reads, policy mismatch, scope separation and untrusted certificates. Its backend remains synthetic. Credentials, startup wiring, browser callback handoff and activation remain unfinished; capabilities still advertise no available integration features. + +The dedicated CI MongoDB fixture uses one CPU, a 1 GiB memory cap and a 512 MiB temporary data mount. The earlier 256 MiB mount failed during replica-set initialization with a no-space error; the increased fixture is still isolated and removed after testing. This does not change production storage or resource limits. diff --git a/docs/review-consent-http.md b/docs/review-consent-http.md new file mode 100644 index 0000000..3415727 --- /dev/null +++ b/docs/review-consent-http.md @@ -0,0 +1,21 @@ +# Browser transport for review consent + +`createReviewConsentRouter` exposes the consent backend through three routes when explicitly mounted at `/api/review-consent`: `GET /csrf`, `POST /preview` and `POST /confirm`. Application startup does not mount this router. Browser UI, private configuration, startup ordering and activation remain unfinished. + +The factory requires a canonical HTTPS origin, the consent backend and a dedicated private 32-byte CSRF key shared by serving processes. Mount it after Passport/session authentication and before any JSON parser. The current startup ordering must change before integration; mounting after a parser fails closed. Transport must be HTTPS directly or through the application's explicitly trusted proxy configuration. + +Identity comes from Passport's authenticated account and a fresh lookup in the session store. Neither request JSON nor a bearer credential can supply it. POSTs require the exact configured Origin and a constant-time-checked CSRF token bound to the account and session. CSRF tokens are derived with a protocol-specific HMAC; issuing one never saves a session. A late CSRF response therefore cannot recreate a session destroyed by a concurrent logout. Authority is reloaded after backend work before returning a preview or confirmation response. + +Requests use exact paths without queries, strict methods, closed JSON fields and literal access/retention acceptance. Cross-origin or cross-site context, bearer authentication, duplicate security headers, content encoding and unexpected content types are rejected. The CSRF GET accepts no body. JSON is limited to 12 KiB; tickets to 8 KiB. A 15-second total deadline closes incomplete requests, and browser disconnects cancel pending preview consumption. Authenticated requests are limited to 60 per account per minute with at most 1,024 live counters. + +Responses disable caching and referrers. Confirmations include only the request ID, accepted policy and confirmation time. Internal account/repository identifiers and provider/database errors are omitted. A confirmation already accepted by the backend can commit despite a lost response; the UI must preserve the same ticket and request ID for explicit recovery. This transport does not create artifact bindings or bypass final review-side authority checks. + +## Validation + +The tests use real Express and express-session middleware with a disposable in-memory session store, a synthetic backend and loopback HTTP representing the trusted TLS proxy boundary. They do not measure real MongoDB or provider latency. TLS verification of the service client and MongoDB authority transactions are tested separately. + +Coverage includes session-derived identity, CSRF replay across sessions, cross-origin/bearer rejection, bounded closed payloads, exact routes, session revocation during preview, no session resurrection during a CSRF/logout race, response redaction, fixed errors, rate limits, unauthorized incomplete uploads, browser cancellation and the authenticated 15-second slow-upload deadline. The optional benchmark performs 50 fresh loopback HTTP preview requests. The CI database job runs these HTTP tests alongside the consent transaction tests and retains both performance reports. + +The final local targeted run passes 12 cases in 16 seconds. Transport-only timings are mean 4.94 ms, median 4.51 ms and p95 7.26 ms. TypeScript and targeted lint pass. + +The final local broader suite passes 723 cases with 63 opt-in/environment-dependent cases pending. The separate CI replica-set job exercises the otherwise skipped consent database tests as well as this transport suite. diff --git a/docs/review-consent-ui.md b/docs/review-consent-ui.md new file mode 100644 index 0000000..7de25eb --- /dev/null +++ b/docs/review-consent-ui.md @@ -0,0 +1,13 @@ +# Artifact consent page + +`/review-link` serves a separate page without the ordinary site's third-party widget or profile header. Its content security policy allows only first-party scripts and connections. The page rejects URL queries and noncanonical aliases, disables caching and referrers, and accepts only filename-shaped manifest entries. + +The handoff fragment contains exactly `clientId`, `intentId` and `token`. The application removes it from the current history entry before starting application requests or the router. Intent credentials and signed preview tickets stay in a controller closure. They are not rendered or written to browser storage. Reloading loses the handoff; users must reopen a new link from their submission. Signed-out users sign in before reopening the link. + +Owners enter the anonymous repository ID and request a preview. The backend supplies the repository name, access policy, retention deadline and expiry. Confirmation requires two separate unchecked approvals. A failed confirmation keeps the same signed ticket and random request ID for an explicit retry. Repository changes are disabled while that confirmation is uncertain. Account changes and page disposal cancel requests and discard late results. Success says that consent is recorded and that artifact linking remains incomplete. + +The page does not mount the consent API or advertise available integration capabilities. Startup configuration, final binding exchange, provider activation and a review-side link launcher remain unfinished. The consent HTTP router still needs to be mounted after authentication and before body parsing. + +Validation uses the compiled frontend bundles. Targeted tests cover fragment removal, storage, approval flags, replay identity, departure during CSRF, account changes during preview, invalid handoffs and policies, endpoint unavailability, expiry, sign-in and mismatched receipts. Server tests check canonical paths, headers and unsafe manifests. Chromium checks at 1280 and 320 pixels exercise the full synthetic form flow with CSP enabled and check overflow, external requests, script errors and WCAG A/AA rules. No live database or provider request is involved. + +Run `npm run build:ui`, then `npx mocha test/review-consent-page.test.js test/review-consent-ui.test.js`. Set `TEST_REVIEW_PAGE_PERF_REPORT` to a private output path to measure 100 isolated HTML responses over loopback HTTP. This benchmark excludes browser rendering, database operations and provider latency. diff --git a/docs/review-intent-client.md b/docs/review-intent-client.md new file mode 100644 index 0000000..8c2fcd6 --- /dev/null +++ b/docs/review-intent-client.md @@ -0,0 +1,20 @@ +# Review intent consumption client + +`src/server/service/review-intent-client.ts` consumes a saved review-side link intent over authenticated HTTPS. The caller supplies a trusted, exact HTTPS origin, a client-bound service credential with a validity window, and an explicit request ID. The returned policy and opaque identifiers come from the authenticated review response. Browser-supplied access, retention and callback fields are rejected. + +The factory returns only `consume`; it copies credential values into a closure. A call makes one POST to `/service/v1/artifact-intents/consume`, validates the peer certificate with Node's trust store, and sends no browser cookies or origin. Redirects and automatic retries are disabled. An uncertain result requires the caller to retain the same request ID for an explicit retry. + +Limits are four active attempts, a ten-second total deadline, five-second inactivity timeout, 16 KiB response headers and 64 KiB body. An AbortSignal cancels the attempt. Invalid credentials or payloads fail before I/O. Responses must match the configured client and requested intent, use the v1 closed schema and valid future timestamps, and omit cookies and content encoding. Duplicate JSON keys, including escaped spellings, invalid UTF-8 and unknown fields fail. Errors expose only a fixed category; remote bodies and credentials are not included. + +This module is not wired into a route or configured in production. It does not authenticate an artifact owner, obtain consent, create a binding, exchange a completion code, or establish browser callback authority. Those are separate pending tasks. Credential distribution and upstream deployment also remain pending. + +## Validation + +Local tests use a disposable certificate and loopback HTTPS server. A child process loads that certificate through `NODE_EXTRA_CA_CERTS` at startup; an independent child with ordinary trust rejects it before sending credentials. No TLS bypass is used. Tests cover successful policy reads, copied configuration, request-ID replay, rejection before I/O, status classification without retry, malformed and oversized responses, duplicate keys, four-slot saturation and recovery, cancellation, credential expiry, inactivity and slow-stream total deadlines. + +- `npx mocha test/review-intent-client.test.js`: 11 behavioral cases; one performance case is opt-in. +- `npm test`: 712 pass, 50 pending opt-in or environment-dependent cases. +- Full TypeScript check and targeted ESLint pass. +- Opt-in 100-request performance run: fresh loopback TLS connections, mean 12.36 ms, median 12 ms, p95 15 ms. These are local synthetic timings, not an external-service latency promise. + +Run the measured test with `TEST_REVIEW_INTENT_PERF=1 TEST_REVIEW_INTENT_PERF_REPORT=/tmp/review-intent-perf.json npx mocha test/review-intent-client.test.js`. diff --git a/docs/review-owner-consent.md b/docs/review-owner-consent.md new file mode 100644 index 0000000..d323a8a --- /dev/null +++ b/docs/review-owner-consent.md @@ -0,0 +1,23 @@ +# Review owner consent backend + +`createReviewOwnerConsent` prepares a signed policy preview from the authenticated review intent consumer and records explicit access/retention acceptance in MongoDB. It is an unwired backend. Browser routes, CSRF protection, UI, completion-code exchange and activation remain unfinished. Capabilities continue to advertise no available integration features. + +The caller must provide an account ID and session ID derived from a current authenticated browser session. Never populate this principal from request JSON or treat a service credential as an owner's identity. The factory also needs the configured intent consumer, a dedicated random 32-byte signing key held outside Git, and a connected MongoDB replica set. Configure bounded server-selection and socket timeouts on that connection. Tests use MongoDB 7 with a three-second server-selection timeout and ten-second socket timeout. + +Preview checks the current account and repository before consuming the review intent, then checks them again afterward. The network request holds no MongoDB transaction or authority lock. The account must be active or have the legacy unset active status; the repository must be ready and unexpired. Ownership uses the current account ObjectId. Coauthors must match the account's current numeric GitHub ID. Legacy username-only coauthors and unrelated administrators cannot provide consent. + +The signed preview binds the saved intent and exact policy to the account, hashed session ID and repository ObjectId. Changing the account, session, repository or policy invalidates confirmation. The signing context is specific to this protocol; key rotation invalidates outstanding previews. Confirmation requires both access and retention acceptance and an explicit 32-hex request ID. Preview and replay expire with the intent or retention policy. + +Confirmation writes one immutable `review_owner_consents` record per client/intent. It records the account, repository, authority role, exact policy, request ID, confirmation time and a hash of the signed preview. It stores no raw session ID, intent token, service credential or preview ticket. Explicit retries return the same receipt only after current authority checks; another request ID or repository conflicts. No binding is created by accepting a policy. + +Transactions touch the current account and repository before writing or reading the receipt. Ownership transfer, coauthor removal and account disablement therefore serialize with confirmation. The private `reviewConsentRevision` fields are excluded from ordinary Mongoose reads. Four transactions may be active per factory. Database commands and commits have limits; transactions are not automatically retried. A conflict or uncertain commit returns a fixed `unavailable` error, and the caller must retain the same ticket and request ID for explicit recovery. + +The final binding flow must still revalidate the initiating review session and saved intent scope with the review service. This backend proves current upstream owner/coauthor consent, not continuing review-side author authority, immutable snapshots, restricted file access or preservation. Receipt retention/export and the HTTP adapter remain separate work; this change enables no provider or public route. + +## Validation + +The opt-in suite runs against a disposable MongoDB replica set. It covers literal acceptance, immutable replay, stable-ID coauthors, administrator/username rejection, ticket tampering, account/session binding, disabled accounts, ownership changes, removed coauthors, archived/expired repositories, permission changes during upstream I/O, concurrent confirmations, expiry, second-repository conflicts and authority-field privacy. A held ownership-change transaction forces a real write conflict; the explicit retry is denied and no receipt is created. + +Run `TEST_REVIEW_CONSENT_MONGO='mongodb://127.0.0.1:28743/?replicaSet=review-consent-test' npx mocha test/review-owner-consent.test.js`. Set `TEST_REVIEW_CONSENT_PERF_REPORT=/tmp/review-consent-perf.json` to include 100 durable receipt replays. The CI job starts and removes its own pinned MongoDB image and preserves the performance report. Local timings are synthetic and are not a production latency guarantee. + +Local validation passes 12 targeted cases with the benchmark enabled, 723 full-suite cases with 51 opt-in/environment-dependent cases pending, the full TypeScript check and targeted lint. The final 100-replay run measured mean 12.85 ms, median 12.82 ms and p95 14.89 ms against a one-CPU, 512 MiB disposable database. An earlier performance attempt returned `unavailable` while the privacy fixture was automatically building indexes. The log does not establish the underlying database error. The final fixture disables automatic schema/index creation and creates its collections explicitly. diff --git a/docs/review-service-capabilities.md b/docs/review-service-capabilities.md new file mode 100644 index 0000000..b2997c4 --- /dev/null +++ b/docs/review-service-capabilities.md @@ -0,0 +1,84 @@ +# Review service capabilities + +`GET /service/v1/capabilities` is an optional server-to-server endpoint for the +`4open.artifacts/1` contract. It returns the authenticated client ID, the supported +contract version, and an empty `available` list. Linking, snapshots, protected +files, retention, publication, entitlements and usage are not advertised. Clients +must decline those workflows until their required capabilities are implemented. + +The endpoint is mounted before JSON parsing, browser sessions and passport. The +entire `/service` namespace returns a typed 404 when configuration is absent. +Only the exact GET path is accepted. Query strings, browser context headers, +cookies, bodies, duplicate authentication headers and unknown paths are rejected. +Rejections close the connection immediately, including requests whose declared +body has not arrived. Responses contain no credentials, owner identity, repository data, cookies or CORS +permission; they have `no-store` and `no-referrer` headers. No database, queue or +provider call is involved. + +## Private configuration + +Leave `REVIEW_SERVICE_KEYS` unset until activation is approved. If configured, +its value is a JSON object with exactly `version: 1` and `keys`. Each key has +exactly these fields: + +- `clientId`: 32 lowercase hexadecimal characters. +- `keyId`: 1–64 ASCII letters, digits, underscores or hyphens. +- `tokenSHA256`: SHA-256 of the **ASCII 64-character lowercase hexadecimal bearer + token**, stored as 64 lowercase hexadecimal characters. Do not hash the decoded + token bytes and do not store a plaintext token in this registration. +- `notBefore` and `notAfter`: UTC timestamps, `YYYY-MM-DDTHH:mm:ssZ`, in 2000–2099. + The start is inclusive and the end exclusive. + +Use private runtime configuration outside Git. The caller sends +`Authorization: Bearer `, `X-4open-Artifact-Client-Id` and +`X-4open-Artifact-Service-Key-Id`. Keys grant only capabilities reads. Generate a +separate token for this operation and environment; never reuse GitHub credentials, +webhook secrets or future exchange credentials. The Go review client requires +HTTPS with a trusted server certificate; a deployment must provide that transport. + +The registry is bounded to 64 KiB, 64 keys, 32 clients and 16 keys per client. +Duplicate client/key pairs and reused digests are rejected. Unknown fields, +plaintext secret fields, invalid timestamps and empty configuration fail startup +with a generic diagnostic. Configuration uses ordinary JSON object parsing. +Credentials are copied into a private registry and compared by constant-time +SHA-256 digest equality. Overlapping validity windows permit rotation. Remove a +key and restart to revoke it; this is not hot reloading. + +Authenticated clients share 60 reads per minute across their keys. Counters are +bounded by registered clients, reset lazily, and have no timer. Limits are local +to each server process; they are not a distributed quota. A 429 response supplies +a typed failure and a bounded retry delay. This endpoint does not accept browser +session or repository-owner authority. + +## Verification + +`mocha --no-config --no-package test/review-capabilities.test.js` passes eight +HTTP/configuration scenarios, including 60 concurrent reads, cross-client and +wrong-key rejection, raw duplicate headers, exact path checks, browser context, +expiry, overlapping keys and restart revocation. Add +`RUN_REVIEW_SERVICE_BENCHMARK=1` for a 150-request synthetic loopback measurement. +The clean-install run averaged 2.035 ms, median 1.732 ms and p95 2.417 ms with fresh +HTTP connections. These are local tooling measurements, not production latency. + +A local HTTPS interoperability rehearsal uses the existing Go +`artifactclient.NewCapabilitiesReader` at review source `ee07d06`. It verifies +certificate trust, accepts the empty capability response, rejects both unsupported +access policies and rejects a wrong credential. No external provider is contacted. + +A clean `npm ci` install, the full TypeScript check and emitted build, UI build, +and targeted ESLint pass. The full test suite reports 700 passing in 36 seconds +and 49 pending opt-in cases; those skipped cases are not claimed as tested. +The capability suite separately passes eight cases including its benchmark. + +An initial check used dependencies copied from another checkout and produced +TypeScript diagnostics. Those installed versions differed from the lockfile; +reinstalling the locked dependencies resolved every diagnostic without source +changes. Use the lockfile when reproducing these results. + +The endpoint is implemented and tested locally. Upstream deployment, private key +distribution, ownership consent and the remaining artifact workflows are pending. + +A raw-socket regression first reproduced a rejected request waiting for its unread +10 MB body. Closing failed connections makes the same header-only request return +400 and close immediately; the complete eight-case endpoint suite and full +TypeScript checking pass. Full PR CI is repeated for this correction. diff --git a/gulpfile.js b/gulpfile.js index 2f5df34..3e61ab1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -26,11 +26,8 @@ const coreJsFiles = [ ]; const markdownFiles = [ - "public/script/external/katex.min.js", - "public/script/external/katex-auto-render.min.js", - "public/script/external/marked-katex-extension.umd.min.js", - "public/script/external/marked-mermaid.js", - ]; + "public/script/markdown-entry.js", +]; const pdfFiles = ["public/script/external/pdf.js"]; const notebookFiles = ["public/script/external/notebook.min.js"]; const orgFiles = ["public/script/external/org.js"]; @@ -73,6 +70,12 @@ function buildCoreJs(cb) { async function buildVendorJs() { const lazyAssets = {}; await Promise.all(Object.entries(lazyGroups).map(async ([name, files]) => { + if (name === "markdown") { + await esbuild.build({ entryPoints: files, bundle: true, format: "iife", minify: true, + target: "es2020", supported: { "template-literal": false }, outfile: "public/script/markdown.min.js" }); + lazyAssets[name] = `/script/${name}.${hashFile(`public/script/${name}.min.js`)}.min.js`; + return; + } // PDF.js is already minified upstream. Re-minifying it breaks rendering // for documents such as #857, even though page.render() resolves. if (name === "pdf") { diff --git a/package-lock.json b/package-lock.json index 41b48f0..3218deb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,39 +9,49 @@ "version": "2.3.0", "license": "GPL-3.0", "dependencies": { - "@aws-sdk/client-s3": "^3.1147.0", - "@aws-sdk/lib-storage": "^3.1147.0", + "@aws-sdk/client-s3": "^3.1161.0", + "@aws-sdk/lib-storage": "^3.1161.0", "@mongodb-js/zstd": "^1.2.2", "@octokit/plugin-throttling": "^8.2.0", "@octokit/rest": "^20.1.2", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.1", "archiver": "^7.0.1", "bullmq": "^5.81.5", "cacheable-lookup": "^6.1.0", "compression": "^1.8.2", "connect-redis": "^10.0.0", "crypto-js": "^4.2.0", + "d3-geo": "^3.1.1", "decompress-stream-to-s3": "^2.1.1", - "dotenv": "^18.0.6", - "express": "^5.2.1", - "express-rate-limit": "^8.7.1", + "dompurify": "^3.4.17", + "dotenv": "^18.0.9", + "express": "^5.3.0", + "express-rate-limit": "^8.7.2", "express-session": "^1.19.0", "express-slow-down": "^3.1.1", "got": "^11.8.6", + "highlight.js": "^11.11.1", "inquirer": "^8.2.7", "isbinaryfile": "^5.0.7", + "katex": "^0.18.10", "marked": "^15.0.12", + "marked-alert": "^2.1.2", + "marked-footnote": "^1.4.0", + "marked-gfm-heading-id": "^4.1.4", + "marked-katex-extension": "^5.1.13", + "mermaid": "^12.2.0", "mime-types": "^3.0.2", - "mongoose": "^7.8.12", - "node-schedule": "^2.1.1", + "mongoose": "^7.8.13", + "node-schedule": "^2.1.2", "parse-github-url": "^1.0.4", "passport": "^0.7.0", "passport-github2": "^0.1.12", - "rate-limit-redis": "^6.0.1", - "re2js": "^2.8.6", + "rate-limit-redis": "^6.0.2", + "re2js": "^2.9.1", "redis": "^5.12.1", - "sanitize-html": "^2.18.0", + "sanitize-html": "^2.18.1", "stripe": "^23.1.0", + "topojson-client": "^3.1.0", "ts-custom-error": "^3.3.1", "unzip-stream": "^0.3.4" }, @@ -53,33 +63,36 @@ "@types/archiver": "^7.0.0", "@types/compression": "^1.8.1", "@types/crypto-js": "^4.2.2", + "@types/d3-geo": "^3.1.1", "@types/express": "^5.0.6", "@types/express-session": "^1.19.0", "@types/inquirer": "^8.2.13", + "@types/katex": "^0.16.8", "@types/mime-types": "^3.0.1", "@types/node-schedule": "^2.1.8", "@types/parse-github-url": "^1.0.3", "@types/passport": "^1.0.17", "@types/passport-github2": "^1.2.9", "@types/sanitize-html": "^2.16.2", + "@types/topojson-client": "^3.1.5", "@types/unzip-stream": "^0.3.4", "c8": "^12.0.0", "chai": "^4.5.0", "esbuild": "^0.28.2", - "eslint": "^10.12.0", + "eslint": "^10.14.0", "gulp-clean-css": "^4.3.0", "gulp-concat": "^2.6.1", "gulp-uglify": "^3.0.2", - "http-proxy-middleware": "^4.2.0", + "http-proxy-middleware": "^4.2.1", "jsdom": "^29.1.1", - "knip": "^6.40.0", + "knip": "^6.44.1", "mocha": "^12.0.3", "ordered-read-streams": "^2.0.0", "ts-node": "^10.9.2", "typescript": "^5.9.3", - "typescript-eslint": "^8.71.1", + "typescript-eslint": "^8.72.1", "vinyl-fs": "^4.0.2", - "vue": "^3.5.43", + "vue": "^3.5.44", "vue-router": "^4.6.4" }, "engines": { @@ -89,6 +102,19 @@ "url": "https://github.com/sponsors/tdurieux" } }, + "node_modules/@antfu/install-pkg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-2.1.0.tgz", + "integrity": "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", @@ -141,14 +167,14 @@ "license": "MIT" }, "node_modules/@aws-sdk/checksums": { - "version": "3.1001.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1001.1.tgz", - "integrity": "sha512-x12Q17KYlJAd3nKf8LV5LV0vt8sh8/6YfQLGPtrGnQf/tW4jqxPGq5GPpuVitpQYM3eUR4XB7CbxZf751NMbLw==", + "version": "3.1002.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1002.1.tgz", + "integrity": "sha512-CXNESwkQC9jdrUG6cKXBfudVt1frGcYQykcCYbNclVrsewuGMbo3PlFjRteMe4OftNAqhyc3xC2jTz7SUqLasQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -157,20 +183,20 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.1147.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1147.0.tgz", - "integrity": "sha512-uTrcJxnJKPr8bc1z7cMxopYSQyAm2dPfiMK+UiBr51L8MzimDxHbW/d9GR6X/LFbWDpMzfFPLhB4UohDrCSaPQ==", + "version": "3.1161.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1161.0.tgz", + "integrity": "sha512-iRZGMsJnYRrI98O+U6o5fHeLPQTP+Dc/w0mWLcBLQcEQ10DA06+XQj0WmdRiCM7vIso7QEtN1fsj9Vzo5/R3LQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/checksums": "^3.1001.1", - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/credential-provider-node": "^3.972.84", - "@aws-sdk/middleware-sdk-s3": "^3.972.77", - "@aws-sdk/signature-v4-multi-region": "^3.996.47", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/checksums": "^3.1002.1", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/credential-provider-node": "^3.972.92", + "@aws-sdk/middleware-sdk-s3": "^3.972.85", + "@aws-sdk/signature-v4-multi-region": "^3.996.51", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -179,16 +205,16 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.978.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.1.tgz", - "integrity": "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==", + "version": "3.979.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.979.1.tgz", + "integrity": "sha512-ayOlV1VhLNMiq8PW9ZkVobG4gkldnUoHjG69yy2b8j+VYFlhz/uAL7ZdYQAoQ+eShtGUdNDnnG5rxDg6yUJrOg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.6", - "@aws-sdk/xml-builder": "^3.972.41", + "@aws-sdk/types": "^3.975.0", + "@aws-sdk/xml-builder": "^3.972.42", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.35.0", - "@smithy/signature-v4": "^5.7.3", + "@smithy/core": "^3.38.0", + "@smithy/signature-v4": "^5.7.5", "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" @@ -198,14 +224,14 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.72", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.72.tgz", - "integrity": "sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew==", + "version": "3.972.80", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.80.tgz", + "integrity": "sha512-uO5TiSvb6j1w9RTe5z28OaOmsrOxVQUPaC5+wZl50RfOXcrYD/KgnS8I9lfyGXc7sGbkCopK6VnhNIAoLqfewQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -214,16 +240,16 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.74", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.74.tgz", - "integrity": "sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw==", + "version": "3.972.82", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.82.tgz", + "integrity": "sha512-vQzlDHlC24Yz42p+rT4NBoP/5VdetkBoujbOLq9h6QYWC8THuZSPYnXIR9OP3XcOrdMgPBBipOrdssfxnIiWUg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -232,21 +258,21 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.17", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.17.tgz", - "integrity": "sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA==", + "version": "3.973.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.25.tgz", + "integrity": "sha512-b5xl71uCRdWsu7P1VeGpiGuCf6zfYETsSNOv8AmYVTrsgbvTJRXdmmOPVfxkvGGVNsiKkGctClQW3jAn0SZLug==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/credential-provider-env": "^3.972.72", - "@aws-sdk/credential-provider-http": "^3.972.74", - "@aws-sdk/credential-provider-login": "^3.972.79", - "@aws-sdk/credential-provider-process": "^3.972.72", - "@aws-sdk/credential-provider-sso": "^3.973.16", - "@aws-sdk/credential-provider-web-identity": "^3.972.78", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/credential-provider-env": "^3.972.80", + "@aws-sdk/credential-provider-http": "^3.972.82", + "@aws-sdk/credential-provider-login": "^3.972.87", + "@aws-sdk/credential-provider-process": "^3.972.80", + "@aws-sdk/credential-provider-sso": "^3.973.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.86", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -256,15 +282,15 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.79", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.79.tgz", - "integrity": "sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw==", + "version": "3.972.87", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.87.tgz", + "integrity": "sha512-C4ZFmcwM+Zj14Z8huVUk54Gv4uzT1ZdzG9cY3Gdh8FePakq7Bss1OFsgZjAhWFOkFOGpBvFBeu0OWjWf8HNKRQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -273,19 +299,19 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.84", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.84.tgz", - "integrity": "sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA==", + "version": "3.972.92", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.92.tgz", + "integrity": "sha512-jC3zmOo7Qxlk+oBivvSsS1Ph7sFGm0QfuhqDGbqgRXi4cxUYMG+dWtBwS2XYYXgc8TIVXJiwgQ65sbNpbYupTA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.72", - "@aws-sdk/credential-provider-http": "^3.972.74", - "@aws-sdk/credential-provider-ini": "^3.973.17", - "@aws-sdk/credential-provider-process": "^3.972.72", - "@aws-sdk/credential-provider-sso": "^3.973.16", - "@aws-sdk/credential-provider-web-identity": "^3.972.78", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/credential-provider-env": "^3.972.80", + "@aws-sdk/credential-provider-http": "^3.972.82", + "@aws-sdk/credential-provider-ini": "^3.973.25", + "@aws-sdk/credential-provider-process": "^3.972.80", + "@aws-sdk/credential-provider-sso": "^3.973.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.86", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" @@ -295,14 +321,14 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.72", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.72.tgz", - "integrity": "sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A==", + "version": "3.972.80", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.80.tgz", + "integrity": "sha512-xqfixO5aclje7tbQJDias+iNND5wp/3QZixCdZcVQpgWPTZ0V5DfTW9A804A5Z7ezu7sH3WRO+vzUjtiJBA8yg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -311,16 +337,16 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.16.tgz", - "integrity": "sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g==", + "version": "3.973.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.24.tgz", + "integrity": "sha512-nlg3BKG03Cgzvv/dMnhxvDJPhAhQSpaN0RUdwgU7ziwkasoev7iI8el8Xy4Rd5sqzgaDUbV269fOXck5SargqA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/token-providers": "3.1138.0", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/token-providers": "3.1161.0", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -329,15 +355,15 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.78", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.78.tgz", - "integrity": "sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA==", + "version": "3.972.86", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.86.tgz", + "integrity": "sha512-zKTyLJBEyfQBVlYqT6Jt+mYV4r8kGbWM7dn6v4Y4MRARizhYafjDXP/iMu76UMRhToSPNmIKX1XAb24gtpBFIA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -346,12 +372,12 @@ } }, "node_modules/@aws-sdk/lib-storage": { - "version": "3.1147.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1147.0.tgz", - "integrity": "sha512-f2eAbdiABtmJOgojJlAI7epzHDC9BO9bij788FDIk1LM1gvN6ibQLPx192f+oCL8A4gc4BH6qnxJZRBb8B3REA==", + "version": "3.1161.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1161.0.tgz", + "integrity": "sha512-ROSytGylVRtkUsp64mpzryv1Puvgo6jAm4RWTW2m3kC4SSsjrvhqZa5uPKfz116a0U1kk/hBz9S1eBItkyd++w==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "buffer": "5.6.0", "events": "3.3.0", @@ -362,19 +388,19 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.1147.0" + "@aws-sdk/client-s3": "^3.1161.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.77", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.77.tgz", - "integrity": "sha512-E7W2UOeUoc+lg3uIfR/dM7ZwusHwhBQrKMnlkRv4EXRR+C0YtV1pg25xC7GdZIhXH+NAMgZPCbE7o5to2cjFiw==", + "version": "3.972.85", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.85.tgz", + "integrity": "sha512-Kyl2G6zhIS9qFoeLdlxvXFrWyc6g9D9ZCIRJnGkkfX6JZhxCZB5ebXBc7GKEeetrnhXcail6ALZT0lKbojcaNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/signature-v4-multi-region": "^3.996.47", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.51", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -383,17 +409,17 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.46.tgz", - "integrity": "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==", + "version": "3.997.54", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.54.tgz", + "integrity": "sha512-/OczlavsS7U5sOodRO10Ug4R/AKFVZiXTstKipWe68EfjPkmdDFNXNl9FCJyXWkg1DG6RBm3qAuVB8FNW9O8KQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/signature-v4-multi-region": "^3.996.47", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.51", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/fetch-http-handler": "^5.8.0", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -402,13 +428,13 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.47", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.47.tgz", - "integrity": "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==", + "version": "3.996.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.51.tgz", + "integrity": "sha512-+aHvUugVBuMPXxgkPfWnTMyGdE/yWS8GHi5+qQ37HowS0YqzrbuaDLznj+Dtmtu5jgCdqz36gXmujOToUhNNWw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.6", - "@smithy/signature-v4": "^5.7.3", + "@aws-sdk/types": "^3.975.0", + "@smithy/signature-v4": "^5.7.5", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -417,15 +443,15 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1138.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1138.0.tgz", - "integrity": "sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw==", + "version": "3.1161.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1161.0.tgz", + "integrity": "sha512-ypbXSofNcSHwf/KA40bFCXJb0MLaAKrfT7sot4dFaA+2lsuXNah4zLRJIAU7Qmvs9PFJ6rnY0bZFEC+mE7attA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.978.1", - "@aws-sdk/nested-clients": "^3.997.46", - "@aws-sdk/types": "^3.974.6", - "@smithy/core": "^3.35.0", + "@aws-sdk/core": "^3.979.1", + "@aws-sdk/nested-clients": "^3.997.54", + "@aws-sdk/types": "^3.975.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -434,9 +460,9 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.974.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz", - "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==", + "version": "3.975.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.975.0.tgz", + "integrity": "sha512-WQlhs6juByzWOzj5fwxOx0mPJaEqMcG/sDpl3Id61B3XhKE63xY+0QWcnG+sWR/sCfprmreyUo1lM7IKZmHMzw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -447,9 +473,9 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", - "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.42.tgz", + "integrity": "sha512-smX8/gNP8Kjq+CWTBMZQLQ912vOQjiWExzwzvMO+eujPB5RY8IbtF0AGdUUXHKN7KoDWK9dIcuW0WG269kiP6w==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -469,9 +495,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, "license": "MIT", "engines": { @@ -489,13 +515,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", - "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "version": "7.29.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.12.tgz", + "integrity": "sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.8" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -505,13 +531,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -528,6 +554,12 @@ "node": ">=18" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, "node_modules/@bramus/specificity": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.3.tgz", @@ -565,6 +597,43 @@ "keyv": "^5.6.0" } }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-13.2.0.tgz", + "integrity": "sha512-bA9dvWlhAUcHkNwU+8w9yiUxJPlj/Lm3VqS7MD+NbIR/7LmpNLuK/AWNFdCo/T3SK5ND0/UHf3IJvl/Jn/i6yg==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "13.2.0", + "@chevrotain/types": "13.2.0" + } + }, + "node_modules/@chevrotain/gast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-13.2.0.tgz", + "integrity": "sha512-bCARdAsG+eEbOveSXcOAC7pVxA04I1ionzeMOwIYf5NX7SaS7zgn4Dgug2BSqoL+iyKeMNSorQjsFSHJHsCNwg==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "13.2.0" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-13.2.0.tgz", + "integrity": "sha512-kxpUkLtiaK09ph+dib+Yvm+3sWSnuwlo25gqfKRgTPNmF6H7uW4gVkMjaRdtnN783S9sM6vUNWh9CsxYX4QwKw==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-13.2.0.tgz", + "integrity": "sha512-7qrYHRlKfWf2AvBfJi5eVOrKAkbxvMFsy/iIHTGIiApNcEecCaVIdR+mIQhyCBsTDJxBz4JWXHMo6krbhR9hVg==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-13.2.0.tgz", + "integrity": "sha512-SzKYY68rseOrb9KILVzPsl6SjkQtdWw01SRGB5aCVI5kFcHltQsOYV3JB0Ffst72DgkvHCyDQ0NLwxtmCCf97Q==", + "license": "Apache-2.0" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -623,9 +692,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz", - "integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.7.tgz", + "integrity": "sha512-zgm9yjQz5iJB4VL8RyVwd71TWB8Q89FSxt/NzXoWqPSkEBOVtn9L6vAPtSc9hpKfB0Awo/AL2VKFHjYetBLFHQ==", "dev": true, "funding": [ { @@ -674,9 +743,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz", - "integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.16.tgz", + "integrity": "sha512-LsDbJPP/B8cPasUJyTA5S99F7C4G93ngW8hUluXz8CFeOaaRnBPtBiIlvgcPYU0HmHNz8CXg+1vwFrIB4F6Dgw==", "dev": true, "funding": [ { @@ -1444,6 +1513,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.7.tgz", + "integrity": "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^2.0.1", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, "node_modules/@inquirer/external-editor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", @@ -1471,101 +1557,11 @@ "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", "license": "MIT" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } + "node_modules/@isaacs/nonrecursive-glob-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.0.3.tgz", + "integrity": "sha512-SI7kyKHq4XXDRozw+r9GlRZkia1ZkFrpLW98r2IC2Bpr8YkFGE3fvjzmxCG0x78A++69HmDNrDvSMYBZmHGpKA==", + "license": "BlueOak-1.0.0" }, "node_modules/@istanbuljs/schema": { "version": "0.1.6", @@ -1629,10 +1625,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@mermaid-js/parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.1.tgz", + "integrity": "sha512-j37dj/h6p6pots1L7pLPPNuABEEu9WP4OYg9pW1g+nYG331j0+3canE9OagtYdytYvdSAMG+2fKU5cxep6LgjA==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~13.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/@mongodb-js/saslprep": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.6.0.tgz", - "integrity": "sha512-19jGXfpctLDJUELLUZGJ5BllcLT7rHdF+gTTaN2jzqu0bGkUhpdcYtgK2Vux+g3zuBAa0TM/W666QMIjYhYujw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.6.1.tgz", + "integrity": "sha512-D4CQtk9IwjCLAvCf1xvHEod3960q8XMUzfmdRU9qjUNlHnesGuTOvJcOTaPm9koZ1caUKA2aTbszZiAkcmSgmg==", "license": "MIT", "optional": true, "dependencies": { @@ -1784,9 +1792,9 @@ } }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", - "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.6.tgz", + "integrity": "sha512-ugMD1XQPQ0usC3F1frfZzZsNs9uf9Xyxe86GO078Mg0dVY+FQA8TlPL8P5EQwavZs0dUe2LjWJnt4TyQl3pUvw==", "cpu": [ "arm64" ], @@ -1797,9 +1805,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", - "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.6.tgz", + "integrity": "sha512-9ESac1RNLXH7jPZri+aOSoWOG3YpxFUdxgcrjlcANf2wDbEUf55bY3R+uGQqYkqcFLF91EkoAcDsJPbbGj84Sw==", "cpu": [ "x64" ], @@ -1810,9 +1818,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", - "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.6.tgz", + "integrity": "sha512-tAObEkjmQ77IgZfg6UEyEUgKP9vLVMkq2KK8WNa5P7hIieevSMHxyKcqM4zCV+TiZu8q7XmWLrqqlSuaZ5Z0zA==", "cpu": [ "arm" ], @@ -1823,9 +1831,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", - "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.6.tgz", + "integrity": "sha512-zHceGhbGw8RQG6oSKK/43ieOjutwb/yVXa5kmdJCfFCLbTdOkd4TGfvbiic70VJf8uomuV4AwOfFxe1i2/ZLmQ==", "cpu": [ "arm64" ], @@ -1836,9 +1844,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", - "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.6.tgz", + "integrity": "sha512-7zKNTbhIGdVO4gsGFNd39g5Go3jN+fuizvWe8SrbGSck9Pcr8x9I5GbtUX1JzBLMaUuI/tcB0JKEYn8RNjKhFg==", "cpu": [ "x64" ], @@ -1849,9 +1857,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", - "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.6.tgz", + "integrity": "sha512-J1mT1tp4v9aqcRwUtFrdJ94yei2SlIKm243nWK5bCyOPNnnNknaPzjG6a2Aa5OSfXE36rrth0X2OPHpispZTJw==", "cpu": [ "x64" ], @@ -1862,9 +1870,9 @@ ] }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz", - "integrity": "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.6.tgz", + "integrity": "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==", "dev": true, "license": "MIT", "optional": true, @@ -1879,8 +1887,8 @@ "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", + "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" } }, "node_modules/@octokit/auth-token": { @@ -2070,9 +2078,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.150.0.tgz", - "integrity": "sha512-oQef2Zu4Prz1KLKznz3HqZzU9uVoA5PMoDZuuLmqms7hKmKSAPzlaMnLllJq3t+rgKmfJJ2siPrpZfFrW06btw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.155.0.tgz", + "integrity": "sha512-A6xv7hDtnTm+xlZ7utn6rRQpRMJHRhVr3gjRGqlx0RUX84mNk0pAsy6YN/wWhuOTkaZhRrrzaLxf0NfbH77j2w==", "cpu": [ "arm" ], @@ -2087,9 +2095,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.150.0.tgz", - "integrity": "sha512-B6ofpoFiAUwIZ0MJ2IgHPvZK8FAtL4qzSZRwOkAKIfxEobE1mQC8nDdiFZj7pUaJiVUVCsfkMsBJKedzO8rZvA==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.155.0.tgz", + "integrity": "sha512-T1wWwH9/JI5g7Z4kEQwDOwhToxdDhRyOTklH+uuNpJ16m3MZFmGl+l2rKc0NHQdbVRG2LTOBsNjpW3h1BWytPA==", "cpu": [ "arm64" ], @@ -2104,9 +2112,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.150.0.tgz", - "integrity": "sha512-J+9IHKzx/bSz1JetOfD4zKXSK9sOm4/a7+0qomJODcTL6JsRXGeV/ZdkAPSIDydfFmWzYCraMlQEosSZEyBYkQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.155.0.tgz", + "integrity": "sha512-ZoRB3qJxJ8DWA41jHja+YeB0lLfjBy4T+Kr15vnN1k65X3zbJt3rPZftEwN4Ju4tmA4K/T9dWWD7Z8ewQLnrVA==", "cpu": [ "arm64" ], @@ -2121,9 +2129,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.150.0.tgz", - "integrity": "sha512-v6IPfcAcSYrBWXV5Tce1DmxDMXLhEYpIIWiRFPJopgCVscZdLaV4MRQOUxvL3usQlw+yrwYOjBFB9IwBx+jUiQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.155.0.tgz", + "integrity": "sha512-vxtL86BtM+IgrpejA86nbTk5j0HKTIMeuaJ1fQciw6p9RfavPtIPjI7gN34owap0yG9txXBaAWpGdxokNwdqIw==", "cpu": [ "x64" ], @@ -2138,9 +2146,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.150.0.tgz", - "integrity": "sha512-AoR/4jD02HET0KO0yNT4nbTE+XJUxiO9jB1X/ycEjUE3WrIJ3IjV/Vf+gskhWLcqqeXfvNnkDtBR7epllAm88A==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.155.0.tgz", + "integrity": "sha512-tN6Ru5e0UVnWaIuiH8jEwnvxuxg7FvhWwSd3oe5oDhOD4ObsSr9/N6WfHLjilUWlPPj65ptwrSDH0Y7lzwTzmw==", "cpu": [ "x64" ], @@ -2155,9 +2163,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.150.0.tgz", - "integrity": "sha512-A/hycCFjLUrCmLoL5O/vBp40ohlaXO1Ta3v5hqYZxicYFs129wZmgZJggcW4mYGYbZebQLhup+N8JjeQl8qwyw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.155.0.tgz", + "integrity": "sha512-h4ozA5n6L6lwolsz9iwunEhKWnIVbmFRots5QxEix0H6nJ5ICpuDa7PuShuhfpIDFe3+Qvyscioyiv+9g+4Syg==", "cpu": [ "arm" ], @@ -2172,9 +2180,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.150.0.tgz", - "integrity": "sha512-pbqahg1Pkz7J4RKmXm30s/iQu99hv64ayXCu8P4p75HcEH5azOdR4eGqiB6lFGkFR3mMpzaYsSKLkS8oJbjmeQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.155.0.tgz", + "integrity": "sha512-058/+LardpfuUlsKIvBGk/CYoakEVcMgqYx8GgyUZeOxFWTojYLlCc1Gb60/y21v3esDhHidgiY/Zbq9LpBhaQ==", "cpu": [ "arm" ], @@ -2189,9 +2197,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.150.0.tgz", - "integrity": "sha512-HV11aRbBQGwqv8bEo+K/6qr88uB4fEe1mhXncMhlVCrj+WpBSraxrUzHaPVmeQRU6x6x8v/3DuCbbo93rpp+fw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.155.0.tgz", + "integrity": "sha512-nrOt2Y3xIAzu+lSPzH0864ieO+iCCXgoo9qFlGANE8Xh0cQ+RP8Wstfp+/wXN2YCyb98cAGzX0VruKYw7it/jw==", "cpu": [ "arm64" ], @@ -2209,9 +2217,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.150.0.tgz", - "integrity": "sha512-k6pVkJqALwtEuP4zukVhGRhdIy4+ofChUIUUsAHHyqDZlNsknmel3JjbggrJiWGaes6h/dIcWmEXsKW4SmK9oQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.155.0.tgz", + "integrity": "sha512-KYAayyN5hQbJPtGKtRxlyc8XYmyntOplyXSQzTDrmbz7l/GCrzQbSswvyJFBTH1LGtsZrbXOAfN52i/Kz6kLKg==", "cpu": [ "arm64" ], @@ -2229,9 +2237,9 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.150.0.tgz", - "integrity": "sha512-tEFg39mw/rHO5n8GK2DR4ZMFfsPQZtnQIPbsIpjoQRomJc/2tRfsCSmCT6gNit+NZGoeJG5aH9ATDH5bf0WnoQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.155.0.tgz", + "integrity": "sha512-CSuSY5hfjtsBnHXydOj8jiNfS1qHbdPZuPz1YL1cSgK7VmNYP+QlZIbG0BBYYBgX/vYEgZwbG9mBdW4YairZ5w==", "cpu": [ "ppc64" ], @@ -2249,9 +2257,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.150.0.tgz", - "integrity": "sha512-0JO7IFkoek6HqV589Menx3hJySNXi6quRhO4tq2P7kpEHKEXoDATnoPVUpn5B7gDH2KLkdo751N0uIrGJFCTCw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.155.0.tgz", + "integrity": "sha512-7KCfOQm+Nz2N8trAXbQkal4TQ5756WVEmhm35y816YHvyFnEfVsICsiV5k0pEszNf/fRm4cm2i6i18SJ6/iCWg==", "cpu": [ "riscv64" ], @@ -2269,9 +2277,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.150.0.tgz", - "integrity": "sha512-7XPzREnyAS5wHU9aB+49Uaqgoo1gVCklHc3DRlc3fJy2tXD/eAJFN1QFz/crj+Ulup5P1+axNREF+/RGaB/IRA==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.155.0.tgz", + "integrity": "sha512-JSlhgoK+/g1TGJI5NuADx2+6syhxnQNLCWLXyO5QLFyetomcr/vIxSdiNlmyAv963YXUzCuwAwRG6npZ5xyJ1A==", "cpu": [ "riscv64" ], @@ -2289,9 +2297,9 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.150.0.tgz", - "integrity": "sha512-7n7ZxcbRWDFaTdyj8M8p0O9OfzoMKm8O6syBAIgcSutbOALq0Bb9G52Me+XH0anMLZV+NgXc726gqgG2q39vcQ==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.155.0.tgz", + "integrity": "sha512-WFIX+dPokWp64mi434Md3U3lJaTwNxAphvGjpltoyQjGUsdXavOIRrnp9dOS34Hd75HOAwDxAI+YwWOwqmJbtg==", "cpu": [ "s390x" ], @@ -2309,9 +2317,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.150.0.tgz", - "integrity": "sha512-Vx0GSA9ZCRTUiczoEQnIIyXkMvtEY8uc6IiwLQtMe5ci2sXPqjrry0Ek5fsPP1k2kCzkML3ow9UOOEgnEDi7Bw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.155.0.tgz", + "integrity": "sha512-g6xSDC/JpbnVOz5uafMGyK3txBVRFMQTx8opv/yHXGxqV3ZMVl3bf7W8XrSnF68dmy3gM4dgReNryuE53+WkeQ==", "cpu": [ "x64" ], @@ -2329,9 +2337,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.150.0.tgz", - "integrity": "sha512-ii4/9m3viDLssMnfXU7/Pni/3nYplApuGayceX4qsVGaqqQJCx3tHIH9M/HWIeSty5i8LjS21zPYT6lVIkwLxw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.155.0.tgz", + "integrity": "sha512-6/4xlxKlZJWrQ0uwDFPc0H9+xLu7068CKhX3vIDW3SbvP/lVgnVuT5v6dyugeWskWpuxqF/WJ4WKwhxjU3pG2w==", "cpu": [ "x64" ], @@ -2349,9 +2357,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.150.0.tgz", - "integrity": "sha512-ZtoxX5rez36ZWkwtiEhjkCPnlTPoQ2I7lIL0IYZ1yjxMYohbvoOjBmUIZzrf9W/HouONq0omIfTeCWEY+R380w==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.155.0.tgz", + "integrity": "sha512-tltklL0J0XjDxIRTBXWOBhvc2mitcmES45pIxhwDWfxRyqz7Vkeu0NekTZEIxGE4f/lXgq3vZcjjffteQOVorg==", "cpu": [ "arm64" ], @@ -2366,9 +2374,9 @@ } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.150.0.tgz", - "integrity": "sha512-VqeRb5JX/bKYBrff7TUDvJyKY0914UzuCkuXIGxJS4FUmvMNfqkCbc7Sq90iMz9DlmAtlggwaBYQ9eg3h3ltiw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.155.0.tgz", + "integrity": "sha512-alAb9TzjatUEsOeoncAziUnfdTDxfyswhalFZ9sC6plUgATIUx2NIZ0+bMn6aJ28AduS1OpG/OAisr/YISn4Ig==", "cpu": [ "arm64" ], @@ -2383,9 +2391,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.150.0.tgz", - "integrity": "sha512-EPqJfeZ4Pgg2BJSsCV8GozxV0FDltRzpVtGVa1r2noJu1iu+opOw3gtBWXwIo9odCT/MdFfyHxdy0/CRqs3OqA==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.155.0.tgz", + "integrity": "sha512-yc5cbSfcGmRv/KpxDFuyk8adEBaWd/9HilWvBbQ4bFbus1V2PUh2mGB2Cfq9rqZTqLeiMT8x0skV+LKp9hOMgQ==", "cpu": [ "ia32" ], @@ -2400,9 +2408,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.150.0.tgz", - "integrity": "sha512-n5YMzbqwPQqozbkrexi0lNZrUz5cnPHalYpFWe6Dau7AmKIWNo+y24IwzDuZEQtEdUHuKhXmMMih/MPjOI+CMw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.155.0.tgz", + "integrity": "sha512-8heq6jGq+rfpTDRDjoQiBHw0maQZf3IXkS99pdHQZPoTL5kyF5qyPS0RidUlSdnWDIqxXb5nux7GBD9cT+imUw==", "cpu": [ "x64" ], @@ -2417,9 +2425,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz", - "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", "dev": true, "license": "MIT", "funding": { @@ -2721,16 +2729,6 @@ "win32" ] }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@redis/bloom": { "version": "5.12.1", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.12.1.tgz", @@ -2825,9 +2823,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.1.tgz", - "integrity": "sha512-i4YPS4B6ts7bjn7UwLnGjiZdprOvHvgGobFZsYK3GIY3E5hIqtj0rReU69BcTpGp+fvtraSNXeG1l+jtJvF55w==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.38.1.tgz", + "integrity": "sha512-U/mB2XiwTLaf+NSgLSWwJfptsMT/H8aBi0aTAA/E5e2/TagFeGCqAyhKGG0w0A9IWTkm4qYEmLoD/HB6aYfnRA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.19.0", @@ -2866,13 +2864,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", - "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.14.1.tgz", + "integrity": "sha512-/T6E1jzCL0fErS9uq8JqES/5yYQQ4ppVmmYHwXw2nlov8KFzLb+3QqHR5iAun1BsuAB50IPxZliV8YMgghLwhA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.18.0", + "@smithy/core": "^3.38.1", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -2880,12 +2878,12 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", - "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", + "version": "5.7.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.5.tgz", + "integrity": "sha512-WPjdr5oWOk1LArpI4pavu+fSo+/evi0Re1olxGvqwuZopd4gYR1UGCi+Soyn/IjmafqbWs+ZC+3AKxJS3ZpwUg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.35.0", + "@smithy/core": "^3.38.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, @@ -3017,6 +3015,259 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.12.tgz", + "integrity": "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.9.tgz", + "integrity": "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -3025,9 +3276,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.10.tgz", + "integrity": "sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==", "dev": true, "license": "MIT" }, @@ -3066,6 +3317,12 @@ "@types/express": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -3104,6 +3361,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -3121,9 +3385,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", - "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", + "version": "26.6.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.6.tgz", + "integrity": "sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog==", "license": "MIT", "dependencies": { "undici-types": "~8.9.0" @@ -3267,6 +3531,34 @@ "@types/node": "*" } }, + "node_modules/@types/topojson-client": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/topojson-client/-/topojson-client-3.1.5.tgz", + "integrity": "sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/topojson-specification": "*" + } + }, + "node_modules/@types/topojson-specification": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/topojson-specification/-/topojson-specification-1.0.5.tgz", + "integrity": "sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unzip-stream": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@types/unzip-stream/-/unzip-stream-0.3.4.tgz", @@ -3294,17 +3586,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.1.tgz", - "integrity": "sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.1.tgz", + "integrity": "sha512-5CokTWXpmbylfFabsbvHC0CAw3XLaGnoa+nFJX5FR592h/LoZsENUE6p/rg5xvkKrFEXz6pYmgI29AgXc2N3BQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/type-utils": "8.71.1", - "@typescript-eslint/utils": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/type-utils": "8.72.1", + "@typescript-eslint/utils": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -3317,15 +3609,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.71.1", + "@typescript-eslint/parser": "^8.72.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.12.tgz", - "integrity": "sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -3333,16 +3625,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.1.tgz", - "integrity": "sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.1.tgz", + "integrity": "sha512-ZGwZP4OCfyA0AQTpEAMWz+rtX01DRhQWW5hB0ucKDEIX5U7jdmPSaMx94rQfaPAQzIYz2BimOgKOJtjfpRFQ9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", "debug": "^4.4.3" }, "engines": { @@ -3383,14 +3675,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/project-service": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.1.tgz", - "integrity": "sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.1.tgz", + "integrity": "sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.71.1", - "@typescript-eslint/types": "^8.71.1", + "@typescript-eslint/tsconfig-utils": "^8.72.1", + "@typescript-eslint/types": "^8.72.1", "debug": "^4.4.3" }, "engines": { @@ -3430,14 +3722,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.1.tgz", - "integrity": "sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.1.tgz", + "integrity": "sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1" + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3448,9 +3740,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.1.tgz", - "integrity": "sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.1.tgz", + "integrity": "sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==", "dev": true, "license": "MIT", "engines": { @@ -3465,15 +3757,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.1.tgz", - "integrity": "sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.1.tgz", + "integrity": "sha512-iY2wlmyrXJmx4yxUGP3aYCFF3cA/6ShddOKmLU2dx7LnEykbAM8WThlxZEfd1NddqceF6eJmZUayxx9oKIYNlg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/utils": "8.71.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1", + "@typescript-eslint/utils": "8.72.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -3515,9 +3807,9 @@ "license": "MIT" }, "node_modules/@typescript-eslint/types": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.1.tgz", - "integrity": "sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.1.tgz", + "integrity": "sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==", "dev": true, "license": "MIT", "engines": { @@ -3529,16 +3821,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.1.tgz", - "integrity": "sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.1.tgz", + "integrity": "sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.71.1", - "@typescript-eslint/tsconfig-utils": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/visitor-keys": "8.71.1", + "@typescript-eslint/project-service": "8.72.1", + "@typescript-eslint/tsconfig-utils": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3582,16 +3874,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/utils": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.1.tgz", - "integrity": "sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.1.tgz", + "integrity": "sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.71.1", - "@typescript-eslint/types": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1" + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3606,13 +3898,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.1.tgz", - "integrity": "sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.1.tgz", + "integrity": "sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.71.1", + "@typescript-eslint/types": "8.72.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3623,18 +3915,28 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vue/compiler-core": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", - "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.44.tgz", + "integrity": "sha512-k34nCytbK8PQ1gR2mGROYOw6Pnv/mumqIdrZ3x2tbyXZDWm7ViKGLTfdB+DRrWqEQsLgGJvn0WN9jBif5zCTLQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.8", - "@vue/shared": "3.5.43", + "@babel/parser": "^7.29.9", + "@vue/shared": "3.5.44", "entities": "^7.0.1", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" } }, "node_modules/@vue/compiler-core/node_modules/entities": { @@ -3651,43 +3953,43 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", - "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.44.tgz", + "integrity": "sha512-W5AoBFWdw1lQAKiHowkfgFKFab82oSRT6sGsPfw/q1GRjsy/ZJFLHGhJyBRIFN9APLTUduDH9U2JAiPOIyKuYg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.43", - "@vue/shared": "3.5.43" + "@vue/compiler-core": "3.5.44", + "@vue/shared": "3.5.44" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", - "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.44.tgz", + "integrity": "sha512-dzHmQ4jXTpdr5FjIQUeykVakN/uzqFbUo9/C5ikBFg3a3PeM6uRxeczx1ZbXBML0/zJstEh3ygYipzR5Z7b2Pw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.8", - "@vue/compiler-core": "3.5.43", - "@vue/compiler-dom": "3.5.43", - "@vue/compiler-ssr": "3.5.43", - "@vue/shared": "3.5.43", + "@babel/parser": "^7.29.9", + "@vue/compiler-core": "3.5.44", + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-ssr": "3.5.44", + "@vue/shared": "3.5.44", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", - "postcss": "^8.5.28", - "source-map-js": "^1.2.1" + "postcss": "^8.5.29", + "source-map-js": "^1.2.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", - "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.44.tgz", + "integrity": "sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.43", - "@vue/shared": "3.5.43" + "@vue/compiler-dom": "3.5.44", + "@vue/shared": "3.5.44" } }, "node_modules/@vue/devtools-api": { @@ -3698,55 +4000,55 @@ "license": "MIT" }, "node_modules/@vue/reactivity": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", - "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.44.tgz", + "integrity": "sha512-u+QJdKeBBEADkPdevsL+6E12QlYVfBD8Zcw5qjDQKtamI6l14KyqazJMobhCKelhWN5zpyKUiEDoTyHE93cqWw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/shared": "3.5.43" + "@vue/shared": "3.5.44" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", - "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.44.tgz", + "integrity": "sha512-uEa/ndwjhrrhnOfb2p+sZy6zeoamJOISqaLSJGk/8R6nABT1ZldpzcJoPzKj+zHVomVBPcmQYyC2r4LtvDV6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.43", - "@vue/shared": "3.5.43" + "@vue/reactivity": "3.5.44", + "@vue/shared": "3.5.44" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", - "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.44.tgz", + "integrity": "sha512-3fc4ARcXVhVBaJ3ujWiNZJARD6pkyRcwG+ZYhMzjKtKUp7MAY0HLwr7r3Smep3Li6ZaWT6ewdmx8XqdaD5oNZg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.43", - "@vue/runtime-core": "3.5.43", - "@vue/shared": "3.5.43", + "@vue/reactivity": "3.5.44", + "@vue/runtime-core": "3.5.44", + "@vue/shared": "3.5.44", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", - "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.44.tgz", + "integrity": "sha512-R4S1UoH4h5Frrauv8Z5XE5TxV9oBiUSgWIC4TyVK1FK+RocRnOLwFA5Xcmo2011RlgJp+AbZCnw6afrhlgRuUQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.43", - "@vue/runtime-dom": "3.5.43", - "@vue/shared": "3.5.43" + "@vue/compiler-ssr": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/shared": "3.5.44" } }, "node_modules/@vue/shared": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", - "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.44.tgz", + "integrity": "sha512-H82XLH0DjmSOdbEwO15x3QTo1f5PWEtv237eLpYKc0mqLu+B8hnq16lFWmuBmNurkeiG3wr+oM1FhOll/YVFRg==", "dev": true, "license": "MIT" }, @@ -3775,19 +4077,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/accepts/node_modules/negotiator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", @@ -3990,11 +4279,21 @@ "license": "MIT" }, "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.4.tgz", + "integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==", "dev": true, - "license": "Python-2.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "PSF-2.0" }, "node_modules/arr-diff": { "version": "4.0.0", @@ -4131,7 +4430,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -4152,9 +4450,9 @@ } }, "node_modules/bare-fs": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.2.tgz", - "integrity": "sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.4.tgz", + "integrity": "sha512-MPC2eyrpbk3A873sK4y6rk+Iv4o2enxTjHZ8zH3HO0mjrpm5XjnguY2XmfidIsDV6vnjpbMBKUAJpV0ISD2SsQ==", "license": "Apache-2.0", "dependencies": { "bare-events": "^2.5.4", @@ -4209,9 +4507,9 @@ } }, "node_modules/bare-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", - "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.5.tgz", + "integrity": "sha512-iM9qJCruvpt2rPaLsrumVDru9pogI7PHziHIa9kTNRFt8C8HuSa+kI9kFezK+NvHEFnnz71rKGZ1EAilq/3V/Q==", "license": "Apache-2.0", "dependencies": { "bare-path": "^3.0.0" @@ -4301,18 +4599,18 @@ } }, "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.4.0.tgz", + "integrity": "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^2.0.0", + "content-type": "^2.1.0", "debug": "^4.4.3", "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", + "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", - "qs": "^6.15.2", + "qs": "^6.16.1", "raw-body": "^3.0.2", "type-is": "^2.1.0" }, @@ -4324,19 +4622,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/body-parser/node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -4367,16 +4652,15 @@ "license": "MIT" }, "node_modules/bowser": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.15.0.tgz", + "integrity": "sha512-xic6KSZglg/I4ymp45fdM4GWfYpBK79Fxd/TOvJktlaPgQqqDxvmGwOPZl2V0dyeS3Lj5c180bLhXF43iyAjZQ==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -4481,6 +4765,12 @@ } } }, + "node_modules/bullmq/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4687,6 +4977,22 @@ "node": "*" } }, + "node_modules/chevrotain": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-13.2.0.tgz", + "integrity": "sha512-GTouk5YzgBxlTPCaDn5q0q2EJKkrYmnPF+HmF8v0Qo4r3iyPG7HJivkN1wthCXZcm3sIcZFpGTRovLPNdJNRCA==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "13.2.0", + "@chevrotain/gast": "13.2.0", + "@chevrotain/regexp-to-ast": "13.2.0", + "@chevrotain/types": "13.2.0", + "@chevrotain/utils": "13.2.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, "node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", @@ -4765,9 +5071,9 @@ } }, "node_modules/cliui/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "dev": true, "license": "MIT", "engines": { @@ -4968,6 +5274,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -5043,9 +5358,9 @@ } }, "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", "license": "MIT", "engines": { "node": ">=18" @@ -5056,12 +5371,16 @@ } }, "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/convert-source-map": { @@ -5095,6 +5414,15 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -5144,6 +5472,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -5182,6 +5511,526 @@ "dev": true, "license": "MIT" }, + "node_modules/cytoscape": { + "version": "3.34.4", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.4.tgz", + "integrity": "sha512-msY99tMY8iWTg+sIPGfSqDcPNOt4xcj+v/K/7qUFaCUxxlqnMkUYLBjRUzn9W5CU/R2hrmtJa5+UtovrEM2ctg==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, "node_modules/data-urls": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", @@ -5377,15 +6226,6 @@ "dev": true, "license": "MIT" }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -5450,6 +6290,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -5553,6 +6402,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", + "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz", @@ -5572,9 +6430,9 @@ } }, "node_modules/dotenv": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-18.0.6.tgz", - "integrity": "sha512-ZMVmohV6CvAJtj++pZNyggluUt6hbRdQAxc2+Z635RL6/AG/ruXv5lHnybUFoxMPL/yrTL/tsF4paZu2l7RA6Q==", + "version": "18.0.9", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-18.0.9.tgz", + "integrity": "sha512-3fVOW4cB02R066PUSwn8O4TJyz9NckQl7F81blIbaq9jKBm2tSmOKky7ZHIXzGvkVPXCSn6jkpziBPZdGFs8Tw==", "license": "BSD-2-Clause", "bin": { "dotenv": "dist/index.cjs" @@ -5648,18 +6506,18 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "license": "EPL-2.0" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5667,12 +6525,16 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.1.tgz", + "integrity": "sha512-Ev/hIyePLZtpL98x562lvPtenrUfGr0lfaf44HFZ+hDEwso7ccV9QAZrEVLg0hxNAzygaAFxy2dYaAKKM0G06A==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/end-of-stream": { @@ -5847,6 +6709,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.53.1.tgz", + "integrity": "sha512-VhMc6IXmexu8iYFfL2aaAww6IvJTdHlqBLruLoEUs33YD4IcP5DdZcLOybMsMmmLsiy1c8PsiLsrM0DdIuuRUA==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, "node_modules/esbuild": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", @@ -5918,9 +6792,9 @@ } }, "node_modules/eslint": { - "version": "10.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.12.0.tgz", - "integrity": "sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==", + "version": "10.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.14.0.tgz", + "integrity": "sha512-T0pNsxkb9IKmtQjhpK3d7nWOpFv+YHuMbTJnSVMoQblF0onvXfErrtp4V1jHtsQPan7dKaGVD0q1WCXnzMF31A==", "dev": true, "license": "MIT", "workspaces": [ @@ -5933,7 +6807,7 @@ "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.3", - "@humanfs/node": "^0.16.6", + "@humanfs/node": "^0.16.8", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", @@ -5954,7 +6828,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.5", + "minimatch": "^10.2.6", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -6141,15 +7015,15 @@ } }, "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", @@ -6165,14 +7039,14 @@ "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", - "type-is": "^2.0.1", + "type-is": "^2.1.0", "vary": "^1.1.2" }, "engines": { @@ -6184,9 +7058,9 @@ } }, "node_modules/express-rate-limit": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz", - "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==", + "version": "8.7.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.2.tgz", + "integrity": "sha512-U1ScocxDD9WI09LaBVypvNkOtNR5yWAmZLkuKJdAzIchA4YKFYF6GT5IKER7epG58oXtjvGlV0/6ii0F83EyZg==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -6530,6 +7404,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -6543,9 +7418,9 @@ } }, "node_modules/formatly": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.7.1.tgz", - "integrity": "sha512-ESuCEJE0SGgabl9q+zABI8LhJDmF+8nhpuqDzuM48HfJ9QTD5o6PqFJDFZY7w6f8xsh5lixuj5nJsQNvYOWvKQ==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.11.4.tgz", + "integrity": "sha512-TeCC10lP8KCgoX+Fe5lryILIT0/Dy+nP0OSVnsuB5CwXoLuOdqxCYlPFF1inR8S/zTy1ec7TjhS2TeRKWzzImA==", "dev": true, "license": "MIT", "dependencies": { @@ -6556,7 +7431,7 @@ "formatly": "bin/index.mjs" }, "engines": { - "node": ">=18.3.0" + "node": ">=18.14.0" } }, "node_modules/forwarded": { @@ -6756,22 +7631,24 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-14.0.3.tgz", + "integrity": "sha512-A1vjIwC3qpdCso45Iw127gnp7sPhqgY8lUQhDrArIs+GbmfaaVPFuoyy7WyLT9EPZCRZ7j7aLdwUhQmHWesEow==", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "minimatch": "^11.0.2", + "minipass": "^7.1.3", + "path-scurry": "^3.0.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6791,9 +7668,9 @@ } }, "node_modules/glob-stream": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", - "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.4.tgz", + "integrity": "sha512-7uzFVQ1ZUBr0nYexxubOvocZXQ4HcHv6WO3TOiYUPaCda7BASuKgOw/NB4Pc9gHM3tK0Jrekq7el8pKhb8kx5w==", "dev": true, "license": "MIT", "dependencies": { @@ -6810,36 +7687,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -7146,6 +7993,12 @@ "xtend": "~4.0.1" } }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -7259,6 +8112,15 @@ "node": ">= 0.4" } }, + "node_modules/highlight.js": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz", + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", @@ -7335,9 +8197,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.0.tgz", - "integrity": "sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.1.tgz", + "integrity": "sha512-60SmYZpPD4dPbC2GZVkVV4uP/V/cbsXSwzGQXeHWbbE3Q1XdccJpqHTg6NJ6iMpFGVA1OEry8c8kHhx4ahiU1w==", "dev": true, "license": "MIT", "dependencies": { @@ -7390,9 +8252,9 @@ } }, "node_modules/httpxy": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.5.5.tgz", - "integrity": "sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.5.6.tgz", + "integrity": "sha512-iqc3nOxvNb5FxbhJozt8wNmHgjlmk4zzba512jMEXVwHCEBCbGAj49eGfy5PI8ts3/LFSFq8siiSNqwfY1AlfQ==", "dev": true, "license": "MIT" }, @@ -7442,6 +8304,16 @@ "node": ">= 4" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -7498,6 +8370,15 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/ioredis": { "version": "5.11.1", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", @@ -7544,9 +8425,9 @@ "license": "MIT" }, "node_modules/ip-address": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", - "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", + "version": "10.7.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.5.tgz", + "integrity": "sha512-t2v0rkILYv9WXmSL7TpAaU3HXgaFYOPUcH3QHgwQ++gpJuH3bqyMy4Fvt/DJCTKwzH3Cf+bAp5W/mFL39KPLzA==", "license": "MIT", "engines": { "node": ">= 12" @@ -8095,6 +8976,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -8146,21 +9028,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -8172,9 +9039,9 @@ } }, "node_modules/js-yaml": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.3.tgz", - "integrity": "sha512-yzzNtgczwgoVHNigCyKTXcQJU4Y8t/ikU4UxXW0BvrLfHTGWZhsNcOT2s/a7vn81/4KqVnNmHjaUEPlvWnNwVw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.5.0.tgz", + "integrity": "sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==", "dev": true, "funding": [ { @@ -8188,7 +9055,7 @@ ], "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "argparse": "^3.0.3" }, "bin": { "js-yaml": "bin/js-yaml.mjs" @@ -8264,6 +9131,22 @@ "node": ">=12.0.0" } }, + "node_modules/katex": { + "version": "0.18.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.10.tgz", + "integrity": "sha512-/B6p9eY9DX7aHBfpkHdpirDTZ5QH9xTwL9y837PWuzuv41O5jFdqNQwVQEQsimY0/iVNkwBY4+4hMfhQ7d4Dbw==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^15.0.0" + }, + "bin": { + "katex": "cli.js" + } + }, "node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", @@ -8274,10 +9157,15 @@ "@keyv/serialize": "^1.1.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, "node_modules/knip": { - "version": "6.40.0", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.40.0.tgz", - "integrity": "sha512-5Lp/ANJu3AkW5f+J+2QG14z7xKZGi/ybb5MU1EWjeGdt6MpuIaLpLFiyBi0ZKryTiuTHiJqo/WKJRaWYimvavw==", + "version": "6.44.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.44.1.tgz", + "integrity": "sha512-Hbd8nvkIsNgFfXgC7Jsb5LHv7jj6VwA3tmfHd53XPX2z9Vc9RnAZqV0BIiP4OrtYqKwCoLseFHQZerb1bNlbSw==", "dev": true, "funding": [ { @@ -8291,19 +9179,18 @@ ], "license": "ISC", "dependencies": { - "fdir": "^6.5.0", - "formatly": "^0.7.0", + "formatly": "^0.11.4", "get-tsconfig": "4.14.3", "jiti": "^2.7.0", - "oxc-parser": "^0.150.0", + "oxc-parser": "^0.155.0", "oxc-resolver": "11.24.2", "picomatch": "^4.0.7", - "smol-toml": "^1.8.0", + "smol-toml": "^1.9.0", "strip-json-comments": "5.0.3", "tinyglobby": "^0.2.17", - "unbash": "^4.0.11", + "unbash": "^6.0.1", "yaml": "^2.9.1", - "zod": "^4.4.3" + "zod": "^4.6.5" }, "bin": { "knip": "bin/knip.js", @@ -8322,6 +9209,12 @@ "dayjs": "^1.11.23" } }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -8416,6 +9309,12 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -8461,7 +9360,6 @@ "version": "11.5.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -8531,6 +9429,46 @@ "node": ">= 18" } }, + "node_modules/marked-alert": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/marked-alert/-/marked-alert-2.1.2.tgz", + "integrity": "sha512-EFNRZ08d8L/iEIPLTlQMDjvwIsj03gxWCczYTht6DCiHJIZhMk4NK5gtPY9UqAYb09eV5VGT+jD4lp396E0I+w==", + "license": "MIT", + "peerDependencies": { + "marked": ">=7.0.0" + } + }, + "node_modules/marked-footnote": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/marked-footnote/-/marked-footnote-1.4.0.tgz", + "integrity": "sha512-fZTxAhI1TcLEs5UOjCfYfTHpyKGaWQevbxaGTEA68B51l7i87SctPFtHETYqPkEN0ka5opvy4Dy1l/yXVC+hmg==", + "license": "MIT", + "peerDependencies": { + "marked": ">=7.0.0" + } + }, + "node_modules/marked-gfm-heading-id": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.4.tgz", + "integrity": "sha512-CspnvVfHSkb/znqdPS4jUR8HtCjq3M/DnrsJCrfLBLvdrgbemmoINKpeWKQYkBiXAoBGejw0cV7xzqrPdup3WA==", + "license": "MIT", + "dependencies": { + "github-slugger": "^2.0.0" + }, + "peerDependencies": { + "marked": ">=13 <19" + } + }, + "node_modules/marked-katex-extension": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/marked-katex-extension/-/marked-katex-extension-5.1.13.tgz", + "integrity": "sha512-+6gVygGg5at9g+YsuIQZk7XcAc6Pg1P0grEJKEWvF7DAwceZ7ZIvU0fhQe6auUqSbnPmSjJxZuICdSCdbGaFRw==", + "license": "MIT", + "peerDependencies": { + "katex": ">=0.16 <0.19", + "marked": ">=4 <19" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -8579,6 +9517,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mermaid": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-12.2.0.tgz", + "integrity": "sha512-w7nr/SFrLO7BaVTs3dQTvYSV6nYSBj0Yy9DjBhsIJtAcHqRzXSq4Pg32ujHZ/AdWsAMNEvvhbm5dt9E2tOiHzw==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^2.0.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "chevrotain": "~13.2.0", + "cytoscape": "^3.34.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.21", + "dompurify": "^3.4.12", + "elkjs": "^0.9.3", + "es-toolkit": "^1.45.1", + "katex": "^0.18.0", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -8650,16 +9634,16 @@ } }, "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.4.tgz", + "integrity": "sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.8" + "@isaacs/nonrecursive-glob-match": "^1.0.3", + "brace-expansion": "^5.0.12" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8744,24 +9728,6 @@ } } }, - "node_modules/mocha/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8769,23 +9735,6 @@ "dev": true, "license": "MIT" }, - "node_modules/mocha/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -8888,16 +9837,16 @@ } }, "node_modules/mongoose": { - "version": "7.8.12", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.12.tgz", - "integrity": "sha512-OLM9mdTuAc8oNDSJ9yoNtsMFFvHxQh/QAWR+yW0GwsXPywn/sdfHfOZnD2rZnNCMl7mo8EZM4zAK4ZOTuq7sMg==", + "version": "7.8.13", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.13.tgz", + "integrity": "sha512-Fd3yPwXQElXRmclX8ypcGcCT/JZtoLcn0GOj6g7FKIw6qngr1vz+QTBCTC4GuwQT9aKxE4SD24nIeujRbWjWNA==", "license": "MIT", "dependencies": { "bson": "^5.5.0", "kareem": "2.5.1", "mongodb": "5.9.2", "mpath": "0.9.0", - "mquery": "5.0.0", + "mquery": "5.0.1", "ms": "2.1.3", "sift": "16.0.1" }, @@ -8925,9 +9874,9 @@ } }, "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.1.tgz", + "integrity": "sha512-C99lWV0TFI34pS008jBsi3JrE2xZPnTgUw5yApQR71nRP0hxLy8D5m1Lbbk83c8AQWJwdJ0329HzFtZ1MRTUIw==", "license": "MIT", "dependencies": { "debug": "4.x" @@ -8975,9 +9924,9 @@ } }, "node_modules/msgpackr-extract": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", - "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.6.tgz", + "integrity": "sha512-prUB8+Gc/1rEzaNo+3Rjo0Hdi6gFjQ9Td0mDsvAIkSGUyp6EADdEBiU71ZP5Mj4jCG+vai0qSNV+yisQAMYuSQ==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -8988,12 +9937,12 @@ "download-msgpackr-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.6", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.6", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.6", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.6", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.6", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.6" } }, "node_modules/mute-stream": { @@ -9058,9 +10007,9 @@ } }, "node_modules/node-schedule": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", - "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.2.tgz", + "integrity": "sha512-JuKopr93W+WP+TaK5yoHJGpzPF9fpkH8Vcc9CXGE993BNeRKsb3L4Wy6FzL0VxkEa6U+8M4Ni0HEm9rMMnwurg==", "license": "MIT", "dependencies": { "cron-parser": "^4.2.0", @@ -9153,15 +10102,19 @@ } }, "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.2.tgz", + "integrity": "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/on-headers": { @@ -9270,13 +10223,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.150.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.150.0.tgz", - "integrity": "sha512-zwajKw1GUa57IOdafIS7/yOObGF2NihwIfZfs5yJTRprMSkiwhWOc/k+n8bwnUPbHr2/XsjfBO8KyTPaW3VpWg==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.155.0.tgz", + "integrity": "sha512-C/ReSf1JUFukK/zvk6jhcNibSssvas5+ktIzK7fbKVd3CedWe59C3QwHEOrEZj/4Rt9+xg0Jeis/Jv1F/KM1mw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.150.0" + "@oxc-project/types": "^0.155.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -9285,25 +10238,25 @@ "url": "https://github.com/sponsors/oxc-project" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.150.0", - "@oxc-parser/binding-android-arm64": "0.150.0", - "@oxc-parser/binding-darwin-arm64": "0.150.0", - "@oxc-parser/binding-darwin-x64": "0.150.0", - "@oxc-parser/binding-freebsd-x64": "0.150.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.150.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.150.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.150.0", - "@oxc-parser/binding-linux-arm64-musl": "0.150.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.150.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.150.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.150.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.150.0", - "@oxc-parser/binding-linux-x64-gnu": "0.150.0", - "@oxc-parser/binding-linux-x64-musl": "0.150.0", - "@oxc-parser/binding-openharmony-arm64": "0.150.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.150.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.150.0", - "@oxc-parser/binding-win32-x64-msvc": "0.150.0" + "@oxc-parser/binding-android-arm-eabi": "0.155.0", + "@oxc-parser/binding-android-arm64": "0.155.0", + "@oxc-parser/binding-darwin-arm64": "0.155.0", + "@oxc-parser/binding-darwin-x64": "0.155.0", + "@oxc-parser/binding-freebsd-x64": "0.155.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.155.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.155.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.155.0", + "@oxc-parser/binding-linux-arm64-musl": "0.155.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.155.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.155.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.155.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.155.0", + "@oxc-parser/binding-linux-x64-gnu": "0.155.0", + "@oxc-parser/binding-linux-x64-musl": "0.155.0", + "@oxc-parser/binding-openharmony-arm64": "0.155.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.155.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.155.0", + "@oxc-parser/binding-win32-x64-msvc": "0.155.0" } }, "node_modules/oxc-resolver": { @@ -9378,17 +10331,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, "node_modules/package-manager-detector": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", - "dev": true, "license": "MIT" }, "node_modules/pako": { @@ -9416,18 +10362,31 @@ "license": "MIT" }, "node_modules/parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", - "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.1.0.tgz", + "integrity": "sha512-sq3qBFpee5jeocbFbO48VHlGr8sT7mbXS0vxlKN6sottBtJzrOG3998sr0llSZcQAGgfpdmEYAIZE0P9z0dE8w==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^8.0.0" + "entities": "^9.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5/node_modules/entities": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-9.0.0.tgz", + "integrity": "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -9494,6 +10453,12 @@ "node": ">= 0.4.0" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9508,33 +10473,28 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-3.0.0.tgz", + "integrity": "sha512-1pTbQqH1wIwMi/eleQ5leFTTCkzFsPcgXNjwOA8YGGW/W4/kHT3dssE7Z6NH+zuqNLOzOB0uaD61yl7cAcVhgg==", "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, "node_modules/path-to-regexp": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", @@ -9652,6 +10612,22 @@ "node": ">= 0.10" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -9662,9 +10638,9 @@ } }, "node_modules/postcss": { - "version": "8.5.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", - "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", "funding": [ { "type": "opencollective", @@ -9681,7 +10657,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.19", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" }, @@ -9792,9 +10768,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.17.1.tgz", + "integrity": "sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -9842,9 +10818,9 @@ } }, "node_modules/rate-limit-redis": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-6.0.1.tgz", - "integrity": "sha512-qys7ulbh5X4Bohfjm6BXFWBiyzK6r5eGdFghPWZfJwggnDFTDwCIRq1u7tmN50vLTp4Kbe1h/ZAuBOn/hVpjtQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-6.0.2.tgz", + "integrity": "sha512-EpUHNriVypMSM/DyaSaweK496XMgTj+FgDXzqoxZIFuLZPfDPOpGhAZS4eDgJmseOaR1nlJNhBJd1oifDAj5qg==", "license": "MIT", "engines": { "node": ">= 16" @@ -9869,12 +10845,12 @@ } }, "node_modules/re2js": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/re2js/-/re2js-2.8.6.tgz", - "integrity": "sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/re2js/-/re2js-2.9.1.tgz", + "integrity": "sha512-JIT3bEEUZTOR2YS1walNEIfH4cF/cQeZcBUFq7ldqO6lR6g0X5CBq/S8rsa9omzZcsNf8qc36jCkWpKKtncrZg==", "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/readable-stream": { @@ -9926,33 +10902,6 @@ "minimatch": "^5.1.0" } }, - "node_modules/readdir-glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/readdirp": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", @@ -10144,6 +11093,24 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -10192,6 +11159,12 @@ "node": ">=0.12.0" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -10280,12 +11253,11 @@ "license": "MIT" }, "node_modules/sanitize-html": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.18.0.tgz", - "integrity": "sha512-CvY+PV+NBhxe3BnjFI5f//vEDKLULm9OlZArso7gdgVsWvanBuq9PNDx+/2llfL+8XFYa/UNBxvqRBg3TWU2Ug==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.18.1.tgz", + "integrity": "sha512-2X5lC8fYla8YaT8g73Nj+HcLWXan5dLyCWLgVvaOmfw7JI78IRsyNtIDytTHdn7hBQbIkCah7Zbmd5jabzeDig==", "license": "MIT", "dependencies": { - "deepmerge": "^4.3.1", "escape-string-regexp": "^4.0.0", "htmlparser2": "^12.0.0", "is-plain-object": "^5.1.0", @@ -10381,9 +11353,9 @@ "license": "MIT" }, "node_modules/serialize-javascript": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.1.2.tgz", - "integrity": "sha512-GL2BWwVa6JydKO6l/ljVgjAZF4QJ3S7dWDWi53s5GZT8PJzD2fNxi67HANQGKAqPrwEpL5ba7gUBGi0Ls/sEoQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.1.4.tgz", + "integrity": "sha512-FqlEuoTq/zRmA4Lg6aOqyH3lCHbUtCB0bmHCeFniX2GBdDqMJM3FwdvEhlE5Pim1dlt2FfBdVPmUNNiGhhKqpw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -10465,6 +11437,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -10477,6 +11450,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10564,6 +11538,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -10583,9 +11558,9 @@ } }, "node_modules/smol-toml": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz", - "integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.1.tgz", + "integrity": "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -10756,21 +11731,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.trim": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", @@ -10840,19 +11800,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", @@ -10883,6 +11830,12 @@ } } }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10938,41 +11891,6 @@ "node": "20 || >=22" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/text-decoder": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", @@ -11013,6 +11931,15 @@ "node": ">= 6" } }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -11031,22 +11958,22 @@ } }, "node_modules/tldts": { - "version": "7.4.18", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.18.tgz", - "integrity": "sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==", + "version": "7.4.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.19.tgz", + "integrity": "sha512-48dH6aSpQnpbzDUc4vIQZ1VjB8tkK+e8c1yx05/SZK+rkssgjEOlxDd/yU8/a4vGYQ9vRgJr9dwMq9TJ0b3X2Q==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.4.18" + "tldts-core": "^7.4.19" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.4.18", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.18.tgz", - "integrity": "sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==", + "version": "7.4.20", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.20.tgz", + "integrity": "sha512-C630TWXZCZmLq0F2bW4QqwnaaxCQ/svpAqvJwnC4JvgEM5ohhBnGsEzTLSOXcN+xjz+kEaHS5uoWIw2klnqd0A==", "dev": true, "license": "MIT" }, @@ -11085,6 +12012,26 @@ "node": ">=0.6" } }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/tough-cookie": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", @@ -11152,6 +12099,15 @@ "node": ">=14.0.0" } }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -11207,9 +12163,9 @@ } }, "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", "license": "0BSD" }, "node_modules/type-check": { @@ -11265,19 +12221,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -11388,16 +12331,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.71.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.71.1.tgz", - "integrity": "sha512-oiNVPC3/NbV1FpaHU07l8O6ynqG9v4PAiW2WvTrAmZdR0f2HI/gE668ElMLCK7Xbsu5k2fK5C4fXNiq05j7/nQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.72.1.tgz", + "integrity": "sha512-3Li9FfPmQp45nxEtFD1ZCrYFI3cYK5kpyNtmm1H7lMagm8u9sy6PTjy3ytwQZs7KNukOyKFHAZ6z6GjYFeaeXw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.71.1", - "@typescript-eslint/parser": "8.71.1", - "@typescript-eslint/typescript-estree": "8.71.1", - "@typescript-eslint/utils": "8.71.1" + "@typescript-eslint/eslint-plugin": "8.72.1", + "@typescript-eslint/parser": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1", + "@typescript-eslint/utils": "8.72.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11443,9 +12386,9 @@ "license": "MIT" }, "node_modules/unbash": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.12.tgz", - "integrity": "sha512-PnKvRX+Fbxl/usVlTr/31tXIOVAH+8OdC9qaxUQcvurwEnoc3Vk/ipi2Ce8IjMxzK9byfSGdsUBjqqJGc1o5lw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-6.0.3.tgz", + "integrity": "sha512-hRzSpMeM8uYMQkrnIVaVy3f44oLlokglVeEyP5hIQj88z60bKPO9VVbEpGfxChGBh9lBkzuWKOTOzPdfD6KNEA==", "dev": true, "license": "ISC", "engines": { @@ -11471,9 +12414,9 @@ } }, "node_modules/undici": { - "version": "7.30.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", - "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", "dev": true, "license": "MIT", "engines": { @@ -11536,6 +12479,19 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -11828,17 +12784,17 @@ } }, "node_modules/vue": { - "version": "3.5.43", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", - "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "version": "3.5.44", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.44.tgz", + "integrity": "sha512-dvvmaOCG7lAMkJTPqPwWeGMsPS6DIsFsfZDUgBp9V6mHf9b3+/wyVwYR4Es+WV6uJC0FKRxp81Obcq0ZwUcGFQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.43", - "@vue/compiler-sfc": "3.5.43", - "@vue/runtime-dom": "3.5.43", - "@vue/server-renderer": "3.5.43", - "@vue/shared": "3.5.43" + "@vue/compiler-dom": "3.5.44", + "@vue/compiler-sfc": "3.5.44", + "@vue/runtime-dom": "3.5.44", + "@vue/server-renderer": "3.5.44", + "@vue/shared": "3.5.44" }, "peerDependencies": { "typescript": "*" @@ -11936,6 +12892,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -12063,24 +13020,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -12168,9 +13107,9 @@ } }, "node_modules/yargs/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "dev": true, "license": "MIT", "engines": { @@ -12261,9 +13200,9 @@ } }, "node_modules/zod": { - "version": "4.6.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", - "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 79305ee..57b6f98 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test:performance": "mocha test/performance-*.test.js", "benchmark:performance": "node scripts/performance-benchmark.js", "build:ui": "node gulpfile.js", - "test:ui": "npm run build:ui && mocha test/frontend-regressions.test.js test/dashboard-ui.test.js test/vue-ui.test.js" + "test:ui": "npm run build:ui && mocha test/frontend-regressions.test.js test/dashboard-ui.test.js test/vue-ui.test.js", + "vendor:markdown": "node scripts/sync-markdown-vendors.js" }, "repository": { "type": "git", @@ -41,38 +42,48 @@ "build" ], "dependencies": { - "@aws-sdk/client-s3": "^3.1147.0", - "@aws-sdk/lib-storage": "^3.1147.0", + "@aws-sdk/client-s3": "^3.1161.0", + "@aws-sdk/lib-storage": "^3.1161.0", "@mongodb-js/zstd": "^1.2.2", "@octokit/plugin-throttling": "^8.2.0", "@octokit/rest": "^20.1.2", - "@smithy/node-http-handler": "^4.12.1", + "@smithy/node-http-handler": "^4.14.1", "archiver": "^7.0.1", "bullmq": "^5.81.5", "cacheable-lookup": "^6.1.0", "compression": "^1.8.2", "connect-redis": "^10.0.0", "crypto-js": "^4.2.0", + "d3-geo": "^3.1.1", "decompress-stream-to-s3": "^2.1.1", - "dotenv": "^18.0.6", - "express": "^5.2.1", - "express-rate-limit": "^8.7.1", + "dompurify": "^3.4.17", + "dotenv": "^18.0.9", + "express": "^5.3.0", + "express-rate-limit": "^8.7.2", "express-session": "^1.19.0", "express-slow-down": "^3.1.1", "got": "^11.8.6", + "highlight.js": "^11.11.1", "inquirer": "^8.2.7", "isbinaryfile": "^5.0.7", + "katex": "^0.18.10", "marked": "^15.0.12", + "marked-alert": "^2.1.2", + "marked-footnote": "^1.4.0", + "marked-gfm-heading-id": "^4.1.4", + "marked-katex-extension": "^5.1.13", + "mermaid": "^12.2.0", "mime-types": "^3.0.2", - "mongoose": "^7.8.12", - "node-schedule": "^2.1.1", + "mongoose": "^7.8.13", + "node-schedule": "^2.1.2", "parse-github-url": "^1.0.4", "passport": "^0.7.0", "passport-github2": "^0.1.12", - "rate-limit-redis": "^6.0.1", - "re2js": "^2.8.6", + "rate-limit-redis": "^6.0.2", + "re2js": "^2.9.1", "redis": "^5.12.1", - "sanitize-html": "^2.18.0", + "sanitize-html": "^2.18.1", + "topojson-client": "^3.1.0", "stripe": "^23.1.0", "ts-custom-error": "^3.3.1", "unzip-stream": "^0.3.4" @@ -82,36 +93,43 @@ "@types/archiver": "^7.0.0", "@types/compression": "^1.8.1", "@types/crypto-js": "^4.2.2", + "@types/d3-geo": "^3.1.1", "@types/express": "^5.0.6", "@types/express-session": "^1.19.0", "@types/inquirer": "^8.2.13", + "@types/katex": "^0.16.8", "@types/mime-types": "^3.0.1", "@types/node-schedule": "^2.1.8", "@types/parse-github-url": "^1.0.3", "@types/passport": "^1.0.17", "@types/passport-github2": "^1.2.9", "@types/sanitize-html": "^2.16.2", + "@types/topojson-client": "^3.1.5", "@types/unzip-stream": "^0.3.4", "c8": "^12.0.0", "chai": "^4.5.0", "esbuild": "^0.28.2", - "eslint": "^10.12.0", + "eslint": "^10.14.0", "gulp-clean-css": "^4.3.0", "gulp-concat": "^2.6.1", "gulp-uglify": "^3.0.2", - "http-proxy-middleware": "^4.2.0", + "http-proxy-middleware": "^4.2.1", "jsdom": "^29.1.1", - "knip": "^6.40.0", + "knip": "^6.44.1", "mocha": "^12.0.3", "ordered-read-streams": "^2.0.0", "ts-node": "^10.9.2", "typescript": "^5.9.3", - "typescript-eslint": "^8.71.1", + "typescript-eslint": "^8.72.1", "vinyl-fs": "^4.0.2", - "vue": "^3.5.43", + "vue": "^3.5.44", "vue-router": "^4.6.4" }, "engines": { "node": "^22.15.0 || ^24.0.0 || >=26.0.0" + }, + "overrides": { + "glob": "^14.0.2", + "minimatch": "^11.0.1" } } diff --git a/public/asset-manifest.json b/public/asset-manifest.json index 5832b49..9ad03c6 100644 --- a/public/asset-manifest.json +++ b/public/asset-manifest.json @@ -1,9 +1,9 @@ { - "core.min.js": "core.3b07189932.min.js", - "vendor.min.js": "vendor.0d01dc82d9.min.js", - "mermaid.min.js": "mermaid.f848a72d16.min.js", - "all.min.css": "all.1d7ab8bc01.min.css", - "markdown.min.js": "markdown.44f2e93820.min.js", + "core.min.js": "core.4a77c8ac41.min.js", + "vendor.min.js": "vendor.b5f28be82b.min.js", + "mermaid.min.js": "mermaid.a7356d5ed1.min.js", + "all.min.css": "all.eb35eabb9a.min.css", + "markdown.min.js": "markdown.6c8dfb7b1d.min.js", "pdf.min.js": "pdf.4d9cc6ff49.min.js", "notebook.min.js": "notebook.c3144c18ae.min.js", "org.min.js": "org.f61f262915.min.js", diff --git a/public/css/all.min.css b/public/css/all.min.css index ee70396..d7b09d9 100644 --- a/public/css/all.min.css +++ b/public/css/all.min.css @@ -6,4 +6,4 @@ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}/*! * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hive:before{content:"\e07f"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-innosoft:before{content:"\e080"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-perbyte:before{content:"\e083"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-uncharted:before{content:"\e084"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-watchman-monitoring:before{content:"\e087"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:'Font Awesome 5 Brands';font-style:normal;font-weight:400;font-display:block;src:url(../fonts/fa-brands-400.eot);src:url(../fonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-brands-400.woff2) format("woff2"),url(../fonts/fa-brands-400.woff) format("woff"),url(../fonts/fa-brands-400.ttf) format("truetype"),url(../fonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:'Font Awesome 5 Brands';font-weight:400}@font-face{font-family:'Font Awesome 5 Free';font-style:normal;font-weight:400;font-display:block;src:url(../fonts/fa-regular-400.eot);src:url(../fonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-regular-400.woff2) format("woff2"),url(../fonts/fa-regular-400.woff) format("woff"),url(../fonts/fa-regular-400.ttf) format("truetype"),url(../fonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:'Font Awesome 5 Free';font-weight:400}@font-face{font-family:'Font Awesome 5 Free';font-style:normal;font-weight:900;font-display:block;src:url(../fonts/fa-solid-900.eot);src:url(../fonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-solid-900.woff2) format("woff2"),url(../fonts/fa-solid-900.woff) format("woff"),url(../fonts/fa-solid-900.ttf) format("truetype"),url(../fonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:'Font Awesome 5 Free';font-weight:900}.nb-notebook{line-height:1.5}.nb-stderr,.nb-stdout{white-space:pre-wrap;margin:1em 0;padding:.1em .5em}.nb-stderr{background-color:#faa}.nb-cell+.nb-cell{margin-top:.5em}.nb-output table{border:1px solid #000;border-collapse:collapse}.nb-output th{font-weight:700}.nb-output td,.nb-output th{border:1px solid #000;padding:.25em;text-align:left;vertical-align:middle;border-collapse:collapse}.nb-notebook blockquote{border-left:5px solid #ccc;margin-left:0;padding-left:1em}.nb-cell{position:relative}.nb-raw-cell{white-space:pre-wrap;background-color:#f5f2f0;font-family:Consolas,Monaco,'Andale Mono',monospace;padding:1em;margin:.5em 0}.nb-output{min-height:1em;width:100%;overflow-x:scroll;border-right:1px dotted #ccc}.nb-output img{max-width:100%}.nb-input:before,.nb-output:before{position:absolute;font-family:monospace;color:#999;left:-7.5em;width:7em;text-align:right}.nb-input:before{content:"In [" attr(data-prompt-number) "]:"}.nb-output:before{content:"Out [" attr(data-prompt-number) "]:"}// Fix pandas dataframe formatting div[style="max-height:1000px;max-width:1500px;overflow:auto;"]{max-height:none!important}@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.10"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}.mermaid{text-align:center;margin:1em 0;overflow:auto}.mermaid svg{max-width:100%;height:auto}.dark-mode .mermaid{filter:invert(1) hue-rotate(180deg)}.dark-mode .mermaid svg{background:0 0}.dark-mode .mermaid .edgeLabel,.dark-mode .mermaid .nodeLabel{filter:invert(1) hue-rotate(180deg)}.mermaid .node circle,.mermaid .node ellipse,.mermaid .node polygon,.mermaid .node rect{stroke-width:2px}.mermaid .flowchart-link{stroke-width:2px}.mermaid .actor{fill:#f9f9f9;stroke:#333;stroke-width:2px}.mermaid .messageLine0,.mermaid .messageLine1{stroke:#333;stroke-width:1.5px}.mermaid .section0,.mermaid .section1,.mermaid .section2,.mermaid .section3{opacity:.8}.mermaid .pieChart .slice{opacity:.9}.markdown-body .mermaid{background:0 0;border:none;padding:10px}.mermaid .error{color:#d32f2f;font-family:monospace;font-size:12px;padding:10px;border:1px solid #d32f2f;border-radius:4px;background-color:#ffebee}@font-face{font-family:octicons-link;src:url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff')}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.5;color:#24292e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .pl-c{color:#6a737d}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#005cc5}.markdown-body .pl-e,.markdown-body .pl-en{color:#6f42c1}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#24292e}.markdown-body .pl-ent{color:#22863a}.markdown-body .pl-k{color:#d73a49}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#032f62}.markdown-body .pl-smw,.markdown-body .pl-v{color:#e36209}.markdown-body .pl-bu{color:#b31d28}.markdown-body .pl-ii{color:#fafbfc;background-color:#b31d28}.markdown-body .pl-c2{color:#fafbfc;background-color:#d73a49}.markdown-body .pl-c2::before{content:"^M"}.markdown-body .pl-sr .pl-cce{font-weight:700;color:#22863a}.markdown-body .pl-ml{color:#735c0f}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:#005cc5}.markdown-body .pl-mi{font-style:italic;color:#24292e}.markdown-body .pl-mb{font-weight:700;color:#24292e}.markdown-body .pl-md{color:#b31d28;background-color:#ffeef0}.markdown-body .pl-mi1{color:#22863a;background-color:#f0fff4}.markdown-body .pl-mc{color:#e36209;background-color:#ffebda}.markdown-body .pl-mi2{color:#f6f8fa;background-color:#005cc5}.markdown-body .pl-mdr{font-weight:700;color:#6f42c1}.markdown-body .pl-ba{color:#586069}.markdown-body .pl-sg{color:#959da5}.markdown-body .pl-corl{text-decoration:underline;color:#032f62}.markdown-body .octicon{display:inline-block;vertical-align:text-top;fill:currentColor}.markdown-body a{background-color:transparent;-webkit-text-decoration-skip:objects}.markdown-body a:active,.markdown-body a:hover{outline-width:0}.markdown-body strong{font-weight:inherit}.markdown-body strong{font-weight:bolder}.markdown-body h1{font-size:2em;margin:.67em 0}.markdown-body img{border-style:none}.markdown-body svg:not(:root){overflow:hidden}.markdown-body hr{box-sizing:content-box;height:0;overflow:visible}.markdown-body input{font:inherit;margin:0}.markdown-body input{overflow:visible}.markdown-body [type=checkbox]{box-sizing:border-box;padding:0}.markdown-body *{box-sizing:border-box}.markdown-body input{font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body a{color:#0366d6;text-decoration:none}.markdown-body a:hover{text-decoration:underline}.markdown-body strong{font-weight:600}.markdown-body hr{height:0;margin:15px 0;overflow:hidden;background:0 0;border:0;border-bottom:1px solid #dfe2e5}.markdown-body hr::before{display:table;content:""}.markdown-body hr::after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse}.markdown-body td,.markdown-body th{padding:0}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:0;margin-bottom:0}.markdown-body h1{font-size:32px;font-weight:600}.markdown-body h2{font-size:24px;font-weight:600}.markdown-body h3{font-size:20px;font-weight:600}.markdown-body h4{font-size:16px;font-weight:600}.markdown-body h5{font-size:14px;font-weight:600}.markdown-body h6{font-size:12px;font-weight:600}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0}.markdown-body ol,.markdown-body ul{padding-left:0;margin-top:0;margin-bottom:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code{font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}.markdown-body .octicon{vertical-align:text-bottom}.markdown-body .pl-0{padding-left:0!important}.markdown-body .pl-1{padding-left:4px!important}.markdown-body .pl-2{padding-left:8px!important}.markdown-body .pl-3{padding-left:16px!important}.markdown-body .pl-4{padding-left:24px!important}.markdown-body .pl-5{padding-left:32px!important}.markdown-body .pl-6{padding-left:40px!important}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e1e4e8;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1{padding-bottom:.3em;font-size:2em;border-bottom:1px solid #eaecef}.markdown-body h2{padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid #eaecef}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{font-size:.85em;color:#6a737d}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto}.markdown-body table th{font-weight:600}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body code{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body .full-commit .btn-outline:not(:disabled):hover{color:#005cc5;border-color:#005cc5}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #d1d5da;border-bottom-color:#c6cbd1;border-radius:3px;box-shadow:inset 0 -1px 0 #c6cbd1}.markdown-body :checked+.radio-label{position:relative;z-index:1;border-color:#0366d6}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item+.task-list-item{margin-top:3px}.markdown-body .task-list-item input{margin:0 .2em .25em -1.6em;vertical-align:middle}.markdown-body hr{border-bottom-color:#eee}.dark-mode{--canvas-bg-color:#1B1916;--sidebar-bg-color:#1A1918;--main-bg-color:#1B1916;--header-bg-color:#1A1918;--hover-bg-color:rgba(255, 255, 255, 0.05);--header-color:#FAF9F6;--color:#D6D1C5;--link-color:#A7B2FF;--link-hover-color:#C8CEFF;--border-color:#33302B;--border-soft:#2A2825;--btn-color:#FAF9F6;--input-color:#E6E1D8;--input-bg:#25231F;--primary-bg:#FAF9F6;--primary-hover-bg:#FFFFFF;--primary-color:#1A1815;--primary-active-color:#1A1815;--accent:#A7B2FF;--accent-soft:rgba(167, 178, 255, 0.12);--ink-muted:#A39D92;--ink-faint:#8A857C;--ink-soft:#B5AFA2;--status-ready:#5FB27A;--status-progress:#E0A94A;--status-error:#F08A82;--status-inactive:#8A857C;--status-error-soft:rgba(240, 138, 130, 0.08);--status-error-soft-hover:rgba(240, 138, 130, 0.14);--admin-nav-bg:#1A1918;--admin-nav-active:rgba(167, 178, 255, 0.12);--admin-stat-bg:#25231F;--paper-card:#25231F;--paper-bg-alt:#201E1B;--highlight:#3A341E;background-color:var(--canvas-bg-color);color:var(--color)}body{--canvas-bg-color:#FAF9F6;--sidebar-bg-color:#FFFFFF;--header-bg-color:#FAF9F6;--header-color:#1A1815;--main-bg-color:#FAF9F6;--hover-bg-color:rgba(26, 24, 21, 0.05);--input-color:#1A1815;--color:#1A1815;--link-color:#3B4AD6;--link-hover-color:#2E3BB8;--border-color:#E6E1D8;--border-soft:#EDE9E1;--btn-color:#1A1815;--input-bg:#FFFFFF;--primary-bg:#1A1815;--primary-hover-bg:#000000;--primary-color:#FAF9F6;--primary-active-color:#FAF9F6;--accent:#3B4AD6;--accent-soft:#EEF0FF;--ink-muted:#6F6A61;--ink-faint:#8A857C;--ink-soft:#4A4641;--paper-bg-alt:#F3F1EC;--paper-card:#FFFFFF;--highlight:#FFF3C4;--status-ready:#2F7A44;--status-progress:#946512;--status-error:#B42318;--status-inactive:#8A857C;--status-error-soft:rgba(180, 35, 24, 0.04);--status-error-soft-hover:rgba(180, 35, 24, 0.08);--radius-control:6px;--radius-button:8px;--button-height:36px;--button-font-size:14px;--button-line-height:20px;--button-padding-x:13px;--button-compact-height:32px;--radius-surface:10px;--font-serif:'Instrument Serif','Iowan Old Style',Georgia,serif;--font-sans:'Inter',ui-sans-serif,system-ui,-apple-system,sans-serif;--font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;background-color:var(--canvas-bg-color);color:var(--color);font-family:var(--font-sans);font-size:14px;letter-spacing:-.005em;line-height:1.5}.fadeIn{opacity:1;animation-name:fadeInOpacity;animation-iteration-count:1;animation-timing-function:ease-in;animation-duration:.6s}@keyframes fadeInOpacity{0%{opacity:0}100%{opacity:1}}.btn:active,.btn:focus{box-shadow:none}:focus:not(:focus-visible){outline:0}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.btn:focus-visible,.dropdown-item:focus-visible,.form-control:focus-visible{outline:2px solid var(--accent);outline-offset:2px;box-shadow:none}input:active,input:hover{box-shadow:none!important}input:-webkit-autofill,input:-webkit-autofill:focus,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill,textarea:-webkit-autofill:focus,textarea:-webkit-autofill:hover{border-color:var(--border-color);-webkit-text-fill-color:var(--color);-webkit-box-shadow:none;transition:background-color 1000s ease-in-out 0s}.custom-select,.form-control,input:-internal-autofill-selected{color:var(--input-color);background-color:var(--input-bg)!important;border-color:var(--border-color)}.form-control:focus{border-color:var(--border-color)}body,html{position:relative;height:100%;overflow-x:hidden}body{overflow-y:hidden}#app{flex:1 1 auto;min-height:0}.app-header{flex:0 0 auto}.app-view{display:block;flex:1 1 auto;min-height:0;overflow-x:hidden!important;overflow-y:auto!important;max-width:100%}.markdown-body a,a{color:var(--link-color)}.markdown-body a:hover,a:hover{text-decoration:none;color:var(--link-hover-color)}.dropdown-menu a.dropdown-item{color:var(--color)}.dropdown-menu a.dropdown-item:hover{color:var(--color);text-decoration:none}.markdown-body .emoji{height:1.3em;margin:0;vertical-align:-.1em}.navbar.navbar-expand-lg{background:var(--header-bg-color)!important;border-bottom:1px solid var(--border-color);transition:background-color .5s ease;padding:10px max(20px,calc((100vw - 1200px)/ 2));font-family:var(--font-sans)}.navbar-toggler{border:1px solid var(--border-color)!important;color:var(--color)!important}.navbar-toggler .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%231A1815' stroke-width='2' stroke-linecap='round' d='M4 8h22M4 15h22M4 22h22'/%3E%3C/svg%3E")!important}.navbar .navbar-nav{align-items:center}.navbar .nav-link{display:inline-flex;align-items:center;font-size:13px;color:var(--ink-muted)!important;padding:6px 0!important;margin:0 12px;border-bottom:1px solid transparent;letter-spacing:-.005em;line-height:1}.navbar .nav-link:hover{color:var(--color)!important}.navbar .nav-link.active{color:var(--color)!important;border-bottom-color:var(--color)}.navbar .nav-link.nav-icon{padding:0!important;margin:0 8px;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px}.navbar .nav-link .fa,.navbar .nav-link .fab{font-size:15px;line-height:1}.navbar #navbarSupportedContent{align-items:center}.navbar-brand{font-family:var(--font-serif)!important;font-size:20px!important;letter-spacing:-.01em;color:var(--color)!important;padding-right:18px;margin-right:12px;border-right:1px solid var(--border-color)}.navbar-brand em,.navbar-brand i{font-style:italic;font-weight:400}.navbar-dark .navbar-toggler{border-color:var(--border-color);color:var(--color)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%231A1815' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar .btn-signin{display:inline-flex;align-items:center;height:32px;padding:0 14px!important;margin-left:6px;border:1px solid var(--color);background:var(--color);color:var(--canvas-bg-color)!important;border-radius:6px;font-size:13px;font-weight:500;line-height:1}.navbar .btn-signin:hover{background:var(--primary-hover-bg);color:var(--canvas-bg-color)!important}.navbar .nav-support{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;order:3;gap:6px;min-height:36px;margin-left:12px;padding:0 12px;border:1px solid var(--border-color);border-radius:8px;color:var(--color);font-size:13px;font-weight:500;text-decoration:none}.navbar .nav-support:hover{background:var(--hover-bg-color);border-color:var(--color);color:var(--color)}@media (max-width:991px){.navbar .nav-support{order:0;width:44px;min-height:44px;margin:0 8px 0 auto;padding:0}.navbar .nav-support-label{display:none}[class*=floatingchat-container-wrap],iframe.floatingchat-container{display:none!important}}.navbar .user-chip{display:inline-flex!important;align-items:center;gap:8px;padding:4px 12px 4px 6px!important;margin-left:6px;border:1px solid var(--border-color);border-radius:999px;background:var(--sidebar-bg-color);font-size:12.5px;color:var(--color)!important}.navbar .user-chip:hover{background:var(--hover-bg-color)}.navbar .user-chip img{width:22px;height:22px}.navbar .user-chip .user-chip-name{color:var(--color);font-weight:500}.navbar .user-chip::after{color:var(--ink-muted)}#navbarSupportedContent{padding-right:0}.generalMessage{display:block;text-align:center;color:var(--header-color)}.rgba-gradient{background:var(--canvas-bg-color);color:var(--color)}.paper-landing{overflow-x:clip}.paper-wrap{max-width:1140px;margin-left:auto;margin-right:auto;padding-left:32px;padding-right:32px}.paper-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);font-weight:500}.paper-hero{max-width:1140px;margin:0 auto;padding:64px 32px 0;display:grid;grid-template-columns:.95fr 1.05fr;gap:48px;align-items:center}.paper-hero h1.hero-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.6rem,5.4vw,4.4rem);line-height:1.02;letter-spacing:-.025em;margin:14px 0 0;color:var(--color);text-wrap:balance}.paper-hero h1 .accent{color:var(--accent);font-style:italic}.hero-subtitle{margin-top:18px;font-size:17px;line-height:1.55;color:var(--ink-soft);max-width:30em;text-wrap:pretty}.paper-cta-row{display:flex;flex-wrap:wrap;gap:18px;align-items:center;margin-top:26px}.btn-hero{display:inline-flex;align-items:center;font-family:var(--font-sans);border:1px solid var(--color);background:var(--color);color:var(--canvas-bg-color);padding:12px 20px;font-size:14px;font-weight:500;border-radius:var(--radius-control);letter-spacing:-.005em;transition:background .15s,border-color .15s,transform .1s}.btn-hero:hover{background:var(--primary-hover-bg);border-color:var(--primary-hover-bg);color:var(--canvas-bg-color);text-decoration:none;transform:translateY(-1px)}.btn-hero-ghost{background:0 0;border:1px solid var(--border-color);color:var(--color);padding:12px 20px;font-size:14px;font-weight:500;border-radius:var(--radius-control)}.btn-hero-ghost:hover{background:var(--hover-bg-color);border-color:var(--color);color:var(--color)}.paper-link-arrow{font-size:14px;color:var(--ink-soft);text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--border-color)}.paper-link-arrow:hover{color:var(--color);text-decoration-color:var(--color)}.paper-reassure{list-style:none;margin:16px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12.5px;color:var(--ink-muted)}.paper-reassure li::before{content:"\2713";color:var(--status-ready);margin-right:6px;font-size:11px}.paper-frame{border-radius:var(--radius-surface);background:var(--paper-card);border:1px solid var(--border-color);box-shadow:0 24px 60px rgba(26,24,21,.1),0 2px 6px rgba(26,24,21,.04);overflow:hidden}.dark-mode .paper-frame{box-shadow:0 24px 60px rgba(0,0,0,.45)}.paper-frame-bar{display:flex;align-items:center;gap:8px;height:34px;padding:0 12px;border-bottom:1px solid var(--border-soft);background:var(--paper-bg-alt);font-family:var(--font-mono);font-size:11px;color:var(--ink-muted)}.paper-frame-bar .dots{display:flex;gap:5px}.paper-frame-bar .dots i{width:8px;height:8px;border-radius:50%;background:var(--border-color)}.paper-frame-bar .url{flex:1;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-frame img{display:block;width:100%;height:auto}.paper-hero-shot .paper-frame{width:112%}.paper-proof{margin-top:64px;padding-top:18px;padding-bottom:18px;border-top:1px solid var(--border-color);display:flex;flex-wrap:wrap;gap:12px 30px;align-items:center}.paper-proof-venues{display:flex;gap:22px;align-items:center;font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;color:var(--ink-soft)}.paper-proof-venues i{font-style:normal;font-family:var(--font-sans);font-size:14px;color:var(--ink-faint)}.paper-stats{margin-top:12px;padding-top:22px;border-top:1px solid var(--border-color)}.paper-stats-meta{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-muted);margin-bottom:14px}.paper-stats-meta-right{display:inline-flex;align-items:center;gap:6px}.paper-stats-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--status-ready)}.paper-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color)}.paper-stat-card{display:flex;flex-direction:column;padding:22px 26px 20px 0;margin-right:26px;border-right:1px solid var(--border-color);min-width:0}.paper-stat-card:last-child{border-right:0;margin-right:0;padding-right:0}.paper-stat-value{font-size:44px;font-weight:500;letter-spacing:-.04em;line-height:1;color:var(--color);font-variant-numeric:tabular-nums}.paper-stat-label{margin-top:6px;font-size:13px;color:var(--ink-soft)}.paper-stat-bars{display:block;width:100%;height:34px;margin-top:18px;overflow:visible}.paper-stat-bars rect{fill:var(--border-color)}.paper-stat-bars rect.is-latest{fill:var(--accent)}.paper-stat-delta{margin-top:10px;font-family:var(--font-mono);font-size:10.5px;color:var(--status-ready)}.paper-section-head{margin-top:110px;display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:end}.paper-qa h2,.paper-section-head h2{font-family:var(--font-serif);font-weight:400;font-size:clamp(2rem,4.2vw,3.25rem);line-height:1.05;letter-spacing:-.02em;margin:10px 0 0;color:var(--color);text-wrap:balance}.paper-feature-title em,.paper-qa h2 em,.paper-section-head h2 em{font-style:italic;color:var(--accent)}.paper-section-head p{font-size:15px;color:var(--ink-soft);max-width:32em;margin:0;line-height:1.6}.paper-preview{margin-top:28px;display:grid;grid-template-columns:1fr 1fr;gap:14px}.paper-preview .pane{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);overflow:hidden;min-width:0}.paper-preview .pane-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-bottom:1px solid var(--border-soft)}.paper-preview .pane-label{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);white-space:nowrap}.paper-preview .pane-url{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.paper-preview .pane-body{padding:16px 18px;font-family:var(--font-mono);font-size:12.5px;line-height:1.9;color:var(--ink-soft)}.paper-preview .pane-body strong{color:var(--color);font-weight:600}.paper-preview .tok{color:var(--ink-faint)}.paper-preview .redact{background:var(--accent-soft);color:var(--accent);border-radius:4px;padding:0 5px}.paper-features{margin-top:28px;padding-top:28px;border-top:1px solid var(--border-color);display:grid;grid-template-columns:5fr 7fr;gap:48px;align-items:start}.paper-features-list{display:flex;flex-direction:column}.paper-feature{border-left:2px solid var(--border-soft);padding:16px 0 16px 20px;transition:border-color .2s}.paper-feature.is-on{border-left-color:var(--color)}.paper-feature-tab{all:unset;display:block;width:100%;cursor:pointer;color:inherit;border-radius:4px}.paper-feature-tab:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.paper-feature-tab .paper-eyebrow{display:block;margin-bottom:6px}.paper-feature-title{display:block;font-family:var(--font-serif);font-size:24px;line-height:1.15;letter-spacing:-.015em;color:var(--ink-muted);transition:color .2s}.paper-feature-title em{color:inherit}.paper-feature-tab:hover .paper-feature-title,.paper-feature.is-on .paper-feature-title{color:var(--color)}.paper-feature.is-on .paper-feature-title em{color:var(--accent)}.paper-feature-more{display:none}.paper-feature.is-on .paper-feature-more{display:block}.paper-feature-more .paper-feature-title-m{display:none}.paper-feature-more p{font-size:14.5px;color:var(--ink-soft);line-height:1.55;margin:10px 0 12px;max-width:30em}.paper-features-stage{min-width:0}.paper-feature-panel{animation:paper-feature-in .25s ease-out}@keyframes paper-feature-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){.paper-feature-panel{animation:none}}.paper-crop{position:relative;height:380px;overflow:hidden;background:var(--paper-card)}.paper-crop img{position:absolute;left:0;top:0;max-width:none;height:auto;display:block}.paper-crop-anonymize img{width:150%;transform:translate(-1.5%,-8%)}.paper-crop-review img{width:160%;transform:translate(-1%,-8%)}.paper-crop-manage img{width:150%;transform:translate(-6%,-9%)}.paper-qa{margin-top:110px;display:grid;grid-template-columns:1fr 1.4fr;gap:40px;align-items:start}.paper-qa h2{margin-bottom:12px}.paper-qa .q{border-top:1px solid var(--border-color);padding:18px 0}.paper-qa .q:last-child{border-bottom:1px solid var(--border-color)}.paper-qa .q a{display:block;font-weight:500;font-size:15px;color:var(--color);margin-bottom:6px}.paper-qa .q a:hover{color:var(--accent);text-decoration:none}.paper-qa .q p{margin:0;font-size:14px;color:var(--ink-soft);line-height:1.6}@media (max-width:1100px){.paper-hero-shot .paper-frame{width:100%}}@media (max-width:991px){.paper-hero{grid-template-columns:1fr;gap:36px;padding-top:40px}.paper-qa,.paper-section-head{grid-template-columns:1fr;gap:16px}.paper-stats-grid{grid-template-columns:1fr 1fr}.paper-stat-card:nth-child(2n){border-right:0;margin-right:0;padding-right:0}.paper-stat-card:nth-child(-n+2){border-bottom:1px solid var(--border-color)}}@media (max-width:767px){.paper-hero,.paper-wrap{padding-left:20px;padding-right:20px}.paper-hero h1.hero-title{font-size:clamp(2.4rem,11vw,3.25rem)}.hero-subtitle{font-size:16px}.paper-cta-row .btn-hero{width:100%;justify-content:center}.paper-proof{margin-top:48px}.paper-stat-card{padding:18px 16px 16px 0;margin-right:16px}.paper-stat-value{font-size:36px}.paper-qa,.paper-section-head{margin-top:72px}.paper-preview{grid-template-columns:1fr}.paper-preview .pane-body{font-size:11.5px;line-height:1.8}.paper-features{display:flex;flex-wrap:wrap;gap:0;padding-top:0;border-top:0;margin-top:24px}.paper-feature,.paper-features-list,.paper-features-stage{display:contents}.paper-feature-tab{order:1;flex:1 1 0;width:auto;padding:12px 0 10px;border-bottom:2px solid var(--border-color);border-radius:0;text-align:left}.paper-feature.is-on .paper-feature-tab{border-bottom-color:var(--color)}.paper-feature-tab .paper-eyebrow{margin:0;font-size:10.5px;letter-spacing:.08em}.paper-feature-tab .paper-feature-title{display:none}.paper-feature-panel{order:2;width:100%;margin-top:20px;animation:none}.paper-crop{height:240px}.paper-crop-anonymize img{width:230%;transform:translate(-1.5%,-8%)}.paper-crop-review img{width:240%;transform:translate(-1%,-8%)}.paper-crop-manage img{width:220%;transform:translate(-3%,-9%)}.paper-feature-more{order:3;width:100%}.paper-feature.is-on .paper-feature-more{margin-top:18px}.paper-feature-more .paper-feature-title-m{display:block;font-size:26px;line-height:1.1}.paper-feature-more p{max-width:none}.paper-footer-inner>.paper-footer-brand{grid-column:1/-1}.paper-footer-foot{flex-direction:column;gap:6px}}.dropdown-menu{background-color:var(--paper-card,var(--main-bg-color))!important;color:var(--color)!important;border:1px solid var(--border-color)!important;border-radius:8px!important;box-shadow:0 12px 28px rgba(26,24,21,.08),0 2px 6px rgba(26,24,21,.05)!important;padding:6px!important;font-family:var(--font-sans);font-size:13px;min-width:11rem}.dropdown-menu .dropdown-item{color:var(--color)!important;padding:7px 10px!important;border-radius:5px;font-size:13px}.dropdown-menu .dropdown-item:focus,.dropdown-menu .dropdown-item:hover{background:var(--hover-bg-color)!important;color:var(--color)!important}.dropdown-menu .dropdown-divider{border-top-color:var(--border-color)!important;margin:4px 2px}.dropdown-menu .dropdown-header{color:var(--ink-muted);font-family:var(--font-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase}.body,.file-content,.markdown-body{background-color:var(--main-bg-color)!important;color:var(--color)!important}.markdown-body table tr{background-color:var(--main-bg-color);border-top:1px solid var(--border-color)}.markdown-body table tr:nth-child(2n){background-color:var(--paper-bg-alt)}.markdown-body table td,.markdown-body table th{border:1px solid var(--border-color)}.file-content{padding:4px 7px;text-align:left;min-height:100%}.badge{font-size:75%;font-weight:500;padding:3px 8px;border-radius:10px;vertical-align:middle}.warning-feedback{width:100%;margin-top:.25rem;font-size:80%;color:#dc8e35}.dropdown-item:focus,.dropdown-item:hover{background-color:var(--hover-bg-color)}.card-header .btn,.card-header .btn:focus,.card-header .btn:hover{border:none!important;background:0 0!important}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:var(--button-height);padding:7px var(--button-padding-x);font:500 var(--button-font-size)/var(--button-line-height) var(--font-sans);vertical-align:middle;max-width:100%;transition:background-color 120ms ease,border-color 120ms ease,color 120ms ease;color:var(--btn-color);border:1px solid var(--border-color);background:var(--paper-card);border-radius:var(--radius-button);white-space:nowrap}.btn:active,.btn:hover{color:var(--btn-color);border:1px solid var(--border-color);background-color:var(--hover-bg-color);box-shadow:none!important}.btn.btn-primary{color:var(--primary-color);background-color:var(--primary-bg)}.btn.btn-primary:active,.btn.btn-primary:hover{color:var(--primary-active-color);background-color:var(--primary-hover-bg)!important}.btn::placeholder{color:var(--color);opacity:1}.white_border{border:1px solid #fff;color:#fff}.leftCol{display:flex;position:relative;flex:0 0 auto;width:100%;z-index:9;flex-direction:column}.card,.leftCol,.sidePanel{background-color:var(--sidebar-bg-color);border:1px solid var(--border-color)}@media (min-width:768px){.leftCol{flex:0 0 310px;width:310px;height:100%}}.leftCol-search{padding:10px 12px 0}.tree-search-box{display:flex;align-items:center;gap:8px;background:var(--canvas-bg-color);border:1px solid var(--border-color);border-radius:8px;padding:7px 10px;transition:border-color .15s}.tree-search-box:focus-within{border-color:var(--ink-muted)}.tree-search-icon{font-size:12px;color:var(--ink-muted);flex-shrink:0}.tree-search-input{flex:1;border:none;outline:0;background:0 0;font-family:var(--font-sans);font-size:13px;color:var(--color);min-width:0}.tree-search-input::placeholder{color:var(--ink-muted)}.tree-search-kbd{font-family:var(--font-sans);font-size:11px;color:var(--ink-muted);background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:4px;padding:1px 6px;line-height:1.4;flex-shrink:0}.tree-search-clear{background:0 0;border:none;color:var(--ink-muted);font-size:16px;cursor:pointer;padding:0 2px;line-height:1}.tree-search-clear:hover{color:var(--color)}.leftCol-project-header{display:flex;align-items:baseline;justify-content:space-between;padding:12px 14px 4px;gap:8px}.project-name{font-family:var(--font-sans);font-size:13px;font-weight:600;color:var(--color);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.project-file-count{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);white-space:nowrap;flex-shrink:0}.files{position:relative;display:block;overflow:auto;height:100%;padding:4px 6px}.files ul{list-style:none;margin:0;padding:0}.files li ul{padding-left:8px;margin-left:7px;border-left:1px solid var(--border-color)}.files .folder>ul{display:none}.files .folder.open>ul{display:block}.files .file,.files .folder{position:relative;color:var(--ink-soft);cursor:pointer;word-break:break-all}.files a{display:flex;align-items:center;gap:4px;padding:1px 8px 1px 2px;font-family:var(--font-sans);font-size:13px;color:var(--ink-soft);border-radius:4px;text-decoration:none;line-height:1.5;position:relative}.files a:hover{background:var(--hover-bg-color);color:var(--color);text-decoration:none}.files:focus{outline:0}.files a.tree-focused{background:var(--hover-bg-color);color:var(--color)}.files .file.active>a{background:rgba(128,128,128,.15);color:var(--color)}.files .file.active>a .tree-icon-file{color:#5b7ba5}.files .file.active>a .tree-count{color:var(--ink-muted)}.tree-icon-file,.tree-icon-folder{display:inline-block;flex-shrink:0}.tree-icon-folder{width:14px;height:12px;color:var(--ink-muted);opacity:.75}.tree-icon-folder svg{display:block;width:100%;height:100%;fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:1.2;stroke-linejoin:round}.files .folder.open>a .tree-icon-folder{color:#a07d2e;opacity:.9}.files .folder.open>a .tree-icon-folder svg{fill-opacity:.12}.dark-mode .files .folder.open>a .tree-icon-folder{color:#c9a03a}.tree-icon-file{width:16px;font-size:14px;text-align:center;color:#5b7ba5}.dark-mode .files .file.active>a .tree-icon-file,.dark-mode .tree-icon-file{color:#7ba3d0}.tree-toggle{display:inline-flex;align-items:center;justify-content:center;width:10px;height:14px;flex-shrink:0;opacity:.6}.tree-toggle::after{content:"";display:block;width:0;height:0;border-style:solid;border-width:3px 0 3px 4.5px;border-color:transparent transparent transparent var(--ink-muted);transition:transform .12s ease}.files .folder.open>a .tree-toggle::after{transform:rotate(90deg)}.tree-spacer{display:inline-block;width:10px;flex-shrink:0}.tree-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-count{font-family:var(--font-sans);font-size:11px;color:var(--ink-muted);flex-shrink:0;margin-left:auto;padding-left:8px;opacity:.7}.files .folder>a{color:var(--color)}.files .file::before,.files .folder::before{display:none}.files .folder>a::after{display:none}.tree-search-path{color:var(--ink-muted);font-size:11px}.tree-search-empty{padding:16px 12px;color:var(--ink-muted);font-size:12.5px;font-family:var(--font-sans);text-align:center}.leftCol{background:var(--paper-bg-alt,#f3f1ec);border-right:1px solid var(--border-color)}.dark-mode .leftCol{background:var(--sidebar-bg-color)}.status-bar{background:var(--canvas-bg-color);border-bottom:1px solid var(--border-color);padding:10px 14px}.paths .breadcrumb-item,.paths .breadcrumb-item a{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);letter-spacing:.02em}.paths .breadcrumb-item.active,.paths .breadcrumb-item:last-child{color:var(--color)}.paths .breadcrumb-item+.breadcrumb-item::before{content:'/';color:var(--ink-muted)}.last-update{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-muted);padding:8px 10px;border-top:1px solid var(--border-color);letter-spacing:.02em}.ace_editor{height:100%}.ace_scroller{cursor:default}.status-bar{display:flex;align-items:center;gap:12px;background-color:var(--canvas-bg-color);padding:10px 14px;margin:0;border-bottom:1px solid var(--border-color);border-radius:0;flex-shrink:0}.status-bar-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}.status-bar-actions .btn{display:inline-flex;align-items:center;gap:6px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);border-radius:6px;padding:4px 10px;font-size:12.5px;min-height:30px}.status-bar-actions .btn:hover{background:var(--hover-bg-color);color:var(--color)}.status-bar-actions .btn.btn-active{border-color:currentColor;font-weight:600}.paths{flex:1 1 auto;min-width:0;padding:0;margin:0;background-color:initial;border:none;border-radius:0;align-items:center;overflow-x:auto;white-space:nowrap;flex-wrap:nowrap}.paths::-webkit-scrollbar{display:none}.paths a{color:var(--color)}.paths .breadcrumb-item{padding-left:3px}.paths .breadcrumb-item::before{padding-right:3px}.content{height:100%;overflow:scroll;padding-top:42px;background-color:var(--main-bg-color);text-align:center}.image-content{max-width:100%;max-height:100%;text-align:center;padding:4px}.pdf-viewer{display:flex;flex-direction:column;height:100%;min-height:0;background-color:var(--main-bg-color)}.pdf-toolbar{display:flex;align-items:center;gap:4px;flex:0 0 auto;padding:4px 8px;border-bottom:1px solid var(--border-color,rgba(0,0,0,.12))}.pdf-toolbar-group{display:inline-flex;align-items:center;gap:4px}.pdf-toolbar-right{margin-left:auto}.pdf-toolbar-btn{border:0;background:0 0;color:inherit;padding:4px 8px;border-radius:4px;font-size:13px;line-height:1.2;cursor:pointer}.pdf-toolbar-btn:hover:not(:disabled){background-color:rgba(128,128,128,.18)}.pdf-toolbar-btn:disabled{opacity:.4;cursor:default}.pdf-toolbar-zoom{min-width:52px;font-variant-numeric:tabular-nums}.pdf-toolbar-page{width:3.5em;text-align:right;padding:2px 4px;font-size:13px;color:inherit;background:0 0;border:1px solid var(--border-color,rgba(0,0,0,.2));border-radius:4px}.pdf-toolbar-count{font-size:13px;opacity:.75;font-variant-numeric:tabular-nums}.pdf-pages{flex:1 1 auto;min-height:0;overflow:auto;padding:8px 0}.pdf-viewer-page{margin:0 auto 12px}.pdf-viewer-page canvas{display:block;box-shadow:0 1px 4px rgba(0,0,0,.25)}.pdf-viewer-error{padding:24px;text-align:center;font-weight:700}.html-doc{display:block;height:100%}.html-doc-frame{display:block;width:100%;height:100%;border:0;background-color:#fff}.file-error{font-weight:700;font-size:50px;text-align:center}pdfviewer{display:block;padding:15px}pdfpageviewer{display:block;width:100%;margin-bottom:10px}notebook{display:block;text-align:left;padding:15px}@media (min-width:768px){notebook{padding-left:100px}}.nb-output td,.nb-output th{border:1px solid var(--border-color)!important}.paper-faq{max-width:1180px;margin:0 auto;padding:48px 32px 80px;font-family:var(--font-sans)}.paper-faq-hero{padding-bottom:40px;border-bottom:1px solid var(--border-color);margin-bottom:36px}.paper-faq-eyebrow{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.14em;text-transform:uppercase;margin-bottom:32px}.paper-faq-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.6rem,5.5vw,4.6rem);line-height:1.03;letter-spacing:-.02em;color:var(--color);margin:0 0 22px;max-width:14em}.paper-faq-title em{font-style:italic;color:var(--color)}.paper-faq-lede{color:var(--ink-soft);font-size:17px;line-height:1.55;max-width:38em;margin:0}.paper-faq-lede a{color:var(--color);border-bottom:1px solid var(--color)}.paper-faq-lede a:hover{color:var(--accent);border-bottom-color:var(--accent)}.paper-faq-body{display:grid;grid-template-columns:220px 1fr;gap:60px;align-items:start}.paper-faq-toc{position:sticky;top:32px;font-size:13.5px}.paper-faq-toc-head{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-muted);letter-spacing:.14em;text-transform:uppercase;padding-bottom:10px;margin-bottom:6px;border-bottom:1px solid var(--border-color)}.paper-faq-toc nav{display:flex;flex-direction:column}.paper-faq-toc nav a{color:var(--ink-soft);padding:8px 0 8px 14px;border-left:2px solid transparent;text-decoration:none;margin-left:-2px}.paper-faq-toc nav a.active,.paper-faq-toc nav a:hover{color:var(--color);border-left-color:var(--color)}.faq-section{min-width:0}.faq-section>div{width:100%}.faq-section h4.faq-cat{font-family:var(--font-serif);font-weight:400;font-size:clamp(1.7rem,2.6vw,2.2rem);letter-spacing:-.015em;color:var(--color);margin:0 0 6px;padding-bottom:14px;border-bottom:1px solid var(--border-color)}.faq-section h4.faq-cat i{display:none}.faq-section .panel-group{margin-bottom:56px}.faq-section .panel-default .panel-title a{display:block;position:relative;padding:22px 0 22px 42px;font-family:var(--font-serif);font-weight:400;font-size:20px;line-height:1.35;letter-spacing:-.005em;color:var(--color);text-decoration:none;cursor:pointer}.faq-section .panel-default .panel-title a:hover{color:var(--accent)}.faq-section .panel-heading{background:0 0;padding:0!important;border:none}.faq-section .panel-default{background:0 0;border:none;border-bottom:1px solid var(--border-soft);border-radius:0;margin-bottom:0!important;box-shadow:none}.faq-section .panel-default .panel-title{margin-bottom:0}.faq-section .panel-default .panel-title a:after{content:"−";position:absolute;left:0;right:auto;top:22px;transform:none;font-family:var(--font-mono);font-size:18px;line-height:1.35;color:var(--ink-muted);float:none;margin:0;font-weight:400}.faq-section .panel-default .panel-title a.collapsed:after{content:"+"}.faq-section .panel-default .panel-body{padding:0 0 22px 42px!important;color:var(--ink-soft);font-size:15px;line-height:1.65;border-top:none;max-width:42em}.table{color:var(--color)}.dropdown-divider,.table td,.table th,hr{border-color:var(--border-color)}.table.repositories td{word-break:break-word}loc{display:block;position:relative;width:100%;height:5px}loc .lang{float:left;height:100%}.rounded-circle.fa{display:inline-block;font-size:75px;height:140px;width:140px;border:4px solid;line-height:140px;text-align:center}.featurette-divider{margin:4.5rem 0;border-color:var(--border-color)}.featurette-heading{font-family:var(--font-serif);font-weight:400;font-size:2rem;line-height:1.15;letter-spacing:-.02em;margin-bottom:14px;color:var(--color)}.dark-mode .shadow{box-shadow:none}.dark-mode .overflow-auto::-webkit-scrollbar{width:10px}.dark-mode .overflow-auto::-webkit-scrollbar-track{-webkit-box-shadow:none;background:inherit;border-radius:0}.dark-mode .overflow-auto::-webkit-scrollbar-thumb{border-radius:10px;border:1px solid var(--border-color);background-color:var(--hover-bg-color)}.dark-mode .rgba-gradient{background:linear-gradient(135deg,#2a2d4a,#151728)}.section-number{padding-right:10px;font-weight:700;font-style:italic}.org-subscript-child{font-size:85%}.highlighted-line{position:absolute;background:rgba(100,200,100,.5);z-index:20}code,pre{font-family:"Fira Code","Courier New",Courier,monospace;line-height:1.1;color:var(--color)}.diff-file,.diff-index,.diff-lines{background:rgba(172,172,172,.5)}.diff-add{background:rgba(100,200,100,.5)}.diff-remove{background:rgba(200,100,100,.5)}.dashboard-title{font-size:1.4rem;font-weight:600;letter-spacing:-.01em}.quota-row{display:flex;gap:16px;margin-bottom:16px;padding:0}.quota-item{flex:1;min-width:0}.quota-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:4px}.quota-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;opacity:.7}.quota-value{font-size:.8rem;font-weight:500;font-variant-numeric:tabular-nums}.quota-progress{height:6px;border-radius:3px;background:var(--hover-bg-color)}.quota-progress .progress-bar{border-radius:3px}.filter-chip{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;font-size:.8rem;font-weight:500;background:var(--primary-bg);color:var(--primary-color);border-radius:12px;line-height:1.4}.filter-chip-close{display:inline-flex;align-items:center;justify-content:center;background:0 0;border:none;color:inherit;opacity:.7;cursor:pointer;padding:0;min-width:24px;min-height:24px;margin:-6px -6px -6px 0;font-size:1rem;line-height:1;border-radius:999px}.filter-chip-close:hover{opacity:1}.repo-list{list-style:none;padding:0;margin:0}.repo-list-item{display:flex;align-items:flex-start;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--border-color);gap:12px}.repo-list-item:first-child{border-top:1px solid var(--border-color)}.repo-list-item.repo-inactive{opacity:.6}.repo-list-item-content{flex:1;min-width:0}.repo-list-item-header{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}.repo-name{font-family:var(--font-mono);font-size:14px;font-weight:500;color:var(--color);word-break:break-word}.repo-name:hover{color:var(--accent)}.repo-source{font-size:.85rem;opacity:.8;display:flex;flex-wrap:wrap;align-items:center;gap:6px}.repo-date{opacity:.7}.repo-meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px;font-size:.8rem;opacity:.7}.repo-meta span{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}.repo-list-item-actions{flex-shrink:0;padding-top:2px;margin-left:14px}.repo-list-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:48px 20px;opacity:.5;font-size:.95rem;list-style:none;border-bottom:1px solid var(--border-color);border-top:1px solid var(--border-color)}.repo-list-empty i{font-size:1.8rem}.type-badge{display:inline-flex;align-items:center;gap:3px;font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:4px;white-space:nowrap}.type-badge{font-family:var(--font-mono);letter-spacing:.06em;padding:2px 8px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-bg-alt,transparent)}.type-badge.type-pr,.type-badge.type-repo{background:var(--paper-bg-alt);color:var(--ink-muted);border-color:var(--border-color)}.dark-mode .type-badge.type-pr,.dark-mode .type-badge.type-repo{background:rgba(255,255,255,.06);color:var(--ink-muted);border-color:var(--border-color)}.type-badge.type-coauthor{background:rgba(99,102,241,.12);color:#4f46e5;border-color:rgba(99,102,241,.35);margin-left:6px}.dark-mode .type-badge.type-coauthor{background:rgba(129,140,248,.18);color:#a5b4fc;border-color:rgba(165,180,252,.45)}.btn-group .btn:not(.btn-primary){background:var(--hover-bg-color)}.anonymize-page{display:flex;flex-direction:column;min-height:0}.anonymize-landing{flex:1 1 0;min-height:min-content;display:flex;align-items:center;justify-content:center;padding:20px}.anonymize-landing-inner{width:100%;max-width:600px}.anonymize-landing-inner h2{font-weight:700}.anonymize-workspace{display:flex;flex-direction:column;flex:1 1 0;min-height:0;background:var(--canvas-bg-color)}.anonymize-topbar{background:var(--paper-card);border-bottom:1px solid var(--border-color);padding:18px 28px;flex:0 0 auto}.anonymize-topbar-inner{max-width:1400px;margin:0 auto}.anonymize-topbar-head{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.anonymize-topbar-title{margin:4px 0 0;font-size:clamp(1.8rem,3vw,2.4rem)}.anonymize-split{flex:1 1 auto;display:flex;min-height:0}.anonymize-form-col{flex:0 0 auto;width:100%;min-width:0;background:var(--canvas-bg-color);border-right:1px solid var(--border-color);overflow-y:auto}.anonymize-form-col .paper-settings-main{max-width:720px;padding:28px 32px 40px;margin:0}.anonymize-form-col .paper-settings-section:first-child{padding-top:0}.anonymize-form-col .anonymize-submit-bar{position:sticky;bottom:0;background:var(--canvas-bg-color);padding:14px 0 0;border-top:1px solid var(--border-color);margin-top:20px}.anonymize-form-col .anonymize-submit-bar .btn{width:100%;padding:12px 16px;font-size:15px}.anonymize-preview-col{flex:1 1 auto;min-width:0;overflow:auto;background:var(--paper-bg-alt);padding:28px 32px}.anonymize-preview-head{display:flex;align-items:baseline;gap:12px;padding-bottom:12px;border-bottom:1px solid var(--border-color);margin-bottom:20px}.anonymize-preview-head .paper-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.anonymize-preview-sub{color:var(--ink-soft);font-size:13px}.anonymize-preview-body{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:28px 32px}.form-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px}.form-grid-3{display:grid;grid-template-columns:2fr 1fr 1fr;gap:16px}@media (max-width:700px){.form-grid-2,.form-grid-3{grid-template-columns:1fr}}.paper-optional{font-family:var(--font-sans);font-weight:400;color:var(--ink-muted);text-transform:none;letter-spacing:0;font-size:11px;margin-left:6px}.paper-plan-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.paper-plan-card{position:relative;display:block;padding:18px 20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease;margin:0}.paper-plan-card input[type=radio]{position:absolute;opacity:0;pointer-events:none}.paper-plan-card.selected{border-color:var(--color);box-shadow:0 0 0 2px var(--color) inset}.paper-plan-head{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.paper-plan-price{font-family:var(--font-serif);font-size:2rem;color:var(--color);margin:6px 0 4px}.paper-plan-price .paper-plan-per{font-family:var(--font-sans);font-size:12px;color:var(--ink-soft)}.paper-plan-desc{font-size:13px;color:var(--ink-soft)}.paper-plan-desc ul{padding-left:18px;margin:0}.paper-settings-copy{color:var(--ink-soft);font-size:13px;margin:0 0 14px}.anonymize-section-title{font-size:.85rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;margin:18px 0 8px 0;padding-bottom:6px;border-bottom:1px solid var(--border-color);opacity:.7}.anonymize-section-title i{margin-right:4px}.anonymize-submit-bar{padding:12px 0 4px 0}.anonymize-submit-bar .btn{font-size:1rem;padding:10px 20px;font-weight:600}.anonymize-page .form-check{padding-top:4px;padding-bottom:4px}.anonymize-page .form-check-label{font-weight:500}.anonymize-preview-col{min-height:0;min-width:0}.pr-diff pre{overflow-x:auto;max-width:100%}@media (min-width:768px){.anonymize-form-col{height:100%;width:520px;flex:0 0 520px}.anonymize-preview-col{height:100%;flex:1}}@media (max-width:767px){.anonymize-workspace{flex:0 0 auto;min-height:0}.anonymize-topbar{padding:14px 16px}.anonymize-topbar-title{font-size:1.6rem;margin-top:2px}.anonymize-split{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}.anonymize-form-col{width:100%!important;flex:0 0 auto!important;height:auto!important;border-right:none;border-bottom:1px solid var(--border-color);overflow:visible}.anonymize-form-col .paper-settings-main{padding:18px 16px 24px;max-width:none}.anonymize-form-col .anonymize-submit-bar{position:sticky;bottom:0;margin:20px -16px 0;padding:12px 16px;background:var(--canvas-bg-color);border-top:1px solid var(--border-color);z-index:10}.anonymize-form-col .anonymize-submit-bar .btn{width:100%;padding:12px 16px;font-size:15px}.anonymize-preview-col{width:100%;flex:0 0 auto;height:auto!important;max-height:none;padding:18px 16px 24px;overflow:visible}.anonymize-preview-body{padding:16px;border-radius:8px}.anonymize-preview-body .pr-diff pre,.anonymize-preview-body pre{overflow-x:auto;max-width:100%}.anonymize-landing-inner{padding:16px}}@media (min-width:1280px){.anonymize-form-col{width:620px;flex:0 0 620px}}@media (max-width:767px){.btn{min-height:38px;padding:6px 14px;font-size:14px}.btn-sm{min-height:34px;padding:5px 10px}.navbar .nav-link{padding:8px 12px}.navbar .nav-link .fa,.navbar .nav-link .fab{font-size:24px;line-height:32px}.container.page{padding-left:15px;padding-right:15px;max-width:100%}h1{font-size:1.6rem}h2{font-size:1.35rem}h3{font-size:1.15rem}.repo-list-item{flex-direction:column}.repo-list-item-actions{align-self:flex-start}.quota-row{flex-direction:column;gap:10px;padding:10px 12px}.dropdown-menu{position:absolute;right:0;left:auto}.form-group{margin-bottom:12px}.form-control{font-size:16px}.status-bar{gap:6px;padding:8px 10px}.status-bar .btn{font-size:12px;padding:4px 10px;min-height:32px}.status-bar .paths{font-size:11.5px;overflow-x:auto;flex-wrap:nowrap;white-space:nowrap;-webkit-overflow-scrolling:touch}.status-bar .paths::-webkit-scrollbar{display:none}.status-bar-actions{flex-shrink:0;gap:4px}#home .display-4{font-size:1.8rem}#home h4.subtext-header{font-size:1rem}.progress:not(.quota-progress){min-width:120px!important}.badge{font-size:70%}.col-2.input-group{width:100%!important;max-width:100%;flex:0 0 100%}.color-text-secondary span{display:inline-block;margin-bottom:2px}.floatingchat-container-wrap{display:none!important}.anonymize-landing{align-items:flex-start;padding-top:60px}.anonymize-page .container-fluid>.row{height:auto!important;min-height:100%}.anonymize-form-col{width:100%!important;flex:0 0 100%!important;border-right:none;border-top:1px solid var(--border-color)}.anonymize-preview-col{height:auto!important;max-height:none}.anonymize-section-title{font-size:.8rem;margin:12px 0 8px 0}.anonymize-submit-bar{position:sticky;bottom:0;background:var(--sidebar-bg-color);padding:10px 0;margin:0 -12px;padding-left:12px;padding-right:12px;border-top:1px solid var(--border-color);z-index:10}.anonymize-submit-bar .btn{font-size:1rem;padding:12px 20px}.anonymize-page .form-check{padding-top:6px;padding-bottom:6px;min-height:36px}.anonymize-page .form-check-label{font-size:.95rem}.anonymize-page .card-body{padding:12px}.pr-title{font-size:1.15rem}.pr-diff pre{font-size:.75rem}.pr-comment .mb-1 h5{font-size:1rem}.nav-tabs .nav-link{padding:8px 12px;font-size:.9rem}}@media (min-width:768px) and (max-width:991px){.container.page{max-width:100%;padding-left:15px;padding-right:15px}.leftCol{flex:0 0 250px;width:250px}}.explorer-page{display:flex;height:100%;width:100%;background:var(--canvas-bg-color);position:relative}.explorer-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;background:var(--canvas-bg-color)}.explorer-content{flex:1 1 auto;min-height:0;width:100%;overflow:auto}.leftCol-close,.leftCol-head{display:none}.leftCol-body{flex:1 1 auto;min-height:0;overflow:auto}.leftCol-foot{padding:8px 12px;border-top:1px solid var(--border-color);background:var(--paper-bg-alt)}.leftCol-foot .last-update{display:block;border-top:none;padding:0}.leftCol-backdrop{display:none}.paper-inline-warning{display:flex;align-items:flex-start;gap:8px;margin:8px;padding:10px 12px;background:rgba(138,107,30,.08);border:1px solid rgba(138,107,30,.35);border-radius:8px;color:var(--color);font-size:12.5px;line-height:1.4}.paper-inline-warning i{color:#8a6b1e;margin-top:2px;flex-shrink:0}.dark-mode .paper-inline-warning i{color:#ffd37a}@media (min-width:768px){.leftCol-body{padding:8px 4px}}.sidebar-toggle{display:none;align-items:center;gap:8px;position:fixed;bottom:18px;right:18px;z-index:1100;padding:10px 14px;background:var(--color);color:var(--canvas-bg-color);border:1px solid var(--color);border-radius:999px;font-family:var(--font-sans);font-size:13px;font-weight:500;box-shadow:0 6px 20px rgba(0,0,0,.15);cursor:pointer;outline:0}.sidebar-toggle:hover{background:var(--primary-hover-bg)}.sidebar-toggle:focus,.sidebar-toggle:focus-visible{outline:0;box-shadow:0 6px 20px rgba(0,0,0,.15)}@media (max-width:991px){.sidebar-toggle{display:inline-flex}.leftCol{position:fixed;top:0;left:0;bottom:0;width:min(86vw,340px);max-width:340px;z-index:1200;border:none;border-right:1px solid var(--border-color);transform:translateX(0);transition:transform .22s ease;box-shadow:4px 0 24px rgba(0,0,0,.18);background:var(--sidebar-bg-color)}.leftCol.collapsed{transform:translateX(-105%);box-shadow:none;pointer-events:none}.leftCol-head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--border-color);background:var(--paper-bg-alt)}.leftCol-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.leftCol-close{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:0 0;border:1px solid var(--border-color);border-radius:8px;color:var(--ink-muted);cursor:pointer}.leftCol-close:hover{color:var(--color);border-color:var(--color)}.leftCol-foot{display:block}.leftCol-body{padding:4px 0}.leftCol-backdrop{display:block;position:fixed;inset:0;background:rgba(0,0,0,.32);z-index:1150;backdrop-filter:blur(2px)}.dark-mode .leftCol-backdrop{background:rgba(0,0,0,.5)}}.admin-nav{display:flex;gap:4px;padding:4px;margin:8px 0 20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;flex-wrap:wrap}.admin-nav a{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;border-radius:7px;color:var(--ink-muted);text-decoration:none;font-family:var(--font-mono);font-size:11.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;transition:background-color .15s,color .15s;white-space:nowrap}.admin-nav a:hover{background:var(--paper-bg-alt);color:var(--color)}.admin-nav a.active{background:var(--color);color:var(--paper-card)}.admin-nav a i{font-size:12px;width:14px;text-align:center}@media (max-width:767px){.admin-nav{flex-direction:column}.admin-nav a{justify-content:flex-start}}.admin-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-bottom:16px}.admin-stat-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px}.admin-stat-card .stat-value{font-family:var(--font-serif);font-size:1.4rem;font-weight:400;line-height:1.1;color:var(--color)}.admin-stat-card .stat-label{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-top:4px}.admin-toolbar{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:16px 0 12px}.admin-toolbar .form-control{max-width:320px}@media (max-width:767px){.admin-toolbar .form-control{max-width:100%;width:100%}}.pagination-compact{display:inline-flex;align-items:center;gap:6px;flex-wrap:nowrap;font-family:var(--font-mono);font-size:12px;color:var(--ink-muted)}.pagination-compact input{width:60px;text-align:center;flex-shrink:0}.pagination-compact .btn{padding:4px 10px;min-height:34px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);flex-shrink:0}.pagination-compact>span{white-space:nowrap}.admin-list-item{display:flex;align-items:flex-start;gap:12px;padding:12px 0;border-bottom:1px solid var(--border-color)}.admin-list-item:last-child{border-bottom:none}.admin-list-item .item-main{flex:1;min-width:0}.admin-list-item .item-title{font-size:1rem;font-weight:600;margin-bottom:4px;word-break:break-word}.admin-list-item .item-meta{font-size:.85rem;opacity:.8}.admin-list-item .item-meta span{margin-right:12px;display:inline-block;margin-bottom:2px}.admin-list-item .item-actions{flex-shrink:0}@media (max-width:767px){.admin-list-item{flex-direction:column;gap:8px}.admin-list-item .item-actions{align-self:flex-start}}.status-badge{display:inline-block;font-size:11px;font-weight:600;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.3px;vertical-align:middle}.status-badge{font-family:var(--font-sans);font-weight:500;letter-spacing:.02em;border:1px solid transparent;text-transform:none;font-size:11.5px;padding:1px 9px 2px;border-radius:999px}.status-badge.status-ready{background:#e8f3ec;color:#2f7a44;border-color:#cfe6d6}.status-badge.status-error{background:#fbe7e7;color:#b42318;border-color:#f3c7c7}.status-badge.status-download,.status-badge.status-preparing{background:#eef0ff;color:#3b4ad6;border-color:#d4d8fa}.status-badge.status-expired,.status-badge.status-expiring,.status-badge.status-removed,.status-badge.status-removing{background:var(--paper-bg-alt);color:var(--ink-muted);border-color:var(--border-color)}.status-badge.status-active{background:#e8f3ec;color:#2f7a44;border-color:#cfe6d6}.queue-job-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 18px;margin-bottom:10px}.queue-job-card .job-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;flex-wrap:wrap;gap:8px}.queue-job-card .job-id{font-family:var(--font-mono);font-size:.95rem;color:var(--color);word-break:break-all;display:flex;align-items:center;gap:10px}.queue-job-card .job-timestamps{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-muted);display:flex;flex-wrap:wrap;gap:14px}.queue-job-card .job-actions{display:flex;gap:8px;margin-top:10px}.queue-job-card .job-actions .btn{font-size:12px;padding:4px 12px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}.admin-section-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:24px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--border-color)}.admin-section-header h2{margin:0;font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em}.admin-section-header h2 i{margin-right:6px;color:var(--ink-muted)}.admin-section-header .section-count{background:var(--paper-bg-alt);color:var(--ink-muted);border:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;padding:1px 10px;border-radius:999px}.user-detail-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:22px;margin-bottom:24px}.user-detail-card .user-header{display:flex;align-items:center;gap:16px;margin-bottom:18px;padding-bottom:16px;border-bottom:1px solid var(--border-color);flex-wrap:wrap}.user-detail-card .user-header img{border-radius:50%}.user-detail-card .user-header h2{margin:0;font-family:var(--font-serif);font-weight:400;font-size:1.6rem;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.user-detail-grid{display:grid;grid-template-columns:160px 1fr;gap:10px 20px;font-size:.92rem}.user-detail-grid .detail-label{font-family:var(--font-mono);font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);white-space:nowrap;padding-top:2px}.user-detail-grid .detail-value{word-break:break-all;color:var(--color)}@media (max-width:767px){.user-detail-grid{grid-template-columns:1fr}.user-detail-grid .detail-label{margin-top:8px;border-top:1px solid var(--border-color);padding-top:8px}.user-detail-grid .detail-label:first-child{border-top:none;margin-top:0;padding-top:0}}.paper-empty{min-height:calc(100vh - 60px);display:flex;align-items:center;justify-content:center;padding:40px 24px}.paper-empty-inner{max-width:560px;text-align:left}.paper-empty-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.4rem,6vw,4.4rem);line-height:1.02;letter-spacing:-.025em;margin:18px 0 20px;color:var(--color)}.paper-empty-title em{color:var(--accent);font-style:italic}.paper-empty-lede{font-size:17px;line-height:1.55;color:var(--ink-soft);text-wrap:pretty}.paper-empty-cta{display:flex;gap:10px;margin-top:28px;flex-wrap:wrap}.paper-page{max-width:1100px;margin:0 auto;padding:32px 32px 80px}@media (max-width:991px){.paper-page,.paper-page.container,.paper-page.container-fluid{padding:20px 18px 60px!important}.paper-page .row{margin-left:0;margin-right:0}.paper-page .row>.w-100,.paper-page .row>[class*=col]{padding-left:0;padding-right:0}.navbar.navbar-expand-lg{padding:10px 18px!important}.navbar .navbar-collapse{margin:10px -18px 0;padding:12px 18px 16px;border-top:1px solid var(--border-color);background:var(--header-bg-color)}.navbar .navbar-nav{align-items:stretch!important;gap:2px}.navbar .navbar-nav .nav-item,.navbar .navbar-nav .nav-link{width:100%}.navbar .nav-link{display:flex!important;align-items:center;justify-content:flex-start;margin:0!important;padding:12px 4px!important;border-bottom:1px solid var(--border-color)!important;border-radius:0;font-size:14px;line-height:1.2;gap:0;min-height:44px;box-sizing:border-box}.navbar .nav-link:not(.btn-signin):not(.user-chip){color:var(--color)!important}.navbar .nav-link .fa,.navbar .nav-link .fab,.navbar .nav-link .fas,.navbar .nav-link i{display:inline-flex!important;align-items:center;justify-content:center;width:28px;flex-shrink:0;font-size:16px;line-height:1;margin:0 10px 0 0!important}.navbar .navbar-nav .nav-item:last-child .nav-link,.navbar .navbar-nav>.nav-link:last-child{border-bottom-color:transparent!important}.navbar .nav-link.active{color:var(--color)!important;border-bottom-color:var(--border-color)!important}.navbar .nav-item>.nav-link:not(.btn-signin):not(.user-chip)::before{content:'';display:inline-block;width:28px;margin-right:10px;flex-shrink:0}.navbar .nav-link.nav-icon::before,.navbar .nav-link:has(>i)::before{display:none!important}.navbar .nav-link.nav-icon{width:100%!important;height:auto!important;justify-content:flex-start!important;padding:12px 4px!important}.navbar .nav-link.nav-icon::after{content:attr(title);font-family:var(--font-sans);font-size:14px;color:var(--color)}.navbar .btn-signin{display:inline-flex!important;width:auto!important;align-self:flex-start;justify-content:center;height:40px;padding:0 18px!important;margin:14px 0 0!important;border-radius:8px;border-bottom:none!important}.navbar .btn-signin i{margin:0 8px 0 0!important;width:auto!important}.navbar .user-chip-wrap{width:100%}.navbar .user-chip{display:flex!important;width:100%;margin:0!important;padding:10px 4px!important;background:0 0!important;border:none!important;border-bottom:1px solid var(--border-color)!important;border-radius:0!important;justify-content:flex-start;gap:0;min-height:44px}.navbar .user-chip::before{display:none!important}.navbar .user-chip img{width:28px!important;height:28px!important;margin-right:10px!important;flex-shrink:0}.navbar .user-chip .user-chip-name{font-size:14px;font-weight:500;color:var(--color)!important}.navbar .user-chip.dropdown-toggle::after{margin-left:auto;color:var(--ink-muted)}.navbar .user-chip-wrap .dropdown-menu{position:static!important;float:none;width:100%;margin:0!important;padding:0!important;background:0 0!important;border:none!important;box-shadow:none!important;transform:none!important}.navbar .user-chip-wrap .dropdown-menu .dropdown-item{padding:10px 4px 10px 42px!important;font-size:13.5px;color:var(--ink-muted)!important;background:0 0!important;border-bottom:1px solid var(--border-color);text-align:left!important}.navbar .user-chip-wrap .dropdown-menu .dropdown-item:last-child{border-bottom:none}.navbar .user-chip-wrap .dropdown-divider{display:none}.navbar-brand{border-right:none!important;padding-right:0!important;margin-right:0!important}.dark-mode .navbar .navbar-collapse{background:var(--header-bg-color);border-top-color:var(--border-color)}.dark-mode .navbar .nav-link{border-bottom-color:var(--border-color)!important}.dark-mode .navbar-toggler .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' d='M4 8h22M4 15h22M4 22h22'/%3E%3C/svg%3E")!important}}.status-pill{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;background:var(--paper-bg-alt);border:1px solid var(--border-color);color:var(--ink-soft);font-family:var(--font-mono);font-size:11.5px;letter-spacing:.08em;text-transform:uppercase}.paper-progress{position:relative;height:auto;background:0 0;margin:18px 0 8px}.paper-progress::before{content:"";display:block;height:8px;background:var(--paper-bg-alt);border-radius:999px}.paper-progress .paper-progress-bar{position:absolute;top:0;left:0;height:8px;background:var(--color);border-radius:999px;transition:width .4s ease;min-width:4px}.paper-progress .paper-progress-label{display:flex;justify-content:space-between;align-items:center;margin-top:10px;font-family:var(--font-mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted)}.paper-progress .paper-progress-pct{color:var(--color)}.paper-progress.paper-progress-ready .paper-progress-bar{background:var(--color)}.paper-error-card{margin-top:18px;padding:20px 22px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-left:3px solid #c53030;border-radius:10px;color:var(--color)}.dark-mode .paper-error-card{border-left-color:#ff8b7b}.paper-ratelimit-card{margin-top:18px;padding:20px 22px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-left:3px solid #d69e2e;border-radius:10px;color:var(--color)}.dark-mode .paper-ratelimit-card{border-left-color:#f6e05e}.paper-ratelimit-head{display:flex;align-items:flex-start;gap:14px}.paper-ratelimit-head>i{font-size:18px;color:#d69e2e;margin-top:4px;flex-shrink:0}.dark-mode .paper-ratelimit-head>i{color:#f6e05e}.paper-ratelimit-card strong{color:var(--color)}.status-pill-ratelimit{border-color:#d69e2e;color:#d69e2e}.dark-mode .status-pill-ratelimit{border-color:#f6e05e;color:#f6e05e}.status-ratelimit{background:#d69e2e}.dark-mode .status-ratelimit{background:#f6e05e}.paper-error-head{display:flex;align-items:flex-start;gap:14px}.paper-error-head>i{font-size:18px;color:#c53030;margin-top:4px;flex-shrink:0}.dark-mode .paper-error-head>i{color:#ff8b7b}.paper-error-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.paper-error-title{font-family:var(--font-serif);font-size:1.4rem;line-height:1.2;margin-top:2px;color:var(--color)}.paper-error-msg{margin:12px 0 0;font-size:14px;line-height:1.55;color:var(--ink-soft);font-family:var(--font-mono);word-break:break-word;white-space:pre-wrap}.paper-error-hints{margin:14px 0 0;padding-left:18px;color:var(--ink-soft);font-size:13.5px;line-height:1.6}.paper-error-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}.paper-detail-grid{display:grid;grid-template-columns:160px 1fr;gap:10px 20px;margin-top:18px;font-size:.92rem}.paper-detail-grid .detail-label{font-family:var(--font-mono);font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);white-space:nowrap;padding-top:2px}.paper-detail-grid .detail-value{word-break:break-all;color:var(--color)}.paper-detail-grid .detail-value a{color:var(--color);border-bottom:1px solid var(--border-color)}.paper-detail-grid .detail-value a:hover{border-bottom-color:var(--color)}@media (max-width:700px){.paper-detail-grid{grid-template-columns:1fr;gap:4px 0}.paper-detail-grid .detail-label{padding-top:8px}}.paper-support-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin:18px 0 28px}.paper-support-card{display:flex;flex-direction:column;gap:10px;padding:20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;color:var(--color);text-decoration:none;transition:border-color .15s ease,transform .15s ease}.paper-support-card:hover{border-color:var(--color);text-decoration:none;color:var(--color)}.paper-support-card .paper-support-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);display:flex;align-items:center;gap:8px}.paper-support-card p{margin:0;font-size:14px;line-height:1.55;color:var(--ink-soft)}.paper-support-card .paper-support-cta{margin-top:auto;font-size:13px;font-weight:500;color:var(--color);display:inline-flex;align-items:center;gap:6px}.paper-support-card:hover .paper-support-cta i{transform:translateX(2px)}.paper-support-card .paper-support-cta i{transition:transform .15s ease}.paper-kofi-wrap{border:1px solid var(--border-color);border-radius:10px;overflow:hidden;background:var(--paper-card)}.paper-kofi-wrap iframe{border:0;width:100%;height:650px;display:block}.dark-mode .paper-kofi-wrap iframe{filter:invert(.88) hue-rotate(180deg) sepia(.15) saturate(1.2) brightness(1.1) contrast(.85)}@media (max-width:700px){.paper-kofi-wrap iframe{height:720px}}.pr-page{min-height:100%;background:var(--canvas-bg-color)}.pr-page-inner{padding-top:24px;padding-bottom:60px}.pr-header{margin:6px 0 18px}.pr-title{margin:4px 0 10px;font-size:clamp(1.6rem,3vw,2.4rem);line-height:1.15;word-break:break-word}.pr-header-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;font-family:var(--font-mono);font-size:12px;color:var(--ink-muted)}.pr-header-meta .pr-meta-item{display:inline-flex;align-items:center;gap:6px}.pr-header-meta .pr-meta-item i{color:var(--ink-muted)}.pr-body-card{margin:18px 0 24px;padding:20px 22px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px}.pr-body-card .paper-section-eyebrow{margin-bottom:12px}.paper-tabs{display:flex;flex-wrap:wrap;gap:0;border-bottom:1px solid var(--border-color);margin:18px 0 0}.paper-tabs .paper-tab{display:inline-flex;align-items:center;gap:8px;padding:10px 16px;background:0 0;border:0;border-bottom:2px solid transparent;font-family:var(--font-sans);font-size:13.5px;font-weight:500;color:var(--ink-muted);cursor:pointer;transition:color .15s ease,border-color .15s ease;margin-bottom:-1px}.paper-tabs .paper-tab:hover{color:var(--color)}.paper-tabs .paper-tab.active{color:var(--color);border-bottom-color:var(--color)}.paper-tabs .paper-tab i{color:inherit;opacity:.85}.paper-tabs .paper-tab:active,.paper-tabs .paper-tab:focus,.paper-tabs .paper-tab:focus-visible{outline:0;box-shadow:none}.paper-tabs .paper-tab:focus-visible{color:var(--color);background:var(--hover-bg-color);border-radius:6px 6px 0 0}.pr-diff{margin:16px 0 28px;display:flex;flex-direction:column;gap:16px}.diff-file-block{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;overflow:hidden}.diff-file-header{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--paper-bg-alt);border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:12.5px;color:var(--color)}.diff-file-icon{color:var(--ink-muted);flex-shrink:0}.diff-file-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-break:break-all}.diff-file-status{flex-shrink:0;padding:2px 8px;border-radius:999px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;border:1px solid var(--border-color);color:var(--ink-muted);background:var(--paper-card)}.diff-file-status-added{color:#2f6b3e;border-color:rgba(47,107,62,.35);background:rgba(47,107,62,.08)}.diff-file-status-deleted{color:#a13a2e;border-color:rgba(161,58,46,.35);background:rgba(161,58,46,.08)}.diff-file-status-renamed{color:#8a6b1e;border-color:rgba(138,107,30,.35);background:rgba(138,107,30,.08)}.dark-mode .diff-file-status-added{color:#a7e2a7;border-color:rgba(167,226,167,.35);background:rgba(167,226,167,.08)}.dark-mode .diff-file-status-deleted{color:#ff8b7b;border-color:rgba(255,139,123,.35);background:rgba(255,139,123,.08)}.dark-mode .diff-file-status-renamed{color:#ffd37a;border-color:rgba(255,211,122,.35);background:rgba(255,211,122,.08)}.diff-file-table{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:12.5px;line-height:1.55;table-layout:fixed}.diff-file-table tr.diff-row{vertical-align:top}.diff-file-table .diff-gutter{width:48px;padding:0 10px;text-align:right;color:var(--ink-muted);background:var(--paper-bg-alt);border-right:1px solid var(--border-color);user-select:none;font-variant-numeric:tabular-nums;vertical-align:top;white-space:nowrap}.diff-file-table .diff-gutter-new{border-right:1px solid var(--border-color)}.diff-file-table .diff-sign{width:18px;padding:0 6px;text-align:center;color:var(--ink-muted);background:var(--paper-bg-alt);user-select:none;vertical-align:top}.diff-file-table .diff-code{padding:1px 12px;white-space:pre-wrap;word-break:break-word;color:var(--color)}.diff-row-hunk td{background:var(--paper-bg-alt)!important;color:var(--ink-muted);border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);font-size:12px;letter-spacing:.02em;padding-top:6px;padding-bottom:6px}.diff-row-hunk .diff-code{color:var(--ink-muted)}.diff-row-add .diff-code{background:rgba(47,107,62,.1);color:#1f4a2a}.diff-row-add .diff-gutter,.diff-row-add .diff-sign{background:rgba(47,107,62,.06);color:#2f6b3e}.diff-row-remove .diff-code{background:rgba(161,58,46,.1);color:#6e1f1a}.diff-row-remove .diff-gutter,.diff-row-remove .diff-sign{background:rgba(161,58,46,.06);color:#a13a2e}.dark-mode .diff-row-add .diff-code{background:rgba(167,226,167,.1);color:#c9f0c9}.dark-mode .diff-row-add .diff-gutter,.dark-mode .diff-row-add .diff-sign{background:rgba(167,226,167,.06);color:#a7e2a7}.dark-mode .diff-row-remove .diff-code{background:rgba(255,139,123,.1);color:#ffc9c0}.dark-mode .diff-row-remove .diff-gutter,.dark-mode .diff-row-remove .diff-sign{background:rgba(255,139,123,.06);color:#ff8b7b}@media (max-width:700px){.diff-file-table{font-size:11.5px}.diff-file-table .diff-gutter{width:36px;padding:0 6px}.diff-file-table .diff-sign{width:14px;padding:0 4px}.diff-file-table .diff-code{padding:1px 8px}}.pr-comments{list-style:none;margin:16px 0 28px;padding:0;display:flex;flex-direction:column;gap:12px}.pr-comment{padding:16px 18px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px}.pr-comment-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;padding-bottom:8px;margin-bottom:8px;border-bottom:1px solid var(--border-color)}.pr-comment-author{font-weight:600;color:var(--color);font-size:14px;display:inline-flex;align-items:center;gap:6px}.pr-comment-author i{color:var(--ink-muted);font-size:12px}.pr-comment-date{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-muted);letter-spacing:.02em}.pr-comment-body{color:var(--color);font-size:14px;line-height:1.6}.pr-comment-body :last-child{margin-bottom:0}@media (max-width:700px){.pr-page-inner{padding-top:14px;padding-bottom:40px}.pr-body-card{padding:14px 14px;border-radius:8px}.pr-comment{padding:12px 14px}.paper-tabs .paper-tab{padding:10px 12px;font-size:13px}.pr-diff pre{font-size:11.5px;padding:12px}}.toast{background-color:var(--paper-card)!important;color:var(--color)!important;border:1px solid var(--border-color)!important;border-radius:10px!important;box-shadow:0 8px 24px rgba(0,0,0,.08);backdrop-filter:none;min-width:280px;max-width:calc(100vw - 24px)}.toast .toast-header{background:0 0!important;color:var(--color)!important;border-bottom:1px solid var(--border-color)!important;font-family:var(--font-sans);padding:10px 12px}.toast .toast-header strong{font-weight:600;color:var(--color);letter-spacing:.01em}.toast .toast-header .close{color:var(--ink-muted)!important;text-shadow:none;opacity:.9;font-size:1.2rem}.toast .toast-header .close:hover{color:var(--color)!important;opacity:1}.toast .toast-body{color:var(--color)!important;padding:12px;font-size:14px;line-height:1.45}.dark-mode .toast{box-shadow:0 8px 24px rgba(0,0,0,.5)}.paper-crumbs{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);letter-spacing:0}.paper-crumbs .here,.paper-crumbs a{color:var(--color);text-decoration:none}.paper-page-title{font-family:var(--font-sans);font-weight:600;font-size:32px;line-height:1.2;letter-spacing:-.035em;margin:8px 0 6px;color:var(--color)}.paper-page-title em{color:inherit;font-style:normal}.paper-page-lede{color:var(--ink-soft);font-size:16px;max-width:640px;line-height:1.55}.paper-meta-rule{display:flex;flex-wrap:wrap;gap:16px 24px;align-items:center;margin:22px 0 18px;padding:12px 0;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:11.5px;color:var(--ink-muted);letter-spacing:.02em}.paper-meta-rule b{color:var(--color);font-weight:500}.paper-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:10px;padding:20px 22px}.dark-mode .paper-card{background:var(--admin-stat-bg)}.paper-section-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);font-weight:500;margin-bottom:10px;display:block}.paper-section-title{font-family:var(--font-serif);font-weight:400;font-size:22px;letter-spacing:-.01em;margin:0 0 8px}.paper-kbd{display:inline-block;font-family:var(--font-mono);font-size:10.5px;padding:1px 6px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:3px;background:var(--paper-card,#fff);color:var(--color)}.paper-pill{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:10.5px;padding:2px 8px;border:1px solid var(--border-color);border-radius:999px;color:var(--ink-soft);background:var(--paper-card,#fff);letter-spacing:.04em}.paper-pill.accent{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}.paper-pill.good{border-color:#2f6b3e;color:#2f6b3e}.paper-pill.warn{border-color:#8a6b1e;color:#8a6b1e}.paper-pill.bad{border-color:#a13a2e;color:#a13a2e}.paper-row{display:grid;grid-template-columns:1fr auto;gap:16px;align-items:start;padding:18px 0;border-bottom:1px solid var(--border-color)}.paper-row:first-child{border-top:1px solid var(--border-color)}.paper-row .row-title{font-family:var(--font-serif);font-size:20px;letter-spacing:-.01em;line-height:1.2}.paper-row .row-title a{color:var(--color)}.paper-row .row-meta{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.03em;margin-top:6px;display:flex;flex-wrap:wrap;gap:14px}.paper-row .row-actions{display:flex;gap:6px;flex-shrink:0}.paper-form-step{display:grid;grid-template-columns:48px 1fr;gap:20px;padding:22px 0;border-bottom:1px solid var(--border-color)}.paper-form-step:first-child{border-top:1px solid var(--border-color)}.paper-form-step .step-num{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-muted);padding-top:4px;letter-spacing:.02em}.paper-form-step .step-title{font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;line-height:1.2;margin-bottom:4px}.paper-form-step .step-sub{color:var(--ink-soft);font-size:13.5px;margin-bottom:14px;text-wrap:pretty}.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}.paper-switch-row{display:flex;align-items:center;justify-content:space-between;padding:10px 0;gap:16px}.paper-switch-row+.paper-switch-row{border-top:1px dashed var(--border-soft)}.paper-switch-row .sw-label{font-weight:500}.paper-switch-row .sw-sub{display:block;font-size:12.5px;color:var(--ink-muted);margin-top:2px}.paper-page .custom-select,.paper-page .form-control{border-radius:6px;border-color:var(--border-color);font-size:14px;padding:8px 12px;height:auto;background:var(--input-bg);color:var(--input-color)}.paper-page .custom-select:focus,.paper-page .form-control:focus{color:var(--input-color);background:var(--input-bg);border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}.custom-select:focus,.form-control:focus,input:focus,select:focus,textarea:focus{color:var(--input-color)!important;background-color:var(--input-bg)!important}.form-control::selection,input::selection,textarea::selection{background:var(--accent-soft);color:var(--input-color)}.dark-mode .form-control::selection,.dark-mode input::selection,.dark-mode textarea::selection{background:rgba(167,178,255,.3);color:var(--input-color)}.btn.btn-ink{background:var(--color);color:var(--canvas-bg-color);border:1px solid var(--color);font-weight:500;border-radius:6px;padding:8px 14px}.btn.btn-ink:hover{background:var(--primary-hover-bg);color:var(--canvas-bg-color);border-color:var(--primary-hover-bg)}.btn.btn-accent{background:var(--accent);color:#fff;border:1px solid var(--accent);font-weight:500;border-radius:6px}.btn.btn-accent:hover{background:#2e3bb8;border-color:#2e3bb8;color:#fff}.paper-page .dashboard-filter-row{display:flex;align-items:center;gap:10px;padding:14px 0;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);margin:18px 0 0;flex-wrap:wrap}.paper-page .dashboard-filter-row .form-control{height:38px;background:var(--paper-card)}.paper-page .dashboard-filter-controls{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.paper-page .dashboard-filter-row .btn-group .btn,.paper-page .dashboard-filter-row .dropdown-toggle{background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);height:38px;padding:0 14px;font-size:14px;font-weight:500;border-radius:var(--radius-control,6px);display:inline-flex;align-items:center;gap:6px;line-height:1}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-label{color:var(--ink-muted);font-weight:500}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-label::after{content:"·";margin-left:6px;color:var(--ink-faint)}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-value{font-weight:500}.paper-page .dashboard-filter-row .dropdown-toggle .fas{font-size:11px;color:var(--ink-muted)}.paper-page .dashboard-filter-row .btn-group .btn:hover,.paper-page .dashboard-filter-row .dropdown-toggle:hover{background:var(--hover-bg-color);border-color:var(--border-color);color:var(--color)}.paper-page .dashboard-filter-row .btn-group{gap:0}.paper-page .dashboard-filter-row .btn-group .btn{border-radius:0;border-right-width:0}.paper-page .dashboard-filter-row .btn-group .btn:first-child{border-top-left-radius:6px;border-bottom-left-radius:6px}.paper-page .dashboard-filter-row .btn-group .btn:last-child{border-top-right-radius:6px;border-bottom-right-radius:6px;border-right-width:1px}.paper-page .dashboard-filter-row .btn-group .btn.btn-primary{background:var(--color);color:var(--canvas-bg-color);border-color:var(--color)}.paper-page .dashboard-filter-row .search-wrap{flex:1 1 280px;min-width:220px}.paper-page .quota-row{display:grid;grid-template-columns:repeat(3,1fr);gap:40px;margin:24px 0 0;padding:0;background:0 0;border:none;border-radius:0}.paper-page .quota-label{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);opacity:1}.paper-page .quota-value{font-family:var(--font-mono);font-size:13px;font-variant-numeric:tabular-nums;color:var(--color)}.paper-page .quota-value .quota-unlimited-tag{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);margin-left:4px}.paper-page .quota-track{height:6px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:999px;overflow:hidden;width:100%;min-width:0}.paper-page .quota-track .quota-fill{height:100%;min-width:2px;border-radius:999px;background:var(--color);transition:width .3s ease}.paper-page .quota-track.quota-warn .quota-fill{background:var(--status-progress)}.paper-page .quota-track.quota-danger .quota-fill{background:var(--status-error)}.paper-page .quota-track.quota-unlimited{background:repeating-linear-gradient(-45deg,var(--paper-bg-alt) 0 4px,var(--border-soft,var(--border-color)) 4px 6px)}.paper-page .repo-list{margin-top:8px}.paper-page .repo-list-item{padding:20px 0;gap:16px;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-page .repo-list-item:first-child{border-top:none}.paper-page .repo-list-item-header{gap:10px;margin-bottom:6px}.paper-page .repo-name{font-family:var(--font-mono);font-size:15px;color:var(--color)}.paper-page .repo-source{font-size:13px;color:var(--ink-soft);opacity:1;gap:10px}.paper-page .repo-source a{color:var(--ink-soft);border-bottom:1px dotted var(--border-color)}.paper-page .repo-source a:hover{color:var(--color)}.paper-page .repo-meta{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);opacity:1;gap:16px;margin-top:10px}.paper-page .type-badge{font-family:var(--font-mono);font-size:11px;padding:3px 8px;letter-spacing:.1em}.paper-page .repo-list-item-actions .dropdown-toggle{background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);border-radius:6px;font-size:13px;padding:6px 12px;height:auto}.paper-settings{max-width:1180px;margin:0 auto;padding:48px 32px 80px}.paper-settings .paper-page-title{max-width:14em}.paper-settings-body{display:grid;grid-template-columns:220px 1fr;gap:60px;margin-top:32px;border-top:1px solid var(--border-color);padding-top:28px}.paper-settings-toc{position:sticky;top:32px;align-self:start}.paper-settings-toc-head{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);margin-bottom:12px}.paper-settings-toc nav{display:flex;flex-direction:column;gap:2px}.paper-settings-toc nav a{padding:8px 0 8px 14px;border-left:2px solid transparent;color:var(--ink-soft);font-size:14px;text-decoration:none}.paper-settings-toc nav a.active,.paper-settings-toc nav a:hover{color:var(--color);border-left-color:var(--color)}.paper-settings-main{min-width:0;max-width:720px}.paper-settings-section{padding:28px 0;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-settings-section:first-child{padding-top:0}.paper-settings-section:last-of-type{border-bottom:none}.paper-settings-section .form-group{margin-bottom:18px}.paper-settings-section .form-check{padding:10px 0 10px 28px;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-settings-section .form-check:last-child{border-bottom:none}.paper-settings-section .form-check-label{font-family:var(--font-sans);font-size:14px;font-weight:500;color:var(--color);cursor:pointer}.paper-settings-section .form-check-input{cursor:pointer;width:16px;height:16px;margin-top:3px;margin-left:-28px;accent-color:var(--color)}.paper-settings-section .form-text{color:var(--ink-soft);font-size:13px;margin-top:4px;max-width:48em}.paper-settings-section .paper-section-copy{margin:-4px 0 14px}.paper-settings-footer{padding-top:24px;display:flex;flex-direction:column;gap:14px}.paper-settings-footer .btn-ink{align-self:flex-start}.paper-settings-actions{display:flex;align-items:center;flex-wrap:wrap;gap:14px}.paper-settings-hint{font-size:13px;color:var(--ink-muted)}.paper-inline-alert{display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:var(--radius-control,6px);border:1px solid var(--border-color);background:var(--paper-bg-alt);font-size:13px;color:var(--color)}.paper-inline-alert i{margin-top:2px;color:var(--ink-muted)}.paper-inline-alert-error{border-color:var(--status-error);background:var(--status-error-soft)}.paper-inline-alert-error i{color:var(--status-error)}.paper-account-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border:1px solid var(--border-color);border-radius:var(--radius-surface,10px);background:var(--paper-card);margin-bottom:20px}.paper-account-card img{flex-shrink:0}.paper-account-identity{flex:1 1 auto;min-width:0}.paper-account-name{font-family:var(--font-mono);font-size:14px;font-weight:500;color:var(--color)}.paper-account-meta{font-size:13px;color:var(--ink-muted);margin-top:2px}.paper-danger-zone{padding:16px;border:1px solid var(--status-error);border-radius:var(--radius-surface,10px);background:var(--status-error-soft)}.paper-danger-head{font-weight:600;font-size:14px;color:var(--status-error);margin-bottom:6px}.paper-danger-copy{font-size:13px;color:var(--ink-soft);max-width:52em;margin-bottom:12px}.paper-danger-zone .paper-inline-alert{margin-bottom:12px}.btn.btn-outline-danger{background:0 0;color:var(--status-error);border:1px solid var(--status-error);font-weight:500;border-radius:var(--radius-control,6px);padding:8px 14px}.btn.btn-outline-danger:hover{background:var(--status-error);color:#fff;border-color:var(--status-error)}.navbar .user-menu{min-width:260px}.navbar .user-menu .dropdown-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px!important}.navbar .user-menu .dropdown-item i{width:16px;margin-top:3px;font-size:13px;color:var(--ink-muted);text-align:center}.navbar .user-menu .dropdown-item span{display:flex;flex-direction:column;line-height:1.3}.navbar .user-menu .dropdown-item small{font-size:12px;color:var(--ink-muted)}.navbar .user-menu .dropdown-header{font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-muted);padding:8px 10px 6px}.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}@media (max-width:900px){.paper-settings-body{grid-template-columns:1fr;gap:24px}.paper-settings-toc{position:static}.paper-settings-toc nav{flex-direction:row;flex-wrap:wrap;gap:8px}.paper-settings-toc nav a{border-left:none;border-bottom:2px solid transparent;padding:4px 0}.paper-settings-toc nav a.active,.paper-settings-toc nav a:hover{border-bottom-color:var(--color);border-left:none}}.paper-table{display:block;margin-top:20px;background:0 0;border:none;border-top:1px solid var(--border-color);border-radius:0;overflow:visible}.paper-table .paper-table-head,.paper-table .paper-table-row{display:grid;grid-template-columns:minmax(280px,2.4fr) minmax(140px,1fr) 140px 90px 140px 52px;align-items:center;gap:18px;padding:12px 4px}.paper-table.paper-table-conferences .paper-table-head,.paper-table.paper-table-conferences .paper-table-row{grid-template-columns:minmax(280px,2.4fr) 140px 90px 220px 52px}.paper-table.paper-table-repos .paper-table-head,.paper-table.paper-table-repos .paper-table-row{grid-template-columns:minmax(280px,2.4fr) minmax(140px,1fr) 90px 140px 52px}.paper-table.paper-table-repos.has-bulk .paper-table-head,.paper-table.paper-table-repos.has-bulk .paper-table-row{grid-template-columns:28px minmax(280px,2.4fr) minmax(140px,1fr) 90px 140px 52px;gap:12px}.paper-table .admin-users-row{grid-template-columns:28px minmax(280px,2.4fr) 90px 120px 100px 52px!important;gap:12px!important}.paper-table.paper-table-dashboard .paper-table-head,.paper-table.paper-table-dashboard .paper-table-row{grid-template-columns:minmax(320px,3fr) 190px 90px 170px 52px;gap:20px;padding:8px 4px}.paper-table.paper-table-dashboard .cell-anon .anon-text{gap:2px}.paper-table.paper-table-dashboard .cell-anon .repo-name{padding:0}.paper-table .paper-table-head{background:0 0;border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-muted);padding-top:8px;padding-bottom:8px}.paper-table .paper-table-head .sortable{cursor:pointer;user-select:none;display:inline-flex;align-items:center;gap:6px;background:0 0;border:none;padding:2px 0;margin:0;font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;border-radius:4px}.paper-table .paper-table-head .sortable:hover{color:var(--color)}.paper-table .paper-table-head .sortable.active{color:var(--color)}.paper-table .paper-table-head .sortable i{font-size:10px}.paper-table .paper-table-head .num,.paper-table .paper-table-row .num{text-align:right}.paper-table .paper-table-head .num .sortable{flex-direction:row-reverse}.paper-table .paper-table-row{border-top:1px solid var(--border-soft,var(--border-color));font-size:14px;transition:background-color .12s ease}.paper-table .paper-table-row:hover{background:var(--hover-bg-color)}.paper-table .paper-table-row.row-clickable{cursor:pointer}.paper-table .paper-table-row.repo-inactive>:not(.cell-actions){opacity:.55}.paper-table .paper-table-row.repo-error{background:var(--status-error-soft);border-left:2px solid var(--status-error);padding-left:6px}.paper-table .paper-table-row.repo-error:hover{background:var(--status-error-soft-hover)}.cell-status .status-sub.status-sub-error,.paper-table .cell-status .status-sub-error{color:var(--status-error)}.paper-table .cell-status .status-sub-warn{color:var(--status-progress)}.paper-table .cell-anon{display:flex;align-items:center;gap:12px;min-width:0}.paper-table .cell-anon .type-badge{flex-shrink:0;min-width:52px;justify-content:center}.paper-table .cell-anon .anon-text{min-width:0;display:flex;flex-direction:column;gap:3px}.paper-table .cell-anon .anon-title{display:flex;align-items:center;gap:8px;min-width:0}.paper-table .cell-anon .anon-title .type-badge{margin-left:0}.paper-table .cell-anon .repo-name{font-family:var(--font-mono);font-size:14px;color:var(--color);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;max-width:100%;padding:2px 0}.paper-table .cell-anon .repo-name-static{color:var(--ink-muted);font-style:italic}.paper-table .cell-anon .anon-sub{font-family:var(--font-sans);font-size:13px;color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:10px;min-width:0}.paper-table .cell-anon .anon-sub .anon-source{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-anon .anon-sub .conf-tag{flex:0 1 auto;min-width:72px;max-width:50%}.paper-table .cell-anon .anon-sub a{color:var(--ink-muted);border-bottom:1px dotted var(--border-color);padding:4px 0;margin:-4px 0}.paper-table .cell-anon .anon-sub a:hover{color:var(--color)}.paper-table .cell-anon .anon-sub .commit-hash{font-family:var(--font-mono);font-size:12px}.paper-table .cell-conf{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-conf span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-status{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;font-size:14px;color:var(--color);min-width:0}.paper-table .cell-status .status-line{display:inline-flex;align-items:center;gap:8px}.paper-table .cell-status .status-sub{flex-basis:100%;font-family:var(--font-sans);font-size:13px;line-height:1.3;color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-views{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--color)}.paper-table .cell-expires{font-family:var(--font-sans);font-size:13px;color:var(--ink-soft)}.paper-table .cell-expires .expires-never{color:var(--ink-muted)}.paper-table .cell-expires .expires-past{color:var(--ink-muted)}.paper-table .empty-dash{color:var(--ink-faint)}.paper-table .cell-actions{display:flex;justify-content:flex-end}.status-dot{width:8px;height:8px;border-radius:999px;display:inline-block;flex-shrink:0;background:var(--status-inactive)}.status-dot.status-ready{background:var(--status-ready)}.status-dot.status-error{background:var(--status-error)}.status-dot.status-preparing,.status-dot.status-progress{background:var(--status-progress)}.status-dot.status-expired,.status-dot.status-removed{background:var(--status-inactive)}.dashboard-page{padding-bottom:96px}.dashboard-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;margin-top:12px;font-size:13px;color:var(--ink-muted)}.dashboard-meta .dashboard-count{font-variant-numeric:tabular-nums}.btn-link-inline{background:0 0;border:none;padding:2px 4px;margin:-2px 0;font:inherit;color:var(--link-color);cursor:pointer;border-radius:4px}.btn-link-inline:hover{color:var(--link-hover-color);text-decoration:underline}.btn.btn-outline-ink{background:0 0;color:var(--color);border:1px solid var(--border-color);font-weight:500;border-radius:var(--radius-control,6px);padding:8px 14px}.btn.btn-outline-ink:hover{background:var(--hover-bg-color);color:var(--color);border-color:var(--ink-faint)}.paper-page .dropdown-menu .dropdown-item-danger,.paper-page .dropdown-menu .dropdown-item-danger i{color:var(--status-error)}.paper-page .dropdown-menu .dropdown-item-danger:focus,.paper-page .dropdown-menu .dropdown-item-danger:hover{background:var(--status-error-soft-hover);color:var(--status-error)}.paper-page .dropdown-menu button.dropdown-item{width:100%;text-align:left;background:0 0;border:none}.paper-page .dropdown-menu .dropdown-divider{border-top-color:var(--border-soft,var(--border-color));margin:4px 0}.paper-table .paper-table-skeleton{pointer-events:none}.skeleton{display:block;background:var(--paper-bg-alt);border-radius:4px;position:relative;overflow:hidden}.skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,var(--hover-bg-color),transparent);animation:skeleton-shimmer 1.4s infinite}.skeleton-line{height:12px}.skeleton-line+.skeleton-line{margin-top:8px}.skeleton-line-sm{height:10px}.skeleton-badge{width:52px;height:22px;border-radius:999px;flex-shrink:0}@keyframes skeleton-shimmer{to{transform:translateX(100%)}}@media (prefers-reduced-motion:reduce){.skeleton::after{animation:none}}.paper-table .paper-table-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:56px 20px;color:var(--ink-muted);text-align:center;border-top:1px solid var(--border-soft,var(--border-color))}.paper-table .paper-table-empty>i{font-size:1.6rem;color:var(--ink-faint)}.paper-table .paper-table-empty-actions{display:flex;gap:8px;margin-top:8px}.bulk-bar{display:flex;align-items:center;gap:8px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:8px;padding:8px 12px;margin:8px 0}.paper-page.admin-page .paper-page-title{font-size:1.6rem;margin:4px 0}.paper-page.admin-page .paper-page-lede{display:none}.paper-page.admin-page .paper-crumbs{margin-bottom:4px}.paper-page.admin-page .admin-nav{margin:6px 0 12px}.admin-filter-toolbar{display:flex;flex-direction:column;gap:6px;margin:8px 0 12px;position:sticky;top:0;z-index:4;background:var(--canvas-bg-color);padding:6px 0;border-bottom:1px solid var(--border-soft,var(--border-color));box-shadow:0 1px 0 var(--canvas-bg-color)}@media (max-width:900px){.admin-filter-toolbar{position:static;box-shadow:none}}.admin-filter-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.admin-filter-row .search-wrap{flex:1;min-width:220px}.admin-filter-row .search-wrap .form-control{height:34px}.admin-filter-inline{display:inline-flex;align-items:center;gap:4px;font-size:12px;color:var(--ink-muted)}.admin-filter-inline>label{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted);margin:0;white-space:nowrap}.admin-filter-inline .form-control-sm{height:28px;padding:2px 8px;font-size:12px}.admin-filter-inline input[type=search].form-control-sm,.admin-filter-inline input[type=text].form-control-sm{width:130px}.admin-filter-inline input[type=date].form-control-sm{width:130px}.admin-filter-inline select.form-control-sm{width:auto;min-width:88px}.admin-filter-inline .arrow{color:var(--ink-muted);font-size:11px}.admin-filter-spacer{flex:1}.admin-active-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;color:var(--ink-muted)}.admin-active-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 10px;border:1px solid var(--border-color);border-radius:999px;background:var(--hover-bg-color);font-size:12px;color:var(--color)}.admin-active-chip .key{color:var(--ink-muted);font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.admin-active-chip button{background:0 0;border:none;cursor:pointer;padding:0 4px;color:var(--ink-muted);font-size:12px;border-radius:50%}.admin-active-chip button:hover{color:#b42318}.admin-search-hint{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);pointer-events:none;border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;background:var(--paper-card)}.admin-filter-row .search-wrap{position:relative}.admin-filter-row .search-wrap .form-control{padding-right:40px}.paper-table-row.row-selected{background:rgba(47,122,68,.06)}.queue-state-pills{display:inline-flex;gap:6px;margin-left:8px;flex-wrap:wrap}.queue-state-pills .pill{font-size:11px;padding:2px 8px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted)}.queue-state-pills .pill-active{color:#c48a2e;border-color:rgba(196,138,46,.4)}.queue-state-pills .pill-failed{color:#b42318;border-color:rgba(180,35,24,.4)}.queue-state-pills .pill-completed{color:#2f7a44;border-color:rgba(47,122,68,.3)}.queue-state-pills .pill-waiting{color:var(--ink-soft)}.queue-state-pills .pill-delayed{color:var(--ink-muted)}.admin-summary{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:8px 0 12px;font-size:13px}.admin-summary .summary-total{font-family:var(--font-serif);font-size:1.05rem;color:var(--color);margin-right:8px}.admin-summary .summary-meta{color:var(--ink-muted);font-size:12px;margin-right:8px}.admin-summary .summary-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted);cursor:pointer;font-size:12px;user-select:none;transition:background-color .1s,border-color .1s}.admin-summary .summary-pill:hover{background:var(--hover-bg-color)}.admin-summary .summary-pill .count{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--color)}.admin-summary .summary-pill.active{background:var(--primary-bg);color:var(--primary-color);border-color:var(--primary-bg)}.admin-summary .summary-pill.active .count{color:inherit}.admin-summary .summary-pill.error .count{color:#b42318}.admin-summary .summary-pill.warn .count{color:#c48a2e}.admin-summary .summary-pill.ok .count{color:#2f7a44}.dark-mode .admin-summary .summary-pill.error .count{color:#f08a82}.dark-mode .admin-summary .summary-pill.warn .count{color:#e8b970}.dark-mode .admin-summary .summary-pill.ok .count{color:#7dc894}.dark-mode .queue-state-pills .pill-active{color:#e8b970;border-color:rgba(232,185,112,.4)}.dark-mode .queue-state-pills .pill-failed{color:#f08a82;border-color:rgba(240,138,130,.4)}.dark-mode .queue-state-pills .pill-completed{color:#7dc894;border-color:rgba(125,200,148,.3)}.job-progress{flex:1;height:6px;background:var(--border-soft,var(--border-color));border-radius:999px;overflow:hidden;margin-left:12px;max-width:200px}.job-progress-bar{height:100%;background:#2f7a44;transition:width .3s}.paper-table-empty{padding:48px 4px;text-align:center;color:var(--ink-muted);display:flex;flex-direction:column;align-items:center;gap:8px;border-top:1px solid var(--border-soft,var(--border-color))}.paper-table-empty>i{font-size:1.6rem}.q-header{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:12px;margin-bottom:4px}.q-header-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.q-range-btns{display:inline-flex;border:1px solid var(--border-color);border-radius:8px;overflow:hidden}.q-range-btns .btn{border:none;border-radius:0;padding:5px 14px;font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);background:var(--paper-card)}.q-range-btns .btn+.btn{border-left:1px solid var(--border-color)}.q-range-btns .btn.active{background:var(--color);color:var(--paper-card)}.btn-dark{background:var(--color)!important;color:var(--paper-card)!important;border-color:var(--color)!important;border-radius:999px;padding:5px 18px;font-family:var(--font-mono);font-size:11px;font-weight:500;letter-spacing:.04em}.q-cards{display:flex;gap:10px;margin:16px 0;flex-wrap:wrap}.q-card{flex:1;min-width:140px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:14px 16px;cursor:pointer;transition:border-color .15s,box-shadow .15s}.q-card:hover{border-color:var(--ink-muted)}.q-card.selected{border-color:var(--color);box-shadow:0 0 0 1px var(--color)}.q-card-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.q-dot{width:8px;height:8px;border-radius:50%;background:#2f7a44;flex-shrink:0}.q-dot-red{background:#b42318}.dark-mode .q-dot{background:#7dc894}.dark-mode .q-dot-red{background:#f08a82}.q-card-name{font-family:var(--font-mono);font-size:11.5px;font-weight:500;color:var(--ink-muted);letter-spacing:.04em}.q-card-count{font-family:var(--font-serif);font-size:2rem;font-weight:400;line-height:1.1;color:var(--color);margin-bottom:6px}.q-card-sub{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-muted);letter-spacing:.04em}.q-card-bar{height:4px;background:var(--border-color);border-radius:2px;overflow:hidden;margin-top:6px}.q-card-bar-fill{height:100%;background:#3b4ad6;border-radius:2px;transition:width .3s}.dark-mode .q-card-bar-fill{background:#a7b2ff}.q-detail{display:grid;grid-template-columns:1fr 320px;gap:16px;margin-bottom:20px}.q-throughput{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px}.q-chart-wrap{position:relative}.q-throughput canvas{width:100%;display:block}.q-chart-tooltip{position:absolute;pointer-events:none;background:var(--paper-card);border:1px solid var(--border-color);border-radius:6px;padding:6px 10px;font-family:var(--font-mono);font-size:11px;line-height:1.6;white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,.15);z-index:10}.q-chart-tooltip .q-tip-time{color:var(--ink-muted);margin-bottom:2px}.q-chart-tooltip .q-tip-completed{color:#3b4ad6}.q-chart-tooltip .q-tip-failed{color:#b42318}.dark-mode .q-chart-tooltip .q-tip-completed{color:#a7b2ff}.dark-mode .q-chart-tooltip .q-tip-failed{color:#f08a82}.q-chart-crosshair{position:absolute;top:0;width:1px;height:100%;background:var(--ink-muted);opacity:.3;pointer-events:none;z-index:9}.q-stats-panel{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px;display:flex;flex-direction:column}.q-section-label{font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--ink-muted);text-transform:lowercase;letter-spacing:.04em;margin-bottom:14px}.q-section-right{float:right;text-transform:uppercase;font-size:10px;letter-spacing:.08em}.q-legend-completed{color:#3b4ad6}.q-legend-failed{color:#b42318;margin-left:8px}.q-legend-exec{color:#b8860b;margin-left:8px;letter-spacing:2px}.dark-mode .q-legend-completed{color:#a7b2ff}.dark-mode .q-legend-failed{color:#f08a82}.dark-mode .q-legend-exec{color:#f5c842}.q-chart-tooltip .q-tip-exec{color:#b8860b}.dark-mode .q-chart-tooltip .q-tip-exec{color:#f5c842}.q-stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px 20px;flex:1}.q-stat-label{font-family:var(--font-mono);font-size:9.5px;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:2px}.q-stat-value{font-family:var(--font-serif);font-size:1.3rem;font-weight:400;color:var(--color);line-height:1.2}.q-stats-actions{display:flex;gap:8px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border-color)}.q-stats-actions .btn{font-family:var(--font-mono);font-size:11px;padding:6px 14px;border-radius:8px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}.q-jobs{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px}.q-jobs-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:12px}.q-state-filters{display:inline-flex;gap:6px;flex-wrap:wrap}.q-state-toggle{cursor:pointer;display:inline-flex;align-items:center}.q-state-toggle input{display:none}.q-state-chip{font-family:var(--font-mono);font-size:11px;padding:3px 10px;border-radius:10px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted);transition:opacity .15s,background .15s;text-transform:capitalize}.q-state-toggle input:checked+.q-state-chip{opacity:1}.q-state-toggle input:not(:checked)+.q-state-chip{opacity:.4;text-decoration:line-through}.q-state-badge,.q-state-chip{font-family:var(--font-mono);font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.3px}.q-state-badge{display:inline-block;padding:2px 8px;border-radius:8px}.q-state-active{background:#dbeafe;color:#1d4ed8}.q-state-waiting{background:#fef3c7;color:#92400e}.q-state-delayed{background:#e0e7ff;color:#4338ca}.q-state-failed{background:#fee2e2;color:#b91c1c}.q-state-completed{background:#dcfce7;color:#166534}.dark-mode .q-state-active{background:rgba(59,130,246,.2);color:#93bbfd}.dark-mode .q-state-waiting{background:rgba(251,191,36,.15);color:#fbbf24}.dark-mode .q-state-delayed{background:rgba(99,102,241,.2);color:#a5b4fc}.dark-mode .q-state-failed{background:rgba(239,68,68,.2);color:#fca5a5}.dark-mode .q-state-completed{background:rgba(34,197,94,.15);color:#86efac}.q-cell-state{width:120px}.q-delay-hint{display:block;font-size:10px;color:var(--ink-muted);margin-top:2px;font-family:var(--font-mono)}.q-search-row{display:flex;align-items:center;gap:10px;margin-bottom:12px}.q-search-row .form-control{max-width:320px;height:34px}.q-auto-refresh{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);display:inline-flex;align-items:center;gap:6px;cursor:pointer;white-space:nowrap}.q-table{width:100%;border-collapse:collapse}.q-table thead th{font-family:var(--font-mono);font-size:9.5px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;padding:8px 10px;border-bottom:1px solid var(--border-color);text-align:left;white-space:nowrap}.q-table tbody tr{border-bottom:1px solid var(--border-color);transition:background .1s}.q-table tbody tr:hover{background:var(--paper-bg-alt)}.q-table tbody tr:last-child{border-bottom:none}.q-table tbody td{padding:10px 10px;font-size:.88rem;vertical-align:middle}.q-cell-id{font-family:var(--font-mono);font-size:12px;white-space:nowrap}.q-cell-id{display:flex;align-items:center;gap:8px}.q-chevron{font-size:9px;color:var(--ink-muted);transition:transform .15s;flex-shrink:0}.q-chevron-open{transform:rotate(90deg)}.q-cell-id a{color:var(--color)}.q-cell-payload{font-family:var(--font-mono);font-size:12px;color:var(--color)}.q-payload-detail{color:var(--ink-muted)}.q-cell-num{font-family:var(--font-mono);font-size:12px;text-align:center;white-space:nowrap}.q-cell-progress{min-width:120px}.q-progress-wrap{display:flex;align-items:center;gap:8px}.q-progress-bar{flex:1;height:6px;border-radius:3px;overflow:hidden;background-image:linear-gradient(to right,#3b4ad6 var(--pct,0),var(--border-color) var(--pct,0))}.dark-mode .q-progress-bar{background-image:linear-gradient(to right,#a7b2ff var(--pct,0),var(--border-color) var(--pct,0))}.q-progress-label{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);white-space:nowrap;min-width:32px;text-align:right}.q-cell-actions{text-align:right;white-space:nowrap}.q-cell-actions .btn{padding:3px 8px;font-size:11px;background:0 0;border:1px solid var(--border-color);color:var(--ink-muted)}.q-cell-actions .btn:hover{color:var(--color)}.q-row-failed{background:rgba(180,35,24,.04)}.dark-mode .q-row-failed{background:rgba(240,138,130,.06)}.q-error-detail{padding:4px 10px 12px;font-size:12px}.q-error-reason{color:#b42318;font-size:.82rem;margin-bottom:4px}.dark-mode .q-error-reason{color:#f08a82}.q-toast-error{background:#fbe7e7;color:#b42318;border:1px solid #f3c7c7;border-radius:8px;padding:10px 16px;margin-bottom:12px;font-family:var(--font-mono);font-size:12px;cursor:pointer;display:flex;align-items:center;gap:8px}.dark-mode .q-toast-error{background:rgba(240,138,130,.1);color:#f08a82;border-color:rgba(240,138,130,.25)}.q-error-stack{font-size:.78rem;max-height:120px;overflow:auto;margin:4px 0 0;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:6px;padding:8px 10px}.q-row-expanded>td{border-bottom-color:transparent}.q-detail-row>td{padding:0 10px 14px!important;border-bottom:1px solid var(--border-color)}.q-job-detail{background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:8px;padding:14px 16px}.q-job-detail-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px 20px;margin-bottom:10px}.q-job-detail-item{display:flex;flex-direction:column;gap:2px}.q-job-detail-label{font-family:var(--font-mono);font-size:9px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em}.q-job-detail-value{font-family:var(--font-mono);font-size:12px;color:var(--color);word-break:break-all}.q-job-detail-value a{color:var(--color)}.q-job-detail-error{margin:8px 0}.q-job-detail-actions{display:flex;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid var(--border-color)}.q-job-detail-actions .btn{font-family:var(--font-mono);font-size:11px;padding:5px 12px;border-radius:6px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}@media (max-width:900px){.q-detail{grid-template-columns:1fr}.q-cards{flex-direction:column}.q-card{min-width:100%}.q-jobs-header{flex-direction:column;align-items:flex-start}.q-tabs{flex-wrap:wrap}.q-search-row{flex-wrap:wrap}.q-search-row .form-control{max-width:100%;flex:1}.q-table{font-size:12px}.q-header{flex-direction:column}.q-job-detail-grid{grid-template-columns:1fr 1fr}}@media (max-width:900px){.paper-table .paper-table-head{display:none}.paper-table .admin-users-row,.paper-table .paper-table-row,.paper-table.paper-table-conferences .paper-table-row,.paper-table.paper-table-repos .paper-table-row{display:flex!important;flex-wrap:wrap;align-items:center;gap:6px 12px;padding:14px 0;grid-template-columns:none!important}.paper-table .cell-anon{flex:1 1 calc(100% - 52px);min-width:0;order:1}.paper-table .cell-actions{flex:0 0 auto;order:2;align-self:center}.paper-table .cell-conf,.paper-table .cell-expires,.paper-table .cell-status,.paper-table .cell-views{order:3;font-size:13px;color:var(--ink-muted);display:inline-flex;align-items:center;gap:6px;text-align:left}.paper-table .cell-status{flex-wrap:nowrap;gap:6px;min-width:0}.paper-table .cell-status .status-sub{flex-basis:auto;font-size:13px;line-height:inherit}.paper-table .cell-status .status-sub::before{content:"·";margin-right:6px;color:var(--ink-faint)}.paper-table .cell-conf{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-views.num{text-align:left}.paper-table .paper-table-skeleton .cell-expires,.paper-table .paper-table-skeleton .cell-status,.paper-table .paper-table-skeleton .cell-views{flex:1 1 30%}.paper-table .cell-views::before{content:'\f06e';font-family:'Font Awesome 5 Free';font-weight:900;font-size:10px;color:var(--ink-muted)}.paper-table .cell-expires::before{content:'\f017';font-family:'Font Awesome 5 Free';font-weight:400;font-size:10px;color:var(--ink-muted)}.paper-table .cell-conf::before{content:'\f51c';font-family:'Font Awesome 5 Free';font-weight:900;font-size:10px;color:var(--ink-muted)}.paper-table-dashboard .cell-conf::before{content:none}.paper-table .cell-conf:has(.empty-dash),.paper-table .cell-expires:has(.empty-dash){display:none}.paper-table .cell-expires::before,.paper-table .cell-views::before{color:var(--ink-faint)}}.paper-page .dropdown-menu{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface,10px);box-shadow:0 12px 28px rgba(26,24,21,.08);padding:6px;min-width:220px;font-family:var(--font-sans);font-size:14px;color:var(--color);margin-top:6px}.paper-page .dropdown-menu .dropdown-header{font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);padding:8px 10px 6px;font-weight:500}.paper-page .dropdown-menu .dropdown-item{border-radius:6px;padding:8px 10px;color:var(--color);font-size:14px;display:flex;align-items:center;gap:8px}.paper-page .dropdown-menu .dropdown-item:focus,.paper-page .dropdown-menu .dropdown-item:hover{background:var(--hover-bg-color);color:var(--color)}.paper-page .dropdown-menu .dropdown-item i{width:16px;font-size:13px;color:var(--ink-muted)}.paper-page .dropdown-menu .form-check.dropdown-item{padding:8px 10px 8px 36px!important;position:relative}.paper-page .dropdown-menu .form-check.dropdown-item .form-check-input{position:absolute;left:12px;top:50%;transform:translateY(-50%);margin:0;width:16px;height:16px;accent-color:var(--color);cursor:pointer}.paper-page .dropdown-menu .form-check.dropdown-item .form-check-label{font-family:var(--font-sans);font-size:14px;color:var(--color);font-weight:400;margin:0;cursor:pointer}.btn.btn-icon-dots{width:32px;height:32px;padding:0;border:1px solid transparent;background:0 0;color:var(--ink-muted);border-radius:6px;display:inline-flex;align-items:center;justify-content:center;line-height:1}.btn.btn-icon-dots:focus,.btn.btn-icon-dots:hover,.btn.btn-icon-dots[aria-expanded=true]{background:var(--hover-bg-color);border-color:var(--border-color);color:var(--color)}.btn.btn-icon-dots::after{display:none}.btn.btn-icon-dots i{font-size:15px}.paper-page .repo-list-empty{border-top:none;padding:56px 20px;color:var(--ink-muted)}.paper-page .filter-chip{background:var(--paper-bg-alt);color:var(--color);border:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:4px 10px;border-radius:999px}@media (max-width:768px){.paper-page .paper-page-title{font-size:clamp(1.4rem,6vw,1.9rem);margin:4px 0 2px}.paper-page .paper-page-lede{font-size:13px;line-height:1.4;margin-bottom:0}.paper-page .btn-ink{padding:6px 12px;font-size:13px}.paper-page .quota-row{grid-template-columns:1fr;gap:12px;padding:0;margin:16px 0 0}.paper-page .dashboard-filter-row{padding:10px 0;margin-top:12px;gap:8px}.paper-page .quota-header{flex-direction:row;align-items:baseline;gap:8px}.paper-page .quota-value{white-space:nowrap}.paper-page .dashboard-filter-controls{width:100%}.paper-page .dashboard-filter-controls .btn-group{flex:1 1 100%;display:flex}.paper-page .dashboard-filter-controls .btn-group .btn{flex:1;justify-content:center}.paper-page .dashboard-filter-controls .dropdown{flex:1 1 0;min-width:0}.paper-page .dashboard-filter-controls .dropdown .dropdown-toggle{width:100%;justify-content:space-between}.paper-page .dashboard-filter-controls .dropdown .filter-btn-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media (max-width:480px){.paper-page .paper-page-lede{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}}.empty-state{text-align:center;padding:40px 20px;opacity:.6}.empty-state i{font-size:2rem;margin-bottom:10px;display:block}.paper-page .progress{height:10px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:999px;overflow:hidden;margin-bottom:20px}.paper-page .progress .progress-bar{background:var(--color);color:var(--canvas-bg-color);font-family:var(--font-mono);font-size:10px;line-height:10px}.paper-page .progress .progress-bar.bg-success{background:var(--status-ready)!important}.paper-page .progress .progress-bar.bg-warning{background:var(--status-progress)!important;color:#fff}.paper-page .progress .progress-bar.bg-danger{background:var(--status-error)!important}.paper-page .progress .progress-bar.bg-dark{background:var(--ink-faint)!important}.paper-page .form>h5,.paper-page form>h5,.paper-page>h5{font-family:var(--font-mono);font-size:11px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-muted);margin:24px 0 8px}.paper-page .accordion .card{background:var(--paper-card,var(--sidebar-bg-color));border:1px solid var(--border-color);border-radius:8px!important;margin-bottom:8px;overflow:hidden}.paper-page .accordion .card+.card{border-top:1px solid var(--border-color)}.paper-page .accordion .card-header{background:0 0;border:none;padding:0}.paper-page .accordion .card-header .btn{color:var(--color)!important;font-family:var(--font-sans);font-weight:500;font-size:14px;padding:14px 18px!important;background:0 0!important;border:none!important;position:relative}.paper-page .accordion .card-header .btn::after{content:"+";position:absolute;right:18px;font-family:var(--font-serif);font-size:20px;color:var(--ink-muted)}.paper-page .accordion .card-header .btn:not(.collapsed)::after{content:"–"}.paper-page .accordion .card-body{padding:6px 18px 18px!important;border-top:1px solid var(--border-soft)}.paper-page .accordion .form-check-label{color:var(--color);font-weight:500;font-size:13.5px}.paper-page .accordion .form-text{color:var(--ink-muted);font-size:12px;margin-top:2px}.faq-section .panel-body code{font-family:var(--font-mono);font-size:13px;padding:1px 6px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:3px;color:var(--color)}.faq-section .panel-body a{color:var(--accent)}@media (max-width:820px){.paper-faq-body{grid-template-columns:1fr;gap:24px}.paper-faq-toc{position:static}}.paper-footer{margin-top:80px;padding:56px max(20px,calc((100vw - 1200px)/ 2)) 28px;border-top:1px solid var(--border-color);background:var(--canvas-bg-color);font-family:var(--font-sans)}.paper-footer-inner{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px}.paper-footer-brand .paper-footer-mark{font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;color:var(--color)}.paper-footer-brand .paper-footer-mark em{font-style:italic;font-weight:400}.paper-footer-tag{margin-top:10px;color:var(--ink-soft);font-size:13.5px;line-height:1.55;max-width:340px}.paper-footer-head{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-muted);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px}.paper-footer-col a{display:block;color:var(--color);font-size:13.5px;padding:4px 0;text-decoration:none}.paper-footer-col a:hover{color:var(--accent)}.paper-footer-rule{height:1px;background:var(--border-color);margin:40px 0 16px}.paper-footer-foot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px;font-size:12px;color:var(--ink-muted);font-family:var(--font-mono);letter-spacing:.02em}.paper-footer-foot .paper-footer-meta{font-family:var(--font-serif);font-style:italic;font-size:13.5px;letter-spacing:0;color:var(--ink-soft)}@media (max-width:768px){.paper-footer-inner{grid-template-columns:1fr 1fr}}.file.folder.truncated .truncated-warning{color:#d39e00;margin-left:4px;font-size:.85em;flex-shrink:0}.file.folder.truncated>a{color:#d39e00}body{--bucket-error-bg:#FCEDED;--bucket-error-fg:#8A1F1F;--bucket-error-bd:#F0C4C4;--bucket-error-dot:#B53737;--bucket-warn-bg:#FFF3DF;--bucket-warn-fg:#7A4D00;--bucket-warn-bd:#F0D8A0;--bucket-warn-dot:#B07A2F;--bucket-info-bg:#EAF2EC;--bucket-info-fg:#2C5D3A;--bucket-info-bd:#C5DCCD;--bucket-info-dot:#5B8D6B;--card-shadow:0 1px 2px rgba(26, 24, 21, 0.04),0 4px 14px rgba(26, 24, 21, 0.05);--card-shadow-hover:0 1px 2px rgba(26, 24, 21, 0.06),0 8px 24px rgba(26, 24, 21, 0.08)}.dark-mode{--bucket-error-bg:rgba(255, 139, 123, 0.10);--bucket-error-fg:#FF8B7B;--bucket-error-bd:rgba(255, 139, 123, 0.28);--bucket-error-dot:#FF8B7B;--bucket-warn-bg:rgba(255, 211, 122, 0.10);--bucket-warn-fg:#FFD37A;--bucket-warn-bd:rgba(255, 211, 122, 0.28);--bucket-warn-dot:#FFD37A;--bucket-info-bg:rgba(152, 200, 168, 0.10);--bucket-info-fg:#98C8A8;--bucket-info-bd:rgba(152, 200, 168, 0.28);--bucket-info-dot:#98C8A8;--card-shadow:0 1px 2px rgba(0, 0, 0, 0.35),0 6px 18px rgba(0, 0, 0, 0.30);--card-shadow-hover:0 2px 4px rgba(0, 0, 0, 0.45),0 12px 28px rgba(0, 0, 0, 0.40)}.errors-page .errors-header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:4px 0 8px}.errors-page .errors-actions{display:flex;gap:6px}.errors-page .kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin:14px 0 18px}.errors-page .kpi-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 20px;display:flex;flex-direction:column;gap:4px;box-shadow:var(--card-shadow);transition:box-shadow 160ms ease,transform 160ms ease}.errors-page .kpi-card:hover{box-shadow:var(--card-shadow-hover)}.errors-page .kpi-label{font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.errors-page .kpi-value{font-family:var(--font-serif);font-size:2.6rem;font-weight:400;line-height:1;margin-top:2px;color:var(--color)}.errors-page .kpi-card.kpi-error .kpi-value{color:var(--bucket-error-fg)}.errors-page .kpi-card.kpi-warn .kpi-value{color:var(--bucket-warn-fg)}.errors-page .kpi-card.kpi-info .kpi-value{color:var(--bucket-info-fg)}.errors-page .kpi-sub{font-size:.78rem;color:var(--ink-muted);font-style:italic}.errors-page .kpi-sub.up{color:var(--bucket-error-fg);font-style:normal}.errors-page .kpi-sub.down{color:var(--bucket-info-fg);font-style:normal}.errors-page .dropped-warn{color:var(--bucket-error-fg);font-weight:600;font-style:normal}.errors-page .volume-chart{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;margin-bottom:18px;box-shadow:var(--card-shadow)}.errors-page .volume-head{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:.7rem;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}.errors-page .volume-legend{display:flex;gap:14px;align-items:center;text-transform:none;letter-spacing:0;font-family:var(--font-sans)}.errors-page .volume-legend .dot{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:5px;vertical-align:middle}.errors-page .dot.dot-error{background:var(--bucket-error-dot)}.errors-page .dot.dot-warn{background:var(--bucket-warn-dot)}.errors-page .dot.dot-info{background:var(--bucket-info-dot)}.errors-page .volume-bars{display:flex;align-items:flex-end;gap:4px;height:80px}.errors-page .volume-bar{display:flex;flex-direction:column-reverse;flex:1 1 0;min-width:6px;height:100%}.errors-page .volume-bar .seg{display:block;width:100%}.errors-page .seg.seg-error{background:var(--bucket-error-dot)}.errors-page .seg.seg-warn{background:var(--bucket-warn-dot)}.errors-page .seg.seg-info{background:var(--bucket-info-dot);opacity:.7}.errors-page .errors-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}.errors-page .seg-tabs{display:inline-flex;border:1px solid var(--border-color);border-radius:8px;overflow:hidden;background:var(--paper-card)}.errors-page .seg-tabs button{background:0 0;border:none;padding:6px 14px;font-size:.85rem;cursor:pointer;color:var(--color);font-family:var(--font-sans)}.errors-page .seg-tabs button+button{border-left:1px solid var(--border-color)}.errors-page .seg-tabs button.active{background:var(--primary-bg);color:var(--primary-color)}.errors-page .errors-toolbar .search-wrap{position:relative;flex:1 1 320px;min-width:220px}.errors-page .errors-toolbar .search-wrap input{width:100%;padding:6px 10px 6px 32px;border:1px solid var(--border-color);border-radius:8px;font-family:var(--font-mono);font-size:.82rem;background:var(--input-bg);color:var(--input-color)}.errors-page .errors-toolbar .search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--ink-muted);font-size:.85rem}.errors-page .errors-toolbar .filter-count{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:.72rem;color:var(--ink-muted)}.errors-page .errors-toolbar .select-wrap{display:inline-flex;align-items:center;gap:6px}.errors-page .errors-toolbar .select-wrap label{font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);margin:0}.errors-page .errors-toolbar .select-wrap select{background:var(--input-bg);color:var(--input-color);border:1px solid var(--border-color);border-radius:8px}.errors-page .errors-toolbar .autoref{display:inline-flex;gap:6px;align-items:center;font-size:.82rem;color:var(--color);cursor:pointer;margin:0}.errors-page .btn-icon{width:32px;padding:4px 0}.errors-page .errors-list{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;box-shadow:var(--card-shadow)}.errors-page .errors-list-head,.errors-page .errors-row-main{display:grid;grid-template-columns:110px 100px 100px 1fr 60px 70px;gap:16px;padding:18px 20px;align-items:start}.errors-page .errors-list-head{padding:12px 20px;align-items:center}.errors-page .errors-list-head{font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);border-bottom:1px solid var(--border-color);background:var(--paper-bg-alt)}.errors-page .errors-row{border-bottom:1px solid var(--border-soft)}.errors-page .errors-row:last-child{border-bottom:none}.errors-page .errors-row.open{background:var(--paper-bg-alt)}.errors-page .errors-row-main{cursor:pointer}.errors-page .errors-row-main:hover{background:var(--hover-bg-color)}.errors-page .col-when .when-rel{font-size:.95rem;color:var(--color);font-variant-numeric:tabular-nums}.errors-page .col-when .when-abs{font-family:var(--font-mono);font-size:.78rem;color:var(--ink-muted);font-variant-numeric:tabular-nums;margin-top:2px}.errors-page .col-sev{display:flex;align-items:center;gap:8px}.errors-page .sev-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.errors-page .sev-dot.sev-error{background:var(--bucket-error-dot)}.errors-page .sev-dot.sev-warn{background:var(--bucket-warn-dot)}.errors-page .sev-dot.sev-info{background:var(--bucket-info-dot)}.errors-page .sev-label{font-family:var(--font-mono);font-size:.74rem;letter-spacing:.08em;color:var(--color)}.errors-page .pill-module{display:inline-block;font-family:var(--font-mono);font-size:.78rem;background:var(--paper-bg-alt);color:var(--ink-soft);border:1px solid var(--border-color);padding:3px 10px;border-radius:6px}.errors-page .col-msg .msg-code{font-family:var(--font-mono);font-size:1rem;color:var(--color);font-weight:600}.errors-page .col-msg .msg-context{color:var(--ink-muted);font-style:italic;font-size:.88rem;margin-left:10px}.errors-page .col-msg .msg-detail{color:var(--ink-soft);font-family:var(--font-mono);font-size:.82rem;margin-left:10px}.errors-page .col-msg .msg-url{color:var(--ink-muted);font-family:var(--font-mono);font-size:.8rem;margin-top:6px;word-break:break-all}.errors-page .count-pill{display:inline-block;padding:2px 8px;border-radius:4px;background:var(--primary-bg);color:var(--primary-color);font-family:var(--font-mono);font-size:.78rem;font-variant-numeric:tabular-nums}.errors-page .count-pill.count-pill-muted{background:0 0;color:var(--ink-muted);border:1px solid var(--border-color)}.errors-page .status-pill{display:inline-block;padding:2px 10px;border-radius:4px;font-family:var(--font-mono);font-size:.8rem;font-variant-numeric:tabular-nums;border:1px solid transparent}.errors-page .status-pill.status-error{background:var(--bucket-error-bg);color:var(--bucket-error-fg);border-color:var(--bucket-error-bd)}.errors-page .status-pill.status-warn{background:var(--bucket-warn-bg);color:var(--bucket-warn-fg);border-color:var(--bucket-warn-bd)}.errors-page .status-pill.status-info{background:var(--bucket-info-bg);color:var(--bucket-info-fg);border-color:var(--bucket-info-bd)}.errors-page .errors-row-detail{padding:0 16px 16px}.errors-page .detail-tabs{display:flex;gap:4px;border-bottom:1px solid var(--border-color);margin-bottom:12px}.errors-page .detail-tabs button{background:0 0;border:0;border-bottom:2px solid transparent;padding:8px 12px;font-size:.85rem;cursor:pointer;color:var(--ink-muted);font-family:var(--font-sans)}.errors-page .detail-tabs button.active{color:var(--color);border-bottom-color:var(--color)}.errors-page .detail-body{display:grid;grid-template-columns:1fr 260px;gap:20px}.errors-page .detail-main pre{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;font-family:var(--font-mono);font-size:.85rem;line-height:1.7;color:var(--color);max-height:26em;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;margin:0;box-shadow:var(--card-shadow)}.errors-page .related-list{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;max-height:22em;overflow:auto;box-shadow:var(--card-shadow)}.errors-page .stack-pre{line-height:1.45;white-space:pre;overflow-x:auto}.errors-page .related-row{display:flex;gap:10px;padding:6px 12px;align-items:center;border-bottom:1px solid var(--border-soft);font-size:.8rem}.errors-page .related-row:last-child{border-bottom:none}.errors-page .detail-actions{display:flex;gap:6px;align-items:center;margin-top:10px}.errors-page .copy-hint{font-size:.78rem;color:var(--bucket-info-fg)}.errors-page .detail-aside{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px;box-shadow:var(--card-shadow)}.errors-page .aside-block+.aside-block{margin-top:16px}.errors-page .aside-label{font-family:var(--font-mono);font-size:.7rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);margin-bottom:4px}.errors-page .aside-value{font-family:var(--font-serif);font-size:1.05rem;color:var(--color);word-break:break-word;line-height:1.3}.errors-page .aside-value.mono{font-family:var(--font-mono);font-size:.82rem}.errors-page .aside-sub{color:var(--ink-muted);font-style:italic;font-size:.8rem;font-family:var(--font-sans)}.errors-page .errors-pager{display:flex;justify-content:space-between;align-items:center;padding:10px 0;color:var(--ink-muted);font-size:.85rem}@media (max-width:720px){.errors-page .errors-list-head,.errors-page .errors-row-main{grid-template-columns:80px 80px 1fr 60px}.errors-page .errors-list-head .col-mod,.errors-page .errors-list-head .col-sev,.errors-page .errors-row-main .col-mod,.errors-page .errors-row-main .col-sev{display:none}.errors-page .detail-body{grid-template-columns:1fr}}.overview-page .ov-header{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:2px}.overview-page .ov-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-left:6px;vertical-align:middle}.overview-page .ov-dot-ok{background:#2f7a44}.overview-page .ov-dot-warn{background:#c48a2e}.overview-page .ov-dot-idle{background:var(--ink-muted)}.dark-mode .overview-page .ov-dot-ok{background:#7dc894}.dark-mode .overview-page .ov-dot-warn{background:#e8b970}.overview-page .ov-kpi-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;margin:16px 0 22px}.overview-page .ov-kpi-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;display:flex;flex-direction:column;gap:4px;box-shadow:var(--card-shadow);transition:box-shadow 160ms ease}.overview-page .ov-kpi-card:hover{box-shadow:var(--card-shadow-hover)}.overview-page .ov-kpi-label{font-family:var(--font-mono);font-size:.68rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.overview-page .ov-kpi-value{font-family:var(--font-serif);font-size:2.6rem;font-weight:400;line-height:1;margin-top:2px;color:var(--color)}.overview-page .ov-kpi-value.ov-val-warn{color:#b42318}.dark-mode .overview-page .ov-kpi-value.ov-val-warn{color:#f08a82}.overview-page .ov-kpi-sub{font-size:.76rem;color:var(--ink-muted);margin-top:2px}.overview-page .ov-daily-row{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:-4px 0 22px}.overview-page .ov-daily-card{background:var(--primary-bg);border:1px solid var(--border-color);border-radius:12px;padding:16px 20px;box-shadow:var(--card-shadow)}.overview-page .ov-daily-label{font-family:var(--font-mono);font-size:.68rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.overview-page .ov-daily-value{font-family:var(--font-serif);font-size:2.35rem;font-weight:400;line-height:1;color:var(--accent,#3b4ad6);margin-top:6px}.overview-page .ov-daily-sub{font-size:.76rem;color:var(--ink-muted);margin-top:5px}.dark-mode .overview-page .ov-daily-card{background:var(--paper-card)}.dark-mode .overview-page .ov-daily-value{color:#a7b2ff}.overview-page .ov-chart-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-bottom:18px}.overview-page .ov-chart-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px 14px;box-shadow:var(--card-shadow)}.overview-page .ov-chart-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}.overview-page .ov-chart-title{font-family:var(--font-serif);font-size:1.05rem}.overview-page .ov-chart-legend{font-family:var(--font-mono);font-size:.7rem;color:var(--ink-muted);display:flex;align-items:center;gap:5px}.overview-page .ov-dot-legend{display:inline-block;width:8px;height:8px;border-radius:50%}.overview-page .ov-dot-accent{background:var(--accent,#3b4ad6)}.overview-page .ov-dot-ok-fill{background:#2f7a44}.overview-page .ov-dot-user-fill{background:#8b5e2e}.overview-page .ov-dot-new-user-fill{background:#c48a2e}.dark-mode .overview-page .ov-dot-ok-fill{background:#7dc894}.dark-mode .overview-page .ov-dot-user-fill{background:#d0a15f}.dark-mode .overview-page .ov-dot-new-user-fill{background:#e8b970}.overview-page .ov-spark-bars{display:flex;align-items:flex-end;gap:3px;height:140px}.overview-page .ov-spark-col{flex:1;display:flex;align-items:flex-end;height:100%;position:relative}.overview-page .ov-spark-fill{width:100%;background:var(--accent,#3b4ad6);border-radius:2px 2px 0 0;transition:height .3s ease;opacity:.55}.overview-page .ov-spark-fill-alt{background:#2f7a44}.overview-page .ov-spark-fill-user{background:#8b5e2e}.overview-page .ov-spark-fill-new-user{background:#c48a2e}.dark-mode .overview-page .ov-spark-fill-alt{background:#7dc894}.dark-mode .overview-page .ov-spark-fill-user{background:#d0a15f}.dark-mode .overview-page .ov-spark-fill-new-user{background:#e8b970}.overview-page .ov-spark-col:hover .ov-spark-fill{opacity:1}.overview-page .ov-spark-col.has-tip::after{content:attr(data-tip);position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%) translateY(4px);z-index:10;padding:6px 8px;border-radius:6px;background:var(--color);color:var(--canvas-bg-color);font-family:var(--font-mono);font-size:.68rem;line-height:1;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 120ms ease,transform 120ms ease}.overview-page .ov-spark-col.has-tip::before{content:"";position:absolute;left:50%;bottom:calc(100% + 4px);transform:translateX(-50%) translateY(4px);z-index:10;border:5px solid transparent;border-top-color:var(--color);opacity:0;pointer-events:none;transition:opacity 120ms ease,transform 120ms ease}.overview-page .ov-spark-col.has-tip:hover::after,.overview-page .ov-spark-col.has-tip:hover::before{opacity:1;transform:translateX(-50%) translateY(0)}.overview-page .ov-spark-x{display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:.66rem;color:var(--ink-muted);margin-top:6px}.overview-page .ov-triple-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:18px}.overview-page .ov-panel-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 20px;box-shadow:var(--card-shadow)}.overview-page .ov-panel-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}.overview-page .ov-panel-title{font-family:var(--font-serif);font-size:1.05rem}.overview-page .ov-panel-meta{font-family:var(--font-mono);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted)}.overview-page .ov-panel-foot{margin-top:12px;padding-top:10px;border-top:1px solid var(--border-color);font-size:.82rem}.overview-page .ov-panel-foot a{color:var(--link-color);text-decoration:none}.overview-page .ov-panel-foot a:hover{text-decoration:underline}.overview-page .ov-performance{min-width:0;margin:22px 0}.overview-page .ov-performance-head{align-items:center;flex-wrap:wrap;gap:12px 20px}.overview-page .ov-performance-head h2{margin:0 0 4px}.overview-page .ov-performance-note{margin:0 0 12px;font-size:12px;line-height:1.5;color:var(--ink-muted)}.overview-page .ov-performance-head .ov-performance-note{margin:0}.overview-page .ov-performance-window{display:flex;align-items:center;gap:8px;flex-shrink:0}.overview-page .ov-performance-window label{margin:0;font-size:12px;color:var(--ink-muted)}.overview-page .ov-performance-window select{width:auto}.overview-page .ov-performance-tabs{margin:0 0 16px;gap:4px}.overview-page .ov-performance-tabs .paper-tab{white-space:nowrap}.overview-page .ov-metrics-scroll{max-width:100%;overflow-x:auto;border:1px solid var(--border-color);border-radius:var(--radius-control,8px)}.overview-page .ov-metrics-table{width:100%;border-collapse:collapse;color:var(--color);font-size:13px;line-height:1.5;font-variant-numeric:tabular-nums}.overview-page .ov-metrics-table td,.overview-page .ov-metrics-table th{padding:10px 12px;border-bottom:1px solid var(--border-color);vertical-align:middle;white-space:nowrap}.overview-page .ov-metrics-table thead th{background:var(--paper-bg-alt);color:var(--ink-muted);font-size:12px;font-weight:500}.overview-page .ov-metrics-table .num{text-align:right}.overview-page .ov-metrics-table tr:last-child>*{border-bottom:0}.overview-page .ov-metrics-table thead tr:last-child>*{border-bottom:1px solid var(--border-color)}.overview-page .ov-metrics-table tbody tr:hover{background:var(--hover-bg-color)}.overview-page .ov-metrics-table .ov-metric-name{min-width:190px;max-width:280px;text-align:left;white-space:normal;overflow-wrap:anywhere;font-weight:500}.overview-page .ov-metrics-table small{display:block;margin-top:3px;font-size:11px;font-weight:400;color:var(--ink-muted)}.overview-page .ov-metrics-table small .ui-icon{width:12px;height:12px}.overview-page .ov-metric-warning-row{background:rgba(196,138,46,.07)}.overview-page .ov-metric-warn,.overview-page .ov-metrics-table small.ov-metric-warn{color:#946519}.dark-mode .overview-page .ov-metric-warn,.dark-mode .overview-page .ov-metrics-table small.ov-metric-warn{color:#e8b970}.overview-page .ov-performance-message{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 12px;margin:0 0 16px;border:1px solid var(--border-color);border-radius:var(--radius-control,8px);font-size:13px;line-height:1.5}.overview-page .ov-performance-message span{flex:1;min-width:160px}.overview-page .ov-performance-warning{background:rgba(196,138,46,.07)}.overview-page .ov-performance-empty{margin:0;padding:24px 12px;text-align:center;font-size:13px;color:var(--ink-muted)}.overview-page .ov-performance-help{margin-top:16px;font-size:12px;line-height:1.6;color:var(--ink-muted)}.overview-page .ov-performance-help summary{width:fit-content;cursor:pointer}.overview-page .ov-performance-help p{margin:8px 0 0;max-width:80ch}@media (max-width:575px){.overview-page .ov-performance{padding:16px 12px}.overview-page .ov-performance-window{width:100%}.overview-page .ov-performance-window select{flex:1;min-width:0}.overview-page .ov-performance-tabs .paper-tab{flex:1;padding:10px 8px}}.overview-page .ov-stacked-bar{display:flex;height:18px;border-radius:6px;overflow:hidden;margin-bottom:14px}.overview-page .ov-bar-seg{min-width:2px;transition:width .4s ease}.overview-page .ov-bar-ready{background:#2f7a44}.overview-page .ov-bar-preparing{background:#c48a2e}.overview-page .ov-bar-error{background:#b42318}.overview-page .ov-bar-expired{background:#8a857c}.overview-page .ov-bar-removed{background:#b5afa2}.dark-mode .overview-page .ov-bar-ready{background:#7dc894}.dark-mode .overview-page .ov-bar-preparing{background:#e8b970}.dark-mode .overview-page .ov-bar-error{background:#f08a82}.dark-mode .overview-page .ov-bar-expired{background:#6b665e}.dark-mode .overview-page .ov-bar-removed{background:#4a453e}.overview-page .ov-bar-legend{display:flex;flex-direction:column;gap:6px}.overview-page .ov-legend-item{display:flex;align-items:center;gap:8px;font-size:.82rem;text-decoration:none;color:inherit}.overview-page .ov-legend-item:hover{text-decoration:none;color:inherit}.overview-page .ov-swatch{display:inline-block;width:12px;height:12px;border-radius:3px;flex-shrink:0}.overview-page .ov-legend-n{margin-left:auto;font-family:var(--font-mono);font-size:.8rem;font-weight:600}.overview-page .ov-error-bars{display:flex;flex-direction:column;gap:12px}.overview-page .ov-ebar-row{display:flex;align-items:center;gap:10px}.overview-page .ov-ebar-label{width:30px;flex-shrink:0;font-family:var(--font-mono);font-size:.72rem;text-transform:uppercase;color:var(--ink-muted)}.overview-page .ov-ebar-track{flex:1;height:10px;background:var(--hover-bg-color);border-radius:5px;overflow:hidden}.overview-page .ov-ebar-fill{height:100%;border-radius:5px;transition:width .4s ease}.overview-page .ov-ebar-error{background:#b42318}.overview-page .ov-ebar-warn{background:#c48a2e}.overview-page .ov-ebar-info{background:var(--accent,#3b4ad6)}.dark-mode .overview-page .ov-ebar-error{background:#f08a82}.dark-mode .overview-page .ov-ebar-warn{background:#e8b970}.overview-page .ov-ebar-n{width:36px;text-align:right;font-family:var(--font-mono);font-size:.82rem;font-weight:600;flex-shrink:0}.overview-page .ov-routes-table{display:flex;flex-direction:column}.overview-page .ov-route-row{display:grid;grid-template-columns:1fr 60px 60px 60px;padding:8px 0;border-bottom:1px solid var(--border-soft,var(--border-color));text-decoration:none;color:inherit;font-size:.85rem;align-items:center}.overview-page .ov-route-row:last-child{border-bottom:none}.overview-page .ov-route-row:hover{background:var(--hover-bg-color);text-decoration:none;color:inherit}.overview-page .ov-route-head{font-family:var(--font-mono);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted);border-bottom:1px solid var(--border-color)}.overview-page .ov-route-head:hover{background:0 0}.overview-page .ov-route-name{font-family:var(--font-mono);font-size:.84rem}.overview-page .ov-route-n{text-align:right;font-family:var(--font-mono);font-size:.84rem}.overview-page .ov-route-lat{color:var(--ink-muted)}.overview-page .ov-n-bad{color:#b42318;font-weight:600}.dark-mode .overview-page .ov-n-bad{color:#f08a82}.overview-page .ov-services-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;box-shadow:var(--card-shadow);margin-bottom:18px}.overview-page .ov-services-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:16px}.overview-page .ov-services-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px}.overview-page .ov-svc{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid var(--border-soft,var(--border-color));border-radius:10px}.overview-page .ov-svc-dot{width:10px;height:10px;border-radius:50%;margin-top:3px;flex-shrink:0}.overview-page .ov-svc-info{display:flex;flex-direction:column;gap:1px;flex:1}.overview-page .ov-svc-name{font-family:var(--font-mono);font-size:.88rem;font-weight:600}.overview-page .ov-svc-meta{font-size:.76rem;color:var(--ink-muted)}.overview-page .ov-svc-detail{font-size:.74rem;color:var(--ink-muted);white-space:nowrap}@media (max-width:900px){.overview-page .ov-kpi-row{grid-template-columns:repeat(3,1fr)}.overview-page .ov-daily-row{grid-template-columns:repeat(3,1fr)}.overview-page .ov-triple-row{grid-template-columns:1fr}}@media (max-width:720px){.overview-page .ov-kpi-row{grid-template-columns:repeat(2,1fr)}.overview-page .ov-daily-row{grid-template-columns:1fr}.overview-page .ov-chart-row{grid-template-columns:1fr}.overview-page .ov-header{flex-direction:column;gap:8px}}@media (max-width:480px){.overview-page .ov-kpi-row{grid-template-columns:1fr 1fr}.overview-page .ov-services-grid{grid-template-columns:1fr 1fr}}.dashboard-heading-actions{display:flex;flex-direction:column;align-items:flex-end;gap:8px;padding-bottom:12px}.dashboard-heading-actions .btn{display:inline-flex;align-items:center;justify-content:center;gap:8px}.dashboard-connections-link{display:inline-flex;align-items:center;gap:7px;min-height:32px;color:var(--ink-muted);font-size:12px;line-height:1.5}.dashboard-connections-link:hover{color:var(--accent);text-decoration:none}.dashboard-connections-link:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:2px}@media (max-width:767px){.dashboard-heading-actions{align-items:flex-start;padding-bottom:0;gap:4px}.dashboard-connections-link{min-height:44px}}.paper-settings-connection-link{display:flex;align-items:center;gap:12px;padding:18px 0;color:var(--color);font-size:14px}.paper-settings-connection-link>i{color:var(--ink-muted);font-size:18px}.paper-settings-connection-link>span:first-of-type{flex:1}.paper-settings-connection-link small{display:block;margin-top:4px;color:var(--ink-muted);font-size:12px}.paper-settings-connection-link:hover{color:var(--accent);text-decoration:none}.paper-settings-connection-link:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.connections-heading{margin-bottom:36px}.connections-heading .paper-page-title{font-size:clamp(38px,4.5vw,56px);margin-bottom:12px}.connections-heading .paper-page-lede{font-size:15px}.connections-grid{display:grid;grid-template-columns:1.15fr 1fr;gap:56px;align-items:start;border-top:1px solid var(--border-color);padding-top:28px}.connections-grid>section{min-width:0}.connection-title,.connection-top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.connection-top{margin-bottom:24px;min-height:28px}.connection-top>i{color:var(--ink-muted);font-size:24px}.connection-top>.fa-key{font-size:18px}.connection-title{margin-bottom:10px}.connections-page h2{font-family:inherit;font-size:21px;font-weight:600;letter-spacing:-.5px;margin:0}.connections-page p{font-size:14px;line-height:1.65;color:var(--ink-muted)}.connection-badge{background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:600;border-radius:5px;padding:4px 8px;white-space:nowrap}.connection-badge-muted{background:var(--paper-bg-alt);color:var(--ink-muted)}.connection-status{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-muted)}.connection-status::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}.connection-status.is-connected{color:var(--status-ready)}.connection-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin:22px 0 16px}.connections-page .btn{font-size:13px;padding:9px 13px;white-space:normal}.connection-revoke p,.connections-page .connection-help{font-size:12px}.connection-link{display:inline-flex;align-items:center;gap:8px;border:0;padding:0;background:0 0;color:var(--accent);font-size:13px;line-height:1.6;font-weight:500;text-align:left;cursor:pointer}.connection-link:hover{color:var(--link-hover-color);text-decoration:underline}.connection-link:disabled{color:var(--ink-muted);opacity:.65;cursor:not-allowed;text-decoration:none}.connection-link:focus-visible{outline:2px solid var(--accent);outline-offset:5px}.connection-installations{list-style:none;padding:0;margin:18px 0;border-top:1px solid var(--border-soft)}.connection-installations li{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 0;font-size:12px;border-bottom:1px solid var(--border-soft)}.connection-revoke{border-top:1px solid var(--border-soft);padding-top:18px;margin-top:24px}.connection-danger{color:var(--status-error)}.connection-resources{margin-top:36px;padding-top:28px;border-top:1px solid var(--border-color)}.connection-count{color:var(--ink-muted);font-size:12px}.connection-empty{display:flex;align-items:center;gap:18px;padding:20px 0;margin:22px 0}.connection-empty>i{font-size:22px;color:var(--ink-muted)}.connection-empty h3{font-size:14px;font-weight:600;margin:0 0 5px}.connection-empty p{font-size:12px;margin:0}.connection-empty>a{margin-left:auto;flex-shrink:0}.connection-resource{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:20px 0;border-bottom:1px solid var(--border-color)}.connection-resource-name{flex:1;min-width:180px;overflow-wrap:anywhere;font-size:14px}.connection-resource-name small{display:block;color:var(--ink-muted);font-size:11px;margin-top:5px}.connection-resource-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.connection-resource-actions .connection-help{flex-basis:100%}.connections-page .connection-footnote{display:flex;align-items:baseline;gap:10px;margin-top:22px;font-size:12px}@media (max-width:767px){.connections-grid{grid-template-columns:1fr;gap:28px}.connections-grid>section+section{border-top:1px solid var(--border-color);padding-top:28px}.connection-empty{flex-wrap:wrap}.connection-empty>div{flex:1;min-width:180px}.connection-empty>a{margin-left:40px}.connection-resource-actions{flex-basis:100%}}.paper-table-empty-actions .btn{display:inline-flex;align-items:center;justify-content:center;gap:6px}.paper-table-empty-actions .btn>i{font-size:1em;line-height:1;color:inherit;margin-right:0!important}.auth-back{display:inline-flex;align-items:center;gap:10px;font-size:13px;color:var(--ink-muted);margin-bottom:48px}.auth-layout{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:start}.auth-intro,.auth-options{padding-top:28px}.auth-title-mobile{display:none}.auth-intro .paper-page-title{font-size:clamp(48px,5vw,76px);line-height:1.06;margin:18px 0 24px}.auth-intro .paper-page-lede{max-width:330px}.auth-options{display:grid;gap:28px}.auth-options h2{font-family:inherit;font-size:21px;font-weight:600;margin:0 0 10px}.auth-options p{font-size:14px;line-height:1.65;color:var(--ink-muted)}.auth-options .auth-existing{font-size:12px;border-left:2px solid var(--accent);padding-left:14px;margin:0}.auth-permissions{padding:6px 0 12px;list-style:none;font-size:13px;line-height:1.6}.auth-permissions li{display:flex;gap:10px;margin:12px 0}.auth-permissions i{color:var(--status-ready);margin-top:4px}.auth-continue{display:flex!important;align-items:center;justify-content:space-between;min-height:48px;padding:12px 16px!important}.auth-options small{display:block;font-size:12px;text-align:center;color:var(--ink-muted);margin-top:12px}.auth-legacy{border-top:1px solid var(--border-color);padding-top:24px}.auth-legacy h2{font-size:16px;margin:0}.auth-legacy p{font-size:13px}.source-url-label-row{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;margin-bottom:8px}.source-url-label-row>.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}.repo-access{margin:0}.repo-access-details{margin:12px 0 24px}.source-url-label-row .repo-access-switch{padding:2px}.source-url-label-row .repo-access-switch button{padding:6px 10px;font-size:12px;line-height:20px}.repo-access-main{display:flex;align-items:center;gap:10px;padding:0 0 8px;flex-wrap:wrap}.repo-access-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.repo-access-switch{display:inline-flex;gap:3px;padding:3px;border:1px solid var(--border-color);border-radius:7px;flex-wrap:wrap}.repo-access-switch button{border:0;border-radius:4px;background:0 0;color:var(--ink-muted);padding:7px 10px;font:inherit;font-size:12px;cursor:pointer}.repo-access-switch button[aria-pressed=true]{background:var(--accent-soft);color:var(--accent);opacity:1;cursor:default;font-weight:600}.repo-access-switch button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.repo-access-details .btn{font-size:12px;padding:8px 12px}.repo-access-note{display:flex;gap:6px 16px;flex-wrap:wrap;padding:0;color:var(--ink-muted);font-size:11px;line-height:1.6}@media (max-width:767px){.auth-page{max-width:480px}.auth-back,.auth-intro .paper-eyebrow,.auth-title-desktop{display:none}.auth-title-mobile{display:inline}.auth-layout{grid-template-columns:1fr;gap:24px}.auth-intro,.auth-options{padding-top:0}.auth-intro .paper-page-title{font-size:40px;line-height:1.15;margin:6px 0 12px}.auth-intro .paper-page-lede{max-width:none;font-size:14px;margin:0}.auth-options{gap:24px}.auth-options .connection-top{margin-bottom:16px}.auth-options h2{font-size:18px}.auth-options p{font-size:13px;margin-bottom:12px}.auth-permissions{padding:0;margin:14px 0 20px;font-size:12px}.auth-permissions li{margin:8px 0}.auth-continue{font-size:14px}.auth-legacy{padding-top:20px}.auth-legacy h2{font-size:15px}.auth-legacy .connection-link{min-height:44px}.repo-access-controls{width:100%;gap:12px}}.text-muted{color:var(--ink-muted)!important}.anonymize-review-note,.draft-status{font-size:13px;color:var(--ink-muted)}.anonymize-review-note{padding:12px 0;margin:0}.anonymize-preview-status{padding:20px 32px;color:var(--ink-muted)}.anonymize-preview-status p{margin:0 0 12px}@media (max-width:575px){.dashboard-page .paper-page-lede{margin-bottom:8px}.dashboard-page .dashboard-heading-actions{gap:4px}.dashboard-page .dashboard-connections-link{min-height:32px}.dashboard-page .dashboard-filter-row{margin-top:12px}.anonymize-preview-status{padding:16px}}.paper-page .paper-page-title{font-size:32px}.paper-page.admin-page .paper-page-title{font-size:28px}.paper-page .paper-page-lede{font-size:14px;line-height:1.5}.paper-settings{padding-top:32px}.paper-settings-body{grid-template-columns:180px minmax(0,1fr);gap:40px;margin-top:24px;padding-top:24px;border-top:0}.paper-settings-main .paper-settings-section{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);padding:20px;margin-bottom:16px}.anonymize-page .paper-section-eyebrow,.paper-settings .paper-section-eyebrow{font-family:var(--font-sans);font-size:16px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:-.01em;margin-bottom:12px}.paper-settings .paper-settings-toc-head{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.paper-settings-section .paper-section-copy{margin-top:0}.paper-settings .quota-row{gap:24px}.paper-settings .quota-header{flex-direction:column;align-items:flex-start;gap:4px}.paper-settings .quota-value{white-space:nowrap}.paper-settings .paper-settings-footer{padding:0 0 20px}.paper-settings .form-control{font-size:14px;border-color:var(--border-color);border-radius:var(--radius-control)}.paper-settings .form-check{padding-top:8px;padding-bottom:8px}.anonymize-page .btn-ink,.paper-page .btn-ink{background:var(--accent);color:#fff;border-color:var(--accent)}.dark-mode .anonymize-page .btn-ink,.dark-mode .paper-page .btn-ink{color:#1a1815}.anonymize-page .btn-ink:hover,.paper-page .btn-ink:hover{background:var(--accent);border-color:var(--accent);color:#fff;filter:brightness(.94)}.dark-mode .anonymize-page .btn-ink:hover,.dark-mode .paper-page .btn-ink:hover{color:#1a1815}.anonymize-page .btn-ink:active,.paper-page .btn-ink:active{filter:brightness(.9)}input[type=date],select{color-scheme:light}.dark-mode input[type=date],.dark-mode select{color-scheme:dark}.draft-status{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;padding:10px 0}.draft-discard{padding:0;border:0;color:var(--accent);font:inherit;font-size:13px;text-decoration:underline;background:0 0;cursor:pointer}.conference-feedback{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px;margin-top:16px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);width:100%}.paper-table{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);margin-top:12px}.paper-table .paper-table-head{font-family:var(--font-sans);text-transform:none;letter-spacing:0;font-size:12px;background:var(--paper-bg-alt);border-radius:var(--radius-surface) var(--radius-surface) 0 0}.paper-table .paper-table-head,.paper-table .paper-table-row{padding-left:16px;padding-right:16px}.dashboard-page{--dashboard-space-xs:4px;--dashboard-space-sm:8px;--dashboard-space-md:12px;--dashboard-space-lg:16px;--dashboard-space-xl:24px;padding-top:var(--dashboard-space-xl)}.project-icon-definitions{position:absolute;width:0;height:0;overflow:hidden}.project-type-svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.project-empty-icon{width:28px;height:28px;color:var(--ink-faint)}.project-type-icon{display:inline-flex;color:var(--ink-muted);flex-shrink:0}.dashboard-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--dashboard-space-lg)}.dashboard-heading .paper-page-title{font-size:28px;margin-bottom:var(--dashboard-space-xs)}.dashboard-heading .paper-page-lede{display:none}.dashboard-heading .btn{min-height:40px;display:inline-flex;gap:var(--dashboard-space-sm);align-items:center}.dashboard-new-mobile{display:none}.dashboard-quota{position:relative;margin:0;padding:0;border:0;background:0 0}.dashboard-quota>summary{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:5px 10px;border:1px solid var(--border-color);border-radius:6px;background:var(--paper-card);color:var(--color);font-family:var(--font-sans);font-size:12px;cursor:pointer;list-style:none;white-space:nowrap}.dashboard-quota>summary::-webkit-details-marker{display:none}.dashboard-quota>summary:hover,.dashboard-quota[open]>summary{background:var(--paper-bg-alt);border-color:var(--ink-faint)}.quota-summary-label{font-weight:600}.quota-summary-value{color:var(--ink-muted);font-variant-numeric:tabular-nums}.quota-summary-ring{width:18px;height:18px;flex-shrink:0;fill:none;stroke-width:2.5}.quota-ring-track{stroke:var(--border-color)}.quota-ring-fill{stroke:var(--accent);transform:rotate(-90deg);transform-origin:50% 50%}.quota-summary-ring.quota-warn .quota-ring-fill{stroke:var(--status-progress)}.quota-summary-ring.quota-danger .quota-ring-fill{stroke:var(--status-error)}.quota-ring-unlimited{stroke:var(--accent);stroke-width:1.7}.quota-summary-chevron{width:14px;height:14px;margin-left:2px;color:var(--ink-muted);transition:transform .15s ease}.dashboard-quota[open] .quota-summary-chevron{transform:rotate(180deg)}.dashboard-quota .quota-panel{position:absolute;z-index:25;top:calc(100% + 8px);left:0;width:344px;max-width:calc(100vw - 36px);padding:16px;border:1px solid var(--border-color);border-radius:12px;background:var(--paper-card);box-shadow:0 12px 36px rgba(26,24,21,.16)}.quota-panel-heading{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.quota-panel-heading h2{font:600 15px var(--font-sans);margin:0}.quota-panel-heading .project-close{margin:-8px -8px -8px 0}.paper-page .quota-usage{display:grid;grid-template-columns:1fr;gap:16px;margin:0;padding:0}.quota-usage .quota-item+.quota-item{padding-top:16px;border-top:1px solid var(--border-soft,var(--border-color))}.quota-usage .quota-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}.quota-usage .quota-label{display:inline-flex;align-items:center;gap:8px;font:500 12px var(--font-sans);letter-spacing:0;text-transform:none;color:var(--ink-muted)}.quota-usage .quota-label .project-type-svg{width:16px;height:16px}.quota-usage .quota-value{display:flex;align-items:baseline;gap:4px;font:12px var(--font-sans);font-variant-numeric:tabular-nums;color:var(--ink-muted);margin-bottom:8px}.quota-usage .quota-value strong{font-size:20px;line-height:1.2;font-weight:600;color:var(--color)}.quota-percent,.quota-usage .quota-unlimited-tag{margin-left:auto;padding:2px 6px;border-radius:4px;background:var(--paper-bg-alt);font:11px var(--font-sans);color:var(--ink-muted);white-space:nowrap}.quota-warn>.quota-header .quota-percent{color:var(--status-progress)}.quota-danger>.quota-header .quota-percent{color:var(--status-error)}.quota-usage .quota-track{height:5px;border:0;background:var(--paper-bg-alt)}.quota-usage .quota-track .quota-fill{min-width:0;background:var(--accent)}.quota-remaining{margin:6px 0 0;color:var(--ink-muted);font-size:11px}.quota-remaining.quota-limit{color:var(--status-error)}.quota-loading{font-size:12px;color:var(--ink-muted);margin:12px 0 0}.dashboard-page .dashboard-filter-row{display:flex;align-items:center;gap:var(--dashboard-space-md);padding:0;margin:var(--dashboard-space-lg) 0 0;border:0;background:0 0}.dashboard-page .dashboard-filter-row .search-wrap{flex:1 1 240px;min-width:0}.dashboard-page .dashboard-filter-row .form-control{height:40px;padding:8px 12px;font-size:13px;background:var(--paper-card)}.dashboard-page .dashboard-filter-controls{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--dashboard-space-sm)}.dashboard-extra-filters{display:flex;gap:var(--dashboard-space-sm)}.dashboard-filters-toggle{display:none}.dashboard-page .dashboard-filter-row .btn-group .btn{border:0;background:0 0;border-radius:6px;height:36px;padding:6px 10px}.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary{background:var(--accent-soft);color:var(--accent)}.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary:active,.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary:hover{background:var(--accent-soft)!important;color:var(--accent)}.dashboard-page .dashboard-filter-row .dropdown-toggle{height:36px;padding:6px 10px}.dashboard-type-filter .project-type-svg{display:none}.sort-direction{width:14px;height:14px}.sort-direction.ascending{transform:rotate(180deg)}.dashboard-page .dashboard-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--dashboard-space-sm) var(--dashboard-space-md);margin:var(--dashboard-space-md) 0 0;padding:0}.dashboard-view-tabs{display:flex;align-items:center;gap:var(--dashboard-space-lg)}.dashboard-view-short{display:none}.dashboard-view-tabs button{min-height:36px;border:0;border-bottom:2px solid transparent;border-radius:0;background:0 0;font:inherit;font-size:13px;color:var(--ink-muted);padding:4px 0}.dashboard-view-tabs button[aria-pressed=true]{color:var(--color);border-bottom-color:var(--color);font-weight:600}.attention-count{padding:1px 5px;margin-left:4px;border-radius:4px;background:var(--paper-bg-alt);font-size:11px;font-variant-numeric:tabular-nums}.attention-count.has-attention{color:var(--status-error)}.dashboard-count{margin-left:auto;font-size:12px;color:var(--ink-muted)}.dashboard-active-filters{display:flex;align-items:center;flex-wrap:wrap;gap:var(--dashboard-space-sm);width:100%}.dashboard-page .filter-chip{display:inline-flex;font-family:var(--font-sans);font-size:11px;text-transform:none;letter-spacing:0;border-radius:6px}.dashboard-page .filter-chip .project-type-svg{width:12px;height:12px;margin-left:4px}.dashboard-page .filter-chip-close{min-width:24px;min-height:24px;display:inline-flex;align-items:center;justify-content:center;margin:-2px -4px -2px 2px}.dashboard-page .btn-link-inline{color:var(--ink-muted);font-size:12px}.dashboard-page .paper-table-dashboard{margin-top:var(--dashboard-space-md);border-left:0;border-right:0;border-radius:0}.dashboard-page .paper-table-dashboard .paper-table-head,.dashboard-page .paper-table-dashboard .paper-table-row{grid-template-columns:minmax(280px,3fr) 180px 72px 148px 64px;gap:var(--dashboard-space-lg);padding:8px 12px}.dashboard-page .paper-table-dashboard .paper-table-head{border-radius:0}.dashboard-page .paper-table .cell-anon{gap:var(--dashboard-space-sm)}.dashboard-page .paper-table .anon-text{flex:1}.dashboard-page .paper-table .repo-name{font-family:var(--font-sans);font-size:14px;font-weight:600}.dashboard-page .paper-table .anon-sub{display:flex;align-items:center;gap:var(--dashboard-space-sm);min-width:0}.anonymous-id{font-size:12px;font-family:var(--font-mono);color:var(--ink-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dashboard-page .paper-table .type-badge{font-family:var(--font-sans);letter-spacing:0;font-size:11px;text-transform:none;border-radius:6px}.dashboard-page .paper-table .status-sub{font-size:12px}.dashboard-page .paper-table .cell-conf{font-family:var(--font-sans);letter-spacing:0;text-transform:none}.dashboard-page .conf-tag .project-type-svg{width:14px;height:14px}.dashboard-row-meta{display:contents}.dashboard-page .cell-actions{gap:0;align-items:center}.copy-link-inline,.dashboard-page .btn-icon-dots,.project-close{display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:6px;background:0 0;color:var(--ink-muted);padding:0;width:32px;height:32px;min-width:32px;cursor:pointer}.copy-link-inline:focus-visible,.copy-link-inline:hover,.dashboard-page .btn-icon-dots:hover,.project-close:hover{background:var(--accent-soft);color:var(--accent)}.copy-link-inline:disabled{opacity:.5;cursor:wait}.mobile-meta-icon{display:none}.project-attention-action{display:inline-block;color:var(--accent);border:0;background:0 0;padding:0;font:inherit;font-size:12px;cursor:pointer;text-decoration:underline;text-underline-offset:3px}.project-attention-action{text-align:left}.dashboard-page .cell-status .project-attention-action{flex-basis:100%}.project-details-row{width:100%;border-bottom:1px solid var(--border-color)}.project-details{padding:var(--dashboard-space-xl);background:var(--paper-bg-alt)}.project-details-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--dashboard-space-sm)}.project-details-heading h2{font:600 16px var(--font-sans);margin:0}.project-details-list{display:grid;grid-template-columns:1fr 1fr;gap:var(--dashboard-space-md) var(--dashboard-space-xl);margin:12px 0}.project-details-list dt{font-size:12px;color:var(--ink-muted);font-weight:500;margin-bottom:4px}.project-details-list dd{margin:0;font-size:13px;overflow-wrap:anywhere}.project-details-list code{color:var(--color);font-size:12px}.project-name-form{margin-top:var(--dashboard-space-lg);padding-top:var(--dashboard-space-lg);border-top:1px solid var(--border-color)}.project-name-form label{display:block;font-size:13px;font-weight:500;margin-bottom:4px}.project-name-form p,.project-progress-help{font-size:12px;color:var(--ink-muted);margin-bottom:8px}.project-name-controls{display:flex;gap:var(--dashboard-space-sm);max-width:560px}.project-name-controls .form-control{min-width:0;font-size:14px}.project-name-controls .btn{flex-shrink:0;min-height:40px}.project-name-form .project-name-error{color:var(--status-error)}.project-details-button{min-height:44px;padding:0 8px;border:0;border-radius:6px;background:0 0;font:inherit;font-size:12px;color:var(--ink-muted);cursor:pointer}.anonymize-page .anonymize-topbar-title{font-family:var(--font-sans);font-size:28px;font-weight:600}.anonymize-topbar{padding:16px 24px}.anonymize-form-col{background:var(--paper-bg-alt)}.anonymize-form-col .paper-settings-main{padding:20px 24px}.anonymize-form-col .paper-settings-section{padding:18px}.anonymize-form-col .paper-settings-section:first-child{padding-top:18px}.anonymize-form-col .anonymize-submit-bar{background:var(--paper-bg-alt);padding:12px 0}.anonymize-preview-head .paper-eyebrow{font-family:var(--font-sans);font-size:14px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:0}.anonymize-preview-col{background:var(--canvas-bg-color);padding:24px}.anonymize-preview-head{flex-wrap:wrap;gap:6px 12px;margin-bottom:16px}.anonymize-preview-body{padding:24px}.anonymize-mobile-tabs,.anonymize-preview-empty,.mobile-preview-jump{display:none}.anonymize-page .repo-access-note{font-size:12px}@media (max-width:900px){.paper-settings-body{grid-template-columns:1fr;gap:18px}.paper-settings-toc{padding:0}.paper-settings-toc-head{display:none}.paper-table .paper-table-row{padding-left:14px;padding-right:14px}}@media (max-width:767px){.anonymize-page .paper-page-title,.paper-page .paper-page-title{font-size:24px}.paper-settings{padding:20px 16px 40px}.paper-settings-main .paper-settings-section{padding:16px}.anonymize-topbar{padding:12px 16px}.anonymize-topbar .paper-crumbs{display:none}.anonymize-mobile-tabs{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--border-color);background:var(--paper-card)}.anonymize-mobile-tabs button{flex:1;font:inherit;font-size:13px;padding:10px;border:0;border-radius:6px;background:0 0;color:var(--ink-muted)}.anonymize-mobile-tabs button[aria-selected=true]{background:var(--accent-soft);color:var(--accent);font-weight:600}.anonymize-page:not(.mobile-preview-active) .anonymize-preview-col{display:none}.mobile-preview-active .anonymize-form-col{display:none}.mobile-preview-active .anonymize-preview-empty{display:block;padding:24px 16px;color:var(--ink-muted)}.anonymize-form-col .paper-settings-main{padding:16px}.anonymize-form-col .anonymize-submit-bar{background:var(--paper-bg-alt)}.mobile-preview-jump{display:block;width:100%;min-height:44px}.anonymize-preview-col{padding:16px}.anonymize-preview-body{padding:16px}}@media (max-width:575px){.paper-settings .quota-header{flex-direction:row;flex-wrap:wrap;align-items:baseline;gap:4px 8px}}@media (max-width:1100px) and (min-width:901px){.dashboard-page .dashboard-filter-row{flex-wrap:wrap}.dashboard-page .dashboard-filter-row .search-wrap{flex-basis:100%}.dashboard-page .dashboard-filter-controls{width:100%;justify-content:space-between}}@media (max-width:900px){.dashboard-page .paper-table.paper-table-dashboard .paper-table-row{gap:4px 8px;padding:8px 12px}.dashboard-page .paper-table .cell-anon{flex:1 1 calc(100% - 96px);min-width:0}.dashboard-page .paper-table .cell-actions{flex:0 0 88px}.dashboard-row-meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;order:3;width:100%}.dashboard-page .dashboard-row-meta .cell-status{flex:1 1 80px;flex-wrap:wrap;gap:2px 8px}.dashboard-page .dashboard-row-meta .cell-expires,.dashboard-page .dashboard-row-meta .cell-views{flex-shrink:0;font-size:12px}.dashboard-page .cell-expires::before,.dashboard-page .cell-views::before{content:none}.dashboard-page .mobile-meta-icon{display:inline-block;width:14px;height:14px}.dashboard-page .dashboard-row-meta .status-sub-date{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.dashboard-page .dashboard-row-meta .status-sub-error,.dashboard-page .dashboard-row-meta .status-sub-warn{flex-basis:100%;white-space:normal;overflow-wrap:anywhere}.dashboard-page .dashboard-row-meta .status-sub::before{content:none}.dashboard-page .project-attention-action{min-height:44px;display:inline-flex;align-items:center}.dashboard-page .paper-table .anon-sub .commit-hash,.dashboard-page .paper-table .anon-sub .conf-tag{display:none}}@media (max-width:767px){.container.dashboard-page{padding-top:12px}.dashboard-heading .paper-page-title{font-size:24px}.dashboard-heading-actions .btn-ink{min-height:44px}.dashboard-new-label{display:none}.dashboard-new-mobile{display:inline}.dashboard-quota>summary{min-height:44px;gap:6px;padding:10px;font-size:12px;line-height:20px}.dashboard-quota .quota-panel{max-height:calc(100dvh - 184px);overflow-y:auto;overscroll-behavior:contain}.dashboard-page .dashboard-filter-row{flex-wrap:wrap;gap:8px;margin-top:12px}.dashboard-page .dashboard-filter-row .search-wrap{flex:1 1 0}.dashboard-page .dashboard-filter-row .form-control{height:44px}.dashboard-page .dashboard-filters-toggle{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:8px;min-height:44px;background:0 0;border:0}.dashboard-page .dashboard-filter-controls{display:none;width:100%}.dashboard-page .dashboard-filter-controls.expanded{display:flex;flex-wrap:wrap;gap:8px}.dashboard-page .dashboard-filter-controls .btn-group{width:100%;display:flex}.dashboard-page .dashboard-filter-controls .btn-group .btn{flex:1;min-height:44px}.dashboard-extra-filters{display:flex;width:100%}.dashboard-page .dashboard-extra-filters .dropdown{flex:1;min-width:0}.dashboard-page .dashboard-extra-filters .dropdown-toggle{width:100%;height:44px}.dashboard-type-filter .type-filter-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.dashboard-type-filter .project-type-svg{display:inline-block}.dashboard-page .dashboard-meta{margin-top:8px;gap:4px 8px}.dashboard-view-tabs{gap:16px}.dashboard-view-tabs button{min-width:44px;min-height:44px;font-size:12px}.dashboard-count{font-size:11px}.dashboard-count{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.dashboard-view-long{display:none}.dashboard-view-short{display:inline}.dashboard-active-filters{display:contents}.dashboard-page .filter-chip{font-size:11px;padding:0 8px;min-height:44px;gap:4px}.dashboard-page .filter-chip-close{min-width:44px;min-height:44px;margin:0}.dashboard-page .btn-link-inline{min-height:44px;font-size:12px}.dashboard-page .paper-table-dashboard{width:calc(100% + 36px);margin:8px -18px 0;border-left:0;border-right:0;border-radius:0}.dashboard-page .paper-table .repo-name{font-size:13px}.project-details{padding:16px 12px}.project-details-list{grid-template-columns:1fr;gap:12px}.project-name-controls{flex-wrap:wrap}.project-name-controls .form-control{flex-basis:100%}.project-name-controls .btn{min-height:44px}}@media (max-width:360px){.dashboard-attention-prefix{display:none}.dashboard-attention-prefix+span{text-transform:capitalize}}.ui-icon-definitions{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}.ui-icon{display:inline-block;width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;vertical-align:-3px}.ui-icon.sort-direction{width:14px;height:14px}.pr-title-row{display:flex;align-items:flex-start;gap:16px}.pr-title-row .pr-title{flex:1;min-width:0}.pr-title-row>.btn{flex-shrink:0}.ui-icon.record-type-icon{color:var(--ink-muted)}.btn .ui-icon,.dropdown-item .ui-icon{width:16px;height:16px}.btn-icon-dots .ui-icon{width:18px;height:18px}.anonymize-page .paper-page-title,.paper-page .paper-page-title{font-size:28px}.paper-faq,.paper-page{padding-top:24px}.paper-page .paper-page-lede{margin-bottom:16px;overflow-wrap:anywhere}.paper-page-title{overflow-wrap:anywhere}.anonymize-page .form-control,.paper-page .form-control{min-height:40px;font-family:var(--font-sans);font-size:14px;border-radius:var(--radius-control)}.anonymize-page textarea.form-control,.paper-page textarea.form-control{height:auto}.anonymize-page .btn,.paper-page .btn,.status-bar-actions .btn{min-height:var(--button-height);padding:7px var(--button-padding-x);font:500 var(--button-font-size)/var(--button-line-height) var(--font-sans);border-radius:var(--radius-button);gap:6px}.anonymize-page .btn.btn-sm,.paper-page .btn.btn-sm,.status-bar-actions .btn{min-height:var(--button-compact-height);padding:5px 10px;font-size:13px;border-radius:6px}.anonymize-page .btn-ink,.paper-page .btn-ink{min-height:40px;padding:9px 15px}.btn-hero>i,.btn>.project-type-svg,.btn>.ui-icon,.btn>i{width:16px;height:16px;margin:0!important;flex-shrink:0}.btn.dropdown-toggle::after{margin-left:0}.btn.disabled,.btn:disabled{cursor:not-allowed}.btn.disabled:hover,.btn:disabled:hover{filter:none}.paper-page .btn.btn-link{background:0 0;border-color:transparent}.auth-continue.btn{min-height:44px;padding:11px 16px!important}.anonymize-page .btn.btn-icon,.paper-page .btn.btn-icon{width:32px;height:32px;min-width:32px;min-height:32px;padding:0}.paper-page .btn.btn-icon-dots{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;min-width:32px;min-height:32px;padding:0;border:0;background:0 0;color:var(--ink-muted)}.paper-page .btn-icon-dots:hover{background:var(--accent-soft);color:var(--accent)}.paper-page .btn-group .btn-primary:active,.paper-page .btn-group .btn-primary:hover{background:var(--accent-soft)!important;color:var(--accent)}.paper-page .paper-section-eyebrow{font-family:var(--font-sans);font-size:16px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:0;margin-bottom:12px}.paper-page .paper-field-label{font-family:var(--font-sans);font-size:13px;letter-spacing:0}.paper-tabs .paper-tab{min-height:44px}.paper-pill,.status-pill{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;border-radius:6px}.paper-meta-rule{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;gap:12px 24px}.paper-meta-rule b:not(.commit-hash){font-family:var(--font-sans);font-weight:500}.connections-grid{gap:16px}.connections-grid>section{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);padding:20px}.connections-heading{margin-bottom:24px}.claim-page .claim-form,.status-page>.paper-settings-section{padding:20px;margin-top:24px;border:1px solid var(--border-color);border-radius:var(--radius-surface);background:var(--paper-card)}.claim-page .claim-form{max-width:640px}.status-page .anonymize-submit-bar{background:0 0;gap:8px;flex-wrap:wrap}.anonymize-landing{padding-top:32px}.paper-settings .quota-usage{grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}.paper-settings .quota-usage .quota-item+.quota-item{padding-top:0;border-top:0;padding-left:16px;border-left:1px solid var(--border-color)}.paper-settings .quota-usage .quota-header{flex-direction:row;align-items:center;flex-wrap:wrap}.paper-settings .quota-usage .quota-value{white-space:normal;flex-wrap:wrap}.paper-faq-hero{padding-bottom:24px;margin-bottom:24px}.paper-faq-eyebrow,.paper-faq-toc-head{font-family:var(--font-sans);font-size:12px;text-transform:none;letter-spacing:0;margin-bottom:8px}.paper-faq-title{font:600 28px/1.2 var(--font-sans);letter-spacing:-.025em;max-width:720px;margin-bottom:12px}.paper-faq-title em{font-style:normal}.paper-faq-lede{font-size:14px}.paper-faq-body{grid-template-columns:180px minmax(0,1fr);gap:40px}.faq-section h4.faq-cat{font:600 18px/1.4 var(--font-sans);padding-bottom:12px}.faq-section h4.faq-cat .ui-icon{display:none}.faq-section .panel-group{margin-bottom:32px}.faq-section .panel-default .panel-title a{font:500 15px/1.5 var(--font-sans);padding:16px 0 16px 28px;min-height:44px}.faq-section .panel-default .panel-title a:after{top:16px}.paper-empty-title{font:600 32px/1.2 var(--font-sans);letter-spacing:-.025em}.paper-empty-title em{font-style:normal}.btn-hero{justify-content:center;gap:8px;min-height:48px;padding:13px 20px;font-size:15px;line-height:20px;background:var(--accent);border-color:var(--accent);color:#fff;border-radius:var(--radius-button)}.dark-mode .btn-hero{color:#1a1815}.btn-hero:hover{background:var(--accent);color:#fff;filter:brightness(.94)}.dark-mode .btn-hero:hover{color:#1a1815}.paper-table:not(.paper-table-dashboard){border-left:0;border-right:0;border-radius:0}.paper-table:not(.paper-table-dashboard) .paper-table-head{border-radius:0}.paper-table .cell-anon{gap:8px}.paper-table .cell-anon .anon-text{flex:1}.paper-table-conferences .cell-anon .repo-name{font-family:var(--font-sans);font-weight:600}.paper-table .anon-sub>a,.paper-table .anon-sub>span{min-width:0;overflow:hidden;text-overflow:ellipsis}.paper-table .anon-sub .ui-icon{width:14px;height:14px}.paper-page .filter-chip{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;border-radius:6px}.paper-page .filter-chip-close .ui-icon{width:12px;height:12px}.admin-nav{gap:4px;border:0;border-bottom:1px solid var(--border-color);border-radius:0;padding:0 0 8px;background:0 0}.admin-nav a{font-family:var(--font-sans);font-size:13px;letter-spacing:0;text-transform:none;border-radius:6px;min-height:40px;padding:8px 12px}.admin-nav a.active{background:var(--accent-soft);color:var(--accent)}.admin-nav .ui-icon{width:16px;height:16px}.admin-filter-toolbar{background:0 0;border:0;padding:0}.admin-filter-inline>label,.admin-filter-inline>span{font-family:var(--font-sans);text-transform:none;letter-spacing:0}.admin-summary .summary-total{font-family:var(--font-sans);font-size:18px;font-weight:600}.admin-summary .summary-pill{font:500 13px/20px var(--font-sans);min-height:32px;padding:5px 10px;border-radius:6px}.admin-summary .summary-pill.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.admin-summary .summary-pill .count{font-family:var(--font-sans)}.admin-active-chip{font-family:var(--font-sans);font-size:12px}.admin-active-chip button .ui-icon{width:12px;height:12px}.user-detail-card .user-header h2{font:600 20px/1.4 var(--font-sans)}.errors-page .errors-toolbar .select-wrap label,.errors-page .volume-head,.overview-page .ov-panel-meta,.paper-detail-grid .detail-label,.paper-progress .paper-progress-label,.status-page .paper-error-eyebrow,.user-detail-grid .detail-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.overview-page .ov-chart-title,.overview-page .ov-panel-title,.paper-error-title{font:600 16px/1.4 var(--font-sans)}.paper-error-head>.ui-icon,.paper-ratelimit-head>.ui-icon{margin-top:3px;color:var(--status-error)}.paper-ratelimit-head>.ui-icon{color:var(--status-progress)}.errors-page .seg-tabs{border-radius:var(--radius-control)}.errors-page .seg-tabs button{min-height:36px;padding:7px 12px;font:500 14px/20px var(--font-sans)}.errors-page .seg-tabs button.active{background:var(--accent-soft);color:var(--accent)}.errors-page .errors-list,.errors-page .kpi-card:hover,.errors-page .volume-chart,.overview-page .ov-chart-card,.overview-page .ov-panel-card{box-shadow:none}.errors-page .kpi-value,.overview-page .ov-daily-value,.overview-page .ov-kpi-value,.q-card-count,.user-hero-name{font-family:var(--font-sans);font-size:28px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.02em}.errors-page .kpi-label,.overview-page .ov-daily-label,.overview-page .ov-kpi-label,.q-card-name,.q-section-label,.user-hero-details .detail-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.overview-page .ov-daily-card{background:var(--paper-card);box-shadow:none;border-radius:var(--radius-surface)}.dark-mode .overview-page .ov-daily-value,.overview-page .ov-daily-value{color:var(--accent)}.errors-page .kpi-card,.overview-page .ov-kpi-card{box-shadow:none;border-radius:var(--radius-surface)}.q-card{border-radius:var(--radius-surface)}.q-card.selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.q-range-btns{border-radius:var(--radius-control)}.q-job-detail-actions .btn,.q-range-btns .btn{font-family:var(--font-sans);font-size:13px}.q-range-btns .btn.active{background:var(--accent-soft);color:var(--accent)}.btn-dark{border-radius:var(--radius-control);font-family:var(--font-sans);font-size:12px}@media (max-width:900px){.paper-settings .quota-usage{grid-template-columns:1fr;gap:16px}.paper-settings .quota-usage .quota-item+.quota-item{padding-left:0;border-left:0;border-top:1px solid var(--border-color);padding-top:16px}.paper-table:not(.paper-table-dashboard) .paper-table-row{gap:4px 8px!important;padding:8px 12px}.paper-table:not(.paper-table-dashboard) .cell-anon{flex:1 1 calc(100% - 52px);order:1}.paper-table:not(.paper-table-dashboard) .cell-actions{order:2;flex:0 0 44px}.paper-table .admin-users-row>[role=cell]:first-child,.paper-table.paper-table-repos.has-bulk .paper-table-row>[role=cell]:first-child{flex:0 0 24px;order:0}.paper-table .admin-users-row .cell-anon,.paper-table.paper-table-repos.has-bulk .cell-anon{flex-basis:calc(100% - 84px)}.paper-table:not(.paper-table-dashboard) .cell-anon .anon-sub{font-size:12px;gap:6px}.paper-table:not(.paper-table-dashboard) .cell-status .status-sub{font-size:12px}.paper-table .cell-expires::before,.paper-table .cell-views::before{content:none}.paper-table .cell-expires .mobile-meta-icon,.paper-table .cell-views .mobile-meta-icon{display:inline-block;width:14px;height:14px}.paper-faq-body{grid-template-columns:1fr;gap:24px}}@media (max-width:767px){.paper-faq,.paper-page{padding-top:20px}.anonymize-page .paper-page-title,.paper-faq-title,.paper-page .paper-page-title,.paper-page.admin-page .paper-page-title{font-size:24px}.anonymize-page .form-control,.paper-page .form-control{min-height:44px}.anonymize-page .btn,.connection-link,.draft-discard,.paper-faq-toc a,.paper-page .btn,.paper-settings-toc nav a{min-height:44px}.anonymize-page .btn,.paper-page .btn,.status-bar-actions .btn{min-height:44px;padding:11px 14px;font-size:14px;line-height:20px;white-space:normal}.anonymize-page .btn.btn-sm,.paper-page .btn.btn-sm{min-height:44px;padding:11px 12px;font-size:14px}.anonymize-page .btn.btn-icon,.copy-link-inline,.paper-page .btn.btn-icon,.paper-page .btn.btn-icon-dots,.project-close{width:44px;height:44px;min-width:44px;min-height:44px;padding:0}.dashboard-page .dashboard-filter-row .btn-group .btn,.dashboard-page .dashboard-filter-row .dropdown-toggle{height:auto;min-height:44px;font-size:14px;padding:11px 10px}.dashboard-heading-actions .btn-ink{padding:11px 16px}.dashboard-page .dashboard-filters-toggle{padding:11px 10px}.errors-page .btn-icon,.pr-title-row .btn,.status-bar-actions .btn{min-width:44px;min-height:44px;padding:0}.status-bar-actions .btn{width:44px;height:44px}.errors-page .seg-tabs button,.paper-tab{min-height:44px}.errors-page .seg-tabs button{padding:11px 12px}.status-bar-actions{flex-wrap:wrap;max-width:100%}.leftCol-close,.tree-search-clear{min-width:44px;min-height:44px}.anonymize-landing{padding:20px 18px}.anonymize-landing .repo-access-switch button,.source-url-label-row .repo-access-switch button{min-height:44px;padding:11px 10px;font-size:13px}.paper-settings-body{margin-top:16px;padding-top:0;gap:16px}.paper-settings-toc nav{gap:0 12px}.paper-settings-toc nav a{display:inline-flex;align-items:center}.paper-faq{padding-left:18px;padding-right:18px}.paper-faq-toc nav{display:flex;flex-direction:row;flex-wrap:wrap;gap:0 16px}.paper-faq-toc a{display:inline-flex;align-items:center;padding:8px 0;border-left:0;border-bottom:2px solid transparent;margin-left:0}.paper-faq-toc nav a.active,.paper-faq-toc nav a:hover{border-bottom-color:var(--accent);color:var(--accent)}.claim-page .claim-form,.status-page>.paper-settings-section{padding:16px;margin-top:16px}.admin-nav{flex-direction:row;flex-wrap:nowrap;overflow-x:auto;gap:4px;max-width:100%;margin:8px 0 16px}.admin-nav a{flex-shrink:0;min-height:44px}.admin-summary .summary-pill{min-height:44px;padding:11px 12px;font-size:14px}.admin-active-chip button{min-width:44px;min-height:44px}.admin-filter-row{gap:8px}.admin-filter-row .admin-filter-inline{gap:8px;flex-wrap:wrap}.errors-page .kpi-grid{grid-template-columns:1fr 1fr;gap:12px}.q-cards{flex-direction:row}.q-card{min-width:130px}.q-header-actions{flex-wrap:wrap;gap:8px}}.billing-plan{padding:1.5rem 0;border-bottom:1px solid var(--paper-border,#d8d4cd)}.billing-plan h3{display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;justify-content:space-between}.billing-price{font-variant-numeric:tabular-nums;font-weight:400}.billing-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}#settings-billing p{max-width:72ch}.repository-hosting{border-top:1px solid var(--border-color);margin-top:24px}.repository-hosting h2{font-size:1.5rem}.hosting-choices{border:0;padding:0;margin:20px 0;min-width:0}.hosting-choices legend{font-size:1rem;font-weight:600}.hosting-choice{display:flex;align-items:flex-start;gap:12px;padding:16px;border:1px solid var(--border-color);background:var(--paper-card);cursor:pointer}.hosting-choice+.hosting-choice{border-top:0}.hosting-choice input{margin-top:5px;accent-color:var(--accent)}.hosting-choice small,.hosting-choice strong{display:block}.hosting-choice small{margin-top:6px;color:var(--ink-muted)}.hosting-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin:20px 0}.repository-pass{margin:20px 0}.repository-pass summary{cursor:pointer;color:var(--link-color)}.repository-pass p{margin-top:12px}.task-review-active.anonymize-page{height:auto!important;min-height:100%;overflow:visible}.task-review-active .anonymize-workspace{flex:0 0 auto;overflow:visible}.repository-task{width:100%;max-width:1020px;margin:0 auto;padding:32px 24px 42px;color:var(--color);font-size:15px;line-height:1.55}.repository-task .paper-crumbs{margin-bottom:24px}.repository-task h1{font:normal 40px/1.15 'Iowan Old Style',Georgia,serif;margin:8px 0 10px}.repository-task h2{font:normal 27px/1.25 'Iowan Old Style',Georgia,serif;margin:0 0 12px}.repository-task h3{font:600 17px/1.5 var(--font-sans);margin:0 0 10px}.repository-task p{margin:0 0 16px}.task-lede,.task-muted{color:var(--ink-muted)}.task-trail{list-style:none;display:flex;flex-wrap:wrap;gap:24px;margin:30px 0;padding:18px 0;border-bottom:1px solid var(--border-color);color:var(--ink-muted);font-size:13px}.task-trail [aria-current]{color:var(--color);font-weight:600}.task-trail button{color:inherit;padding:0;border:0;background:0 0;font:inherit;cursor:pointer}.task-columns{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(245px,1fr);gap:34px;align-items:start}.task-section-title{display:flex;align-items:baseline;justify-content:space-between;gap:16px}.task-text-button{border:0;padding:7px 0;background:0 0;color:var(--link-color);cursor:pointer;font:inherit;text-align:left}.task-text-button:focus-visible,.task-trail button:focus-visible{outline:3px solid var(--accent);outline-offset:3px}.task-summary{background:var(--paper-bg-alt);padding:24px;border-radius:5px}.task-summary dt{margin-top:18px;font-size:12px;font-weight:400;color:var(--ink-muted)}.task-summary dd{margin:3px 0;overflow-wrap:anywhere}.task-redactions,.task-summary h3{overflow-wrap:anywhere}.task-summary dl{margin-bottom:0}.task-summary-foot{border-top:1px solid var(--border-color);margin-top:28px;padding-top:24px}.task-summary-foot p{color:var(--ink-muted);margin:0}.task-hosting{border-top:1px solid var(--border-color);margin-top:22px;padding-top:20px}.repository-task .hosting-choices{margin:18px 0}.repository-task .hosting-choice{padding:18px;gap:13px;font-weight:400;margin:0}.repository-task .hosting-choice:first-of-type{border-radius:6px 6px 0 0}.repository-task .hosting-choice:last-of-type{border-radius:0 0 6px 6px}.repository-task .hosting-choice small{margin-top:0;font-size:13px;line-height:1.55}.task-terms{font-size:13px;color:var(--ink-soft)}.task-actions{display:flex;flex-wrap:wrap;align-items:center;gap:18px;margin:24px 0 16px}.repository-task .btn{padding:10px 16px;border-radius:5px;font-size:15px}.task-status{background:var(--accent-soft);padding:16px 20px;margin:20px 0}.task-status p:last-child{margin:0}@media(max-width:650px){.repository-task{padding:24px 18px}.repository-task h1{font-size:32px}.task-columns{display:block}.task-summary{margin-top:28px}.task-trail{gap:15px;font-size:12px}}.repository-task{scroll-margin-top:70px}.task-columns{align-items:stretch}.repository-task .btn-ink{background:var(--color);border-color:var(--color);color:var(--canvas-bg-color);box-shadow:none}.repository-task .btn-ink:hover{background:var(--ink-soft);border-color:var(--ink-soft)}.task-field{margin-bottom:24px}.task-field>label{display:block;font-weight:600;margin-bottom:8px}.task-field input,.task-field select,.task-field textarea{width:100%;padding:12px;border:1px solid var(--border-color);border-radius:4px;background:var(--paper-card);color:var(--color);font:inherit}.task-field textarea{resize:vertical}.task-field small{display:block;margin-top:8px;font-size:13px;color:var(--ink-muted)}.task-field :is(input,select,textarea):focus-visible{outline:3px solid var(--accent);outline-offset:3px}.task-paybox{background:var(--paper-card);border:1px solid var(--border-color);border-radius:5px;padding:22px;margin-top:20px}.task-ready{border-left:3px solid var(--success,#2f7a44);padding:16px 20px;background:var(--paper-bg-alt);margin-bottom:22px}.repository-task.status-page .paper-progress{margin-top:24px}.task-date-options summary{cursor:pointer;color:var(--link-color);font-size:13px}.task-date-options[open] summary{margin-bottom:12px}.task-result{padding:18px;background:var(--paper-bg-alt);overflow-wrap:anywhere}.task-result .detail-label{font-size:12px;color:var(--ink-muted);margin-bottom:6px}.task-configure-columns{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start}.task-live-preview{min-width:0;border:1px solid var(--border-color);border-radius:5px;background:var(--paper-card)}.task-preview-head{padding:20px 24px;border-bottom:1px solid var(--border-color)}.task-preview-head h2{font-size:23px;margin-bottom:6px}.task-preview-head p{margin:0;font-size:13px}.task-preview-content,.task-preview-message{padding:24px}.task-preview-content{overflow:auto;max-height:650px;overflow-wrap:anywhere}.repository-task .task-preview-content h1{font:600 28px/1.3 var(--font-sans)}.repository-task .task-preview-content h2{font:600 22px/1.3 var(--font-sans)}@media(max-width:650px){.task-live-preview{margin-top:28px}.task-preview-content{max-height:none}}.anonymize-page .anonymize-topbar{padding:14px 22px;background:var(--canvas-bg-color)}.anonymize-page .anonymize-topbar-inner{max-width:none}.anonymize-page .anonymize-topbar-head{justify-content:space-between}.anonymize-page .anonymize-topbar-title{font:600 19px/1.25 var(--font-sans);margin:0}.anonymize-page .anonymize-split{display:grid;grid-template-columns:minmax(320px,42%) minmax(0,1fr);overflow:hidden}.anonymize-page .anonymize-form-col{width:auto;min-height:0;overflow:hidden!important;background:var(--paper-card)}.anonymize-page .anonymize-form-col .paper-settings-main{display:flex;flex-direction:column;height:100%;min-height:0;max-width:none;padding:0}.anonymize-fields{flex:1;min-height:0;overflow:auto;position:relative;scrollbar-width:thin}.anonymize-page .paper-settings-main .paper-settings-section{border:0;border-top:1px solid var(--border-color);border-radius:0;padding:18px 20px 20px;margin:0;box-shadow:none}.anonymize-page .paper-settings-main .paper-settings-section:first-child{border-top:0;padding-top:18px}.anonymize-page .paper-section-eyebrow{font:600 15px/1.4 var(--font-sans);margin:0 0 16px;letter-spacing:0}.anonymize-page .paper-field-label{font:500 13px/1.5 var(--font-sans);margin-bottom:8px}.anonymize-page .form-group{margin-bottom:16px}.anonymize-page .form-group:last-child{margin-bottom:0}.anonymize-page .form-control{font-size:14px;min-height:40px;padding:9px 10px;border-radius:4px}.anonymize-page textarea.form-control{line-height:1.6}.anonymize-page .form-grid-2{gap:12px}.anonymize-page .form-check{padding-top:10px;padding-bottom:10px;font-size:14px}.anonymize-page .form-check-label{font-size:14px;line-height:1.5}.anonymize-page .form-text{font-size:12px;line-height:1.6}.anonymize-page .repo-access-details{margin:12px 0 16px}.anonymize-page .repo-access-note{font-size:11px}.anonymize-page .anonymize-submit-bar{flex:none;position:static;margin:0;padding:16px 20px;background:var(--paper-card);border-top:1px solid var(--border-color)}.anonymize-page .anonymize-submit-bar .btn{width:100%;font-size:14px;padding:10px 12px;min-height:43px}.checkout-return-note{font-size:11px;line-height:1.5;color:var(--ink-muted);margin:8px 0 0;text-align:center}.anonymize-fields>.alert,.anonymize-fields>.anonymize-review-note,.anonymize-fields>.draft-status{margin:12px 20px}.anonymize-page .anonymize-preview-col{min-height:0;padding:20px;position:relative;background:var(--canvas-bg-color)}.anonymize-page .anonymize-preview-head{border:0;padding:0;min-height:32px;margin-bottom:14px;display:flex;align-items:center;justify-content:space-between}.anonymize-preview-head button,.anonymize-preview-head strong{font-size:13px}.anonymize-page .anonymize-preview-body{padding:24px;border-radius:6px;min-height:480px;overflow-wrap:anywhere;font:14px/1.7 var(--font-sans)}.anonymize-page .anonymize-preview-body h1{font-size:27px;line-height:1.2;margin:0 0 20px;padding-bottom:14px}.anonymize-page .anonymize-preview-body h2{font-size:18px;margin:28px 0 12px}.anonymize-page .anonymize-preview-body pre{font-size:12px;padding:15px}.anonymize-page.preview-expanded .anonymize-split{grid-template-columns:1fr}.anonymize-page.preview-expanded .anonymize-form-col{display:none}.hosting-usage{font-size:12px;color:var(--ink-muted);margin:0 0 4px}.expiration-choices{border:0;padding:0;margin:0 0 16px;min-width:0}.expiration-choice{display:flex;align-items:flex-start;gap:10px;padding:12px 0;margin:0;cursor:pointer;font-size:14px;font-weight:500;line-height:1.5}.expiration-choice input{width:16px;height:16px;margin:4px 0 0;flex:none;accent-color:var(--accent)}.expiration-choice small{display:block;font-size:12px;font-weight:400;color:var(--ink-muted);margin-top:2px}.dated-options{padding:2px 0 14px 26px}.hosting-date,.hosting-property{display:grid;grid-template-columns:48px minmax(0,1fr);gap:8px;align-items:center;margin-bottom:10px}.hosting-property{margin-bottom:0}.hosting-date .paper-field-label,.hosting-property label{font-size:12px;font-weight:400;margin:0}.hosting-date .invalid-feedback{grid-column:1/-1}.anonymize-page .conference-field{border-top:1px solid var(--border-color);padding:16px 0;margin:0}.subscription-choice{padding:16px;background:var(--paper-bg-alt);border-radius:6px;margin-top:4px}.subscription-choice h3{font-size:13px;font-weight:600;margin:0 0 10px}.billing-cycle{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;border:0;padding:0;margin:0 0 12px;min-width:0}.cycle-option{display:flex;align-items:center;gap:7px;padding:10px 8px;border:1px solid var(--border-color);border-radius:4px;background:var(--paper-card);font-size:12px;cursor:pointer;margin:0}.cycle-option.selected{border-color:var(--accent);background:var(--accent-soft)}.cycle-option input{accent-color:var(--accent);margin:0;flex:none}.subscription-choice p{font-size:12px;line-height:1.5;margin:0 0 8px}.subscription-choice p:last-child{margin:0}.hosting-terms{font-size:12px;line-height:1.5;color:var(--ink-muted)}.capacity-reason{font-size:13px}.capacity-options,.repository-pass{margin-top:12px;font-size:12px}.capacity-options summary,.repository-pass summary{cursor:pointer}.capacity-options p,.repository-pass p{margin:8px 0}.capacity-options .task-text-button{display:block}@media(max-width:767px){.anonymize-page .anonymize-split{display:block;overflow:visible}.anonymize-page .anonymize-form-col .paper-settings-main{height:auto}.anonymize-fields{overflow:visible}.anonymize-page .paper-settings-main .paper-settings-section{padding:18px 16px 20px}.anonymize-page .anonymize-topbar{padding:14px 16px}.anonymize-page .anonymize-preview-col{padding:16px}.anonymize-page .anonymize-preview-body{padding:20px}.anonymize-page .anonymize-submit-bar{padding:16px}.anonymize-page .preview-expand{display:none}.anonymize-page .anonymize-mobile-tabs{padding:0;gap:0;background:var(--paper-card)}.anonymize-mobile-tabs button{border-radius:0;padding:12px;min-height:44px}.anonymize-mobile-tabs button[aria-selected=true]{background:0 0;box-shadow:inset 0 -2px var(--accent)}.cycle-option{min-height:44px}}.anonymize-page{--font-sans:system-ui,-apple-system,sans-serif;font-family:var(--font-sans)}body:not(.dark-mode) .anonymize-page{--paper-card:#fff;--canvas-bg-color:#faf9f6;--paper-bg-alt:#f5f4f0;--border-color:#dedbd4;--color:#242320;--ink-muted:#68655e}.anonymize-page .repo-access-controls .btn{font-size:12px;padding:8px 12px;min-height:36px}.anonymize-page .form-check-label{font-weight:400}.anonymize-page .form-check{padding-left:26px}.anonymize-page .form-check input{width:16px;height:16px;margin-left:-26px;accent-color:var(--accent)}.anonymize-page .form-check small{margin-top:6px}.anonymize-page .btn.btn-icon.refresh-branches{display:inline-flex;align-items:center;justify-content:center;width:40px;min-width:40px;height:100%;min-height:40px;padding:0}@media (max-width:767px){.anonymize-page #branch,.anonymize-page #commit{min-height:44px}.anonymize-page .btn.btn-icon.refresh-branches{width:44px;min-width:44px;min-height:44px}}.anonymize-page .redaction-syntax{font-size:12px;margin-top:8px}.redaction-syntax summary{cursor:pointer;color:var(--link-color);min-height:28px}.anonymize-page .anonymize-preview-body h2{border:0;padding:0;font-weight:700}.anonymize-page .anonymize-preview-body h1{font-weight:700}.anonymize-page .anonymize-preview-body pre{background:var(--paper-bg-alt);line-height:1.7}.anonymize-page .anonymize-preview-body pre code{font:12px/1.7 ui-monospace,monospace;background:0 0;padding:0}.anonymize-page .anonymize-preview-body{--main-bg-color:var(--paper-card);background:var(--paper-card)}.anonymize-page .anonymize-submit-bar .btn{font-weight:600}.anonymize-page .form-grid-2 .form-group{margin-bottom:0}.anonymize-page .form-check small{margin-left:-26px}@media (min-width:992px){body:has([class*=floatingchat-container-wrap]) .anonymize-page .anonymize-submit-bar{padding-left:228px}}.user-settings{max-width:1060px;padding:28px 26px 40px}.user-settings .settings-title{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:22px}.user-settings .settings-title h1{font:650 26px/1.3 var(--font-sans);letter-spacing:-.025em;margin:0}.settings-title>span{color:var(--ink-muted);font-size:12px}.settings-nav{display:flex;gap:28px;border-bottom:1px solid var(--border-color)}.settings-nav button{border:0;border-radius:0;background:0 0;color:var(--ink-muted);padding:12px 0;min-height:44px;font:13px/1.4 var(--font-sans)}.settings-nav button[aria-current=page]{color:var(--color);font-weight:600;box-shadow:inset 0 -2px var(--accent)}.settings-nav button:hover{color:var(--accent)}.user-settings .settings-scope{color:var(--ink-muted);font-size:12px;margin:20px 0 4px}.user-settings .settings-row{display:grid;grid-template-columns:210px minmax(0,1fr);gap:32px;padding:22px 0;border-bottom:1px solid var(--border-color)}.user-settings .settings-label,.user-settings .settings-row>h2{font-size:13px;font-weight:600;line-height:1.5;margin:8px 0 0;display:block}.user-settings .settings-help{font-size:12px;color:var(--ink-muted);margin:7px 0 0;line-height:1.6}.user-settings .form-control{font-size:14px;min-height:40px;padding:9px 10px;border-radius:4px;background:var(--paper-card)}.user-settings textarea.form-control{line-height:1.6}.settings-syntax{font-size:12px;margin-top:8px}.settings-syntax summary{cursor:pointer;color:var(--link-color);min-height:26px}.settings-syntax p{margin:8px 0}.settings-checks{display:grid;grid-template-columns:1fr 1fr;gap:14px 18px;padding:4px 0;margin-bottom:12px}.user-settings .settings-check{display:flex;gap:9px;align-items:center;font-size:13px;font-weight:400;margin:0}.user-settings .settings-check .form-check-input{position:static;width:16px;height:16px;margin:0;flex:none;accent-color:var(--accent)}.settings-save{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:18px 0 22px}.settings-save-status{color:var(--ink-muted);font-size:12px}.settings-save #save{margin-left:auto;min-width:125px}.settings-save-error{color:var(--status-error);flex-basis:100%}.user-settings .settings-inline{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px}.user-settings .btn{font-size:13px;min-height:40px;padding:9px 13px}.settings-inline .btn{flex-shrink:0}.user-settings #settings-quota{margin:22px 0 16px}.user-settings .quota-row{gap:20px}.user-settings .quota-item{padding:0;background:0 0;border:0}.user-settings .quota-label{font-size:12px}.user-settings .quota-value{font-size:13px;margin:6px 0}.user-settings .quota-value strong{font-size:14px}.user-settings .quota-percent,.user-settings .quota-remaining{font-size:11px}.user-settings .settings-plan{background:var(--paper-card);border:1px solid var(--border-color);border-radius:6px;padding:20px;margin:20px 0 0}.user-settings .settings-plan h3{font-size:15px;margin:0 0 10px}.user-settings .settings-plan>p{font-size:13px}.settings-cycles{display:flex;flex-wrap:wrap;gap:10px;border:0;padding:0;margin:16px 0}.settings-cycles label{display:flex;align-items:center;gap:8px;border:1px solid var(--border-color);background:var(--paper-card);border-radius:5px;padding:12px;font-size:13px;margin:0;cursor:pointer;flex:1}.settings-cycles label.selected{border-color:var(--accent);background:var(--accent-soft)}.settings-cycles input{accent-color:var(--accent)}.settings-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}.settings-notice{background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:5px;padding:14px 16px;margin:16px 0;font-size:13px}.settings-notice p{margin:6px 0 12px}.settings-text-button{border:0;background:0 0;color:var(--link-color);padding:8px 0;font-size:12px}.settings-pass{margin-top:18px}.settings-pass label{display:block;font-size:13px;margin-bottom:8px}.settings-pass .btn{margin-top:12px}.settings-purchase{padding:12px 0;border-bottom:1px solid var(--border-color);font-size:13px;overflow-wrap:anywhere}.settings-purchase:last-child{border:0}.settings-purchase>span{display:block;color:var(--ink-muted);margin-top:4px}.user-settings .paper-account-card{padding:24px 0;border:0;background:0 0}.user-settings .paper-danger-zone{margin-top:24px}@media (max-width:767px){.user-settings{padding:22px 18px 90px}.user-settings .settings-row{grid-template-columns:1fr;gap:12px;padding:20px 0}.user-settings .settings-label,.user-settings .settings-row>h2{margin-top:0}.settings-nav{gap:18px;justify-content:space-between}.settings-nav button{text-align:left;font-size:12px;max-width:124px}.user-settings .settings-check{font-size:12px;min-height:28px}.settings-save{gap:8px}.settings-save-status{font-size:11px}.settings-save #save{min-width:120px}.user-settings .quota-row{grid-template-columns:1fr}.settings-cycles label{flex-basis:100%}}.user-settings .settings-save .draft-status{margin:0;padding:0}.settings-delete-confirm{max-width:480px;margin-top:18px}.settings-delete-confirm label{display:block;font-size:13px;margin-bottom:8px}.user-settings .quota-usage{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}.user-settings .quota-usage .quota-item+.quota-item{border:0;padding:0}.user-settings .quota-percent,.user-settings .quota-remaining{display:none}@media (max-width:767px){.user-settings .quota-usage{grid-template-columns:1fr}}.settings-header-actions{display:flex;align-items:center;gap:16px}.settings-header-actions>span{color:var(--ink-muted);font-size:12px}.user-settings .settings-upgrade{color:var(--accent);border-color:var(--accent);background:0 0}.user-settings .settings-comparison{width:100%;table-layout:fixed;margin:16px 0 20px;font-size:12px}.settings-comparison td,.settings-comparison th{padding:10px 8px;border-bottom:1px solid var(--border-color);text-align:left;overflow-wrap:anywhere;vertical-align:top}.settings-comparison th:first-child{width:42%;padding-left:0}.settings-comparison tbody th{font-weight:400}.settings-comparison thead th{font-weight:600}@media(max-width:580px){.settings-header-actions>span{display:none}.settings-header-actions{gap:8px}.user-settings .settings-comparison{font-size:11px}}.navbar .nav-link.nav-upgrade{border:1px solid var(--accent);border-radius:5px;color:var(--accent)!important;padding:7px 12px!important;margin-right:12px;font-size:12px;line-height:1.5}.navbar .nav-link.nav-upgrade:hover{background:var(--accent-soft)}@media(max-width:991px){.navbar .nav-link.nav-upgrade{display:inline-block;min-height:44px;padding:11px 14px!important;margin:8px 0}} \ No newline at end of file + */.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hive:before{content:"\e07f"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-innosoft:before{content:"\e080"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-perbyte:before{content:"\e083"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-uncharted:before{content:"\e084"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-watchman-monitoring:before{content:"\e087"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:'Font Awesome 5 Brands';font-style:normal;font-weight:400;font-display:block;src:url(../fonts/fa-brands-400.eot);src:url(../fonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-brands-400.woff2) format("woff2"),url(../fonts/fa-brands-400.woff) format("woff"),url(../fonts/fa-brands-400.ttf) format("truetype"),url(../fonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:'Font Awesome 5 Brands';font-weight:400}@font-face{font-family:'Font Awesome 5 Free';font-style:normal;font-weight:400;font-display:block;src:url(../fonts/fa-regular-400.eot);src:url(../fonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-regular-400.woff2) format("woff2"),url(../fonts/fa-regular-400.woff) format("woff"),url(../fonts/fa-regular-400.ttf) format("truetype"),url(../fonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:'Font Awesome 5 Free';font-weight:400}@font-face{font-family:'Font Awesome 5 Free';font-style:normal;font-weight:900;font-display:block;src:url(../fonts/fa-solid-900.eot);src:url(../fonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../fonts/fa-solid-900.woff2) format("woff2"),url(../fonts/fa-solid-900.woff) format("woff"),url(../fonts/fa-solid-900.ttf) format("truetype"),url(../fonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:'Font Awesome 5 Free';font-weight:900}.nb-notebook{line-height:1.5}.nb-stderr,.nb-stdout{white-space:pre-wrap;margin:1em 0;padding:.1em .5em}.nb-stderr{background-color:#faa}.nb-cell+.nb-cell{margin-top:.5em}.nb-output table{border:1px solid #000;border-collapse:collapse}.nb-output th{font-weight:700}.nb-output td,.nb-output th{border:1px solid #000;padding:.25em;text-align:left;vertical-align:middle;border-collapse:collapse}.nb-notebook blockquote{border-left:5px solid #ccc;margin-left:0;padding-left:1em}.nb-cell{position:relative}.nb-raw-cell{white-space:pre-wrap;background-color:#f5f2f0;font-family:Consolas,Monaco,'Andale Mono',monospace;padding:1em;margin:.5em 0}.nb-output{min-height:1em;width:100%;overflow-x:scroll;border-right:1px dotted #ccc}.nb-output img{max-width:100%}.nb-input:before,.nb-output:before{position:absolute;font-family:monospace;color:#999;left:-7.5em;width:7em;text-align:right}.nb-input:before{content:"In [" attr(data-prompt-number) "]:"}.nb-output:before{content:"Out [" attr(data-prompt-number) "]:"}// Fix pandas dataframe formatting div[style="max-height:1000px;max-width:1500px;overflow:auto;"]{max-height:none!important}@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,math,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.18.10"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}.mermaid{text-align:center;margin:1em 0;overflow:auto}.mermaid svg{max-width:100%;height:auto}.dark-mode .mermaid{filter:invert(1) hue-rotate(180deg)}.dark-mode .mermaid svg{background:0 0}.dark-mode .mermaid .edgeLabel,.dark-mode .mermaid .nodeLabel{filter:invert(1) hue-rotate(180deg)}.mermaid .node circle,.mermaid .node ellipse,.mermaid .node polygon,.mermaid .node rect{stroke-width:2px}.mermaid .flowchart-link{stroke-width:2px}.mermaid .actor{fill:#f9f9f9;stroke:#333;stroke-width:2px}.mermaid .messageLine0,.mermaid .messageLine1{stroke:#333;stroke-width:1.5px}.mermaid .section0,.mermaid .section1,.mermaid .section2,.mermaid .section3{opacity:.8}.mermaid .pieChart .slice{opacity:.9}.markdown-body .mermaid{background:0 0;border:none;padding:10px}.mermaid .error{color:#d32f2f;font-family:monospace;font-size:12px;padding:10px;border:1px solid #d32f2f;border-radius:4px;background-color:#ffebee}@font-face{font-family:octicons-link;src:url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff')}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.5;color:#24292e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .pl-c{color:#6a737d}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#005cc5}.markdown-body .pl-e,.markdown-body .pl-en{color:#6f42c1}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#24292e}.markdown-body .pl-ent{color:#22863a}.markdown-body .pl-k{color:#d73a49}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#032f62}.markdown-body .pl-smw,.markdown-body .pl-v{color:#e36209}.markdown-body .pl-bu{color:#b31d28}.markdown-body .pl-ii{color:#fafbfc;background-color:#b31d28}.markdown-body .pl-c2{color:#fafbfc;background-color:#d73a49}.markdown-body .pl-c2::before{content:"^M"}.markdown-body .pl-sr .pl-cce{font-weight:700;color:#22863a}.markdown-body .pl-ml{color:#735c0f}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:#005cc5}.markdown-body .pl-mi{font-style:italic;color:#24292e}.markdown-body .pl-mb{font-weight:700;color:#24292e}.markdown-body .pl-md{color:#b31d28;background-color:#ffeef0}.markdown-body .pl-mi1{color:#22863a;background-color:#f0fff4}.markdown-body .pl-mc{color:#e36209;background-color:#ffebda}.markdown-body .pl-mi2{color:#f6f8fa;background-color:#005cc5}.markdown-body .pl-mdr{font-weight:700;color:#6f42c1}.markdown-body .pl-ba{color:#586069}.markdown-body .pl-sg{color:#959da5}.markdown-body .pl-corl{text-decoration:underline;color:#032f62}.markdown-body .octicon{display:inline-block;vertical-align:text-top;fill:currentColor}.markdown-body a{background-color:transparent;-webkit-text-decoration-skip:objects}.markdown-body a:active,.markdown-body a:hover{outline-width:0}.markdown-body strong{font-weight:inherit}.markdown-body strong{font-weight:bolder}.markdown-body h1{font-size:2em;margin:.67em 0}.markdown-body img{border-style:none}.markdown-body svg:not(:root){overflow:hidden}.markdown-body hr{box-sizing:content-box;height:0;overflow:visible}.markdown-body input{font:inherit;margin:0}.markdown-body input{overflow:visible}.markdown-body [type=checkbox]{box-sizing:border-box;padding:0}.markdown-body *{box-sizing:border-box}.markdown-body input{font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body a{color:#0366d6;text-decoration:none}.markdown-body a:hover{text-decoration:underline}.markdown-body strong{font-weight:600}.markdown-body hr{height:0;margin:15px 0;overflow:hidden;background:0 0;border:0;border-bottom:1px solid #dfe2e5}.markdown-body hr::before{display:table;content:""}.markdown-body hr::after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse}.markdown-body td,.markdown-body th{padding:0}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:0;margin-bottom:0}.markdown-body h1{font-size:32px;font-weight:600}.markdown-body h2{font-size:24px;font-weight:600}.markdown-body h3{font-size:20px;font-weight:600}.markdown-body h4{font-size:16px;font-weight:600}.markdown-body h5{font-size:14px;font-weight:600}.markdown-body h6{font-size:12px;font-weight:600}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0}.markdown-body ol,.markdown-body ul{padding-left:0;margin-top:0;margin-bottom:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code{font-family:SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px}.markdown-body .octicon{vertical-align:text-bottom}.markdown-body .pl-0{padding-left:0!important}.markdown-body .pl-1{padding-left:4px!important}.markdown-body .pl-2{padding-left:8px!important}.markdown-body .pl-3{padding-left:16px!important}.markdown-body .pl-4{padding-left:24px!important}.markdown-body .pl-5{padding-left:32px!important}.markdown-body .pl-6{padding-left:40px!important}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e1e4e8;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:.25em solid #dfe2e5}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1{padding-bottom:.3em;font-size:2em;border-bottom:1px solid #eaecef}.markdown-body h2{padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid #eaecef}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{font-size:.85em;color:#6a737d}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto}.markdown-body table th{font-weight:600}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body code{padding:0;padding-top:.2em;padding-bottom:.2em;margin:0;font-size:85%;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body .full-commit .btn-outline:not(:disabled):hover{color:#005cc5;border-color:#005cc5}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px SFMono-Regular,Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #d1d5da;border-bottom-color:#c6cbd1;border-radius:3px;box-shadow:inset 0 -1px 0 #c6cbd1}.markdown-body :checked+.radio-label{position:relative;z-index:1;border-color:#0366d6}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item+.task-list-item{margin-top:3px}.markdown-body .task-list-item input{margin:0 .2em .25em -1.6em;vertical-align:middle}.markdown-body hr{border-bottom-color:#eee}.dark-mode{--canvas-bg-color:#1B1916;--sidebar-bg-color:#1A1918;--main-bg-color:#1B1916;--header-bg-color:#1A1918;--hover-bg-color:rgba(255, 255, 255, 0.05);--header-color:#FAF9F6;--color:#D6D1C5;--link-color:#A7B2FF;--link-hover-color:#C8CEFF;--border-color:#33302B;--border-soft:#2A2825;--btn-color:#FAF9F6;--input-color:#E6E1D8;--input-bg:#25231F;--primary-bg:#FAF9F6;--primary-hover-bg:#FFFFFF;--primary-color:#1A1815;--primary-active-color:#1A1815;--accent:#A7B2FF;--accent-soft:rgba(167, 178, 255, 0.12);--ink-muted:#A39D92;--ink-faint:#8A857C;--ink-soft:#B5AFA2;--status-ready:#5FB27A;--status-progress:#E0A94A;--status-error:#F08A82;--status-inactive:#8A857C;--status-error-soft:rgba(240, 138, 130, 0.08);--status-error-soft-hover:rgba(240, 138, 130, 0.14);--admin-nav-bg:#1A1918;--admin-nav-active:rgba(167, 178, 255, 0.12);--admin-stat-bg:#25231F;--paper-card:#25231F;--paper-bg-alt:#201E1B;--highlight:#3A341E;background-color:var(--canvas-bg-color);color:var(--color)}body{--canvas-bg-color:#FAF9F6;--sidebar-bg-color:#FFFFFF;--header-bg-color:#FAF9F6;--header-color:#1A1815;--main-bg-color:#FAF9F6;--hover-bg-color:rgba(26, 24, 21, 0.05);--input-color:#1A1815;--color:#1A1815;--link-color:#3B4AD6;--link-hover-color:#2E3BB8;--border-color:#E6E1D8;--border-soft:#EDE9E1;--btn-color:#1A1815;--input-bg:#FFFFFF;--primary-bg:#1A1815;--primary-hover-bg:#000000;--primary-color:#FAF9F6;--primary-active-color:#FAF9F6;--accent:#3B4AD6;--accent-soft:#EEF0FF;--ink-muted:#6F6A61;--ink-faint:#8A857C;--ink-soft:#4A4641;--paper-bg-alt:#F3F1EC;--paper-card:#FFFFFF;--highlight:#FFF3C4;--status-ready:#2F7A44;--status-progress:#946512;--status-error:#B42318;--status-inactive:#8A857C;--status-error-soft:rgba(180, 35, 24, 0.04);--status-error-soft-hover:rgba(180, 35, 24, 0.08);--radius-control:6px;--radius-button:8px;--button-height:36px;--button-font-size:14px;--button-line-height:20px;--button-padding-x:13px;--button-compact-height:32px;--radius-surface:10px;--font-serif:'Instrument Serif','Iowan Old Style',Georgia,serif;--font-sans:'Inter',ui-sans-serif,system-ui,-apple-system,sans-serif;--font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;background-color:var(--canvas-bg-color);color:var(--color);font-family:var(--font-sans);font-size:14px;letter-spacing:-.005em;line-height:1.5}.fadeIn{opacity:1;animation-name:fadeInOpacity;animation-iteration-count:1;animation-timing-function:ease-in;animation-duration:.6s}@keyframes fadeInOpacity{0%{opacity:0}100%{opacity:1}}.btn:active,.btn:focus{box-shadow:none}:focus:not(:focus-visible){outline:0}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.btn:focus-visible,.dropdown-item:focus-visible,.form-control:focus-visible{outline:2px solid var(--accent);outline-offset:2px;box-shadow:none}input:active,input:hover{box-shadow:none!important}input:-webkit-autofill,input:-webkit-autofill:focus,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill,textarea:-webkit-autofill:focus,textarea:-webkit-autofill:hover{border-color:var(--border-color);-webkit-text-fill-color:var(--color);-webkit-box-shadow:none;transition:background-color 1000s ease-in-out 0s}.custom-select,.form-control,input:-internal-autofill-selected{color:var(--input-color);background-color:var(--input-bg)!important;border-color:var(--border-color)}.form-control:focus{border-color:var(--border-color)}body,html{position:relative;height:100%;overflow-x:hidden}body{overflow-y:hidden}#app{flex:1 1 auto;min-height:0}.app-header{flex:0 0 auto}.app-view{display:block;flex:1 1 auto;min-height:0;overflow-x:hidden!important;overflow-y:auto!important;max-width:100%}.markdown-body a,a{color:var(--link-color)}.markdown-body a:hover,a:hover{text-decoration:none;color:var(--link-hover-color)}.dropdown-menu a.dropdown-item{color:var(--color)}.dropdown-menu a.dropdown-item:hover{color:var(--color);text-decoration:none}.markdown-body .emoji{height:1.3em;margin:0;vertical-align:-.1em}.navbar.navbar-expand-lg{background:var(--header-bg-color)!important;border-bottom:1px solid var(--border-color);transition:background-color .5s ease;padding:10px max(20px,calc((100vw - 1200px)/ 2));font-family:var(--font-sans)}.navbar-toggler{border:1px solid var(--border-color)!important;color:var(--color)!important}.navbar-toggler .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%231A1815' stroke-width='2' stroke-linecap='round' d='M4 8h22M4 15h22M4 22h22'/%3E%3C/svg%3E")!important}.navbar .navbar-nav{align-items:center}.navbar .nav-link{display:inline-flex;align-items:center;font-size:13px;color:var(--ink-muted)!important;padding:6px 0!important;margin:0 12px;border-bottom:1px solid transparent;letter-spacing:-.005em;line-height:1}.navbar .nav-link:hover{color:var(--color)!important}.navbar .nav-link.active{color:var(--color)!important;border-bottom-color:var(--color)}.navbar .nav-link.nav-icon{padding:0!important;margin:0 8px;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px}.navbar .nav-link .fa,.navbar .nav-link .fab{font-size:15px;line-height:1}.navbar #navbarSupportedContent{align-items:center}.navbar-brand{font-family:var(--font-serif)!important;font-size:20px!important;letter-spacing:-.01em;color:var(--color)!important;padding-right:18px;margin-right:12px;border-right:1px solid var(--border-color)}.navbar-brand em,.navbar-brand i{font-style:italic;font-weight:400}.navbar-dark .navbar-toggler{border-color:var(--border-color);color:var(--color)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%231A1815' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar .btn-signin{display:inline-flex;align-items:center;height:32px;padding:0 14px!important;margin-left:6px;border:1px solid var(--color);background:var(--color);color:var(--canvas-bg-color)!important;border-radius:6px;font-size:13px;font-weight:500;line-height:1}.navbar .btn-signin:hover{background:var(--primary-hover-bg);color:var(--canvas-bg-color)!important}.navbar .nav-support{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;order:3;gap:6px;min-height:36px;margin-left:12px;padding:0 12px;border:1px solid var(--border-color);border-radius:8px;color:var(--color);font-size:13px;font-weight:500;text-decoration:none}.navbar .nav-support:hover{background:var(--hover-bg-color);border-color:var(--color);color:var(--color)}@media (max-width:991px){.navbar .nav-support{order:0;width:44px;min-height:44px;margin:0 8px 0 auto;padding:0}.navbar .nav-support-label{display:none}[class*=floatingchat-container-wrap],iframe.floatingchat-container{display:none!important}}.navbar .user-chip{display:inline-flex!important;align-items:center;gap:8px;padding:4px 12px 4px 6px!important;margin-left:6px;border:1px solid var(--border-color);border-radius:999px;background:var(--sidebar-bg-color);font-size:13px;color:var(--color)!important}.navbar .user-chip:hover{background:var(--hover-bg-color)}.navbar .user-chip img{width:22px;height:22px}.navbar .user-chip .user-chip-name{color:var(--color);font-weight:500}.navbar .user-chip::after{color:var(--ink-muted)}#navbarSupportedContent{padding-right:0}.generalMessage{display:block;text-align:center;color:var(--header-color)}.rgba-gradient{background:var(--canvas-bg-color);color:var(--color)}.paper-landing{overflow-x:clip}.paper-wrap{max-width:1140px;margin-left:auto;margin-right:auto;padding-left:32px;padding-right:32px}.paper-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);font-weight:500}.paper-hero{max-width:1140px;margin:0 auto;padding:64px 32px 0;display:grid;grid-template-columns:.95fr 1.05fr;gap:48px;align-items:center}.paper-hero h1.hero-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.6rem,5.4vw,4.4rem);line-height:1.02;letter-spacing:-.025em;margin:14px 0 0;color:var(--color);text-wrap:balance}.paper-hero h1 .accent{color:var(--accent);font-style:italic}.hero-subtitle{margin-top:18px;font-size:18px;line-height:1.55;color:var(--ink-soft);max-width:30em;text-wrap:pretty}.paper-cta-row{display:flex;flex-wrap:wrap;gap:18px;align-items:center;margin-top:26px}.btn-hero{display:inline-flex;align-items:center;font-family:var(--font-sans);border:1px solid var(--color);background:var(--color);color:var(--canvas-bg-color);padding:12px 20px;font-size:14px;font-weight:500;border-radius:var(--radius-control);letter-spacing:-.005em;transition:background .15s,border-color .15s,transform .1s}.btn-hero:hover{background:var(--primary-hover-bg);border-color:var(--primary-hover-bg);color:var(--canvas-bg-color);text-decoration:none;transform:translateY(-1px)}.btn-hero-ghost{background:0 0;border:1px solid var(--border-color);color:var(--color);padding:12px 20px;font-size:14px;font-weight:500;border-radius:var(--radius-control)}.btn-hero-ghost:hover{background:var(--hover-bg-color);border-color:var(--color);color:var(--color)}.paper-link-arrow{font-size:14px;color:var(--ink-soft);text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--border-color)}.paper-link-arrow:hover{color:var(--color);text-decoration-color:var(--color)}.paper-reassure{list-style:none;margin:16px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:6px 16px;font-size:13px;color:var(--ink-muted)}.paper-reassure li::before{content:"\2713";color:var(--status-ready);margin-right:6px;font-size:11px}.paper-frame{border-radius:var(--radius-surface);background:var(--paper-card);border:1px solid var(--border-color);box-shadow:0 24px 60px rgba(26,24,21,.1),0 2px 6px rgba(26,24,21,.04);overflow:hidden}.dark-mode .paper-frame{box-shadow:0 24px 60px rgba(0,0,0,.45)}.paper-frame-bar{display:flex;align-items:center;gap:8px;height:34px;padding:0 12px;border-bottom:1px solid var(--border-soft);background:var(--paper-bg-alt);font-family:var(--font-mono);font-size:11px;color:var(--ink-muted)}.paper-frame-bar .dots{display:flex;gap:5px}.paper-frame-bar .dots i{width:8px;height:8px;border-radius:50%;background:var(--border-color)}.paper-frame-bar .url{flex:1;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-frame img{display:block;width:100%;height:auto}.paper-hero-shot .paper-frame{width:112%}.paper-proof{margin-top:64px;padding-top:18px;padding-bottom:18px;border-top:1px solid var(--border-color);display:flex;flex-wrap:wrap;gap:12px 30px;align-items:center}.paper-proof-venues{display:flex;gap:22px;align-items:center;font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;color:var(--ink-soft)}.paper-proof-venues i{font-style:normal;font-family:var(--font-sans);font-size:14px;color:var(--ink-faint)}.paper-stats{margin-top:12px;padding-top:22px;border-top:1px solid var(--border-color)}.paper-stats-meta{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-muted);margin-bottom:14px}.paper-stats-meta-right{display:inline-flex;align-items:center;gap:6px}.paper-stats-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--status-ready)}.paper-stats-grid{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color)}.paper-stat-card{display:flex;flex-direction:column;padding:22px 26px 20px 0;margin-right:26px;border-right:1px solid var(--border-color);min-width:0}.paper-stat-card:last-child{border-right:0;margin-right:0;padding-right:0}.paper-stat-value{font-size:44px;font-weight:500;letter-spacing:-.04em;line-height:1;color:var(--color);font-variant-numeric:tabular-nums}.paper-stat-label{margin-top:6px;font-size:13px;color:var(--ink-soft)}.paper-stat-bars{display:block;width:100%;height:34px;margin-top:18px;overflow:visible}.paper-stat-bars rect{fill:var(--border-color)}.paper-stat-bars rect.is-latest{fill:var(--accent)}.paper-stat-delta{margin-top:10px;font-family:var(--font-mono);font-size:11px;color:var(--status-ready)}.paper-section-head{margin-top:110px;display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:end}.paper-qa h2,.paper-section-head h2{font-family:var(--font-serif);font-weight:400;font-size:clamp(2rem,4.2vw,3.25rem);line-height:1.05;letter-spacing:-.02em;margin:10px 0 0;color:var(--color);text-wrap:balance}.paper-feature-title em,.paper-qa h2 em,.paper-section-head h2 em{font-style:italic;color:var(--accent)}.paper-section-head p{font-size:15px;color:var(--ink-soft);max-width:32em;margin:0;line-height:1.6}.paper-preview{margin-top:28px;display:grid;grid-template-columns:1fr 1fr;gap:14px}.paper-preview .pane{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);overflow:hidden;min-width:0}.paper-preview .pane-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-bottom:1px solid var(--border-soft)}.paper-preview .pane-label{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);white-space:nowrap}.paper-preview .pane-url{font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.paper-preview .pane-body{padding:16px 18px;font-family:var(--font-mono);font-size:13px;line-height:1.9;color:var(--ink-soft)}.paper-preview .pane-body strong{color:var(--color);font-weight:600}.paper-preview .tok{color:var(--ink-faint)}.paper-preview .redact{background:var(--accent-soft);color:var(--accent);border-radius:4px;padding:0 5px}.paper-features{margin-top:28px;padding-top:28px;border-top:1px solid var(--border-color);display:grid;grid-template-columns:5fr 7fr;gap:48px;align-items:start}.paper-features-list{display:flex;flex-direction:column}.paper-feature{border-left:2px solid var(--border-soft);padding:16px 0 16px 20px;transition:border-color .2s}.paper-feature.is-on{border-left-color:var(--color)}.paper-feature-tab{all:unset;display:block;width:100%;cursor:pointer;color:inherit;border-radius:4px}.paper-feature-tab:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.paper-feature-tab .paper-eyebrow{display:block;margin-bottom:6px}.paper-feature-title{display:block;font-family:var(--font-serif);font-size:24px;line-height:1.15;letter-spacing:-.015em;color:var(--ink-muted);transition:color .2s}.paper-feature-title em{color:inherit}.paper-feature-tab:hover .paper-feature-title,.paper-feature.is-on .paper-feature-title{color:var(--color)}.paper-feature.is-on .paper-feature-title em{color:var(--accent)}.paper-feature-more{display:none}.paper-feature.is-on .paper-feature-more{display:block}.paper-feature-more .paper-feature-title-m{display:none}.paper-feature-more p{font-size:15px;color:var(--ink-soft);line-height:1.55;margin:10px 0 12px;max-width:30em}.paper-features-stage{min-width:0}.paper-feature-panel{animation:paper-feature-in .25s ease-out}@keyframes paper-feature-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){.paper-feature-panel{animation:none}}.paper-crop{position:relative;height:380px;overflow:hidden;background:var(--paper-card)}.paper-crop img{position:absolute;left:0;top:0;max-width:none;height:auto;display:block}.paper-crop-anonymize img{width:150%;transform:translate(-1.5%,-8%)}.paper-crop-review img{width:160%;transform:translate(-1%,-8%)}.paper-crop-manage img{width:150%;transform:translate(-6%,-9%)}.paper-qa{margin-top:110px;display:grid;grid-template-columns:1fr 1.4fr;gap:40px;align-items:start}.paper-qa h2{margin-bottom:12px}.paper-qa .q{border-top:1px solid var(--border-color);padding:18px 0}.paper-qa .q:last-child{border-bottom:1px solid var(--border-color)}.paper-qa .q a{display:block;font-weight:500;font-size:15px;color:var(--color);margin-bottom:6px}.paper-qa .q a:hover{color:var(--accent);text-decoration:none}.paper-qa .q p{margin:0;font-size:14px;color:var(--ink-soft);line-height:1.6}@media (max-width:1100px){.paper-hero-shot .paper-frame{width:100%}}@media (max-width:991px){.paper-hero{grid-template-columns:1fr;gap:36px;padding-top:40px}.paper-qa,.paper-section-head{grid-template-columns:1fr;gap:16px}.paper-stats-grid{grid-template-columns:1fr 1fr}.paper-stat-card:nth-child(2n){border-right:0;margin-right:0;padding-right:0}.paper-stat-card:nth-child(-n+2){border-bottom:1px solid var(--border-color)}}@media (max-width:767px){.paper-hero,.paper-wrap{padding-left:20px;padding-right:20px}.paper-hero h1.hero-title{font-size:clamp(2.4rem,11vw,3.25rem)}.hero-subtitle{font-size:16px}.paper-cta-row .btn-hero{width:100%;justify-content:center}.paper-proof{margin-top:48px}.paper-stat-card{padding:18px 16px 16px 0;margin-right:16px}.paper-stat-value{font-size:40px}.paper-qa,.paper-section-head{margin-top:72px}.paper-preview{grid-template-columns:1fr}.paper-preview .pane-body{font-size:12px;line-height:1.8}.paper-features{display:flex;flex-wrap:wrap;gap:0;padding-top:0;border-top:0;margin-top:24px}.paper-feature,.paper-features-list,.paper-features-stage{display:contents}.paper-feature-tab{order:1;flex:1 1 0;width:auto;padding:12px 0 10px;border-bottom:2px solid var(--border-color);border-radius:0;text-align:left}.paper-feature.is-on .paper-feature-tab{border-bottom-color:var(--color)}.paper-feature-tab .paper-eyebrow{margin:0;font-size:11px;letter-spacing:.08em}.paper-feature-tab .paper-feature-title{display:none}.paper-feature-panel{order:2;width:100%;margin-top:20px;animation:none}.paper-crop{height:240px}.paper-crop-anonymize img{width:230%;transform:translate(-1.5%,-8%)}.paper-crop-review img{width:240%;transform:translate(-1%,-8%)}.paper-crop-manage img{width:220%;transform:translate(-3%,-9%)}.paper-feature-more{order:3;width:100%}.paper-feature.is-on .paper-feature-more{margin-top:18px}.paper-feature-more .paper-feature-title-m{display:block;font-size:28px;line-height:1.1}.paper-feature-more p{max-width:none}.paper-footer-inner>.paper-footer-brand{grid-column:1/-1}.paper-footer-foot{flex-direction:column;gap:6px}}.dropdown-menu{background-color:var(--paper-card,var(--main-bg-color))!important;color:var(--color)!important;border:1px solid var(--border-color)!important;border-radius:8px!important;box-shadow:0 12px 28px rgba(26,24,21,.08),0 2px 6px rgba(26,24,21,.05)!important;padding:6px!important;font-family:var(--font-sans);font-size:13px;min-width:11rem}.dropdown-menu .dropdown-item{color:var(--color)!important;padding:7px 10px!important;border-radius:5px;font-size:13px}.dropdown-menu .dropdown-item:focus,.dropdown-menu .dropdown-item:hover{background:var(--hover-bg-color)!important;color:var(--color)!important}.dropdown-menu .dropdown-divider{border-top-color:var(--border-color)!important;margin:4px 2px}.dropdown-menu .dropdown-header{color:var(--ink-muted);font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase}.body,.file-content,.markdown-body{background-color:var(--main-bg-color)!important;color:var(--color)!important}.markdown-body table tr{background-color:var(--main-bg-color);border-top:1px solid var(--border-color)}.markdown-body table tr:nth-child(2n){background-color:var(--paper-bg-alt)}.markdown-body table td,.markdown-body table th{border:1px solid var(--border-color)}.file-content{padding:4px 7px;text-align:left;min-height:100%}.badge{font-size:75%;font-weight:500;padding:3px 8px;border-radius:10px;vertical-align:middle}.warning-feedback{width:100%;margin-top:.25rem;font-size:80%;color:#dc8e35}.dropdown-item:focus,.dropdown-item:hover{background-color:var(--hover-bg-color)}.card-header .btn,.card-header .btn:focus,.card-header .btn:hover{border:none!important;background:0 0!important}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:var(--button-height);padding:7px var(--button-padding-x);font:500 var(--button-font-size)/var(--button-line-height) var(--font-sans);vertical-align:middle;max-width:100%;transition:background-color 120ms ease,border-color 120ms ease,color 120ms ease;color:var(--btn-color);border:1px solid var(--border-color);background:var(--paper-card);border-radius:var(--radius-button);white-space:nowrap}.btn:active,.btn:hover{color:var(--btn-color);border:1px solid var(--border-color);background-color:var(--hover-bg-color);box-shadow:none!important}.btn.btn-primary{color:var(--primary-color);background-color:var(--primary-bg)}.btn.btn-primary:active,.btn.btn-primary:hover{color:var(--primary-active-color);background-color:var(--primary-hover-bg)!important}.btn::placeholder{color:var(--color);opacity:1}.white_border{border:1px solid #fff;color:#fff}.leftCol{display:flex;position:relative;flex:0 0 auto;width:100%;z-index:9;flex-direction:column}.card,.leftCol,.sidePanel{background-color:var(--sidebar-bg-color);border:1px solid var(--border-color)}@media (min-width:768px){.leftCol{flex:0 0 310px;width:310px;height:100%}}.leftCol-search{padding:10px 12px 0}.tree-search-box{display:flex;align-items:center;gap:8px;background:var(--canvas-bg-color);border:1px solid var(--border-color);border-radius:8px;padding:7px 10px;transition:border-color .15s}.tree-search-box:focus-within{border-color:var(--ink-muted)}.tree-search-icon{font-size:12px;color:var(--ink-muted);flex-shrink:0}.tree-search-input{flex:1;border:none;outline:0;background:0 0;font-family:var(--font-sans);font-size:13px;color:var(--color);min-width:0}.tree-search-input::placeholder{color:var(--ink-muted)}.tree-search-kbd{font-family:var(--font-sans);font-size:11px;color:var(--ink-muted);background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:4px;padding:1px 6px;line-height:1.4;flex-shrink:0}.tree-search-clear{background:0 0;border:none;color:var(--ink-muted);font-size:16px;cursor:pointer;padding:0 2px;line-height:1}.tree-search-clear:hover{color:var(--color)}.leftCol-project-header{display:flex;align-items:baseline;justify-content:space-between;padding:12px 14px 4px;gap:8px}.project-name{font-family:var(--font-sans);font-size:13px;font-weight:600;color:var(--color);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.project-file-count{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);white-space:nowrap;flex-shrink:0}.files{position:relative;display:block;overflow:auto;height:100%;padding:4px 6px}.files ul{list-style:none;margin:0;padding:0}.files li ul{padding-left:8px;margin-left:7px;border-left:1px solid var(--border-color)}.files .folder>ul{display:none}.files .folder.open>ul{display:block}.files .file,.files .folder{position:relative;color:var(--ink-soft);cursor:pointer;word-break:break-all}.files a{display:flex;align-items:center;gap:4px;padding:1px 8px 1px 2px;font-family:var(--font-sans);font-size:13px;color:var(--ink-soft);border-radius:4px;text-decoration:none;line-height:1.5;position:relative}.files a:hover{background:var(--hover-bg-color);color:var(--color);text-decoration:none}.files:focus{outline:0}.files a.tree-focused{background:var(--hover-bg-color);color:var(--color)}.files .file.active>a{background:rgba(128,128,128,.15);color:var(--color)}.files .file.active>a .tree-icon-file{color:#5b7ba5}.files .file.active>a .tree-count{color:var(--ink-muted)}.tree-icon-file,.tree-icon-folder{display:inline-block;flex-shrink:0}.tree-icon-folder{width:14px;height:12px;color:var(--ink-muted);opacity:.75}.tree-icon-folder svg{display:block;width:100%;height:100%;fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:1.2;stroke-linejoin:round}.files .folder.open>a .tree-icon-folder{color:#a07d2e;opacity:.9}.files .folder.open>a .tree-icon-folder svg{fill-opacity:.12}.dark-mode .files .folder.open>a .tree-icon-folder{color:#c9a03a}.tree-icon-file{width:16px;font-size:14px;text-align:center;color:#5b7ba5}.dark-mode .files .file.active>a .tree-icon-file,.dark-mode .tree-icon-file{color:#7ba3d0}.tree-toggle{display:inline-flex;align-items:center;justify-content:center;width:10px;height:14px;flex-shrink:0;opacity:.6}.tree-toggle::after{content:"";display:block;width:0;height:0;border-style:solid;border-width:3px 0 3px 4.5px;border-color:transparent transparent transparent var(--ink-muted);transition:transform .12s ease}.files .folder.open>a .tree-toggle::after{transform:rotate(90deg)}.tree-spacer{display:inline-block;width:10px;flex-shrink:0}.tree-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-count{font-family:var(--font-sans);font-size:11px;color:var(--ink-muted);flex-shrink:0;margin-left:auto;padding-left:8px;opacity:.7}.files .folder>a{color:var(--color)}.files .file::before,.files .folder::before{display:none}.files .folder>a::after{display:none}.tree-search-path{color:var(--ink-muted);font-size:11px}.tree-search-empty{padding:16px 12px;color:var(--ink-muted);font-size:13px;font-family:var(--font-sans);text-align:center}.leftCol{background:var(--paper-bg-alt,#f3f1ec);border-right:1px solid var(--border-color)}.dark-mode .leftCol{background:var(--sidebar-bg-color)}.status-bar{background:var(--canvas-bg-color);border-bottom:1px solid var(--border-color);padding:10px 14px}.paths .breadcrumb-item,.paths .breadcrumb-item a{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);letter-spacing:.02em}.paths .breadcrumb-item.active,.paths .breadcrumb-item:last-child{color:var(--color)}.paths .breadcrumb-item+.breadcrumb-item::before{content:'/';color:var(--ink-muted)}.last-update{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);padding:8px 10px;border-top:1px solid var(--border-color);letter-spacing:.02em}.ace_editor{height:100%}.ace_scroller{cursor:default}.status-bar{display:flex;align-items:center;gap:12px;background-color:var(--canvas-bg-color);padding:10px 14px;margin:0;border-bottom:1px solid var(--border-color);border-radius:0;flex-shrink:0}.status-bar-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}.status-bar-actions .btn{display:inline-flex;align-items:center;gap:6px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);border-radius:6px;padding:4px 10px;font-size:13px;min-height:30px}.status-bar-actions .btn:hover{background:var(--hover-bg-color);color:var(--color)}.status-bar-actions .btn.btn-active{border-color:currentColor;font-weight:600}.paths{flex:1 1 auto;min-width:0;padding:0;margin:0;background-color:initial;border:none;border-radius:0;align-items:center;overflow-x:auto;white-space:nowrap;flex-wrap:nowrap}.paths::-webkit-scrollbar{display:none}.paths a{color:var(--color)}.paths .breadcrumb-item{padding-left:3px}.paths .breadcrumb-item::before{padding-right:3px}.content{height:100%;overflow:scroll;padding-top:42px;background-color:var(--main-bg-color);text-align:center}.image-content{max-width:100%;max-height:100%;text-align:center;padding:4px}.pdf-viewer{display:flex;flex-direction:column;height:100%;min-height:0;background-color:var(--main-bg-color)}.pdf-toolbar{display:flex;align-items:center;gap:4px;flex:0 0 auto;padding:4px 8px;border-bottom:1px solid var(--border-color,rgba(0,0,0,.12))}.pdf-toolbar-group{display:inline-flex;align-items:center;gap:4px}.pdf-toolbar-right{margin-left:auto}.pdf-toolbar-btn{border:0;background:0 0;color:inherit;padding:4px 8px;border-radius:4px;font-size:13px;line-height:1.2;cursor:pointer}.pdf-toolbar-btn:hover:not(:disabled){background-color:rgba(128,128,128,.18)}.pdf-toolbar-btn:disabled{opacity:.4;cursor:default}.pdf-toolbar-zoom{min-width:52px;font-variant-numeric:tabular-nums}.pdf-toolbar-page{width:3.5em;text-align:right;padding:2px 4px;font-size:13px;color:inherit;background:0 0;border:1px solid var(--border-color,rgba(0,0,0,.2));border-radius:4px}.pdf-toolbar-count{font-size:13px;opacity:.75;font-variant-numeric:tabular-nums}.pdf-pages{flex:1 1 auto;min-height:0;overflow:auto;padding:8px 0}.pdf-viewer-page{margin:0 auto 12px}.pdf-viewer-page canvas{display:block;box-shadow:0 1px 4px rgba(0,0,0,.25)}.pdf-viewer-error{padding:24px;text-align:center;font-weight:700}.html-doc{display:block;height:100%}.html-doc-frame{display:block;width:100%;height:100%;border:0;background-color:#fff}.file-error{font-weight:700;font-size:50px;text-align:center}pdfviewer{display:block;padding:15px}pdfpageviewer{display:block;width:100%;margin-bottom:10px}notebook{display:block;text-align:left;padding:15px}@media (min-width:768px){notebook{padding-left:100px}}.nb-output td,.nb-output th{border:1px solid var(--border-color)!important}.paper-faq{max-width:1180px;margin:0 auto;padding:48px 32px 80px;font-family:var(--font-sans)}.paper-faq-hero{padding-bottom:40px;border-bottom:1px solid var(--border-color);margin-bottom:36px}.paper-faq-eyebrow{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.14em;text-transform:uppercase;margin-bottom:32px}.paper-faq-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.6rem,5.5vw,4.6rem);line-height:1.03;letter-spacing:-.02em;color:var(--color);margin:0 0 22px;max-width:14em}.paper-faq-title em{font-style:italic;color:var(--color)}.paper-faq-lede{color:var(--ink-soft);font-size:18px;line-height:1.55;max-width:38em;margin:0}.paper-faq-lede a{color:var(--color);border-bottom:1px solid var(--color)}.paper-faq-lede a:hover{color:var(--accent);border-bottom-color:var(--accent)}.paper-faq-body{display:grid;grid-template-columns:220px 1fr;gap:60px;align-items:start}.paper-faq-toc{position:sticky;top:32px;font-size:14px}.paper-faq-toc-head{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.14em;text-transform:uppercase;padding-bottom:10px;margin-bottom:6px;border-bottom:1px solid var(--border-color)}.paper-faq-toc nav{display:flex;flex-direction:column}.paper-faq-toc nav a{color:var(--ink-soft);padding:8px 0 8px 14px;border-left:2px solid transparent;text-decoration:none;margin-left:-2px}.paper-faq-toc nav a.active,.paper-faq-toc nav a:hover{color:var(--color);border-left-color:var(--color)}.faq-section{min-width:0}.faq-section>div{width:100%}.faq-section h4.faq-cat{font-family:var(--font-serif);font-weight:400;font-size:clamp(1.7rem,2.6vw,2.2rem);letter-spacing:-.015em;color:var(--color);margin:0 0 6px;padding-bottom:14px;border-bottom:1px solid var(--border-color)}.faq-section h4.faq-cat i{display:none}.faq-section .panel-group{margin-bottom:56px}.faq-section .panel-default .panel-title a{display:block;position:relative;padding:22px 0 22px 42px;font-family:var(--font-serif);font-weight:400;font-size:20px;line-height:1.35;letter-spacing:-.005em;color:var(--color);text-decoration:none;cursor:pointer}.faq-section .panel-default .panel-title a:hover{color:var(--accent)}.faq-section .panel-heading{background:0 0;padding:0!important;border:none}.faq-section .panel-default{background:0 0;border:none;border-bottom:1px solid var(--border-soft);border-radius:0;margin-bottom:0!important;box-shadow:none}.faq-section .panel-default .panel-title{margin-bottom:0}.faq-section .panel-default .panel-title a:after{content:"−";position:absolute;left:0;right:auto;top:22px;transform:none;font-family:var(--font-mono);font-size:18px;line-height:1.35;color:var(--ink-muted);float:none;margin:0;font-weight:400}.faq-section .panel-default .panel-title a.collapsed:after{content:"+"}.faq-section .panel-default .panel-body{padding:0 0 22px 42px!important;color:var(--ink-soft);font-size:15px;line-height:1.65;border-top:none;max-width:42em}.table{color:var(--color)}.dropdown-divider,.table td,.table th,hr{border-color:var(--border-color)}.table.repositories td{word-break:break-word}loc{display:block;position:relative;width:100%;height:5px}loc .lang{float:left;height:100%}.rounded-circle.fa{display:inline-block;font-size:75px;height:140px;width:140px;border:4px solid;line-height:140px;text-align:center}.featurette-divider{margin:4.5rem 0;border-color:var(--border-color)}.featurette-heading{font-family:var(--font-serif);font-weight:400;font-size:2rem;line-height:1.15;letter-spacing:-.02em;margin-bottom:14px;color:var(--color)}.dark-mode .shadow{box-shadow:none}.dark-mode .overflow-auto::-webkit-scrollbar{width:10px}.dark-mode .overflow-auto::-webkit-scrollbar-track{-webkit-box-shadow:none;background:inherit;border-radius:0}.dark-mode .overflow-auto::-webkit-scrollbar-thumb{border-radius:10px;border:1px solid var(--border-color);background-color:var(--hover-bg-color)}.dark-mode .rgba-gradient{background:linear-gradient(135deg,#2a2d4a,#151728)}.section-number{padding-right:10px;font-weight:700;font-style:italic}.org-subscript-child{font-size:85%}.highlighted-line{position:absolute;background:rgba(100,200,100,.5);z-index:20}code,pre{font-family:"Fira Code","Courier New",Courier,monospace;line-height:1.1;color:var(--color)}.diff-file,.diff-index,.diff-lines{background:rgba(172,172,172,.5)}.diff-add{background:rgba(100,200,100,.5)}.diff-remove{background:rgba(200,100,100,.5)}.dashboard-title{font-size:1.375rem;font-weight:600;letter-spacing:-.01em}.quota-row{display:flex;gap:16px;margin-bottom:16px;padding:0}.quota-item{flex:1;min-width:0}.quota-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:4px}.quota-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;opacity:.7}.quota-value{font-size:.8125rem;font-weight:500;font-variant-numeric:tabular-nums}.quota-progress{height:6px;border-radius:3px;background:var(--hover-bg-color)}.quota-progress .progress-bar{border-radius:3px}.filter-chip{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;font-size:.8125rem;font-weight:500;background:var(--primary-bg);color:var(--primary-color);border-radius:12px;line-height:1.4}.filter-chip-close{display:inline-flex;align-items:center;justify-content:center;background:0 0;border:none;color:inherit;opacity:.7;cursor:pointer;padding:0;min-width:24px;min-height:24px;margin:-6px -6px -6px 0;font-size:1rem;line-height:1;border-radius:999px}.filter-chip-close:hover{opacity:1}.repo-list{list-style:none;padding:0;margin:0}.repo-list-item{display:flex;align-items:flex-start;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--border-color);gap:12px}.repo-list-item:first-child{border-top:1px solid var(--border-color)}.repo-list-item.repo-inactive{opacity:.6}.repo-list-item-content{flex:1;min-width:0}.repo-list-item-header{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}.repo-name{font-family:var(--font-mono);font-size:14px;font-weight:500;color:var(--color);word-break:break-word}.repo-name:hover{color:var(--accent)}.repo-source{font-size:.875rem;opacity:.8;display:flex;flex-wrap:wrap;align-items:center;gap:6px}.repo-date{opacity:.7}.repo-meta{display:flex;flex-wrap:wrap;gap:12px;margin-top:8px;font-size:.8125rem;opacity:.7}.repo-meta span{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}.repo-list-item-actions{flex-shrink:0;padding-top:2px;margin-left:14px}.repo-list-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:48px 20px;opacity:.5;font-size:.9375rem;list-style:none;border-bottom:1px solid var(--border-color);border-top:1px solid var(--border-color)}.repo-list-empty i{font-size:1.75rem}.type-badge{display:inline-flex;align-items:center;gap:3px;font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:4px;white-space:nowrap}.type-badge{font-family:var(--font-mono);letter-spacing:.06em;padding:2px 8px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-bg-alt,transparent)}.type-badge.type-pr,.type-badge.type-repo{background:var(--paper-bg-alt);color:var(--ink-muted);border-color:var(--border-color)}.dark-mode .type-badge.type-pr,.dark-mode .type-badge.type-repo{background:rgba(255,255,255,.06);color:var(--ink-muted);border-color:var(--border-color)}.type-badge.type-coauthor{background:rgba(99,102,241,.12);color:#4f46e5;border-color:rgba(99,102,241,.35);margin-left:6px}.dark-mode .type-badge.type-coauthor{background:rgba(129,140,248,.18);color:#a5b4fc;border-color:rgba(165,180,252,.45)}.btn-group .btn:not(.btn-primary){background:var(--hover-bg-color)}.anonymize-page{display:flex;flex-direction:column;min-height:0}.anonymize-landing{flex:1 1 0;min-height:min-content;display:flex;align-items:center;justify-content:center;padding:20px}.anonymize-landing-inner{width:100%;max-width:600px}.anonymize-landing-inner h2{font-weight:700}.anonymize-workspace{display:flex;flex-direction:column;flex:1 1 0;min-height:0;background:var(--canvas-bg-color)}.anonymize-topbar{background:var(--paper-card);border-bottom:1px solid var(--border-color);padding:18px 28px;flex:0 0 auto}.anonymize-topbar-inner{max-width:1400px;margin:0 auto}.anonymize-topbar-head{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.anonymize-topbar-title{margin:4px 0 0;font-size:clamp(1.8rem,3vw,2.4rem)}.anonymize-split{flex:1 1 auto;display:flex;min-height:0}.anonymize-form-col{flex:0 0 auto;width:100%;min-width:0;background:var(--canvas-bg-color);border-right:1px solid var(--border-color);overflow-y:auto}.anonymize-form-col .paper-settings-main{max-width:720px;padding:28px 32px 40px;margin:0}.anonymize-form-col .paper-settings-section:first-child{padding-top:0}.anonymize-form-col .anonymize-submit-bar{position:sticky;bottom:0;background:var(--canvas-bg-color);padding:14px 0 0;border-top:1px solid var(--border-color);margin-top:20px}.anonymize-form-col .anonymize-submit-bar .btn{width:100%;padding:12px 16px;font-size:15px}.anonymize-preview-col{flex:1 1 auto;min-width:0;overflow:auto;background:var(--paper-bg-alt);padding:28px 32px}.anonymize-preview-head{display:flex;align-items:baseline;gap:12px;padding-bottom:12px;border-bottom:1px solid var(--border-color);margin-bottom:20px}.anonymize-preview-head .paper-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.anonymize-preview-sub{color:var(--ink-soft);font-size:13px}.anonymize-preview-body{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:28px 32px}.form-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px}.form-grid-3{display:grid;grid-template-columns:2fr 1fr 1fr;gap:16px}@media (max-width:700px){.form-grid-2,.form-grid-3{grid-template-columns:1fr}}.paper-optional{font-family:var(--font-sans);font-weight:400;color:var(--ink-muted);text-transform:none;letter-spacing:0;font-size:11px;margin-left:6px}.paper-plan-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.paper-plan-card{position:relative;display:block;padding:18px 20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease;margin:0}.paper-plan-card input[type=radio]{position:absolute;opacity:0;pointer-events:none}.paper-plan-card.selected{border-color:var(--color);box-shadow:0 0 0 2px var(--color) inset}.paper-plan-head{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.paper-plan-price{font-family:var(--font-serif);font-size:2rem;color:var(--color);margin:6px 0 4px}.paper-plan-price .paper-plan-per{font-family:var(--font-sans);font-size:12px;color:var(--ink-soft)}.paper-plan-desc{font-size:13px;color:var(--ink-soft)}.paper-plan-desc ul{padding-left:18px;margin:0}.paper-settings-copy{color:var(--ink-soft);font-size:13px;margin:0 0 14px}.anonymize-section-title{font-size:.875rem;font-weight:600;text-transform:uppercase;letter-spacing:.3px;margin:18px 0 8px 0;padding-bottom:6px;border-bottom:1px solid var(--border-color);opacity:.7}.anonymize-section-title i{margin-right:4px}.anonymize-submit-bar{padding:12px 0 4px 0}.anonymize-submit-bar .btn{font-size:1rem;padding:10px 20px;font-weight:600}.anonymize-page .form-check{padding-top:4px;padding-bottom:4px}.anonymize-page .form-check-label{font-weight:500}.anonymize-preview-col{min-height:0;min-width:0}.pr-diff pre{overflow-x:auto;max-width:100%}@media (min-width:768px){.anonymize-form-col{height:100%;width:520px;flex:0 0 520px}.anonymize-preview-col{height:100%;flex:1}}@media (max-width:767px){.anonymize-workspace{flex:0 0 auto;min-height:0}.anonymize-topbar{padding:14px 16px}.anonymize-topbar-title{font-size:1.5rem;margin-top:2px}.anonymize-split{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}.anonymize-form-col{width:100%!important;flex:0 0 auto!important;height:auto!important;border-right:none;border-bottom:1px solid var(--border-color);overflow:visible}.anonymize-form-col .paper-settings-main{padding:18px 16px 24px;max-width:none}.anonymize-form-col .anonymize-submit-bar{position:sticky;bottom:0;margin:20px -16px 0;padding:12px 16px;background:var(--canvas-bg-color);border-top:1px solid var(--border-color);z-index:10}.anonymize-form-col .anonymize-submit-bar .btn{width:100%;padding:12px 16px;font-size:15px}.anonymize-preview-col{width:100%;flex:0 0 auto;height:auto!important;max-height:none;padding:18px 16px 24px;overflow:visible}.anonymize-preview-body{padding:16px;border-radius:8px}.anonymize-preview-body .pr-diff pre,.anonymize-preview-body pre{overflow-x:auto;max-width:100%}.anonymize-landing-inner{padding:16px}}@media (min-width:1280px){.anonymize-form-col{width:620px;flex:0 0 620px}}@media (max-width:767px){.btn{min-height:38px;padding:6px 14px;font-size:14px}.btn-sm{min-height:34px;padding:5px 10px}.navbar .nav-link{padding:8px 12px}.navbar .nav-link .fa,.navbar .nav-link .fab{font-size:24px;line-height:32px}.container.page{padding-left:15px;padding-right:15px;max-width:100%}h1{font-size:1.5rem}h2{font-size:1.375rem}h3{font-size:1.125rem}.repo-list-item{flex-direction:column}.repo-list-item-actions{align-self:flex-start}.quota-row{flex-direction:column;gap:10px;padding:10px 12px}.dropdown-menu{position:absolute;right:0;left:auto}.form-group{margin-bottom:12px}.form-control{font-size:16px}.status-bar{gap:6px;padding:8px 10px}.status-bar .btn{font-size:12px;padding:4px 10px;min-height:32px}.status-bar .paths{font-size:12px;overflow-x:auto;flex-wrap:nowrap;white-space:nowrap;-webkit-overflow-scrolling:touch}.status-bar .paths::-webkit-scrollbar{display:none}.status-bar-actions{flex-shrink:0;gap:4px}#home .display-4{font-size:1.75rem}#home h4.subtext-header{font-size:1rem}.progress:not(.quota-progress){min-width:120px!important}.badge{font-size:70%}.col-2.input-group{width:100%!important;max-width:100%;flex:0 0 100%}.color-text-secondary span{display:inline-block;margin-bottom:2px}.floatingchat-container-wrap{display:none!important}.anonymize-landing{align-items:flex-start;padding-top:60px}.anonymize-page .container-fluid>.row{height:auto!important;min-height:100%}.anonymize-form-col{width:100%!important;flex:0 0 100%!important;border-right:none;border-top:1px solid var(--border-color)}.anonymize-preview-col{height:auto!important;max-height:none}.anonymize-section-title{font-size:.8125rem;margin:12px 0 8px 0}.anonymize-submit-bar{position:sticky;bottom:0;background:var(--sidebar-bg-color);padding:10px 0;margin:0 -12px;padding-left:12px;padding-right:12px;border-top:1px solid var(--border-color);z-index:10}.anonymize-submit-bar .btn{font-size:1rem;padding:12px 20px}.anonymize-page .form-check{padding-top:6px;padding-bottom:6px;min-height:36px}.anonymize-page .form-check-label{font-size:.9375rem}.anonymize-page .card-body{padding:12px}.pr-title{font-size:1.125rem}.pr-diff pre{font-size:.75rem}.pr-comment .mb-1 h5{font-size:1rem}.nav-tabs .nav-link{padding:8px 12px;font-size:.875rem}}@media (min-width:768px) and (max-width:991px){.container.page{max-width:100%;padding-left:15px;padding-right:15px}.leftCol{flex:0 0 250px;width:250px}}.explorer-page{display:flex;height:100%;width:100%;background:var(--canvas-bg-color);position:relative}.explorer-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;background:var(--canvas-bg-color)}.explorer-content{flex:1 1 auto;min-height:0;width:100%;overflow:auto}.leftCol-close,.leftCol-head{display:none}.leftCol-body{flex:1 1 auto;min-height:0;overflow:auto}.leftCol-foot{padding:8px 12px;border-top:1px solid var(--border-color);background:var(--paper-bg-alt)}.leftCol-foot .last-update{display:block;border-top:none;padding:0}.leftCol-backdrop{display:none}.paper-inline-warning{display:flex;align-items:flex-start;gap:8px;margin:8px;padding:10px 12px;background:rgba(138,107,30,.08);border:1px solid rgba(138,107,30,.35);border-radius:8px;color:var(--color);font-size:13px;line-height:1.4}.paper-inline-warning i{color:#8a6b1e;margin-top:2px;flex-shrink:0}.dark-mode .paper-inline-warning i{color:#ffd37a}@media (min-width:768px){.leftCol-body{padding:8px 4px}}.sidebar-toggle{display:none;align-items:center;gap:8px;position:fixed;bottom:18px;right:18px;z-index:1100;padding:10px 14px;background:var(--color);color:var(--canvas-bg-color);border:1px solid var(--color);border-radius:999px;font-family:var(--font-sans);font-size:13px;font-weight:500;box-shadow:0 6px 20px rgba(0,0,0,.15);cursor:pointer;outline:0}.sidebar-toggle:hover{background:var(--primary-hover-bg)}.sidebar-toggle:focus,.sidebar-toggle:focus-visible{outline:0;box-shadow:0 6px 20px rgba(0,0,0,.15)}@media (max-width:991px){.sidebar-toggle{display:inline-flex}.leftCol{position:fixed;top:0;left:0;bottom:0;width:min(86vw,340px);max-width:340px;z-index:1200;border:none;border-right:1px solid var(--border-color);transform:translateX(0);transition:transform .22s ease;box-shadow:4px 0 24px rgba(0,0,0,.18);background:var(--sidebar-bg-color)}.leftCol.collapsed{transform:translateX(-105%);box-shadow:none;pointer-events:none}.leftCol-head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--border-color);background:var(--paper-bg-alt)}.leftCol-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.leftCol-close{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:0 0;border:1px solid var(--border-color);border-radius:8px;color:var(--ink-muted);cursor:pointer}.leftCol-close:hover{color:var(--color);border-color:var(--color)}.leftCol-foot{display:block}.leftCol-body{padding:4px 0}.leftCol-backdrop{display:block;position:fixed;inset:0;background:rgba(0,0,0,.32);z-index:1150;backdrop-filter:blur(2px)}.dark-mode .leftCol-backdrop{background:rgba(0,0,0,.5)}}.admin-nav{display:flex;gap:4px;padding:4px;margin:8px 0 20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;flex-wrap:wrap}.admin-nav a{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;border-radius:7px;color:var(--ink-muted);text-decoration:none;font-family:var(--font-mono);font-size:12px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;transition:background-color .15s,color .15s;white-space:nowrap}.admin-nav a:hover{background:var(--paper-bg-alt);color:var(--color)}.admin-nav a.active{background:var(--color);color:var(--paper-card)}.admin-nav a i{font-size:12px;width:14px;text-align:center}@media (max-width:767px){.admin-nav{flex-direction:column}.admin-nav a{justify-content:flex-start}}.admin-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-bottom:16px}.admin-stat-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:8px;padding:10px 12px}.admin-stat-card .stat-value{font-family:var(--font-serif);font-size:1.375rem;font-weight:400;line-height:1.1;color:var(--color)}.admin-stat-card .stat-label{font-family:var(--font-mono);font-size:10px;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-top:4px}.admin-toolbar{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:16px 0 12px}.admin-toolbar .form-control{max-width:320px}@media (max-width:767px){.admin-toolbar .form-control{max-width:100%;width:100%}}.pagination-compact{display:inline-flex;align-items:center;gap:6px;flex-wrap:nowrap;font-family:var(--font-mono);font-size:12px;color:var(--ink-muted)}.pagination-compact input{width:60px;text-align:center;flex-shrink:0}.pagination-compact .btn{padding:4px 10px;min-height:34px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);flex-shrink:0}.pagination-compact>span{white-space:nowrap}.admin-list-item{display:flex;align-items:flex-start;gap:12px;padding:12px 0;border-bottom:1px solid var(--border-color)}.admin-list-item:last-child{border-bottom:none}.admin-list-item .item-main{flex:1;min-width:0}.admin-list-item .item-title{font-size:1rem;font-weight:600;margin-bottom:4px;word-break:break-word}.admin-list-item .item-meta{font-size:.875rem;opacity:.8}.admin-list-item .item-meta span{margin-right:12px;display:inline-block;margin-bottom:2px}.admin-list-item .item-actions{flex-shrink:0}@media (max-width:767px){.admin-list-item{flex-direction:column;gap:8px}.admin-list-item .item-actions{align-self:flex-start}}.status-badge{display:inline-block;font-size:11px;font-weight:600;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.3px;vertical-align:middle}.status-badge{font-family:var(--font-sans);font-weight:500;letter-spacing:.02em;border:1px solid transparent;text-transform:none;font-size:12px;padding:1px 9px 2px;border-radius:999px}.status-badge.status-ready{background:#e8f3ec;color:#2f7a44;border-color:#cfe6d6}.status-badge.status-error{background:#fbe7e7;color:#b42318;border-color:#f3c7c7}.status-badge.status-download,.status-badge.status-preparing{background:#eef0ff;color:#3b4ad6;border-color:#d4d8fa}.status-badge.status-expired,.status-badge.status-expiring,.status-badge.status-removed,.status-badge.status-removing{background:var(--paper-bg-alt);color:var(--ink-muted);border-color:var(--border-color)}.status-badge.status-active{background:#e8f3ec;color:#2f7a44;border-color:#cfe6d6}.queue-job-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 18px;margin-bottom:10px}.queue-job-card .job-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;flex-wrap:wrap;gap:8px}.queue-job-card .job-id{font-family:var(--font-mono);font-size:.9375rem;color:var(--color);word-break:break-all;display:flex;align-items:center;gap:10px}.queue-job-card .job-timestamps{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);display:flex;flex-wrap:wrap;gap:14px}.queue-job-card .job-actions{display:flex;gap:8px;margin-top:10px}.queue-job-card .job-actions .btn{font-size:12px;padding:4px 12px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}.admin-section-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:24px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--border-color)}.admin-section-header h2{margin:0;font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em}.admin-section-header h2 i{margin-right:6px;color:var(--ink-muted)}.admin-section-header .section-count{background:var(--paper-bg-alt);color:var(--ink-muted);border:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;padding:1px 10px;border-radius:999px}.user-detail-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:22px;margin-bottom:24px}.user-detail-card .user-header{display:flex;align-items:center;gap:16px;margin-bottom:18px;padding-bottom:16px;border-bottom:1px solid var(--border-color);flex-wrap:wrap}.user-detail-card .user-header img{border-radius:50%}.user-detail-card .user-header h2{margin:0;font-family:var(--font-serif);font-weight:400;font-size:1.5rem;display:flex;align-items:center;gap:10px;flex-wrap:wrap}.user-detail-grid{display:grid;grid-template-columns:160px 1fr;gap:10px 20px;font-size:.9375rem}.user-detail-grid .detail-label{font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);white-space:nowrap;padding-top:2px}.user-detail-grid .detail-value{word-break:break-all;color:var(--color)}@media (max-width:767px){.user-detail-grid{grid-template-columns:1fr}.user-detail-grid .detail-label{margin-top:8px;border-top:1px solid var(--border-color);padding-top:8px}.user-detail-grid .detail-label:first-child{border-top:none;margin-top:0;padding-top:0}}.paper-empty{min-height:calc(100vh - 60px);display:flex;align-items:center;justify-content:center;padding:40px 24px}.paper-empty-inner{max-width:560px;text-align:left}.paper-empty-title{font-family:var(--font-serif);font-weight:400;font-size:clamp(2.4rem,6vw,4.4rem);line-height:1.02;letter-spacing:-.025em;margin:18px 0 20px;color:var(--color)}.paper-empty-title em{color:var(--accent);font-style:italic}.paper-empty-lede{font-size:18px;line-height:1.55;color:var(--ink-soft);text-wrap:pretty}.paper-empty-cta{display:flex;gap:10px;margin-top:28px;flex-wrap:wrap}.paper-page{max-width:1100px;margin:0 auto;padding:32px 32px 80px}@media (max-width:991px){.paper-page,.paper-page.container,.paper-page.container-fluid{padding:20px 18px 60px!important}.paper-page .row{margin-left:0;margin-right:0}.paper-page .row>.w-100,.paper-page .row>[class*=col]{padding-left:0;padding-right:0}.navbar.navbar-expand-lg{padding:10px 18px!important}.navbar .navbar-collapse{margin:10px -18px 0;padding:12px 18px 16px;border-top:1px solid var(--border-color);background:var(--header-bg-color)}.navbar .navbar-nav{align-items:stretch!important;gap:2px}.navbar .navbar-nav .nav-item,.navbar .navbar-nav .nav-link{width:100%}.navbar .nav-link{display:flex!important;align-items:center;justify-content:flex-start;margin:0!important;padding:12px 4px!important;border-bottom:1px solid var(--border-color)!important;border-radius:0;font-size:14px;line-height:1.2;gap:0;min-height:44px;box-sizing:border-box}.navbar .nav-link:not(.btn-signin):not(.user-chip){color:var(--color)!important}.navbar .nav-link .fa,.navbar .nav-link .fab,.navbar .nav-link .fas,.navbar .nav-link i{display:inline-flex!important;align-items:center;justify-content:center;width:28px;flex-shrink:0;font-size:16px;line-height:1;margin:0 10px 0 0!important}.navbar .navbar-nav .nav-item:last-child .nav-link,.navbar .navbar-nav>.nav-link:last-child{border-bottom-color:transparent!important}.navbar .nav-link.active{color:var(--color)!important;border-bottom-color:var(--border-color)!important}.navbar .nav-item>.nav-link:not(.btn-signin):not(.user-chip)::before{content:'';display:inline-block;width:28px;margin-right:10px;flex-shrink:0}.navbar .nav-link.nav-icon::before,.navbar .nav-link:has(>i)::before{display:none!important}.navbar .nav-link.nav-icon{width:100%!important;height:auto!important;justify-content:flex-start!important;padding:12px 4px!important}.navbar .nav-link.nav-icon::after{content:attr(title);font-family:var(--font-sans);font-size:14px;color:var(--color)}.navbar .btn-signin{display:inline-flex!important;width:auto!important;align-self:flex-start;justify-content:center;height:40px;padding:0 18px!important;margin:14px 0 0!important;border-radius:8px;border-bottom:none!important}.navbar .btn-signin i{margin:0 8px 0 0!important;width:auto!important}.navbar .user-chip-wrap{width:100%}.navbar .user-chip{display:flex!important;width:100%;margin:0!important;padding:10px 4px!important;background:0 0!important;border:none!important;border-bottom:1px solid var(--border-color)!important;border-radius:0!important;justify-content:flex-start;gap:0;min-height:44px}.navbar .user-chip::before{display:none!important}.navbar .user-chip img{width:28px!important;height:28px!important;margin-right:10px!important;flex-shrink:0}.navbar .user-chip .user-chip-name{font-size:14px;font-weight:500;color:var(--color)!important}.navbar .user-chip.dropdown-toggle::after{margin-left:auto;color:var(--ink-muted)}.navbar .user-chip-wrap .dropdown-menu{position:static!important;float:none;width:100%;margin:0!important;padding:0!important;background:0 0!important;border:none!important;box-shadow:none!important;transform:none!important}.navbar .user-chip-wrap .dropdown-menu .dropdown-item{padding:10px 4px 10px 42px!important;font-size:14px;color:var(--ink-muted)!important;background:0 0!important;border-bottom:1px solid var(--border-color);text-align:left!important}.navbar .user-chip-wrap .dropdown-menu .dropdown-item:last-child{border-bottom:none}.navbar .user-chip-wrap .dropdown-divider{display:none}.navbar-brand{border-right:none!important;padding-right:0!important;margin-right:0!important}.dark-mode .navbar .navbar-collapse{background:var(--header-bg-color);border-top-color:var(--border-color)}.dark-mode .navbar .nav-link{border-bottom-color:var(--border-color)!important}.dark-mode .navbar-toggler .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Cpath stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' d='M4 8h22M4 15h22M4 22h22'/%3E%3C/svg%3E")!important}}.status-pill{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;background:var(--paper-bg-alt);border:1px solid var(--border-color);color:var(--ink-soft);font-family:var(--font-mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase}.paper-progress{position:relative;height:auto;background:0 0;margin:18px 0 8px}.paper-progress::before{content:"";display:block;height:8px;background:var(--paper-bg-alt);border-radius:999px}.paper-progress .paper-progress-bar{position:absolute;top:0;left:0;height:8px;background:var(--color);border-radius:999px;transition:width .4s ease;min-width:4px}.paper-progress .paper-progress-label{display:flex;justify-content:space-between;align-items:center;margin-top:10px;font-family:var(--font-mono);font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted)}.paper-progress .paper-progress-pct{color:var(--color)}.paper-progress.paper-progress-ready .paper-progress-bar{background:var(--color)}.paper-error-card{margin-top:18px;padding:20px 22px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-left:3px solid #c53030;border-radius:10px;color:var(--color)}.dark-mode .paper-error-card{border-left-color:#ff8b7b}.paper-ratelimit-card{margin-top:18px;padding:20px 22px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-left:3px solid #d69e2e;border-radius:10px;color:var(--color)}.dark-mode .paper-ratelimit-card{border-left-color:#f6e05e}.paper-ratelimit-head{display:flex;align-items:flex-start;gap:14px}.paper-ratelimit-head>i{font-size:18px;color:#d69e2e;margin-top:4px;flex-shrink:0}.dark-mode .paper-ratelimit-head>i{color:#f6e05e}.paper-ratelimit-card strong{color:var(--color)}.status-pill-ratelimit{border-color:#d69e2e;color:#d69e2e}.dark-mode .status-pill-ratelimit{border-color:#f6e05e;color:#f6e05e}.status-ratelimit{background:#d69e2e}.dark-mode .status-ratelimit{background:#f6e05e}.paper-error-head{display:flex;align-items:flex-start;gap:14px}.paper-error-head>i{font-size:18px;color:#c53030;margin-top:4px;flex-shrink:0}.dark-mode .paper-error-head>i{color:#ff8b7b}.paper-error-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted)}.paper-error-title{font-family:var(--font-serif);font-size:1.375rem;line-height:1.2;margin-top:2px;color:var(--color)}.paper-error-msg{margin:12px 0 0;font-size:14px;line-height:1.55;color:var(--ink-soft);font-family:var(--font-mono);word-break:break-word;white-space:pre-wrap}.paper-error-hints{margin:14px 0 0;padding-left:18px;color:var(--ink-soft);font-size:14px;line-height:1.6}.paper-error-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px}.paper-detail-grid{display:grid;grid-template-columns:160px 1fr;gap:10px 20px;margin-top:18px;font-size:.9375rem}.paper-detail-grid .detail-label{font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);white-space:nowrap;padding-top:2px}.paper-detail-grid .detail-value{word-break:break-all;color:var(--color)}.paper-detail-grid .detail-value a{color:var(--color);border-bottom:1px solid var(--border-color)}.paper-detail-grid .detail-value a:hover{border-bottom-color:var(--color)}@media (max-width:700px){.paper-detail-grid{grid-template-columns:1fr;gap:4px 0}.paper-detail-grid .detail-label{padding-top:8px}}.paper-support-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin:18px 0 28px}.paper-support-card{display:flex;flex-direction:column;gap:10px;padding:20px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;color:var(--color);text-decoration:none;transition:border-color .15s ease,transform .15s ease}.paper-support-card:hover{border-color:var(--color);text-decoration:none;color:var(--color)}.paper-support-card .paper-support-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);display:flex;align-items:center;gap:8px}.paper-support-card p{margin:0;font-size:14px;line-height:1.55;color:var(--ink-soft)}.paper-support-card .paper-support-cta{margin-top:auto;font-size:13px;font-weight:500;color:var(--color);display:inline-flex;align-items:center;gap:6px}.paper-support-card:hover .paper-support-cta i{transform:translateX(2px)}.paper-support-card .paper-support-cta i{transition:transform .15s ease}.pr-page{min-height:100%;background:var(--canvas-bg-color)}.pr-page-inner{padding-top:24px;padding-bottom:60px}.pr-header{margin:6px 0 18px}.pr-title{margin:4px 0 10px;font-size:clamp(1.6rem,3vw,2.4rem);line-height:1.15;word-break:break-word}.pr-header-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;font-family:var(--font-mono);font-size:12px;color:var(--ink-muted)}.pr-header-meta .pr-meta-item{display:inline-flex;align-items:center;gap:6px}.pr-header-meta .pr-meta-item i{color:var(--ink-muted)}.pr-body-card{margin:18px 0 24px;padding:20px 22px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px}.pr-body-card .paper-section-eyebrow{margin-bottom:12px}.paper-tabs{display:flex;flex-wrap:wrap;gap:0;border-bottom:1px solid var(--border-color);margin:18px 0 0}.paper-tabs .paper-tab{display:inline-flex;align-items:center;gap:8px;padding:10px 16px;background:0 0;border:0;border-bottom:2px solid transparent;font-family:var(--font-sans);font-size:14px;font-weight:500;color:var(--ink-muted);cursor:pointer;transition:color .15s ease,border-color .15s ease;margin-bottom:-1px}.paper-tabs .paper-tab:hover{color:var(--color)}.paper-tabs .paper-tab.active{color:var(--color);border-bottom-color:var(--color)}.paper-tabs .paper-tab i{color:inherit;opacity:.85}.paper-tabs .paper-tab:active,.paper-tabs .paper-tab:focus,.paper-tabs .paper-tab:focus-visible{outline:0;box-shadow:none}.paper-tabs .paper-tab:focus-visible{color:var(--color);background:var(--hover-bg-color);border-radius:6px 6px 0 0}.pr-diff{margin:16px 0 28px;display:flex;flex-direction:column;gap:16px}.diff-file-block{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;overflow:hidden}.diff-file-header{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--paper-bg-alt);border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:13px;color:var(--color)}.diff-file-icon{color:var(--ink-muted);flex-shrink:0}.diff-file-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-break:break-all}.diff-file-status{flex-shrink:0;padding:2px 8px;border-radius:999px;font-size:10px;letter-spacing:.12em;text-transform:uppercase;border:1px solid var(--border-color);color:var(--ink-muted);background:var(--paper-card)}.diff-file-status-added{color:#2f6b3e;border-color:rgba(47,107,62,.35);background:rgba(47,107,62,.08)}.diff-file-status-deleted{color:#a13a2e;border-color:rgba(161,58,46,.35);background:rgba(161,58,46,.08)}.diff-file-status-renamed{color:#8a6b1e;border-color:rgba(138,107,30,.35);background:rgba(138,107,30,.08)}.dark-mode .diff-file-status-added{color:#a7e2a7;border-color:rgba(167,226,167,.35);background:rgba(167,226,167,.08)}.dark-mode .diff-file-status-deleted{color:#ff8b7b;border-color:rgba(255,139,123,.35);background:rgba(255,139,123,.08)}.dark-mode .diff-file-status-renamed{color:#ffd37a;border-color:rgba(255,211,122,.35);background:rgba(255,211,122,.08)}.diff-file-table{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:13px;line-height:1.55;table-layout:fixed}.diff-file-table tr.diff-row{vertical-align:top}.diff-file-table .diff-gutter{width:48px;padding:0 10px;text-align:right;color:var(--ink-muted);background:var(--paper-bg-alt);border-right:1px solid var(--border-color);user-select:none;font-variant-numeric:tabular-nums;vertical-align:top;white-space:nowrap}.diff-file-table .diff-gutter-new{border-right:1px solid var(--border-color)}.diff-file-table .diff-sign{width:18px;padding:0 6px;text-align:center;color:var(--ink-muted);background:var(--paper-bg-alt);user-select:none;vertical-align:top}.diff-file-table .diff-code{padding:1px 12px;white-space:pre-wrap;word-break:break-word;color:var(--color)}.diff-row-hunk td{background:var(--paper-bg-alt)!important;color:var(--ink-muted);border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);font-size:12px;letter-spacing:.02em;padding-top:6px;padding-bottom:6px}.diff-row-hunk .diff-code{color:var(--ink-muted)}.diff-row-add .diff-code{background:rgba(47,107,62,.1);color:#1f4a2a}.diff-row-add .diff-gutter,.diff-row-add .diff-sign{background:rgba(47,107,62,.06);color:#2f6b3e}.diff-row-remove .diff-code{background:rgba(161,58,46,.1);color:#6e1f1a}.diff-row-remove .diff-gutter,.diff-row-remove .diff-sign{background:rgba(161,58,46,.06);color:#a13a2e}.dark-mode .diff-row-add .diff-code{background:rgba(167,226,167,.1);color:#c9f0c9}.dark-mode .diff-row-add .diff-gutter,.dark-mode .diff-row-add .diff-sign{background:rgba(167,226,167,.06);color:#a7e2a7}.dark-mode .diff-row-remove .diff-code{background:rgba(255,139,123,.1);color:#ffc9c0}.dark-mode .diff-row-remove .diff-gutter,.dark-mode .diff-row-remove .diff-sign{background:rgba(255,139,123,.06);color:#ff8b7b}@media (max-width:700px){.diff-file-table{font-size:12px}.diff-file-table .diff-gutter{width:36px;padding:0 6px}.diff-file-table .diff-sign{width:14px;padding:0 4px}.diff-file-table .diff-code{padding:1px 8px}}.pr-comments{list-style:none;margin:16px 0 28px;padding:0;display:flex;flex-direction:column;gap:12px}.pr-comment{padding:16px 18px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px}.pr-comment-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px;flex-wrap:wrap;padding-bottom:8px;margin-bottom:8px;border-bottom:1px solid var(--border-color)}.pr-comment-author{font-weight:600;color:var(--color);font-size:14px;display:inline-flex;align-items:center;gap:6px}.pr-comment-author i{color:var(--ink-muted);font-size:12px}.pr-comment-date{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);letter-spacing:.02em}.pr-comment-body{color:var(--color);font-size:14px;line-height:1.6}.pr-comment-body :last-child{margin-bottom:0}@media (max-width:700px){.pr-page-inner{padding-top:14px;padding-bottom:40px}.pr-body-card{padding:14px 14px;border-radius:8px}.pr-comment{padding:12px 14px}.paper-tabs .paper-tab{padding:10px 12px;font-size:13px}.pr-diff pre{font-size:12px;padding:12px}}.toast{background-color:var(--paper-card)!important;color:var(--color)!important;border:1px solid var(--border-color)!important;border-radius:10px!important;box-shadow:0 8px 24px rgba(0,0,0,.08);backdrop-filter:none;min-width:280px;max-width:calc(100vw - 24px)}.toast .toast-header{background:0 0!important;color:var(--color)!important;border-bottom:1px solid var(--border-color)!important;font-family:var(--font-sans);padding:10px 12px}.toast .toast-header strong{font-weight:600;color:var(--color);letter-spacing:.01em}.toast .toast-header .close{color:var(--ink-muted)!important;text-shadow:none;opacity:.9;font-size:1.25rem}.toast .toast-header .close:hover{color:var(--color)!important;opacity:1}.toast .toast-body{color:var(--color)!important;padding:12px;font-size:14px;line-height:1.45}.dark-mode .toast{box-shadow:0 8px 24px rgba(0,0,0,.5)}.paper-crumbs{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);letter-spacing:0}.paper-crumbs .here,.paper-crumbs a{color:var(--color);text-decoration:none}.paper-page-title{font-family:var(--font-sans);font-weight:600;font-size:32px;line-height:1.2;letter-spacing:-.035em;margin:8px 0 6px;color:var(--color)}.paper-page-title em{color:inherit;font-style:normal}.paper-page-lede{color:var(--ink-soft);font-size:16px;max-width:640px;line-height:1.55}.paper-meta-rule{display:flex;flex-wrap:wrap;gap:16px 24px;align-items:center;margin:22px 0 18px;padding:12px 0;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);letter-spacing:.02em}.paper-meta-rule b{color:var(--color);font-weight:500}.paper-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:10px;padding:20px 22px}.dark-mode .paper-card{background:var(--admin-stat-bg)}.paper-section-eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);font-weight:500;margin-bottom:10px;display:block}.paper-section-title{font-family:var(--font-serif);font-weight:400;font-size:22px;letter-spacing:-.01em;margin:0 0 8px}.paper-kbd{display:inline-block;font-family:var(--font-mono);font-size:11px;padding:1px 6px;border:1px solid var(--border-color);border-bottom-width:2px;border-radius:3px;background:var(--paper-card,#fff);color:var(--color)}.paper-pill{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:11px;padding:2px 8px;border:1px solid var(--border-color);border-radius:999px;color:var(--ink-soft);background:var(--paper-card,#fff);letter-spacing:.04em}.paper-pill.accent{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}.paper-pill.good{border-color:#2f6b3e;color:#2f6b3e}.paper-pill.warn{border-color:#8a6b1e;color:#8a6b1e}.paper-pill.bad{border-color:#a13a2e;color:#a13a2e}.paper-row{display:grid;grid-template-columns:1fr auto;gap:16px;align-items:start;padding:18px 0;border-bottom:1px solid var(--border-color)}.paper-row:first-child{border-top:1px solid var(--border-color)}.paper-row .row-title{font-family:var(--font-serif);font-size:20px;letter-spacing:-.01em;line-height:1.2}.paper-row .row-title a{color:var(--color)}.paper-row .row-meta{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.03em;margin-top:6px;display:flex;flex-wrap:wrap;gap:14px}.paper-row .row-actions{display:flex;gap:6px;flex-shrink:0}.paper-form-step{display:grid;grid-template-columns:48px 1fr;gap:20px;padding:22px 0;border-bottom:1px solid var(--border-color)}.paper-form-step:first-child{border-top:1px solid var(--border-color)}.paper-form-step .step-num{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);padding-top:4px;letter-spacing:.02em}.paper-form-step .step-title{font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;line-height:1.2;margin-bottom:4px}.paper-form-step .step-sub{color:var(--ink-soft);font-size:14px;margin-bottom:14px;text-wrap:pretty}.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}.paper-switch-row{display:flex;align-items:center;justify-content:space-between;padding:10px 0;gap:16px}.paper-switch-row+.paper-switch-row{border-top:1px dashed var(--border-soft)}.paper-switch-row .sw-label{font-weight:500}.paper-switch-row .sw-sub{display:block;font-size:13px;color:var(--ink-muted);margin-top:2px}.paper-page .custom-select,.paper-page .form-control{border-radius:6px;border-color:var(--border-color);font-size:14px;padding:8px 12px;height:auto;background:var(--input-bg);color:var(--input-color)}.paper-page .custom-select:focus,.paper-page .form-control:focus{color:var(--input-color);background:var(--input-bg);border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}.custom-select:focus,.form-control:focus,input:focus,select:focus,textarea:focus{color:var(--input-color)!important;background-color:var(--input-bg)!important}.form-control::selection,input::selection,textarea::selection{background:var(--accent-soft);color:var(--input-color)}.dark-mode .form-control::selection,.dark-mode input::selection,.dark-mode textarea::selection{background:rgba(167,178,255,.3);color:var(--input-color)}.btn.btn-ink{background:var(--color);color:var(--canvas-bg-color);border:1px solid var(--color);font-weight:500;border-radius:6px;padding:8px 14px}.btn.btn-ink:hover{background:var(--primary-hover-bg);color:var(--canvas-bg-color);border-color:var(--primary-hover-bg)}.btn.btn-accent{background:var(--accent);color:#fff;border:1px solid var(--accent);font-weight:500;border-radius:6px}.btn.btn-accent:hover{background:#2e3bb8;border-color:#2e3bb8;color:#fff}.paper-page .dashboard-filter-row{display:flex;align-items:center;gap:10px;padding:14px 0;border-top:1px solid var(--border-color);border-bottom:1px solid var(--border-color);margin:18px 0 0;flex-wrap:wrap}.paper-page .dashboard-filter-row .form-control{height:38px;background:var(--paper-card)}.paper-page .dashboard-filter-controls{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.paper-page .dashboard-filter-row .btn-group .btn,.paper-page .dashboard-filter-row .dropdown-toggle{background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);height:38px;padding:0 14px;font-size:14px;font-weight:500;border-radius:var(--radius-control,6px);display:inline-flex;align-items:center;gap:6px;line-height:1}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-label{color:var(--ink-muted);font-weight:500}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-label::after{content:"·";margin-left:6px;color:var(--ink-faint)}.paper-page .dashboard-filter-row .dropdown-toggle .filter-btn-value{font-weight:500}.paper-page .dashboard-filter-row .dropdown-toggle .fas{font-size:11px;color:var(--ink-muted)}.paper-page .dashboard-filter-row .btn-group .btn:hover,.paper-page .dashboard-filter-row .dropdown-toggle:hover{background:var(--hover-bg-color);border-color:var(--border-color);color:var(--color)}.paper-page .dashboard-filter-row .btn-group{gap:0}.paper-page .dashboard-filter-row .btn-group .btn{border-radius:0;border-right-width:0}.paper-page .dashboard-filter-row .btn-group .btn:first-child{border-top-left-radius:6px;border-bottom-left-radius:6px}.paper-page .dashboard-filter-row .btn-group .btn:last-child{border-top-right-radius:6px;border-bottom-right-radius:6px;border-right-width:1px}.paper-page .dashboard-filter-row .btn-group .btn.btn-primary{background:var(--color);color:var(--canvas-bg-color);border-color:var(--color)}.paper-page .dashboard-filter-row .search-wrap{flex:1 1 280px;min-width:220px}.paper-page .quota-row{display:grid;grid-template-columns:repeat(3,1fr);gap:40px;margin:24px 0 0;padding:0;background:0 0;border:none;border-radius:0}.paper-page .quota-label{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);opacity:1}.paper-page .quota-value{font-family:var(--font-mono);font-size:13px;font-variant-numeric:tabular-nums;color:var(--color)}.paper-page .quota-value .quota-unlimited-tag{font-family:var(--font-sans);font-size:12px;color:var(--ink-muted);margin-left:4px}.paper-page .quota-track{height:6px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:999px;overflow:hidden;width:100%;min-width:0}.paper-page .quota-track .quota-fill{height:100%;min-width:2px;border-radius:999px;background:var(--color);transition:width .3s ease}.paper-page .quota-track.quota-warn .quota-fill{background:var(--status-progress)}.paper-page .quota-track.quota-danger .quota-fill{background:var(--status-error)}.paper-page .quota-track.quota-unlimited{background:repeating-linear-gradient(-45deg,var(--paper-bg-alt) 0 4px,var(--border-soft,var(--border-color)) 4px 6px)}.paper-page .repo-list{margin-top:8px}.paper-page .repo-list-item{padding:20px 0;gap:16px;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-page .repo-list-item:first-child{border-top:none}.paper-page .repo-list-item-header{gap:10px;margin-bottom:6px}.paper-page .repo-name{font-family:var(--font-mono);font-size:15px;color:var(--color)}.paper-page .repo-source{font-size:13px;color:var(--ink-soft);opacity:1;gap:10px}.paper-page .repo-source a{color:var(--ink-soft);border-bottom:1px dotted var(--border-color)}.paper-page .repo-source a:hover{color:var(--color)}.paper-page .repo-meta{font-family:var(--font-mono);font-size:12px;color:var(--ink-muted);opacity:1;gap:16px;margin-top:10px}.paper-page .type-badge{font-family:var(--font-mono);font-size:11px;padding:3px 8px;letter-spacing:.1em}.paper-page .repo-list-item-actions .dropdown-toggle{background:var(--paper-card);border:1px solid var(--border-color);color:var(--color);border-radius:6px;font-size:13px;padding:6px 12px;height:auto}.paper-settings{max-width:1180px;margin:0 auto;padding:48px 32px 80px}.paper-settings .paper-page-title{max-width:14em}.paper-settings-body{display:grid;grid-template-columns:220px 1fr;gap:60px;margin-top:32px;border-top:1px solid var(--border-color);padding-top:28px}.paper-settings-toc{position:sticky;top:32px;align-self:start}.paper-settings-toc-head{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);margin-bottom:12px}.paper-settings-toc nav{display:flex;flex-direction:column;gap:2px}.paper-settings-toc nav a{padding:8px 0 8px 14px;border-left:2px solid transparent;color:var(--ink-soft);font-size:14px;text-decoration:none}.paper-settings-toc nav a.active,.paper-settings-toc nav a:hover{color:var(--color);border-left-color:var(--color)}.paper-settings-main{min-width:0;max-width:720px}.paper-settings-section{padding:28px 0;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-settings-section:first-child{padding-top:0}.paper-settings-section:last-of-type{border-bottom:none}.paper-settings-section .form-group{margin-bottom:18px}.paper-settings-section .form-check{padding:10px 0 10px 28px;border-bottom:1px solid var(--border-soft,var(--border-color))}.paper-settings-section .form-check:last-child{border-bottom:none}.paper-settings-section .form-check-label{font-family:var(--font-sans);font-size:14px;font-weight:500;color:var(--color);cursor:pointer}.paper-settings-section .form-check-input{cursor:pointer;width:16px;height:16px;margin-top:3px;margin-left:-28px;accent-color:var(--color)}.paper-settings-section .form-text{color:var(--ink-soft);font-size:13px;margin-top:4px;max-width:48em}.paper-settings-section .paper-section-copy{margin:-4px 0 14px}.paper-settings-footer{padding-top:24px;display:flex;flex-direction:column;gap:14px}.paper-settings-footer .btn-ink{align-self:flex-start}.paper-settings-actions{display:flex;align-items:center;flex-wrap:wrap;gap:14px}.paper-settings-hint{font-size:13px;color:var(--ink-muted)}.paper-inline-alert{display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:var(--radius-control,6px);border:1px solid var(--border-color);background:var(--paper-bg-alt);font-size:13px;color:var(--color)}.paper-inline-alert i{margin-top:2px;color:var(--ink-muted)}.paper-inline-alert-error{border-color:var(--status-error);background:var(--status-error-soft)}.paper-inline-alert-error i{color:var(--status-error)}.paper-account-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border:1px solid var(--border-color);border-radius:var(--radius-surface,10px);background:var(--paper-card);margin-bottom:20px}.paper-account-card img{flex-shrink:0}.paper-account-identity{flex:1 1 auto;min-width:0}.paper-account-name{font-family:var(--font-mono);font-size:14px;font-weight:500;color:var(--color)}.paper-account-meta{font-size:13px;color:var(--ink-muted);margin-top:2px}.paper-danger-zone{padding:16px;border:1px solid var(--status-error);border-radius:var(--radius-surface,10px);background:var(--status-error-soft)}.paper-danger-head{font-weight:600;font-size:14px;color:var(--status-error);margin-bottom:6px}.paper-danger-copy{font-size:13px;color:var(--ink-soft);max-width:52em;margin-bottom:12px}.paper-danger-zone .paper-inline-alert{margin-bottom:12px}.btn.btn-outline-danger{background:0 0;color:var(--status-error);border:1px solid var(--status-error);font-weight:500;border-radius:var(--radius-control,6px);padding:8px 14px}.btn.btn-outline-danger:hover{background:var(--status-error);color:#fff;border-color:var(--status-error)}.navbar .user-menu{min-width:260px}.navbar .user-menu .dropdown-item{display:flex;align-items:flex-start;gap:10px;padding:8px 10px!important}.navbar .user-menu .dropdown-item i{width:16px;margin-top:3px;font-size:13px;color:var(--ink-muted);text-align:center}.navbar .user-menu .dropdown-item span{display:flex;flex-direction:column;line-height:1.3}.navbar .user-menu .dropdown-item small{font-size:12px;color:var(--ink-muted)}.navbar .user-menu .dropdown-header{font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-muted);padding:8px 10px 6px}.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}@media (max-width:900px){.paper-settings-body{grid-template-columns:1fr;gap:24px}.paper-settings-toc{position:static}.paper-settings-toc nav{flex-direction:row;flex-wrap:wrap;gap:8px}.paper-settings-toc nav a{border-left:none;border-bottom:2px solid transparent;padding:4px 0}.paper-settings-toc nav a.active,.paper-settings-toc nav a:hover{border-bottom-color:var(--color);border-left:none}}.paper-table{display:block;margin-top:20px;background:0 0;border:none;border-top:1px solid var(--border-color);border-radius:0;overflow:visible}.paper-table .paper-table-head,.paper-table .paper-table-row{display:grid;grid-template-columns:minmax(280px,2.4fr) minmax(140px,1fr) 140px 90px 140px 52px;align-items:center;gap:18px;padding:12px 4px}.paper-table.paper-table-conferences .paper-table-head,.paper-table.paper-table-conferences .paper-table-row{grid-template-columns:minmax(280px,2.4fr) 140px 90px 220px 52px}.paper-table.paper-table-repos .paper-table-head,.paper-table.paper-table-repos .paper-table-row{grid-template-columns:minmax(280px,2.4fr) minmax(140px,1fr) 90px 140px 52px}.paper-table.paper-table-repos.has-bulk .paper-table-head,.paper-table.paper-table-repos.has-bulk .paper-table-row{grid-template-columns:28px minmax(280px,2.4fr) minmax(140px,1fr) 90px 140px 52px;gap:12px}.paper-table .admin-users-row{grid-template-columns:28px minmax(280px,2.4fr) 90px 120px 100px 52px!important;gap:12px!important}.paper-table.paper-table-dashboard .paper-table-head,.paper-table.paper-table-dashboard .paper-table-row{grid-template-columns:minmax(320px,3fr) 190px 90px 170px 52px;gap:20px;padding:8px 4px}.paper-table.paper-table-dashboard .cell-anon .anon-text{gap:2px}.paper-table.paper-table-dashboard .cell-anon .repo-name{padding:0}.paper-table .paper-table-head{background:0 0;border-bottom:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-muted);padding-top:8px;padding-bottom:8px}.paper-table .paper-table-head .sortable{cursor:pointer;user-select:none;display:inline-flex;align-items:center;gap:6px;background:0 0;border:none;padding:2px 0;margin:0;font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;border-radius:4px}.paper-table .paper-table-head .sortable:hover{color:var(--color)}.paper-table .paper-table-head .sortable.active{color:var(--color)}.paper-table .paper-table-head .sortable i{font-size:10px}.paper-table .paper-table-head .num,.paper-table .paper-table-row .num{text-align:right}.paper-table .paper-table-head .num .sortable{flex-direction:row-reverse}.paper-table .paper-table-row{border-top:1px solid var(--border-soft,var(--border-color));font-size:14px;transition:background-color .12s ease}.paper-table .paper-table-row:hover{background:var(--hover-bg-color)}.paper-table .paper-table-row.row-clickable{cursor:pointer}.paper-table .paper-table-row.repo-inactive>:not(.cell-actions){opacity:.55}.paper-table .paper-table-row.repo-error{background:var(--status-error-soft);border-left:2px solid var(--status-error);padding-left:6px}.paper-table .paper-table-row.repo-error:hover{background:var(--status-error-soft-hover)}.cell-status .status-sub.status-sub-error,.paper-table .cell-status .status-sub-error{color:var(--status-error)}.paper-table .cell-status .status-sub-warn{color:var(--status-progress)}.paper-table .cell-anon{display:flex;align-items:center;gap:12px;min-width:0}.paper-table .cell-anon .type-badge{flex-shrink:0;min-width:52px;justify-content:center}.paper-table .cell-anon .anon-text{min-width:0;display:flex;flex-direction:column;gap:3px}.paper-table .cell-anon .anon-title{display:flex;align-items:center;gap:8px;min-width:0}.paper-table .cell-anon .anon-title .type-badge{margin-left:0}.paper-table .cell-anon .repo-name{font-family:var(--font-mono);font-size:14px;color:var(--color);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;max-width:100%;padding:2px 0}.paper-table .cell-anon .repo-name-static{color:var(--ink-muted);font-style:italic}.paper-table .cell-anon .anon-sub{font-family:var(--font-sans);font-size:13px;color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:10px;min-width:0}.paper-table .cell-anon .anon-sub .anon-source{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-anon .anon-sub .conf-tag{flex:0 1 auto;min-width:72px;max-width:50%}.paper-table .cell-anon .anon-sub a{color:var(--ink-muted);border-bottom:1px dotted var(--border-color);padding:4px 0;margin:-4px 0}.paper-table .cell-anon .anon-sub a:hover{color:var(--color)}.paper-table .cell-anon .anon-sub .commit-hash{font-family:var(--font-mono);font-size:12px}.paper-table .cell-conf{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-conf span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-status{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;font-size:14px;color:var(--color);min-width:0}.paper-table .cell-status .status-line{display:inline-flex;align-items:center;gap:8px}.paper-table .cell-status .status-sub{flex-basis:100%;font-family:var(--font-sans);font-size:13px;line-height:1.3;color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-views{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--color)}.paper-table .cell-expires{font-family:var(--font-sans);font-size:13px;color:var(--ink-soft)}.paper-table .cell-expires .expires-never{color:var(--ink-muted)}.paper-table .cell-expires .expires-past{color:var(--ink-muted)}.paper-table .empty-dash{color:var(--ink-faint)}.paper-table .cell-actions{display:flex;justify-content:flex-end}.status-dot{width:8px;height:8px;border-radius:999px;display:inline-block;flex-shrink:0;background:var(--status-inactive)}.status-dot.status-ready{background:var(--status-ready)}.status-dot.status-error{background:var(--status-error)}.status-dot.status-preparing,.status-dot.status-progress{background:var(--status-progress)}.status-dot.status-expired,.status-dot.status-removed{background:var(--status-inactive)}.dashboard-page{padding-bottom:96px}.dashboard-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;margin-top:12px;font-size:13px;color:var(--ink-muted)}.dashboard-meta .dashboard-count{font-variant-numeric:tabular-nums}.btn-link-inline{background:0 0;border:none;padding:2px 4px;margin:-2px 0;font:inherit;color:var(--link-color);cursor:pointer;border-radius:4px}.btn-link-inline:hover{color:var(--link-hover-color);text-decoration:underline}.btn.btn-outline-ink{background:0 0;color:var(--color);border:1px solid var(--border-color);font-weight:500;border-radius:var(--radius-control,6px);padding:8px 14px}.btn.btn-outline-ink:hover{background:var(--hover-bg-color);color:var(--color);border-color:var(--ink-faint)}.paper-page .dropdown-menu .dropdown-item-danger,.paper-page .dropdown-menu .dropdown-item-danger i{color:var(--status-error)}.paper-page .dropdown-menu .dropdown-item-danger:focus,.paper-page .dropdown-menu .dropdown-item-danger:hover{background:var(--status-error-soft-hover);color:var(--status-error)}.paper-page .dropdown-menu button.dropdown-item{width:100%;text-align:left;background:0 0;border:none}.paper-page .dropdown-menu .dropdown-divider{border-top-color:var(--border-soft,var(--border-color));margin:4px 0}.paper-table .paper-table-skeleton{pointer-events:none}.skeleton{display:block;background:var(--paper-bg-alt);border-radius:4px;position:relative;overflow:hidden}.skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,var(--hover-bg-color),transparent);animation:skeleton-shimmer 1.4s infinite}.skeleton-line{height:12px}.skeleton-line+.skeleton-line{margin-top:8px}.skeleton-line-sm{height:10px}.skeleton-badge{width:52px;height:22px;border-radius:999px;flex-shrink:0}@keyframes skeleton-shimmer{to{transform:translateX(100%)}}@media (prefers-reduced-motion:reduce){.skeleton::after{animation:none}}.paper-table .paper-table-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:56px 20px;color:var(--ink-muted);text-align:center;border-top:1px solid var(--border-soft,var(--border-color))}.paper-table .paper-table-empty>i{font-size:1.5rem;color:var(--ink-faint)}.paper-table .paper-table-empty-actions{display:flex;gap:8px;margin-top:8px}.bulk-bar{display:flex;align-items:center;gap:8px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:8px;padding:8px 12px;margin:8px 0}.paper-page.admin-page .paper-page-title{font-size:1.5rem;margin:4px 0}.paper-page.admin-page .paper-page-lede{display:none}.paper-page.admin-page .paper-crumbs{margin-bottom:4px}.paper-page.admin-page .admin-nav{margin:6px 0 12px}.admin-filter-toolbar{display:flex;flex-direction:column;gap:6px;margin:8px 0 12px;position:sticky;top:0;z-index:4;background:var(--canvas-bg-color);padding:6px 0;border-bottom:1px solid var(--border-soft,var(--border-color));box-shadow:0 1px 0 var(--canvas-bg-color)}@media (max-width:900px){.admin-filter-toolbar{position:static;box-shadow:none}}.admin-filter-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.admin-filter-row .search-wrap{flex:1;min-width:220px}.admin-filter-row .search-wrap .form-control{height:34px}.admin-filter-inline{display:inline-flex;align-items:center;gap:4px;font-size:12px;color:var(--ink-muted)}.admin-filter-inline>label{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted);margin:0;white-space:nowrap}.admin-filter-inline .form-control-sm{height:28px;padding:2px 8px;font-size:12px}.admin-filter-inline input[type=search].form-control-sm,.admin-filter-inline input[type=text].form-control-sm{width:130px}.admin-filter-inline input[type=date].form-control-sm{width:130px}.admin-filter-inline select.form-control-sm{width:auto;min-width:88px}.admin-filter-inline .arrow{color:var(--ink-muted);font-size:11px}.admin-filter-spacer{flex:1}.admin-active-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;color:var(--ink-muted)}.admin-active-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 10px;border:1px solid var(--border-color);border-radius:999px;background:var(--hover-bg-color);font-size:12px;color:var(--color)}.admin-active-chip .key{color:var(--ink-muted);font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.admin-active-chip button{background:0 0;border:none;cursor:pointer;padding:0 4px;color:var(--ink-muted);font-size:12px;border-radius:50%}.admin-active-chip button:hover{color:#b42318}.admin-search-hint{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);pointer-events:none;border:1px solid var(--border-color);border-radius:4px;padding:1px 5px;background:var(--paper-card)}.admin-filter-row .search-wrap{position:relative}.admin-filter-row .search-wrap .form-control{padding-right:40px}.paper-table-row.row-selected{background:rgba(47,122,68,.06)}.queue-state-pills{display:inline-flex;gap:6px;margin-left:8px;flex-wrap:wrap}.queue-state-pills .pill{font-size:11px;padding:2px 8px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted)}.queue-state-pills .pill-active{color:#c48a2e;border-color:rgba(196,138,46,.4)}.queue-state-pills .pill-failed{color:#b42318;border-color:rgba(180,35,24,.4)}.queue-state-pills .pill-completed{color:#2f7a44;border-color:rgba(47,122,68,.3)}.queue-state-pills .pill-waiting{color:var(--ink-soft)}.queue-state-pills .pill-delayed{color:var(--ink-muted)}.admin-summary{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:8px 0 12px;font-size:13px}.admin-summary .summary-total{font-family:var(--font-serif);font-size:1rem;color:var(--color);margin-right:8px}.admin-summary .summary-meta{color:var(--ink-muted);font-size:12px;margin-right:8px}.admin-summary .summary-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted);cursor:pointer;font-size:12px;user-select:none;transition:background-color .1s,border-color .1s}.admin-summary .summary-pill:hover{background:var(--hover-bg-color)}.admin-summary .summary-pill .count{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--color)}.admin-summary .summary-pill.active{background:var(--primary-bg);color:var(--primary-color);border-color:var(--primary-bg)}.admin-summary .summary-pill.active .count{color:inherit}.admin-summary .summary-pill.error .count{color:#b42318}.admin-summary .summary-pill.warn .count{color:#c48a2e}.admin-summary .summary-pill.ok .count{color:#2f7a44}.dark-mode .admin-summary .summary-pill.error .count{color:#f08a82}.dark-mode .admin-summary .summary-pill.warn .count{color:#e8b970}.dark-mode .admin-summary .summary-pill.ok .count{color:#7dc894}.dark-mode .queue-state-pills .pill-active{color:#e8b970;border-color:rgba(232,185,112,.4)}.dark-mode .queue-state-pills .pill-failed{color:#f08a82;border-color:rgba(240,138,130,.4)}.dark-mode .queue-state-pills .pill-completed{color:#7dc894;border-color:rgba(125,200,148,.3)}.job-progress{flex:1;height:6px;background:var(--border-soft,var(--border-color));border-radius:999px;overflow:hidden;margin-left:12px;max-width:200px}.job-progress-bar{height:100%;background:#2f7a44;transition:width .3s}.paper-table-empty{padding:48px 4px;text-align:center;color:var(--ink-muted);display:flex;flex-direction:column;align-items:center;gap:8px;border-top:1px solid var(--border-soft,var(--border-color))}.paper-table-empty>i{font-size:1.5rem}.q-header{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:12px;margin-bottom:4px}.q-header-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.q-range-btns{display:inline-flex;border:1px solid var(--border-color);border-radius:8px;overflow:hidden}.q-range-btns .btn{border:none;border-radius:0;padding:5px 14px;font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);background:var(--paper-card)}.q-range-btns .btn+.btn{border-left:1px solid var(--border-color)}.q-range-btns .btn.active{background:var(--color);color:var(--paper-card)}.q-cards{display:flex;gap:10px;margin:16px 0;flex-wrap:wrap}.q-card{flex:1;min-width:140px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:14px 16px;cursor:pointer;transition:border-color .15s,box-shadow .15s}.q-card:hover{border-color:var(--ink-muted)}.q-card.selected{border-color:var(--color);box-shadow:0 0 0 1px var(--color)}.q-card-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.q-dot{width:8px;height:8px;border-radius:50%;background:#2f7a44;flex-shrink:0}.q-dot-red{background:#b42318}.dark-mode .q-dot{background:#7dc894}.dark-mode .q-dot-red{background:#f08a82}.q-card-name{font-family:var(--font-mono);font-size:12px;font-weight:500;color:var(--ink-muted);letter-spacing:.04em}.q-card-count{font-family:var(--font-serif);font-size:2rem;font-weight:400;line-height:1.1;color:var(--color);margin-bottom:6px}.q-card-sub{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.04em}.q-card-bar{height:4px;background:var(--border-color);border-radius:2px;overflow:hidden;margin-top:6px}.q-card-bar-fill{height:100%;background:#3b4ad6;border-radius:2px;transition:width .3s}.dark-mode .q-card-bar-fill{background:#a7b2ff}.q-detail{display:grid;grid-template-columns:1fr 320px;gap:16px;margin-bottom:20px}.q-throughput{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px}.q-chart-wrap{position:relative}.q-throughput canvas{width:100%;display:block}.q-chart-tooltip{position:absolute;pointer-events:none;background:var(--paper-card);border:1px solid var(--border-color);border-radius:6px;padding:6px 10px;font-family:var(--font-mono);font-size:11px;line-height:1.6;white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,.15);z-index:10}.q-chart-tooltip .q-tip-time{color:var(--ink-muted);margin-bottom:2px}.q-chart-tooltip .q-tip-completed{color:#3b4ad6}.q-chart-tooltip .q-tip-failed{color:#b42318}.dark-mode .q-chart-tooltip .q-tip-completed{color:#a7b2ff}.dark-mode .q-chart-tooltip .q-tip-failed{color:#f08a82}.q-chart-crosshair{position:absolute;top:0;width:1px;height:100%;background:var(--ink-muted);opacity:.3;pointer-events:none;z-index:9}.q-stats-panel{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px;display:flex;flex-direction:column}.q-section-label{font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--ink-muted);text-transform:lowercase;letter-spacing:.04em;margin-bottom:14px}.q-section-right{float:right;text-transform:uppercase;font-size:10px;letter-spacing:.08em}.q-legend-completed{color:#3b4ad6}.q-legend-failed{color:#b42318;margin-left:8px}.q-legend-exec{color:#b8860b;margin-left:8px;letter-spacing:2px}.dark-mode .q-legend-completed{color:#a7b2ff}.dark-mode .q-legend-failed{color:#f08a82}.dark-mode .q-legend-exec{color:#f5c842}.q-chart-tooltip .q-tip-exec{color:#b8860b}.dark-mode .q-chart-tooltip .q-tip-exec{color:#f5c842}.q-stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px 20px;flex:1}.q-stat-label{font-family:var(--font-mono);font-size:10px;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:2px}.q-stat-value{font-family:var(--font-serif);font-size:1.25rem;font-weight:400;color:var(--color);line-height:1.2}.q-stats-actions{display:flex;gap:8px;margin-top:16px;padding-top:14px;border-top:1px solid var(--border-color)}.q-stats-actions .btn{font-family:var(--font-mono);font-size:11px;padding:6px 14px;border-radius:8px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}.q-jobs{background:var(--paper-card);border:1px solid var(--border-color);border-radius:10px;padding:16px 20px}.q-jobs-header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:12px}.q-state-filters{display:inline-flex;gap:6px;flex-wrap:wrap}.q-state-toggle{cursor:pointer;display:inline-flex;align-items:center}.q-state-toggle input{display:none}.q-state-chip{font-family:var(--font-mono);font-size:11px;padding:3px 10px;border-radius:10px;border:1px solid var(--border-color);background:var(--paper-card);color:var(--ink-muted);transition:opacity .15s,background .15s;text-transform:capitalize}.q-state-toggle input:checked+.q-state-chip{opacity:1}.q-state-toggle input:not(:checked)+.q-state-chip{opacity:.4;text-decoration:line-through}.q-state-badge,.q-state-chip{font-family:var(--font-mono);font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.3px}.q-state-badge{display:inline-block;padding:2px 8px;border-radius:8px}.q-state-active{background:#dbeafe;color:#1d4ed8}.q-state-waiting{background:#fef3c7;color:#92400e}.q-state-delayed{background:#e0e7ff;color:#4338ca}.q-state-failed{background:#fee2e2;color:#b91c1c}.q-state-completed{background:#dcfce7;color:#166534}.dark-mode .q-state-active{background:rgba(59,130,246,.2);color:#93bbfd}.dark-mode .q-state-waiting{background:rgba(251,191,36,.15);color:#fbbf24}.dark-mode .q-state-delayed{background:rgba(99,102,241,.2);color:#a5b4fc}.dark-mode .q-state-failed{background:rgba(239,68,68,.2);color:#fca5a5}.dark-mode .q-state-completed{background:rgba(34,197,94,.15);color:#86efac}.q-cell-state{width:120px}.q-delay-hint{display:block;font-size:10px;color:var(--ink-muted);margin-top:2px;font-family:var(--font-mono)}.q-search-row{display:flex;align-items:center;gap:10px;margin-bottom:12px}.q-search-row .form-control{max-width:320px;height:34px}.q-auto-refresh{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);display:inline-flex;align-items:center;gap:6px;cursor:pointer;white-space:nowrap}.q-table{width:100%;border-collapse:collapse}.q-table thead th{font-family:var(--font-mono);font-size:10px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;padding:8px 10px;border-bottom:1px solid var(--border-color);text-align:left;white-space:nowrap}.q-table tbody tr{border-bottom:1px solid var(--border-color);transition:background .1s}.q-table tbody tr:hover{background:var(--paper-bg-alt)}.q-table tbody tr:last-child{border-bottom:none}.q-table tbody td{padding:10px 10px;font-size:.875rem;vertical-align:middle}.q-cell-id{font-family:var(--font-mono);font-size:12px;white-space:nowrap}.q-cell-id{display:flex;align-items:center;gap:8px}.q-chevron{font-size:10px;color:var(--ink-muted);transition:transform .15s;flex-shrink:0}.q-chevron-open{transform:rotate(90deg)}.q-cell-id a{color:var(--color)}.q-cell-payload{font-family:var(--font-mono);font-size:12px;color:var(--color)}.q-payload-detail{color:var(--ink-muted)}.q-cell-num{font-family:var(--font-mono);font-size:12px;text-align:center;white-space:nowrap}.q-cell-progress{min-width:120px}.q-progress-wrap{display:flex;align-items:center;gap:8px}.q-progress-bar{flex:1;height:6px;border-radius:3px;overflow:hidden;background-image:linear-gradient(to right,#3b4ad6 var(--pct,0),var(--border-color) var(--pct,0))}.dark-mode .q-progress-bar{background-image:linear-gradient(to right,#a7b2ff var(--pct,0),var(--border-color) var(--pct,0))}.q-progress-label{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);white-space:nowrap;min-width:32px;text-align:right}.q-cell-actions{text-align:right;white-space:nowrap}.q-cell-actions .btn{padding:3px 8px;font-size:11px;background:0 0;border:1px solid var(--border-color);color:var(--ink-muted)}.q-cell-actions .btn:hover{color:var(--color)}.q-row-failed{background:rgba(180,35,24,.04)}.dark-mode .q-row-failed{background:rgba(240,138,130,.06)}.q-error-detail{padding:4px 10px 12px;font-size:12px}.q-error-reason{color:#b42318;font-size:.8125rem;margin-bottom:4px}.dark-mode .q-error-reason{color:#f08a82}.q-toast-error{background:#fbe7e7;color:#b42318;border:1px solid #f3c7c7;border-radius:8px;padding:10px 16px;margin-bottom:12px;font-family:var(--font-mono);font-size:12px;cursor:pointer;display:flex;align-items:center;gap:8px}.dark-mode .q-toast-error{background:rgba(240,138,130,.1);color:#f08a82;border-color:rgba(240,138,130,.25)}.q-error-stack{font-size:.75rem;max-height:120px;overflow:auto;margin:4px 0 0;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:6px;padding:8px 10px}.q-row-expanded>td{border-bottom-color:transparent}.q-detail-row>td{padding:0 10px 14px!important;border-bottom:1px solid var(--border-color)}.q-job-detail{background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:8px;padding:14px 16px}.q-job-detail-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px 20px;margin-bottom:10px}.q-job-detail-item{display:flex;flex-direction:column;gap:2px}.q-job-detail-label{font-family:var(--font-mono);font-size:10px;font-weight:500;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em}.q-job-detail-value{font-family:var(--font-mono);font-size:12px;color:var(--color);word-break:break-all}.q-job-detail-value a{color:var(--color)}.q-job-detail-error{margin:8px 0}.q-job-detail-actions{display:flex;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid var(--border-color)}.q-job-detail-actions .btn{font-family:var(--font-mono);font-size:11px;padding:5px 12px;border-radius:6px;background:var(--paper-card);border:1px solid var(--border-color);color:var(--color)}@media (max-width:900px){.q-detail{grid-template-columns:1fr}.q-cards{flex-direction:column}.q-card{min-width:100%}.q-jobs-header{flex-direction:column;align-items:flex-start}.q-tabs{flex-wrap:wrap}.q-search-row{flex-wrap:wrap}.q-search-row .form-control{max-width:100%;flex:1}.q-table{font-size:12px}.q-header{flex-direction:column}.q-job-detail-grid{grid-template-columns:1fr 1fr}}@media (max-width:900px){.paper-table .paper-table-head{display:none}.paper-table .admin-users-row,.paper-table .paper-table-row,.paper-table.paper-table-conferences .paper-table-row,.paper-table.paper-table-repos .paper-table-row{display:flex!important;flex-wrap:wrap;align-items:center;gap:6px 12px;padding:14px 0;grid-template-columns:none!important}.paper-table .cell-anon{flex:1 1 calc(100% - 52px);min-width:0;order:1}.paper-table .cell-actions{flex:0 0 auto;order:2;align-self:center}.paper-table .cell-conf,.paper-table .cell-expires,.paper-table .cell-status,.paper-table .cell-views{order:3;font-size:13px;color:var(--ink-muted);display:inline-flex;align-items:center;gap:6px;text-align:left}.paper-table .cell-status{flex-wrap:nowrap;gap:6px;min-width:0}.paper-table .cell-status .status-sub{flex-basis:auto;font-size:13px;line-height:inherit}.paper-table .cell-status .status-sub::before{content:"·";margin-right:6px;color:var(--ink-faint)}.paper-table .cell-conf{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.paper-table .cell-views.num{text-align:left}.paper-table .paper-table-skeleton .cell-expires,.paper-table .paper-table-skeleton .cell-status,.paper-table .paper-table-skeleton .cell-views{flex:1 1 30%}.paper-table .cell-views::before{content:'\f06e';font-family:'Font Awesome 5 Free';font-weight:900;font-size:10px;color:var(--ink-muted)}.paper-table .cell-expires::before{content:'\f017';font-family:'Font Awesome 5 Free';font-weight:400;font-size:10px;color:var(--ink-muted)}.paper-table .cell-conf::before{content:'\f51c';font-family:'Font Awesome 5 Free';font-weight:900;font-size:10px;color:var(--ink-muted)}.paper-table-dashboard .cell-conf::before{content:none}.paper-table .cell-conf:has(.empty-dash),.paper-table .cell-expires:has(.empty-dash){display:none}.paper-table .cell-expires::before,.paper-table .cell-views::before{color:var(--ink-faint)}}.paper-page .dropdown-menu{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface,10px);box-shadow:0 12px 28px rgba(26,24,21,.08);padding:6px;min-width:220px;font-family:var(--font-sans);font-size:14px;color:var(--color);margin-top:6px}.paper-page .dropdown-menu .dropdown-header{font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-muted);padding:8px 10px 6px;font-weight:500}.paper-page .dropdown-menu .dropdown-item{border-radius:6px;padding:8px 10px;color:var(--color);font-size:14px;display:flex;align-items:center;gap:8px}.paper-page .dropdown-menu .dropdown-item:focus,.paper-page .dropdown-menu .dropdown-item:hover{background:var(--hover-bg-color);color:var(--color)}.paper-page .dropdown-menu .dropdown-item i{width:16px;font-size:13px;color:var(--ink-muted)}.paper-page .dropdown-menu .form-check.dropdown-item{padding:8px 10px 8px 36px!important;position:relative}.paper-page .dropdown-menu .form-check.dropdown-item .form-check-input{position:absolute;left:12px;top:50%;transform:translateY(-50%);margin:0;width:16px;height:16px;accent-color:var(--color);cursor:pointer}.paper-page .dropdown-menu .form-check.dropdown-item .form-check-label{font-family:var(--font-sans);font-size:14px;color:var(--color);font-weight:400;margin:0;cursor:pointer}.btn.btn-icon-dots{width:32px;height:32px;padding:0;border:1px solid transparent;background:0 0;color:var(--ink-muted);border-radius:6px;display:inline-flex;align-items:center;justify-content:center;line-height:1}.btn.btn-icon-dots:focus,.btn.btn-icon-dots:hover,.btn.btn-icon-dots[aria-expanded=true]{background:var(--hover-bg-color);border-color:var(--border-color);color:var(--color)}.btn.btn-icon-dots::after{display:none}.btn.btn-icon-dots i{font-size:15px}.paper-page .repo-list-empty{border-top:none;padding:56px 20px;color:var(--ink-muted)}.paper-page .filter-chip{background:var(--paper-bg-alt);color:var(--color);border:1px solid var(--border-color);font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:4px 10px;border-radius:999px}@media (max-width:768px){.paper-page .paper-page-title{font-size:clamp(1.4rem,6vw,1.9rem);margin:4px 0 2px}.paper-page .paper-page-lede{font-size:13px;line-height:1.4;margin-bottom:0}.paper-page .btn-ink{padding:6px 12px;font-size:13px}.paper-page .quota-row{grid-template-columns:1fr;gap:12px;padding:0;margin:16px 0 0}.paper-page .dashboard-filter-row{padding:10px 0;margin-top:12px;gap:8px}.paper-page .quota-header{flex-direction:row;align-items:baseline;gap:8px}.paper-page .quota-value{white-space:nowrap}.paper-page .dashboard-filter-controls{width:100%}.paper-page .dashboard-filter-controls .btn-group{flex:1 1 100%;display:flex}.paper-page .dashboard-filter-controls .btn-group .btn{flex:1;justify-content:center}.paper-page .dashboard-filter-controls .dropdown{flex:1 1 0;min-width:0}.paper-page .dashboard-filter-controls .dropdown .dropdown-toggle{width:100%;justify-content:space-between}.paper-page .dashboard-filter-controls .dropdown .filter-btn-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media (max-width:480px){.paper-page .paper-page-lede{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}}.empty-state{text-align:center;padding:40px 20px;opacity:.6}.empty-state i{font-size:2rem;margin-bottom:10px;display:block}.paper-page .progress{height:10px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:999px;overflow:hidden;margin-bottom:20px}.paper-page .progress .progress-bar{background:var(--color);color:var(--canvas-bg-color);font-family:var(--font-mono);font-size:10px;line-height:10px}.paper-page .progress .progress-bar.bg-success{background:var(--status-ready)!important}.paper-page .progress .progress-bar.bg-warning{background:var(--status-progress)!important;color:#fff}.paper-page .progress .progress-bar.bg-danger{background:var(--status-error)!important}.paper-page .progress .progress-bar.bg-dark{background:var(--ink-faint)!important}.paper-page .form>h5,.paper-page form>h5,.paper-page>h5{font-family:var(--font-mono);font-size:11px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-muted);margin:24px 0 8px}.paper-page .accordion .card{background:var(--paper-card,var(--sidebar-bg-color));border:1px solid var(--border-color);border-radius:8px!important;margin-bottom:8px;overflow:hidden}.paper-page .accordion .card+.card{border-top:1px solid var(--border-color)}.paper-page .accordion .card-header{background:0 0;border:none;padding:0}.paper-page .accordion .card-header .btn{color:var(--color)!important;font-family:var(--font-sans);font-weight:500;font-size:14px;padding:14px 18px!important;background:0 0!important;border:none!important;position:relative}.paper-page .accordion .card-header .btn::after{content:"+";position:absolute;right:18px;font-family:var(--font-serif);font-size:20px;color:var(--ink-muted)}.paper-page .accordion .card-header .btn:not(.collapsed)::after{content:"–"}.paper-page .accordion .card-body{padding:6px 18px 18px!important;border-top:1px solid var(--border-soft)}.paper-page .accordion .form-check-label{color:var(--color);font-weight:500;font-size:14px}.paper-page .accordion .form-text{color:var(--ink-muted);font-size:12px;margin-top:2px}.faq-section .panel-body code{font-family:var(--font-mono);font-size:13px;padding:1px 6px;background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:3px;color:var(--color)}.faq-section .panel-body a{color:var(--accent)}@media (max-width:820px){.paper-faq-body{grid-template-columns:1fr;gap:24px}.paper-faq-toc{position:static}}.paper-footer{margin-top:80px;padding:56px max(20px,calc((100vw - 1200px)/ 2)) 28px;border-top:1px solid var(--border-color);background:var(--canvas-bg-color);font-family:var(--font-sans)}.paper-footer-inner{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px}.paper-footer-brand .paper-footer-mark{font-family:var(--font-serif);font-size:22px;letter-spacing:-.01em;color:var(--color)}.paper-footer-brand .paper-footer-mark em{font-style:italic;font-weight:400}.paper-footer-tag{margin-top:10px;color:var(--ink-soft);font-size:14px;line-height:1.55;max-width:340px}.paper-footer-head{font-family:var(--font-mono);font-size:11px;color:var(--ink-muted);letter-spacing:.1em;text-transform:uppercase;margin-bottom:14px}.paper-footer-col a{display:block;color:var(--color);font-size:14px;padding:4px 0;text-decoration:none}.paper-footer-col a:hover{color:var(--accent)}.paper-footer-rule{height:1px;background:var(--border-color);margin:40px 0 16px}.paper-footer-foot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px;font-size:12px;color:var(--ink-muted);font-family:var(--font-mono);letter-spacing:.02em}.paper-footer-foot .paper-footer-meta{font-family:var(--font-serif);font-style:italic;font-size:14px;letter-spacing:0;color:var(--ink-soft)}@media (max-width:768px){.paper-footer-inner{grid-template-columns:1fr 1fr}}.file.folder.truncated .truncated-warning{color:#d39e00;margin-left:4px;font-size:.85em;flex-shrink:0}.file.folder.truncated>a{color:#d39e00}body{--bucket-error-bg:#FCEDED;--bucket-error-fg:#8A1F1F;--bucket-error-bd:#F0C4C4;--bucket-error-dot:#B53737;--bucket-warn-bg:#FFF3DF;--bucket-warn-fg:#7A4D00;--bucket-warn-bd:#F0D8A0;--bucket-warn-dot:#B07A2F;--bucket-info-bg:#EAF2EC;--bucket-info-fg:#2C5D3A;--bucket-info-bd:#C5DCCD;--bucket-info-dot:#5B8D6B;--card-shadow:0 1px 2px rgba(26, 24, 21, 0.04),0 4px 14px rgba(26, 24, 21, 0.05);--card-shadow-hover:0 1px 2px rgba(26, 24, 21, 0.06),0 8px 24px rgba(26, 24, 21, 0.08)}.dark-mode{--bucket-error-bg:rgba(255, 139, 123, 0.10);--bucket-error-fg:#FF8B7B;--bucket-error-bd:rgba(255, 139, 123, 0.28);--bucket-error-dot:#FF8B7B;--bucket-warn-bg:rgba(255, 211, 122, 0.10);--bucket-warn-fg:#FFD37A;--bucket-warn-bd:rgba(255, 211, 122, 0.28);--bucket-warn-dot:#FFD37A;--bucket-info-bg:rgba(152, 200, 168, 0.10);--bucket-info-fg:#98C8A8;--bucket-info-bd:rgba(152, 200, 168, 0.28);--bucket-info-dot:#98C8A8;--card-shadow:0 1px 2px rgba(0, 0, 0, 0.35),0 6px 18px rgba(0, 0, 0, 0.30);--card-shadow-hover:0 2px 4px rgba(0, 0, 0, 0.45),0 12px 28px rgba(0, 0, 0, 0.40)}.errors-page .errors-header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:4px 0 8px}.errors-page .errors-actions{display:flex;gap:6px}.errors-page .kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin:14px 0 18px}.errors-page .kpi-card{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 20px;display:flex;flex-direction:column;gap:4px;box-shadow:var(--card-shadow);transition:box-shadow 160ms ease,transform 160ms ease}.errors-page .kpi-card:hover{box-shadow:var(--card-shadow-hover)}.errors-page .kpi-label{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.errors-page .kpi-value{font-family:var(--font-serif);font-size:2.6rem;font-weight:400;line-height:1;margin-top:2px;color:var(--color)}.errors-page .kpi-card.kpi-error .kpi-value{color:var(--bucket-error-fg)}.errors-page .kpi-card.kpi-warn .kpi-value{color:var(--bucket-warn-fg)}.errors-page .kpi-card.kpi-info .kpi-value{color:var(--bucket-info-fg)}.errors-page .kpi-sub{font-size:.75rem;color:var(--ink-muted);font-style:italic}.errors-page .kpi-sub.up{color:var(--bucket-error-fg);font-style:normal}.errors-page .kpi-sub.down{color:var(--bucket-info-fg);font-style:normal}.errors-page .dropped-warn{color:var(--bucket-error-fg);font-weight:600;font-style:normal}.errors-page .volume-chart{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;margin-bottom:18px;box-shadow:var(--card-shadow)}.errors-page .volume-head{display:flex;justify-content:space-between;align-items:center;font-family:var(--font-mono);font-size:.6875rem;color:var(--ink-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}.errors-page .volume-legend{display:flex;gap:14px;align-items:center;text-transform:none;letter-spacing:0;font-family:var(--font-sans)}.errors-page .volume-legend .dot{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:5px;vertical-align:middle}.errors-page .dot.dot-error{background:var(--bucket-error-dot)}.errors-page .dot.dot-warn{background:var(--bucket-warn-dot)}.errors-page .dot.dot-info{background:var(--bucket-info-dot)}.errors-page .volume-bars{display:flex;align-items:flex-end;gap:4px;height:80px}.errors-page .volume-bar{display:flex;flex-direction:column-reverse;flex:1 1 0;min-width:6px;height:100%}.errors-page .volume-bar .seg{display:block;width:100%}.errors-page .seg.seg-error{background:var(--bucket-error-dot)}.errors-page .seg.seg-warn{background:var(--bucket-warn-dot)}.errors-page .seg.seg-info{background:var(--bucket-info-dot);opacity:.7}.errors-page .errors-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}.errors-page .seg-tabs{display:inline-flex;border:1px solid var(--border-color);border-radius:8px;overflow:hidden;background:var(--paper-card)}.errors-page .seg-tabs button{background:0 0;border:none;padding:6px 14px;font-size:.875rem;cursor:pointer;color:var(--color);font-family:var(--font-sans)}.errors-page .seg-tabs button+button{border-left:1px solid var(--border-color)}.errors-page .seg-tabs button.active{background:var(--primary-bg);color:var(--primary-color)}.errors-page .errors-toolbar .search-wrap{position:relative;flex:1 1 320px;min-width:220px}.errors-page .errors-toolbar .search-wrap input{width:100%;padding:6px 10px 6px 32px;border:1px solid var(--border-color);border-radius:8px;font-family:var(--font-mono);font-size:.8125rem;background:var(--input-bg);color:var(--input-color)}.errors-page .errors-toolbar .search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--ink-muted);font-size:.875rem}.errors-page .errors-toolbar .filter-count{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:.75rem;color:var(--ink-muted)}.errors-page .errors-toolbar .select-wrap{display:inline-flex;align-items:center;gap:6px}.errors-page .errors-toolbar .select-wrap label{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);margin:0}.errors-page .errors-toolbar .select-wrap select{background:var(--input-bg);color:var(--input-color);border:1px solid var(--border-color);border-radius:8px}.errors-page .errors-toolbar .autoref{display:inline-flex;gap:6px;align-items:center;font-size:.8125rem;color:var(--color);cursor:pointer;margin:0}.errors-page .btn-icon{width:32px;padding:4px 0}.errors-page .errors-list{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;overflow:hidden;box-shadow:var(--card-shadow)}.errors-page .errors-list-head,.errors-page .errors-row-main{display:grid;grid-template-columns:110px 100px 100px 1fr 60px 70px;gap:16px;padding:18px 20px;align-items:start}.errors-page .errors-list-head{padding:12px 20px;align-items:center}.errors-page .errors-list-head{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);border-bottom:1px solid var(--border-color);background:var(--paper-bg-alt)}.errors-page .errors-row{border-bottom:1px solid var(--border-soft)}.errors-page .errors-row:last-child{border-bottom:none}.errors-page .errors-row.open{background:var(--paper-bg-alt)}.errors-page .errors-row-main{cursor:pointer}.errors-page .errors-row-main:hover{background:var(--hover-bg-color)}.errors-page .col-when .when-rel{font-size:.9375rem;color:var(--color);font-variant-numeric:tabular-nums}.errors-page .col-when .when-abs{font-family:var(--font-mono);font-size:.75rem;color:var(--ink-muted);font-variant-numeric:tabular-nums;margin-top:2px}.errors-page .col-sev{display:flex;align-items:center;gap:8px}.errors-page .sev-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.errors-page .sev-dot.sev-error{background:var(--bucket-error-dot)}.errors-page .sev-dot.sev-warn{background:var(--bucket-warn-dot)}.errors-page .sev-dot.sev-info{background:var(--bucket-info-dot)}.errors-page .sev-label{font-family:var(--font-mono);font-size:.75rem;letter-spacing:.08em;color:var(--color)}.errors-page .pill-module{display:inline-block;font-family:var(--font-mono);font-size:.75rem;background:var(--paper-bg-alt);color:var(--ink-soft);border:1px solid var(--border-color);padding:3px 10px;border-radius:6px}.errors-page .col-msg .msg-code{font-family:var(--font-mono);font-size:1rem;color:var(--color);font-weight:600}.errors-page .col-msg .msg-context{color:var(--ink-muted);font-style:italic;font-size:.875rem;margin-left:10px}.errors-page .col-msg .msg-detail{color:var(--ink-soft);font-family:var(--font-mono);font-size:.8125rem;margin-left:10px}.errors-page .col-msg .msg-url{color:var(--ink-muted);font-family:var(--font-mono);font-size:.8125rem;margin-top:6px;word-break:break-all}.errors-page .count-pill{display:inline-block;padding:2px 8px;border-radius:4px;background:var(--primary-bg);color:var(--primary-color);font-family:var(--font-mono);font-size:.75rem;font-variant-numeric:tabular-nums}.errors-page .count-pill.count-pill-muted{background:0 0;color:var(--ink-muted);border:1px solid var(--border-color)}.errors-page .status-pill{display:inline-block;padding:2px 10px;border-radius:4px;font-family:var(--font-mono);font-size:.8125rem;font-variant-numeric:tabular-nums;border:1px solid transparent}.errors-page .status-pill.status-error{background:var(--bucket-error-bg);color:var(--bucket-error-fg);border-color:var(--bucket-error-bd)}.errors-page .status-pill.status-warn{background:var(--bucket-warn-bg);color:var(--bucket-warn-fg);border-color:var(--bucket-warn-bd)}.errors-page .status-pill.status-info{background:var(--bucket-info-bg);color:var(--bucket-info-fg);border-color:var(--bucket-info-bd)}.errors-page .errors-row-detail{padding:0 16px 16px}.errors-page .detail-tabs{display:flex;gap:4px;border-bottom:1px solid var(--border-color);margin-bottom:12px}.errors-page .detail-tabs button{background:0 0;border:0;border-bottom:2px solid transparent;padding:8px 12px;font-size:.875rem;cursor:pointer;color:var(--ink-muted);font-family:var(--font-sans)}.errors-page .detail-tabs button.active{color:var(--color);border-bottom-color:var(--color)}.errors-page .detail-body{display:grid;grid-template-columns:1fr 260px;gap:20px}.errors-page .detail-main pre{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;font-family:var(--font-mono);font-size:.875rem;line-height:1.7;color:var(--color);max-height:26em;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;margin:0;box-shadow:var(--card-shadow)}.errors-page .related-list{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;max-height:22em;overflow:auto;box-shadow:var(--card-shadow)}.errors-page .stack-pre{line-height:1.45;white-space:pre;overflow-x:auto}.errors-page .related-row{display:flex;gap:10px;padding:6px 12px;align-items:center;border-bottom:1px solid var(--border-soft);font-size:.8125rem}.errors-page .related-row:last-child{border-bottom:none}.errors-page .detail-actions{display:flex;gap:6px;align-items:center;margin-top:10px}.errors-page .copy-hint{font-size:.75rem;color:var(--bucket-info-fg)}.errors-page .detail-aside{background:var(--paper-card);border:1px solid var(--border-color);border-radius:12px;padding:18px;box-shadow:var(--card-shadow)}.errors-page .aside-block+.aside-block{margin-top:16px}.errors-page .aside-label{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted);margin-bottom:4px}.errors-page .aside-value{font-family:var(--font-serif);font-size:1rem;color:var(--color);word-break:break-word;line-height:1.3}.errors-page .aside-value.mono{font-family:var(--font-mono);font-size:.8125rem}.errors-page .aside-sub{color:var(--ink-muted);font-style:italic;font-size:.8125rem;font-family:var(--font-sans)}.errors-page .errors-pager{display:flex;justify-content:space-between;align-items:center;padding:10px 0;color:var(--ink-muted);font-size:.875rem}@media (max-width:720px){.errors-page .errors-list-head,.errors-page .errors-row-main{grid-template-columns:80px 80px 1fr 60px}.errors-page .errors-list-head .col-mod,.errors-page .errors-list-head .col-sev,.errors-page .errors-row-main .col-mod,.errors-page .errors-row-main .col-sev{display:none}.errors-page .detail-body{grid-template-columns:1fr}}.overview-page .ov-header{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:2px}.overview-page .ov-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-left:6px;vertical-align:middle}.overview-page .ov-dot-ok{background:#2f7a44}.overview-page .ov-dot-warn{background:#c48a2e}.overview-page .ov-dot-idle{background:var(--ink-muted)}.dark-mode .overview-page .ov-dot-ok{background:#7dc894}.dark-mode .overview-page .ov-dot-warn{background:#e8b970}.overview-page .ov-kpi-row{display:grid;grid-template-columns:repeat(5,1fr);gap:16px;margin:16px 0 22px}.overview-page .ov-kpi-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;display:flex;flex-direction:column;gap:4px;box-shadow:var(--card-shadow);transition:box-shadow 160ms ease}.overview-page .ov-kpi-card:hover{box-shadow:var(--card-shadow-hover)}.overview-page .ov-kpi-label{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.overview-page .ov-kpi-value{font-family:var(--font-serif);font-size:2.6rem;font-weight:400;line-height:1;margin-top:2px;color:var(--color)}.overview-page .ov-kpi-value.ov-val-warn{color:#b42318}.dark-mode .overview-page .ov-kpi-value.ov-val-warn{color:#f08a82}.overview-page .ov-kpi-sub{font-size:.75rem;color:var(--ink-muted);margin-top:2px}.overview-page .ov-daily-row{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:-4px 0 22px}.overview-page .ov-daily-card{background:var(--primary-bg);border:1px solid var(--border-color);border-radius:12px;padding:16px 20px;box-shadow:var(--card-shadow)}.overview-page .ov-daily-label{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-muted)}.overview-page .ov-daily-value{font-family:var(--font-serif);font-size:2.5rem;font-weight:400;line-height:1;color:var(--accent,#3b4ad6);margin-top:6px}.overview-page .ov-daily-sub{font-size:.75rem;color:var(--ink-muted);margin-top:5px}.dark-mode .overview-page .ov-daily-card{background:var(--paper-card)}.dark-mode .overview-page .ov-daily-value{color:#a7b2ff}.overview-page .ov-chart-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-bottom:18px}.overview-page .ov-chart-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px 14px;box-shadow:var(--card-shadow)}.overview-page .ov-chart-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}.overview-page .ov-chart-title{font-family:var(--font-serif);font-size:1rem}.overview-page .ov-chart-legend{font-family:var(--font-mono);font-size:.6875rem;color:var(--ink-muted);display:flex;align-items:center;gap:5px}.overview-page .ov-dot-legend{display:inline-block;width:8px;height:8px;border-radius:50%}.overview-page .ov-dot-accent{background:var(--accent,#3b4ad6)}.overview-page .ov-dot-ok-fill{background:#2f7a44}.overview-page .ov-dot-user-fill{background:#8b5e2e}.overview-page .ov-dot-new-user-fill{background:#c48a2e}.dark-mode .overview-page .ov-dot-ok-fill{background:#7dc894}.dark-mode .overview-page .ov-dot-user-fill{background:#d0a15f}.dark-mode .overview-page .ov-dot-new-user-fill{background:#e8b970}.overview-page .ov-spark-bars{display:flex;align-items:flex-end;gap:3px;height:140px}.overview-page .ov-spark-col{flex:1;display:flex;align-items:flex-end;height:100%;position:relative}.overview-page .ov-spark-fill{width:100%;background:var(--accent,#3b4ad6);border-radius:2px 2px 0 0;transition:height .3s ease;opacity:.55}.overview-page .ov-spark-fill-alt{background:#2f7a44}.overview-page .ov-spark-fill-user{background:#8b5e2e}.overview-page .ov-spark-fill-new-user{background:#c48a2e}.dark-mode .overview-page .ov-spark-fill-alt{background:#7dc894}.dark-mode .overview-page .ov-spark-fill-user{background:#d0a15f}.dark-mode .overview-page .ov-spark-fill-new-user{background:#e8b970}.overview-page .ov-spark-col:hover .ov-spark-fill{opacity:1}.overview-page .ov-spark-col.has-tip::after{content:attr(data-tip);position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%) translateY(4px);z-index:10;padding:6px 8px;border-radius:6px;background:var(--color);color:var(--canvas-bg-color);font-family:var(--font-mono);font-size:.6875rem;line-height:1;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 120ms ease,transform 120ms ease}.overview-page .ov-spark-col.has-tip::before{content:"";position:absolute;left:50%;bottom:calc(100% + 4px);transform:translateX(-50%) translateY(4px);z-index:10;border:5px solid transparent;border-top-color:var(--color);opacity:0;pointer-events:none;transition:opacity 120ms ease,transform 120ms ease}.overview-page .ov-spark-col.has-tip:hover::after,.overview-page .ov-spark-col.has-tip:hover::before{opacity:1;transform:translateX(-50%) translateY(0)}.overview-page .ov-spark-x{display:flex;justify-content:space-between;font-family:var(--font-mono);font-size:.6875rem;color:var(--ink-muted);margin-top:6px}.overview-page .ov-triple-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:18px}.overview-page .ov-panel-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 20px;box-shadow:var(--card-shadow)}.overview-page .ov-panel-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}.overview-page .ov-panel-title{font-family:var(--font-serif);font-size:1rem}.overview-page .ov-panel-meta{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted)}.overview-page .ov-panel-foot{margin-top:12px;padding-top:10px;border-top:1px solid var(--border-color);font-size:.8125rem}.overview-page .ov-panel-foot a{color:var(--link-color);text-decoration:none}.overview-page .ov-panel-foot a:hover{text-decoration:underline}.overview-page .ov-performance{min-width:0;margin:22px 0}.overview-page .ov-performance-head{align-items:center;flex-wrap:wrap;gap:12px 20px}.overview-page .ov-performance-head h2{margin:0 0 4px}.overview-page .ov-performance-note{margin:0 0 12px;font-size:12px;line-height:1.5;color:var(--ink-muted)}.overview-page .ov-performance-head .ov-performance-note{margin:0}.overview-page .ov-performance-window{display:flex;align-items:center;gap:8px;flex-shrink:0}.overview-page .ov-performance-window label{margin:0;font-size:12px;color:var(--ink-muted)}.overview-page .ov-performance-window select{width:auto}.overview-page .ov-performance-tabs{margin:0 0 16px;gap:4px}.overview-page .ov-performance-tabs .paper-tab{white-space:nowrap}.overview-page .ov-metrics-scroll{max-width:100%;overflow-x:auto;border:1px solid var(--border-color);border-radius:var(--radius-control,8px)}.overview-page .ov-metrics-table{width:100%;border-collapse:collapse;color:var(--color);font-size:13px;line-height:1.5;font-variant-numeric:tabular-nums}.overview-page .ov-metrics-table td,.overview-page .ov-metrics-table th{padding:10px 12px;border-bottom:1px solid var(--border-color);vertical-align:middle;white-space:nowrap}.overview-page .ov-metrics-table thead th{background:var(--paper-bg-alt);color:var(--ink-muted);font-size:12px;font-weight:500}.overview-page .ov-metrics-table .num{text-align:right}.overview-page .ov-metrics-table tr:last-child>*{border-bottom:0}.overview-page .ov-metrics-table thead tr:last-child>*{border-bottom:1px solid var(--border-color)}.overview-page .ov-metrics-table tbody tr:hover{background:var(--hover-bg-color)}.overview-page .ov-metrics-table .ov-metric-name{min-width:190px;max-width:280px;text-align:left;white-space:normal;overflow-wrap:anywhere;font-weight:500}.overview-page .ov-metrics-table small{display:block;margin-top:3px;font-size:11px;font-weight:400;color:var(--ink-muted)}.overview-page .ov-metrics-table small .ui-icon{width:12px;height:12px}.overview-page .ov-metric-warning-row{background:rgba(196,138,46,.07)}.overview-page .ov-metric-warn,.overview-page .ov-metrics-table small.ov-metric-warn{color:#946519}.dark-mode .overview-page .ov-metric-warn,.dark-mode .overview-page .ov-metrics-table small.ov-metric-warn{color:#e8b970}.overview-page .ov-performance-message{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:10px 12px;margin:0 0 16px;border:1px solid var(--border-color);border-radius:var(--radius-control,8px);font-size:13px;line-height:1.5}.overview-page .ov-performance-message span{flex:1;min-width:160px}.overview-page .ov-performance-warning{background:rgba(196,138,46,.07)}.overview-page .ov-performance-empty{margin:0;padding:24px 12px;text-align:center;font-size:13px;color:var(--ink-muted)}.overview-page .ov-performance-help{margin-top:16px;font-size:12px;line-height:1.6;color:var(--ink-muted)}.overview-page .ov-performance-help summary{width:fit-content;cursor:pointer}.overview-page .ov-performance-help p{margin:8px 0 0;max-width:80ch}@media (max-width:575px){.overview-page .ov-performance{padding:16px 12px}.overview-page .ov-performance-window{width:100%}.overview-page .ov-performance-window select{flex:1;min-width:0}.overview-page .ov-performance-tabs .paper-tab{flex:1;padding:10px 8px}}.overview-page .ov-stacked-bar{display:flex;height:18px;border-radius:6px;overflow:hidden;margin-bottom:14px}.overview-page .ov-bar-seg{min-width:2px;transition:width .4s ease}.overview-page .ov-bar-ready{background:#2f7a44}.overview-page .ov-bar-preparing{background:#c48a2e}.overview-page .ov-bar-error{background:#b42318}.overview-page .ov-bar-expired{background:#8a857c}.overview-page .ov-bar-removed{background:#b5afa2}.dark-mode .overview-page .ov-bar-ready{background:#7dc894}.dark-mode .overview-page .ov-bar-preparing{background:#e8b970}.dark-mode .overview-page .ov-bar-error{background:#f08a82}.dark-mode .overview-page .ov-bar-expired{background:#6b665e}.dark-mode .overview-page .ov-bar-removed{background:#4a453e}.overview-page .ov-bar-legend{display:flex;flex-direction:column;gap:6px}.overview-page .ov-legend-item{display:flex;align-items:center;gap:8px;font-size:.8125rem;text-decoration:none;color:inherit}.overview-page .ov-legend-item:hover{text-decoration:none;color:inherit}.overview-page .ov-swatch{display:inline-block;width:12px;height:12px;border-radius:3px;flex-shrink:0}.overview-page .ov-legend-n{margin-left:auto;font-family:var(--font-mono);font-size:.8125rem;font-weight:600}.overview-page .ov-error-bars{display:flex;flex-direction:column;gap:12px}.overview-page .ov-ebar-row{display:flex;align-items:center;gap:10px}.overview-page .ov-ebar-label{width:30px;flex-shrink:0;font-family:var(--font-mono);font-size:.75rem;text-transform:uppercase;color:var(--ink-muted)}.overview-page .ov-ebar-track{flex:1;height:10px;background:var(--hover-bg-color);border-radius:5px;overflow:hidden}.overview-page .ov-ebar-fill{height:100%;border-radius:5px;transition:width .4s ease}.overview-page .ov-ebar-error{background:#b42318}.overview-page .ov-ebar-warn{background:#c48a2e}.overview-page .ov-ebar-info{background:var(--accent,#3b4ad6)}.dark-mode .overview-page .ov-ebar-error{background:#f08a82}.dark-mode .overview-page .ov-ebar-warn{background:#e8b970}.overview-page .ov-ebar-n{width:36px;text-align:right;font-family:var(--font-mono);font-size:.8125rem;font-weight:600;flex-shrink:0}.overview-page .ov-routes-table{display:flex;flex-direction:column}.overview-page .ov-route-row{display:grid;grid-template-columns:1fr 60px 60px 60px;padding:8px 0;border-bottom:1px solid var(--border-soft,var(--border-color));text-decoration:none;color:inherit;font-size:.875rem;align-items:center}.overview-page .ov-route-row:last-child{border-bottom:none}.overview-page .ov-route-row:hover{background:var(--hover-bg-color);text-decoration:none;color:inherit}.overview-page .ov-route-head{font-family:var(--font-mono);font-size:.6875rem;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-muted);border-bottom:1px solid var(--border-color)}.overview-page .ov-route-head:hover{background:0 0}.overview-page .ov-route-name{font-family:var(--font-mono);font-size:.8125rem}.overview-page .ov-route-n{text-align:right;font-family:var(--font-mono);font-size:.8125rem}.overview-page .ov-route-lat{color:var(--ink-muted)}.overview-page .ov-n-bad{color:#b42318;font-weight:600}.dark-mode .overview-page .ov-n-bad{color:#f08a82}.overview-page .ov-services-card{background:var(--paper-card,#fff);border:1px solid var(--border-color);border-radius:12px;padding:18px 22px;box-shadow:var(--card-shadow);margin-bottom:18px}.overview-page .ov-services-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:16px}.overview-page .ov-services-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px}.overview-page .ov-svc{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid var(--border-soft,var(--border-color));border-radius:10px}.overview-page .ov-svc-dot{width:10px;height:10px;border-radius:50%;margin-top:3px;flex-shrink:0}.overview-page .ov-svc-info{display:flex;flex-direction:column;gap:1px;flex:1}.overview-page .ov-svc-name{font-family:var(--font-mono);font-size:.875rem;font-weight:600}.overview-page .ov-svc-meta{font-size:.75rem;color:var(--ink-muted)}.overview-page .ov-svc-detail{font-size:.75rem;color:var(--ink-muted);white-space:nowrap}@media (max-width:900px){.overview-page .ov-kpi-row{grid-template-columns:repeat(3,1fr)}.overview-page .ov-daily-row{grid-template-columns:repeat(3,1fr)}.overview-page .ov-triple-row{grid-template-columns:1fr}}@media (max-width:720px){.overview-page .ov-kpi-row{grid-template-columns:repeat(2,1fr)}.overview-page .ov-daily-row{grid-template-columns:1fr}.overview-page .ov-chart-row{grid-template-columns:1fr}.overview-page .ov-header{flex-direction:column;gap:8px}}@media (max-width:480px){.overview-page .ov-kpi-row{grid-template-columns:1fr 1fr}.overview-page .ov-services-grid{grid-template-columns:1fr 1fr}}.dashboard-heading-actions{display:flex;flex-direction:column;align-items:flex-end;gap:8px;padding-bottom:12px}.dashboard-heading-actions .btn{display:inline-flex;align-items:center;justify-content:center;gap:8px}.dashboard-connections-link{display:inline-flex;align-items:center;gap:7px;min-height:32px;color:var(--ink-muted);font-size:12px;line-height:1.5}.dashboard-connections-link:hover{color:var(--accent);text-decoration:none}.dashboard-connections-link:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:2px}@media (max-width:767px){.dashboard-heading-actions{align-items:flex-start;padding-bottom:0;gap:4px}.dashboard-connections-link{min-height:44px}}.paper-settings-connection-link{display:flex;align-items:center;gap:12px;padding:18px 0;color:var(--color);font-size:14px}.paper-settings-connection-link>i{color:var(--ink-muted);font-size:18px}.paper-settings-connection-link>span:first-of-type{flex:1}.paper-settings-connection-link small{display:block;margin-top:4px;color:var(--ink-muted);font-size:12px}.paper-settings-connection-link:hover{color:var(--accent);text-decoration:none}.paper-settings-connection-link:focus-visible{outline:2px solid var(--accent);outline-offset:4px}.connections-heading{margin-bottom:36px}.connections-heading .paper-page-title{font-size:clamp(38px,4.5vw,56px);margin-bottom:12px}.connections-heading .paper-page-lede{font-size:15px}.connections-grid{display:grid;grid-template-columns:1.15fr 1fr;gap:56px;align-items:start;border-top:1px solid var(--border-color);padding-top:28px}.connections-grid>section{min-width:0}.connection-title,.connection-top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.connection-top{margin-bottom:24px;min-height:28px}.connection-top>i{color:var(--ink-muted);font-size:24px}.connection-top>.fa-key{font-size:18px}.connection-title{margin-bottom:10px}.connections-page h2{font-family:inherit;font-size:22px;font-weight:600;letter-spacing:-.5px;margin:0}.connections-page p{font-size:14px;line-height:1.65;color:var(--ink-muted)}.connection-badge{background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:600;border-radius:5px;padding:4px 8px;white-space:nowrap}.connection-badge-muted{background:var(--paper-bg-alt);color:var(--ink-muted)}.connection-status{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-muted)}.connection-status::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}.connection-status.is-connected{color:var(--status-ready)}.connection-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin:22px 0 16px}.connections-page .btn{font-size:13px;padding:9px 13px;white-space:normal}.connection-revoke p,.connections-page .connection-help{font-size:12px}.connection-link{display:inline-flex;align-items:center;gap:8px;border:0;padding:0;background:0 0;color:var(--accent);font-size:13px;line-height:1.6;font-weight:500;text-align:left;cursor:pointer}.connection-link:hover{color:var(--link-hover-color);text-decoration:underline}.connection-link:disabled{color:var(--ink-muted);opacity:.65;cursor:not-allowed;text-decoration:none}.connection-link:focus-visible{outline:2px solid var(--accent);outline-offset:5px}.connection-installations{list-style:none;padding:0;margin:18px 0;border-top:1px solid var(--border-soft)}.connection-installations li{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 0;font-size:12px;border-bottom:1px solid var(--border-soft)}.connection-revoke{border-top:1px solid var(--border-soft);padding-top:18px;margin-top:24px}.connection-danger{color:var(--status-error)}.connection-resources{margin-top:36px;padding-top:28px;border-top:1px solid var(--border-color)}.connection-count{color:var(--ink-muted);font-size:12px}.connection-empty{display:flex;align-items:center;gap:18px;padding:20px 0;margin:22px 0}.connection-empty>i{font-size:22px;color:var(--ink-muted)}.connection-empty h3{font-size:14px;font-weight:600;margin:0 0 5px}.connection-empty p{font-size:12px;margin:0}.connection-empty>a{margin-left:auto;flex-shrink:0}.connection-resource{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:20px 0;border-bottom:1px solid var(--border-color)}.connection-resource-name{flex:1;min-width:180px;overflow-wrap:anywhere;font-size:14px}.connection-resource-name small{display:block;color:var(--ink-muted);font-size:11px;margin-top:5px}.connection-resource-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.connection-resource-actions .connection-help{flex-basis:100%}.connections-page .connection-footnote{display:flex;align-items:baseline;gap:10px;margin-top:22px;font-size:12px}@media (max-width:767px){.connections-grid{grid-template-columns:1fr;gap:28px}.connections-grid>section+section{border-top:1px solid var(--border-color);padding-top:28px}.connection-empty{flex-wrap:wrap}.connection-empty>div{flex:1;min-width:180px}.connection-empty>a{margin-left:40px}.connection-resource-actions{flex-basis:100%}}.paper-table-empty-actions .btn{display:inline-flex;align-items:center;justify-content:center;gap:6px}.paper-table-empty-actions .btn>i{font-size:1em;line-height:1;color:inherit;margin-right:0!important}.auth-back{display:inline-flex;align-items:center;gap:10px;font-size:13px;color:var(--ink-muted);margin-bottom:48px}.auth-layout{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:start}.auth-intro,.auth-options{padding-top:28px}.auth-title-mobile{display:none}.auth-intro .paper-page-title{font-size:clamp(48px,5vw,76px);line-height:1.06;margin:18px 0 24px}.auth-intro .paper-page-lede{max-width:330px}.auth-options{display:grid;gap:28px}.auth-options h2{font-family:inherit;font-size:22px;font-weight:600;margin:0 0 10px}.auth-options p{font-size:14px;line-height:1.65;color:var(--ink-muted)}.auth-options .auth-existing{font-size:12px;border-left:2px solid var(--accent);padding-left:14px;margin:0}.auth-permissions{padding:6px 0 12px;list-style:none;font-size:13px;line-height:1.6}.auth-permissions li{display:flex;gap:10px;margin:12px 0}.auth-permissions i{color:var(--status-ready);margin-top:4px}.auth-continue{display:flex!important;align-items:center;justify-content:space-between;min-height:48px;padding:12px 16px!important}.auth-options small{display:block;font-size:12px;text-align:center;color:var(--ink-muted);margin-top:12px}.auth-legacy{border-top:1px solid var(--border-color);padding-top:24px}.auth-legacy h2{font-size:16px;margin:0}.auth-legacy p{font-size:13px}.source-url-label-row{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;margin-bottom:8px}.source-url-label-row>.paper-field-label{font-family:var(--font-sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--color);margin-bottom:6px;display:block}.repo-access{margin:0}.repo-access-details{margin:12px 0 24px}.source-url-label-row .repo-access-switch{padding:2px}.source-url-label-row .repo-access-switch button{padding:6px 10px;font-size:12px;line-height:20px}.repo-access-main{display:flex;align-items:center;gap:10px;padding:0 0 8px;flex-wrap:wrap}.repo-access-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.repo-access-switch{display:inline-flex;gap:3px;padding:3px;border:1px solid var(--border-color);border-radius:7px;flex-wrap:wrap}.repo-access-switch button{border:0;border-radius:4px;background:0 0;color:var(--ink-muted);padding:7px 10px;font:inherit;font-size:12px;cursor:pointer}.repo-access-switch button[aria-pressed=true]{background:var(--accent-soft);color:var(--accent);opacity:1;cursor:default;font-weight:600}.repo-access-switch button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.repo-access-details .btn{font-size:12px;padding:8px 12px}.repo-access-note{display:flex;gap:6px 16px;flex-wrap:wrap;padding:0;color:var(--ink-muted);font-size:11px;line-height:1.6}@media (max-width:767px){.auth-page{max-width:480px}.auth-back,.auth-intro .paper-eyebrow,.auth-title-desktop{display:none}.auth-title-mobile{display:inline}.auth-layout{grid-template-columns:1fr;gap:24px}.auth-intro,.auth-options{padding-top:0}.auth-intro .paper-page-title{font-size:40px;line-height:1.15;margin:6px 0 12px}.auth-intro .paper-page-lede{max-width:none;font-size:14px;margin:0}.auth-options{gap:24px}.auth-options .connection-top{margin-bottom:16px}.auth-options h2{font-size:18px}.auth-options p{font-size:13px;margin-bottom:12px}.auth-permissions{padding:0;margin:14px 0 20px;font-size:12px}.auth-permissions li{margin:8px 0}.auth-continue{font-size:14px}.auth-legacy{padding-top:20px}.auth-legacy h2{font-size:15px}.auth-legacy .connection-link{min-height:44px}.repo-access-controls{width:100%;gap:12px}}.text-muted{color:var(--ink-muted)!important}.anonymize-review-note,.draft-status{font-size:13px;color:var(--ink-muted)}.anonymize-review-note{padding:12px 0;margin:0}.anonymize-preview-status{padding:20px 32px;color:var(--ink-muted)}.anonymize-preview-status p{margin:0 0 12px}@media (max-width:575px){.dashboard-page .paper-page-lede{margin-bottom:8px}.dashboard-page .dashboard-heading-actions{gap:4px}.dashboard-page .dashboard-connections-link{min-height:32px}.dashboard-page .dashboard-filter-row{margin-top:12px}.anonymize-preview-status{padding:16px}}.paper-page .paper-page-title{font-size:32px}.paper-page.admin-page .paper-page-title{font-size:28px}.paper-page .paper-page-lede{font-size:14px;line-height:1.5}.paper-settings{padding-top:32px}.paper-settings-body{grid-template-columns:180px minmax(0,1fr);gap:40px;margin-top:24px;padding-top:24px;border-top:0}.paper-settings-main .paper-settings-section{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);padding:20px;margin-bottom:16px}.anonymize-page .paper-section-eyebrow,.paper-settings .paper-section-eyebrow{font-family:var(--font-sans);font-size:16px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:-.01em;margin-bottom:12px}.paper-settings .paper-settings-toc-head{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.paper-settings-section .paper-section-copy{margin-top:0}.paper-settings .quota-row{gap:24px}.paper-settings .quota-header{flex-direction:column;align-items:flex-start;gap:4px}.paper-settings .quota-value{white-space:nowrap}.paper-settings .paper-settings-footer{padding:0 0 20px}.paper-settings .form-control{font-size:14px;border-color:var(--border-color);border-radius:var(--radius-control)}.paper-settings .form-check{padding-top:8px;padding-bottom:8px}.anonymize-page .btn-ink,.explorer-page .btn-ink,.paper-page .btn-ink{background:var(--accent);color:#fff;border-color:var(--accent)}.dark-mode .anonymize-page .btn-ink,.dark-mode .explorer-page .btn-ink,.dark-mode .paper-page .btn-ink{color:#1a1815}.anonymize-page .btn-ink:hover,.explorer-page .btn-ink:hover,.paper-page .btn-ink:hover{background:var(--accent);border-color:var(--accent);color:#fff;filter:brightness(.94)}.dark-mode .anonymize-page .btn-ink:hover,.dark-mode .explorer-page .btn-ink:hover,.dark-mode .paper-page .btn-ink:hover{color:#1a1815}.anonymize-page .btn-ink:active,.explorer-page .btn-ink:active,.paper-page .btn-ink:active{filter:brightness(.9)}input[type=date],select{color-scheme:light}.dark-mode input[type=date],.dark-mode select{color-scheme:dark}.draft-status{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;padding:10px 0}.draft-discard{padding:0;border:0;color:var(--accent);font:inherit;font-size:13px;text-decoration:underline;background:0 0;cursor:pointer}.conference-feedback{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px;margin-top:16px;background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);width:100%}.paper-table{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);margin-top:12px}.dashboard-page .paper-table-dashboard{width:100%}.paper-account-email{margin:20px 0}.paper-account-email .form-control{max-width:420px}.paper-settings .paper-account-email .btn[type=submit]{min-height:36px;padding:7px 18px;white-space:nowrap}.paper-table .paper-table-head{font-family:var(--font-sans);text-transform:none;letter-spacing:0;font-size:12px;background:var(--paper-bg-alt);border-radius:var(--radius-surface) var(--radius-surface) 0 0}.paper-table .paper-table-head,.paper-table .paper-table-row{padding-left:16px;padding-right:16px}.dashboard-page{--dashboard-space-xs:4px;--dashboard-space-sm:8px;--dashboard-space-md:12px;--dashboard-space-lg:16px;--dashboard-space-xl:24px;padding-top:var(--dashboard-space-xl)}.project-icon-definitions{position:absolute;width:0;height:0;overflow:hidden}.project-type-svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}.project-empty-icon{width:28px;height:28px;color:var(--ink-faint)}.project-type-icon{display:inline-flex;color:var(--ink-muted);flex-shrink:0}.dashboard-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--dashboard-space-lg)}.dashboard-heading .paper-page-title{font-size:28px;margin-bottom:var(--dashboard-space-xs)}.dashboard-heading .paper-page-lede{display:none}.dashboard-heading .btn{min-height:40px;display:inline-flex;gap:var(--dashboard-space-sm);align-items:center}.dashboard-new-mobile{display:none}.dashboard-quota{position:relative;margin:0;padding:0;border:0;background:0 0}.dashboard-quota>summary{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:5px 10px;border:1px solid var(--border-color);border-radius:6px;background:var(--paper-card);color:var(--color);font-family:var(--font-sans);font-size:12px;cursor:pointer;list-style:none;white-space:nowrap}.dashboard-quota>summary::-webkit-details-marker{display:none}.dashboard-quota>summary:hover,.dashboard-quota[open]>summary{background:var(--paper-bg-alt);border-color:var(--ink-faint)}.quota-summary-label{font-weight:600}.quota-summary-value{color:var(--ink-muted);font-variant-numeric:tabular-nums}.quota-summary-ring{width:18px;height:18px;flex-shrink:0;fill:none;stroke-width:2.5}.quota-ring-track{stroke:var(--border-color)}.quota-ring-fill{stroke:var(--accent);transform:rotate(-90deg);transform-origin:50% 50%}.quota-summary-ring.quota-warn .quota-ring-fill{stroke:var(--status-progress)}.quota-summary-ring.quota-danger .quota-ring-fill{stroke:var(--status-error)}.quota-ring-unlimited{stroke:var(--accent);stroke-width:1.7}.quota-summary-chevron{width:14px;height:14px;margin-left:2px;color:var(--ink-muted);transition:transform .15s ease}.dashboard-quota[open] .quota-summary-chevron{transform:rotate(180deg)}.dashboard-quota .quota-panel{position:absolute;z-index:25;top:calc(100% + 8px);left:0;width:344px;max-width:calc(100vw - 36px);padding:16px;border:1px solid var(--border-color);border-radius:12px;background:var(--paper-card);box-shadow:0 12px 36px rgba(26,24,21,.16)}.quota-panel-heading{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.quota-panel-heading h2{font:600 15px var(--font-sans);margin:0}.quota-panel-heading .project-close{margin:-8px -8px -8px 0}.paper-page .quota-usage{display:grid;grid-template-columns:1fr;gap:16px;margin:0;padding:0}.quota-usage .quota-item+.quota-item{padding-top:16px;border-top:1px solid var(--border-soft,var(--border-color))}.quota-usage .quota-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}.quota-usage .quota-label{display:inline-flex;align-items:center;gap:8px;font:500 12px var(--font-sans);letter-spacing:0;text-transform:none;color:var(--ink-muted)}.quota-usage .quota-label .project-type-svg{width:16px;height:16px}.quota-usage .quota-value{display:flex;align-items:baseline;gap:4px;font:12px var(--font-sans);font-variant-numeric:tabular-nums;color:var(--ink-muted);margin-bottom:8px}.quota-usage .quota-value strong{font-size:20px;line-height:1.2;font-weight:600;color:var(--color)}.quota-percent,.quota-usage .quota-unlimited-tag{margin-left:auto;padding:2px 6px;border-radius:4px;background:var(--paper-bg-alt);font:11px var(--font-sans);color:var(--ink-muted);white-space:nowrap}.quota-warn>.quota-header .quota-percent{color:var(--status-progress)}.quota-danger>.quota-header .quota-percent{color:var(--status-error)}.quota-usage .quota-track{height:5px;border:0;background:var(--paper-bg-alt)}.quota-usage .quota-track .quota-fill{min-width:0;background:var(--accent)}.quota-remaining{margin:6px 0 0;color:var(--ink-muted);font-size:11px}.quota-remaining.quota-limit{color:var(--status-error)}.quota-loading{font-size:12px;color:var(--ink-muted);margin:12px 0 0}.dashboard-page .dashboard-filter-row{display:flex;align-items:center;gap:var(--dashboard-space-md);padding:0;margin:var(--dashboard-space-lg) 0 0;border:0;background:0 0}.dashboard-page .dashboard-filter-row .search-wrap{flex:1 1 240px;min-width:0}.dashboard-page .dashboard-filter-row .form-control{height:40px;padding:8px 12px;font-size:13px;background:var(--paper-card)}.dashboard-page .dashboard-filter-controls{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--dashboard-space-sm)}.dashboard-extra-filters{display:flex;gap:var(--dashboard-space-sm)}.dashboard-filters-toggle{display:none}.dashboard-page .dashboard-filter-row .btn-group .btn{border:0;background:0 0;border-radius:6px;height:36px;padding:6px 10px}.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary{background:var(--accent-soft);color:var(--accent)}.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary:active,.dashboard-page .dashboard-filter-row .btn-group .btn.btn-primary:hover{background:var(--accent-soft)!important;color:var(--accent)}.dashboard-page .dashboard-filter-row .dropdown-toggle{height:36px;padding:6px 10px}.dashboard-type-filter .project-type-svg{display:none}.sort-direction{width:14px;height:14px}.sort-direction.ascending{transform:rotate(180deg)}.dashboard-page .dashboard-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--dashboard-space-sm) var(--dashboard-space-md);margin:var(--dashboard-space-md) 0 0;padding:0}.dashboard-view-tabs{display:flex;align-items:center;gap:var(--dashboard-space-lg)}.dashboard-view-short{display:none}.dashboard-view-tabs button{min-height:36px;border:0;border-bottom:2px solid transparent;border-radius:0;background:0 0;font:inherit;font-size:13px;color:var(--ink-muted);padding:4px 0}.dashboard-view-tabs button[aria-pressed=true]{color:var(--color);border-bottom-color:var(--color);font-weight:600}.attention-count{padding:1px 5px;margin-left:4px;border-radius:4px;background:var(--paper-bg-alt);font-size:11px;font-variant-numeric:tabular-nums}.attention-count.has-attention{color:var(--status-error)}.dashboard-count{margin-left:auto;font-size:12px;color:var(--ink-muted)}.dashboard-active-filters{display:flex;align-items:center;flex-wrap:wrap;gap:var(--dashboard-space-sm);width:100%}.dashboard-page .filter-chip{display:inline-flex;font-family:var(--font-sans);font-size:11px;text-transform:none;letter-spacing:0;border-radius:6px}.dashboard-page .filter-chip .project-type-svg{width:12px;height:12px;margin-left:4px}.dashboard-page .filter-chip-close{min-width:24px;min-height:24px;display:inline-flex;align-items:center;justify-content:center;margin:-2px -4px -2px 2px}.dashboard-page .btn-link-inline{color:var(--ink-muted);font-size:12px}.dashboard-page .paper-table-dashboard{margin-top:var(--dashboard-space-md);border-left:0;border-right:0;border-radius:0}.dashboard-page .paper-table-dashboard .paper-table-head,.dashboard-page .paper-table-dashboard .paper-table-row{grid-template-columns:minmax(280px,3fr) 180px 72px 148px 64px;gap:var(--dashboard-space-lg);padding:8px 12px}.dashboard-page .paper-table-dashboard .paper-table-head{border-radius:0}.dashboard-page .paper-table .cell-anon{gap:var(--dashboard-space-sm)}.dashboard-page .paper-table .anon-text{flex:1}.dashboard-page .paper-table .repo-name{font-family:var(--font-sans);font-size:14px;font-weight:600}.dashboard-page .paper-table .anon-sub{display:flex;align-items:center;gap:var(--dashboard-space-sm);min-width:0}.anonymous-id{font-size:12px;font-family:var(--font-mono);color:var(--ink-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dashboard-page .paper-table .type-badge{font-family:var(--font-sans);letter-spacing:0;font-size:11px;text-transform:none;border-radius:6px}.dashboard-page .paper-table .status-sub{font-size:12px}.dashboard-page .paper-table .cell-conf{font-family:var(--font-sans);letter-spacing:0;text-transform:none}.dashboard-page .conf-tag .project-type-svg{width:14px;height:14px}.dashboard-row-meta{display:contents}.dashboard-page .cell-actions{gap:0;align-items:center}.copy-link-inline,.dashboard-page .btn-icon-dots,.project-close{display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:6px;background:0 0;color:var(--ink-muted);padding:0;width:32px;height:32px;min-width:32px;cursor:pointer}.copy-link-inline:focus-visible,.copy-link-inline:hover,.dashboard-page .btn-icon-dots:hover,.project-close:hover{background:var(--accent-soft);color:var(--accent)}.copy-link-inline:disabled{opacity:.5;cursor:wait}.mobile-meta-icon{display:none}.project-attention-action{display:inline-block;color:var(--accent);border:0;background:0 0;padding:0;font:inherit;font-size:12px;cursor:pointer;text-decoration:underline;text-underline-offset:3px}.project-attention-action{text-align:left}.dashboard-page .cell-status .project-attention-action{flex-basis:100%}.project-details-row{width:100%;border-bottom:1px solid var(--border-color)}.project-details{padding:var(--dashboard-space-xl);background:var(--paper-bg-alt)}.project-details-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--dashboard-space-sm)}.project-details-heading h2{font:600 16px var(--font-sans);margin:0}.project-details-list{display:grid;grid-template-columns:1fr 1fr;gap:var(--dashboard-space-md) var(--dashboard-space-xl);margin:12px 0}.project-details-list dt{font-size:12px;color:var(--ink-muted);font-weight:500;margin-bottom:4px}.project-details-list dd{margin:0;font-size:13px;overflow-wrap:anywhere}.project-details-list code{color:var(--color);font-size:12px}.project-name-form{margin-top:var(--dashboard-space-lg);padding-top:var(--dashboard-space-lg);border-top:1px solid var(--border-color)}.project-name-form label{display:block;font-size:13px;font-weight:500;margin-bottom:4px}.project-name-form p,.project-progress-help{font-size:12px;color:var(--ink-muted);margin-bottom:8px}.project-name-controls{display:flex;gap:var(--dashboard-space-sm);max-width:560px}.project-name-controls .form-control{min-width:0;font-size:14px}.project-name-controls .btn{flex-shrink:0;min-height:40px}.project-name-form .project-name-error{color:var(--status-error)}.project-details-button{min-height:44px;padding:0 8px;border:0;border-radius:6px;background:0 0;font:inherit;font-size:12px;color:var(--ink-muted);cursor:pointer}.anonymize-page .anonymize-topbar-title{font-family:var(--font-sans);font-size:28px;font-weight:600}.anonymize-topbar{padding:16px 24px}.anonymize-form-col{background:var(--paper-bg-alt)}.anonymize-form-col .paper-settings-main{padding:20px 24px}.anonymize-form-col .paper-settings-section{padding:18px}.anonymize-form-col .paper-settings-section:first-child{padding-top:18px}.anonymize-form-col .anonymize-submit-bar{background:var(--paper-bg-alt);padding:12px 0}.anonymize-preview-head .paper-eyebrow{font-family:var(--font-sans);font-size:14px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:0}.anonymize-preview-col{background:var(--canvas-bg-color);padding:24px}.anonymize-preview-head{flex-wrap:wrap;gap:6px 12px;margin-bottom:16px}.anonymize-preview-body{padding:24px}.anonymize-mobile-tabs,.anonymize-preview-empty,.mobile-preview-jump{display:none}.anonymize-page .repo-access-note{font-size:12px}@media (max-width:900px){.paper-settings-body{grid-template-columns:1fr;gap:18px}.paper-settings-toc{padding:0}.paper-settings-toc-head{display:none}.paper-table .paper-table-row{padding-left:14px;padding-right:14px}}@media (max-width:767px){.anonymize-page .paper-page-title,.paper-page .paper-page-title{font-size:24px}.paper-settings{padding:20px 16px 40px}.paper-settings-main .paper-settings-section{padding:16px}.anonymize-topbar{padding:12px 16px}.anonymize-topbar .paper-crumbs{display:none}.anonymize-mobile-tabs{display:flex;gap:4px;padding:8px 16px;border-bottom:1px solid var(--border-color);background:var(--paper-card)}.anonymize-mobile-tabs button{flex:1;font:inherit;font-size:13px;padding:10px;border:0;border-radius:6px;background:0 0;color:var(--ink-muted)}.anonymize-mobile-tabs button[aria-selected=true]{background:var(--accent-soft);color:var(--accent);font-weight:600}.anonymize-page:not(.mobile-preview-active) .anonymize-preview-col{display:none}.mobile-preview-active .anonymize-form-col{display:none}.mobile-preview-active .anonymize-preview-empty{display:block;padding:24px 16px;color:var(--ink-muted)}.anonymize-form-col .paper-settings-main{padding:16px}.anonymize-form-col .anonymize-submit-bar{background:var(--paper-bg-alt)}.mobile-preview-jump{display:block;width:100%;min-height:44px}.anonymize-preview-col{padding:16px}.anonymize-preview-body{padding:16px}}@media (max-width:575px){.paper-settings .quota-header{flex-direction:row;flex-wrap:wrap;align-items:baseline;gap:4px 8px}}@media (max-width:1100px) and (min-width:901px){.dashboard-page .dashboard-filter-row{flex-wrap:wrap}.dashboard-page .dashboard-filter-row .search-wrap{flex-basis:100%}.dashboard-page .dashboard-filter-controls{width:100%;justify-content:space-between}}@media (max-width:900px){.dashboard-page .paper-table.paper-table-dashboard .paper-table-row{gap:4px 8px;padding:8px 12px}.dashboard-page .paper-table .cell-anon{flex:1 1 calc(100% - 96px);min-width:0}.dashboard-page .paper-table .cell-actions{flex:0 0 88px}.dashboard-row-meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;order:3;width:100%}.dashboard-page .dashboard-row-meta .cell-status{flex:1 1 80px;flex-wrap:wrap;gap:2px 8px}.dashboard-page .dashboard-row-meta .cell-expires,.dashboard-page .dashboard-row-meta .cell-views{flex-shrink:0;font-size:12px}.dashboard-page .cell-expires::before,.dashboard-page .cell-views::before{content:none}.dashboard-page .mobile-meta-icon{display:inline-block;width:14px;height:14px}.dashboard-page .dashboard-row-meta .status-sub-date{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.dashboard-page .dashboard-row-meta .status-sub-error,.dashboard-page .dashboard-row-meta .status-sub-warn{flex-basis:100%;white-space:normal;overflow-wrap:anywhere}.dashboard-page .dashboard-row-meta .status-sub::before{content:none}.dashboard-page .project-attention-action{min-height:44px;display:inline-flex;align-items:center}.dashboard-page .paper-table .anon-sub .commit-hash,.dashboard-page .paper-table .anon-sub .conf-tag{display:none}}@media (max-width:767px){.container.dashboard-page{padding-top:12px}.dashboard-heading .paper-page-title{font-size:24px}.dashboard-heading-actions .btn-ink{min-height:44px}.dashboard-new-label{display:none}.dashboard-new-mobile{display:inline}.dashboard-quota>summary{min-height:44px;gap:6px;padding:10px;font-size:12px;line-height:20px}.dashboard-quota .quota-panel{max-height:calc(100dvh - 184px);overflow-y:auto;overscroll-behavior:contain}.dashboard-page .dashboard-filter-row{flex-wrap:wrap;gap:8px;margin-top:12px}.dashboard-page .dashboard-filter-row .search-wrap{flex:1 1 0}.dashboard-page .dashboard-filter-row .form-control{height:44px}.dashboard-page .dashboard-filters-toggle{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:8px;min-height:44px;background:0 0;border:0}.dashboard-page .dashboard-filter-controls{display:none;width:100%}.dashboard-page .dashboard-filter-controls.expanded{display:flex;flex-wrap:wrap;gap:8px}.dashboard-page .dashboard-filter-controls .btn-group{width:100%;display:flex}.dashboard-page .dashboard-filter-controls .btn-group .btn{flex:1;min-height:44px}.dashboard-extra-filters{display:flex;width:100%}.dashboard-page .dashboard-extra-filters .dropdown{flex:1;min-width:0}.dashboard-page .dashboard-extra-filters .dropdown-toggle{width:100%;height:44px}.dashboard-type-filter .type-filter-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.dashboard-type-filter .project-type-svg{display:inline-block}.dashboard-page .dashboard-meta{margin-top:8px;gap:4px 8px}.dashboard-view-tabs{gap:16px}.dashboard-view-tabs button{min-width:44px;min-height:44px;font-size:12px}.dashboard-count{font-size:11px}.dashboard-count{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.dashboard-view-long{display:none}.dashboard-view-short{display:inline}.dashboard-active-filters{display:contents}.dashboard-page .filter-chip{font-size:11px;padding:0 8px;min-height:44px;gap:4px}.dashboard-page .filter-chip-close{min-width:44px;min-height:44px;margin:0}.dashboard-page .btn-link-inline{min-height:44px;font-size:12px}.dashboard-page .paper-table-dashboard{width:calc(100% + 36px);margin:8px -18px 0;border-left:0;border-right:0;border-radius:0}.dashboard-page .paper-table .repo-name{font-size:13px}.project-details{padding:16px 12px}.project-details-list{grid-template-columns:1fr;gap:12px}.project-name-controls{flex-wrap:wrap}.project-name-controls .form-control{flex-basis:100%}.project-name-controls .btn{min-height:44px}}@media (max-width:360px){.dashboard-attention-prefix{display:none}.dashboard-attention-prefix+span{text-transform:capitalize}}.ui-icon-definitions{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}.ui-icon{display:inline-block;width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0;vertical-align:-3px}.ui-icon.sort-direction{width:14px;height:14px}.pr-title-row{display:flex;align-items:flex-start;gap:16px}.pr-title-row .pr-title{flex:1;min-width:0}.pr-title-row>.btn{flex-shrink:0}.ui-icon.record-type-icon{color:var(--ink-muted)}.btn .ui-icon,.dropdown-item .ui-icon{width:16px;height:16px}.btn-icon-dots .ui-icon{width:18px;height:18px}.anonymize-page .paper-page-title,.paper-page .paper-page-title{font-size:28px}.paper-faq,.paper-page{padding-top:24px}.paper-page .paper-page-lede{margin-bottom:16px;overflow-wrap:anywhere}.paper-page-title{overflow-wrap:anywhere}.anonymize-page .form-control,.paper-page .form-control{min-height:40px;font-family:var(--font-sans);font-size:14px;border-radius:var(--radius-control)}.anonymize-page textarea.form-control,.paper-page textarea.form-control{height:auto}.anonymize-page .btn,.paper-page .btn,.status-bar-actions .btn{min-height:var(--button-height);padding:7px var(--button-padding-x);font:500 var(--button-font-size)/var(--button-line-height) var(--font-sans);border-radius:var(--radius-button);gap:6px}.anonymize-page .btn.btn-sm,.paper-page .btn.btn-sm,.status-bar-actions .btn{min-height:var(--button-compact-height);padding:5px 10px;font-size:13px;border-radius:6px}.anonymize-page .btn-ink,.paper-page .btn-ink{min-height:40px;padding:9px 15px}.btn-hero>i,.btn>.project-type-svg,.btn>.ui-icon,.btn>i{width:16px;height:16px;margin:0!important;flex-shrink:0}.btn.dropdown-toggle::after{margin-left:0}.btn.disabled,.btn:disabled{cursor:not-allowed}.btn.disabled:hover,.btn:disabled:hover{filter:none}.paper-page .btn.btn-link{background:0 0;border-color:transparent}.auth-continue.btn{min-height:44px;padding:11px 16px!important}.anonymize-page .btn.btn-icon,.paper-page .btn.btn-icon{width:32px;height:32px;min-width:32px;min-height:32px;padding:0}.paper-page .btn.btn-icon-dots{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;min-width:32px;min-height:32px;padding:0;border:0;background:0 0;color:var(--ink-muted)}.paper-page .btn-icon-dots:hover{background:var(--accent-soft);color:var(--accent)}.paper-page .btn-group .btn-primary:active,.paper-page .btn-group .btn-primary:hover{background:var(--accent-soft)!important;color:var(--accent)}.paper-page .paper-section-eyebrow{font-family:var(--font-sans);font-size:16px;font-weight:600;color:var(--color);text-transform:none;letter-spacing:0;margin-bottom:12px}.paper-page .paper-field-label{font-family:var(--font-sans);font-size:13px;letter-spacing:0}.paper-tabs .paper-tab{min-height:44px}.paper-pill,.status-pill{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;border-radius:6px}.paper-meta-rule{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;gap:12px 24px}.paper-meta-rule b:not(.commit-hash){font-family:var(--font-sans);font-weight:500}.connections-grid{gap:16px}.connections-grid>section{background:var(--paper-card);border:1px solid var(--border-color);border-radius:var(--radius-surface);padding:20px}.connections-heading{margin-bottom:24px}.claim-page .claim-form,.status-page>.paper-settings-section{padding:20px;margin-top:24px;border:1px solid var(--border-color);border-radius:var(--radius-surface);background:var(--paper-card)}.claim-page .claim-form{max-width:640px}.status-page .anonymize-submit-bar{background:0 0;gap:8px;flex-wrap:wrap}.anonymize-landing{padding-top:32px}.paper-settings .quota-usage{grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}.paper-settings .quota-usage .quota-item+.quota-item{padding-top:0;border-top:0;padding-left:16px;border-left:1px solid var(--border-color)}.paper-settings .quota-usage .quota-header{flex-direction:row;align-items:center;flex-wrap:wrap}.paper-settings .quota-usage .quota-value{white-space:normal;flex-wrap:wrap}.paper-faq-hero{padding-bottom:24px;margin-bottom:24px}.paper-faq-eyebrow,.paper-faq-toc-head{font-family:var(--font-sans);font-size:12px;text-transform:none;letter-spacing:0;margin-bottom:8px}.paper-faq-title{font:600 28px/1.2 var(--font-sans);letter-spacing:-.025em;max-width:720px;margin-bottom:12px}.paper-faq-title em{font-style:normal}.paper-faq-lede{font-size:14px}.paper-faq-body{grid-template-columns:180px minmax(0,1fr);gap:40px}.faq-section h4.faq-cat{font:600 18px/1.4 var(--font-sans);padding-bottom:12px}.faq-section h4.faq-cat .ui-icon{display:none}.faq-section .panel-group{margin-bottom:32px}.faq-section .panel-default .panel-title a{font:500 15px/1.5 var(--font-sans);padding:16px 0 16px 28px;min-height:44px}.faq-section .panel-default .panel-title a:after{top:16px}.paper-empty-title{font:600 32px/1.2 var(--font-sans);letter-spacing:-.025em}.paper-empty-title em{font-style:normal}.btn-hero{justify-content:center;gap:8px;min-height:48px;padding:13px 20px;font-size:15px;line-height:20px;background:var(--accent);border-color:var(--accent);color:#fff;border-radius:var(--radius-button)}.dark-mode .btn-hero{color:#1a1815}.btn-hero:hover{background:var(--accent);color:#fff;filter:brightness(.94)}.dark-mode .btn-hero:hover{color:#1a1815}.paper-table:not(.paper-table-dashboard){border-left:0;border-right:0;border-radius:0}.paper-table:not(.paper-table-dashboard) .paper-table-head{border-radius:0}.paper-table .cell-anon{gap:8px}.paper-table .cell-anon .anon-text{flex:1}.paper-table-conferences .cell-anon .repo-name{font-family:var(--font-sans);font-weight:600}.paper-table .anon-sub>a,.paper-table .anon-sub>span{min-width:0;overflow:hidden;text-overflow:ellipsis}.paper-table .anon-sub .ui-icon{width:14px;height:14px}.paper-page .filter-chip{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none;border-radius:6px}.paper-page .filter-chip-close .ui-icon{width:12px;height:12px}.admin-nav{gap:4px;border:0;border-bottom:1px solid var(--border-color);border-radius:0;padding:0 0 8px;background:0 0}.admin-nav a{font-family:var(--font-sans);font-size:13px;letter-spacing:0;text-transform:none;border-radius:6px;min-height:40px;padding:8px 12px}.admin-nav a.active{background:var(--accent-soft);color:var(--accent)}.admin-nav .ui-icon{width:16px;height:16px}.admin-filter-toolbar{background:0 0;border:0;padding:0}.admin-filter-inline>label,.admin-filter-inline>span{font-family:var(--font-sans);text-transform:none;letter-spacing:0}.admin-summary .summary-total{font-family:var(--font-sans);font-size:18px;font-weight:600}.admin-summary .summary-pill{font:500 13px/20px var(--font-sans);min-height:32px;padding:5px 10px;border-radius:6px}.admin-summary .summary-pill.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.admin-summary .summary-pill .count{font-family:var(--font-sans)}.admin-active-chip{font-family:var(--font-sans);font-size:12px}.admin-active-chip button .ui-icon{width:12px;height:12px}.user-detail-card .user-header h2{font:600 20px/1.4 var(--font-sans)}.errors-page .errors-toolbar .select-wrap label,.errors-page .volume-head,.overview-page .ov-panel-meta,.paper-detail-grid .detail-label,.paper-progress .paper-progress-label,.status-page .paper-error-eyebrow,.user-detail-grid .detail-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.overview-page .ov-chart-title,.overview-page .ov-panel-title,.paper-error-title{font:600 16px/1.4 var(--font-sans)}.paper-error-head>.ui-icon,.paper-ratelimit-head>.ui-icon{margin-top:3px;color:var(--status-error)}.paper-ratelimit-head>.ui-icon{color:var(--status-progress)}.errors-page .seg-tabs{border-radius:var(--radius-control)}.errors-page .seg-tabs button{min-height:36px;padding:7px 12px;font:500 14px/20px var(--font-sans)}.errors-page .seg-tabs button.active{background:var(--accent-soft);color:var(--accent)}.errors-page .errors-list,.errors-page .kpi-card:hover,.errors-page .volume-chart,.overview-page .ov-chart-card,.overview-page .ov-panel-card{box-shadow:none}.errors-page .kpi-value,.overview-page .ov-daily-value,.overview-page .ov-kpi-value,.q-card-count,.user-hero-name{font-family:var(--font-sans);font-size:28px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.02em}.errors-page .kpi-label,.overview-page .ov-daily-label,.overview-page .ov-kpi-label,.q-card-name,.q-section-label,.user-hero-details .detail-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.overview-page .ov-daily-card{background:var(--paper-card);box-shadow:none;border-radius:var(--radius-surface)}.dark-mode .overview-page .ov-daily-value,.overview-page .ov-daily-value{color:var(--accent)}.errors-page .kpi-card,.overview-page .ov-kpi-card{box-shadow:none;border-radius:var(--radius-surface)}.q-card{border-radius:var(--radius-surface)}.q-card.selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.q-range-btns{border-radius:var(--radius-control)}.q-job-detail-actions .btn,.q-range-btns .btn{font-family:var(--font-sans);font-size:13px}.q-range-btns .btn.active{background:var(--accent-soft);color:var(--accent)}@media (max-width:900px){.paper-settings .quota-usage{grid-template-columns:1fr;gap:16px}.paper-settings .quota-usage .quota-item+.quota-item{padding-left:0;border-left:0;border-top:1px solid var(--border-color);padding-top:16px}.paper-table:not(.paper-table-dashboard) .paper-table-row{gap:4px 8px!important;padding:8px 12px}.paper-table:not(.paper-table-dashboard) .cell-anon{flex:1 1 calc(100% - 52px);order:1}.paper-table:not(.paper-table-dashboard) .cell-actions{order:2;flex:0 0 44px}.paper-table .admin-users-row>[role=cell]:first-child,.paper-table.paper-table-repos.has-bulk .paper-table-row>[role=cell]:first-child{flex:0 0 24px;order:0}.paper-table .admin-users-row .cell-anon,.paper-table.paper-table-repos.has-bulk .cell-anon{flex-basis:calc(100% - 84px)}.paper-table:not(.paper-table-dashboard) .cell-anon .anon-sub{font-size:12px;gap:6px}.paper-table:not(.paper-table-dashboard) .cell-status .status-sub{font-size:12px}.paper-table .cell-expires::before,.paper-table .cell-views::before{content:none}.paper-table .cell-expires .mobile-meta-icon,.paper-table .cell-views .mobile-meta-icon{display:inline-block;width:14px;height:14px}.paper-faq-body{grid-template-columns:1fr;gap:24px}}@media (max-width:767px){.paper-faq,.paper-page{padding-top:20px}.paper-settings .paper-account-email .btn[type=submit]{min-height:40px;padding:9px 18px}.anonymize-page .paper-page-title,.paper-faq-title,.paper-page .paper-page-title,.paper-page.admin-page .paper-page-title{font-size:24px}.anonymize-page .form-control,.paper-page .form-control{min-height:44px}.anonymize-page .btn,.connection-link,.draft-discard,.paper-faq-toc a,.paper-page .btn,.paper-settings-toc nav a{min-height:44px}.anonymize-page .btn,.paper-page .btn,.status-bar-actions .btn{min-height:44px;padding:11px 14px;font-size:14px;line-height:20px;white-space:normal}.anonymize-page .btn.btn-sm,.paper-page .btn.btn-sm{min-height:44px;padding:11px 12px;font-size:14px}.anonymize-page .btn.btn-icon,.copy-link-inline,.paper-page .btn.btn-icon,.paper-page .btn.btn-icon-dots,.project-close{width:44px;height:44px;min-width:44px;min-height:44px;padding:0}.dashboard-page .dashboard-filter-row .btn-group .btn,.dashboard-page .dashboard-filter-row .dropdown-toggle{height:auto;min-height:44px;font-size:14px;padding:11px 10px}.dashboard-heading-actions .btn-ink{padding:11px 16px}.dashboard-page .dashboard-filters-toggle{padding:11px 10px}.errors-page .btn-icon,.pr-title-row .btn,.status-bar-actions .btn{min-width:44px;min-height:44px;padding:0}.status-bar-actions .btn{width:44px;height:44px}.errors-page .seg-tabs button,.paper-tab{min-height:44px}.errors-page .seg-tabs button{padding:11px 12px}.status-bar-actions{flex-wrap:wrap;max-width:100%}.leftCol-close,.tree-search-clear{min-width:44px;min-height:44px}.anonymize-landing{padding:20px 18px}.anonymize-landing .repo-access-switch button,.source-url-label-row .repo-access-switch button{min-height:44px;padding:11px 10px;font-size:13px}.paper-settings-body{margin-top:16px;padding-top:0;gap:16px}.paper-settings-toc nav{gap:0 12px}.paper-settings-toc nav a{display:inline-flex;align-items:center}.paper-faq{padding-left:18px;padding-right:18px}.paper-faq-toc nav{display:flex;flex-direction:row;flex-wrap:wrap;gap:0 16px}.paper-faq-toc a{display:inline-flex;align-items:center;padding:8px 0;border-left:0;border-bottom:2px solid transparent;margin-left:0}.paper-faq-toc nav a.active,.paper-faq-toc nav a:hover{border-bottom-color:var(--accent);color:var(--accent)}.claim-page .claim-form,.status-page>.paper-settings-section{padding:16px;margin-top:16px}.admin-nav{flex-direction:row;flex-wrap:nowrap;overflow-x:auto;gap:4px;max-width:100%;margin:8px 0 16px}.admin-nav a{flex-shrink:0;min-height:44px}.admin-summary .summary-pill{min-height:44px;padding:11px 12px;font-size:14px}.admin-active-chip button{min-width:44px;min-height:44px}.admin-filter-row{gap:8px}.admin-filter-row .admin-filter-inline{gap:8px;flex-wrap:wrap}.errors-page .kpi-grid{grid-template-columns:1fr 1fr;gap:12px}.q-cards{flex-direction:row}.q-card{min-width:130px}.q-header-actions{flex-wrap:wrap;gap:8px}}.paper-page.container:not(.auth-page){max-width:1180px}.paper-page>.row{margin-left:0;margin-right:0}.paper-page>.row>.w-100,.paper-page>.row>[class*=col]{padding-left:0;padding-right:0}.anonymize-landing{align-items:flex-start}.status-page .paper-support-grid{margin-bottom:0}.explorer-content .ace_editor{font-family:var(--font-mono);line-height:1.6}.dark-mode .explorer-content .ace-nord-dark,.dark-mode .explorer-content .ace-nord-dark .ace_gutter{background:var(--main-bg-color)}.explorer-content .file-content.markdown-body{max-width:960px;padding:32px 40px 96px}.leftCol-project-header .project-name{white-space:normal;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.file-error{font-size:inherit;font-weight:inherit;text-align:left}.file-error-card{width:100%;max-width:520px;margin-top:0}.file-error-msg{margin:12px 0 0;font-size:14px;line-height:1.55;color:var(--ink-soft)}.file-error-actions{display:flex;gap:8px;margin-top:16px}.file-error .paper-empty-title{font-size:20px;text-align:center}@media (min-width:992px){.leftCol-foot{padding-bottom:72px}}.explorer-content .ace-chrome,.explorer-content .ace-chrome .ace_gutter{background:var(--main-bg-color)}@media (max-width:991px){.explorer-content .file-content.markdown-body{padding:20px 16px 96px}.anonymize-landing{padding:20px 18px}.anonymize-landing-inner{padding:0}.paper-faq-toc nav,.paper-settings-toc nav{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}.paper-faq-toc nav a,.paper-settings-toc nav a{flex-shrink:0;white-space:nowrap}.paper-faq-body,.paper-settings-body{grid-template-columns:minmax(0,1fr)}.paper-faq-toc,.paper-settings-toc{position:static}.paper-faq-toc nav,.paper-settings-toc nav{display:flex;flex-direction:row;gap:0 16px}.paper-faq-toc nav a,.paper-settings-toc nav a{display:inline-flex;align-items:center;padding:8px 0;margin-left:0;border-left:0;border-bottom:2px solid transparent}.paper-faq-toc nav a.active,.paper-faq-toc nav a:hover,.paper-settings-toc nav a.active,.paper-settings-toc nav a:hover{border-bottom-color:var(--accent)}.paper-page .dashboard-filter-controls .dropdown .dropdown-toggle{justify-content:flex-start;gap:6px}.paper-page .dashboard-filter-controls .dropdown .dropdown-toggle::after{margin-left:auto}}@media (max-width:480px){.paper-page .paper-page-lede{display:block;-webkit-line-clamp:unset;overflow:visible}}.navbar .btn-signin,.navbar .btn-signin:hover{background:var(--accent);border-color:var(--accent);color:#fff!important}.dark-mode .navbar .btn-signin,.dark-mode .navbar .btn-signin:hover{color:#1a1815!important}.navbar .btn-signin:hover{filter:brightness(.94)}.paper-progress .paper-progress-bar,.paper-progress.paper-progress-ready .paper-progress-bar{background:var(--accent)}.paper-progress .paper-progress-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.paper-error-eyebrow,.paper-support-card .paper-support-eyebrow{font-family:var(--font-sans);font-size:12px;font-weight:500;letter-spacing:0;text-transform:none}.leftCol-foot .last-update{font-family:var(--font-sans);font-size:11px;letter-spacing:0}.paper-proof-venues span{font-family:var(--font-sans);font-size:18px;font-weight:600;letter-spacing:.02em;color:var(--ink-soft)}.markdown-body mark.redaction{background:var(--accent-soft);color:var(--accent);border-radius:3px;padding:0 2px}.ace_marker-layer .ace_redaction{position:absolute;background:var(--accent-soft);border-radius:3px}.leftCol-project-header{flex-wrap:wrap;row-gap:2px}.project-expiry{flex:1 0 100%;display:flex;align-items:center;gap:4px;font-size:12px;color:var(--ink-muted)}.project-expiry .ui-icon{width:12px;height:12px}.status-bar-actions .btn-icon-only{width:32px;padding:0;justify-content:center}.share-link-row{display:flex;gap:8px;margin-top:4px}.share-link-input{flex:1;min-width:0;font-family:var(--font-mono);font-size:14px;background:var(--paper-bg-alt)}.share-link-row .btn{flex-shrink:0;display:inline-flex;align-items:center;gap:6px;min-width:112px;justify-content:center}.share-link-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.share-link-actions .btn{display:inline-flex;align-items:center;gap:6px}.status-page .paper-support-grid{gap:0 32px}.status-page .paper-support-card{padding:16px 0 0;border:0;border-top:1px solid var(--border-color);border-radius:0;background:0 0}.status-page .paper-support-card:hover{border-color:var(--color)}@media (min-width:992px){.paper-table .cell-expires{white-space:nowrap}}.paper-settings .paper-settings-main .paper-settings-section{background:0 0;border:0;border-bottom:1px solid var(--border-color);border-radius:0;padding:0 0 28px;margin-bottom:28px}.paper-settings .paper-settings-main .paper-settings-section:last-of-type{border-bottom:0}@media (max-width:991px){.paper-settings .paper-settings-main .paper-settings-section{padding:0 0 20px;margin-bottom:20px}.share-link-row{flex-direction:column}.share-link-actions{flex-direction:column}.share-link-actions .btn{justify-content:center}.status-page .paper-support-grid{row-gap:20px}}.paper-page .q-range-btns{display:inline-flex;gap:2px;padding:2px;border:1px solid var(--border-color);border-radius:var(--radius-control);background:var(--paper-card);overflow:visible}.paper-page .q-range-btns .btn,.paper-page .q-range-btns .btn+.btn{min-height:0;height:28px;padding:0 10px;border:0;border-radius:4px;background:0 0;color:var(--ink-muted);font:500 13px/1 var(--font-sans)}.paper-page .q-range-btns .btn:hover{background:var(--hover-bg-color);color:var(--color)}.paper-page .q-range-btns .btn.active{background:var(--accent-soft);color:var(--accent)}.q-header-actions>.btn{height:34px;min-height:34px}.paper-page .btn.btn-danger-soft{background:0 0;border-color:var(--status-error);color:var(--status-error)}.paper-page .btn.btn-danger-soft:hover{background:var(--status-error-soft-hover);border-color:var(--status-error);color:var(--status-error)}.q-auto-refresh,.q-section-label,.q-state-chip{font-family:var(--font-sans);letter-spacing:0;text-transform:none}.q-state-chip{font-size:12px;text-transform:capitalize}.overview-page .ov-ebar-fill{display:block}.paper-table .paper-table-row.repo-error{border-left:0;box-shadow:inset 2px 0 0 var(--status-error);padding-left:16px}.admin-page .q-table th,.admin-page .type-badge,.admin-page .user-detail-section h2,.admin-page section>h2,.admin-page th,.admin-page>h2,.errors-page .col-count,.errors-page .col-mod,.errors-page .col-msg,.errors-page .col-sev,.errors-page .col-status,.errors-page .col-when,.overview-page .ov-chart-legend,.overview-page .ov-ebar-label,.overview-page .ov-route-n,.overview-page .ov-route-name,.q-card-sub,.q-delay-hint,.q-legend-completed,.q-legend-exec,.q-legend-failed,.q-section-right,.q-stat-label,.user-detail-grid .detail-label{font-family:var(--font-sans);letter-spacing:0;text-transform:none}.admin-page th,.errors-page [class^=col-]{font-size:12px;font-weight:500;color:var(--ink-muted)}.q-card-sub,.q-stat-label,.user-detail-grid .detail-label{font-size:12px}.q-stat-value{font-family:var(--font-sans);font-size:20px;font-weight:600;font-variant-numeric:tabular-nums}.q-state-badge{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:capitalize}.admin-page .type-badge{font-size:12px;text-transform:none}.errors-page .col-sev span,.errors-page .sev-label{text-transform:capitalize}.dark-mode .overview-page .ov-daily-value,.overview-page .ov-daily-value{color:var(--color)}.dark-mode .overview-page .ov-spark-fill-alt,.dark-mode .overview-page .ov-spark-fill-new-user,.dark-mode .overview-page .ov-spark-fill-user,.overview-page .ov-spark-fill-alt,.overview-page .ov-spark-fill-new-user,.overview-page .ov-spark-fill-user{background:var(--accent)}.dark-mode .overview-page .ov-dot-new-user-fill,.dark-mode .overview-page .ov-dot-ok-fill,.dark-mode .overview-page .ov-dot-user-fill,.overview-page .ov-dot-new-user-fill,.overview-page .ov-dot-ok-fill,.overview-page .ov-dot-user-fill{background:var(--accent)}.errors-page .seg-tabs{display:inline-flex;gap:2px;padding:2px;border:1px solid var(--border-color);border-radius:var(--radius-control);background:var(--paper-card)}.errors-page .seg-tabs button,.errors-page .seg-tabs button+button{min-height:0;height:30px;padding:0 10px;border:0;border-radius:4px;background:0 0;color:var(--ink-muted);font:500 13px/1 var(--font-sans)}.errors-page .seg-tabs button:hover{background:var(--hover-bg-color);color:var(--color)}.errors-page .seg-tabs button.active{background:var(--accent-soft);color:var(--accent)}.user-detail-card .user-header .status-dot-wrap{display:inline-flex;align-items:center;gap:6px;font-size:14px;font-weight:400;color:var(--ink-soft)}.user-detail-card .user-actions{display:flex;flex-wrap:wrap;gap:8px}.admin-page .paper-table .cell-anon .anon-sub{flex-wrap:wrap;row-gap:2px}.admin-page .paper-table .anon-sub>a,.admin-page .paper-table .anon-sub>span{flex:0 1 auto;max-width:100%;white-space:nowrap}.admin-page .paper-table .cell-views .mobile-meta-icon{margin-right:4px}.q-jobs{overflow-x:auto}.q-job-detail-label{font-family:var(--font-sans);font-size:12px;letter-spacing:0;text-transform:none}.admin-section-header h2{font-family:var(--font-sans);font-size:16px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--color)}.errors-page .sev-label{font-family:var(--font-sans);font-size:13px;letter-spacing:0}.email-prompt{width:min(560px,calc(100vw - 32px));padding:28px;border:1px solid var(--paper-border,#e5e1d9);border-radius:12px;background:var(--paper-bg,#faf9f6);color:inherit;box-shadow:0 16px 64px #0003}.email-prompt::backdrop{background:#0006}.email-prompt h2{margin:0 0 12px;font-size:24px}.email-prompt p{margin-bottom:20px}.email-prompt-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;margin-top:24px}body.dark-mode .email-prompt{background:#242320;color:#faf9f6}.file-error .viewer-error{width:100%;max-width:65ch;padding:2rem 1rem;overflow-wrap:anywhere;font-size:1rem;font-weight:400;text-align:left}.viewer-error .paper-empty-title{margin-bottom:1rem}.viewer-error p{line-height:1.6}.viewer-error .paper-error-actions{display:flex;flex-wrap:wrap;gap:.75rem}.markdown-body .markdown-alert{margin:1rem 0;padding:.5rem 1rem;border-left:4px solid var(--alert-color,#0969da)}.markdown-body .markdown-alert-note{--alert-color:#0969da}.markdown-body .markdown-alert-tip{--alert-color:#1a7f37}.markdown-body .markdown-alert-important{--alert-color:#8250df}.markdown-body .markdown-alert-warning{--alert-color:#9a6700}.markdown-body .markdown-alert-caution{--alert-color:#cf222e}.markdown-body .markdown-alert-title{color:var(--alert-color);font-weight:600;display:flex;align-items:center;gap:.5rem}.markdown-body .markdown-alert-title svg{fill:currentColor;flex-shrink:0}.markdown-body .markdown-alert>:last-child{margin-bottom:0}body.dark-mode .markdown-alert-note{--alert-color:#79c0ff}body.dark-mode .markdown-alert-tip{--alert-color:#7ee787}body.dark-mode .markdown-alert-important{--alert-color:#d2a8ff}body.dark-mode .markdown-alert-warning{--alert-color:#e3b341}body.dark-mode .markdown-alert-caution{--alert-color:#ff7b72}.markdown-body .emoji{display:inline;vertical-align:text-bottom}.markdown-body .footnotes{margin-top:2rem;border-top:1px solid var(--border-color,#d0d7de);padding-top:1rem;font-size:.875em}.markdown-body .katex-display{overflow-x:auto;overflow-y:hidden;padding:.25rem 0}.markdown-body .markdown-diagram{margin:1rem 0;border:1px solid var(--border-color,#d0d7de);padding:.75rem}.markdown-body .markdown-diagram svg{display:block;width:100%;max-height:360px}.markdown-body .markdown-diagram path{fill:#a7b2ff;fill-opacity:.5;stroke:var(--link-color,#3b4ad6);stroke-width:1;vector-effect:non-scaling-stroke}.markdown-body .diagram-controls{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.5rem}.markdown-body .diagram-controls button{min-width:44px;min-height:44px;padding:.25rem .75rem;border:1px solid var(--border-color,#d0d7de);background:var(--main-bg-color,#fff);color:inherit;border-radius:4px}.markdown-body .diagram-controls button:hover{background:var(--hover-bg-color,#f6f8fa)}.markdown-body .diagram-controls button:focus-visible{outline:2px solid var(--link-color,#0969da);outline-offset:2px}.markdown-body .hljs-keyword,.markdown-body .hljs-selector-tag{color:#cf222e}.markdown-body .hljs-attr,.markdown-body .hljs-string{color:#0a3069}.markdown-body .hljs-literal,.markdown-body .hljs-number,.markdown-body .hljs-title{color:#8250df}.markdown-body .hljs-comment{color:#57606a}body.dark-mode .markdown-body .hljs-keyword{color:#ff7b72}body.dark-mode .markdown-body .hljs-attr,body.dark-mode .markdown-body .hljs-string{color:#a5d6ff}body.dark-mode .markdown-body .hljs-literal,body.dark-mode .markdown-body .hljs-number,body.dark-mode .markdown-body .hljs-title{color:#d2a8ff}body.dark-mode .markdown-body .hljs-comment{color:#b1bac4}body.dark-mode .markdown-body .gh-light-mode-only,body:not(.dark-mode) .markdown-body .gh-dark-mode-only{display:none}.billing-plan{padding:1.5rem 0;border-bottom:1px solid var(--paper-border,#d8d4cd)}.billing-plan h3{display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;justify-content:space-between}.billing-price{font-variant-numeric:tabular-nums;font-weight:400}.billing-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}#settings-billing p{max-width:72ch}.repository-hosting{border-top:1px solid var(--border-color);margin-top:24px}.repository-hosting h2{font-size:1.5rem}.hosting-choices{border:0;padding:0;margin:20px 0;min-width:0}.hosting-choices legend{font-size:1rem;font-weight:600}.hosting-choice{display:flex;align-items:flex-start;gap:12px;padding:16px;border:1px solid var(--border-color);background:var(--paper-card);cursor:pointer}.hosting-choice+.hosting-choice{border-top:0}.hosting-choice input{margin-top:5px;accent-color:var(--accent)}.hosting-choice small,.hosting-choice strong{display:block}.hosting-choice small{margin-top:6px;color:var(--ink-muted)}.hosting-actions{display:flex;flex-wrap:wrap;align-items:center;gap:16px;margin:20px 0}.repository-pass{margin:20px 0}.repository-pass summary{cursor:pointer;color:var(--link-color)}.repository-pass p{margin-top:12px}.task-review-active.anonymize-page{height:auto!important;min-height:100%;overflow:visible}.task-review-active .anonymize-workspace{flex:0 0 auto;overflow:visible}.repository-task{width:100%;max-width:1020px;margin:0 auto;padding:32px 24px 42px;color:var(--color);font-size:15px;line-height:1.55}.repository-task .paper-crumbs{margin-bottom:24px}.repository-task h1{font:normal 40px/1.15 'Iowan Old Style',Georgia,serif;margin:8px 0 10px}.repository-task h2{font:normal 27px/1.25 'Iowan Old Style',Georgia,serif;margin:0 0 12px}.repository-task h3{font:600 17px/1.5 var(--font-sans);margin:0 0 10px}.repository-task p{margin:0 0 16px}.task-lede,.task-muted{color:var(--ink-muted)}.task-trail{list-style:none;display:flex;flex-wrap:wrap;gap:24px;margin:30px 0;padding:18px 0;border-bottom:1px solid var(--border-color);color:var(--ink-muted);font-size:13px}.task-trail [aria-current]{color:var(--color);font-weight:600}.task-trail button{color:inherit;padding:0;border:0;background:0 0;font:inherit;cursor:pointer}.task-columns{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(245px,1fr);gap:34px;align-items:start}.task-section-title{display:flex;align-items:baseline;justify-content:space-between;gap:16px}.task-text-button{border:0;padding:7px 0;background:0 0;color:var(--link-color);cursor:pointer;font:inherit;text-align:left}.task-text-button:focus-visible,.task-trail button:focus-visible{outline:3px solid var(--accent);outline-offset:3px}.task-summary{background:var(--paper-bg-alt);padding:24px;border-radius:5px}.task-summary dt{margin-top:18px;font-size:12px;font-weight:400;color:var(--ink-muted)}.task-summary dd{margin:3px 0;overflow-wrap:anywhere}.task-redactions,.task-summary h3{overflow-wrap:anywhere}.task-summary dl{margin-bottom:0}.task-summary-foot{border-top:1px solid var(--border-color);margin-top:28px;padding-top:24px}.task-summary-foot p{color:var(--ink-muted);margin:0}.task-hosting{border-top:1px solid var(--border-color);margin-top:22px;padding-top:20px}.repository-task .hosting-choices{margin:18px 0}.repository-task .hosting-choice{padding:18px;gap:13px;font-weight:400;margin:0}.repository-task .hosting-choice:first-of-type{border-radius:6px 6px 0 0}.repository-task .hosting-choice:last-of-type{border-radius:0 0 6px 6px}.repository-task .hosting-choice small{margin-top:0;font-size:13px;line-height:1.55}.task-terms{font-size:13px;color:var(--ink-soft)}.task-actions{display:flex;flex-wrap:wrap;align-items:center;gap:18px;margin:24px 0 16px}.repository-task .btn{padding:10px 16px;border-radius:5px;font-size:15px}.task-status{background:var(--accent-soft);padding:16px 20px;margin:20px 0}.task-status p:last-child{margin:0}@media(max-width:650px){.repository-task{padding:24px 18px}.repository-task h1{font-size:32px}.task-columns{display:block}.task-summary{margin-top:28px}.task-trail{gap:15px;font-size:12px}}.repository-task{scroll-margin-top:70px}.task-columns{align-items:stretch}.repository-task .btn-ink{background:var(--color);border-color:var(--color);color:var(--canvas-bg-color);box-shadow:none}.repository-task .btn-ink:hover{background:var(--ink-soft);border-color:var(--ink-soft)}.task-field{margin-bottom:24px}.task-field>label{display:block;font-weight:600;margin-bottom:8px}.task-field input,.task-field select,.task-field textarea{width:100%;padding:12px;border:1px solid var(--border-color);border-radius:4px;background:var(--paper-card);color:var(--color);font:inherit}.task-field textarea{resize:vertical}.task-field small{display:block;margin-top:8px;font-size:13px;color:var(--ink-muted)}.task-field :is(input,select,textarea):focus-visible{outline:3px solid var(--accent);outline-offset:3px}.task-paybox{background:var(--paper-card);border:1px solid var(--border-color);border-radius:5px;padding:22px;margin-top:20px}.task-ready{border-left:3px solid var(--success,#2f7a44);padding:16px 20px;background:var(--paper-bg-alt);margin-bottom:22px}.repository-task.status-page .paper-progress{margin-top:24px}.task-date-options summary{cursor:pointer;color:var(--link-color);font-size:13px}.task-date-options[open] summary{margin-bottom:12px}.task-result{padding:18px;background:var(--paper-bg-alt);overflow-wrap:anywhere}.task-result .detail-label{font-size:12px;color:var(--ink-muted);margin-bottom:6px}.task-configure-columns{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start}.task-live-preview{min-width:0;border:1px solid var(--border-color);border-radius:5px;background:var(--paper-card)}.task-preview-head{padding:20px 24px;border-bottom:1px solid var(--border-color)}.task-preview-head h2{font-size:23px;margin-bottom:6px}.task-preview-head p{margin:0;font-size:13px}.task-preview-content,.task-preview-message{padding:24px}.task-preview-content{overflow:auto;max-height:650px;overflow-wrap:anywhere}.repository-task .task-preview-content h1{font:600 28px/1.3 var(--font-sans)}.repository-task .task-preview-content h2{font:600 22px/1.3 var(--font-sans)}@media(max-width:650px){.task-live-preview{margin-top:28px}.task-preview-content{max-height:none}}.anonymize-page .anonymize-topbar{padding:14px 22px;background:var(--canvas-bg-color)}.anonymize-page .anonymize-topbar-inner{max-width:none}.anonymize-page .anonymize-topbar-head{justify-content:space-between}.anonymize-page .anonymize-topbar-title{font:600 19px/1.25 var(--font-sans);margin:0}.anonymize-page .anonymize-split{display:grid;grid-template-columns:minmax(320px,42%) minmax(0,1fr);overflow:hidden}.anonymize-page .anonymize-form-col{width:auto;min-height:0;overflow:hidden!important;background:var(--paper-card)}.anonymize-page .anonymize-form-col .paper-settings-main{display:flex;flex-direction:column;height:100%;min-height:0;max-width:none;padding:0}.anonymize-fields{flex:1;min-height:0;overflow:auto;position:relative;scrollbar-width:thin}.anonymize-page .paper-settings-main .paper-settings-section{border:0;border-top:1px solid var(--border-color);border-radius:0;padding:18px 20px 20px;margin:0;box-shadow:none}.anonymize-page .paper-settings-main .paper-settings-section:first-child{border-top:0;padding-top:18px}.anonymize-page .paper-section-eyebrow{font:600 15px/1.4 var(--font-sans);margin:0 0 16px;letter-spacing:0}.anonymize-page .paper-field-label{font:500 13px/1.5 var(--font-sans);margin-bottom:8px}.anonymize-page .form-group{margin-bottom:16px}.anonymize-page .form-group:last-child{margin-bottom:0}.anonymize-page .form-control{font-size:14px;min-height:40px;padding:9px 10px;border-radius:4px}.anonymize-page textarea.form-control{line-height:1.6}.anonymize-page .form-grid-2{gap:12px}.anonymize-page .form-check{padding-top:10px;padding-bottom:10px;font-size:14px}.anonymize-page .form-check-label{font-size:14px;line-height:1.5}.anonymize-page .form-text{font-size:12px;line-height:1.6}.anonymize-page .repo-access-details{margin:12px 0 16px}.anonymize-page .repo-access-note{font-size:11px}.anonymize-page .anonymize-submit-bar{flex:none;position:static;margin:0;padding:16px 20px;background:var(--paper-card);border-top:1px solid var(--border-color)}.anonymize-page .anonymize-submit-bar .btn{width:100%;font-size:14px;padding:10px 12px;min-height:43px}.checkout-return-note{font-size:11px;line-height:1.5;color:var(--ink-muted);margin:8px 0 0;text-align:center}.anonymize-fields>.alert,.anonymize-fields>.anonymize-review-note,.anonymize-fields>.draft-status{margin:12px 20px}.anonymize-page .anonymize-preview-col{min-height:0;padding:20px;position:relative;background:var(--canvas-bg-color)}.anonymize-page .anonymize-preview-head{border:0;padding:0;min-height:32px;margin-bottom:14px;display:flex;align-items:center;justify-content:space-between}.anonymize-preview-head button,.anonymize-preview-head strong{font-size:13px}.anonymize-page .anonymize-preview-body{padding:24px;border-radius:6px;min-height:480px;overflow-wrap:anywhere;font:14px/1.7 var(--font-sans)}.anonymize-page .anonymize-preview-body h1{font-size:27px;line-height:1.2;margin:0 0 20px;padding-bottom:14px}.anonymize-page .anonymize-preview-body h2{font-size:18px;margin:28px 0 12px}.anonymize-page .anonymize-preview-body pre{font-size:12px;padding:15px}.anonymize-page.preview-expanded .anonymize-split{grid-template-columns:1fr}.anonymize-page.preview-expanded .anonymize-form-col{display:none}.hosting-usage{font-size:12px;color:var(--ink-muted);margin:0 0 4px}.expiration-choices{border:0;padding:0;margin:0 0 16px;min-width:0}.expiration-choice{display:flex;align-items:flex-start;gap:10px;padding:12px 0;margin:0;cursor:pointer;font-size:14px;font-weight:500;line-height:1.5}.expiration-choice input{width:16px;height:16px;margin:4px 0 0;flex:none;accent-color:var(--accent)}.expiration-choice small{display:block;font-size:12px;font-weight:400;color:var(--ink-muted);margin-top:2px}.dated-options{padding:2px 0 14px 26px}.hosting-date,.hosting-property{display:grid;grid-template-columns:48px minmax(0,1fr);gap:8px;align-items:center;margin-bottom:10px}.hosting-property{margin-bottom:0}.hosting-date .paper-field-label,.hosting-property label{font-size:12px;font-weight:400;margin:0}.hosting-date .invalid-feedback{grid-column:1/-1}.anonymize-page .conference-field{border-top:1px solid var(--border-color);padding:16px 0;margin:0}.subscription-choice{padding:16px;background:var(--paper-bg-alt);border-radius:6px;margin-top:4px}.subscription-choice h3{font-size:13px;font-weight:600;margin:0 0 10px}.billing-cycle{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;border:0;padding:0;margin:0 0 12px;min-width:0}.cycle-option{display:flex;align-items:center;gap:7px;padding:10px 8px;border:1px solid var(--border-color);border-radius:4px;background:var(--paper-card);font-size:12px;cursor:pointer;margin:0}.cycle-option.selected{border-color:var(--accent);background:var(--accent-soft)}.cycle-option input{accent-color:var(--accent);margin:0;flex:none}.subscription-choice p{font-size:12px;line-height:1.5;margin:0 0 8px}.subscription-choice p:last-child{margin:0}.hosting-terms{font-size:12px;line-height:1.5;color:var(--ink-muted)}.capacity-reason{font-size:13px}.capacity-options,.repository-pass{margin-top:12px;font-size:12px}.capacity-options summary,.repository-pass summary{cursor:pointer}.capacity-options p,.repository-pass p{margin:8px 0}.capacity-options .task-text-button{display:block}@media(max-width:767px){.anonymize-page .anonymize-split{display:block;overflow:visible}.anonymize-page .anonymize-form-col .paper-settings-main{height:auto}.anonymize-fields{overflow:visible}.anonymize-page .paper-settings-main .paper-settings-section{padding:18px 16px 20px}.anonymize-page .anonymize-topbar{padding:14px 16px}.anonymize-page .anonymize-preview-col{padding:16px}.anonymize-page .anonymize-preview-body{padding:20px}.anonymize-page .anonymize-submit-bar{padding:16px}.anonymize-page .preview-expand{display:none}.anonymize-page .anonymize-mobile-tabs{padding:0;gap:0;background:var(--paper-card)}.anonymize-mobile-tabs button{border-radius:0;padding:12px;min-height:44px}.anonymize-mobile-tabs button[aria-selected=true]{background:0 0;box-shadow:inset 0 -2px var(--accent)}.cycle-option{min-height:44px}}.anonymize-page{--font-sans:system-ui,-apple-system,sans-serif;font-family:var(--font-sans)}body:not(.dark-mode) .anonymize-page{--paper-card:#fff;--canvas-bg-color:#faf9f6;--paper-bg-alt:#f5f4f0;--border-color:#dedbd4;--color:#242320;--ink-muted:#68655e}.anonymize-page .repo-access-controls .btn{font-size:12px;padding:8px 12px;min-height:36px}.anonymize-page .form-check-label{font-weight:400}.anonymize-page .form-check{padding-left:26px}.anonymize-page .form-check input{width:16px;height:16px;margin-left:-26px;accent-color:var(--accent)}.anonymize-page .form-check small{margin-top:6px}.anonymize-page .btn.btn-icon.refresh-branches{display:inline-flex;align-items:center;justify-content:center;width:40px;min-width:40px;height:100%;min-height:40px;padding:0}@media (max-width:767px){.anonymize-page #branch,.anonymize-page #commit{min-height:44px}.anonymize-page .btn.btn-icon.refresh-branches{width:44px;min-width:44px;min-height:44px}}.anonymize-page .redaction-syntax{font-size:12px;margin-top:8px}.redaction-syntax summary{cursor:pointer;color:var(--link-color);min-height:28px}.anonymize-page .anonymize-preview-body h2{border:0;padding:0;font-weight:700}.anonymize-page .anonymize-preview-body h1{font-weight:700}.anonymize-page .anonymize-preview-body pre{background:var(--paper-bg-alt);line-height:1.7}.anonymize-page .anonymize-preview-body pre code{font:12px/1.7 ui-monospace,monospace;background:0 0;padding:0}.anonymize-page .anonymize-preview-body{--main-bg-color:var(--paper-card);background:var(--paper-card)}.anonymize-page .anonymize-submit-bar .btn{font-weight:600}.anonymize-page .form-grid-2 .form-group{margin-bottom:0}.anonymize-page .form-check small{margin-left:-26px}@media (min-width:992px){body:has([class*=floatingchat-container-wrap]) .anonymize-page .anonymize-submit-bar{padding-left:228px}}.user-settings{max-width:1060px;padding:28px 26px 40px}.user-settings .settings-title{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:22px}.user-settings .settings-title h1{font:650 26px/1.3 var(--font-sans);letter-spacing:-.025em;margin:0}.settings-title>span{color:var(--ink-muted);font-size:12px}.settings-nav{display:flex;gap:28px;border-bottom:1px solid var(--border-color)}.settings-nav button{border:0;border-radius:0;background:0 0;color:var(--ink-muted);padding:12px 0;min-height:44px;font:13px/1.4 var(--font-sans)}.settings-nav button[aria-current=page]{color:var(--color);font-weight:600;box-shadow:inset 0 -2px var(--accent)}.settings-nav button:hover{color:var(--accent)}.user-settings .settings-scope{color:var(--ink-muted);font-size:12px;margin:20px 0 4px}.user-settings .settings-row{display:grid;grid-template-columns:210px minmax(0,1fr);gap:32px;padding:22px 0;border-bottom:1px solid var(--border-color)}.user-settings .settings-label,.user-settings .settings-row>h2{font-size:13px;font-weight:600;line-height:1.5;margin:8px 0 0;display:block}.user-settings .settings-help{font-size:12px;color:var(--ink-muted);margin:7px 0 0;line-height:1.6}.user-settings .form-control{font-size:14px;min-height:40px;padding:9px 10px;border-radius:4px;background:var(--paper-card)}.user-settings textarea.form-control{line-height:1.6}.settings-syntax{font-size:12px;margin-top:8px}.settings-syntax summary{cursor:pointer;color:var(--link-color);min-height:26px}.settings-syntax p{margin:8px 0}.settings-checks{display:grid;grid-template-columns:1fr 1fr;gap:14px 18px;padding:4px 0;margin-bottom:12px}.user-settings .settings-check{display:flex;gap:9px;align-items:center;font-size:13px;font-weight:400;margin:0}.user-settings .settings-check .form-check-input{position:static;width:16px;height:16px;margin:0;flex:none;accent-color:var(--accent)}.settings-save{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:18px 0 22px}.settings-save-status{color:var(--ink-muted);font-size:12px}.settings-save #save{margin-left:auto;min-width:125px}.settings-save-error{color:var(--status-error);flex-basis:100%}.user-settings .settings-inline{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px}.user-settings .btn{font-size:13px;min-height:40px;padding:9px 13px}.settings-inline .btn{flex-shrink:0}.user-settings #settings-quota{margin:22px 0 16px}.user-settings .quota-row{gap:20px}.user-settings .quota-item{padding:0;background:0 0;border:0}.user-settings .quota-label{font-size:12px}.user-settings .quota-value{font-size:13px;margin:6px 0}.user-settings .quota-value strong{font-size:14px}.user-settings .quota-percent,.user-settings .quota-remaining{font-size:11px}.user-settings .settings-plan{background:var(--paper-card);border:1px solid var(--border-color);border-radius:6px;padding:20px;margin:20px 0 0}.user-settings .settings-plan h3{font-size:15px;margin:0 0 10px}.user-settings .settings-plan>p{font-size:13px}.settings-cycles{display:flex;flex-wrap:wrap;gap:10px;border:0;padding:0;margin:16px 0}.settings-cycles label{display:flex;align-items:center;gap:8px;border:1px solid var(--border-color);background:var(--paper-card);border-radius:5px;padding:12px;font-size:13px;margin:0;cursor:pointer;flex:1}.settings-cycles label.selected{border-color:var(--accent);background:var(--accent-soft)}.settings-cycles input{accent-color:var(--accent)}.settings-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}.settings-notice{background:var(--paper-bg-alt);border:1px solid var(--border-color);border-radius:5px;padding:14px 16px;margin:16px 0;font-size:13px}.settings-notice p{margin:6px 0 12px}.settings-text-button{border:0;background:0 0;color:var(--link-color);padding:8px 0;font-size:12px}.settings-pass{margin-top:18px}.settings-pass label{display:block;font-size:13px;margin-bottom:8px}.settings-pass .btn{margin-top:12px}.settings-purchase{padding:12px 0;border-bottom:1px solid var(--border-color);font-size:13px;overflow-wrap:anywhere}.settings-purchase:last-child{border:0}.settings-purchase>span{display:block;color:var(--ink-muted);margin-top:4px}.user-settings .paper-account-card{padding:24px 0;border:0;background:0 0}.user-settings .paper-danger-zone{margin-top:24px}@media (max-width:767px){.user-settings{padding:22px 18px 90px}.user-settings .settings-row{grid-template-columns:1fr;gap:12px;padding:20px 0}.user-settings .settings-label,.user-settings .settings-row>h2{margin-top:0}.settings-nav{gap:18px;justify-content:space-between}.settings-nav button{text-align:left;font-size:12px;max-width:124px}.user-settings .settings-check{font-size:12px;min-height:28px}.settings-save{gap:8px}.settings-save-status{font-size:11px}.settings-save #save{min-width:120px}.user-settings .quota-row{grid-template-columns:1fr}.settings-cycles label{flex-basis:100%}}.user-settings .settings-save .draft-status{margin:0;padding:0}.settings-delete-confirm{max-width:480px;margin-top:18px}.settings-delete-confirm label{display:block;font-size:13px;margin-bottom:8px}.user-settings .quota-usage{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}.user-settings .quota-usage .quota-item+.quota-item{border:0;padding:0}.user-settings .quota-percent,.user-settings .quota-remaining{display:none}@media (max-width:767px){.user-settings .quota-usage{grid-template-columns:1fr}}.settings-header-actions{display:flex;align-items:center;gap:16px}.settings-header-actions>span{color:var(--ink-muted);font-size:12px}.user-settings .settings-upgrade{color:var(--accent);border-color:var(--accent);background:0 0}.user-settings .settings-comparison{width:100%;table-layout:fixed;margin:16px 0 20px;font-size:12px}.settings-comparison td,.settings-comparison th{padding:10px 8px;border-bottom:1px solid var(--border-color);text-align:left;overflow-wrap:anywhere;vertical-align:top}.settings-comparison th:first-child{width:42%;padding-left:0}.settings-comparison tbody th{font-weight:400}.settings-comparison thead th{font-weight:600}@media(max-width:580px){.settings-header-actions>span{display:none}.settings-header-actions{gap:8px}.user-settings .settings-comparison{font-size:11px}}.navbar .nav-link.nav-upgrade{border:1px solid var(--accent);border-radius:5px;color:var(--accent)!important;padding:7px 12px!important;margin-right:12px;font-size:12px;line-height:1.5}.navbar .nav-link.nav-upgrade:hover{background:var(--accent-soft)}@media(max-width:991px){.navbar .nav-link.nav-upgrade{display:inline-block;min-height:44px;padding:11px 14px!important;margin:8px 0}} \ No newline at end of file diff --git a/public/css/katex.min.css b/public/css/katex.min.css index 2a68304..303721d 100644 --- a/public/css/katex.min.css +++ b/public/css/katex.min.css @@ -1 +1 @@ -@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.10"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} \ No newline at end of file +@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-display:block;font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,math,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.18.10"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} diff --git a/public/css/style.css b/public/css/style.css index 07bad0b..f5fab07 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,3 +1,5 @@ +/* Type scale: 10 11 12 13 14 15 16 18 20 22 24 28 32 40px (rem values use the same + steps so they still follow the reader's browser font size). */ /* ===== Paper design tokens ===== Editorial minimal — warm neutrals, single indigo accent. Serif display (Instrument Serif), Inter body, JetBrains Mono. */ @@ -396,7 +398,7 @@ a:hover { border: 1px solid var(--border-color); border-radius: 999px; background: var(--sidebar-bg-color); - font-size: 12.5px; + font-size: 13px; color: var(--color) !important; } @@ -489,7 +491,7 @@ a:hover { .hero-subtitle { margin-top: 18px; - font-size: 17px; + font-size: 18px; line-height: 1.55; color: var(--ink-soft); max-width: 30em; @@ -563,7 +565,7 @@ a:hover { display: flex; flex-wrap: wrap; gap: 6px 16px; - font-size: 12.5px; + font-size: 13px; color: var(--ink-muted); } @@ -748,7 +750,7 @@ a:hover { .paper-stat-delta { margin-top: 10px; font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; color: var(--status-ready); } @@ -833,7 +835,7 @@ a:hover { .paper-preview .pane-body { padding: 16px 18px; font-family: var(--font-mono); - font-size: 12.5px; + font-size: 13px; line-height: 1.9; color: var(--ink-soft); } @@ -935,7 +937,7 @@ a:hover { } .paper-feature-more p { - font-size: 14.5px; + font-size: 15px; color: var(--ink-soft); line-height: 1.55; margin: 10px 0 12px; @@ -1077,7 +1079,7 @@ a:hover { margin-right: 16px; } .paper-stat-value { - font-size: 36px; + font-size: 40px; } .paper-section-head, .paper-qa { @@ -1087,7 +1089,7 @@ a:hover { grid-template-columns: 1fr; } .paper-preview .pane-body { - font-size: 11.5px; + font-size: 12px; line-height: 1.8; } /* Tabs become a strip above the screenshot; the selected description @@ -1120,7 +1122,7 @@ a:hover { } .paper-feature-tab .paper-eyebrow { margin: 0; - font-size: 10.5px; + font-size: 11px; letter-spacing: 0.08em; } .paper-feature-tab .paper-feature-title { @@ -1147,7 +1149,7 @@ a:hover { } .paper-feature-more .paper-feature-title-m { display: block; - font-size: 26px; + font-size: 28px; line-height: 1.1; } .paper-feature-more p { @@ -1195,7 +1197,7 @@ a:hover { .dropdown-menu .dropdown-header { color: var(--ink-muted); font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; } @@ -1592,7 +1594,7 @@ a:hover { .tree-search-empty { padding: 16px 12px; color: var(--ink-muted); - font-size: 12.5px; + font-size: 13px; font-family: var(--font-sans); text-align: center; } @@ -1628,7 +1630,7 @@ a:hover { .last-update { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; color: var(--ink-muted); padding: 8px 10px; border-top: 1px solid var(--border-color); @@ -1669,7 +1671,7 @@ a:hover { color: var(--color); border-radius: 6px; padding: 4px 10px; - font-size: 12.5px; + font-size: 13px; min-height: 30px; } .status-bar-actions .btn:hover { @@ -1901,7 +1903,7 @@ notebook { .paper-faq-title em { font-style: italic; color: var(--color); } .paper-faq-lede { color: var(--ink-soft); - font-size: 17px; + font-size: 18px; line-height: 1.55; max-width: 38em; margin: 0; @@ -1918,11 +1920,11 @@ notebook { .paper-faq-toc { position: sticky; top: 32px; - font-size: 13.5px; + font-size: 14px; } .paper-faq-toc-head { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; color: var(--ink-muted); letter-spacing: 0.14em; text-transform: uppercase; @@ -2149,7 +2151,7 @@ code { /* ===== Dashboard & List Styles ===== */ .dashboard-title { - font-size: 1.4rem; + font-size: 1.375rem; font-weight: 600; letter-spacing: -0.01em; } @@ -2183,7 +2185,7 @@ code { } .quota-value { - font-size: 0.8rem; + font-size: 0.8125rem; font-weight: 500; font-variant-numeric: tabular-nums; } @@ -2204,7 +2206,7 @@ code { align-items: center; gap: 4px; padding: 3px 10px; - font-size: 0.8rem; + font-size: 0.8125rem; font-weight: 500; background: var(--primary-bg); color: var(--primary-color); @@ -2285,7 +2287,7 @@ code { } .repo-source { - font-size: 0.85rem; + font-size: 0.875rem; opacity: 0.8; display: flex; flex-wrap: wrap; @@ -2302,7 +2304,7 @@ code { flex-wrap: wrap; gap: 12px; margin-top: 8px; - font-size: 0.8rem; + font-size: 0.8125rem; opacity: 0.7; } @@ -2326,14 +2328,14 @@ code { gap: 8px; padding: 48px 20px; opacity: 0.5; - font-size: 0.95rem; + font-size: 0.9375rem; list-style: none; border-bottom: 1px solid var(--border-color); border-top: 1px solid var(--border-color); } .repo-list-empty i { - font-size: 1.8rem; + font-size: 1.75rem; } /* Type badges (Repo / PR) */ @@ -2341,7 +2343,7 @@ code { display: inline-flex; align-items: center; gap: 3px; - font-size: 0.7rem; + font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px; @@ -2562,7 +2564,7 @@ code { .paper-settings-copy { color: var(--ink-soft); font-size: 13px; margin: 0 0 14px; } .anonymize-section-title { - font-size: 0.85rem; + font-size: 0.875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.3px; @@ -2624,7 +2626,7 @@ code { @media (max-width: 767px) { .anonymize-workspace { flex: 0 0 auto; min-height: 0; } .anonymize-topbar { padding: 14px 16px; } - .anonymize-topbar-title { font-size: 1.6rem; margin-top: 2px; } + .anonymize-topbar-title { font-size: 1.5rem; margin-top: 2px; } .anonymize-split { display: flex; flex-direction: column; @@ -2715,9 +2717,9 @@ code { max-width: 100%; } - h1 { font-size: 1.6rem; } - h2 { font-size: 1.35rem; } - h3 { font-size: 1.15rem; } + h1 { font-size: 1.5rem; } + h2 { font-size: 1.375rem; } + h3 { font-size: 1.125rem; } /* Dashboard list items */ .repo-list-item { @@ -2762,7 +2764,7 @@ code { min-height: 32px; } .status-bar .paths { - font-size: 11.5px; + font-size: 12px; overflow-x: auto; flex-wrap: nowrap; white-space: nowrap; @@ -2776,7 +2778,7 @@ code { /* Home hero section */ #home .display-4 { - font-size: 1.8rem; + font-size: 1.75rem; } #home h4.subtext-header { @@ -2837,7 +2839,7 @@ code { } .anonymize-section-title { - font-size: 0.8rem; + font-size: 0.8125rem; margin: 12px 0 8px 0; } @@ -2866,7 +2868,7 @@ code { } .anonymize-page .form-check-label { - font-size: 0.95rem; + font-size: 0.9375rem; } .anonymize-page .card-body { @@ -2875,7 +2877,7 @@ code { /* PR preview on mobile */ .pr-title { - font-size: 1.15rem; + font-size: 1.125rem; } .pr-diff pre { @@ -2888,7 +2890,7 @@ code { .nav-tabs .nav-link { padding: 8px 12px; - font-size: 0.9rem; + font-size: 0.875rem; } } @@ -2960,7 +2962,7 @@ code { border: 1px solid rgba(138, 107, 30, 0.35); border-radius: 8px; color: var(--color); - font-size: 12.5px; + font-size: 13px; line-height: 1.4; } .paper-inline-warning i { color: #8A6B1E; margin-top: 2px; flex-shrink: 0; } @@ -3084,7 +3086,7 @@ code { color: var(--ink-muted); text-decoration: none; font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; @@ -3134,7 +3136,7 @@ code { .admin-stat-card .stat-value { font-family: var(--font-serif); - font-size: 1.4rem; + font-size: 1.375rem; font-weight: 400; line-height: 1.1; color: var(--color); @@ -3142,7 +3144,7 @@ code { .admin-stat-card .stat-label { font-family: var(--font-mono); - font-size: 9.5px; + font-size: 10px; color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.08em; @@ -3224,7 +3226,7 @@ code { } .admin-list-item .item-meta { - font-size: 0.85rem; + font-size: 0.875rem; opacity: 0.8; } @@ -3266,7 +3268,7 @@ code { letter-spacing: 0.02em; border: 1px solid transparent; text-transform: none; - font-size: 11.5px; + font-size: 12px; padding: 1px 9px 2px; border-radius: 999px; } @@ -3324,7 +3326,7 @@ code { .queue-job-card .job-id { font-family: var(--font-mono); - font-size: 0.95rem; + font-size: 0.9375rem; color: var(--color); word-break: break-all; display: flex; @@ -3334,7 +3336,7 @@ code { .queue-job-card .job-timestamps { font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; color: var(--ink-muted); display: flex; flex-wrap: wrap; @@ -3418,7 +3420,7 @@ code { margin: 0; font-family: var(--font-serif); font-weight: 400; - font-size: 1.6rem; + font-size: 1.5rem; display: flex; align-items: center; gap: 10px; @@ -3429,12 +3431,12 @@ code { display: grid; grid-template-columns: 160px 1fr; gap: 10px 20px; - font-size: 0.92rem; + font-size: 0.9375rem; } .user-detail-grid .detail-label { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); @@ -3489,7 +3491,7 @@ code { font-style: italic; } .paper-empty-lede { - font-size: 17px; + font-size: 18px; line-height: 1.55; color: var(--ink-soft); text-wrap: pretty; @@ -3649,7 +3651,7 @@ code { } .navbar .user-chip-wrap .dropdown-menu .dropdown-item { padding: 10px 4px 10px 42px !important; - font-size: 13.5px; + font-size: 14px; color: var(--ink-muted) !important; background: transparent !important; border-bottom: 1px solid var(--border-color); @@ -3676,7 +3678,7 @@ code { border: 1px solid var(--border-color); color: var(--ink-soft); font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; } @@ -3713,7 +3715,7 @@ code { align-items: center; margin-top: 10px; font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-muted); @@ -3782,7 +3784,7 @@ code { } .paper-error-title { font-family: var(--font-serif); - font-size: 1.4rem; + font-size: 1.375rem; line-height: 1.2; margin-top: 2px; color: var(--color); @@ -3800,7 +3802,7 @@ code { margin: 14px 0 0; padding-left: 18px; color: var(--ink-soft); - font-size: 13.5px; + font-size: 14px; line-height: 1.6; } .paper-error-actions { @@ -3816,11 +3818,11 @@ code { grid-template-columns: 160px 1fr; gap: 10px 20px; margin-top: 18px; - font-size: 0.92rem; + font-size: 0.9375rem; } .paper-detail-grid .detail-label { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); @@ -3888,26 +3890,6 @@ code { .paper-support-card:hover .paper-support-cta i { transform: translateX(2px); } .paper-support-card .paper-support-cta i { transition: transform 0.15s ease; } -/* Ko-fi embed wrapper */ -.paper-kofi-wrap { - border: 1px solid var(--border-color); - border-radius: 10px; - overflow: hidden; - background: var(--paper-card); -} -.paper-kofi-wrap iframe { - border: 0; - width: 100%; - height: 650px; - display: block; -} -.dark-mode .paper-kofi-wrap iframe { - filter: invert(0.88) hue-rotate(180deg) sepia(0.15) saturate(1.2) brightness(1.1) contrast(0.85); -} -@media (max-width: 700px) { - .paper-kofi-wrap iframe { height: 720px; } -} - /* ===== Pull request page (paper) ===== */ .pr-page { min-height: 100%; @@ -3966,7 +3948,7 @@ code { border: 0; border-bottom: 2px solid transparent; font-family: var(--font-sans); - font-size: 13.5px; + font-size: 14px; font-weight: 500; color: var(--ink-muted); cursor: pointer; @@ -4012,7 +3994,7 @@ code { background: var(--paper-bg-alt); border-bottom: 1px solid var(--border-color); font-family: var(--font-mono); - font-size: 12.5px; + font-size: 13px; color: var(--color); } .diff-file-icon { color: var(--ink-muted); flex-shrink: 0; } @@ -4046,7 +4028,7 @@ code { width: 100%; border-collapse: collapse; font-family: var(--font-mono); - font-size: 12.5px; + font-size: 13px; line-height: 1.55; table-layout: fixed; } @@ -4121,7 +4103,7 @@ code { .dark-mode .diff-row-remove .diff-gutter { background: rgba(255,139,123,0.06); color: #FF8B7B; } @media (max-width: 700px) { - .diff-file-table { font-size: 11.5px; } + .diff-file-table { font-size: 12px; } .diff-file-table .diff-gutter { width: 36px; padding: 0 6px; } .diff-file-table .diff-sign { width: 14px; padding: 0 4px; } .diff-file-table .diff-code { padding: 1px 8px; } @@ -4163,7 +4145,7 @@ code { .pr-comment-author i { color: var(--ink-muted); font-size: 12px; } .pr-comment-date { font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; color: var(--ink-muted); letter-spacing: 0.02em; } @@ -4175,7 +4157,7 @@ code { .pr-body-card { padding: 14px 14px; border-radius: 8px; } .pr-comment { padding: 12px 14px; } .paper-tabs .paper-tab { padding: 10px 12px; font-size: 13px; } - .pr-diff pre { font-size: 11.5px; padding: 12px; } + .pr-diff pre { font-size: 12px; padding: 12px; } } /* Toasts — paper style, dark-mode aware */ @@ -4205,7 +4187,7 @@ code { color: var(--ink-muted) !important; text-shadow: none; opacity: 0.9; - font-size: 1.2rem; + font-size: 1.25rem; } .toast .toast-header .close:hover { color: var(--color) !important; opacity: 1; } .toast .toast-body { @@ -4253,7 +4235,7 @@ code { border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; color: var(--ink-muted); letter-spacing: 0.02em; } @@ -4289,7 +4271,7 @@ code { .paper-kbd { display: inline-block; font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; padding: 1px 6px; border: 1px solid var(--border-color); border-bottom-width: 2px; @@ -4304,7 +4286,7 @@ code { align-items: center; gap: 6px; font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; padding: 2px 8px; border: 1px solid var(--border-color); border-radius: 999px; @@ -4357,7 +4339,7 @@ code { .paper-form-step:first-child { border-top: 1px solid var(--border-color); } .paper-form-step .step-num { font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; color: var(--ink-muted); padding-top: 4px; letter-spacing: 0.02em; @@ -4371,7 +4353,7 @@ code { } .paper-form-step .step-sub { color: var(--ink-soft); - font-size: 13.5px; + font-size: 14px; margin-bottom: 14px; text-wrap: pretty; } @@ -4400,7 +4382,7 @@ code { .paper-switch-row .sw-label { font-weight: 500; } .paper-switch-row .sw-sub { display: block; - font-size: 12.5px; + font-size: 13px; color: var(--ink-muted); margin-top: 2px; } @@ -5088,7 +5070,7 @@ textarea::selection { text-align: center; border-top: 1px solid var(--border-soft, var(--border-color)); } -.paper-table .paper-table-empty > i { font-size: 1.6rem; color: var(--ink-faint); } +.paper-table .paper-table-empty > i { font-size: 1.5rem; color: var(--ink-faint); } .paper-table .paper-table-empty-actions { display: flex; gap: 8px; margin-top: 8px; } .bulk-bar { @@ -5103,7 +5085,7 @@ textarea::selection { } /* Tighter admin page header — admin is power-user UI, no need for huge title + lede */ -.paper-page.admin-page .paper-page-title { font-size: 1.6rem; margin: 4px 0; } +.paper-page.admin-page .paper-page-title { font-size: 1.5rem; margin: 4px 0; } .paper-page.admin-page .paper-page-lede { display: none; } .paper-page.admin-page .paper-crumbs { margin-bottom: 4px; } .paper-page.admin-page .admin-nav { margin: 6px 0 12px; } @@ -5241,7 +5223,7 @@ textarea::selection { } .admin-summary .summary-total { font-family: var(--font-serif); - font-size: 1.05rem; + font-size: 1rem; color: var(--color); margin-right: 8px; } @@ -5307,7 +5289,7 @@ textarea::selection { gap: 8px; border-top: 1px solid var(--border-soft, var(--border-color)); } -.paper-table-empty > i { font-size: 1.6rem; } +.paper-table-empty > i { font-size: 1.5rem; } /* ===== Queue dashboard (redesigned) ===== */ .q-header { @@ -5344,18 +5326,6 @@ textarea::selection { background: var(--color); color: var(--paper-card); } -.btn-dark { - background: var(--color) !important; - color: var(--paper-card) !important; - border-color: var(--color) !important; - border-radius: 999px; - padding: 5px 18px; - font-family: var(--font-mono); - font-size: 11px; - font-weight: 500; - letter-spacing: 0.04em; -} - /* Queue overview cards */ .q-cards { display: flex; @@ -5396,7 +5366,7 @@ textarea::selection { .dark-mode .q-dot-red { background: #F08A82; } .q-card-name { font-family: var(--font-mono); - font-size: 11.5px; + font-size: 12px; font-weight: 500; color: var(--ink-muted); letter-spacing: 0.04em; @@ -5411,7 +5381,7 @@ textarea::selection { } .q-card-sub { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; color: var(--ink-muted); letter-spacing: 0.04em; } @@ -5521,7 +5491,7 @@ textarea::selection { } .q-stat-label { font-family: var(--font-mono); - font-size: 9.5px; + font-size: 10px; color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.08em; @@ -5529,7 +5499,7 @@ textarea::selection { } .q-stat-value { font-family: var(--font-serif); - font-size: 1.3rem; + font-size: 1.25rem; font-weight: 400; color: var(--color); line-height: 1.2; @@ -5656,7 +5626,7 @@ textarea::selection { } .q-table thead th { font-family: var(--font-mono); - font-size: 9.5px; + font-size: 10px; font-weight: 500; color: var(--ink-muted); text-transform: uppercase; @@ -5674,7 +5644,7 @@ textarea::selection { .q-table tbody tr:last-child { border-bottom: none; } .q-table tbody td { padding: 10px 10px; - font-size: 0.88rem; + font-size: 0.875rem; vertical-align: middle; } .q-cell-id { @@ -5688,7 +5658,7 @@ textarea::selection { gap: 8px; } .q-chevron { - font-size: 9px; + font-size: 10px; color: var(--ink-muted); transition: transform 0.15s; flex-shrink: 0; @@ -5758,7 +5728,7 @@ textarea::selection { } .q-error-reason { color: #B42318; - font-size: 0.82rem; + font-size: 0.8125rem; margin-bottom: 4px; } .dark-mode .q-error-reason { color: #F08A82; } @@ -5784,7 +5754,7 @@ textarea::selection { } .q-error-stack { - font-size: 0.78rem; + font-size: 0.75rem; max-height: 120px; overflow: auto; margin: 4px 0 0; @@ -5819,7 +5789,7 @@ textarea::selection { } .q-job-detail-label { font-family: var(--font-mono); - font-size: 9px; + font-size: 10px; font-weight: 500; color: var(--ink-muted); text-transform: uppercase; @@ -6181,7 +6151,7 @@ textarea::selection { .paper-page .accordion .form-check-label { color: var(--color); font-weight: 500; - font-size: 13.5px; + font-size: 14px; } .paper-page .accordion .form-text { color: var(--ink-muted); @@ -6238,13 +6208,13 @@ textarea::selection { .paper-footer-tag { margin-top: 10px; color: var(--ink-soft); - font-size: 13.5px; + font-size: 14px; line-height: 1.55; max-width: 340px; } .paper-footer-head { font-family: var(--font-mono); - font-size: 10.5px; + font-size: 11px; color: var(--ink-muted); letter-spacing: 0.1em; text-transform: uppercase; @@ -6253,7 +6223,7 @@ textarea::selection { .paper-footer-col a { display: block; color: var(--color); - font-size: 13.5px; + font-size: 14px; padding: 4px 0; text-decoration: none; } @@ -6278,7 +6248,7 @@ textarea::selection { .paper-footer-foot .paper-footer-meta { font-family: var(--font-serif); font-style: italic; - font-size: 13.5px; + font-size: 14px; letter-spacing: 0; color: var(--ink-soft); } @@ -6360,7 +6330,7 @@ body { .errors-page .kpi-card:hover { box-shadow: var(--card-shadow-hover); } .errors-page .kpi-label { font-family: var(--font-mono); - font-size: 0.7rem; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); @@ -6376,7 +6346,7 @@ body { .errors-page .kpi-card.kpi-error .kpi-value { color: var(--bucket-error-fg); } .errors-page .kpi-card.kpi-warn .kpi-value { color: var(--bucket-warn-fg); } .errors-page .kpi-card.kpi-info .kpi-value { color: var(--bucket-info-fg); } -.errors-page .kpi-sub { font-size: 0.78rem; color: var(--ink-muted); font-style: italic; } +.errors-page .kpi-sub { font-size: 0.75rem; color: var(--ink-muted); font-style: italic; } .errors-page .kpi-sub.up { color: var(--bucket-error-fg); font-style: normal; } .errors-page .kpi-sub.down { color: var(--bucket-info-fg); font-style: normal; } .errors-page .dropped-warn { color: var(--bucket-error-fg); font-weight: 600; font-style: normal; } @@ -6392,7 +6362,7 @@ body { .errors-page .volume-head { display: flex; justify-content: space-between; align-items: center; font-family: var(--font-mono); - font-size: 0.7rem; color: var(--ink-muted); + font-size: 0.6875rem; color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px; } @@ -6418,7 +6388,7 @@ body { } .errors-page .seg-tabs button { background: transparent; border: none; - padding: 6px 14px; font-size: 0.85rem; cursor: pointer; + padding: 6px 14px; font-size: 0.875rem; cursor: pointer; color: var(--color); font-family: var(--font-sans); } .errors-page .seg-tabs button + button { border-left: 1px solid var(--border-color); } @@ -6429,23 +6399,23 @@ body { padding: 6px 10px 6px 32px; border: 1px solid var(--border-color); border-radius: 8px; - font-family: var(--font-mono); font-size: 0.82rem; + font-family: var(--font-mono); font-size: 0.8125rem; background: var(--input-bg); color: var(--input-color); } -.errors-page .errors-toolbar .search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-muted); font-size: 0.85rem; } -.errors-page .errors-toolbar .filter-count { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 0.72rem; color: var(--ink-muted); } +.errors-page .errors-toolbar .search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--ink-muted); font-size: 0.875rem; } +.errors-page .errors-toolbar .filter-count { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 0.75rem; color: var(--ink-muted); } .errors-page .errors-toolbar .select-wrap { display: inline-flex; align-items: center; gap: 6px; } .errors-page .errors-toolbar .select-wrap label { font-family: var(--font-mono); - font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); margin: 0; } .errors-page .errors-toolbar .select-wrap select { background: var(--input-bg); color: var(--input-color); border: 1px solid var(--border-color); border-radius: 8px; } -.errors-page .errors-toolbar .autoref { display: inline-flex; gap: 6px; align-items: center; font-size: 0.82rem; color: var(--color); cursor: pointer; margin: 0; } +.errors-page .errors-toolbar .autoref { display: inline-flex; gap: 6px; align-items: center; font-size: 0.8125rem; color: var(--color); cursor: pointer; margin: 0; } .errors-page .btn-icon { width: 32px; padding: 4px 0; } .errors-page .errors-list { @@ -6466,7 +6436,7 @@ body { .errors-page .errors-list-head { padding: 12px 20px; align-items: center; } .errors-page .errors-list-head { font-family: var(--font-mono); - font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); border-bottom: 1px solid var(--border-color); background: var(--paper-bg-alt); @@ -6476,18 +6446,18 @@ body { .errors-page .errors-row.open { background: var(--paper-bg-alt); } .errors-page .errors-row-main { cursor: pointer; } .errors-page .errors-row-main:hover { background: var(--hover-bg-color); } -.errors-page .col-when .when-rel { font-size: 0.95rem; color: var(--color); font-variant-numeric: tabular-nums; } -.errors-page .col-when .when-abs { font-family: var(--font-mono); font-size: 0.78rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; margin-top: 2px; } +.errors-page .col-when .when-rel { font-size: 0.9375rem; color: var(--color); font-variant-numeric: tabular-nums; } +.errors-page .col-when .when-abs { font-family: var(--font-mono); font-size: 0.75rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; margin-top: 2px; } .errors-page .col-sev { display: flex; align-items: center; gap: 8px; } .errors-page .sev-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; } .errors-page .sev-dot.sev-error { background: var(--bucket-error-dot); } .errors-page .sev-dot.sev-warn { background: var(--bucket-warn-dot); } .errors-page .sev-dot.sev-info { background: var(--bucket-info-dot); } -.errors-page .sev-label { font-family: var(--font-mono); font-size: 0.74rem; letter-spacing: 0.08em; color: var(--color); } +.errors-page .sev-label { font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.08em; color: var(--color); } .errors-page .pill-module { display: inline-block; - font-family: var(--font-mono); font-size: 0.78rem; + font-family: var(--font-mono); font-size: 0.75rem; background: var(--paper-bg-alt); color: var(--ink-soft); border: 1px solid var(--border-color); padding: 3px 10px; border-radius: 6px; @@ -6498,17 +6468,17 @@ body { color: var(--color); font-weight: 600; } -.errors-page .col-msg .msg-context { color: var(--ink-muted); font-style: italic; font-size: 0.88rem; margin-left: 10px; } +.errors-page .col-msg .msg-context { color: var(--ink-muted); font-style: italic; font-size: 0.875rem; margin-left: 10px; } .errors-page .col-msg .msg-detail { color: var(--ink-soft); font-family: var(--font-mono); - font-size: 0.82rem; + font-size: 0.8125rem; margin-left: 10px; } .errors-page .col-msg .msg-url { color: var(--ink-muted); font-family: var(--font-mono); - font-size: 0.8rem; + font-size: 0.8125rem; margin-top: 6px; word-break: break-all; } @@ -6517,7 +6487,7 @@ body { display: inline-block; padding: 2px 8px; border-radius: 4px; background: var(--primary-bg); color: var(--primary-color); - font-family: var(--font-mono); font-size: 0.78rem; font-variant-numeric: tabular-nums; + font-family: var(--font-mono); font-size: 0.75rem; font-variant-numeric: tabular-nums; } .errors-page .count-pill.count-pill-muted { background: transparent; color: var(--ink-muted); @@ -6526,7 +6496,7 @@ body { .errors-page .status-pill { display: inline-block; padding: 2px 10px; border-radius: 4px; - font-family: var(--font-mono); font-size: 0.8rem; font-variant-numeric: tabular-nums; + font-family: var(--font-mono); font-size: 0.8125rem; font-variant-numeric: tabular-nums; border: 1px solid transparent; } .errors-page .status-pill.status-error { background: var(--bucket-error-bg); color: var(--bucket-error-fg); border-color: var(--bucket-error-bd); } @@ -6538,7 +6508,7 @@ body { .errors-page .detail-tabs button { background: transparent; border: 0; border-bottom: 2px solid transparent; - padding: 8px 12px; font-size: 0.85rem; cursor: pointer; + padding: 8px 12px; font-size: 0.875rem; cursor: pointer; color: var(--ink-muted); font-family: var(--font-sans); } .errors-page .detail-tabs button.active { color: var(--color); border-bottom-color: var(--color); } @@ -6549,7 +6519,7 @@ body { border-radius: 12px; padding: 18px 22px; font-family: var(--font-mono); - font-size: 0.85rem; + font-size: 0.875rem; line-height: 1.7; color: var(--color); max-height: 26em; @@ -6579,11 +6549,11 @@ body { .errors-page .related-row { display: flex; gap: 10px; padding: 6px 12px; align-items: center; border-bottom: 1px solid var(--border-soft); - font-size: 0.8rem; + font-size: 0.8125rem; } .errors-page .related-row:last-child { border-bottom: none; } .errors-page .detail-actions { display: flex; gap: 6px; align-items: center; margin-top: 10px; } -.errors-page .copy-hint { font-size: 0.78rem; color: var(--bucket-info-fg); } +.errors-page .copy-hint { font-size: 0.75rem; color: var(--bucket-info-fg); } .errors-page .detail-aside { background: var(--paper-card); @@ -6595,20 +6565,20 @@ body { .errors-page .aside-block + .aside-block { margin-top: 16px; } .errors-page .aside-label { font-family: var(--font-mono); - font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); margin-bottom: 4px; } .errors-page .aside-value { font-family: var(--font-serif); - font-size: 1.05rem; + font-size: 1rem; color: var(--color); word-break: break-word; line-height: 1.3; } -.errors-page .aside-value.mono { font-family: var(--font-mono); font-size: 0.82rem; } -.errors-page .aside-sub { color: var(--ink-muted); font-style: italic; font-size: 0.8rem; font-family: var(--font-sans); } +.errors-page .aside-value.mono { font-family: var(--font-mono); font-size: 0.8125rem; } +.errors-page .aside-sub { color: var(--ink-muted); font-style: italic; font-size: 0.8125rem; font-family: var(--font-sans); } -.errors-page .errors-pager { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; color: var(--ink-muted); font-size: 0.85rem; } +.errors-page .errors-pager { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; color: var(--ink-muted); font-size: 0.875rem; } @media (max-width: 720px) { .errors-page .errors-list-head, @@ -6666,7 +6636,7 @@ body { .overview-page .ov-kpi-card:hover { box-shadow: var(--card-shadow-hover); } .overview-page .ov-kpi-label { font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); @@ -6682,7 +6652,7 @@ body { .overview-page .ov-kpi-value.ov-val-warn { color: #B42318; } .dark-mode .overview-page .ov-kpi-value.ov-val-warn { color: #F08A82; } .overview-page .ov-kpi-sub { - font-size: 0.76rem; + font-size: 0.75rem; color: var(--ink-muted); margin-top: 2px; } @@ -6703,21 +6673,21 @@ body { } .overview-page .ov-daily-label { font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-muted); } .overview-page .ov-daily-value { font-family: var(--font-serif); - font-size: 2.35rem; + font-size: 2.5rem; font-weight: 400; line-height: 1; color: var(--accent, #3B4AD6); margin-top: 6px; } .overview-page .ov-daily-sub { - font-size: 0.76rem; + font-size: 0.75rem; color: var(--ink-muted); margin-top: 5px; } @@ -6750,11 +6720,11 @@ body { } .overview-page .ov-chart-title { font-family: var(--font-serif); - font-size: 1.05rem; + font-size: 1rem; } .overview-page .ov-chart-legend { font-family: var(--font-mono); - font-size: 0.7rem; + font-size: 0.6875rem; color: var(--ink-muted); display: flex; align-items: center; @@ -6813,7 +6783,7 @@ body { background: var(--color); color: var(--canvas-bg-color); font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.6875rem; line-height: 1; white-space: nowrap; opacity: 0; @@ -6842,7 +6812,7 @@ body { display: flex; justify-content: space-between; font-family: var(--font-mono); - font-size: 0.66rem; + font-size: 0.6875rem; color: var(--ink-muted); margin-top: 6px; } @@ -6869,11 +6839,11 @@ body { } .overview-page .ov-panel-title { font-family: var(--font-serif); - font-size: 1.05rem; + font-size: 1rem; } .overview-page .ov-panel-meta { font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); @@ -6882,7 +6852,7 @@ body { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-color); - font-size: 0.82rem; + font-size: 0.8125rem; } .overview-page .ov-panel-foot a { color: var(--link-color); text-decoration: none; } .overview-page .ov-panel-foot a:hover { text-decoration: underline; } @@ -7034,7 +7004,7 @@ body { display: flex; align-items: center; gap: 8px; - font-size: 0.82rem; + font-size: 0.8125rem; text-decoration: none; color: inherit; } @@ -7048,7 +7018,7 @@ body { .overview-page .ov-legend-n { margin-left: auto; font-family: var(--font-mono); - font-size: 0.8rem; + font-size: 0.8125rem; font-weight: 600; } @@ -7059,7 +7029,7 @@ body { width: 30px; flex-shrink: 0; font-family: var(--font-mono); - font-size: 0.72rem; + font-size: 0.75rem; text-transform: uppercase; color: var(--ink-muted); } @@ -7084,7 +7054,7 @@ body { width: 36px; text-align: right; font-family: var(--font-mono); - font-size: 0.82rem; + font-size: 0.8125rem; font-weight: 600; flex-shrink: 0; } @@ -7098,14 +7068,14 @@ body { border-bottom: 1px solid var(--border-soft, var(--border-color)); text-decoration: none; color: inherit; - font-size: 0.85rem; + font-size: 0.875rem; align-items: center; } .overview-page .ov-route-row:last-child { border-bottom: none; } .overview-page .ov-route-row:hover { background: var(--hover-bg-color); text-decoration: none; color: inherit; } .overview-page .ov-route-head { font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); @@ -7114,12 +7084,12 @@ body { .overview-page .ov-route-head:hover { background: transparent; } .overview-page .ov-route-name { font-family: var(--font-mono); - font-size: 0.84rem; + font-size: 0.8125rem; } .overview-page .ov-route-n { text-align: right; font-family: var(--font-mono); - font-size: 0.84rem; + font-size: 0.8125rem; } .overview-page .ov-route-lat { color: var(--ink-muted); } .overview-page .ov-n-bad { color: #B42318; font-weight: 600; } @@ -7162,15 +7132,15 @@ body { .overview-page .ov-svc-info { display: flex; flex-direction: column; gap: 1px; flex: 1; } .overview-page .ov-svc-name { font-family: var(--font-mono); - font-size: 0.88rem; + font-size: 0.875rem; font-weight: 600; } .overview-page .ov-svc-meta { - font-size: 0.76rem; + font-size: 0.75rem; color: var(--ink-muted); } .overview-page .ov-svc-detail { - font-size: 0.74rem; + font-size: 0.75rem; color: var(--ink-muted); white-space: nowrap; } @@ -7221,7 +7191,7 @@ body { .connection-top > i { color: var(--ink-muted); font-size: 24px; } .connection-top > .fa-key { font-size: 18px; } .connection-title { margin-bottom: 10px; } -.connections-page h2 { font-family: inherit; font-size: 21px; font-weight: 600; letter-spacing: -.5px; margin: 0; } +.connections-page h2 { font-family: inherit; font-size: 22px; font-weight: 600; letter-spacing: -.5px; margin: 0; } .connections-page p { font-size: 14px; line-height: 1.65; color: var(--ink-muted); } .connection-badge { background: var(--accent-soft); color: var(--accent); font-size: 11px; font-weight: 600; border-radius: 5px; padding: 4px 8px; white-space: nowrap; } .connection-badge-muted { background: var(--paper-bg-alt); color: var(--ink-muted); } @@ -7270,7 +7240,7 @@ body { .auth-intro .paper-page-title { font-size: clamp(48px, 5vw, 76px); line-height: 1.06; margin: 18px 0 24px; } .auth-intro .paper-page-lede { max-width: 330px; } .auth-options { display: grid; gap: 28px; } -.auth-options h2 { font-family: inherit; font-size: 21px; font-weight: 600; margin: 0 0 10px; } +.auth-options h2 { font-family: inherit; font-size: 22px; font-weight: 600; margin: 0 0 10px; } .auth-options p { font-size: 14px; line-height: 1.65; color: var(--ink-muted); } .auth-options .auth-existing { font-size: 12px; border-left: 2px solid var(--accent); padding-left: 14px; margin: 0; } .auth-permissions { padding: 6px 0 12px; list-style: none; font-size: 13px; line-height: 1.6; } @@ -7359,11 +7329,11 @@ body { .paper-settings .paper-settings-footer { padding: 0 0 20px; } .paper-settings .form-control { font-size: 14px; border-color: var(--border-color); border-radius: var(--radius-control); } .paper-settings .form-check { padding-top: 8px; padding-bottom: 8px; } -.paper-page .btn-ink, .anonymize-page .btn-ink { background: var(--accent); color: #fff; border-color: var(--accent); } -.dark-mode .paper-page .btn-ink, .dark-mode .anonymize-page .btn-ink { color: #1A1815; } -.paper-page .btn-ink:hover, .anonymize-page .btn-ink:hover { background: var(--accent); border-color: var(--accent); color: #fff; filter: brightness(.94); } -.dark-mode .paper-page .btn-ink:hover, .dark-mode .anonymize-page .btn-ink:hover { color: #1A1815; } -.paper-page .btn-ink:active, .anonymize-page .btn-ink:active { filter: brightness(.9); } +.paper-page .btn-ink, .anonymize-page .btn-ink, .explorer-page .btn-ink { background: var(--accent); color: #fff; border-color: var(--accent); } +.dark-mode .paper-page .btn-ink, .dark-mode .anonymize-page .btn-ink, .dark-mode .explorer-page .btn-ink { color: #1A1815; } +.paper-page .btn-ink:hover, .anonymize-page .btn-ink:hover, .explorer-page .btn-ink:hover { background: var(--accent); border-color: var(--accent); color: #fff; filter: brightness(.94); } +.dark-mode .paper-page .btn-ink:hover, .dark-mode .anonymize-page .btn-ink:hover, .dark-mode .explorer-page .btn-ink:hover { color: #1A1815; } +.paper-page .btn-ink:active, .anonymize-page .btn-ink:active, .explorer-page .btn-ink:active { filter: brightness(.9); } input[type="date"], select { color-scheme: light; } .dark-mode input[type="date"], .dark-mode select { color-scheme: dark; } .draft-status { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; padding: 10px 0; } @@ -7371,6 +7341,10 @@ input[type="date"], select { color-scheme: light; } .conference-feedback { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px; margin-top: 16px; background: var(--paper-card); border: 1px solid var(--border-color); border-radius: var(--radius-surface); width: 100%; } .paper-table { background: var(--paper-card); border: 1px solid var(--border-color); border-radius: var(--radius-surface); margin-top: 12px; } +.dashboard-page .paper-table-dashboard { width: 100%; } +.paper-account-email { margin: 20px 0; } +.paper-account-email .form-control { max-width: 420px; } +.paper-settings .paper-account-email .btn[type="submit"] { min-height: 36px; padding: 7px 18px; white-space: nowrap; } .paper-table .paper-table-head { font-family: var(--font-sans); text-transform: none; letter-spacing: 0; font-size: 12px; background: var(--paper-bg-alt); border-radius: var(--radius-surface) var(--radius-surface) 0 0; } .paper-table .paper-table-head, .paper-table .paper-table-row { padding-left: 16px; padding-right: 16px; } @@ -7718,7 +7692,6 @@ input[type="date"], select { color-scheme: light; } .q-range-btns { border-radius: var(--radius-control); } .q-range-btns .btn, .q-job-detail-actions .btn { font-family: var(--font-sans); font-size: 13px; } .q-range-btns .btn.active { background: var(--accent-soft); color: var(--accent); } -.btn-dark { border-radius: var(--radius-control); font-family: var(--font-sans); font-size: 12px; } @media (max-width: 900px) { .paper-settings .quota-usage { grid-template-columns: 1fr; gap: 16px; } .paper-settings .quota-usage .quota-item + .quota-item { padding-left: 0; border-left: 0; border-top: 1px solid var(--border-color); padding-top: 16px; } @@ -7735,6 +7708,7 @@ input[type="date"], select { color-scheme: light; } } @media (max-width: 767px) { .paper-page, .paper-faq { padding-top: 20px; } + .paper-settings .paper-account-email .btn[type="submit"] { min-height: 40px; padding: 9px 18px; } .paper-page .paper-page-title, .paper-page.admin-page .paper-page-title, .anonymize-page .paper-page-title, .paper-faq-title { font-size: 24px; } .paper-page .form-control, .anonymize-page .form-control { min-height: 44px; } .paper-page .btn, .anonymize-page .btn, .paper-settings-toc nav a, .connection-link, .draft-discard, .paper-faq-toc a { min-height: 44px; } @@ -7774,6 +7748,211 @@ input[type="date"], select { color-scheme: light; } .q-header-actions { flex-wrap: wrap; gap: 8px; } } +/* ===== UI polish: explorer, errors, page alignment ===== */ +/* One content column for every app page, aligned with settings and the FAQ. */ +/* The sign-in page keeps its own narrow width. */ +.paper-page.container:not(.auth-page) { max-width: 1180px; } +.paper-page > .row { margin-left: 0; margin-right: 0; } +.paper-page > .row > .w-100, .paper-page > .row > [class*='col'] { padding-left: 0; padding-right: 0; } +.anonymize-landing { align-items: flex-start; } +.status-page .paper-support-grid { margin-bottom: 0; } +/* Explorer: readable code and Markdown. */ +.explorer-content .ace_editor { font-family: var(--font-mono); line-height: 1.6; } +.dark-mode .explorer-content .ace-nord-dark, .dark-mode .explorer-content .ace-nord-dark .ace_gutter { background: var(--main-bg-color); } +.explorer-content .file-content.markdown-body { max-width: 960px; padding: 32px 40px 96px; } +.leftCol-project-header .project-name { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } +/* Error states inside the explorer use the same card as the status page. */ +.file-error { font-size: inherit; font-weight: inherit; text-align: left; } +.file-error-card { width: 100%; max-width: 520px; margin-top: 0; } +.file-error-msg { margin: 12px 0 0; font-size: 14px; line-height: 1.55; color: var(--ink-soft); } +.file-error-actions { display: flex; gap: 8px; margin-top: 16px; } +.file-error .paper-empty-title { font-size: 20px; text-align: center; } +@media (min-width: 992px) { + /* Keep the sidebar footer clear of the floating Ko-fi button. */ + .leftCol-foot { padding-bottom: 72px; } +} +.explorer-content .ace-chrome, .explorer-content .ace-chrome .ace_gutter { background: var(--main-bg-color); } +@media (max-width: 991px) { + .explorer-content .file-content.markdown-body { padding: 20px 16px 96px; } + /* Same 18px inset as every other page. */ + .anonymize-landing { padding: 20px 18px; } + .anonymize-landing-inner { padding: 0; } + /* Section links scroll in one row instead of wrapping under a lone underline. */ + .paper-settings-toc nav, .paper-faq-toc nav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; } + .paper-settings-toc nav a, .paper-faq-toc nav a { flex-shrink: 0; white-space: nowrap; } + .paper-settings-body, .paper-faq-body { grid-template-columns: minmax(0, 1fr); } + /* The TOC stacks above the content at the same breakpoint as the grid. */ + .paper-settings-toc, .paper-faq-toc { position: static; } + .paper-settings-toc nav, .paper-faq-toc nav { display: flex; flex-direction: row; gap: 0 16px; } + .paper-settings-toc nav a, .paper-faq-toc nav a { display: inline-flex; align-items: center; padding: 8px 0; margin-left: 0; border-left: 0; border-bottom: 2px solid transparent; } + .paper-settings-toc nav a.active, .paper-settings-toc nav a:hover, .paper-faq-toc nav a.active, .paper-faq-toc nav a:hover { border-bottom-color: var(--accent); } + .paper-page .dashboard-filter-controls .dropdown .dropdown-toggle { justify-content: flex-start; gap: 6px; } + .paper-page .dashboard-filter-controls .dropdown .dropdown-toggle::after { margin-left: auto; } +} +@media (max-width: 480px) { + .paper-page .paper-page-lede { display: block; -webkit-line-clamp: unset; overflow: visible; } +} + +/* ===== Design refinements ===== */ +/* One primary colour: accent for actions and progress; amber/red stay for states. */ +.navbar .btn-signin, .navbar .btn-signin:hover { background: var(--accent); border-color: var(--accent); color: #fff !important; } +.dark-mode .navbar .btn-signin, .dark-mode .navbar .btn-signin:hover { color: #1A1815 !important; } +.navbar .btn-signin:hover { filter: brightness(.94); } +.paper-progress .paper-progress-bar, .paper-progress.paper-progress-ready .paper-progress-bar { background: var(--accent); } +.paper-progress .paper-progress-label { font-family: var(--font-sans); font-size: 12px; letter-spacing: 0; text-transform: none; } +/* Mono is for IDs, hashes and paths; labels use the sans face. */ +.paper-error-eyebrow, .paper-support-card .paper-support-eyebrow { font-family: var(--font-sans); font-size: 12px; font-weight: 500; letter-spacing: 0; text-transform: none; } +.leftCol-foot .last-update { font-family: var(--font-sans); font-size: 11px; letter-spacing: 0; } +.paper-proof-venues span { font-family: var(--font-sans); font-size: 18px; font-weight: 600; letter-spacing: .02em; color: var(--ink-soft); } +/* Redacted terms, in rendered documents and in code. */ +.markdown-body mark.redaction { background: var(--accent-soft); color: var(--accent); border-radius: 3px; padding: 0 2px; } +.ace_marker-layer .ace_redaction { position: absolute; background: var(--accent-soft); border-radius: 3px; } +/* Explorer chrome. */ +.leftCol-project-header { flex-wrap: wrap; row-gap: 2px; } +.project-expiry { flex: 1 0 100%; display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--ink-muted); } +.project-expiry .ui-icon { width: 12px; height: 12px; } +.status-bar-actions .btn-icon-only { width: 32px; padding: 0; justify-content: center; } +/* Status page: the share link leads; support links sit flat inside their section. */ +.share-link-row { display: flex; gap: 8px; margin-top: 4px; } +.share-link-input { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: 14px; background: var(--paper-bg-alt); } +.share-link-row .btn { flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; min-width: 112px; justify-content: center; } +.share-link-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } +.share-link-actions .btn { display: inline-flex; align-items: center; gap: 6px; } +.status-page .paper-support-grid { gap: 0 32px; } +.status-page .paper-support-card { padding: 16px 0 0; border: 0; border-top: 1px solid var(--border-color); border-radius: 0; background: transparent; } +.status-page .paper-support-card:hover { border-color: var(--color); } +@media (min-width: 992px) { .paper-table .cell-expires { white-space: nowrap; } } +/* Settings: plain sections separated by rules rather than a stack of cards. */ +.paper-settings .paper-settings-main .paper-settings-section { background: transparent; border: 0; border-bottom: 1px solid var(--border-color); border-radius: 0; padding: 0 0 28px; margin-bottom: 28px; } +.paper-settings .paper-settings-main .paper-settings-section:last-of-type { border-bottom: 0; } +@media (max-width: 991px) { + .paper-settings .paper-settings-main .paper-settings-section { padding: 0 0 20px; margin-bottom: 20px; } + .share-link-row { flex-direction: column; } + .share-link-actions { flex-direction: column; } + .share-link-actions .btn { justify-content: center; } + .status-page .paper-support-grid { row-gap: 20px; } +} +/* Segmented control: one outlined group, borderless segments, tinted selection. */ +.paper-page .q-range-btns { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--border-color); border-radius: var(--radius-control); background: var(--paper-card); overflow: visible; } +.paper-page .q-range-btns .btn, .paper-page .q-range-btns .btn + .btn { min-height: 0; height: 28px; padding: 0 10px; border: 0; border-radius: 4px; background: transparent; color: var(--ink-muted); font: 500 13px/1 var(--font-sans); } +.paper-page .q-range-btns .btn:hover { background: var(--hover-bg-color); color: var(--color); } +.paper-page .q-range-btns .btn.active { background: var(--accent-soft); color: var(--accent); } +.q-header-actions > .btn { height: 34px; min-height: 34px; } +/* Destructive actions are outlined in red rather than filled. */ +.paper-page .btn.btn-danger-soft { background: transparent; border-color: var(--status-error); color: var(--status-error); } +.paper-page .btn.btn-danger-soft:hover { background: var(--status-error-soft-hover); border-color: var(--status-error); color: var(--status-error); } +.q-section-label, .q-state-chip, .q-auto-refresh { font-family: var(--font-sans); letter-spacing: 0; text-transform: none; } +.q-state-chip { font-size: 12px; text-transform: capitalize; } + +/* ===== Admin consistency ===== */ +/* The error bar fills are spans: without display:block their width is ignored. */ +.overview-page .ov-ebar-fill { display: block; } +/* Error rows mark themselves with an inset edge so their content stays aligned. */ +.paper-table .paper-table-row.repo-error { border-left: 0; box-shadow: inset 2px 0 0 var(--status-error); padding-left: 16px; } +/* Labels, headers and chips use the sans face like the rest of the app. */ +.admin-page th, .admin-page .q-table th, .errors-page .col-when, .errors-page .col-sev, .errors-page .col-mod, .errors-page .col-msg, .errors-page .col-count, .errors-page .col-status, +.overview-page .ov-ebar-label, .overview-page .ov-route-n, .overview-page .ov-route-name, .overview-page .ov-chart-legend, +.q-section-right, .q-legend-completed, .q-legend-failed, .q-legend-exec, .q-stat-label, .q-card-sub, .q-delay-hint, +.admin-page .type-badge, .user-detail-grid .detail-label, .admin-page .user-detail-section h2, .admin-page > h2, .admin-page section > h2 { + font-family: var(--font-sans); letter-spacing: 0; text-transform: none; +} +.admin-page th, .errors-page [class^="col-"] { font-size: 12px; font-weight: 500; color: var(--ink-muted); } +.q-stat-label, .q-card-sub, .user-detail-grid .detail-label { font-size: 12px; } +.q-stat-value { font-family: var(--font-sans); font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; } +.q-state-badge { font-family: var(--font-sans); font-size: 12px; letter-spacing: 0; text-transform: capitalize; } +.admin-page .type-badge { font-size: 12px; text-transform: none; } +.errors-page .sev-label, .errors-page .col-sev span { text-transform: capitalize; } +/* One accent for overview numbers and charts; colour is kept for states. */ +.overview-page .ov-daily-value, .dark-mode .overview-page .ov-daily-value { color: var(--color); } +.overview-page .ov-spark-fill-alt, .overview-page .ov-spark-fill-user, .overview-page .ov-spark-fill-new-user, +.dark-mode .overview-page .ov-spark-fill-alt, .dark-mode .overview-page .ov-spark-fill-user, .dark-mode .overview-page .ov-spark-fill-new-user { background: var(--accent); } +.overview-page .ov-dot-ok-fill, .overview-page .ov-dot-user-fill, .overview-page .ov-dot-new-user-fill, +.dark-mode .overview-page .ov-dot-ok-fill, .dark-mode .overview-page .ov-dot-user-fill, .dark-mode .overview-page .ov-dot-new-user-fill { background: var(--accent); } +/* Errors filter uses the same segmented control as the queue range. */ +.errors-page .seg-tabs { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--border-color); border-radius: var(--radius-control); background: var(--paper-card); } +.errors-page .seg-tabs button, .errors-page .seg-tabs button + button { min-height: 0; height: 30px; padding: 0 10px; border: 0; border-radius: 4px; background: transparent; color: var(--ink-muted); font: 500 13px/1 var(--font-sans); } +.errors-page .seg-tabs button:hover { background: var(--hover-bg-color); color: var(--color); } +.errors-page .seg-tabs button.active { background: var(--accent-soft); color: var(--accent); } +/* User detail: status reads as metadata next to the name; actions are spaced. */ +.user-detail-card .user-header .status-dot-wrap { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 400; color: var(--ink-soft); } +.user-detail-card .user-actions { display: flex; flex-wrap: wrap; gap: 8px; } +/* Row metadata wraps instead of truncating every item to a few letters. */ +.admin-page .paper-table .cell-anon .anon-sub { flex-wrap: wrap; row-gap: 2px; } +.admin-page .paper-table .anon-sub > span, .admin-page .paper-table .anon-sub > a { flex: 0 1 auto; max-width: 100%; white-space: nowrap; } +.admin-page .paper-table .cell-views .mobile-meta-icon { margin-right: 4px; } +.q-jobs { overflow-x: auto; } +.q-job-detail-label { font-family: var(--font-sans); font-size: 12px; letter-spacing: 0; text-transform: none; } +.admin-section-header h2 { font-family: var(--font-sans); font-size: 16px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--color); } +.errors-page .sev-label { font-family: var(--font-sans); font-size: 13px; letter-spacing: 0; } + +/* Email setup after sign-in. Native dialog traps focus and makes the page inert. */ +.email-prompt { + width: min(560px, calc(100vw - 32px)); + padding: 28px; + border: 1px solid var(--paper-border, #e5e1d9); + border-radius: 12px; + background: var(--paper-bg, #faf9f6); + color: inherit; + box-shadow: 0 16px 64px #0003; +} +.email-prompt::backdrop { background: #0006; } +.email-prompt h2 { margin: 0 0 12px; font-size: 24px; } +.email-prompt p { margin-bottom: 20px; } +.email-prompt-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 24px; } +body.dark-mode .email-prompt { background: #242320; color: #faf9f6; } +.file-error .viewer-error { + width: 100%; + max-width: 65ch; + padding: 2rem 1rem; + overflow-wrap: anywhere; + font-size: 1rem; + font-weight: 400; + text-align: left; +} +.viewer-error .paper-empty-title { margin-bottom: 1rem; } +.viewer-error p { line-height: 1.6; } +.viewer-error .paper-error-actions { display: flex; flex-wrap: wrap; gap: .75rem; } + +/* GitHub Markdown extensions share the document viewer's type and theme. */ +.markdown-body .markdown-alert { + margin: 1rem 0; + padding: .5rem 1rem; + border-left: 4px solid var(--alert-color, #0969da); +} +.markdown-body .markdown-alert-note { --alert-color: #0969da; } +.markdown-body .markdown-alert-tip { --alert-color: #1a7f37; } +.markdown-body .markdown-alert-important { --alert-color: #8250df; } +.markdown-body .markdown-alert-warning { --alert-color: #9a6700; } +.markdown-body .markdown-alert-caution { --alert-color: #cf222e; } +.markdown-body .markdown-alert-title { color: var(--alert-color); font-weight: 600; display: flex; align-items: center; gap: .5rem; } +.markdown-body .markdown-alert-title svg { fill: currentColor; flex-shrink: 0; } +.markdown-body .markdown-alert > :last-child { margin-bottom: 0; } +body.dark-mode .markdown-alert-note { --alert-color: #79c0ff; } +body.dark-mode .markdown-alert-tip { --alert-color: #7ee787; } +body.dark-mode .markdown-alert-important { --alert-color: #d2a8ff; } +body.dark-mode .markdown-alert-warning { --alert-color: #e3b341; } +body.dark-mode .markdown-alert-caution { --alert-color: #ff7b72; } +.markdown-body .emoji { display: inline; vertical-align: text-bottom; } +.markdown-body .footnotes { margin-top: 2rem; border-top: 1px solid var(--border-color, #d0d7de); padding-top: 1rem; font-size: .875em; } +.markdown-body .katex-display { overflow-x: auto; overflow-y: hidden; padding: .25rem 0; } +.markdown-body .markdown-diagram { margin: 1rem 0; border: 1px solid var(--border-color, #d0d7de); padding: .75rem; } +.markdown-body .markdown-diagram svg { display: block; width: 100%; max-height: 360px; } +.markdown-body .markdown-diagram path { fill: #a7b2ff; fill-opacity: .5; stroke: var(--link-color, #3b4ad6); stroke-width: 1; vector-effect: non-scaling-stroke; } +.markdown-body .diagram-controls { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .5rem; } +.markdown-body .diagram-controls button { min-width: 44px; min-height: 44px; padding: .25rem .75rem; border: 1px solid var(--border-color, #d0d7de); background: var(--main-bg-color, white); color: inherit; border-radius: 4px; } +.markdown-body .diagram-controls button:hover { background: var(--hover-bg-color, #f6f8fa); } +.markdown-body .diagram-controls button:focus-visible { outline: 2px solid var(--link-color, #0969da); outline-offset: 2px; } +.markdown-body .hljs-keyword, .markdown-body .hljs-selector-tag { color: #cf222e; } +.markdown-body .hljs-string, .markdown-body .hljs-attr { color: #0a3069; } +.markdown-body .hljs-number, .markdown-body .hljs-literal, .markdown-body .hljs-title { color: #8250df; } +.markdown-body .hljs-comment { color: #57606a; } +body.dark-mode .markdown-body .hljs-keyword { color: #ff7b72; } +body.dark-mode .markdown-body .hljs-string, body.dark-mode .markdown-body .hljs-attr { color: #a5d6ff; } +body.dark-mode .markdown-body .hljs-number, body.dark-mode .markdown-body .hljs-literal, body.dark-mode .markdown-body .hljs-title { color: #d2a8ff; } +body.dark-mode .markdown-body .hljs-comment { color: #b1bac4; } + +body:not(.dark-mode) .markdown-body .gh-dark-mode-only, +body.dark-mode .markdown-body .gh-light-mode-only { display: none; } /* Billing follows the settings page's typography and section rules. */ .billing-plan { padding: 1.5rem 0; border-bottom: 1px solid var(--paper-border, #d8d4cd); } .billing-plan h3 { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; justify-content: space-between; } diff --git a/public/i18n/locale-en.json b/public/i18n/locale-en.json index 7d4a03b..938a4fd 100644 --- a/public/i18n/locale-en.json +++ b/public/i18n/locale-en.json @@ -1,4 +1,12 @@ { + "VIEWER_ERRORS": { + "title": "Content could not be loaded", + "access_title": "GitHub access needs attention", + "reader_help": "Reading this repository? You do not need to sign in. Try again after the owner restores access, or let them know this anonymous link is unavailable.", + "owner_help": "Own this repository? If your GitHub account was suspended or recently recovered, sign in to Anonymous GitHub again and check the GitHub connection and repository permissions in your dashboard. Then retry this page. Keep the existing anonymous URL; do not delete the repository to restore access.", + "retry": "Try again", + "dashboard": "Open owner dashboard" +}, "ERRORS": { "cache_busy": "Downloads are busy. Please try again shortly.", "invalid_sort": "The selected sort order is invalid.", @@ -6,9 +14,9 @@ "repository_changed": "The repository changed while processing. Please retry.", "repository_job_cancelled": "The repository changed or was removed while processing.", "storage_delete_failed": "Unable to remove cached files. Please try again later.", - "unknown_error": "Unknown error, contact the admin.", - "unreachable": "Anonymous GitHub is unreachable, contact the admin.", - "request_error": "Unable to download the file, check your connection or contact the admin.", + "unknown_error": "We could not load this content. Try again. If it still fails, contact support with the anonymous URL and the time of the error.", + "unreachable": "The service could not complete this request. Wait a moment and try again.", + "request_error": "The connection was interrupted. Check your internet connection and try again.", "not_found": "The requested resource could not be found.", "not_connected": "You must be logged in to perform this action.", "not_authorized": "You do not have permission to perform this action.", @@ -17,7 +25,7 @@ "user_banned": "Your account has been banned. Contact the admin for more information.", "repo_access_limited": "GitHub blocked access because the repository's organization restricts third-party OAuth apps. Ask an org owner to approve Anonymous GitHub under Settings → Third-party Access → OAuth app policy, or anonymize a personal fork instead.", "repo_saml_enforcement": "The repository's organization enforces SAML single sign-on. Authorize your token for that organization (GitHub → Settings → Applications, or re-run the org's SSO sign-in), then retry. Alternatively, anonymize a personal fork.", - "repo_not_found": "The repository was not found on GitHub. Check the URL and spelling, make sure you are signed in to the account that can see it, and confirm the repo isn't hidden under an org that restricts third-party app access.", + "repo_not_found": "Anonymous GitHub cannot access the source repository on GitHub. It may be private, unavailable, or require the owner to reconnect GitHub.", "repo_empty": "The selected branch has no commits on GitHub. Push at least one commit, or pick a different branch, then retry.", "repo_not_accessible": "Anonymous GitHub cannot access this repository. Verify the repository exists and that Anonymous GitHub has been authorized for the owning organization.", "repository_archived": "This repository has been archived and its files are no longer available.", @@ -64,6 +72,7 @@ "invalid_request": "The request is missing required fields or is malformed.", "invalid_project_name": "Use a project name of up to 100 characters without line breaks or control characters.", "no_file_selected": "Please select a file.", + "submodule_not_available": "This public submodule file is no longer available at its recorded commit.", "file_not_found": "The requested file is not found.", "file_not_accessible": "The requested file is not accessible.", "file_not_supported": "The file type is not supported. Anonymous GitHub cannot handle it.", @@ -109,6 +118,15 @@ "gistId_not_specified": "A gist ID must be provided.", "gistId_already_used": "The gist ID is already used.", "missing_token": "An authentication token is required.", + "token_attempts_rate_limited": "Too many token authentication attempts. Wait before trying again.", + "refresh_rate_limited": "A refresh was already requested for this repository. Wait one minute before trying again.", + "session_required": "Sign in through your browser to manage maintenance tokens.", + "invalid_token_options": "Provide a token name, 1 to 20 repository IDs, and an expiration of 1 to 90 days.", + "token_limit_reached": "You have reached the limit of 50 API tokens. Revoke an unused token before creating another.", + "invalid_token_id": "The token ID is invalid.", + "invalid_settings": "Provide supported settings with boolean values, or a list of anonymization terms.", + "token_scope_forbidden": "This token does not allow this operation or repository.", + "authentication_unavailable": "Authentication is temporarily unavailable. Try again later.", "invalid_token": "The provided authentication token is invalid.", "login_failed": "Login failed. Please try again.", "server_error": "An unexpected server error occurred. Please try again later.", @@ -118,7 +136,7 @@ "storage_write_size_mismatch": "The downloaded file was smaller than expected. The upstream source may have returned an incomplete response — please try again.", "storage_read_error": "An error occurred while reading the file from storage — please try again.", "upstream_error": "A temporary error occurred while fetching from GitHub — please try again.", - "token_expired": "Your GitHub access token has expired. Please log out and log in again to refresh it.", + "token_expired": "The repository owner needs to renew their GitHub connection before this content can be loaded.", "job_is_active": "This job is currently running — wait for it to finish or remove it first.", "github_app_access_required": "Allow this repository on GitHub using the read-only access button, then retry. Organization access may require administrator approval.", "github_app_reconnect_required": "Reconnect your GitHub App account to restore read-only access.", @@ -156,6 +174,6 @@ "page_branch_mismatch": "GitHub Pages on this repository is served from the '{{pageBranch}}' branch, but you selected '{{selectedBranch}}'. Switch the branch above to '{{pageBranch}}' to anonymize the Pages site.", "folder_truncated": "This folder has more than 10,000 entries; only a partial listing is shown.", "repo_truncated": "Some folders in this repository have too many files to be fully listed. Affected folders are marked with a warning icon.", - "submodules_not_included": "This repository uses git submodules. Submodule contents are not included in the anonymized repository and appear as empty folders." + "submodules_not_included": "Some submodule contents could not be included. Only direct public GitHub submodules within the size limits are supported. Private, external, nested, or unavailable submodules appear as empty folders." } } diff --git a/public/index.html b/public/index.html index 926d500..232fada 100644 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,6 @@ /> Anonymous Github - . Keep tokens in a secret store; +never put them in URLs, prompts, source control, or logs. + +## Connect a client + +A person signs in with GitHub and creates the anonymized repository first. +In their signed-in browser session, POST /api/user/maintenance-tokens with: +{"name":"Paper maintenance","repoIds":["paper-123"],"expiresInDays":30} + +The response contains the token once, its id, selected repoIds and expiresAt. +Expiry is 1 to 90 days, default 30. Select 1 to 20 existing repository IDs. +There is a limit of 50 stored API tokens per account. A person can inspect +GET /api/user/maintenance-tokens and revoke one with +DELETE /api/user/maintenance-tokens/{id} using the same browser session. +These endpoints require a session, not a bearer token. Do not hand the client +an admin token or browser cookie. Maintenance tokens cannot log into the UI. + +## Maintain a repository + +1. GET /api/maintenance/repositories/{repoId} reads saved status, branch, + commit and options without querying GitHub or downloading content. +2. PATCH the same URL with only the settings to change, for example: + {"terms":["Alice Smith","Example University"],"image":false,"update":false} + Omitted settings stay unchanged. terms replaces the whole term list; + [] clears it. Supported boolean fields: update, image, pdf, notebook, link. + update controls automatic snapshot updates. Settings edits work in ready + or error state, and cannot change the upstream source or connection. +3. POST /api/repo/{repoId}/refresh with {} requests the latest snapshot of the + saved branch using the repository's existing connection. Poll the saved + status endpoint every 30 seconds until ready or error. Do not repeat + refresh while work is in progress. +4. POST /api/repo/{repoId}/extend with {} adds six months to the current + expiration, or to today if already expired. An expired snapshot is queued + for restoration. This operation is NOT idempotent. Read the expiration + date after an ambiguous network failure before deciding whether to retry. + +Refresh completion is asynchronous. A successful response does not mean the +snapshot is ready. Refresh uses the existing GitHub quota and preparation +limits. All maintenance tokens for an account share an API rate budget, +even across different client IPs. Each account can request one refresh per +repository per minute, shared across its tokens. Bearer authentication attempts +also have a limit of 120 per minute per IP, including invalid tokens. Both limits +return HTTP 429 with Retry-After. Do not use maintenance calls to crawl GitHub or retrieve source files. + +## Errors and access limits + +Errors normally contain {"error":"code"}. The instance rate limiter may +return text for HTTP 429; always check status and Retry-After first. +401: token expired, revoked or invalid. Ask the owner to replace it. +403: token_scope_forbidden or not_authorized. The token must name this +repository and its account must still be an owner, coauthor or administrator. +404: repository does not exist. +409: state changed or operation is unavailable. Read status before retrying. +429: back off until Retry-After or resetAt, when supplied. Never retry in a loop. +5xx: retry reads with backoff. Inspect state before retrying a write. + +Scoped tokens cannot create repositories, change source repositories, delete +snapshots, manage coauthors, search GitHub, preview arbitrary branches or files, +download archives, read file contents, access admin routes, or mint tokens. +Use the signed-in UI for those management tasks. Existing public review links +keep their usual access rules. Revocation and membership checks apply on each +maintenance request. Never publish private maintenance responses as review links. diff --git a/public/maintenance-openapi.json b/public/maintenance-openapi.json new file mode 100644 index 0000000..00c90d0 --- /dev/null +++ b/public/maintenance-openapi.json @@ -0,0 +1,608 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Anonymous GitHub maintenance API", + "version": "1.0.0", + "description": "Maintain existing anonymized snapshots. Session-only token issuance is documented at /llms.txt. No GitHub proxy or file download access." + }, + "servers": [ + { + "url": "/" + } + ], + "security": [ + { + "maintenanceToken": [] + } + ], + "components": { + "securitySchemes": { + "maintenanceToken": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "Error": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "resetAt": { + "type": "number", + "description": "Unix timestamp in milliseconds, when available." + } + } + }, + "RepositoryStatus": { + "type": "object", + "required": [ + "repoId", + "options" + ], + "properties": { + "repoId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "statusMessage": { + "type": "string" + }, + "statusDate": { + "type": "string", + "format": "date-time" + }, + "settingsSavedAt": { + "type": "string", + "format": "date-time" + }, + "source": { + "type": "object", + "properties": { + "branch": { + "type": "string" + }, + "commit": { + "type": "string" + } + } + }, + "options": { + "type": "object", + "properties": { + "terms": { + "type": "array", + "maxItems": 1000, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 1000 + } + }, + "update": { + "type": "boolean" + }, + "image": { + "type": "boolean" + }, + "pdf": { + "type": "boolean" + }, + "notebook": { + "type": "boolean" + }, + "link": { + "type": "boolean" + }, + "expirationDate": { + "type": "string", + "format": "date-time" + }, + "expirationMode": { + "type": "string", + "enum": [ + "never", + "redirect", + "remove" + ] + }, + "page": { + "type": "boolean" + }, + "pageSource": { + "type": "object", + "properties": { + "branch": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "paths": { + "/api/maintenance/repositories/{repoId}": { + "parameters": [ + { + "name": "repoId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "description": "Existing anonymized repository ID selected when issuing the token." + } + ], + "get": { + "operationId": "getRepositoryMaintenanceStatus", + "summary": "Read saved status and settings without GitHub calls", + "responses": { + "200": { + "description": "Operation succeeded; refresh or restoration may still be running.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryStatus" + } + } + } + }, + "400": { + "description": "Invalid settings.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Invalid or expired token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Scope or membership denied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Repository not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Repository state changed or operation unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429": { + "description": "Rate limited. Respect Retry-After.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + }, + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "503": { + "description": "Service unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "patch": { + "operationId": "patchRepositorySettings", + "summary": "Change only supplied settings; terms replaces the entire list", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "terms": { + "type": "array", + "maxItems": 1000, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 1000 + } + }, + "update": { + "type": "boolean" + }, + "image": { + "type": "boolean" + }, + "pdf": { + "type": "boolean" + }, + "notebook": { + "type": "boolean" + }, + "link": { + "type": "boolean" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Operation succeeded; refresh or restoration may still be running.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "repoId": { + "type": "string" + }, + "settingsSavedAt": { + "type": "string", + "format": "date-time" + } + } + } + } + } + }, + "400": { + "description": "Invalid settings.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Invalid or expired token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Scope or membership denied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Repository not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Repository state changed or operation unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429": { + "description": "Rate limited. Respect Retry-After.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + }, + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "503": { + "description": "Service unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/api/repo/{repoId}/refresh": { + "parameters": [ + { + "name": "repoId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "description": "Existing anonymized repository ID selected when issuing the token." + } + ], + "post": { + "operationId": "refreshRepository", + "summary": "Queue refresh of the saved branch. Poll status, not refresh.", + "responses": { + "200": { + "description": "Operation succeeded; refresh or restoration may still be running.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "Invalid settings.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Invalid or expired token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Scope or membership denied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Repository not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Repository state changed or operation unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429": { + "description": "Rate limited. Respect Retry-After.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + }, + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "503": { + "description": "Service unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + }, + "description": "One refresh per repository per minute per account, shared across tokens and IPs. HTTP 429 includes Retry-After." + } + }, + "/api/repo/{repoId}/extend": { + "parameters": [ + { + "name": "repoId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "description": "Existing anonymized repository ID selected when issuing the token." + } + ], + "post": { + "operationId": "extendRepository", + "summary": "Extend expiration by six months. Not idempotent; inspect state before retrying.", + "responses": { + "200": { + "description": "Operation succeeded; refresh or restoration may still be running.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "expirationDate": { + "type": "string", + "format": "date-time" + } + } + } + } + } + }, + "400": { + "description": "Invalid settings.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Invalid or expired token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Scope or membership denied.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "Repository not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Repository state changed or operation unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "429": { + "description": "Rate limited. Respect Retry-After.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + }, + "text/plain": { + "schema": { + "type": "string" + } + } + } + }, + "503": { + "description": "Service unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + } +} diff --git a/public/partials/admin/errors.htm b/public/partials/admin/errors.htm index f4078d3..4dabaa6 100644 --- a/public/partials/admin/errors.htm +++ b/public/partials/admin/errors.htm @@ -5,7 +5,7 @@

Errors

- +
@@ -128,7 +128,7 @@
- {{ fmt?.uppercase(row?._bucket) }} + {{ fmt?.title(row?._bucket) }}
{{ row?.module }}
diff --git a/public/partials/admin/overview.htm b/public/partials/admin/overview.htm index 893a561..aa44c7b 100644 --- a/public/partials/admin/overview.htm +++ b/public/partials/admin/overview.htm @@ -1,5 +1,5 @@
-
Admin · System Health
+
Admin  /  Overview

Overview

@@ -105,11 +105,11 @@
Users · 30d - total users + total users · {{ fmt?.number(historyRange('nbUsers').min) }}–{{ fmt?.number(historyRange('nbUsers').max) }}
- +
@@ -308,7 +308,7 @@ RouteRequestsAverageFirst byte p95p95p99Interrupted5xxSlow {{ row.route }}{{ row.service }} · {{ row.method }} - {{ fmt.number(row.count) }}{{ row.avgMs }} ms + {{ fmt.number(row.count) }}{{ formatMs(row.avgMs) }} {{ firstByteBound(row) }}{{ latencyBound(row, 'p95UpperMs') }}{{ latencyBound(row, 'p99UpperMs') }} {{ fmt.number(row.aborted) }}{{ fmt.number(row.errors) }}{{ fmt.number(row.slow) }} @@ -340,7 +340,7 @@ Work stages and cache results Work stageSamplesAveragep95 - {{ row.metric }}{{ row.service }}{{ fmt.number(row.count) }}{{ row.avgMs }} ms{{ latencyBound(row, 'p95UpperMs') }} + {{ row.metric }}{{ row.service }}{{ fmt.number(row.count) }}{{ formatMs(row.avgMs) }}{{ latencyBound(row, 'p95UpperMs') }}
diff --git a/public/partials/admin/queues.htm b/public/partials/admin/queues.htm index dcd5696..c92ea15 100644 --- a/public/partials/admin/queues.htm +++ b/public/partials/admin/queues.htm @@ -11,7 +11,7 @@
- +
@@ -44,7 +44,7 @@
- +
@@ -52,30 +52,30 @@
- +
-
WAITING
+
Waiting
-
ACTIVE
+
Active
-
COMPLETED (24H)
+
Completed (24h)
-
FAILED (24H)
+
Failed (24h)
-
DELAYED
+
Delayed
-
WORKERS
+
Workers
@@ -94,7 +94,7 @@
- +
`; - const html = `Content${body}`; + const html = `Content${body}`; return res.contentType("text/html").send(html); } } @@ -174,7 +146,7 @@ async function webView( }); } res.set("Cache-Control", "private, no-cache, must-revalidate"); - const pageETag = fileETag(JSON.stringify(["web-v1", repo.model.source.commit, repo.model.treeGeneration, repo.model.contentCacheRevision, + const pageETag = fileETag(JSON.stringify(["web-v2", repo.model.source.commit, repo.model.treeGeneration, repo.model.contentCacheRevision, await f.sha(), repo.model.source.repositoryName, repo.model.source.branch, config.APP_HOSTNAME, config.ANONYMIZATION_MASK]), requestPath, repo.options); res.set("ETag", pageETag); @@ -189,8 +161,8 @@ async function webView( if (f.extension() == "md") { try { const content = await streamToString(await f.anonymizedContent()); - const body = sanitizeHtml(marked.marked.parse(content, { async: false }), sanitizeOptions); - const html = `Content
${body}
`; + const body = renderMarkdown(content); + const html = `Content
${body}
`; res.contentType("text/html").send(html); } catch { // Raw Markdown must re-enter rendering when a transient failure clears. diff --git a/src/server/server-options.ts b/src/server/server-options.ts new file mode 100644 index 0000000..dc4f6c6 --- /dev/null +++ b/src/server/server-options.ts @@ -0,0 +1,9 @@ +import { RequestHandler } from "express"; + +/** The asterisk request target asks about the server, not a session or route. */ +export const serverOptions: RequestHandler = (req, res, next) => { + if (req.originalUrl !== "*") return next(); + if (req.method !== "OPTIONS") { res.status(400).json({ error: "invalid_request" }); return; } + res.setHeader("Allow", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); + res.status(204).end(); +}; diff --git a/src/server/service/review-callbacks.ts b/src/server/service/review-callbacks.ts new file mode 100644 index 0000000..7c3dfe1 --- /dev/null +++ b/src/server/service/review-callbacks.ts @@ -0,0 +1,28 @@ +import { decodeReviewJSON } from "./review-json"; +const invalid = (): never => { throw new Error("Invalid review callback registry"); }; +export function createReviewCallbacks(raw: string) { + let value: unknown; + try { value = decodeReviewJSON(Buffer.from(raw, "utf8")); } catch { return invalid(); } + if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "callbacks,version") return invalid(); + const config = value as { version: unknown; callbacks: unknown }; + if (config.version !== 1 || !Array.isArray(config.callbacks) || !config.callbacks.length || config.callbacks.length > 32) return invalid(); + const callbacks = new Map(), destinations = new Set(); + for (const item of config.callbacks) { + if (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).sort().join(",") !== "callbackId,clientId,url") return invalid(); + const { clientId, callbackId, url } = item; + if (![clientId, callbackId].every(id => typeof id === "string" && /^[a-f0-9]{32}$/.test(id)) || typeof url !== "string" || url.length > 2048) return invalid(); + let parsed: URL; + try { parsed = new URL(url); } catch { return invalid(); } + if (parsed.protocol !== "https:" || parsed.href !== url || parsed.username || parsed.password || parsed.search || parsed.hash || url.includes("?") || url.includes("#") || !/^\/[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*$/.test(parsed.pathname)) return invalid(); + const key = clientId + ":" + callbackId; + if (callbacks.has(key)) return invalid(); + callbacks.set(key, url); destinations.add(url); + } + const formAction = "'self' " + [...destinations].join(" "); + if (Buffer.byteLength(formAction) > 4096) return invalid(); + return Object.freeze({ + formAction, + resolve(clientId: string, callbackId: string): string | undefined { return callbacks.get(clientId + ":" + callbackId); }, + }); +} +export type ReviewCallbacks = ReturnType; diff --git a/src/server/service/review-capabilities.ts b/src/server/service/review-capabilities.ts new file mode 100644 index 0000000..173ef31 --- /dev/null +++ b/src/server/service/review-capabilities.ts @@ -0,0 +1,200 @@ +import { createHash, randomBytes, timingSafeEqual } from "crypto"; +import { RequestHandler } from "express"; + +const contract = "4open.artifacts/1"; +const clientPattern = /^[a-f0-9]{32}$/; +const keyPattern = /^[A-Za-z0-9_-]{1,64}$/; +const secretPattern = /^[a-f0-9]{64}$/; +type Key = { clientId: string; digest: Buffer; from: number; until: number }; + +function invalid(): never { + // Never include private configuration or request values in diagnostic errors. + throw new Error("Invalid REVIEW_SERVICE_KEYS configuration"); +} +function record(value: unknown, fields: string[]): Record { + if (!value || typeof value !== "object" || Array.isArray(value)) invalid(); + const data = value as Record; + if ( + Object.keys(data).length !== fields.length || + fields.some((k) => !Object.prototype.hasOwnProperty.call(data, k)) + ) + invalid(); + return data; +} +function timestamp(value: unknown): number { + if ( + typeof value !== "string" || + !/^20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(value) + ) + invalid(); + const time = Date.parse(value); + if ( + !Number.isFinite(time) || + new Date(time).toISOString() !== value.replace("Z", ".000Z") + ) + invalid(); + return time; +} +function registry(raw: string): Map { + if (Buffer.byteLength(raw, "utf8") > 65536) invalid(); + let parsed: unknown; + try { + parsed = JSON.parse(raw); + } catch { + invalid(); + } + const config = record(parsed, ["version", "keys"]); + if ( + config.version !== 1 || + !Array.isArray(config.keys) || + config.keys.length < 1 || + config.keys.length > 64 + ) + invalid(); + const keys = new Map(), + digests = new Set(), + clients = new Map(); + for (const entry of config.keys) { + const k = record(entry, [ + "clientId", + "keyId", + "tokenSHA256", + "notBefore", + "notAfter", + ]); + if ( + typeof k.clientId !== "string" || + !clientPattern.test(k.clientId) || + typeof k.keyId !== "string" || + !keyPattern.test(k.keyId) || + typeof k.tokenSHA256 !== "string" || + !secretPattern.test(k.tokenSHA256) + ) + invalid(); + const from = timestamp(k.notBefore), + until = timestamp(k.notAfter); + const id = k.clientId + ":" + k.keyId; + const count = (clients.get(k.clientId) || 0) + 1; + if ( + until <= from || + keys.has(id) || + digests.has(k.tokenSHA256) || + count > 16 + ) + invalid(); + clients.set(k.clientId, count); + if (clients.size > 32) invalid(); + digests.add(k.tokenSHA256); + keys.set(id, { + clientId: k.clientId, + digest: Buffer.from(k.tokenSHA256, "hex"), + from, + until, + }); + } + return keys; +} + +/** Optional server-to-server read-only endpoint. No browser, owner or repository authority is inferred. */ +export function createReviewCapabilities( + raw?: string, + now: () => number = Date.now, +): RequestHandler { + const keys = raw === undefined ? null : registry(raw); + // One bounded counter per registered client, shared by its rotation keys. + const rates = new Map(); + const dummyDigest = Buffer.alloc(32); + return (req, res) => { + res.setHeader("Cache-Control", "no-store"); + res.setHeader("Referrer-Policy", "no-referrer"); + res.setHeader("X-Content-Type-Options", "nosniff"); + const fail = (status: number, code: string, retry?: number) => { + // Do not keep a rejected request alive while Node drains an unread body. + res.setHeader("Connection", "close"); + if (retry) res.setHeader("Retry-After", String(retry)); + res.status(status).json({ + contract, + code, + requestId: randomBytes(16).toString("hex"), + ...(retry ? { retryAfterSeconds: retry } : {}), + }); + }; + if (!keys || req.originalUrl !== "/service/v1/capabilities") + return fail(404, "not-found"); + if (req.method !== "GET") return fail(405, "invalid-request"); + // Reject browser context and bodies before authentication; never read or log them. + if ( + [ + "cookie", + "origin", + "referer", + "sec-fetch-site", + "sec-fetch-mode", + "sec-fetch-dest", + "sec-fetch-user", + ].some((h) => req.headers[h] !== undefined) + ) + return fail(403, "forbidden"); + if ( + req.headers["transfer-encoding"] !== undefined || + req.headers["content-type"] !== undefined || + (req.headers["content-length"] !== undefined && + req.headers["content-length"] !== "0") + ) + return fail(400, "invalid-request"); + const one = (name: string): string | undefined => { + let count = 0; + for (let i = 0; i < req.rawHeaders.length; i += 2) + if (req.rawHeaders[i].toLowerCase() === name) count++; + const value = req.headers[name]; + return count === 1 && typeof value === "string" ? value : undefined; + }; + const client = one("x-4open-artifact-client-id"), + keyId = one("x-4open-artifact-service-key-id"), + authorization = one("authorization"); + if ( + !client || + !clientPattern.test(client) || + !keyId || + !keyPattern.test(keyId) || + !authorization || + !/^Bearer [a-f0-9]{64}$/i.test(authorization) || + !secretPattern.test(authorization.slice(7)) + ) + return fail(401, "unauthorized"); + const key = keys.get(client + ":" + keyId); + const digest = createHash("sha256") + .update(authorization.slice(7), "ascii") + .digest(); + const equal = timingSafeEqual(digest, key ? key.digest : dummyDigest); + const time = now(); + if ( + !key || + !equal || + !Number.isFinite(time) || + time < key.from || + time >= key.until + ) + return fail(401, "unauthorized"); + const minute = Math.floor(time / 60000); + let rate = rates.get(client); + if (!rate || rate.minute !== minute) { + rate = { minute, count: 0 }; + rates.set(client, rate); + } + if (rate.count >= 60) + return fail( + 429, + "rate-limited", + Math.max(1, Math.ceil((60000 - (time % 60000)) / 1000)), + ); + rate.count++; + // No link, snapshot, protected-delivery or retention support exists yet. + res.status(200).json({ + contract, + clientId: client, + contracts: [contract], + available: [], + }); + }; +} diff --git a/src/server/service/review-completion-http.ts b/src/server/service/review-completion-http.ts new file mode 100644 index 0000000..b33d751 --- /dev/null +++ b/src/server/service/review-completion-http.ts @@ -0,0 +1,262 @@ +import { randomBytes } from "crypto"; +import { RequestHandler } from "express"; +import { + createReviewOwnerConsent, + ReviewConsentError, +} from "./review-owner-consent"; +import { + createReviewServiceAuth, + singleReviewHeader, +} from "./review-service-auth"; +import { decodeReviewJSON } from "./review-json"; + +const contract = "4open.artifacts/1"; +const opaque = /^[a-f0-9]{32}$/; +function exact( + value: unknown, + keys: string[], +): value is Record { + return ( + !!value && + typeof value === "object" && + !Array.isArray(value) && + Object.keys(value).sort().join(",") === keys.sort().join(",") + ); +} +function receipt(value: unknown, clientId: string, intentId: string): unknown { + if ( + !exact(value, [ + "contract", + "clientId", + "intentId", + "bindingId", + "submissionRef", + "policy", + "entitlementId", + ]) || + value.contract !== contract || + value.clientId !== clientId || + value.intentId !== intentId || + ![value.bindingId, value.submissionRef, value.entitlementId].every( + (id) => typeof id === "string" && opaque.test(id), + ) || + !exact(value.policy, ["version", "access", "retainUntil"]) + ) + throw new Error("Invalid receipt"); + const policy = value.policy; + if ( + !Number.isSafeInteger(policy.version) || + (policy.version as number) < 1 || + !["anonymous-link", "restricted-review"].includes( + policy.access as string, + ) || + typeof policy.retainUntil !== "string" || + !/^2[01][0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test( + policy.retainUntil, + ) || + !Number.isFinite(Date.parse(policy.retainUntil)) || + new Date(policy.retainUntil).toISOString() !== + policy.retainUntil.replace("Z", ".000Z") + ) + throw new Error("Invalid receipt"); + return value; +} + +/** Mount before global JSON parsing and browser authentication. Disabled without + * its own scoped registry. This factory is intentionally not mounted at startup. */ +export function createReviewCompletionService( + raw: string | undefined, + backend: Pick< + ReturnType, + "exchange" | "receipt" + >, + now: () => number = Date.now, +): RequestHandler { + const auth = + raw === undefined ? undefined : createReviewServiceAuth(raw, now); + const rates = new Map(); + let active = 0; + return async (req, res) => { + res.set({ + "Cache-Control": "no-store", + "Referrer-Policy": "no-referrer", + "X-Content-Type-Options": "nosniff", + }); + res.removeHeader("Access-Control-Allow-Origin"); + const fail = (status: number, code: string) => { + if (res.headersSent || res.destroyed) return; + res.setHeader("Connection", "close"); + res + .status(status) + .json({ contract, code, requestId: randomBytes(16).toString("hex") }); + }; + const exchange = req.originalUrl === "/service/v1/completions/exchange"; + const read = /^\/service\/v1\/intents\/([a-f0-9]{32})\/receipt$/.exec( + req.originalUrl, + ); + if (!auth || (!exchange && !read)) return fail(404, "not-found"); + if (req.method !== (exchange ? "POST" : "GET")) + return fail(405, "invalid-request"); + if ( + !req.secure || + [ + "cookie", + "origin", + "referer", + "sec-fetch-site", + "sec-fetch-mode", + "sec-fetch-dest", + "sec-fetch-user", + ].some((name) => req.headers[name] !== undefined) + ) + return fail(403, "forbidden"); + if (req.body !== undefined) return fail(503, "unavailable"); + if (req.headers["content-encoding"] !== undefined) + return fail(400, "invalid-request"); + for (const name of ["content-type", "content-length", "transfer-encoding"]) + if ( + req.headers[name] !== undefined && + singleReviewHeader(req, name) === undefined + ) + return fail(400, "invalid-request"); + if (exchange) { + if ( + !/^application\/json(?:\s*;\s*charset=utf-8)?$/i.test( + singleReviewHeader(req, "content-type") || "", + ) + ) + return fail(400, "invalid-request"); + const length = req.headers["content-length"]; + if ( + length !== undefined && + (!/^\d+$/.test(String(length)) || Number(length) > 65536) + ) + return fail(400, "invalid-request"); + } else if ( + req.headers["transfer-encoding"] !== undefined || + req.headers["content-type"] !== undefined || + (req.headers["content-length"] !== undefined && + req.headers["content-length"] !== "0") + ) + return fail(400, "invalid-request"); + const principal = auth.authenticate( + req, + exchange ? "completion.exchange" : "receipt.read", + ); + if (!principal) return fail(401, "unauthorized"); + const minute = Math.floor(now() / 60000); + let rate = rates.get(principal.clientId); + if (!rate || rate.minute !== minute) { + rate = { minute, count: 0 }; + rates.set(principal.clientId, rate); + } + if (rate.count++ >= 60) return fail(429, "rate-limited"); + if (active >= 4) return fail(503, "unavailable"); + active++; + let cancelBody: (() => void) | undefined; + let ended = false; + const close = () => { + ended = true; + cancelBody?.(); + }; + res.once("close", close); + const timer = setTimeout(() => { + ended = true; + fail(503, "unavailable"); + cancelBody?.(); + }, 15000); + try { + let intentId = read?.[1] || ""; + let result: unknown; + if (exchange) { + const bytes = await new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let size = 0; + const cleanup = () => { + req.off("data", data); + req.off("end", end); + req.off("aborted", abort); + req.off("error", abort); + cancelBody = undefined; + }; + const abort = () => { + cleanup(); + reject(new Error("Request unavailable")); + }; + const data = (chunk: Buffer) => { + size += chunk.length; + if (size > 65536) { + fail(400, "invalid-request"); + abort(); + } else chunks.push(chunk); + }; + const end = () => { + cleanup(); + resolve(Buffer.concat(chunks)); + }; + cancelBody = abort; + req.on("data", data); + req.once("end", end); + req.once("aborted", abort); + req.once("error", abort); + }); + if (ended) return; + let command: unknown; + try { + command = decodeReviewJSON(bytes); + } catch { + return fail(400, "invalid-request"); + } + if ( + !exact(command, [ + "contract", + "clientId", + "intentId", + "code", + "requestId", + ]) || + typeof command.contract !== "string" || + ![command.clientId, command.intentId, command.requestId].every( + (id) => typeof id === "string" && opaque.test(id), + ) || + typeof command.code !== "string" || + !/^[a-f0-9]{64}$/.test(command.code) + ) + return fail(400, "invalid-request"); + if (command.contract !== contract) + return fail(422, "unsupported-version"); + if (command.clientId !== principal.clientId) + return fail(403, "forbidden"); + if (!principal.valid()) return fail(401, "unauthorized"); + intentId = command.intentId as string; + result = await backend.exchange( + principal.clientId, + command as Parameters[1], + ); + } else { + if (!principal.valid()) return fail(401, "unauthorized"); + result = await backend.receipt(principal.clientId, intentId); + } + if (ended) return; + if (!principal.valid()) return fail(401, "unauthorized"); + res.status(200).json(receipt(result, principal.clientId, intentId)); + } catch (error) { + if (ended) return; + if (error instanceof ReviewConsentError) { + const codes = { + invalid: [400, "invalid-request"], + forbidden: [403, "forbidden"], + expired: [410, "expired"], + conflict: [409, "conflict"], + unavailable: [503, "unavailable"], + } as const; + const [status, code] = codes[error.kind]; + fail(status, code); + } else fail(503, "unavailable"); + } finally { + clearTimeout(timer); + res.off("close", close); + active--; + } + }; +} diff --git a/src/server/service/review-consent-http.ts b/src/server/service/review-consent-http.ts new file mode 100644 index 0000000..8124494 --- /dev/null +++ b/src/server/service/review-consent-http.ts @@ -0,0 +1,336 @@ +import { ReviewCallbacks } from "./review-callbacks"; +import { createHmac, timingSafeEqual } from "crypto"; +import * as express from "express"; +import { Request, Response } from "express"; +import { createReviewOwnerConsent } from "./review-owner-consent"; + +type BrowserSession = { passport?: { user?: unknown } }; +type Context = { accountId: string; sessionId: string }; +const opaque = /^[a-f0-9]{32}$/; +const secret = /^[a-f0-9]{64}$/; +function fields( + value: unknown, + names: string[], +): value is Record { + return ( + !!value && + typeof value === "object" && + !Array.isArray(value) && + Object.keys(value).sort().join(",") === names.sort().join(",") + ); +} +function reject(res: Response, status: number, code: string) { + if (!res.headersSent && !res.destroyed) res.setHeader("Connection", "close"); + if (!res.headersSent && !res.destroyed) + res.status(status).json({ error: { code } }); +} +async function current(req: Request): Promise { + const id = (req.user as { user?: { _id?: unknown } } | undefined)?.user?._id; + const accountId = id === undefined ? "" : String(id); + if ( + !req.isAuthenticated?.() || + !/^[a-f0-9]{24}$/.test(accountId) || + !/^[A-Za-z0-9_-]{24,256}$/.test(req.sessionID || "") + ) + return undefined; + const session = await new Promise( + (resolve, fail) => + req.sessionStore.get(req.sessionID, (error, value) => + error + ? fail(new Error("Session unavailable")) + : resolve(value as BrowserSession | undefined), + ), + ); + if (session?.passport?.user !== accountId) return undefined; + return { accountId, sessionId: req.sessionID }; +} + +/** Mount after current Passport/session authentication and BEFORE any JSON + * parser. This factory is not registered by application startup. TLS must be + * established directly or identified through the configured trusted proxy. */ +export function createReviewConsentRouter( + origin: string, + backend: ReturnType, + csrfKey: Buffer, + callbacks?: ReviewCallbacks, +) { + try { + if (new URL(origin).origin !== origin || !origin.startsWith("https://")) + throw new Error(); + } catch { + throw new Error("Invalid review consent origin"); + } + if (!Buffer.isBuffer(csrfKey) || csrfKey.length !== 32) + throw new Error("Invalid review consent CSRF key"); + const key = Buffer.from(csrfKey); + const csrf = (actor: Context) => + createHmac("sha256", key) + .update( + "4open.review-consent-csrf/1." + + actor.accountId + + "." + + actor.sessionId, + ) + .digest(); + const router = express.Router({ strict: true, caseSensitive: true }); + const rates = new Map(); + router.use((req, res, next) => { + res.setHeader("Cache-Control", "no-store"); + res.setHeader("Referrer-Policy", "no-referrer"); + res.setHeader("X-Content-Type-Options", "nosniff"); + res.removeHeader("Access-Control-Allow-Origin"); + if (!["/csrf", "/preview", "/confirm", ...(callbacks ? ["/handoff"] : [])].includes(req.url)) + return reject(res, 404, "not-found"); + if (req.method !== (req.url === "/csrf" ? "GET" : "POST")) + return reject(res, 405, "invalid-request"); + if ( + req.url === "/csrf" && + (req.headers["transfer-encoding"] !== undefined || + (req.headers["content-length"] !== undefined && + req.headers["content-length"] !== "0")) + ) + return reject(res, 400, "invalid-request"); + if ( + !req.secure || + req.headers.authorization !== undefined || + req.headers["content-encoding"] !== undefined + ) + return reject(res, 403, "forbidden"); + for (const name of [ + "origin", + "content-type", + "x-review-csrf", + "sec-fetch-site", + ]) { + if ( + req.rawHeaders.filter((v, i) => i % 2 === 0 && v.toLowerCase() === name) + .length > 1 + ) + return reject(res, 403, "forbidden"); + } + if ( + (req.headers.origin !== undefined && req.headers.origin !== origin) || + (req.method === "POST" && req.headers.origin !== origin) || + (req.headers["sec-fetch-site"] !== undefined && + req.headers["sec-fetch-site"] !== "same-origin") + ) + return reject(res, 403, "forbidden"); + if ( + req.method === "POST" && + !/^application\/json(?:\s*;\s*charset=utf-8)?$/i.test( + req.headers["content-type"] || "", + ) + ) + return reject(res, 415, "invalid-request"); + // Fail closed if mounted after a body parser that bypassed our byte limit. + if (req.body !== undefined) return reject(res, 503, "unavailable"); + const controller = new AbortController(); + res.locals.consentSignal = controller.signal; + const timer = setTimeout(() => { + controller.abort(); + reject(res, 408, "expired"); + req.destroy(); + }, 15000); + const done = () => { + clearTimeout(timer); + controller.abort(); + }; + res.once("close", done); + res.once("finish", done); + next(); + }); + router.use(async (req, res, next) => { + try { + const actor = await current(req); + if (res.destroyed || res.headersSent || res.locals.consentSignal.aborted) + return; + if (!actor) return reject(res, 401, "unauthorized"); + if (req.method === "POST") { + const supplied = req.headers["x-review-csrf"]; + if ( + typeof supplied !== "string" || + !secret.test(supplied) || + !timingSafeEqual(Buffer.from(supplied, "hex"), csrf(actor)) + ) + return reject(res, 403, "forbidden"); + } + const now = Date.now(); + for (const [id, rate] of rates) if (rate.until <= now) rates.delete(id); + const rate = rates.get(actor.accountId) || { + until: now + 60000, + count: 0, + }; + if ( + rate.count >= 60 || + (!rates.has(actor.accountId) && rates.size >= 1024) + ) { + res.setHeader("Retry-After", "60"); + return reject(res, 429, "rate-limited"); + } + rate.count++; + rates.set(actor.accountId, rate); + res.locals.consentActor = actor; + next(); + } catch { + reject(res, 503, "unavailable"); + } + }); + router.get("/csrf", async (req, res) => { + try { + const fresh = await current(req); + if (!fresh || fresh.accountId !== res.locals.consentActor.accountId) + return reject(res, 401, "unauthorized"); + // Derive without saving the session: a late CSRF response must never + // resurrect a session that a concurrent logout has destroyed. + res.json({ csrf: csrf(fresh).toString("hex"), handoffEnabled: !!callbacks }); + } catch { + reject(res, 503, "unavailable"); + } + }); + router.use(express.json({ limit: 12288, strict: true, inflate: false })); + // Uploads can outlive the session checked before body parsing. Revalidate + // before any provider call or consent write, not only before the response. + router.use(async (req, res, next) => { + try { + const actor = res.locals.consentActor as Context; + const fresh = await current(req); + if (res.destroyed || res.headersSent || res.locals.consentSignal.aborted) + return; + if ( + !fresh || + fresh.accountId !== actor.accountId || + fresh.sessionId !== actor.sessionId + ) + return reject(res, 401, "unauthorized"); + next(); + } catch { + reject(res, 503, "unavailable"); + } + }); + router.post(["/preview", "/confirm", "/handoff"], async (req, res) => { + const actor = res.locals.consentActor as Context; + try { + let result: unknown; + if (req.url === "/handoff") { + if (!callbacks || !fields(req.body, ["ticket"]) || typeof req.body.ticket !== "string" || !req.body.ticket || req.body.ticket.length > 8192) return reject(res, 400, "invalid-request"); + const result = await backend.handoff(actor, req.body.ticket, callbacks.resolve); + if (res.destroyed || res.headersSent) return; + const fresh = await current(req); + if (!fresh || fresh.accountId !== actor.accountId || fresh.sessionId !== actor.sessionId) return reject(res, 401, "unauthorized"); + res.json({ completion: { contract: result.completion.contract, clientId: result.completion.clientId, intentId: result.completion.intentId, code: result.completion.code, expiresAt: result.completion.expiresAt }, callbackUrl: result.callbackUrl }); + } else if (req.url === "/preview") { + if ( + !fields(req.body, ["repositoryId", "intent"]) || + typeof req.body.repositoryId !== "string" || + !/^[A-Za-z0-9_-]{3,128}$/.test(req.body.repositoryId) || + !fields(req.body.intent, [ + "contract", + "clientId", + "intentId", + "token", + "requestId", + ]) + ) + return reject(res, 422, "invalid-request"); + const input = req.body.intent; + if ( + input.contract !== "4open.artifacts/1" || + ![input.clientId, input.intentId, input.requestId].every( + (v) => typeof v === "string" && opaque.test(v), + ) || + typeof input.token !== "string" || + !secret.test(input.token) + ) + return reject(res, 422, "invalid-request"); + const preview = await backend.preview( + actor, + req.body.repositoryId, + { + contract: input.contract, + clientId: input.clientId as string, + intentId: input.intentId as string, + requestId: input.requestId as string, + token: input.token, + }, + res.locals.consentSignal, + ); + result = { + ticket: preview.ticket, + repositoryName: preview.repositoryName, + policy: { + version: preview.policy.version, + access: preview.policy.access, + retainUntil: preview.policy.retainUntil, + }, + expiresAt: preview.expiresAt, + }; + } else { + if ( + !fields(req.body, [ + "ticket", + "requestId", + "acceptAccess", + "acceptRetention", + ]) || + typeof req.body.ticket !== "string" || + req.body.ticket.length > 8192 || + typeof req.body.requestId !== "string" || + !opaque.test(req.body.requestId) || + req.body.acceptAccess !== true || + req.body.acceptRetention !== true + ) + return reject(res, 422, "invalid-request"); + const saved = await backend.confirm(actor, req.body.ticket, { + requestId: req.body.requestId, + acceptAccess: true, + acceptRetention: true, + }); + result = { + requestId: saved.requestId, + policy: { + version: saved.policy.version, + access: saved.policy.access, + retainUntil: saved.policy.retainUntil, + }, + confirmedAt: saved.confirmedAt, + }; + } + const fresh = await current(req); + if ( + !fresh || + fresh.accountId !== actor.accountId || + fresh.sessionId !== actor.sessionId + ) + return reject(res, 401, "unauthorized"); + if (!res.headersSent && !res.destroyed) res.json(result); + } catch (error) { + const kind = (error as { kind?: string })?.kind; + const status = + kind === "invalid" || kind === "protocol" + ? 422 + : kind === "forbidden" || kind === "rejected" + ? 403 + : kind === "expired" + ? 410 + : kind === "conflict" + ? 409 + : 503; + reject( + res, + status, + status === 503 + ? "unavailable" + : status === 422 + ? "invalid-request" + : kind!, + ); + } + }); + router.use(((error, _req, res, _next) => + reject( + res, + error?.status === 413 ? 413 : 400, + "invalid-request", + )) as express.ErrorRequestHandler); + return router; +} diff --git a/src/server/service/review-intent-client.ts b/src/server/service/review-intent-client.ts new file mode 100644 index 0000000..17dcd5f --- /dev/null +++ b/src/server/service/review-intent-client.ts @@ -0,0 +1,357 @@ +import { decodeReviewJSON } from "./review-json"; +import * as https from "https"; +import { ClientRequest } from "http"; + +const contract = "4open.artifacts/1", + limit = 65536; +const opaque = /^[a-f0-9]{32}$/, + secret = /^[a-f0-9]{64}$/; +export type IntentClientFailure = + | "invalid" + | "busy" + | "unavailable" + | "rejected" + | "conflict" + | "expired" + | "not-found" + | "protocol"; +export class IntentClientError extends Error { + constructor(public readonly kind: IntentClientFailure) { + super("Review intent consumption " + kind); + this.name = "IntentClientError"; + } +} +export interface ReviewIntentClientConfig { + origin: string; + clientId: string; + keyId: string; + token: string; + notBefore: string; + notAfter: string; +} +export interface IntentConsumption { + contract: string; + clientId: string; + intentId: string; + token: string; + requestId: string; +} +export type ConsumedReviewIntent = Readonly<{ + contract: string; + clientId: string; + intentId: string; + submissionRef: string; + callbackId: string; + entitlementId: string; + expiresAt: string; + policy: Readonly<{ + version: number; + access: "anonymous-link" | "restricted-review"; + retainUntil: string; + }>; +}>; +function fail(kind: IntentClientFailure): never { + throw new IntentClientError(kind); +} +function object(value: unknown, fields: string[]): Record { + if (!value || typeof value !== "object" || Array.isArray(value)) + fail("protocol"); + const out = value as Record; + if ( + Object.keys(out).length !== fields.length || + fields.some((k) => !Object.prototype.hasOwnProperty.call(out, k)) + ) + fail("protocol"); + return out; +} +function instant(value: unknown): number { + if ( + typeof value !== "string" || + !/^2[01][0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(value) + ) + fail("protocol"); + const time = Date.parse(value); + if ( + !Number.isFinite(time) || + new Date(time).toISOString() !== value.replace("Z", ".000Z") + ) + fail("protocol"); + return time; +} +function id(value: unknown): value is string { + return typeof value === "string" && opaque.test(value); +} + +function decode(bytes: Buffer): unknown { + try { + return decodeReviewJSON(bytes); + } catch { + return fail("protocol"); + } +} + +function intent( + bytes: Buffer, + clientId: string, + intentId: string, +): ConsumedReviewIntent { + const p = object(decode(bytes), [ + "contract", + "clientId", + "intentId", + "submissionRef", + "callbackId", + "policy", + "expiresAt", + "entitlementId", + ]); + const policy = object(p.policy, ["version", "access", "retainUntil"]), + now = Date.now(); + if ( + p.contract !== contract || + p.clientId !== clientId || + p.intentId !== intentId || + !id(p.submissionRef) || + !id(p.callbackId) || + !id(p.entitlementId) + ) + fail("protocol"); + if ( + !Number.isSafeInteger(policy.version) || + (policy.version as number) < 1 || + (policy.access !== "anonymous-link" && + policy.access !== "restricted-review") || + instant(policy.retainUntil) <= now + ) + fail("protocol"); + const expires = instant(p.expiresAt); + if (expires <= now || expires > now + 600000) fail("protocol"); + return Object.freeze({ + contract, + clientId, + intentId, + submissionRef: p.submissionRef, + callbackId: p.callbackId, + entitlementId: p.entitlementId, + expiresAt: p.expiresAt as string, + policy: Object.freeze({ + version: policy.version as number, + access: policy.access as "anonymous-link" | "restricted-review", + retainUntil: policy.retainUntil as string, + }), + }); +} + +/** Trusted server configuration only. One attempt per call; the caller retains + * its request ID for an explicit retry. No browser callback or owner authority + * is established by consuming an intent. No credentials are exposed on the + * returned object, through serialization, or in transport errors. */ +export function createReviewIntentConsumer(config: ReviewIntentClientConfig) { + let origin: string, + clientId: string, + keyId: string, + token: string, + from: number, + until: number; + try { + const c = object(config, [ + "origin", + "clientId", + "keyId", + "token", + "notBefore", + "notAfter", + ]); + if ( + typeof c.origin !== "string" || + c.origin.length > 2048 || + !id(c.clientId) || + typeof c.keyId !== "string" || + !/^[A-Za-z0-9_-]{1,64}$/.test(c.keyId) || + typeof c.token !== "string" || + !secret.test(c.token) + ) + fail("invalid"); + const url = new URL(c.origin); + if ( + url.protocol !== "https:" || + url.origin !== c.origin || + url.username || + url.password + ) + fail("invalid"); + origin = c.origin; + clientId = c.clientId; + keyId = c.keyId; + token = c.token; + from = instant(c.notBefore); + until = instant(c.notAfter); + if (until <= from || Date.now() < from || Date.now() >= until) + fail("invalid"); + } catch { + return fail("invalid"); + } + let active = 0; + return Object.freeze({ + consume( + input: IntentConsumption, + signal?: AbortSignal, + ): Promise { + let body: Buffer, expectedIntent: string; + try { + const p = object(input, [ + "contract", + "clientId", + "intentId", + "token", + "requestId", + ]); + if ( + p.contract !== contract || + p.clientId !== clientId || + !id(p.intentId) || + !id(p.requestId) || + typeof p.token !== "string" || + !secret.test(p.token) || + Date.now() < from || + Date.now() >= until + ) + fail("invalid"); + expectedIntent = p.intentId; + body = Buffer.from( + JSON.stringify({ + contract, + clientId, + intentId: expectedIntent, + token: p.token, + requestId: p.requestId, + }), + ); + } catch { + return Promise.reject(new IntentClientError("invalid")); + } + if (signal?.aborted) + return Promise.reject(new IntentClientError("unavailable")); + if (active >= 4) return Promise.reject(new IntentClientError("busy")); + active++; + return new Promise((resolve, reject) => { + let request: ClientRequest | undefined, + settled = false; + const abort = () => finish("unavailable"); + const timer = setTimeout(abort, 10000); + function finish( + error?: IntentClientFailure, + value?: ConsumedReviewIntent, + ) { + if (settled) return; + settled = true; + clearTimeout(timer); + signal?.removeEventListener("abort", abort); + active--; + if (error) { + request?.destroy(); + reject(new IntentClientError(error)); + } else resolve(value!); + } + signal?.addEventListener("abort", abort, { once: true }); + try { + request = https.request( + origin + "/service/v1/artifact-intents/consume", + { + method: "POST", + agent: false, + rejectUnauthorized: true, + minVersion: "TLSv1.2", + maxHeaderSize: 16384, + headers: { + Authorization: "Bearer " + token, + "X-4open-Artifact-Client-Id": clientId, + "X-4open-Artifact-Service-Key-Id": keyId, + "Content-Type": "application/json", + Accept: "application/json", + "Accept-Encoding": "identity", + "Content-Length": body.length, + }, + }, + (response) => { + response.once("aborted", abort); + response.once("error", abort); + const status = response.statusCode || 0; + if (status !== 200) { + const kind: IntentClientFailure = + status === 404 + ? "not-found" + : status === 409 + ? "conflict" + : status === 410 + ? "expired" + : [400, 401, 403, 422].includes(status) + ? "rejected" + : [408, 429, 500, 502, 503, 504].includes(status) + ? "unavailable" + : "protocol"; + finish(kind); + return; + } + const types = response.rawHeaders.filter( + (_v, i) => + i % 2 === 0 && + response.rawHeaders[i].toLowerCase() === "content-type", + ).length; + const length = response.headers["content-length"]; + if ( + types !== 1 || + !/^application\/json(?:\s*;\s*charset=(?:utf-8|"utf-8"))?\s*$/i.test( + response.headers["content-type"] || "", + ) || + response.headers["content-encoding"] !== undefined || + response.headers["set-cookie"] !== undefined || + (length !== undefined && + (!/^[0-9]+$/.test(length) || Number(length) > limit)) + ) { + finish("protocol"); + return; + } + let size = 0; + const chunks: Buffer[] = []; + response.on("data", (chunk: Buffer) => { + size += chunk.length; + if (size > limit) finish("protocol"); + else if (!settled) chunks.push(chunk); + }); + response.once("end", () => { + if (settled) return; + if ( + signal?.aborted || + Date.now() < from || + Date.now() >= until + ) { + finish("unavailable"); + return; + } + try { + finish( + undefined, + intent( + Buffer.concat(chunks, size), + clientId, + expectedIntent, + ), + ); + } catch { + finish("protocol"); + } + }); + }, + ); + request.once("error", abort); + request.setTimeout(5000, abort); + if (signal?.aborted) abort(); + if (!settled) request.end(body); + } catch { + finish("unavailable"); + } + }); + }, + }); +} diff --git a/src/server/service/review-json.ts b/src/server/service/review-json.ts new file mode 100644 index 0000000..abc6cd0 --- /dev/null +++ b/src/server/service/review-json.ts @@ -0,0 +1,68 @@ +function invalid(): never { + throw new Error("Invalid review JSON"); +} + +// JSON.parse checks syntax; this bounded walk additionally rejects duplicate +// decoded keys, including escaped spellings, before typed field validation. +export function decodeReviewJSON(bytes: Buffer): unknown { + if (bytes.length > 65536) invalid(); + const text = bytes.toString("utf8"); + if (!Buffer.from(text, "utf8").equals(bytes)) invalid(); + let parsed: unknown; + try { + parsed = JSON.parse(text); + } catch { + invalid(); + } + let at = 0; + const space = () => { + while (/\s/.test(text[at] || "") && at < text.length) at++; + }; + const string = (): string => { + const start = at++; + while (at < text.length) { + if (text[at] === "\\") { + at += 2; + continue; + } + if (text[at++] === '"') return JSON.parse(text.slice(start, at)); + } + return invalid(); + }; + const walk = (depth: number): void => { + if (depth > 8) invalid(); + space(); + if (text[at] === '"') { + string(); + return; + } + if (text[at] === "{" || text[at] === "[") { + const isObject = text[at++] === "{", + end = isObject ? "}" : "]", + keys = new Set(); + space(); + if (text[at] === end) { + at++; + return; + } + for (;;) { + space(); + if (isObject) { + const key = string(); + if (keys.has(key)) invalid(); + keys.add(key); + space(); + at++; // colon; syntax already checked + } + walk(depth + 1); + space(); + if (text[at++] === end) return; + } + } + while (at < text.length && !/[\s,}\]]/.test(text[at])) at++; + }; + walk(0); + space(); + if (at !== text.length) invalid(); + return parsed; +} diff --git a/src/server/service/review-owner-consent.ts b/src/server/service/review-owner-consent.ts new file mode 100644 index 0000000..db4857f --- /dev/null +++ b/src/server/service/review-owner-consent.ts @@ -0,0 +1,602 @@ +import { createHash, createHmac, timingSafeEqual, randomBytes } from "crypto"; +import { Connection, ClientSession, Types } from "mongoose"; +import { + ConsumedReviewIntent, + IntentConsumption, + createReviewIntentConsumer, +} from "./review-intent-client"; + +type Principal = { accountId: string; sessionId: string }; +type Authority = { + accountId: string; + repositoryId: string; + repositoryName: string; + role: "owner" | "coauthor"; +}; +type Quote = { + version: 1; + accountId: string; + sessionHash: string; + repositoryId: string; + intent: ConsumedReviewIntent; +}; +export type ConsentReceipt = Readonly<{ + clientId: string; + intentId: string; + accountId: string; + repositoryId: string; + role: "owner" | "coauthor"; + policy: ConsumedReviewIntent["policy"]; + requestId: string; + confirmedAt: string; +}>; +type SavedConsent = { + _id: string; + accountId: string; + repositoryId: string; + requestId: string; + ticketHash: string; + receipt: ConsentReceipt; + intent: ConsumedReviewIntent; +}; +type BindingReceipt = { + contract: "4open.artifacts/1"; + clientId: string; + intentId: string; + bindingId: string; + submissionRef: string; + policy: ConsumedReviewIntent["policy"]; + entitlementId: string; +}; +type SavedCompletion = { + _id: string; + accountId: string; + repositoryId: string; + ticketHash: string; + intent: ConsumedReviewIntent; + nonce: string; + codeHash: string; + expiresAt: string; + exchange?: { requestId: string; receipt: BindingReceipt }; +}; +export class ReviewConsentError extends Error { + constructor( + public readonly kind: + "invalid" | "forbidden" | "expired" | "conflict" | "unavailable", + ) { + super("Review owner consent " + kind); + this.name = "ReviewConsentError"; + } +} +const deny = (kind: ReviewConsentError["kind"]): never => { + throw new ReviewConsentError(kind); +}; +const hash = (value: string) => + createHash("sha256").update(value).digest("hex"); +const objectId = (value: unknown): value is string => + typeof value === "string" && /^[a-f0-9]{24}$/.test(value); +function principal(value: Principal): { + accountId: string; + sessionHash: string; +} { + if ( + !value || + !objectId(value.accountId) || + typeof value.sessionId !== "string" || + !/^[A-Za-z0-9_-]{24,256}$/.test(value.sessionId) + ) + deny("invalid"); + return { accountId: value.accountId, sessionHash: hash(value.sessionId) }; +} + +/** Server-side consent storage. Principals must come from current authenticated + * browser sessions, never request JSON. A future HTTP adapter must apply its + * own same-origin/CSRF checks. No route or provider is enabled by this factory. */ +export function createReviewOwnerConsent( + connection: Connection, + consumer: ReturnType, + signingKey: Buffer, +) { + if (!Buffer.isBuffer(signingKey) || signingKey.length !== 32) deny("invalid"); + const key = Buffer.from(signingKey); + let activeTransactions = 0; + let activeReads = 0; + const signature = (body: string) => + createHmac("sha256", key) + .update("4open.review-consent/1." + body) + .digest(); + function decode(ticket: string, who: ReturnType): Quote { + if ( + typeof ticket !== "string" || + ticket.length > 8192 || + !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{43}$/.test(ticket) + ) + deny("invalid"); + const [body, encoded] = ticket.split("."); + const supplied = Buffer.from(encoded, "base64url"); + if ( + supplied.length !== 32 || + supplied.toString("base64url") !== encoded || + !timingSafeEqual(supplied, signature(body)) + ) + deny("invalid"); + let quote: Quote; + try { + quote = JSON.parse(Buffer.from(body, "base64url").toString("utf8")); + } catch { + return deny("invalid"); + } + if ( + quote.version !== 1 || + quote.accountId !== who.accountId || + quote.sessionHash !== who.sessionHash || + !objectId(quote.repositoryId) + ) + deny("forbidden"); + if ( + Date.parse(quote.intent.expiresAt) <= Date.now() || + Date.parse(quote.intent.policy.retainUntil) <= Date.now() + ) + deny("expired"); + return quote; + } + async function transaction( + who: ReturnType, + repository: { repoId: string } | { _id: Types.ObjectId }, + work: (authority: Authority, session: ClientSession) => Promise, + ): Promise { + if (connection.readyState !== 1 || activeTransactions >= 4) + return deny("unavailable"); + activeTransactions++; + const session = await connection.startSession().catch(() => { + activeTransactions--; + return deny("unavailable"); + }); + try { + session.startTransaction({ + readConcern: { level: "snapshot" }, + writeConcern: { w: "majority", wtimeoutMS: 3000 }, + maxCommitTimeMS: 5000, + }); + // Touch the authority documents in the same transaction as the receipt. + // Concurrent disablement, ownership changes and coauthor removal then + // conflict with this transaction instead of producing a stale consent. + const user = ( + await connection.db.collection("users").findOneAndUpdate( + { + _id: new Types.ObjectId(who.accountId), + $or: [{ status: "active" }, { status: { $exists: false } }], + }, + { $inc: { reviewConsentRevision: 1 } }, + { + session, + returnDocument: "after", + maxTimeMS: 3000, + projection: { externalIDs: 1 }, + }, + ) + ).value; + if (!user) return deny("forbidden"); + const githubId = user.externalIDs?.github; + const roles: Record[] = [ + { owner: new Types.ObjectId(who.accountId) }, + ]; + if (typeof githubId === "string" && /^[1-9][0-9]{0,19}$/.test(githubId)) + roles.push({ "coauthors.githubId": githubId }); + const repo = ( + await connection.db + .collection("anonymizedrepositories") + .findOneAndUpdate( + { ...repository, status: "ready", $or: roles }, + { $inc: { reviewConsentRevision: 1 } }, + { + session, + returnDocument: "after", + maxTimeMS: 3000, + projection: { repoId: 1, owner: 1, options: 1 }, + }, + ) + ).value; + if (!repo) return deny("forbidden"); + if ( + repo.options?.expirationMode !== "never" && + repo.options?.expirationDate && + (!Number.isFinite(new Date(repo.options.expirationDate).getTime()) || + new Date(repo.options.expirationDate).getTime() <= Date.now()) + ) + return deny("expired"); + const authority: Authority = { + accountId: who.accountId, + repositoryId: String(repo._id), + repositoryName: repo.repoId, + role: String(repo.owner) === who.accountId ? "owner" : "coauthor", + }; + const result = await work(authority, session); + await session.commitTransaction(); + return result; + } catch (error) { + if (session.inTransaction()) + await session.abortTransaction().catch(() => undefined); + if (error instanceof ReviewConsentError) throw error; + return deny("unavailable"); + } finally { + activeTransactions--; + await session.endSession().catch(() => undefined); + } + } + const completionCode = (id: string, nonce: string) => + createHmac("sha256", key) + .update("4open.review-completion/1." + id + "." + nonce) + .digest("hex"); + const api = { + async handoff(actor: Principal, ticket: string, resolveCallback: (clientId: string, callbackId: string) => string | undefined) { + const quote = decode(ticket, principal(actor)); + const callbackUrl = resolveCallback(quote.intent.clientId, quote.intent.callbackId); + if (!callbackUrl) return deny("forbidden"); + const completion = await api.completion(actor, ticket); + return Object.freeze({ completion, callbackUrl }); + }, + // Called only with the current authenticated browser principal. The HTTP + // adapter must retain its session reload, same-origin and CSRF checks. + async completion(actor: Principal, ticket: string) { + const who = principal(actor), + quote = decode(ticket, who); + return transaction( + who, + { _id: new Types.ObjectId(quote.repositoryId) }, + async (authority, session) => { + decode(ticket, who); + const id = quote.intent.clientId + ":" + quote.intent.intentId; + const consent = await connection.db + .collection("review_owner_consents") + .findOne({ _id: id }, { session, maxTimeMS: 3000 }); + if ( + !consent || + consent.ticketHash !== hash(ticket) || + consent.accountId !== who.accountId || + consent.repositoryId !== authority.repositoryId || + !consent.intent + ) + return deny("forbidden"); + const collection = + connection.db.collection("review_completions"); + let saved = await collection.findOne( + { _id: id }, + { session, maxTimeMS: 3000 }, + ); + if ( + saved && + (saved.ticketHash !== consent.ticketHash || + saved.accountId !== who.accountId || + saved.repositoryId !== authority.repositoryId) + ) + return deny("conflict"); + if (!saved) { + const nonce = randomBytes(32).toString("hex"); + saved = { + _id: id, + accountId: who.accountId, + repositoryId: authority.repositoryId, + ticketHash: consent.ticketHash, + intent: consent.intent, + nonce, + codeHash: hash(completionCode(id, nonce)), + expiresAt: new Date( + Math.floor( + Math.min( + Date.now() + 300000, + Date.parse(consent.intent.expiresAt), + Date.parse(consent.intent.policy.retainUntil), + ) / 1000, + ) * 1000, + ) + .toISOString() + .replace(/\.000Z$/, "Z"), + }; + await collection.insertOne(saved, { session, maxTimeMS: 3000 }); + } + if (Date.parse(saved.expiresAt) <= Date.now()) return deny("expired"); + const code = completionCode(id, saved.nonce); + // A key rotation must not silently issue a different code for a saved + // intent. Keep the issuing key available for the short code lifetime. + if (hash(code) !== saved.codeHash) return deny("conflict"); + return Object.freeze({ + contract: "4open.artifacts/1", + clientId: saved.intent.clientId, + intentId: saved.intent.intentId, + code, + expiresAt: saved.expiresAt, + }); + }, + ); + }, + // authenticatedClientId must come from service credential verification, + // never browser identity or the request body's clientId alone. + async exchange( + authenticatedClientId: string, + input: { + contract: string; + clientId: string; + intentId: string; + code: string; + requestId: string; + }, + ): Promise { + if ( + !input || + Object.keys(input).sort().join(",") !== + "clientId,code,contract,intentId,requestId" || + input.contract !== "4open.artifacts/1" || + ![input.clientId, input.intentId, input.requestId].every( + (value) => typeof value === "string" && /^[a-f0-9]{32}$/.test(value), + ) || + typeof input.code !== "string" || + !/^[a-f0-9]{64}$/.test(input.code) + ) + return deny("invalid"); + const command = { ...input }; + if (authenticatedClientId !== command.clientId) return deny("forbidden"); + if (connection.readyState !== 1 || activeReads >= 4) + return deny("unavailable"); + const id = command.clientId + ":" + command.intentId; + const collection = + connection.db.collection("review_completions"); + const read = async (session?: ClientSession) => { + const saved = await collection.findOne( + { _id: id }, + { session, maxTimeMS: 3000, ...(session ? {} : { readConcern: { level: "majority" as const } }) }, + ); + if (!saved) return deny("forbidden"); + if ( + !timingSafeEqual( + Buffer.from(saved.codeHash, "hex"), + Buffer.from(hash(command.code), "hex"), + ) + ) + return deny( + saved.exchange?.requestId === command.requestId + ? "conflict" + : "forbidden", + ); + return saved; + }; + activeReads++; + try { + const before = await read(); + if (before.exchange) { + if (before.exchange.requestId !== command.requestId) + return deny("conflict"); + return Object.freeze(before.exchange.receipt); + } + return await transaction( + { accountId: before.accountId, sessionHash: "" }, + { _id: new Types.ObjectId(before.repositoryId) }, + async (_authority, session) => { + const saved = await read(session); + if (saved.exchange) { + if (saved.exchange.requestId !== command.requestId) + return deny("conflict"); + return Object.freeze(saved.exchange.receipt); + } + if ( + Date.parse(saved.expiresAt) <= Date.now() || + Date.parse(saved.intent.policy.retainUntil) <= Date.now() + ) + return deny("expired"); + // The unique request document detects a reused key on another intent. + const requests = connection.db.collection<{ + _id: string; + intentId: string; + }>("review_completion_requests"); + const requestKey = command.clientId + ":" + command.requestId; + if ( + await requests.findOne( + { _id: requestKey }, + { session, maxTimeMS: 3000 }, + ) + ) + return deny("conflict"); + const receipt: BindingReceipt = { + contract: "4open.artifacts/1", + clientId: saved.intent.clientId, + intentId: saved.intent.intentId, + bindingId: randomBytes(16).toString("hex"), + submissionRef: saved.intent.submissionRef, + policy: { ...saved.intent.policy }, + entitlementId: saved.intent.entitlementId, + }; + await connection.db + .collection<{ + _id: string; + clientId: string; + intentId: string; + accountId: string; + repositoryId: string; + state: "pending"; + revision: number; + receipt: BindingReceipt; + }>("review_artifact_bindings") + .insertOne( + { + _id: receipt.bindingId, + clientId: command.clientId, + intentId: command.intentId, + accountId: saved.accountId, + repositoryId: saved.repositoryId, + state: "pending", + revision: 1, + receipt, + }, + { session, maxTimeMS: 3000 }, + ); + await requests.insertOne( + { _id: requestKey, intentId: command.intentId }, + { session, maxTimeMS: 3000 }, + ); + await collection.updateOne( + { _id: id }, + { $set: { exchange: { requestId: command.requestId, receipt } } }, + { session, maxTimeMS: 3000 }, + ); + return Object.freeze(receipt); + }, + ); + } catch (error) { + if (error instanceof ReviewConsentError) throw error; + return deny("unavailable"); + } finally { + activeReads--; + } + }, + async receipt( + authenticatedClientId: string, + intentId: string, + ): Promise { + if ( + ![authenticatedClientId, intentId].every( + (value) => typeof value === "string" && /^[a-f0-9]{32}$/.test(value), + ) + ) + return deny("invalid"); + if (connection.readyState !== 1 || activeReads >= 4) + return deny("unavailable"); + activeReads++; + try { + const saved = await connection.db + .collection("review_completions") + .findOne( + { _id: authenticatedClientId + ":" + intentId }, + { maxTimeMS: 3000, readConcern: { level: "majority" }, projection: { exchange: 1 } }, + ); + if (!saved?.exchange) return deny("forbidden"); + return Object.freeze(saved.exchange.receipt); + } catch (error) { + if (error instanceof ReviewConsentError) throw error; + return deny("unavailable"); + } finally { + activeReads--; + } + }, + async preview( + actor: Principal, + repositoryName: string, + input: IntentConsumption, + signal?: AbortSignal, + ) { + const who = principal(actor); + const command = { ...input }; + if ( + typeof repositoryName !== "string" || + !/^[A-Za-z0-9_-]{3,128}$/.test(repositoryName) + ) + return deny("invalid"); + const before = await transaction( + who, + { repoId: repositoryName }, + async (authority) => authority, + ); + // The provider call holds no database transaction or authority lock. + const intent = await consumer.consume(command, signal); + const current = await transaction( + who, + { _id: new Types.ObjectId(before.repositoryId) }, + async (authority) => authority, + ); + if ( + signal?.aborted || + Date.parse(intent.expiresAt) <= Date.now() || + Date.parse(intent.policy.retainUntil) <= Date.now() + ) + return deny("expired"); + const quote: Quote = { + version: 1, + accountId: who.accountId, + sessionHash: who.sessionHash, + repositoryId: current.repositoryId, + intent, + }; + const body = Buffer.from(JSON.stringify(quote)).toString("base64url"); + return Object.freeze({ + ticket: body + "." + signature(body).toString("base64url"), + repositoryName: current.repositoryName, + policy: intent.policy, + expiresAt: intent.expiresAt, + }); + }, + async confirm( + actor: Principal, + ticket: string, + input: { + requestId: string; + acceptAccess: boolean; + acceptRetention: boolean; + }, + ): Promise { + const who = principal(actor); + if ( + !input || + Object.keys(input).sort().join(",") !== + "acceptAccess,acceptRetention,requestId" || + input.acceptAccess !== true || + input.acceptRetention !== true || + typeof input.requestId !== "string" || + !/^[a-f0-9]{32}$/.test(input.requestId) + ) + return deny("invalid"); + const quote = decode(ticket, who), + requestId = input.requestId; + return transaction( + who, + { _id: new Types.ObjectId(quote.repositoryId) }, + async (authority, session) => { + decode(ticket, who); // Recheck expiry after waiting for authority locks. + const collection = connection.db.collection( + "review_owner_consents", + ); + const id = quote.intent.clientId + ":" + quote.intent.intentId; + const ticketHash = hash(ticket); + // A string _id binds each intent to at most one accepted repository and + // session. No raw session ID, intent token or service key is persisted. + const existing = await collection.findOne( + { _id: id }, + { session, maxTimeMS: 3000 }, + ); + if (existing) { + if ( + existing.ticketHash !== ticketHash || + existing.requestId !== requestId || + existing.accountId !== who.accountId || + existing.repositoryId !== authority.repositoryId + ) + return deny("conflict"); + return Object.freeze(existing.receipt as ConsentReceipt); + } + const receipt: ConsentReceipt = Object.freeze({ + clientId: quote.intent.clientId, + intentId: quote.intent.intentId, + accountId: who.accountId, + repositoryId: authority.repositoryId, + role: authority.role, + policy: quote.intent.policy, + requestId, + confirmedAt: new Date().toISOString(), + }); + await collection.insertOne( + { + _id: id, + accountId: who.accountId, + repositoryId: authority.repositoryId, + requestId, + ticketHash, + receipt, + intent: quote.intent, + }, + { session, maxTimeMS: 3000 }, + ); + return receipt; + }, + ); + }, + }; + return Object.freeze(api); +} diff --git a/src/server/service/review-service-auth.ts b/src/server/service/review-service-auth.ts new file mode 100644 index 0000000..d6bba33 --- /dev/null +++ b/src/server/service/review-service-auth.ts @@ -0,0 +1,160 @@ +import { createHash, timingSafeEqual } from "crypto"; +import { Request } from "express"; +import { decodeReviewJSON } from "./review-json"; + +type Scope = "completion.exchange" | "receipt.read"; +type Key = { + clientId: string; + digest: Buffer; + from: number; + until: number; + scopes: Scope[]; +}; +const opaque = /^[a-f0-9]{32}$/; +const secret = /^[a-f0-9]{64}$/; +const invalid = (): never => { + throw new Error("Invalid review exchange credentials"); +}; +function fields(value: unknown, names: string[]): Record { + if ( + !value || + typeof value !== "object" || + Array.isArray(value) || + Object.keys(value).sort().join(",") !== names.sort().join(",") + ) + return invalid(); + return value as Record; +} +function date(value: unknown): number { + if ( + typeof value !== "string" || + !/^20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(value) + ) + return invalid(); + const number = Date.parse(value); + if ( + !Number.isFinite(number) || + new Date(number).toISOString() !== value.replace("Z", ".000Z") + ) + return invalid(); + return number; +} +export function singleReviewHeader( + req: Request, + name: string, +): string | undefined { + let count = 0; + for (let i = 0; i < req.rawHeaders.length; i += 2) + if (req.rawHeaders[i].toLowerCase() === name) count++; + const value = req.headers[name]; + return count === 1 && typeof value === "string" ? value : undefined; +} + +/** Separate scoped registry: the capabilities-only registry is not accepted. */ +export function createReviewServiceAuth( + raw: string, + now: () => number = Date.now, +) { + let parsed: unknown; + try { + parsed = decodeReviewJSON(Buffer.from(raw, "utf8")); + } catch { + return invalid(); + } + const config = fields(parsed, ["version", "keys"]); + if ( + config.version !== 1 || + !Array.isArray(config.keys) || + config.keys.length < 1 || + config.keys.length > 64 + ) + return invalid(); + const keys = new Map(), + digests = new Set(), + clients = new Map(); + for (const entry of config.keys) { + const k = fields(entry, [ + "clientId", + "keyId", + "tokenSHA256", + "notBefore", + "notAfter", + "scopes", + ]); + if ( + typeof k.clientId !== "string" || + !opaque.test(k.clientId) || + typeof k.keyId !== "string" || + !/^[A-Za-z0-9_-]{1,64}$/.test(k.keyId) || + typeof k.tokenSHA256 !== "string" || + !secret.test(k.tokenSHA256) || + !Array.isArray(k.scopes) || + k.scopes.length < 1 || + k.scopes.length > 2 || + k.scopes.some( + (scope) => scope !== "completion.exchange" && scope !== "receipt.read", + ) || + new Set(k.scopes).size !== k.scopes.length + ) + return invalid(); + const from = date(k.notBefore), + until = date(k.notAfter), + id = k.clientId + ":" + k.keyId; + const count = (clients.get(k.clientId) || 0) + 1; + if ( + until <= from || + keys.has(id) || + digests.has(k.tokenSHA256) || + count > 16 + ) + return invalid(); + clients.set(k.clientId, count); + if (clients.size > 32) return invalid(); + digests.add(k.tokenSHA256); + keys.set(id, { + clientId: k.clientId, + digest: Buffer.from(k.tokenSHA256, "hex"), + from, + until, + scopes: [...k.scopes] as Scope[], + }); + } + const dummy = Buffer.alloc(32); + return Object.freeze({ + authenticate( + req: Request, + scope: Scope, + ): { clientId: string; valid: () => boolean } | undefined { + const client = singleReviewHeader(req, "x-4open-artifact-client-id"), + keyId = singleReviewHeader(req, "x-4open-artifact-service-key-id"), + authorization = singleReviewHeader(req, "authorization"); + if ( + !client || + !opaque.test(client) || + !keyId || + !/^[A-Za-z0-9_-]{1,64}$/.test(keyId) || + !authorization || + !/^Bearer [a-f0-9]{64}$/i.test(authorization) || + !secret.test(authorization.slice(7)) + ) + return undefined; + const key = keys.get(client + ":" + keyId); + const matches = timingSafeEqual( + createHash("sha256").update(authorization.slice(7), "ascii").digest(), + key?.digest || dummy, + ); + const valid = () => { + const time = now(); + return ( + !!key && + matches && + key.scopes.includes(scope) && + Number.isFinite(time) && + time >= key.from && + time < key.until + ); + }; + return valid() ? Object.freeze({ clientId: client, valid }) : undefined; + }, + }); +} diff --git a/src/streamer/index.ts b/src/streamer/index.ts index 28106b0..8e80c7f 100644 --- a/src/streamer/index.ts +++ b/src/streamer/index.ts @@ -1,3 +1,4 @@ +import { installFatalErrorLogging } from "../core/process-monitoring"; import { config as dotenv } from "dotenv"; dotenv(); @@ -16,6 +17,7 @@ import { startPerformanceMonitoring } from "../core/performance-monitoring"; import { getAnonymizationPoolStats } from "../core/anonymization-pool"; const logger = createLogger("streamer"); +installFatalErrorLogging("streamer"); const app = express(); app.use(monitorRequests("streamer")); diff --git a/src/streamer/route.ts b/src/streamer/route.ts index e96abf3..447c759 100644 --- a/src/streamer/route.ts +++ b/src/streamer/route.ts @@ -34,6 +34,7 @@ router.post( getToken: () => token, anonymizerOptions: { ...anonymizerOptions, repoId }, contentOptions, + submodules: req.body.submodules, }, res ); @@ -89,7 +90,7 @@ router.post("/", async (req: express.Request, res: express.Response) => { const content = await source.getFileContentCache( filePath, repoId, - () => ({ sha: fileSha, size: fileSize }) + () => ({ sha: fileSha, size: fileSize, upstream: req.body.upstream }) ); if (res.destroyed || res.writableEnded) { content.destroy(); anonymizer.destroy(); return; } const mime = lookup(filePath); diff --git a/test/admin-queue-jobs.test.js b/test/admin-queue-jobs.test.js new file mode 100644 index 0000000..6df891e --- /dev/null +++ b/test/admin-queue-jobs.test.js @@ -0,0 +1,32 @@ +const { expect } = require("chai"); + +describe("admin queue jobs", function () { + it("returns job payloads, failure reasons and stacks as values, not JSON strings", async function () { + const queues = require("../src/queue"); + const metrics = require("../src/queue/queueMetrics"); + const router = require("../src/server/routes/admin").default; + const handler = router.stack.find(layer => layer.route?.path === "/queues").route.stack.at(-1).handle; + const originals = [queues.downloadQueue, queues.removeQueue, queues.cacheQueue, metrics.queryMetrics]; + const failed = { + data: { repoId: "paper-7F2A" }, failedReason: "branch_not_found", stacktrace: ["Error: branch_not_found\n at fetch"], returnvalue: null, + // BullMQ's asJSON() stringifies these four fields. + asJSON() { return { id: "paper-7F2A", name: "paper-7F2A", data: JSON.stringify(this.data), failedReason: JSON.stringify(this.failedReason), stacktrace: JSON.stringify(this.stacktrace), returnvalue: JSON.stringify(this.returnvalue) }; }, + }; + const queue = { getJobCounts: async () => ({}), getWorkers: async () => [], isPaused: async () => false, + getJobs: async ([state]) => state === "failed" ? [failed] : [] }; + queues.downloadQueue = queue; queues.removeQueue = queue; queues.cacheQueue = queue; + metrics.queryMetrics = async () => []; + try { + let body; + // The remove queue keeps this snapshot apart from other tests' cache entries. + await handler({ query: { queue: "remove" } }, { json: data => { body = data; } }); + const job = body.jobs.find(j => j.id === "paper-7F2A"); + expect(job.data).to.deep.equal({ repoId: "paper-7F2A" }); + expect(job.failedReason).to.equal("branch_not_found"); + expect(job.stacktrace).to.deep.equal(["Error: branch_not_found\n at fetch"]); + expect(job._state).to.equal("failed"); + } finally { + [queues.downloadQueue, queues.removeQueue, queues.cacheQueue, metrics.queryMetrics] = originals; + } + }); +}); diff --git a/test/app-reinstall-recovery.test.js b/test/app-reinstall-recovery.test.js index 55392a9..77f99e4 100644 --- a/test/app-reinstall-recovery.test.js +++ b/test/app-reinstall-recovery.test.js @@ -50,7 +50,7 @@ for (const type of ["repository", "pull-request"]) { expect(writes).to.have.length(0); expect(model.githubAccess.installationId).to.equal(100); }); it("replaces only the binding while preserving the URL, source and snapshot", async function () { - expect(await run()).to.deep.equal({ connection: "github-app" }); + expect(await run()).to.deep.equal({ connection: "github-app", fullName: "owner/private" }); expect(writes).to.have.length(1); expect(writes[0].update).to.deep.equal({ $set: { githubAccess: selected.binding } }); expect(writes[0].filter.githubAccess).to.deep.equal(model.githubAccess); diff --git a/test/asset-build.test.js b/test/asset-build.test.js index 8b30d77..3fe4cdf 100644 --- a/test/asset-build.test.js +++ b/test/asset-build.test.js @@ -95,7 +95,7 @@ describe("asset build", function () { fs.writeFileSync(path.join(directory, groups.markdownFiles[0]), "function {"); const result = build(); expect(result.status).not.to.equal(0); - expect(result.stderr).to.include("uglify"); + expect(result.stderr).to.match(/uglify|ERROR/); expect(fs.existsSync(path.join(directory, "public/asset-manifest.json"))).to.equal(false); }); }); diff --git a/test/credential-migration.test.js b/test/credential-migration.test.js index 18afd77..9947af6 100644 --- a/test/credential-migration.test.js +++ b/test/credential-migration.test.js @@ -179,26 +179,34 @@ describeMongo("credential access (MongoDB)", function () { await setCredential(owner.id, "old-token"); await Credential.updateOne({ ownerId: owner._id }, { $set: { updatedAt: new Date(0) } }); const originalFetch = global.fetch; + const oauthConfig = [config.GITHUB_OAUTH_ENABLED, config.CLIENT_ID, config.CLIENT_SECRET]; + config.GITHUB_OAUTH_ENABLED = true; config.CLIENT_ID = "fixture-client"; config.CLIENT_SECRET = "fixture-secret"; global.fetch = async (url, options) => { expect(url).to.include("api.github.com/applications/"); expect(JSON.parse(options.body).access_token).to.equal("old-token"); return { ok: true, json: async () => ({ token: "refreshed-token" }) }; }; try { expect(await repo.getToken()).to.equal("refreshed-token"); } - finally { global.fetch = originalFetch; } + finally { global.fetch = originalFetch; [config.GITHUB_OAUTH_ENABLED, config.CLIENT_ID, config.CLIENT_SECRET] = oauthConfig; } expect(await getCredentialToken(owner.id)).to.equal("refreshed-token"); expect((await RepoModel.collection.findOne({ _id: repo.model._id })).source.accessToken).to.equal(undefined); }); it("OAuth login writes a credential and returns a token-free session user", async () => { const passport = require("passport"); require("../src/server/routes/connection"); - const result = await new Promise((resolve, reject) => passport._strategy("github")._verify({ githubOAuthContext: { expires: Date.now() + 60000 } }, "oauth-secret", "refresh-secret", { - id: "external-test", username: owner.username, emails: [], photos: [], - }, (error, user) => error ? reject(error) : resolve(user))); + const originalFetch = global.fetch; + global.fetch = async () => ({ ok: true, json: async () => [{ email: "private@example.com", primary: true, verified: true }] }); + let result; + try { + result = await new Promise((resolve, reject) => passport._strategy("github")._verify({ githubOAuthContext: { expires: Date.now() + 60000 } }, "oauth-secret", "refresh-secret", { + id: "external-test", username: owner.username, emails: [], photos: [], + }, (error, user) => error ? reject(error) : resolve(user))); + } finally { global.fetch = originalFetch; } expect(JSON.stringify(result)).not.to.include("oauth-secret"); expect(JSON.stringify(result)).not.to.include("refresh-secret"); expect(await getCredentialToken(owner.id)).to.equal("oauth-secret"); expect((await UserModel.collection.findOne({ _id: owner._id })).accessTokens).to.equal(undefined); + expect((await UserModel.findById(owner.id)).emails[0].email).to.equal("private@example.com"); }); it("reads hidden legacy resource tokens only during compatibility mode", async () => { const resource = await GistModel.collection.insertOne({ gistId: "legacy-gist", owner: owner._id, source: { accessToken: "legacy-resource" } }); diff --git a/test/dependency-archive.test.js b/test/dependency-archive.test.js new file mode 100644 index 0000000..9d3f307 --- /dev/null +++ b/test/dependency-archive.test.js @@ -0,0 +1,43 @@ +const { expect } = require("chai"); +const fs = require("node:fs/promises"); +const os = require("node:os"); +const path = require("node:path"); +const { once } = require("node:events"); +const archiver = require("archiver"); +const { Parse } = require("unzip-stream"); + +describe("archive glob dependency compatibility", function () { + it("archives nested matches and excludes ignored files with the patched matcher", async function () { + const directory = await fs.mkdtemp(path.join(os.tmpdir(), "archive-dependencies-")); + const archive = archiver("zip"); + const parser = Parse(); + try { + await fs.mkdir(path.join(directory, "nested")); + await Promise.all(["README.md", "nested/notes.md", "nested/private.md", "nested/code.js"] + .map(name => fs.writeFile(path.join(directory, name), name))); + // archiver-utils uses glob.sync, while archiver.glob uses readdir-glob + // and Minimatch. Exercise both callers of the major-version overrides. + expect(require("archiver-utils").file.expand({ cwd: directory }, ["**/*.md", "!**/private.md"]).sort()) + .to.deep.equal(["README.md", "nested/notes.md"]); + const entries = []; + const reads = []; + parser.on("entry", entry => { + const chunks = []; + entry.on("data", chunk => chunks.push(chunk)); + reads.push(once(entry, "end").then(() => entries.push({ name: entry.path, body: Buffer.concat(chunks).toString() }))); + }); + const finished = once(parser, "finish"); + archive.pipe(parser); + archive.glob("**/*.md", { cwd: directory, ignore: ["**/private.md"] }); + await archive.finalize(); + await finished; + await Promise.all(reads); + expect(entries.sort((a, b) => a.name.localeCompare(b.name))) + .to.deep.equal(["nested/notes.md", "README.md"].map(name => ({ name, body: name }))); + } finally { + archive.destroy(); + parser.destroy(); + await fs.rm(directory, { recursive: true, force: true }); + } + }); +}); diff --git a/test/deploy-owner-alert-migration.test.js b/test/deploy-owner-alert-migration.test.js new file mode 100644 index 0000000..9274526 --- /dev/null +++ b/test/deploy-owner-alert-migration.test.js @@ -0,0 +1,109 @@ +const { expect } = require("chai"); +const { spawnSync } = require("child_process"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); + +describe("owner alert migration deployment", function () { + this.timeout(10000); + function deploy(options = {}) { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "owner-alert-deploy-")); + const statePath = path.join(directory, "state.json"), logPath = path.join(directory, "calls.jsonl"); + fs.mkdirSync(path.join(directory, "scripts")); + fs.copyFileSync(path.join(__dirname, "../scripts/deploy.sh"), path.join(directory, "scripts/deploy.sh")); + fs.writeFileSync(statePath, JSON.stringify({ ...options })); + fs.writeFileSync(path.join(directory, "docker"), `#!/usr/bin/env node +const fs = require("fs"); +const args = process.argv.slice(2), state = JSON.parse(fs.readFileSync(process.env.TASK_DOCKER_STATE)); +fs.appendFileSync(process.env.TASK_DOCKER_LOG, JSON.stringify(args) + "\\n"); +const finish = (output = "", code = 0) => { fs.writeFileSync(process.env.TASK_DOCKER_STATE, JSON.stringify(state)); process.stdout.write(output); process.exit(code); }; +if (args[0] === "image" && args[1] === "inspect") { + if (args.some(a => a.includes("Labels"))) finish(state.badProtocol ? "" : "recipient-v2"); + finish(state.built ? "sha:new" : "sha:old"); +} +if (args[0] === "inspect") { + if (args.some(a => a.includes("Labels"))) finish(state.oldProtocol || ""); + if (args.includes("{{.State.Running}}")) finish(state.stopped && !state.refuseStop ? "false" : "true"); + finish("healthy"); +} +if (args[0] === "compose") { + if (args[1] === "build") { if (state.buildFails) finish("", 1); state.built = true; finish(); } + if (args[1] === "stop") { state.stopped = true; finish(); } + if (args[1] === "run") { if (state.reconcileFails) finish("", 1); state.reconciled = true; finish(); } + if (args[1] === "up") { if (args.includes("streamer")) state.streamers = true; else state.api = true; finish(); } + if (args[1] === "ps") { + if (args.includes("-q")) finish(args.includes("streamer") ? "old-api\\nold-1\\nold-2\\nold-3\\nold-4\\n" : "new-api"); + if (args.includes("streamer")) finish(state.streamers ? "new-1\\nnew-2\\nnew-3\\nnew-4\\n" : "old-1\\nold-2\\nold-3\\nold-4\\n"); + finish(); + } +} +finish("unexpected docker call", 2); +`, { mode: 0o755 }); + try { + const result = spawnSync("bash", [path.join(directory, "scripts/deploy.sh")], { + encoding: "utf8", timeout: 8000, + env: { ...process.env, PATH: directory + path.delimiter + process.env.PATH, + FORCE: String(options.force ?? 1), STREAMER_REPLICAS: "4", TASK_DOCKER_STATE: statePath, TASK_DOCKER_LOG: logPath }, + }); + return { ...result, calls: fs.readFileSync(logPath, "utf8").trim().split("\n").map(line => JSON.parse(line)) }; + } finally { fs.rmSync(directory, { recursive: true, force: true }); } + } + + it("stops and verifies every legacy writer before starting any migration-capable instance", () => { + const result = deploy(); + expect(result.status, result.stderr).to.equal(0); + const stop = result.calls.findIndex(args => args[0] === "compose" && args[1] === "stop"); + const up = result.calls.findIndex(args => args[0] === "compose" && args[1] === "up"); + expect(stop).to.be.greaterThan(-1); expect(up).to.be.greaterThan(stop); + const reconcile = result.calls.findIndex(args => args[0] === "compose" && args[1] === "run"); + expect(reconcile).to.be.greaterThan(stop); expect(reconcile).to.be.lessThan(up); + expect(result.calls[reconcile]).to.include("./build/scripts/reconcile-legacy-owner-alerts.js"); + const checked = result.calls.slice(stop + 1, up).filter(args => args[0] === "inspect").map(args => args.at(-1)); + expect(checked).to.deep.equal(["old-api", "old-1", "old-2", "old-3", "old-4"]); + expect(result.calls[stop]).to.include.members(["anonymous_github", "streamer"]); + expect(result.calls[up]).to.include.members(["--force-recreate", "streamer=4"]); + const api = result.calls.findIndex(args => args[0] === "compose" && args[1] === "up" && args.at(-1) === "anonymous_github"); + expect(api).to.be.greaterThan(up); + for (const name of ["new-1", "new-2", "new-3", "new-4"]) { + expect(result.calls.slice(up + 1, api).some(args => args[0] === "inspect" && args.at(-1) === name)).to.equal(true); + } + }); + it("stops recipient-v1 writers before restoring permanent recipient suppression", () => { + const result = deploy({ oldProtocol: "recipient-v1" }); + expect(result.status, result.stderr).to.equal(0); + const stop = result.calls.findIndex(args => args[0] === "compose" && args[1] === "stop"); + const reconcile = result.calls.findIndex(args => args[0] === "compose" && args[1] === "run"); + const up = result.calls.findIndex(args => args[0] === "compose" && args[1] === "up"); + expect(stop).to.be.greaterThan(-1); expect(reconcile).to.be.greaterThan(stop); expect(up).to.be.greaterThan(reconcile); + }); + it("aborts before migration starts if a legacy writer remains running", () => { + const result = deploy({ refuseStop: true }); + expect(result.status).not.to.equal(0); + expect(result.stderr).to.include("still running"); + expect(result.calls.some(args => args[0] === "compose" && args[1] === "up")).to.equal(false); + }); + it("keeps old containers running if the image build fails", () => { + const result = deploy({ buildFails: true }); + expect(result.status).not.to.equal(0); + expect(result.calls.some(args => args[0] === "compose" && ["stop", "up"].includes(args[1]))).to.equal(false); + }); + it("keeps old containers running if the replacement lacks migration support", () => { + const result = deploy({ badProtocol: true }); + expect(result.status).not.to.equal(0); + expect(result.stderr).to.include("lacks recipient alert migration support"); + expect(result.calls.some(args => args[0] === "compose" && ["stop", "up"].includes(args[1]))).to.equal(false); + }); + it("starts no application instance if rollback reconciliation cannot be prepared", () => { + const result = deploy({ reconcileFails: true }); + expect(result.status).not.to.equal(0); + expect(result.calls.some(args => args[0] === "compose" && args[1] === "up")).to.equal(false); + }); + for (const stopped of [true, false]) it(`reconciles legacy ${stopped ? "stopped" : "running"} containers when the replacement was already built`, () => { + const result = deploy({ built: true, stopped, force: 0 }); + expect(result.status, result.stderr).to.equal(0); + const reconcile = result.calls.findIndex(args => args[0] === "compose" && args[1] === "run"); + const up = result.calls.findIndex(args => args[0] === "compose" && args[1] === "up"); + expect(reconcile).to.be.greaterThan(-1); expect(up).to.be.greaterThan(reconcile); + expect(result.stdout).not.to.include("nothing to deploy"); + }); +}); diff --git a/test/download-header-performance.test.js b/test/download-header-performance.test.js index 3f87621..2ccbf39 100644 --- a/test/download-header-performance.test.js +++ b/test/download-header-performance.test.js @@ -43,6 +43,35 @@ describe("download headers and website revalidation", function () { expect(response.headers["etag"]).to.match(/^"f-/); expect(response.headers["content-type"]).to.include("text/plain"); expect(transfers).to.equal(0); }); + for (const name of ["page.shtml", "drawing.svgz", "schema.xsd"]) { + it(`sandboxes ${name} on GET, HEAD, and revalidation`, async function () { + const path = `/api/repo/fixture/file/${name}`; + const first = await fetch(path); + const head = await fetch(path, "HEAD"); + const cached = await fetch(path, "GET", { "If-None-Match": first.headers.etag }); + expect(first.status).to.equal(200); + expect(head.status).to.equal(200); + expect(cached.status).to.equal(304); + for (const response of [first, head, cached]) { + expect(response.headers["content-security-policy"]).to.include("sandbox"); + expect(response.headers["content-security-policy"]).not.to.include("allow-scripts"); + expect(response.headers["content-security-policy"]).not.to.include("allow-same-origin"); + expect(response.headers["x-content-type-options"]).to.equal("nosniff"); + } + }); + } + it("sandboxes MIME aliases even when anonymization changes their extension", async function () { + AnonymizedFile.prototype.originalPath = async () => "original.shtml"; + const response = await fetch("/api/repo/fixture/file/page.txt"); + expect(response.status).to.equal(200); + expect(response.headers["content-security-policy"]).to.include("sandbox"); + }); + it("keeps explicit downloads as attachments", async function () { + const response = await fetch("/api/repo/fixture/file/page.shtml?download=1"); + expect(response.status).to.equal(200); + expect(response.headers["content-disposition"]).to.include("attachment"); + expect(response.headers["x-content-type-options"]).to.equal("nosniff"); + }); it("preserves content gates for HEAD", async function () { repo.options.pdf = false; expect((await fetch("/api/repo/fixture/file/private.pdf", "HEAD")).status).to.equal(403); @@ -65,13 +94,13 @@ describe("download headers and website revalidation", function () { expect(second.headers["content-security-policy"]).to.include("sandbox"); }); it("renders Markdown again after a transient failure served the raw fallback", async function () { - const marked = require("marked").marked, render = marked.parse; + const markdown = require("../src/markdown"), render = markdown.renderMarkdown; let first; try { - marked.parse = () => { throw Error("transient rendering failure"); }; + markdown.renderMarkdown = () => { throw Error("transient rendering failure"); }; first = await fetch("/w/fixture/page.md"); expect(first.status).to.equal(200); expect(first.body).to.equal("fixture content"); - } finally { marked.parse = render; } + } finally { markdown.renderMarkdown = render; } const recovered = await fetch("/w/fixture/page.md", "GET", { "If-None-Match": first.headers.etag }); expect(recovered.status).to.equal(200); expect(recovered.body).to.include("Anonymized page"); expect(recovered.headers.etag).not.to.equal(first.headers.etag); diff --git a/test/error-codes.test.js b/test/error-codes.test.js index ea1f52a..006617f 100644 --- a/test/error-codes.test.js +++ b/test/error-codes.test.js @@ -68,7 +68,9 @@ function extractBackendErrorCodes(files) { // Multi-line matching for AnonymousError calls that span lines // (e.g. ternary expressions where the string is on the next line) const multiLinePattern = - /AnonymousError\([\s\S]*?["']([a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+)+)["']/g; + // Stop at a closing parenthesis so a dynamic code cannot consume an + // unrelated string in a later statement, such as a monitoring label. + /AnonymousError\([^)]*?["']([a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+)+)["']/g; let m; while ((m = multiLinePattern.exec(content)) !== null) { const code = m[1]; diff --git a/test/file-route-sandbox.test.js b/test/file-route-sandbox.test.js index 0f4220c..99436e7 100644 --- a/test/file-route-sandbox.test.js +++ b/test/file-route-sandbox.test.js @@ -17,6 +17,12 @@ describe("file route scriptable-document detection", function () { "style.xsl", "transform.xslt", "archive.mhtml", + "archive.mht", + "page.shtml", + "figures/plot.svgz", + "schema.xsd", + "schema.rng", + "feed.atom", ]; scriptable.forEach((path) => { expect(isScriptableDocument(path), path).to.equal(true); @@ -26,6 +32,8 @@ describe("file route scriptable-document detection", function () { it("is case insensitive on the extension", function () { expect(isScriptableDocument("README.HTML")).to.equal(true); expect(isScriptableDocument("logo.SVG")).to.equal(true); + expect(isScriptableDocument("logo.SVGZ")).to.equal(true); + expect(isScriptableDocument("page.SHTML")).to.equal(true); }); it("leaves everything else alone", function () { diff --git a/test/fixtures/github-gate-cancellation.js b/test/fixtures/github-gate-cancellation.js new file mode 100644 index 0000000..2800643 --- /dev/null +++ b/test/fixtures/github-gate-cancellation.js @@ -0,0 +1,61 @@ +const assert = require("node:assert/strict"); +const { Readable } = require("node:stream"); +const { setImmediate: nextTurn, setTimeout: delay } = require("node:timers/promises"); +const { setTimeout, clearTimeout } = require("node:timers"); +const redis = require("redis"); +let reads = 0; +const resetAt = Date.now() + 10 * 60 * 1000; +let readGate = async () => String(resetAt); +const client = { isOpen: true, on() { return this; }, async connect() {}, + async get() { reads++; return readGate(); }, destroy() { this.isOpen = false; } }; +redis.createClient = () => client; +require("ts-node/register/transpile-only"); +const github = require("../../src/core/GitHubUtils"); +const GitHubStream = require("../../src/core/source/GitHubStream").default; +const got = require("got"); + +async function main() { + const preAborted = new AbortController(); preAborted.abort(); + await assert.rejects(github.waitForTokenGate("token", preAborted.signal), { name: "AbortError" }); + assert.equal(reads, 0); + + const firstController = new AbortController(), secondController = new AbortController(); + const first = github.waitForTokenGate("token", firstController.signal); + let secondSettled = false; + const second = github.waitForTokenGate("token", secondController.signal).finally(() => { secondSettled = true; }); + await nextTurn(); firstController.abort(); + await assert.rejects(first, { name: "AbortError" }); + await delay(20); assert.equal(secondSettled, false); + secondController.abort(); await assert.rejects(second, { name: "AbortError" }); + + // A shared Redis lookup may remain pending. Cancelling one caller must + // release that caller without closing the shared client. + readGate = () => new Promise(() => {}); + const lookupController = new AbortController(); + const lookup = github.waitForTokenGate("token", lookupController.signal); + await nextTurn(); lookupController.abort(); + await assert.rejects(lookup, { name: "AbortError" }); assert.equal(client.isOpen, true); + + let entered; + const gateEntered = new Promise(resolve => { entered = resolve; }); + readGate = async () => { entered(); return String(resetAt); }; + let metadataCalls = 0; + github.octokit = () => ({ repos: { getContent: async () => { metadataCalls++; throw Error("unexpected metadata request"); } } }); + got.stream = () => new Readable({ read() { + this.destroy(Object.assign(Error("private web 404"), { response: { statusCode: 404 } })); + } }); + const source = new GitHubStream({ repoId: "gate-cancel", organization: "owner", repoName: "repo", commit: "pinned", getToken: () => "token" }); + const input = source.deferredRawDownload("token", "file.bin"); + const collected = (async () => { for await (const chunk of input) void chunk; })().catch(() => {}); + await gateEntered; await nextTurn(); + const closed = new Promise(resolve => input.once("close", resolve)); input.destroy(); + await closed; await collected; + assert.equal(metadataCalls, 0); assert.equal(input.closed, true); +} + +const watchdog = setTimeout(() => { console.error("Cancellation did not release the gate wait"); process.exit(1); }, 5000); +watchdog.unref(); +main().then(() => { clearTimeout(watchdog); console.log("gate cancellations released"); }, + error => { clearTimeout(watchdog); console.error(error); process.exit(1); }); +// Do not force a successful exit: any retained ten-minute timer must fail +// the parent test's process timeout. diff --git a/test/fixtures/github-stream-failure.js b/test/fixtures/github-stream-failure.js new file mode 100644 index 0000000..faa4b28 --- /dev/null +++ b/test/fixtures/github-stream-failure.js @@ -0,0 +1,44 @@ +const assert = require("node:assert/strict"); +const { Readable } = require("node:stream"); +const { setImmediate } = require("node:timers"); +require("ts-node/register/transpile-only"); +const GitHubStream = require("../../src/core/source/GitHubStream").default; +const failure = Object.assign(new Error("upstream failed"), process.argv[2] === "reset" ? { code: "ECONNRESET" } : { response: { statusCode: 401 } }); +const source = new GitHubStream({ repoId: "fixture", organization: "owner", repoName: "repo", commit: "abc", getToken: () => "token" }); +async function main() { + if (process.argv[2] === "fatal") { + require("../../src/core/process-monitoring").installFatalErrorLogging("streamer"); + await new Promise(() => setImmediate(() => { throw new Error("fixture fatal"); })); + } + if (process.argv[2] === "fallback" || process.argv[2] === "public-raw") { + source.downloadFile = () => { + const input = new Readable({ read() {} }); + process.nextTick(() => input.destroy(Object.assign(new Error("blob unavailable"), { response: { statusCode: 404 } }))); + return input; + }; + source.downloadFileViaRaw = () => { + const input = new Readable({ read() {} }); + process.nextTick(() => input.destroy(failure)); + return input; + }; + const content = await source.downloadWithFallback(process.argv[2] === "public-raw" ? "" : "token", "blob", "file"); + await new Promise(resolve => setImmediate(resolve)); + await assert.rejects(async () => { for await (const chunk of content) void chunk; }, error => error === failure); + } else if (process.argv[2] === "lfs") { + const pointer = "version https://git-lfs.github.com/spec/v1\noid sha256:" + "a".repeat(64) + "\nsize 10\n"; + source.downloadFileViaRaw = () => new Readable({ read() { this.destroy(failure); } }); + const content = source.resolveLfsPointer(Readable.from([pointer]), "token", "file"); + // A producer must not start emitting errors while its consumer is still awaiting. + await new Promise(resolve => setImmediate(resolve)); + await assert.rejects(async () => { for await (const chunk of content) void chunk; }, error => error === failure); + } else { + source.downloadFile = () => { + const content = new Readable({ read() {} }); + process.nextTick(() => content.destroy(failure)); + return content; + }; + await assert.rejects(source.downloadWithFallback("token", "blob", "file"), error => error === failure); + await new Promise(resolve => setImmediate(resolve)); + } +} +main().then(() => { process.stdout.write("failure contained\n"); process.exit(0); }, error => { console.error(error); process.exit(1); }); diff --git a/test/fixtures/markdown/README.md b/test/fixtures/markdown/README.md new file mode 100644 index 0000000..e404f07 --- /dev/null +++ b/test/fixtures/markdown/README.md @@ -0,0 +1,5 @@ +# GFM extension fixtures + +`gfm-extensions.json` contains the tables, strikethrough, autolink and task-list examples from GitHub's [cmark-gfm extension tests](https://github.com/github/cmark-gfm/blob/27d942c8b0a62d192f616e5bf3578f4b6a89e180/test/extensions.txt), retrieved November 2, 2026. The examples are by Yuki Izumi and contributors and are licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). + +The Markdown and expected HTML are unchanged. They have been extracted into JSON with their section names. `` is upstream's crash-only test, not expected rendered HTML. The test normalizes HTML serialization and insignificant block whitespace, and sanitizes expected HTML with the production policy. Footnote links, alerts, math, media and diagrams have separate behavioral assertions in `markdown-compatibility.test.js`. diff --git a/test/fixtures/markdown/gfm-extensions.json b/test/fixtures/markdown/gfm-extensions.json new file mode 100644 index 0000000..f0461e4 --- /dev/null +++ b/test/fixtures/markdown/gfm-extensions.json @@ -0,0 +1,122 @@ +[ + { + "section": "Tables", + "markdown": "| abc | def |\n| --- | --- |\n| ghi | jkl |\n| mno | pqr |\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abcdef
ghijkl
mnopqr
\n" + }, + { + "section": "Tables", + "markdown": "Hello!\n\n| _abc_ | セン |\n| ----- | ---- |\n| 1. Block elements inside cells don't work. | |\n| But _**inline elements do**_. | x |\n\nHi!\n", + "html": "

Hello!

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abcセン
1. Block elements inside cells don't work.
But inline elements do.x
\n

Hi!

\n" + }, + { + "section": "Tables", + "markdown": "| Not enough table | to be considered table |\n\n| Not enough table | to be considered table |\n| Not enough table | to be considered table |\n\n| Just enough table | to be considered table |\n| ----------------- | ---------------------- |\n\n| ---- | --- |\n\n|x|\n|-|\n\n| xyz |\n| --- |\n", + "html": "

| Not enough table | to be considered table |

\n

| Not enough table | to be considered table |\n| Not enough table | to be considered table |

\n\n\n\n\n\n\n\n
Just enough tableto be considered table
\n

| ---- | --- |

\n\n\n\n\n\n\n
x
\n\n\n\n\n\n\n
xyz
\n" + }, + { + "section": "Tables", + "markdown": "abc | def\n--- | ---\nxyz | ghi\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n
abcdef
xyzghi
\n" + }, + { + "section": "Tables", + "markdown": "Hello!\n\n| _abc_ | セン |\n| ----- | ---- |\n| this row has a space at the end | | \n| But _**inline elements do**_. | x |\n\nHi!\n", + "html": "

Hello!

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abcセン
this row has a space at the end
But inline elements do.x
\n

Hi!

\n" + }, + { + "section": "Tables", + "markdown": "aaa | bbb | ccc | ddd | eee\n:-- | --- | :-: | --- | --:\nfff | ggg | hhh | iii | jjj\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
aaabbbcccdddeee
fffggghhhiiijjj
\n" + }, + { + "section": "Tables", + "markdown": "| a | b | c |\n| --- | --- |\n| this | isn't | okay |\n", + "html": "

| a | b | c |\n| --- | --- |\n| this | isn't | okay |

\n" + }, + { + "section": "Tables", + "markdown": "| a | b | c |\n| --- | --- | ---\n| x\n| a | b\n| 1 | 2 | 3 | 4 | 5 |\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abc
x
ab
123
\n" + }, + { + "section": "Tables", + "markdown": "| a | b |\n| --- | --- |\n| Escaped pipes are \\|okay\\|. | Like \\| this. |\n| Within `\\|code\\| is okay` too. |\n| _**`c\\|`**_ \\| complex\n| don't **\\_reparse\\_**\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ab
Escaped pipes are |okay|.Like | this.
Within |code| is okay too.
c| | complex
don't _reparse_
\n" + }, + { + "section": "Tables", + "markdown": "| a |\n--- |\n", + "html": "\n\n\n\n\n\n
a
\n" + }, + { + "section": "Tables", + "markdown": "| a | b |\n| --- | --- |\n| \\\\ | `\\\\` |\n| \\\\\\\\ | `\\\\\\\\` |\n| \\_ | `\\_` |\n| \\| | `\\|` |\n| \\a | `\\a` |\n\n\\\\ `\\\\`\n\n\\\\\\\\ `\\\\\\\\`\n\n\\_ `\\_`\n\n\\| `\\|`\n\n\\a `\\a`\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ab
\\\\\\
\\\\\\\\\\\\
_\\_
||
\\a\\a
\n

\\ \\\\

\n

\\\\ \\\\\\\\

\n

_ \\_

\n

| \\|

\n

\\a \\a

\n" + }, + { + "section": "Tables", + "markdown": "| a |\n| --- |\n| hello |\n| ok
sure |\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n
a
hello
ok
sure
\n" + }, + { + "section": "Tables", + "markdown": "Here's a link to [Freedom Planet 2][].\n\n| Here's a link to [Freedom Planet 2][] in a table header. |\n| --- |\n| Here's a link to [Freedom Planet 2][] in a table row. |\n\n[Freedom Planet 2]: http://www.freedomplanet2.com/\n", + "html": "

Here's a link to Freedom Planet 2.

\n\n\n\n\n\n\n\n\n\n\n\n
Here's a link to Freedom Planet 2 in a table header.
Here's a link to Freedom Planet 2 in a table row.
\n" + }, + { + "section": "Tables", + "markdown": "| a | b | c |\n| --- | --- | --- |\n| d || e |\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abc
de
\n" + }, + { + "section": "Tables", + "markdown": "| a | b |\n| --- | --- |\n|***(a)***|\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n\n
ab
(a)
\n" + }, + { + "section": "Tables", + "markdown": "123\n456\n| a | b |\n| ---| --- |\nd | e\n", + "html": "

123\n456

\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ab
de
\n" + }, + { + "section": "Strikethroughs", + "markdown": "A proper ~strikethrough~.\n", + "html": "

A proper strikethrough.

\n" + }, + { + "section": "Strikethroughs", + "markdown": "These are ~not strikethroughs.\n\nNo, they are not~\n\nThis ~is ~ legit~ isn't ~ legit.\n\nThis is not ~~~~~one~~~~~ huge strikethrough.\n\n~one~ ~~two~~ ~~~three~~~\n\nNo ~mismatch~~\n", + "html": "

These are ~not strikethroughs.

\n

No, they are not~

\n

This is ~ legit isn't ~ legit.

\n

This is not ~~~~~one~~~~~ huge strikethrough.

\n

one two ~~~three~~~

\n

No ~mismatch~~

\n" + }, + { + "section": "Autolinks", + "markdown": ": http://google.com https://google.com\n\n http://google.com/å\n\nscyther@pokemon.com\n\nscy.the_rbe-edr+ill@pokemon.com\n\nscyther@pokemon.com.\n\nscyther@pokemon.com/\n\nscyther@pokemon.com/beedrill@pokemon.com\n\nmailto:scyther@pokemon.com\n\nThis is a mailto:scyther@pokemon.com\n\nmailto:scyther@pokemon.com.\n\nmmmmailto:scyther@pokemon.com\n\nmailto:scyther@pokemon.com/\n\nmailto:scyther@pokemon.com/message\n\nmailto:scyther@pokemon.com/mailto:beedrill@pokemon.com\n\nxmpp:scyther@pokemon.com\n\nxmpp:scyther@pokemon.com.\n\nxmpp:scyther@pokemon.com/message\n\nxmpp:scyther@pokemon.com/message.\n\nEmail me at:scyther@pokemon.com\n\nwww.github.com www.github.com/á\n\nwww.google.com/a_b\n\nUnderscores not allowed in host name www.xxx.yyy._zzz\n\nUnderscores not allowed in host name www.xxx._yyy.zzz\n\nUnderscores allowed in domain name www._xxx.yyy.zzz\n\n**Autolink and http://inlines**\n\n![http://inline.com/image](http://inline.com/image)\n\na.w@b.c\n\nFull stop outside parens shouldn't be included http://google.com/ok.\n\n(Full stop inside parens shouldn't be included http://google.com/ok.)\n\n\"http://google.com\"\n\n'http://google.com'\n\nhttp://🍄.ga/ http://x🍄.ga/\n", + "html": "

: http://google.com https://google.com

\n

http://google.com/å http://google.com/å

\n

scyther@pokemon.com

\n

scy.the_rbe-edr+ill@pokemon.com

\n

scyther@pokemon.com.

\n

scyther@pokemon.com/

\n

scyther@pokemon.com/beedrill@pokemon.com

\n

mailto:scyther@pokemon.com

\n

This is a mailto:scyther@pokemon.com

\n

mailto:scyther@pokemon.com.

\n

mmmmailto:scyther@pokemon.com

\n

mailto:scyther@pokemon.com/

\n

mailto:scyther@pokemon.com/message

\n

mailto:scyther@pokemon.com/mailto:beedrill@pokemon.com

\n

xmpp:scyther@pokemon.com

\n

xmpp:scyther@pokemon.com.

\n

xmpp:scyther@pokemon.com/message

\n

xmpp:scyther@pokemon.com/message.

\n

Email me at:scyther@pokemon.com

\n

www.github.com www.github.com/á

\n

www.google.com/a_b

\n

Underscores not allowed in host name www.xxx.yyy._zzz

\n

Underscores not allowed in host name www.xxx._yyy.zzz

\n

Underscores allowed in domain name www._xxx.yyy.zzz

\n

Autolink and http://inlines

\n

\"http://inline.com/image\"

\n

a.w@b.c

\n

Full stop outside parens shouldn't be included http://google.com/ok.

\n

(Full stop inside parens shouldn't be included http://google.com/ok.)

\n

"http://google.com"

\n

'http://google.com'

\n

http://🍄.ga/ http://x🍄.ga/

\n" + }, + { + "section": "Autolinks", + "markdown": "This shouldn't crash everything: (_A_@_.A\n", + "html": "\n" + }, + { + "section": "Autolinks", + "markdown": "These should not link:\n\n* @a.b.c@. x\n* n@. b\n", + "html": "

These should not link:

\n
    \n
  • @a.b.c@. x
  • \n
  • n@. b
  • \n
\n" + }, + { + "section": "Task lists", + "markdown": "- [ ] foo\n- [x] bar\n", + "html": "
    \n
  • foo
  • \n
  • bar
  • \n
\n" + }, + { + "section": "Task lists", + "markdown": "- [x] foo\n - [ ] bar\n - [x] baz\n- [ ] bim\n\nShow a regular (non task) list to show that it has the same structure\n- [@] foo\n - [@] bar\n - [@] baz\n- [@] bim\n", + "html": "
    \n
  • foo\n
      \n
    • bar
    • \n
    • baz
    • \n
    \n
  • \n
  • bim
  • \n
\n

Show a regular (non task) list to show that it has the same structure

\n
    \n
  • [@] foo\n
      \n
    • [@] bar
    • \n
    • [@] baz
    • \n
    \n
  • \n
  • [@] bim
  • \n
\n" + }, + { + "section": "Task lists", + "markdown": "- [x] foo\n - [ ] bar\n - [x] baz\n- [ ] bim\n\nShow a regular (non task) list to show that it has the same structure\n- [@] foo\n - [@] bar\n - [@] baz\n- [@] bim\n", + "html": "
    \n
  • foo\n
      \n
    • bar
    • \n
    • baz
    • \n
    \n
  • \n
  • bim
  • \n
\n

Show a regular (non task) list to show that it has the same structure

\n
    \n
  • [@] foo\n
      \n
    • [@] bar
    • \n
    • [@] baz
    • \n
    \n
  • \n
  • [@] bim
  • \n
\n" + } +] diff --git a/test/fixtures/performance-streamer.js b/test/fixtures/performance-streamer.js index 3949f34..ee03454 100644 --- a/test/fixtures/performance-streamer.js +++ b/test/fixtures/performance-streamer.js @@ -9,7 +9,18 @@ const maintenance = require(path.join(base, "core/temporary-storage")); const startMaintenance = maintenance.startTemporaryStorageMaintenance; // Exercise the real startup/timer wiring without waiting a production minute. maintenance.startTemporaryStorageMaintenance = () => startMaintenance(25); -GitHubStream.prototype.downloadWithFallback = async function () { +const downloadWithFallback = GitHubStream.prototype.downloadWithFallback; +GitHubStream.prototype.downloadWithFallback = async function (token, sha, filePath) { + if (filePath.startsWith("fail-")) { + this.downloadFile = () => { + const input = new Readable({ read() {} }); + const failure = Object.assign(new Error("fixture upstream failure"), filePath === "fail-reset.txt" + ? { code: "ECONNRESET" } : { response: { statusCode: 401 } }); + process.nextTick(() => input.destroy(failure)); + return input; + }; + return downloadWithFallback.call(this, token, sha, filePath); + } process.send({ download: true }); return Readable.from([Buffer.alloc(300000, "x")]); }; diff --git a/test/fixtures/review-intent-client.js b/test/fixtures/review-intent-client.js new file mode 100644 index 0000000..d15216b --- /dev/null +++ b/test/fixtures/review-intent-client.js @@ -0,0 +1,46 @@ +require("ts-node/register/transpile-only"); +const process = require("process"); +const { setTimeout } = require("timers"); +const { AbortController } = globalThis; +const { + createReviewIntentConsumer, +} = require("../../src/server/service/review-intent-client"); + +process.on("message", async ({ id, config, input, mode }) => { + try { + const client = createReviewIntentConsumer(config); + const controller = new AbortController(); + if (mode === "preabort") controller.abort(); + if (mode === "abort") setTimeout(() => controller.abort(), 100); + if (mode === "mutate") { + config.token = "invalid"; + config.origin = "https://invalid.example"; + } + if (mode === "parallel") { + const pending = Array.from({ length: 5 }, () => + client.consume(input, controller.signal), + ); + const results = await Promise.allSettled(pending); + const followup = await client.consume(input); + process.send({ + id, + results: results.map((r) => + r.status === "fulfilled" ? "ok" : r.reason.kind, + ), + followup, + }); + return; + } + const started = Date.now(); + const result = await client.consume(input, controller.signal); + process.send({ + id, + result, + elapsedMs: Date.now() - started, + frozen: Object.isFrozen(result) && Object.isFrozen(result.policy), + serialized: JSON.stringify(client), + }); + } catch (error) { + process.send({ id, kind: error.kind, message: error.message }); + } +}); diff --git a/test/frontend-regressions.test.js b/test/frontend-regressions.test.js index 9207831..f9469f8 100644 --- a/test/frontend-regressions.test.js +++ b/test/frontend-regressions.test.js @@ -228,3 +228,41 @@ describe("frontend production regressions", function () { expect(h.scope.options.endDate.getTime()).to.be.greaterThan(h.scope.options.startDate.getTime()); }); }); + +describe("redaction highlighting", () => { + const { JSDOM } = require("jsdom"); + function helpers() { + const { window } = new JSDOM(""); + const h = harness(); + Object.assign(h.context, { DOMParser: window.DOMParser, NodeFilter: window.NodeFilter }); + return h.defs; + } + it("matches the mask with and without a counter", () => { + const { redactionPattern } = helpers(); + expect("by XXXX-1 and XXXX-12 at XXXX.".match(redactionPattern("XXXX"))).to.deep.equal(["XXXX-1", "XXXX-12", "XXXX"]); + expect("a.b-3 axb-3".match(redactionPattern("a.b"))).to.deep.equal(["a.b-3"]); + }); + it("marks redactions in text without touching attributes", () => { + const { highlightRedactions } = helpers(); + const html = highlightRedactions('

By XXXX-1 link

', "XXXX"); + expect(html).to.include('By XXXX-1'); + expect(html).to.include('href="https://x.test/XXXX-2"'); + expect(highlightRedactions("

nothing here

", "XXXX")).to.equal("

nothing here

"); + }); + it("leaves Mermaid sources and Prism code blocks for their renderers", () => { + const { highlightRedactions } = helpers(); + const html = highlightRedactions('
graph TD; XXXX-1-->B
print("XXXX-2")
XXXX-3', "XXXX"); + expect(html).to.include('
graph TD; XXXX-1-->B
'); + expect(html).to.include('print("XXXX-2")'); + expect(html).to.include('XXXX-3'); + }); + it("marks a highlighted code block when Prism hands it over, using the configured mask", () => { + const { window } = new (require("jsdom").JSDOM)('
"MASK-4"
'); + const h = harness(); + Object.assign(h.context, { DOMParser: window.DOMParser, NodeFilter: window.NodeFilter }); + h.defs.setRedactionMask("MASK"); + const code = window.document.querySelector("code"); + h.defs.markRedactionsIn(code); + expect(code.querySelector("mark.redaction").textContent).to.equal("MASK-4"); + }); +}); diff --git a/test/github-app.test.js b/test/github-app.test.js index 6b92ea6..d984da1 100644 --- a/test/github-app.test.js +++ b/test/github-app.test.js @@ -95,7 +95,11 @@ describe("GitHub App protocol boundaries", () => { const GitHubStream = require("../src/core/source/GitHubStream").default; const source = Object.create(GitHubStream.prototype); source.data = { organization: "other", repoName: "public", commit: "abc" }; - await source.downloadWithFallback(handle, "blob", "README.md"); + const content = await source.downloadWithFallback(handle, "blob", "README.md"); + expect(captured).to.equal(undefined); + let body = ""; + for await (const chunk of content) body += chunk; + expect(body).to.equal("file"); expect(captured.url).to.include("other/public"); expect(captured.url).to.include("README.md"); expect(captured.options.headers.authorization).to.equal(undefined); @@ -534,7 +538,34 @@ describeMongo("GitHub App credential and repository integration", function () { expect(session.githubAppFlow).to.equal(undefined); const replay = await request("/github/app/callback?state=state&code=code"); expect(replay.status).to.equal(400); - expect(calls).to.have.length(2); + expect(calls).to.have.length(3); + }); + it("saves a verified private email during App registration", async () => { + authenticated = false; + session.githubAppFlow = { state: "state", expires: Date.now() + 60000, returnTo: "/dashboard" }; + mock(url => url.includes("/login/oauth/access_token") ? data() : url.includes("/user/emails?") + ? [{ email: "private@example.com", primary: true, verified: true }] + : { id: 20, login: "new-owner" }); + expect((await request("/github/app/callback?state=state&code=code")).location).to.equal("/dashboard"); + const created = await Users.findOne({ "externalIDs.github": "20" }); + expect(created.emails[0].email).to.equal("private@example.com"); + expect(created.emails[0].default).to.equal(true); + }); + it("preserves an email preference saved while App sign-in retrieves emails", async () => { + authenticated = false; + session.githubAppFlow = { state: "state", expires: Date.now() + 60000, returnTo: "/dashboard" }; + mock(async url => { + if (url.includes("/login/oauth/access_token")) return data(); + if (url.includes("/user/emails?")) { + await Users.updateOne({ _id: owner._id }, { $set: { notificationEmail: "chosen@example.com" } }); + return [{ email: "github@example.com", primary: true, verified: true }]; + } + return { id: 10, login: "owner" }; + }); + expect((await request("/github/app/callback?state=state&code=code")).location).to.equal("/dashboard"); + const current = await Users.findById(owner.id); + expect(current.notificationEmail).to.equal("chosen@example.com"); + expect(current.emails).to.have.length(0); }); it("signs an existing user in through the App without changing legacy access", async () => { authenticated = false; @@ -622,12 +653,54 @@ describeMongo("GitHub App credential and repository integration", function () { const body = { type: "repository", id: resource.repoId, connection: "github-app" }; expect((await request("/github/connections/migrate", { ...body, preview: true })).status).to.equal(200); expect((await CachedRepos.findById(cached.id)).name).to.equal("other/old-public"); - expect((await request("/github/connections/migrate", body)).status).to.equal(200); + const repaired = await request("/github/connections/migrate", body); + expect(repaired.status).to.equal(200); + expect(repaired.data.fullName).to.equal("other/public"); expect((await Repos.findById(resource.id)).githubAccess.publicRead).to.equal(true); expect((await Repos.findById(resource.id)).source.repositoryName).to.equal("other/public"); expect((await CachedRepos.findById(cached.id)).name).to.equal("other/public"); }); + for (const permission of ["granted", "missing", "replacement", "missing-commit"]) { + it(`repairs a public binding after the source becomes private (${permission})`, async () => { + const Repos = require("../src/core/model/anonymizedRepositories/anonymizedRepositories.model").default; + await app.saveAppGrant(owner.id, data()); + const resource = await Repos.create({ repoId: "visibility-" + permission, owner: owner.id, status: "ready", + source: { type: "GitHubStream", repositoryName: "owner/repo", commit: "pinned", branch: "main" }, + options: { terms: ["author"], update: false }, + githubAccess: { kind: "github-app", repositoryId: 42, publicRead: true, revision: "original" } }); + const before = resource.toObject(); + mock(url => { + if (url.includes("/user/installations?")) return { installations: [{ id: 4, app_id: 123, account: { id: 10, login: "owner", type: "User" } }] }; + if (url.includes("/user/installations/4/repositories")) return { repositories: permission === "missing" ? [] : [ + { id: permission === "replacement" ? 99 : 42, full_name: "owner/repo", private: true, visibility: "private" }, + ] }; + if (url.endsWith("/repos/owner/repo") || url.endsWith("/repositories/42")) return { id: 42, full_name: "owner/repo", private: true, visibility: "private" }; + if (url.endsWith("/app/installations/4")) return { app_id: 123, permissions: { contents: "read", metadata: "read" } }; + if (url.endsWith("/access_tokens")) return { token: "ghs_private", expires_at: new Date(Date.now() + 3600000).toISOString() }; + if (url.endsWith("/repos/owner/repo/commits/pinned")) return permission === "missing-commit" ? { status: 404 } : { sha: "pinned" }; + throw new Error("Unexpected request " + url); + }); + await rejects(app.boundAppToken(owner.id, resource.githubAccess, "owner/repo"), "github_app_access_required"); + const result = await request("/github/connections/migrate", { type: "repository", id: resource.repoId, connection: "github-app" }); + const after = await Repos.findById(resource.id); + expect(after.repoId).to.equal(before.repoId); + expect(after.toObject().source).to.deep.equal(before.source); + expect(after.toObject().options).to.deep.equal(before.options); + expect(after.status).to.equal("ready"); + if (permission === "granted") { + expect(result.status).to.equal(200); + expect(after.githubAccess.publicRead).not.to.equal(true); + expect(after.githubAccess.installationId).to.equal(4); + expect(after.githubAccess.repositoryId).to.equal(42); + expect(await app.boundAppToken(owner.id, after.githubAccess, "owner/repo")).to.equal("ghs_private"); + } else { + expect(result.status).to.equal(403); + expect(after.toObject().githubAccess).to.deep.equal(before.githubAccess); + } + }); + } + it("recovers a renamed private pull request through its stable repository ID", async () => { const PRs = require("../src/core/model/anonymizedPullRequests/anonymizedPullRequests.model").default; await app.saveAppGrant(owner.id, data()); diff --git a/test/github-submodules.test.js b/test/github-submodules.test.js new file mode 100644 index 0000000..d011474 --- /dev/null +++ b/test/github-submodules.test.js @@ -0,0 +1,308 @@ +const { expect } = require("chai"); +const { Readable, PassThrough } = require("node:stream"); +const { once } = require("node:events"); +const { setImmediate } = require("node:timers"); +const archiver = require("archiver"); +const got = require("got"); +require("ts-node/register/transpile-only"); +const utils = require("../src/core/GitHubUtils"); +const { expandSubmodules, submoduleRepository, validateMounts, MAX_SUBMODULES, MAX_SUBMODULE_ENTRIES } = require("../src/core/source/github-submodules"); +const GitHubStream = require("../src/core/source/GitHubStream").default; +const FileModel = require("../src/core/model/files/files.model").default; +const storage = require("../src/core/storage").default; +const { streamAnonymizedZip } = require("../src/core/zipStream"); +const config = require("../src/config").default; +const sha = "a".repeat(40), blob = "b".repeat(40); +const parent = { repoId: "submodule-test", organization: "parent", repoName: "repo", commit: "c".repeat(40), getToken: () => "parent-private-token" }; +const upstream = { organization: "child", repoName: "public", commit: sha, path: "src/Alice.txt" }; +const mount = () => new FileModel({ repoId: parent.repoId, name: "dependency", path: "vendor", sha, submodule: "unsupported" }); + +async function zip(entries) { + const archive = archiver("zip"); const chunks = []; + archive.on("data", chunk => chunks.push(chunk)); const done = once(archive, "end"); + for (const [name, content] of Object.entries(entries)) archive.append(content, { name }); + await archive.finalize(); await done; return Buffer.concat(chunks); +} + +async function readZip(options) { + const response = new PassThrough(); const parser = require("unzip-stream").Parse(); + const entries = [], files = {}; + parser.on("entry", entry => { + const chunks = []; entry.on("data", chunk => chunks.push(chunk)); + entries.push(once(entry, "end").then(() => { files[entry.path] = Buffer.concat(chunks).toString(); })); + }); + response.pipe(parser); const done = once(parser, "finish"); + await streamAnonymizedZip(options, response); await done; await Promise.all(entries); + return files; +} + +const zipOptions = { ...parent, getToken: () => "", anonymizerOptions: { terms: ["Alice"], image: true, link: true }, + submodules: [{ organization: "child", repoName: "public", commit: sha, path: "vendor/dependency" }] }; + +describe("public GitHub submodules", function () { + this.timeout(10000); + const restore = []; + function stub(object, key, value) { const previous = object[key]; object[key] = value; restore.push(() => { object[key] = previous; }); } + afterEach(() => restore.splice(0).reverse().forEach(fn => fn())); + beforeEach(() => stub(utils, "waitForTokenGate", async () => {})); + + function api(tree = [{ path: "src/Alice.txt", type: "blob", sha: blob, size: 5 }], options = {}) { + const calls = []; + stub(utils, "octokit", token => ({ + repos: { getContent: async args => { + calls.push({ method: "contents", token, ...args }); + return { data: { submodule_git_url: options.url || "https://github.com/child/public.git", sha } }; + } }, + git: { getTree: async args => { + calls.push({ method: "tree", token, ...args }); + if (options.error) throw options.error; + return { data: { tree, truncated: options.truncated || false } }; + } }, + })); + return calls; + } + + it("mounts a pinned child tree without forwarding parent credentials or exposing .gitmodules", async () => { + const calls = api([ + { path: ".gitmodules", type: "blob", sha: blob, size: 100 }, + { path: "src", type: "tree", sha: blob }, + { path: "src/Alice.txt", type: "blob", sha: blob, size: 5 }, + { path: "nested", type: "commit", sha }, + ]); + const files = await expandSubmodules([mount(), { name: ".gitmodules", path: "" }], parent); + expect(files.map(file => file.name)).to.deep.equal(["dependency", "src", "Alice.txt", "nested"]); + expect(files[0].submodule).to.equal("included"); + expect(files[2]).to.include({ path: "vendor/dependency/src", sha: blob, size: 5 }); + expect(files[2].upstream).to.deep.equal(upstream); + expect(files[3].submodule).to.equal("nested"); + expect(calls).to.deep.equal([ + { method: "contents", token: "parent-private-token", owner: "parent", repo: "repo", path: "vendor/dependency", ref: parent.commit }, + { method: "tree", token: "", owner: "child", repo: "public", tree_sha: sha, recursive: "1" }, + ]); + expect(new FileModel(files[2]).toObject().upstream).to.deep.equal(upstream); + }); + + it("discovers submodules through scoped public-read contexts without authorizing child or write requests", async () => { + const { registerGitHubToken } = require("../src/core/github-token-context"); + const token = "public-read:submodule-discovery"; + registerGitHubToken(token, { publicRepository: "parent/repo", quotaKey: "submodule-test", + renew: async () => "parent-user-token" }); + const calls = []; + stub(globalThis, "fetch", async (url, options) => { + calls.push({ url: String(url), authorization: new globalThis.Headers(options.headers).get("authorization") }); + const result = String(url).includes("/contents/") + ? { submodule_git_url: "https://github.com/child/public.git", sha } + : { tree: [{ path: "src/Alice.txt", type: "blob", sha: blob, size: 5 }], truncated: false }; + return new globalThis.Response(JSON.stringify(result), { headers: { "content-type": "application/json" } }); + }); + const files = await expandSubmodules([mount()], { ...parent, getToken: () => token }); + expect(files[0].submodule).to.equal("included"); + expect(calls).to.deep.equal([ + { url: `https://api.github.com/repos/parent/repo/contents/vendor%2Fdependency?ref=${parent.commit}`, authorization: "token parent-user-token" }, + { url: `https://api.github.com/repos/child/public/git/trees/${sha}?recursive=1`, authorization: null }, + ]); + for (const request of [ + "GET /repos/child/public/contents/file.txt", "GET /repos/parent/repo-other/contents/file.txt", + "PUT /repos/parent/repo/contents/file.txt", "DELETE /repos/parent/repo/contents/file.txt", + ]) { + let caught; try { await utils.octokit(token).request(request); } catch (error) { caught = error; } + expect(caught?.message).to.equal("github_app_access_required"); + } + expect(calls).to.have.length(2); + }); + + it("recognizes gitlinks from the parent tree and leaves ordinary repositories at one request", async () => { + const source = new GitHubStream(parent); let calls = 0; + source.getGHTree = async () => { calls++; return { tree: [{ path: "README.md", type: "blob", sha: blob, size: 5 }] }; }; + expect((await source.getFiles()).map(file => file.name)).to.deep.equal(["README.md"]); expect(calls).to.equal(1); + api(); + source.getGHTree = async () => ({ tree: [{ path: "vendor/dependency", type: "commit", mode: "160000", sha }] }); + expect((await source.getFiles())[0].submodule).to.equal("included"); + }); + + it("supports relative and SSH GitHub URLs and rejects other hosts and unsafe paths", () => { + for (const url of ["../public.git", "git@github.com:parent/public.git", "ssh://git@github.com/parent/public.git", "https://github.com/parent/public.git"]) { + expect(submoduleRepository(url, parent)).to.deep.equal({ organization: "parent", repoName: "public" }); + } + for (const url of ["https://evil.test/child/repo", "file:///tmp/repo", "https://github.com@evil.test/a/b", "https://github.com/a/b?token=secret", "https://github.com/a%2fb/c"]) expect(submoduleRepository(url, parent)).to.equal(null); + for (const path of ["../escape", "/absolute", "a/../b", "a\\b", "a//b", "a\0b"]) expect(() => validateMounts([{ ...upstream, path }])).to.throw("invalid_request"); + expect(() => validateMounts([{ ...upstream, path: "lib" }, { ...upstream, path: "lib/nested" }])).to.throw("invalid_request"); + }); + + it("leaves private, missing and external children unsupported without blocking parent files", async () => { + api([], { error: Object.assign(new Error("not found"), { status: 404 }) }); + const result = await expandSubmodules([mount(), { name: "README.md", path: "", size: 3 }], parent); + expect(result[0].submodule).to.equal("unavailable"); expect(result[1].name).to.equal("README.md"); + const calls = api([], { url: "https://gitlab.com/child/repo.git" }); + expect((await expandSubmodules([mount()], parent))[0].submodule).to.equal("unsupported"); + expect(calls).to.have.length(1); + }); + + it("does not persist transient failures as permanently missing dependencies", async () => { + const error = Object.assign(new Error("rate limited"), { status: 403 }); api([], { error }); + let caught; try { await expandSubmodules([mount()], parent); } catch (err) { caught = err; } + expect(caught).to.equal(error); + }); + + it("bounds module count and rejects oversized or truncated child trees", async () => { + const calls = api(); + const mounts = Array.from({ length: MAX_SUBMODULES + 1 }, (_, i) => ({ ...mount().toObject(), name: `dependency${i}` })); + const result = await expandSubmodules(mounts, parent); + expect(result[MAX_SUBMODULES].submodule).to.equal("limit"); expect(calls).to.have.length(MAX_SUBMODULES * 2); + api(Array.from({ length: MAX_SUBMODULE_ENTRIES + 1 }, (_, i) => ({ path: `file${i}`, type: "blob", sha: blob, size: 1 }))); + expect((await expandSubmodules([mount()], parent))[0].submodule).to.equal("limit"); + api([], { truncated: true }); expect((await expandSubmodules([mount()], parent))[0].submodule).to.equal("limit"); + }); + + for (const hydrated of [false, true]) it(`fetches child content from ${hydrated ? "hydrated" : "plain"} metadata and caches under its parent mount`, async () => { + const fileOrigin = hydrated ? new FileModel({ upstream }).upstream : upstream; + const source = new GitHubStream({ ...parent, cacheGeneration: "tree:date" }); const cache = new Map(), requests = []; + stub(storage, "fileInfo", async (_id, path) => { if (!cache.has(path)) throw Object.assign(Error("missing"), { code: "ENOENT" }); return { size: cache.get(path).length }; }); + stub(storage, "write", async (id, path, input) => { expect(id).to.equal(parent.repoId); const chunks = []; for await (const chunk of input) chunks.push(Buffer.from(chunk)); cache.set(path, Buffer.concat(chunks)); }); + stub(storage, "read", async (_id, path) => Readable.from([cache.get(path)])); + stub(got, "stream", (url, options) => { requests.push({ url, options }); return Readable.from([Buffer.from("Alice")]); }); + for (let i = 0; i < 2; i++) { + const stream = await source.getFileContentCache("vendor/dependency/src/Alice.txt", parent.repoId, () => ({ sha: blob, size: 5, upstream: fileOrigin })); + for await (const chunk of stream) expect(chunk.toString()).to.equal("Alice"); + } + expect(requests).to.have.length(1); + expect(requests[0].url).to.equal(`https://github.com/child/public/raw/${sha}/src/Alice.txt`); + expect(requests[0].options.headers).to.deep.equal({}); + expect([...cache.keys()][0]).to.match(/\/vendor\/dependency\/src\/Alice\.txt$/); + const changed = { ...upstream, commit: "d".repeat(40) }; + const stream = await source.getFileContentCache("vendor/dependency/src/Alice.txt", parent.repoId, () => ({ sha: blob, size: 5, upstream: changed })); + stream.resume(); expect(requests).to.have.length(2); + }); + + it("does not report an unavailable public child as lost parent access", async () => { + const { isAccessFailure } = require("../src/core/owner-notifications"); + const source = new GitHubStream(parent); + stub(storage, "fileInfo", async () => { throw Object.assign(Error("missing"), { code: "ENOENT" }); }); + stub(storage, "write", async (_id, _path, input) => { for await (const _chunk of input) { /* consume */ } }); + stub(got, "stream", () => Readable.from((async function* () { + yield* []; + throw Object.assign(Error("not found"), { response: { statusCode: 404 } }); + })())); + let error; + try { await source.getFileContentCache("vendor/lib/file.txt", parent.repoId, () => ({ sha: blob, size: 5, upstream })); } + catch (caught) { error = caught; } + expect(error?.message).to.equal("submodule_not_available"); + expect(isAccessFailure(error)).to.equal(false); + }); + + it("includes pinned child archives with anonymized names and content, omitting manifests and disabled images", async () => { + const main = await zip({ "parent/README.md": "Alice parent", "parent/.gitmodules": "https://github.com/private/identity" }); + const child = await zip({ "child/src/Alice.txt": "Alice child", "child/.gitmodules": "identity", "child/photo.png": "image" }); + const calls = []; + stub(got, "stream", (url, options) => { calls.push({ url, options }); return Readable.from([calls.length === 1 ? main : child]); }); + expect(await readZip({ ...zipOptions, contentOptions: { image: false } })).to.deep.equal({ + "README.md": "XXXX-1 parent", "vendor/dependency/src/XXXX-1.txt": "XXXX-1 child", + }); + expect(calls.map(call => call.url)).to.deep.equal([ + `https://codeload.github.com/parent/repo/zip/${parent.commit}`, `https://codeload.github.com/child/public/zip/${sha}`, + ]); + expect(calls.every(call => call.options === undefined)).to.equal(true); + }); + + it("aborts the ZIP if a child exceeds the per-file limit", async () => { + const main = await zip({ "parent/README.md": "ok" }), child = await zip({ "child/large.txt": "too much data" }); + let calls = 0; stub(got, "stream", () => Readable.from([++calls === 1 ? main : child])); stub(config, "MAX_FILE_SIZE", 4); + const response = new PassThrough(); response.resume(); const errors = []; + response.on("error", error => errors.push(error)); const done = new Promise(resolve => response.once("close", resolve)); + await streamAnonymizedZip(zipOptions, response); await done; + expect(errors.map(error => error.message)).to.deep.equal(["file_too_big"]); + }); +}); + +describe("submodule transport and public metadata", function () { + this.timeout(10000); + it("does not expose upstream repository coordinates in the public tree", async () => { + const Repository = require("../src/core/Repository").default; + const files = await Repository.prototype.anonymizedFiles.call({ + _model: { options: { terms: ["Alice"] } }, + files: async () => [{ name: "Alice.txt", path: "vendor/dependency/src", sha: blob, size: 5, upstream, submodule: "included" }], + }, { includeSha: false }); + expect(files[0]).to.have.all.keys("name", "path", "sha", "size"); + expect(files[0].name).to.equal("XXXX-1.txt"); + expect(JSON.stringify(files)).not.to.include("public"); + }); + + for (const mode of ["send", "anonymizedContent"]) { + it(`preserves child origin through ${mode} and the real streamer route`, async () => { + const express = require("express"); + const File = require("../src/core/AnonymizedFile").default; + const { AnonymizeTransformer } = require("../src/core/anonymize-utils"); + const router = require("../src/streamer/route").router; + const previous = { endpoint: config.STREAMER_ENTRYPOINT, stream: got.stream, + info: storage.fileInfo, write: storage.write, read: storage.read }; + let server, payload; const cache = new Map(), requests = []; + try { + storage.fileInfo = async (_id, path) => { if (!cache.has(path)) throw Object.assign(Error("missing"), { code: "ENOENT" }); return { size: cache.get(path).length }; }; + storage.write = async (_id, path, stream) => { const chunks = []; for await (const chunk of stream) chunks.push(Buffer.from(chunk)); cache.set(path, Buffer.concat(chunks)); }; + storage.read = async (_id, path) => Readable.from([cache.get(path)]); + got.stream = (url, options) => { + if (!String(url).startsWith("https://github.com/")) return previous.stream(url, options); + requests.push({ url, options }); return Readable.from([Buffer.from("Alice")]); + }; + const app = express(); app.use(express.json()); + app.use((req, _res, next) => { payload = req.body; next(); }); app.use("/api", router); + server = await new Promise(resolve => { const listening = app.listen(0, "127.0.0.1", () => resolve(listening)); }); + config.STREAMER_ENTRYPOINT = `http://127.0.0.1:${server.address().port}/`; + const file = new File({ anonymizedPath: "vendor/dependency/src/XXXX-1.txt", repository: { + repoId: parent.repoId, options: { terms: ["Alice"] }, + model: { source: { repositoryName: "parent/repo", commit: parent.commit } }, + getToken: parent.getToken, owner: { id: "owner" }, + generateAnonymizeTransformer: filePath => new AnonymizeTransformer({ terms: ["Alice"], filePath, image: true, link: true }), + } }); + file._file = { name: "Alice.txt", path: "vendor/dependency/src", sha: blob, size: 5, upstream }; + let content = ""; + if (mode === "send") { + const response = new PassThrough(); response.setHeader = () => {}; + response.on("data", chunk => { content += chunk; }); + await file.send(response); + } else { + for await (const chunk of await file.anonymizedContent()) content += chunk; + } + expect(content).to.equal("XXXX-1"); + expect(payload.upstream).to.deep.equal(upstream); + expect(payload.filePath).to.equal("vendor/dependency/src/Alice.txt"); + expect(requests).to.have.length(1); + expect(requests[0].url).to.equal(`https://github.com/child/public/raw/${sha}/src/Alice.txt`); + expect(requests[0].options.headers).to.deep.equal({}); + } finally { + config.STREAMER_ENTRYPOINT = previous.endpoint; got.stream = previous.stream; + storage.fileInfo = previous.info; storage.write = previous.write; storage.read = previous.read; + if (server) { server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); } + } + }); + } + + it("cancels the current child archive when the reader disconnects", async () => { + const main = await zip({ "parent/README.md": "ok" }); const child = new PassThrough(); + const previous = got.stream; let calls = 0, started; + const childStarted = new Promise(resolve => { started = resolve; }); + const response = new PassThrough(); response.resume(); + try { + got.stream = () => { if (++calls === 1) return Readable.from([main]); started(); return child; }; + await streamAnonymizedZip({ ...zipOptions, submodules: [...zipOptions.submodules, + { ...zipOptions.submodules[0], path: "other/dependency" }] }, response); + await childStarted; response.destroy(); + await new Promise(resolve => setImmediate(resolve)); + expect(child.destroyed).to.equal(true); expect(calls).to.equal(2); + } finally { got.stream = previous; child.destroy(); response.destroy(); } + }); +}); + +describe("legacy submodule manifests", function () { + it("rejects a cached .gitmodules file even when its public name was anonymized", async () => { + const File = require("../src/core/AnonymizedFile").default; + const file = new File({ anonymizedPath: ".XXXX-1", repository: { + options: { terms: ["gitmodules"] }, model: { source: { type: "GitHubStream" } }, + } }); + file._file = { name: ".gitmodules", path: "", sha: blob, size: 5 }; + for (const read of [() => file.getFileInfo(), () => file.originalPath()]) { + let error; try { await read(); } catch (caught) { error = caught; } + expect(error?.message).to.equal("file_not_found"); + } + }); +}); diff --git a/test/maintenance-api.test.js b/test/maintenance-api.test.js new file mode 100644 index 0000000..e893607 --- /dev/null +++ b/test/maintenance-api.test.js @@ -0,0 +1,297 @@ +const { expect } = require("chai"); +require("ts-node/register/transpile-only"); +const express = require("express"); +const UserModel = require("../src/core/model/users/users.model").default; +const { bearerTokenAuth, hashToken, maintenanceTokenAllows } = require("../src/server/routes/token-auth"); +const { maintenanceSettings } = require("../src/server/routes/maintenance"); +const { router: connection } = require("../src/server/routes/connection"); + +describe("repository maintenance tokens", () => { + let server, base, originalFind, originalUpdate, model, lookups, logins; + before(async () => { + originalFind = UserModel.findOne; + originalUpdate = UserModel.updateOne; + UserModel.findOne = async () => { lookups++; return model; }; + UserModel.updateOne = async () => ({ matchedCount: 1 }); + const app = express(); + app.use(express.json()); + app.use((req, _res, next) => { + // An existing cookie must not override the supplied restricted token. + req.user = { username: "cookie-admin" }; + req.login = (_user, callback) => { logins++; callback(); }; + next(); + }); + app.use(bearerTokenAuth); + app.use("/github", connection); + app.use((_req, res) => res.json({ reached: true, scoped: !!res.locals.maintenanceToken })); + server = await new Promise(resolve => { const s = app.listen(0, "127.0.0.1", () => resolve(s)); }); + base = `http://127.0.0.1:${server.address().port}`; + }); + beforeEach(() => { + lookups = logins = 0; + model = { _id: "owner", username: "alice", status: "active", apiTokens: [{ + tokenHash: hashToken("secret"), scope: "repository-maintenance", repoIds: ["paper-123"], + expiresAt: new Date(Date.now() + 60_000), + }] }; + }); + after(async () => { + UserModel.findOne = originalFind; + UserModel.updateOne = originalUpdate; + await new Promise(resolve => server.close(resolve)); + }); + const request = (path, method = "GET") => globalThis.fetch(base + path, { + method, headers: { authorization: "Bearer secret" }, + }); + it("allows only the selected repository and explicit maintenance operations", async () => { + for (const [path, method] of [ + ["/api/maintenance/repositories/paper-123", "GET"], + ["/api/maintenance/repositories/paper-123", "PATCH"], + ["/api/repo/paper-123/refresh", "POST"], ["/api/repo/paper-123/extend", "POST"], + ]) { + const response = await request(path, method); + expect(response.status).to.equal(200); + expect((await response.json()).scoped).to.equal(true); + } + }); + it("blocks downloads, previews, source replacement, token minting, and other repos", async () => { + for (const [path, method] of [ + ["/api/repo/paper-123/file/README.md", "GET"], ["/api/repo/paper-123/zip", "GET"], + ["/api/repo/owner/repository/readme", "GET"], ["/api/repo/paper-123", "POST"], + ["/api/maintenance/repositories/another", "PATCH"], ["/api/repo/another/refresh", "POST"], + ["/api/admin", "GET"], ["/api/user/maintenance-tokens", "POST"], + ["/github/login-token", "POST"], ["/w/paper-123/", "GET"], + ]) expect((await request(path, method)).status, path).to.equal(403); + }); + it("rejects encoded scope bypasses and unexpected methods", () => { + for (const path of ["/api/repo/paper%2D123/refresh", "/api/repo/paper-123/refresh/extra", "/api/repo/paper-123//refresh"]) { + expect(maintenanceTokenAllows("POST", path, ["paper-123"])).to.equal(false); + } + expect(maintenanceTokenAllows("DELETE", "/api/maintenance/repositories/paper-123", ["paper-123"])).to.equal(false); + }); + it("rejects expired, revoked, banned, and malformed scoped tokens despite a session", async () => { + model.apiTokens[0].expiresAt = new Date(0); + expect((await request("/api/repo/paper-123/refresh", "POST")).status).to.equal(401); + model.apiTokens[0].expiresAt = undefined; + expect((await request("/api/repo/paper-123/refresh", "POST")).status).to.equal(401); + model.apiTokens[0].expiresAt = new Date(Date.now() + 60_000); + model.status = "banned"; + expect((await request("/api/repo/paper-123/refresh", "POST")).status).to.equal(401); + model = null; + expect((await request("/api/repo/paper-123/refresh", "POST")).status).to.equal(401); + expect(lookups).to.equal(4); + }); + it("cannot exchange a maintenance token in a body for an unrestricted session", async () => { + const response = await globalThis.fetch(base + "/github/login-token", { method: "POST", + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token: "secret" }) }); + expect(response.status).to.equal(401); + expect(logins).to.equal(0); + }); + it("rejects malformed authorization instead of falling back to a cookie", async () => { + for (const authorization of ["Bearer", "Basic anything", "Bearer " + "x".repeat(513)]) { + const response = await globalThis.fetch(base + "/api/admin", { headers: { authorization } }); + expect(response.status).to.equal(401); + } + expect(lookups).to.equal(0); + }); + it("fails closed when authentication storage is unavailable", async () => { + const find = UserModel.findOne; + UserModel.findOne = async () => { throw new Error("database offline"); }; + try { + const response = await request("/api/maintenance/repositories/paper-123"); + expect(response.status).to.equal(503); + expect(await response.json()).to.deep.equal({ error: "authentication_unavailable" }); + } finally { UserModel.findOne = find; } + }); + it("preserves legacy unrestricted admin tokens", async () => { + delete model.apiTokens[0].scope; + expect((await request("/api/admin")).status).to.equal(200); + }); +}); + +describe("maintenance settings patch", () => { + it("preserves omitted fields and accepts explicit false and empty terms", () => { + expect(maintenanceSettings({ terms: [], update: false })).to.deep.equal({ + "options.terms": [], "options.update": false, + }); + }); + it("rejects source changes, arbitrary option paths, invalid types and oversized terms", () => { + for (const body of [null, [], {}, { source: {} }, { "options.update": true }, + { image: "false" }, { terms: [1] }, { terms: [""] }, { terms: ["a".repeat(1001)] }, + { terms: Array(1001).fill("a") }, { pageSource: { branch: "other" } }]) { + expect(() => maintenanceSettings(body)).to.throw("invalid_settings"); + } + }); +}); + +describe("maintenance routes enforce current membership", () => { + const db = require("../src/server/database"); + const RepoModel = require("../src/core/model/anonymizedRepositories/anonymizedRepositories.model").default; + const maintenance = require("../src/server/routes/maintenance").default; + const tokens = require("../src/server/routes/maintenance-tokens").default; + let server, base, originals, user, repo, writes, tokenWrite; + before(async () => { + originals = [UserModel.findById, UserModel.updateOne, db.getRepository, RepoModel.updateOne]; + UserModel.findById = async () => user; + UserModel.updateOne = async (filter, update) => { + tokenWrite = { filter, update }; + return { matchedCount: 1, modifiedCount: 1 }; + }; + db.getRepository = async () => repo; + RepoModel.updateOne = (filter, update) => { + writes.push({ filter, update }); + return { exec: async () => ({ matchedCount: 1 }) }; + }; + const app = express(); + app.use(express.json()); + app.use((req, _res, next) => { + req.user = { user }; + req.isAuthenticated = () => req.headers["x-session"] === "yes"; + next(); + }); + app.use("/api/maintenance", maintenance); + app.use("/api/user/maintenance-tokens", tokens); + server = await new Promise(resolve => { const s = app.listen(0, "127.0.0.1", () => resolve(s)); }); + base = `http://127.0.0.1:${server.address().port}`; + }); + beforeEach(() => { + user = new UserModel({ username: "alice", externalIDs: { github: "123" } }); + repo = { repoId: "paper-123", status: "ready", owner: { id: user.id }, + assertNotArchived() {}, model: { _id: "repo", status: "ready", statusDate: new Date(), + source: { branch: "main", commit: "abc", accessToken: "never-return" }, + options: { terms: ["Alice"], image: true, pdf: true }, coauthors: [], + githubAccess: { secret: "never-return" } } }; + writes = []; tokenWrite = null; + }); + after(async () => { + [UserModel.findById, UserModel.updateOne, db.getRepository, RepoModel.updateOne] = originals; + await new Promise(resolve => server.close(resolve)); + }); + function call(path, method = "GET", body, headers = {}) { + return globalThis.fetch(base + path, { method, + headers: { "Content-Type": "application/json", ...headers }, + body: body ? JSON.stringify(body) : undefined }); + } + it("returns saved state without credentials and marks it uncacheable", async () => { + const response = await call("/api/maintenance/repositories/paper-123"); + expect(response.status).to.equal(200); + expect(response.headers.get("cache-control")).to.equal("no-store"); + const body = await response.json(); + expect(body.source).to.deep.equal({ branch: "main", commit: "abc" }); + expect(JSON.stringify(body)).not.to.include("never-return"); + }); + it("denies reads and writes after membership removal", async () => { + repo.owner.id = "other-owner"; + for (const method of ["GET", "PATCH"]) { + expect((await call("/api/maintenance/repositories/paper-123", method, + method === "PATCH" ? { image: false } : undefined)).status).to.equal(403); + } + expect(writes).to.have.length(0); + }); + it("lets coauthors patch individual options without replacing the source", async () => { + repo.owner.id = "other-owner"; + repo.model.coauthors = [{ githubId: "123" }]; + const response = await call("/api/maintenance/repositories/paper-123", "PATCH", { image: false }); + expect(response.status).to.equal(200); + expect(writes[0].update.$set["options.image"]).to.equal(false); + expect(Object.keys(writes[0].update.$set)).to.deep.equal(["options.image", "settingsSavedAt"]); + expect(writes[0].filter.status).to.equal("ready"); + }); + it("rejects settings changes while removal is underway", async () => { + repo.status = "removing"; + expect((await call("/api/maintenance/repositories/paper-123", "PATCH", { image: false })).status).to.equal(409); + expect(writes).to.have.length(0); + }); + it("issues only hashed, bounded, scoped credentials from a session", async () => { + const response = await call("/api/user/maintenance-tokens", "POST", + { name: "client", repoIds: ["paper-123"] }, { "x-session": "yes" }); + expect(response.status).to.equal(201); + const body = await response.json(); + expect(body.token).to.match(/^[a-f0-9]{64}$/); + expect(tokenWrite.update.$push.apiTokens.tokenHash).to.equal(hashToken(body.token)); + expect(tokenWrite.update.$push.apiTokens.scope).to.equal("repository-maintenance"); + expect(tokenWrite.filter["apiTokens.50"]).to.deep.equal({ $exists: false }); + expect(body).not.to.have.property("tokenHash"); + }); + it("rejects token issuance without session, with bearer, or for unrelated repositories", async () => { + const body = { name: "client", repoIds: ["paper-123"] }; + expect((await call("/api/user/maintenance-tokens", "POST", body)).status).to.equal(401); + expect((await call("/api/user/maintenance-tokens", "POST", body, + { "x-session": "yes", authorization: "Bearer anything" })).status).to.equal(401); + repo.owner.id = "other-owner"; + expect((await call("/api/user/maintenance-tokens", "POST", body, { "x-session": "yes" })).status).to.equal(403); + expect(tokenWrite).to.equal(null); + }); + it("rejects token lifetimes above 90 days", async () => { + expect((await call("/api/user/maintenance-tokens", "POST", + { name: "client", repoIds: ["paper-123"], expiresInDays: 91 }, + { "x-session": "yes" })).status).to.equal(400); + expect(tokenWrite).to.equal(null); + }); + it("revokes only the signed-in account's maintenance token", async () => { + const id = "507f1f77bcf86cd799439011"; + expect((await call(`/api/user/maintenance-tokens/${id}`, "DELETE", undefined, + { "x-session": "yes" })).status).to.equal(200); + expect(String(tokenWrite.filter._id)).to.equal(user.id); + expect(tokenWrite.update.$pull.apiTokens).to.deep.equal({ _id: id, scope: "repository-maintenance" }); + }); +}); + +describe("maintenance abuse limits", () => { + const { rateLimit } = require("express-rate-limit"); + const { tokenAttemptLimitOptions, maintenanceRefreshLimitOptions } = require("../src/server/routes/maintenance-limits"); + let server, base, originals, lookups, model, reached; + beforeEach(async () => { + originals = [UserModel.findOne, UserModel.updateOne]; + UserModel.findOne = async () => { lookups++; return model; }; + UserModel.updateOne = async () => ({ matchedCount: 1 }); + const app = express(); + app.use(rateLimit({ ...tokenAttemptLimitOptions, limit: 8 })); + app.use(bearerTokenAuth); + app.use(rateLimit(maintenanceRefreshLimitOptions)); + app.use((_req, res) => { reached++; res.json({ ok: true }); }); + server = await new Promise(resolve => { const s = app.listen(0, "127.0.0.1", () => resolve(s)); }); + base = `http://127.0.0.1:${server.address().port}`; + model = { _id: "owner", status: "active", apiTokens: ["one", "two"].map(token => ({ + tokenHash: hashToken(token), scope: "repository-maintenance", repoIds: ["paper-123", "Paper-123"], + expiresAt: new Date(Date.now() + 60_000), + })) }; + lookups = reached = 0; + }); + afterEach(async () => { + [UserModel.findOne, UserModel.updateOne] = originals; + await new Promise(resolve => server.close(resolve)); + }); + it("shares refresh cooldown across tokens, casing and trailing slashes; allows polling", async () => { + const call = (path, token, method = "POST") => globalThis.fetch(base + path, { + method, headers: { authorization: `Bearer ${token}` }, + }); + expect((await call("/api/repo/paper-123/refresh", "one")).status).to.equal(200); + const denied = await call("/api/repo/Paper-123/refresh/", "two"); + expect(denied.status).to.equal(429); + expect(Number(denied.headers.get("retry-after"))).to.be.greaterThan(0); + expect(await denied.json()).to.deep.equal({ error: "refresh_rate_limited" }); + expect((await call("/api/maintenance/repositories/paper-123", "two", "GET")).status).to.equal(200); + // A removed coauthor's requests cannot consume another account's cooldown. + model._id = "another-owner"; + expect((await call("/api/repo/paper-123/refresh", "one")).status).to.equal(200); + }); + it("charges failed authentication before MongoDB and never reaches a route", async () => { + model = null; + const beforeRoutes = reached; + for (let i = 0; i < 8; i++) { + expect((await globalThis.fetch(base + "/api/admin", { + headers: { authorization: "Bearer invalid" }, + })).status).to.equal(401); + } + const beforeLookups = lookups; + const response = await globalThis.fetch(base + "/api/admin", { + headers: { authorization: "Bearer invalid" }, + }); + expect(response.status).to.equal(429); + expect(await response.json()).to.deep.equal({ error: "token_attempts_rate_limited" }); + expect(lookups).to.equal(beforeLookups); + expect(reached).to.equal(beforeRoutes); + // Anonymous review traffic does not use the bearer authentication budget. + expect((await globalThis.fetch(base + "/")).status).to.equal(200); + }); +}); diff --git a/test/markdown-compatibility.test.js b/test/markdown-compatibility.test.js new file mode 100644 index 0000000..f6df8b9 --- /dev/null +++ b/test/markdown-compatibility.test.js @@ -0,0 +1,239 @@ +const normalizeFootnotes = html => html.replace(/footnote-(?:[a-f0-9]+-){4}/g, "footnote-"); +const { expect } = require("chai"); +const { JSDOM } = require("jsdom"); +const fs = require("node:fs"); +const path = require("node:path"); +require("ts-node/register/transpile-only"); +const { renderMarkdown, sanitizeMarkdownHtml } = require("../src/markdown"); +const examples = require("./fixtures/markdown/gfm-extensions.json"); +const fragment = source => JSDOM.fragment(renderMarkdown(source)); +const stl = "solid triangle\nfacet normal 0 0 1\nouter loop\nvertex 0 0 0\nvertex 1 0 0\nvertex 0 1 0\nendloop\nendfacet\nendsolid triangle"; + +function normalize(html) { + const dom = JSDOM.fragment(html); + // Formatting whitespace between block elements and boolean-attribute spelling + // have no semantic effect; keep whitespace inside text and code intact. + for (const element of dom.querySelectorAll("*")) { + for (const node of [...element.childNodes]) { + if (node.nodeType === 3 && element.tagName !== "PRE" && element.tagName !== "CODE") { + if (/^\s*$/.test(node.textContent) && /^(TABLE|THEAD|TBODY|TR|UL|OL|SECTION|DIV)$/.test(element.tagName)) node.remove(); + else if (node.nextSibling?.nodeName === "UL") node.textContent = node.textContent.trimEnd(); + } + } + } + const container = dom.ownerDocument.createElement("div"); container.append(dom); + return container.innerHTML.trim(); +} + +describe("GitHub Markdown compatibility", function () { + for (const [index, example] of examples.entries()) { + it(`cmark-gfm ${example.section} example ${index + 1}`, function () { + if (example.html.trim() === "") { + expect(() => renderMarkdown(example.markdown)).not.to.throw(); + return; + } + expect(normalize(renderMarkdown(example.markdown))).to.equal(normalize(sanitizeMarkdownHtml(example.html))); + }); + } + + const cases = [ + ["setext headings", "Title\n=====\n\nSubtitle\n--------", "h1#title + h2#subtitle"], + ["nested emphasis", "***both*** and ~~deleted~~", "em strong, strong em"], + ["reference links", "[link][ref]\n\n[ref]: https://example.com \"Title\"", 'a[href="https://example.com"][title="Title"]'], + ["nested lists", "1. First\n - Nested\n - Deep", "ol ul ul li"], + ["ordered list starts", "42. Forty-two", 'ol[start="42"]'], + ["hard line breaks", "one \ntwo\\\nthree", "p br"], + ["autolink email", "", 'a[href="mailto:reader@example.com"]'], + ["disclosures", "
More\n\n**Details**\n\n
", "details[open] summary"], + ["keyboard text", "Ctrl + C", "kbd"], + ["subscript and superscript", "H2O and x2", "sub + sup, sub"], + ["custom anchors", '\n\n[Go](#custom)', 'a[name="custom"]'], + ["picture sources", 'Logo', 'picture source[media][srcset]'], + ["aligned HTML", '

Centered

', 'p[align="center"]'], + ["theme images", '![Dark](logo.png#gh-dark-mode-only)', 'img.gh-dark-mode-only'], + ["video links", '[Movie](clip.mp4?download=1)', 'video[controls][src="clip.mp4?download=1"]'], + ["image dimensions", '', 'img[width="120"][height="80"][align="right"]'], + ["table spans", '
Heading
Cell
', 'th[colspan="2"]'], + ["emoji", ":smile: :+1: :unknown_emoji:", 'img.emoji[alt=":smile:"]'], + ["current emoji shortcodes", ":melting_face: :saluting_face: :pink_heart:", 'img.emoji[alt=":melting_face:"]'], + ["syntax highlighting", "```javascript\nconst answer = 42;\n```", "code.language-javascript .hljs-keyword"], + ["inline math", "Value $x^2$.", ".katex math msup"], + ["backtick math", "Value $`x^2`$.", ".katex math msup"], + ["display math", "$$\nx^2\n$$", ".katex-display math"], + ["same-line display math", "$$x^2$$", ".katex-display math"], + ["math fences", "~~~math\nx^2\n~~~", ".katex-display math"], + ["Mermaid tilde fences", "~~~mermaid\ngraph TD; A-->B\n~~~", "pre.mermaid"], + ["Mermaid long fences", "````mermaid\ngraph TD; A-->B\n````", "pre.mermaid"], + ["GeoJSON", '```geojson\n{"type":"LineString","coordinates":[[0,0],[10,10]]}\n```', 'figure[data-diagram="geojson"] svg path'], + ["TopoJSON", '```topojson\n{"type":"Topology","objects":{"line":{"type":"LineString","arcs":[0]}},"arcs":[[[0,0],[10,10]]]}\n```', 'figure[data-diagram="topojson"] svg path'], + ["ASCII STL", `\`\`\`stl\n${stl}\n\`\`\``, 'figure[data-diagram="stl"] svg path'], + ]; + for (const [name, source, selector] of cases) it(`renders ${name}`, function () { + expect(fragment(source).querySelector(selector), name).not.to.equal(null); + }); + for (const type of ["NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION"]) it(`renders ${type} alerts`, function () { + const dom = fragment(`> [!${type}]\n> **Read** this.\n>\n> - Details`); + const alert = dom.querySelector(`.markdown-alert-${type.toLowerCase()}`); + expect(alert.querySelector("svg")).not.to.equal(null); + expect(alert.querySelector("strong").textContent).to.equal("Read"); + expect(alert.querySelector("li").textContent).to.equal("Details"); + }); + it("does not turn nested quotes into alerts", function () { + expect(fragment("> Outer\n>\n> > [!NOTE]\n> > Nested").querySelector(".markdown-alert")).to.equal(null); + }); + it("resolves all repeated footnote references and backrefs", function () { + const dom = fragment("One[^long] and two[^long].\n\n[^long]: First **paragraph**.\n\n Second paragraph."); + for (const link of dom.querySelectorAll('a[href^="#"]')) { + expect(dom.querySelector(`[id="${link.getAttribute("href").slice(1)}"]`), link.outerHTML).not.to.equal(null); + } + expect(dom.querySelectorAll("[data-footnote-backref]")).to.have.length(2); + expect(dom.querySelector(".footnotes").textContent).to.include("Second paragraph."); + }); + it("keeps footnote navigation and accessibility targets within each document", function () { + const source = "One[^a].\n\n[^a]: Details"; + const dom = JSDOM.fragment(`
${renderMarkdown(source)}
${renderMarkdown(source)}
`); + const ids = [...dom.querySelectorAll("[id]")].map(node => node.id); + expect(new Set(ids).size).to.equal(ids.length); + for (const article of dom.querySelectorAll("article")) { + for (const node of article.querySelectorAll("[href], [aria-describedby]")) { + const target = node.getAttribute("aria-describedby") || node.getAttribute("href").slice(1); + expect(article.querySelector(`[id="${target}"]`)).not.to.equal(null); + } + } + }); + for (const source of ["Costs $5 and $10 per run.", "Budget: $100-$200."]) it(`preserves currency prose: ${source}`, function () { + const dom = fragment(source); + expect(dom.querySelector(".katex")).to.equal(null); + expect(dom.textContent.trim()).to.equal(source); + }); + it("uses unique GitHub heading slugs including suffixed collisions and Unicode", function () { + const dom = fragment("# Hello\n# Hello\n# Hello-1\n# Hello\n# 中文 *标题*\n# constructor"); + expect([...dom.querySelectorAll("h1")].map(h => h.id)).to.deep.equal(["hello", "hello-1", "hello-1-1", "hello-2", "中文-标题", "constructor"]); + }); + it("keeps formatting syntax literal inside code and escaped text", function () { + const dom = fragment("`$x$ :smile: [^a]`\n\n```text\n> [!NOTE]\n$x$ :smile:\n```\n\n\\*literal\\* and \\$5"); + expect(dom.querySelector(".katex, .emoji, .markdown-alert")).to.equal(null); + expect(dom.textContent).to.include("*literal* and $5"); + }); + it("resolves Markdown and HTML links/images without rewriting code or fragment links", function () { + const dom = JSDOM.fragment(renderMarkdown('[Guide](../guide.md#top) [Local](#top)\n\n\n\n``', "/r/repo/docs/")); + expect(dom.querySelector("a").getAttribute("href")).to.equal("/r/repo/guide.md#top"); + expect(dom.querySelectorAll("a")[1].getAttribute("href")).to.equal("#top"); + expect(dom.querySelector("img").getAttribute("srcset")).to.equal("/r/repo/docs/x.png 1x, /r/repo/docs/x2.png 2x"); + expect(dom.querySelector("code").textContent).to.equal(''); + }); + it("renders GeoJSON polygons rather than their spherical complement", function () { + const source = '```geojson\n{"type":"Polygon","coordinates":[[[-90,35],[-90,30],[-85,30],[-85,35],[-90,35]]]}\n```'; + const path = fragment(source).querySelector("svg path").getAttribute("d"); + expect(path.match(/M/g)).to.have.length(1); + expect(path).not.to.match(/NaN|Infinity/); + }); + it("centers a lone GeoJSON feature at its real coordinates", function () { + const source = '```geojson\n{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[120,45]}}\n```'; + expect(fragment(source).querySelector("svg path").getAttribute("d")).to.include("M320,160"); + }); + it("does not leak references, slug counts, macros or base URLs between documents", function () { + renderMarkdown("# Title\n\n[ref]: a.md\n\n[^a]: note\n\n$\\gdef\\custom{hello}$", "/first/"); + const dom = JSDOM.fragment(renderMarkdown("# Title\n\n[ref] [^a] [file](b.md) $\\custom$", "/second/")); + expect(dom.querySelector("h1").id).to.equal("title"); + expect(dom.textContent).to.include("[ref] [^a]"); + expect(dom.querySelector('a[href="/second/b.md"]')).not.to.equal(null); + expect(dom.querySelector(".katex").textContent).not.to.include("hello"); + }); + for (const language of ["geojson", "topojson", "stl"]) it(`keeps invalid ${language} readable`, function () { + const dom = fragment(`\`\`\`${language}\nnot a diagram \n\`\`\``); + expect(dom.querySelector("pre code").textContent).to.include("not a diagram"); + expect(dom.querySelector("script")).to.equal(null); + }); + it("blocks active content through HTML, TeX, images and generated-content spoofing", function () { + const dom = fragment('\n\nSafe\n\n$\\href{javascript:alert(1)}{click}$'); + expect(dom.querySelector("script,svg,[onload],[autofocus]")).to.equal(null); + expect(dom.querySelector("input").disabled).to.equal(true); + expect(dom.querySelector("input").type).to.equal("checkbox"); + for (const el of dom.querySelectorAll("[href],[src],[srcset]")) expect(el.outerHTML).not.to.include("javascript:"); + }); + + it("serves the shared renderer through the project-page route", async function () { + this.timeout(10000); // Includes cold TypeScript compilation of the route dependencies. + const { Readable } = require("node:stream"); + const utils = require("../src/server/routes/route-utils"); + const File = require("../src/core/AnonymizedFile").default; + const restores = []; + const stub = (object, key, value) => { const previous = object[key]; restores.push(() => { object[key] = previous; }); object[key] = value; }; + try { + stub(utils, "getRepo", async () => ({ options: { terms: [], page: true, pageSource: { path: "/", branch: "main" } }, model: { source: { branch: "main" } } })); + stub(File.prototype, "getFileInfo", async () => ({ name: "README.md", path: "", size: 100 })); + stub(File.prototype, "originalPath", async () => "README.md"); + stub(File.prototype, "isFileSupported", () => true); + stub(File.prototype, "extension", () => "md"); + stub(File.prototype, "sha", async () => "sha"); + stub(File.prototype, "anonymizedContent", async () => Readable.from(["# Title\n\n> [!TIP]\n> Text with $x^2$ and note[^a].\n\n[^a]: Details"])); + stub(File.prototype, "send", () => { throw new Error("Unexpected raw fallback"); }); + stub(utils, "handleError", error => { throw error; }); + const headers = {}; + let html; + const res = { + header(name, value) { headers[name] = value; return this; }, + set(name, value) { headers[name] = value; return this; }, + contentType() { return this; }, send(value) { html = value; return this; }, + }; + const handler = require("../src/server/routes/webview").default.stack[0].route.stack[0].handle; + await handler({ path: "/repo/README.md", params: { repoId: "repo" }, headers: {}, method: "GET" }, res); + const page = JSDOM.fragment(html); + expect(page.querySelector("#title")).not.to.equal(null); + expect(page.querySelector(".markdown-alert-tip .katex")).not.to.equal(null); + expect(page.querySelector(".footnotes")).not.to.equal(null); + expect(html).to.include('/script/markdown.min.js'); + expect(headers["Content-Security-Policy"]).to.include("sandbox"); + expect(headers["Content-Security-Policy"]).not.to.include("allow-same-origin"); + } finally { restores.reverse().forEach(restore => restore()); } + }); + + describe("shipped browser bundle", function () { + let dom; + before(function () { + dom = new JSDOM("
", { runScripts: "outside-only", url: "https://viewer.test/" }); + dom.window.Object.hasOwn = undefined; + dom.window.eval(fs.readFileSync(path.join(__dirname, "../public/script/markdown.min.js"), "utf8")); + dom.window.eval(fs.readFileSync(path.join(__dirname, "../public/script/utils.js"), "utf8")); + }); + after(function () { dom.window.close(); }); + it("matches the server renderer across the feature matrix", function () { + for (const [name, source] of cases) { + expect(normalizeFootnotes(dom.window.renderMD(source, "/r/repo/")), name).to.equal(normalizeFootnotes(renderMarkdown(source, "/r/repo/"))); + } + }); + it("enhances Mermaid inserted while another diagram is rendering", async function () { + const { setTimeout: delay } = require("node:timers/promises"); + const sources = []; + let settings; + dom.window.mermaid = { + initialize(options) { settings = options; }, + async run({ nodes }) { + sources.push(nodes[0].textContent); + nodes[0].dataset.processed = "true"; + if (sources.length === 1) { + dom.window.document.querySelector("main").insertAdjacentHTML("beforeend", dom.window.renderMD("~~~mermaid\ngraph TD; B-->C\n~~~")); + await delay(10); + } + }, + }; + dom.window.document.querySelector("main").innerHTML = dom.window.renderMD("```mermaid\ngraph TD; A-->B\n```"); + await delay(80); + expect(settings.securityLevel).to.equal("strict"); + expect(sources).to.deep.equal(["graph TD; A-->B", "graph TD; B-->C"]); + }); + it("supports keyboard-activated diagram zoom and model rotation", function () { + dom.window.document.querySelector("main").innerHTML = dom.window.renderMD(`\`\`\`stl\n${stl}\n\`\`\``); + const figure = dom.window.document.querySelector("figure"); + figure.querySelector('[data-diagram-action="in"]').click(); + expect(figure.querySelector("g").getAttribute("transform")).to.include("scale(1.25)"); + const before = figure.querySelector("path").getAttribute("d"); + figure.querySelector('[data-diagram-action="rotate"]').click(); + expect(figure.querySelector("path").getAttribute("d")).not.to.equal(before); + figure.querySelector('[data-diagram-action="reset"]').click(); + expect(figure.querySelector("g").getAttribute("transform")).to.include("scale(1)"); + expect(figure.querySelector("path").getAttribute("d")).to.equal(before); + }); + }); +}); diff --git a/test/markdown-fonts.test.js b/test/markdown-fonts.test.js new file mode 100644 index 0000000..03ad026 --- /dev/null +++ b/test/markdown-fonts.test.js @@ -0,0 +1,35 @@ +const { expect } = require("chai"); +const express = require("express"); +const fs = require("node:fs"); +const path = require("node:path"); +const ts = require("typescript"); + +// Exercise the production static middleware without starting databases/workers. +function staticApp() { + const app = express(); + const source = fs.readFileSync(path.join(__dirname, "../src/server/index.ts"), "utf8"); + const start = source.indexOf(" app.use(\n express.static"); + const end = source.indexOf("\n\n app", start); + new Function("app", "express", "join", ts.transpileModule(source.slice(start, end), {}).outputText)(app, express, (...parts) => path.join(__dirname, "..", ...parts)); + return app; +} +module.exports = { staticApp }; + +if (typeof describe === "function") describe("sandboxed Markdown font requests", function () { + let server; + before(async function () { server = await new Promise(resolve => { const listener = staticApp().listen(0, "127.0.0.1", () => resolve(listener)); }); }); + after(async function () { if (server) await new Promise(resolve => server.close(resolve)); }); + it("allows anonymous opaque-origin font requests", async function () { + const response = await globalThis.fetch(`http://127.0.0.1:${server.address().port}/css/fonts/KaTeX_Main-Regular.woff2`, { headers: { Origin: "null" } }); + expect(response.status).to.equal(200); + expect(response.headers.get("access-control-allow-origin")).to.equal("*"); + expect(response.headers.get("access-control-allow-credentials")).to.equal(null); + await response.arrayBuffer(); + }); + it("does not enable CORS for unrelated assets", async function () { + const response = await globalThis.fetch(`http://127.0.0.1:${server.address().port}/script/markdown.min.js`, { headers: { Origin: "null" } }); + expect(response.status).to.equal(200); + expect(response.headers.get("access-control-allow-origin")).to.equal(null); + await response.arrayBuffer(); + }); +}); diff --git a/test/markdown-sanitization.test.js b/test/markdown-sanitization.test.js index 9cae728..7fcc735 100644 --- a/test/markdown-sanitization.test.js +++ b/test/markdown-sanitization.test.js @@ -1,42 +1,6 @@ const { expect } = require("chai"); -const { marked } = require("marked"); -const sanitizeHtml = require("sanitize-html"); - -const sanitizeOptions = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat([ - "img", - "video", - "input", - "details", - "summary", - "del", - "ins", - "sup", - "sub", - ]), - allowedAttributes: { - ...sanitizeHtml.defaults.allowedAttributes, - img: ["src", "srcset", "alt", "title", "width", "height", "loading"], - video: ["src", "controls", "title"], - input: ["type", "checked", "disabled"], - code: ["class"], - span: ["class"], - div: ["class"], - pre: ["class"], - td: ["align"], - th: ["align"], - }, - allowedSchemes: ["http", "https", "mailto"], -}; - -/** - * Mirrors the server-side rendering pipeline in webview.ts: - * sanitizeHtml(marked(content, opts), sanitizeOptions) - */ -function renderAndSanitize(markdown) { - const raw = marked(markdown, { async: false }); - return sanitizeHtml(raw, sanitizeOptions); -} +require("ts-node/register/transpile-only"); +const { renderMarkdown: renderAndSanitize } = require("../src/markdown"); describe("Markdown sanitization", function () { // --------------------------------------------------------------- @@ -192,9 +156,9 @@ describe("Markdown sanitization", function () { describe("preserves safe markdown content", function () { it("preserves headings", function () { const html = renderAndSanitize("# Heading 1\n## Heading 2"); - expect(html).to.include("

"); + expect(html).to.match(/

/); expect(html).to.include("Heading 1"); - expect(html).to.include("

"); + expect(html).to.match(/

/); }); it("preserves paragraphs", function () { @@ -226,7 +190,7 @@ describe("Markdown sanitization", function () { it("preserves code blocks", function () { const html = renderAndSanitize("```js\nconsole.log('hi')\n```"); expect(html).to.include(" html.replace(/footnote-(?:[a-f0-9]+-){4}/g, "footnote-"); +const { expect } = require("chai"); +const fs = require("node:fs"); +const path = require("node:path"); +const vm = require("node:vm"); +const { JSDOM } = require("jsdom"); +require("ts-node/register/transpile-only"); +const { renderMarkdown } = require("../src/markdown"); +const { renderDiagram } = require("../src/markdown-diagrams"); +const root = path.resolve(__dirname, ".."); + +function checkHtml(html, allowMediaData = false) { + const dom = JSDOM.fragment(html); + expect(dom.querySelector("script,iframe,object,embed,form,base,meta,link")).to.equal(null); + for (const element of dom.querySelectorAll("*")) { + for (const attr of element.attributes) { + expect(attr.name).not.to.match(/^on/i); + if (["href", "xlink:href", "src", "srcset", "poster"].includes(attr.name)) { + // Strip URL control characters before checking executable schemes. + // eslint-disable-next-line no-control-regex + expect(attr.value.replace(/[\u0000-\u0020]/g, "")).not.to.match(allowMediaData && ["src", "srcset", "poster"].includes(attr.name) ? /(?:javascript|vbscript):/i : /(?:javascript|vbscript|data):/i); + } + } + } +} + +describe("Markdown security boundaries", function () { + const attacks = [ + '', + 'link', + '', + '', + '

', + '
More
', + '', + 'Text[^a" onclick="alert(1)]\n\n[^a" onclick="alert(1)]: Note', + '$\\href{javascript:alert(1)}{click}$', + '$\\url{javascript:alert(1)}$', + '$\\includegraphics{https://evil.test/tracker.svg}$', + '$\\htmlData{evil=1 onmouseover=alert(1)}{x}$', + '$\\htmlStyle{background:url(https://evil.test/tracker)}{x}$', + '', + '```javascript\n\n```', + '```mermaid\ngraph TD; A[""]\n```', + ]; + let browser; + before(function () { + browser = new JSDOM("", { runScripts: "outside-only", url: "https://viewer.test/" }); + vm.runInContext(fs.readFileSync(path.join(root, "public/script/markdown.min.js"), "utf8"), browser.getInternalVMContext()); + }); + after(function () { browser.window.close(); }); + for (const [index, source] of attacks.entries()) it(`rejects active content payload ${index + 1} on server and browser`, function () { + const server = renderMarkdown(source); + const client = browser.window.MarkdownRenderer.render(source); + checkHtml(server); checkHtml(client); + expect(normalizeFootnotes(client)).to.equal(normalizeFootnotes(server)); + }); + + it("sanitizes mutation payloads with the shipped DOMPurify", function () { + const dom = new JSDOM("", { runScripts: "outside-only" }); + try { + vm.runInContext(fs.readFileSync(path.join(root, "public/script/external/purify.min.js"), "utf8"), dom.getInternalVMContext()); + for (const source of attacks.slice(0, 7)) checkHtml(dom.window.DOMPurify.sanitize(source), true); + } finally { dom.window.close(); } + }); + + it("enforces strict Mermaid mode even when source HTML supplies an initialization flag", async function () { + const dom = new JSDOM('
graph TD; A-->B
', { runScripts: "outside-only" }); + let configuration; + try { + dom.window.mermaid = { + initialize(value) { configuration = value; }, + async run({ nodes }) { nodes.forEach(node => { node.dataset.processed = "true"; }); }, + }; + vm.runInContext(fs.readFileSync(path.join(root, "public/script/markdown.min.js"), "utf8"), dom.getInternalVMContext()); + await new Promise(resolve => require("node:timers").setTimeout(resolve, 0)); + expect(configuration.securityLevel).to.equal("strict"); + } finally { dom.window.close(); } + }); + + for (const [library, source, publicFile] of [ + ["Mermaid", "node_modules/mermaid/dist/mermaid.min.js", "public/script/external/mermaid.min.js"], + ["DOMPurify", "node_modules/dompurify/dist/purify.min.js", "public/script/external/purify.min.js"], + ["KaTeX", "node_modules/katex/dist/katex.min.js", "public/script/external/katex.min.js"], + ["KaTeX CSS", "node_modules/katex/dist/katex.min.css", "public/css/katex.min.css"], + ]) it(`ships the audited ${library} version without an outdated vendored copy`, function () { + expect(fs.readFileSync(path.join(root, publicFile)).equals(fs.readFileSync(path.join(root, source)))).to.equal(true); + }); + + it("rejects array-like objects before TopoJSON loops over their forged length", function () { + const topology = { type: "Topology", arcs: [{ 0: [0, 0], length: 1e12 }], objects: { line: { type: "LineString", arcs: [0] } } }; + expect(() => renderDiagram(JSON.stringify(topology), "topojson")).to.throw("array"); + }); + it("bounds repeated TopoJSON arc expansion before allocating coordinates", function () { + const topology = { type: "Topology", arcs: [Array.from({ length: 1000 }, (_, i) => [i / 100, 0])], objects: { line: { type: "LineString", arcs: Array(1000).fill(0) } } }; + expect(() => renderDiagram(JSON.stringify(topology), "topojson")).to.throw("expansion limit"); + }); + it("rejects deeply nested geometry before recursive library calls", function () { + let geometry = { type: "Point", coordinates: [0, 0] }; + for (let i = 0; i < 40; i++) geometry = { type: "GeometryCollection", geometries: [geometry] }; + expect(() => renderDiagram(JSON.stringify(geometry), "geojson")).to.throw("complexity limit"); + }); + it("rejects nonnumeric coordinate objects without invoking D3", function () { + expect(() => renderDiagram('{"type":"Point","coordinates":[{"valueOf":"attack"},0]}', "geojson")).to.throw("position"); + }); + it("bounds diagram input size and keeps rejected source readable", function () { + expect(() => renderDiagram(" ".repeat(1_000_001), "stl")).to.throw("source limit"); + const source = '```geojson\n{"type":"Point","coordinates":[null,0]}\n```'; + expect(JSDOM.fragment(renderMarkdown(source)).querySelector("pre code").textContent).to.include('"coordinates":[null,0]'); + }); +}); diff --git a/test/mermaid-security.test.js b/test/mermaid-security.test.js new file mode 100644 index 0000000..cf726c2 --- /dev/null +++ b/test/mermaid-security.test.js @@ -0,0 +1,57 @@ +const { expect } = require("chai"); +const fs = require("node:fs"); +const path = require("node:path"); +const vm = require("node:vm"); +const { JSDOM } = require("jsdom"); + +const read = file => fs.readFileSync(path.join(__dirname, "../public", file), "utf8"); +const loader = [...read("index.html").matchAll(/