From 2ae1972a3b3cd6faa435cacbd1d6a7b15e67e02d Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:05:00 +0900 Subject: [PATCH 1/8] =?UTF-8?q?perf(core):=20=EB=8C=80=EC=9A=A9=EB=9F=89?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=83=81=ED=95=9C=20=EC=B4=9D=20?= =?UTF-8?q?=EA=B1=B4=EC=88=98=C2=B7=EC=BB=A4=EC=84=9C=20=EA=B3=84=EC=95=BD?= =?UTF-8?q?=20=EC=8B=A0=EC=84=A4=20=EB=B0=8F=20=EC=9A=94=EC=B2=AD=EB=8B=B9?= =?UTF-8?q?=20=EB=B0=98=EB=B3=B5=20=EC=8B=A4=ED=96=89=20=EB=B9=84=EC=9A=A9?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 공개 이슈 gnuboard/g7 이 지목한 병목은 "같은 일을 반복 실행한다" 축이다. 응답·행의 무게를 다룬 · 과 겹치지 않는다. 공통 계약 — 총 건수 상한과 페이지 이동 범위는 별개 결정이다. 묶으면 필요 없이 기능이 깎인다. 총 건수만 상한을 받고(파생 테이블 COUNT), "다음" 이동은 per_page + 1 실측으로 끝까지 열어 둔다. 계산이 불가능해지는 것은 마지막 페이지 번호 하나뿐이며 그 사실은 last_page: null 이 알린다. 최신순처럼 실제 컬럼으로 정렬하는 목록은 커서로 전환해 깊이와 무관하게 일정 속도로 이동한다. 관련도순은 계산값 정렬이라 커서 키로 쓸 수 없어 offset 을 유지한다. 계약의 입구는 표준 paginate 와 같은 폭이어야 한다. 관계·쿼리 빌더를 받지 못하면 그 좁은 만큼이 그대로 운영 500 이 되고, 실제로 관리자 알림 목록에서 그렇게 터졌다. 응답 조립도 컬렉션마다 손으로 하면 형태가 늘어나는 순간 없는 값을 부르거나 새 필드를 흘리므로, 형태 판정을 paginationMeta 한 곳에 모았다. 표준 paginate 응답은 필드 단위로 이전과 동일하다. 요청당 반복 비용 — 훅 구독마다 남기던 로그 400줄, 요청당 스무 번 넘던 설정 파일 재읽기, 이미 캐시된 목록의 DB 재조회를 없앴다. 권한 판정은 요청 스코프 메모를 두어 화면 요소마다 나가던 조회를 한 번으로 줄였고, 크로스 요청 캐시는 두지 않아 권한 변경이 종전처럼 다음 요청에 반영된다. 검색 질의는 활성 엔진이 만든다. 저장소가 구체 엔진을 지목하면 플러그인이 등록한 엔진은 호출될 기회 자체를 잃고 오류 없이 다른 방식으로 동작한다. 해석기를 두어 활성 엔진에 위임하고, 전문검색이 없는 DBMS 의 부분일치 폴백도 드라이버명 하드코딩 대신 선언형 config 로 옮겼다. 총 건수가 잘린 목록에서 순번을 역산하면 0 과 음수가 나온다. 지어내지 않고 null 을 돌려주며, 그 원칙을 last_page 와 동일하게 적용했다. --- .env.example | 4 +- AGENTS.md | 43 ++- CHANGELOG.md | 23 ++ .../Pagination/BoundedTotalAware.php | 39 ++ .../ActivityLogRepositoryInterface.php | 13 +- .../NotificationLogRepositoryInterface.php | 3 +- app/Enums/TotalRelation.php | 60 +++ app/Extension/HookListenerRegistrar.php | 13 +- .../Api/Base/AuthBaseController.php | 32 +- .../Api/Base/PublicBaseController.php | 28 +- .../Api/Public/PublicSearchController.php | 103 +++++- app/Http/Middleware/PermissionMiddleware.php | 25 +- .../ActivityLog/ActivityLogIndexRequest.php | 4 + .../NotificationLogIndexRequest.php | 4 + app/Http/Requests/Public/SearchRequest.php | 40 +- .../Requests/Settings/SaveSettingsRequest.php | 12 + app/Http/Resources/ActivityLogCollection.php | 14 +- .../IdentityMessageDefinitionCollection.php | 11 +- app/Http/Resources/BaseApiCollection.php | 71 +++- .../NotificationDefinitionCollection.php | 13 +- .../Resources/NotificationLogCollection.php | 15 +- app/Http/Resources/Traits/HasRowNumber.php | 14 +- .../Resources/UserNotificationCollection.php | 21 +- app/Models/User.php | 164 ++++++--- app/Providers/AuthServiceProvider.php | 28 +- app/Providers/CoreServiceProvider.php | 2 +- app/Providers/ModuleRouteServiceProvider.php | 13 +- app/Providers/SettingsServiceProvider.php | 1 + app/Repositories/ActivityLogRepository.php | 39 +- .../Concerns/FiltersByDateRange.php | 72 ++++ .../Concerns/PaginatesWithDeferredJoin.php | 54 ++- app/Repositories/IdentityPolicyRepository.php | 2 + .../IdentityVerificationLogRepository.php | 4 + app/Repositories/JsonConfigRepository.php | 91 ++++- app/Repositories/LanguagePackRepository.php | 5 + .../NotificationLogRepository.php | 28 +- app/Repositories/NotificationRepository.php | 11 +- app/Repositories/RoleRepository.php | 3 + .../ScheduleHistoryRepository.php | 4 + app/Repositories/ScheduleRepository.php | 16 +- app/Repositories/UserRepository.php | 19 +- .../Contracts/KeywordPredicateProvider.php | 78 ++++ app/Search/DTO/KeywordSearchContext.php | 36 ++ app/Search/Engines/DatabaseFulltextEngine.php | 300 ++++++++++++--- app/Search/KeywordSearch.php | 280 ++++++++++++++ app/Search/SearchCategoryPayload.php | 100 +++++ app/Search/SearchPagePolicy.php | 68 ++++ app/Services/ActivityLogService.php | 19 +- app/Services/AuthService.php | 1 + .../LanguagePack/LanguagePackRegistry.php | 10 +- app/Services/NotificationLogService.php | 24 +- app/Services/UserService.php | 3 + app/Support/GuestRoleResolver.php | 108 ++++++ app/Support/Query/BoundedCount.php | 89 +++++ app/Support/Query/BoundedPage.php | 125 +++++++ app/Support/Query/BoundedPaginator.php | 209 +++++++++++ app/Support/Query/KeysetPaginator.php | 149 ++++++++ app/Support/Query/PaginationLimits.php | 80 ++++ config/core.php | 48 +++ config/settings/defaults.json | 15 +- docs/README.md | 5 +- docs/backend/README.md | 1 + docs/backend/api/README.md | 27 ++ docs/backend/api/activity-logs.md | 22 ++ docs/backend/api/search.md | 25 +- docs/backend/api/settings.md | 16 + docs/backend/core-config.md | 22 +- docs/backend/pagination.md | 344 ++++++++++++++++++ docs/backend/search-system.md | 165 ++++++++- docs/backend/service-repository.md | 24 +- docs/requirements.md | 36 +- lang-packs/_bundled/g7-core-ja/CHANGELOG.md | 3 + .../g7-core-ja/backend/ja/pagination.php | 12 + .../_bundled/g7-core-ja/backend/ja/search.php | 4 + .../g7-core-ja/backend/ja/validation.php | 6 + lang/en/pagination.php | 21 ++ lang/en/search.php | 4 + lang/en/validation.php | 8 + lang/ko/pagination.php | 23 ++ lang/ko/search.php | 4 + lang/ko/validation.php | 8 + tests/Concerns/CountsQueries.php | 81 +++++ .../Api/Admin/ActivityLogControllerTest.php | 12 + .../Admin/NotificationLogControllerTest.php | 61 +++- .../Middleware/PermissionMiddlewareTest.php | 6 +- .../AdminNotificationListTest.php | 150 ++++++++ .../DeferredJoinBoundedTotalTest.php | 194 ++++++++++ .../ListQueryCountRegressionTest.php | 221 +++++++++++ .../Search/SearchAccuracyContractTest.php | 152 ++++++++ .../Search/SearchBadgeAccuracyTest.php | 89 +++++ .../bounded-total-and-storefront.spec.ts | 270 ++++++++++++++ .../HasRowNumberBoundedTotalTest.php | 113 ++++++ .../PaginationLastPageCollapseTest.php | 181 +++++++++ .../CoreActivityLogListenerSignatureTest.php | 6 + .../Search/DatabaseFulltextEngineTest.php | 16 +- tests/Unit/Search/KeywordSearchTest.php | 289 +++++++++++++++ tests/Unit/Search/SearchPagePolicyTest.php | 121 ++++++ tests/Unit/Support/Query/BoundedCountTest.php | 100 +++++ .../Support/Query/BoundedPaginatorTest.php | 333 +++++++++++++++++ .../Support/Query/PaginationContractTest.php | 240 ++++++++++++ tests/scenarios/bounded-total-pagination.yaml | 164 +++++++++ 101 files changed, 6160 insertions(+), 322 deletions(-) create mode 100644 app/Contracts/Pagination/BoundedTotalAware.php create mode 100644 app/Enums/TotalRelation.php create mode 100644 app/Repositories/Concerns/FiltersByDateRange.php create mode 100644 app/Search/Contracts/KeywordPredicateProvider.php create mode 100644 app/Search/DTO/KeywordSearchContext.php create mode 100644 app/Search/KeywordSearch.php create mode 100644 app/Search/SearchCategoryPayload.php create mode 100644 app/Search/SearchPagePolicy.php create mode 100644 app/Support/GuestRoleResolver.php create mode 100644 app/Support/Query/BoundedCount.php create mode 100644 app/Support/Query/BoundedPage.php create mode 100644 app/Support/Query/BoundedPaginator.php create mode 100644 app/Support/Query/KeysetPaginator.php create mode 100644 app/Support/Query/PaginationLimits.php create mode 100644 docs/backend/pagination.md create mode 100644 lang-packs/_bundled/g7-core-ja/backend/ja/pagination.php create mode 100644 lang/en/pagination.php create mode 100644 lang/ko/pagination.php create mode 100644 tests/Concerns/CountsQueries.php create mode 100644 tests/Feature/Notifications/AdminNotificationListTest.php create mode 100644 tests/Feature/Performance/DeferredJoinBoundedTotalTest.php create mode 100644 tests/Feature/Performance/ListQueryCountRegressionTest.php create mode 100644 tests/Feature/Search/SearchAccuracyContractTest.php create mode 100644 tests/Feature/Search/SearchBadgeAccuracyTest.php create mode 100644 tests/Playwright/specs/pagination/bounded-total-and-storefront.spec.ts create mode 100644 tests/Unit/Http/Resources/HasRowNumberBoundedTotalTest.php create mode 100644 tests/Unit/Layouts/PaginationLastPageCollapseTest.php create mode 100644 tests/Unit/Search/KeywordSearchTest.php create mode 100644 tests/Unit/Search/SearchPagePolicyTest.php create mode 100644 tests/Unit/Support/Query/BoundedCountTest.php create mode 100644 tests/Unit/Support/Query/BoundedPaginatorTest.php create mode 100644 tests/Unit/Support/Query/PaginationContractTest.php create mode 100644 tests/scenarios/bounded-total-pagination.yaml diff --git a/.env.example b/.env.example index 861b052d..46373dbb 100644 --- a/.env.example +++ b/.env.example @@ -22,7 +22,9 @@ BCRYPT_ROUNDS=12 LOG_CHANNEL=stack LOG_STACK=single LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug +# 설치 직후 기본값은 운영 기준(error). 관리자 환경설정 > 고급에서 조정하며, +# 그 값이 이 항목보다 우선합니다. debug 로 두면 정상 동작 기록까지 매 요청 쌓입니다. +LOG_LEVEL=error DB_CONNECTION=mysql diff --git a/AGENTS.md b/AGENTS.md index 745899c4..be419f3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ -### 백엔드 [backend/](docs/backend/) (33개) +### 백엔드 [backend/](docs/backend/) (34개) | 문서 | 설명 | TL;DR 핵심 | |------|------|-----------| @@ -33,6 +33,7 @@ | [language-pack-service.md](docs/backend/language-pack-service.md) | LanguagePackService (백엔드 Service 레이어) | LanguagePackService 가 install/activate/deactivate/uninsta... | | [middleware.md](docs/backend/middleware.md) | 미들웨어 등록 규칙 | 인증 필요 미들웨어 → 전역 등록 금지! | | [notification-system.md](docs/backend/notification-system.md) | 알림 시스템 (Notification System) | GenericNotification 범용 클래스 1개로 모든 알림 처리 (개별 클래스 불필요) | +| [pagination.md](docs/backend/pagination.md) | 대용량 목록 페이지네이션 (Pagination) | 총 건수만 상한을 받는다 — 상한 이하면 정확, 초과면 "이상"(total_relation=at_least) | | [response-helper.md](docs/backend/response-helper.md) | API 응답 규칙 (ResponseHelper) | 모든 API 응답은 ResponseHelper 사용 | | [routing.md](docs/backend/routing.md) | 라우트 네이밍 및 경로 | 모든 라우트는 name() 필수: ->name('api.users.index') | | [search-system.md](docs/backend/search-system.md) | Scout 검색 엔진 시스템 (Search System) | Laravel Scout + DatabaseFulltextEngine: MySQL FULLTEXT + ... | @@ -397,6 +398,28 @@ Icon 은 `` 글리프라 박스 크기가 곧 `font-size` 다. `w-N h-N` 은 > 상세: [service-repository.md "목록 조회 컬럼 프루닝과 지연 조인" / "정렬 컬럼 화이트리스트" / "화면 정렬 옵션은 게이트의 부분집합이어야 한다" / "관계 테이블 컬럼 기준 정렬" / "허용되는 Raw 쿼리"](docs/backend/service-repository.md) +### 대용량 목록의 총 건수와 페이지 이동 + +총 건수 상한과 페이지 이동 범위는 **별개 결정**이다. 묶으면 필요 없이 기능이 깎인다. 총 건수만 상한을 받고, "다음" 이동은 `per_page + 1` 실측으로 끝까지 열어 둔다. 계산이 불가능해지는 것은 마지막 페이지 번호 하나뿐이다. + +| ❌ 금지 | ✅ 올바른 사용 | +|--------|---------------| +| 같은 술어를 `count()` 한 번, `get()` 한 번 실행 | `BoundedPaginator::paginate()` 한 번 (총 건수 + 페이지를 한 번에) | +| `paginate(PHP_INT_MAX)` 후 PHP `array_slice` | 실제 `page`/`per_page` 를 저장소까지 하달 | +| `forPage($page, $perPage + 1)` | offset 은 `per_page` 기준으로 따로 계산 (안 그러면 페이지가 깊어질수록 경계가 밀린다) | +| Scout `->keys()->all()` + 무제한 `whereIn` | 키워드 술어를 페이지 쿼리에 직접 밀어넣기 (`DatabaseFulltextEngine::whereFulltext`) | +| FULLTEXT 원문 키워드를 raw 로 바인딩 | 코어 sanitizer 경유 — `+` `-` `*` `"` 입력이 파싱 오류로 500 이 된다 | +| `whereDate` / `whereYear`+`whereMonth` | `FiltersByDateRange` 의 범위 조건 (컬럼에 함수를 씌우면 인덱스를 못 쓴다) | +| 총 건수를 모르는데 `last_page` 를 1 로 채움 | `null` 로 내보내 화면이 마지막 페이지 점프만 감추게 한다 | +| 상한값을 저장소·화면에 리터럴로 재기입 | `PaginationLimits` 단일 해석 + 확장은 `core.pagination.filter_*` 필터 훅으로만 조정 | +| 결과 크기가 데이터 증가에 비례하는데 상한 없는 `->get()` / `->pluck()` | 목록은 페이지네이션, 순회는 `chunkById`/`lazyById`, 몇 건이면 `limit` — 운영자 등록 수에 묶인 설정성 테이블만 예외이며 그 근거를 코드에 남긴다 | +| 배지·요약 건수를 `int` 하나로 돌려주기 | `BoundedPaginator::count()` 의 `BoundedCount` — 잘린 값과 정확한 값이 구분되지 않으면 잘린 10,000 이 "정확히 10,000 건" 으로 화면에 나간다 | +| 여러 카테고리 건수를 합치며 정확도는 버리기 | 하나라도 부정확하면 합계도 부정확. 단, 특정 탭만 볼 때는 그 카테고리의 정확도만 본다 | +| 정렬 마지막이 비고유 컬럼 | 기본키를 덧붙여 전순서 보장 (동률 구간에서 행이 겹치거나 샌다) | +| 관련도순(`_ft_score`)에 커서 적용 | 계산값은 WHERE 절 경계로 쓸 수 없다 — offset 유지 (`KeysetPaginator::supports` 가 판정) | + +> 상세: [pagination.md](docs/backend/pagination.md) + ### 검색 인덱스 재생성(리인덱싱) | ❌ 금지 | ✅ 올바른 사용 | @@ -412,6 +435,24 @@ Icon 은 `` 글리프라 박스 크기가 곧 `font-size` 다. `w-N h-N` 은 > 상세: [search-system.md](docs/backend/search-system.md) +### 검색 질의는 활성 엔진이 만든다 + +검색 엔진은 `core.search.engine_drivers` 훅으로 교체 가능하다. 그런데 그 교체가 실제로 먹는 것은 **활성 엔진을 거치는 경로뿐**이다. 저장소가 구체 엔진 클래스를 지목하면 등록된 엔진은 호출될 기회 자체를 잃고, 오류도 경고도 없이 그 사이트의 검색만 조용히 다른 방식으로 동작한다. + +| ❌ 금지 | ✅ 올바른 사용 | +|--------|---------------| +| `DatabaseFulltextEngine::whereFulltext(...)` 등 구체 엔진 정적 호출 | `KeywordSearch::apply()` / `::applyAny()` (해석기가 활성 엔진에 위임) | +| `DB::getDriverName() === 'pgsql'` 처럼 드라이버명을 코드에 비교 | 선언형 `config('core.search.*')` + `core.search.like_operators` 필터 훅 | +| 매칭 ID 전량을 PHP 로 적재 후 `whereIn` (`search()->keys()->all()`) | 술어를 페이지 쿼리에 직접 부착 — ID 왕복도 목록 폭발도 없다 | +| 엔진에게 페이지 번호를 넘겨 한 페이지만 받기 | 페이지네이션은 DB 담당. 엔진은 **키 집합 상한**만 책임진다 (`KeywordSearchContext`) | +| 키 집합 상한을 총 건수 상한과 다른 값으로 두기 | 둘 다 `PaginationLimits::resultCap()` — 갈라지면 엔진이 돌려준 건수와 화면 총 건수의 근거가 달라진다 | +| 부분일치 폴백을 "전문검색 없을 때의 임시방편" 으로 취급 | 전문검색 미제공 DBMS 에서는 **그것이 정상 경로** — 와일드카드 escape + 대소문자 규칙을 갖춘다 | +| 확장이 `Model::search()` 를 쓰는 것을 금지로 오해 | Scout 경로는 그대로 유효하다. 새 계약은 대체가 아니라 **추가 통로** | + +정적 검사는 이 저장소 안만 볼 수 있다 — 외부 엔진이 상한을 지키는지는 강제할 수 없으므로, 코어는 **값을 손에 쥐어 주는 것**까지 하고 그 값이 도달하는지를 계약 테스트가 고정한다. + +> 상세: [search-system.md "키워드 술어는 활성 엔진이 만든다"](docs/backend/search-system.md) + ### Listener 데이터 접근 | 금지 | 올바른 사용 | diff --git a/CHANGELOG.md b/CHANGELOG.md index a39aac7f..ce5fa7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,19 @@ - 목록 화면이 뒤쪽 페이지로 갈수록 느려지는 문제를 구조적으로 해결할 수 있도록, 확장이 함께 쓸 수 있는 공통 조회 방식을 코어에 추가했습니다. 목록을 두 단계(먼저 이번 페이지에 해당하는 항목만 추려내고, 그 항목에 대해서만 본문·상세 정보를 읽기)로 나눠 읽으므로 게시글·주문·로그가 수십만 건으로 늘어나도 마지막 페이지 조회 비용이 첫 페이지와 비슷하게 유지됩니다. 목록 정렬 기준을 미리 정해 둔 항목으로만 해석하는 공통 처리도 함께 제공하므로, 확장이 목록 조회를 직접 만들 때 정렬 처리를 매번 새로 구현하지 않아도 됩니다. 주문 목록의 「발송일」처럼 정렬 기준이 다른 표에 있는 값일 때도 총 건수와 페이지 경계가 어긋나지 않게 조회하는 공통 처리를 함께 넣었습니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) +#### 대용량 목록 + +- 검색 결과나 목록이 아주 많을 때 화면이 열리지 않거나 서버가 멈추던 문제를 개선했습니다. 이제 총 건수는 일정 규모까지만 정확히 세고 그보다 많으면 "10,000건 이상" 처럼 표시하며, 다음 페이지로 넘기는 것은 끝까지 그대로 됩니다. 마지막 페이지로 바로 뛰는 버튼만 이때 감춰집니다 — 그 위치를 계산하려면 전체를 세야 하기 때문입니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) +- 총 건수를 세는 범위와 주소로 직접 요청할 수 있는 최대 페이지 번호를 관리자 > 환경설정 > 고급에서 조정할 수 있습니다. 두 값 모두 0 으로 두면 제한하지 않습니다. +- 검색 결과가 많아 총 건수를 정확히 세지 못한 경우, 검색어를 더 구체적으로 입력하면 정확한 건수를 볼 수 있다는 안내를 함께 표시합니다. +- 활동 로그·알림 발송 기록·본인인증 기록·스케줄 실행 이력·회원 목록에도 같은 방식을 적용했습니다. 기록이 수십만 건 쌓여도 목록 첫 화면이 총 건수를 세는 비용에 끌려가지 않습니다. +- 알림함도 같은 방식으로 조회합니다. 알림이 오래 쌓인 계정에서도 목록이 빠르게 열립니다. +- 알림 발송 기록을 최신순으로 볼 때 뒤쪽 페이지를 이동하는 방식이 활동 로그와 동일해졌습니다. 몇 페이지를 넘겨도 속도가 일정하게 유지됩니다. +- 검색 결과 화면에서 다른 분류의 건수 배지가 필요 없을 때는 그 숫자를 세는 작업을 건너뛸 수 있습니다. +- 검색 결과의 탭 배지(게시글 N · 상품 N · 페이지 N)도 세지 못한 건수를 정확한 것처럼 표시하지 않습니다. 탭을 열어 본 목록과 배지의 숫자가 서로 다른 기준을 말하던 문제가 사라집니다. +- 활동 로그 목록을 페이지 번호 대신 이어보기 방식으로도 조회할 수 있습니다. 기록이 많이 쌓인 사이트에서 뒤쪽으로 갈수록 느려지지 않습니다. 화면 동작은 그대로이며, 필요할 때만 쓰는 선택지입니다. +- 통합 검색도 최신순·조회순·가격순처럼 목록 자체의 순서로 볼 때는 이어보기 방식으로 뒤쪽 페이지를 이동할 수 있습니다. 검색 결과가 아주 많아도 몇 페이지를 넘기든 속도가 일정합니다. 관련도순은 계산된 점수로 정렬하므로 종전의 페이지 번호 방식을 유지합니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) + #### 성능 점검 - 확장이 자기 성능 측정 대상을 직접 등록할 수 있게 했습니다. 모듈·플러그인이 자신의 목록 화면, 관리자 화면, 저장 동작, 정기 작업 중 속도를 재고 싶은 것을 선언해 두면, 사이트 관리자가 성능 점검을 실행할 때 코어 항목과 함께 측정됩니다. 확장을 설치하면 그 확장의 측정 대상이 자동으로 목록에 나타나고, 제거하면 함께 사라집니다. 화면 측정은 응답 시간과 함께 그 화면이 실행한 데이터베이스 조회 횟수를 보여주므로, 목록 자체는 빠른데 화면이 느린 원인을 찾을 수 있습니다. @@ -78,6 +91,13 @@ - 알림 정의 목록이 지금 보고 있는 채널의 템플릿만 받아오도록 바꿨습니다. 예전에는 정의마다 모든 채널의 제목과 본문이 함께 실려, 채널을 여러 개 쓰는 사이트일수록 목록이 무거웠습니다. 채널을 지정하지 않고 목록을 조회하면 템플릿 본문은 함께 오지 않으며, 전체 채널의 내용이 필요하면 정의 단건 조회를 이용하면 됩니다. 화면에 보이는 내용과 '되돌리기' 버튼 표시 조건은 이전과 동일합니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) - 본인인증 메시지 정의 목록이 대표 템플릿 한 건만 받아오도록 바꿨습니다. 전체 템플릿은 정의를 열었을 때 종전대로 받아옵니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) - 역할 목록에 할당된 권한 개수를 함께 표시할 수 있도록 집계 값을 제공합니다. 권한 목록 자체를 싣지 않고도 규모를 확인할 수 있습니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) +- 페이지를 한 번 열 때마다 서버가 되풀이하던 준비 작업을 정리했습니다. 내부 동작 기록을 매 요청 400줄씩 남기던 부분, 환경설정 파일을 한 요청에서 스무 번 넘게 다시 읽던 부분, 이미 저장해 둔 목록을 매번 데이터베이스에서 다시 가져오던 부분을 없앴습니다. 화면에 보이는 내용은 그대로이며 응답이 빨라집니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) +- 권한 확인이 화면 요소마다 데이터베이스를 다시 조회하던 것을 요청당 한 번으로 줄였습니다. 메뉴·버튼이 많은 화면일수록 체감 차이가 큽니다. 권한을 바꾸면 종전처럼 다음 요청부터 즉시 반영됩니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) +- 게시글·상품·쿠폰 검색이 조건에 맞는 항목 전체를 먼저 메모리에 올린 뒤 다시 조회하던 방식을 없앴습니다. 검색 결과가 아주 많아도 메모리 사용량이 늘지 않습니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) +- 플러그인으로 다른 검색엔진을 연결했을 때 게시판·상품·페이지 검색이 그 엔진을 쓰지 않고 기본 방식으로 동작하던 문제를 해소했습니다. 이제 연결한 검색엔진이 모든 검색 화면에 적용됩니다. 전문검색을 제공하지 않는 데이터베이스에서도 검색어의 `%` `_` 같은 기호를 입력한 글자 그대로 찾도록 개선했습니다. +- 기간으로 걸러 보는 목록(주문·회원·쿠폰·스케줄 등)의 날짜 조건을 색인을 활용하는 방식으로 바꿨습니다. 기록이 많은 사이트에서 기간 검색이 빨라집니다. +- 설치 직후 기록 수준(`LOG_LEVEL`) 기본값을 운영 기준(`error`)으로 바꿨습니다. 이전 기본값은 정상 동작까지 모두 기록해 로그 파일이 빠르게 커졌습니다. 관리자 > 환경설정 > 고급에서 조정할 수 있으며, 그 값이 이 항목보다 우선합니다. +- 권장 서버 사양(CPU·메모리)을 시스템 요구사항 문서에 추가했습니다. - 활동 로그·알림 발송 이력·본인인증 기록·스케줄 실행 이력·회원 목록을 뒤쪽 페이지에서도 빠르게 열 수 있도록 조회 방식을 바꿨습니다. 예전에는 페이지가 뒤로 갈수록 건너뛰는 기록의 본문·변경 내역까지 함께 읽어 느려졌지만, 이제 현재 페이지에 해당하는 기록만 상세 정보를 읽습니다. 화면에 보이는 내용은 이전과 동일합니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) - 확장이 업로드 허용 형식을 넓힐 수 있도록 `core.attachment.allowed_extensions` 필터를 추가했습니다. 관리자 설정 목록을 기준으로, 확장이 자기 기능에 필요한 형식만 덧붙일 수 있습니다. @@ -122,6 +142,9 @@ #### 화면 표시·목록 상태 +- 건수가 아주 많아 총 건수를 끝까지 세지 못하는 목록에서 항목 번호가 0 이나 음수로 표시되던 문제를 수정했습니다. 번호를 지어내지 않고 「-」로 표시하며, 총 건수를 정확히 센 목록의 번호는 종전과 동일합니다. +- 활동 로그 목록의 총 건수가 끝까지 세지 못한 값인데도 정확한 숫자처럼 표시되던 문제를 수정했습니다. 이제 그 경우 「이상」 표시가 함께 나옵니다. + - 목록 표의 각 칸에 넣은 날짜·숫자 서식이 적용되지 않아 값이 비어 보이던 문제를 수정했습니다. 같은 서식을 일반 화면에서 쓰면 정상이었지만 목록 표의 칸 안에서는 값이 사라지거나(날짜 서식) 서식이 빠진 원래 값이 그대로 나왔습니다(숫자·대문자 서식). 목록 표와 카드 목록, 펼침 영역 등 반복해서 그려지는 모든 자리에서 서식이 정상 적용됩니다. (#87 @glitter-gim 님께서 제보해주셨습니다.) - 위 문제와 같은 뿌리에서 비롯된 화면 표시 오류를 함께 정리했습니다. 같은 방식으로 작성한 값이 어디에 놓이느냐(목록 칸·본문·표시 조건·버튼 동작)에 따라 다르게 해석되던 것을 한 가지 기준으로 통일했으며, 아래 항목이 그 결과입니다. 대부분 오류 메시지 없이 값이 비거나 잘못 보이던 증상이라 눈치채기 어려웠습니다. - 행마다 달라지는 조건(예: "활성 상태인 항목만 표시")을 목록에 걸면 해당하는 행만 걸러지지 않고 **목록 전체가 사라지던** 문제를 수정했습니다. 이제 행마다 조건을 따져 표시하며, 검색엔진 봇이 보는 화면에도 같은 규칙이 적용됩니다. diff --git a/app/Contracts/Pagination/BoundedTotalAware.php b/app/Contracts/Pagination/BoundedTotalAware.php new file mode 100644 index 00000000..72cf351f --- /dev/null +++ b/app/Contracts/Pagination/BoundedTotalAware.php @@ -0,0 +1,39 @@ + $ids 삭제할 활동 로그 ID 목록 + * @param array $ids 삭제할 활동 로그 ID 목록 * @return int 삭제된 건수 */ public function deleteMany(array $ids): int; diff --git a/app/Contracts/Repositories/NotificationLogRepositoryInterface.php b/app/Contracts/Repositories/NotificationLogRepositoryInterface.php index e53edf92..0673c73d 100644 --- a/app/Contracts/Repositories/NotificationLogRepositoryInterface.php +++ b/app/Contracts/Repositories/NotificationLogRepositoryInterface.php @@ -5,6 +5,7 @@ namespace App\Contracts\Repositories; use App\Models\NotificationLog; use App\Models\User; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; interface NotificationLogRepositoryInterface @@ -44,5 +45,5 @@ interface NotificationLogRepositoryInterface * * @param User|null $scopeUser 스코프 적용 대상 사용자 (null이면 스코프 미적용) */ - public function getPaginated(array $filters = [], int $perPage = 20, ?User $scopeUser = null): LengthAwarePaginator; + public function getPaginated(array $filters = [], int $perPage = 20, ?User $scopeUser = null): LengthAwarePaginator|CursorPaginator; } diff --git a/app/Enums/TotalRelation.php b/app/Enums/TotalRelation.php new file mode 100644 index 00000000..d7212976 --- /dev/null +++ b/app/Enums/TotalRelation.php @@ -0,0 +1,60 @@ + + */ + public static function values(): array + { + return array_column(self::cases(), 'value'); + } + + /** + * 유효한 값인지 확인합니다. + * + * @param string $value 검증할 값 + * @return bool 유효 여부 + */ + public static function isValid(string $value): bool + { + return in_array($value, self::values(), true); + } + + /** + * 총 건수가 정확한지 여부를 반환합니다. + * + * @return bool 정확하면 true + */ + public function isExact(): bool + { + return $this === self::Exact; + } + + /** + * 다국어 라벨을 반환합니다. + * + * @return string lang 키 해석 결과 + */ + public function label(): string + { + return __('pagination.total_relation.'.$this->value); + } +} diff --git a/app/Extension/HookListenerRegistrar.php b/app/Extension/HookListenerRegistrar.php index 685e3ef7..280498db 100644 --- a/app/Extension/HookListenerRegistrar.php +++ b/app/Extension/HookListenerRegistrar.php @@ -124,15 +124,10 @@ class HookListenerRegistrar self::addQueuedAction($hookName, $listenerClass, $method, $priority); } - Log::info('훅 리스너 등록 완료', [ - 'hook' => $hookName, - 'listener' => $listenerClass, - 'method' => $method, - 'priority' => $priority, - 'type' => $type, - 'sync' => $forceSync, - 'source' => $source, - ]); + // 등록 성공은 로그로 남기지 않는다. 리스너 112개 × 구독 400건이 요청마다 부팅되므로 + // 한 줄씩만 남겨도 매 요청 400줄이 쌓인다. 기본 설치가 production + LOG_LEVEL 조합상 + // 이 줄들을 그대로 기록하므로, 정상 동작을 알리는 데 드는 비용이 동작 자체보다 커진다. + // 등록 실패는 아래 catch 에서 계속 기록한다. } } diff --git a/app/Http/Controllers/Api/Base/AuthBaseController.php b/app/Http/Controllers/Api/Base/AuthBaseController.php index 52432b15..1a2abdc0 100644 --- a/app/Http/Controllers/Api/Base/AuthBaseController.php +++ b/app/Http/Controllers/Api/Base/AuthBaseController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api\Base; +use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; /** @@ -21,7 +22,7 @@ abstract class AuthBaseController extends BaseApiController /** * 사용자 소유권을 확인합니다. * - * @param int $userId 확인할 사용자 ID + * @param int $userId 확인할 사용자 ID * @return bool */ protected function isOwner(int $userId): bool @@ -32,7 +33,7 @@ abstract class AuthBaseController extends BaseApiController /** * 사용자가 특정 리소스에 접근할 수 있는지 확인합니다. * - * @param mixed $resource 접근하려는 리소스 + * @param mixed $resource 접근하려는 리소스 * @return bool */ protected function canAccessResource($resource): bool @@ -48,8 +49,8 @@ abstract class AuthBaseController extends BaseApiController /** * 사용자 활동을 기록합니다. * - * @param string $action 수행한 작업 - * @param array $data 관련 데이터 + * @param string $action 수행한 작업 + * @param array $data 관련 데이터 * @return void */ protected function logUserActivity(string $action, array $data = []): void @@ -58,20 +59,20 @@ abstract class AuthBaseController extends BaseApiController Log::info("User Activity: {$action}", [ 'user_id' => $this->getCurrentUser()?->uuid, 'data' => $data, - 'timestamp' => now() + 'timestamp' => now(), ]); } /** * 리소스 소유권을 확인하고, 소유자가 아니면 Forbidden 응답을 반환합니다. * - * @param mixed $resource 확인할 리소스 - * @param string $messageKey 오류 메시지 키 - * @return \Illuminate\Http\JsonResponse|null 소유자이면 null, 아니면 Forbidden 응답 + * @param mixed $resource 확인할 리소스 + * @param string $messageKey 오류 메시지 키 + * @return JsonResponse|null 소유자이면 null, 아니면 Forbidden 응답 */ protected function checkOwnership($resource, string $messageKey = 'common.forbidden') { - if (!$this->canAccessResource($resource)) { + if (! $this->canAccessResource($resource)) { return $this->forbidden($messageKey); } @@ -81,14 +82,21 @@ abstract class AuthBaseController extends BaseApiController /** * API 사용량을 기록합니다. * - * @param string $endpoint 엔드포인트 - * @param array $data 관련 데이터 + * @param string $endpoint 엔드포인트 + * @param array $data 관련 데이터 * @return void */ protected function logApiUsage(string $endpoint, array $data = []): void { // TODO: API 사용량 통계 시스템 구현 - Log::info("Auth API Usage: {$endpoint}", [ + // + // PublicBaseController 와 같은 이유로 디버그 모드에서만 기록한다. + // 통계 시스템이 서기 전까지 요청마다 로그 파일에 줄을 쌓지 않는다. + if (! config('app.debug')) { + return; + } + + Log::debug("Auth API Usage: {$endpoint}", [ 'user_id' => $this->getCurrentUser()?->uuid, 'ip' => request()->ip(), 'user_agent' => request()->userAgent(), diff --git a/app/Http/Controllers/Api/Base/PublicBaseController.php b/app/Http/Controllers/Api/Base/PublicBaseController.php index 84c1ccfd..cc108813 100644 --- a/app/Http/Controllers/Api/Base/PublicBaseController.php +++ b/app/Http/Controllers/Api/Base/PublicBaseController.php @@ -3,9 +3,7 @@ namespace App\Http\Controllers\Api\Base; use App\Contracts\Extension\CacheInterface; -use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; -use Symfony\Component\HttpFoundation\BinaryFileResponse; /** * 공개 API용 베이스 컨트롤러 @@ -27,9 +25,9 @@ abstract class PublicBaseController extends BaseApiController * 이는 확장 설치 직후·활성화 전 등의 일시적 상태에서 얻은 "not found" 같은 * 에러 응답이 영구 캐시되어 복구 후에도 잘못된 응답을 반환하는 문제를 방지한다. * - * @param string $key 캐시 키 - * @param callable $callback 데이터 생성 콜백 - * @param int $ttl 캐시 유지 시간 (초) + * @param string $key 캐시 키 + * @param callable $callback 데이터 생성 콜백 + * @param int $ttl 캐시 유지 시간 (초) * @return mixed */ protected function cached(string $key, callable $callback, int $ttl = 3600) @@ -54,18 +52,27 @@ abstract class PublicBaseController extends BaseApiController /** * API 사용량을 기록합니다. * - * @param string $endpoint 엔드포인트 - * @param array $data 관련 데이터 + * @param string $endpoint 엔드포인트 + * @param array $data 관련 데이터 * @return void */ protected function logApiUsage(string $endpoint, array $data = []): void { // TODO: API 사용량 통계 시스템 구현 - Log::info("Public API Usage: {$endpoint}", [ + // + // 호출 지점은 레이아웃·확장 번들·정적 자산처럼 페이지 로드마다 여러 번 열리는 + // 공개 엔드포인트다. 통계 시스템이 서기 전까지 이 자리가 요청마다 로그 파일에 + // 줄을 쌓지 않도록, 디버그 모드에서만 기록한다. 기본 설치는 APP_DEBUG=false 이므로 + // 아무것도 쓰지 않는다. + if (! config('app.debug')) { + return; + } + + Log::debug("Public API Usage: {$endpoint}", [ 'ip' => request()->ip(), 'user_agent' => request()->userAgent(), 'data' => $data, - 'timestamp' => now() + 'timestamp' => now(), ]); } @@ -80,8 +87,7 @@ abstract class PublicBaseController extends BaseApiController 'ip' => request()->ip(), 'user_agent' => request()->userAgent(), 'referer' => request()->header('referer'), - 'timestamp' => now() + 'timestamp' => now(), ]; } } - diff --git a/app/Http/Controllers/Api/Public/PublicSearchController.php b/app/Http/Controllers/Api/Public/PublicSearchController.php index 551187a3..5bcf8cce 100644 --- a/app/Http/Controllers/Api/Public/PublicSearchController.php +++ b/app/Http/Controllers/Api/Public/PublicSearchController.php @@ -2,9 +2,11 @@ namespace App\Http\Controllers\Api\Public; +use App\Enums\TotalRelation; use App\Extension\HookManager; use App\Http\Controllers\Api\Base\PublicBaseController; use App\Http\Requests\Public\SearchRequest; +use App\Support\Query\PaginationLimits; use Illuminate\Http\JsonResponse; /** @@ -18,7 +20,7 @@ class PublicSearchController extends PublicBaseController /** * 통합 검색 수행 * - * @param SearchRequest $request HTTP 요청 + * @param SearchRequest $request HTTP 요청 * @return JsonResponse 검색 결과 */ public function search(SearchRequest $request): JsonResponse @@ -36,6 +38,14 @@ class PublicSearchController extends PublicBaseController return $this->success(__('search.empty_keyword'), $this->buildEmptyResponse()); } + // 특정 탭을 보는 중이라면, 다른 탭의 배지 건수를 아예 세지 않을 수 있다. + // 기본값은 세는 쪽이다 — 탭 배지가 화면 요소라 끄면 숫자가 사라진다. 다만 배지가 + // 필요 없는 화면(더 보기 페이지 등)은 with_counts=false 로 그 비용을 없앤다. + // 전체 탭은 배지가 화면의 주 정보라 이 값과 무관하게 항상 센다. + $includeInactiveCounts = $type === 'all' + ? true + : $request->boolean('with_counts', true); + // 검색 컨텍스트 구성 (모듈별 파라미터는 request 전체를 전달하여 각 모듈이 직접 처리) $context = [ 'q' => $q, @@ -44,6 +54,10 @@ class PublicSearchController extends PublicBaseController 'page' => $page, 'per_page' => $perPage, 'all_tab_limit' => 5, + // 커서는 특정 탭을 볼 때만 의미가 있다. 전체 탭은 카테고리마다 몇 건씩만 + // 보여 주므로 깊은 페이지 자체가 없고, 카테고리별 커서를 하나로 합칠 수도 없다. + 'cursor' => $type === 'all' ? null : ($validated['cursor'] ?? null), + 'include_inactive_counts' => $includeInactiveCounts, 'user' => $request->user(), 'request' => $request, ]; @@ -56,8 +70,14 @@ class PublicSearchController extends PublicBaseController // 프론트엔드용 응답 구조로 변환 (훅을 통해 모듈이 처리) $response = $this->buildResponse($q, $results, $context); + // 총 건수가 상한에 걸렸으면 "N건" 이 아니라 "N건 이상" 으로 알린다. + // 세지 않은 값을 정확한 것처럼 말하지 않는다. + $messageKey = ($response['total_is_exact'] ?? true) + ? 'search.results_found' + : 'search.results_found_at_least'; + return $this->success( - __('search.results_found', ['count' => number_format($response['total'])]), + __($messageKey, ['count' => number_format($response['total'])]), $response ); } @@ -72,6 +92,9 @@ class PublicSearchController extends PublicBaseController return [ 'q' => '', 'total' => 0, + 'total_relation' => TotalRelation::Exact->value, + 'total_is_exact' => true, + 'result_cap' => null, ]; } @@ -81,9 +104,9 @@ class PublicSearchController extends PublicBaseController * 코어는 기본 응답 구조만 생성하고, * 각 모듈이 core.search.build_response 훅을 통해 자신의 카테고리 응답을 구성합니다. * - * @param string $q 검색어 - * @param array $results Hook에서 반환된 검색 결과 - * @param array $context 검색 컨텍스트 + * @param string $q 검색어 + * @param array $results Hook에서 반환된 검색 결과 + * @param array $context 검색 컨텍스트 * @return array 프론트엔드용 응답 구조 */ private function buildResponse(string $q, array $results, array $context): array @@ -97,8 +120,15 @@ class PublicSearchController extends PublicBaseController // 모듈은 $results에서 자신의 데이터를 가져와 $response에 추가 $response = HookManager::applyFilters('core.search.build_response', $response, $results, $context); - // 전체 합계를 항상 보존 (탭 UI에서 전체 건수 표시용) + // 전체 합계를 항상 보존 (탭 UI에서 전체 건수 표시용). + // "전체" 탭 배지도 잘린 합계인지 알아야 하므로 정확도를 함께 붙인다. $response['all_count'] = $response['total']; + $response['all_count_is_exact'] = $this->resolveTotalAccuracy($results)['total_is_exact']; + + // 탭 배지는 카테고리마다 하나씩 그려지므로 정확도도 카테고리마다 필요하다. + // 모듈이 각자 자기 키를 내보내면 어떤 배지는 정확도를 받고 어떤 배지는 못 받아, + // 상한에 걸린 숫자가 그 배지에서만 정확한 값처럼 보인다. 코어가 일괄로 붙인다. + $response['counts_are_exact'] = $this->resolveCategoryAccuracy($results); // 특정 탭 조회 시 total을 해당 탭의 count로 설정 $type = $context['type'] ?? 'all'; @@ -109,13 +139,14 @@ class PublicSearchController extends PublicBaseController } } - return $response; + // 정확도 메타 — 전체 탭은 카테고리 합집합, 특정 탭은 그 카테고리 하나만 본다. + return array_merge($response, $this->resolveTotalAccuracy($results, $type)); } /** * 전체 검색 결과 수 계산 * - * @param array $results Hook에서 반환된 검색 결과 + * @param array $results Hook에서 반환된 검색 결과 * @return int 전체 결과 수 */ private function calculateTotal(array $results): int @@ -127,4 +158,60 @@ class PublicSearchController extends PublicBaseController return $total; } + + /** + * 카테고리별 총 건수 정확도를 모읍니다. + * + * 탭 배지는 카테고리 수만큼 그려지므로 정확도도 그 수만큼 있어야 합니다. 하나라도 + * 빠지면 그 배지에서만 상한에 걸린 숫자가 정확한 값처럼 보이고, 오류로는 드러나지 + * 않은 채 그냥 틀린 숫자로 남습니다. + * + * @param array $results Hook에서 반환된 검색 결과 + * @return array 카테고리 => 정확 여부 + */ + private function resolveCategoryAccuracy(array $results): array + { + $accuracy = []; + + foreach ($results as $category => $categoryData) { + $accuracy[$category] = ($categoryData['total_is_exact'] ?? true) === true; + } + + return $accuracy; + } + + /** + * 응답 전체의 총 건수 정확도를 정합니다. + * + * 전체 탭에서는 카테고리 중 하나라도 상한에 걸리면 합계도 "이상" 이다 — 정확한 + * 카테고리 몇 개를 더해 봐야 전체가 정확해지지 않는다. + * + * 특정 탭을 보는 중이면 `total` 이 그 카테고리 건수로 바뀌므로 정확도도 그 카테고리 + * 하나만 본다. 다른 카테고리가 잘렸다는 이유로 정확한 값을 "이상" 이라 말하지 않는다. + * + * @param array $results Hook에서 반환된 검색 결과 + * @param string $type 조회 탭 (all 이면 전체 합계) + * @return array{total_relation: string, total_is_exact: bool, result_cap: int|null} 정확도 메타 + */ + private function resolveTotalAccuracy(array $results, string $type = 'all'): array + { + $scoped = ($type !== 'all' && array_key_exists($type, $results)) + ? [$results[$type]] + : $results; + + $isExact = true; + + foreach ($scoped as $categoryData) { + if (($categoryData['total_is_exact'] ?? true) === false) { + $isExact = false; + break; + } + } + + return [ + 'total_relation' => $isExact ? TotalRelation::Exact->value : TotalRelation::AtLeast->value, + 'total_is_exact' => $isExact, + 'result_cap' => PaginationLimits::resultCap('search'), + ]; + } } diff --git a/app/Http/Middleware/PermissionMiddleware.php b/app/Http/Middleware/PermissionMiddleware.php index 32a75e38..2c31a3d5 100644 --- a/app/Http/Middleware/PermissionMiddleware.php +++ b/app/Http/Middleware/PermissionMiddleware.php @@ -6,6 +6,7 @@ use App\Enums\PermissionType; use App\Helpers\PermissionHelper; use App\Helpers\ResponseHelper; use App\Models\Role; +use App\Support\GuestRoleResolver; use Closure; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; @@ -14,13 +15,6 @@ use Symfony\Component\HttpFoundation\Response; class PermissionMiddleware { - /** - * guest role 캐시 - * - * @var Role|null - */ - protected static $guestRoleCache = null; - /** * 특정 권한을 가진 사용자 또는 비회원(guest)의 접근을 허용합니다. * @@ -125,10 +119,9 @@ class PermissionMiddleware return false; } - return $guestRole->permissions() - ->where('identifier', $permission) - ->where('type', $type) - ->exists(); + // 적재해 둔 권한 컬렉션에서 판정한다. permissions() 로 빌더를 다시 열면 + // 캐시해 둔 의미가 사라져 판정마다 쿼리가 나간다. + return GuestRoleResolver::hasPermission($permission, $type); } /** @@ -138,13 +131,7 @@ class PermissionMiddleware */ protected function getGuestRole(): ?Role { - if (self::$guestRoleCache === null) { - self::$guestRoleCache = Role::where('identifier', 'guest') - ->with('permissions') - ->first(); - } - - return self::$guestRoleCache; + return GuestRoleResolver::resolve(); } /** @@ -158,7 +145,7 @@ class PermissionMiddleware */ public static function clearGuestRoleCache(): void { - self::$guestRoleCache = null; + GuestRoleResolver::flush(); } /** diff --git a/app/Http/Requests/ActivityLog/ActivityLogIndexRequest.php b/app/Http/Requests/ActivityLog/ActivityLogIndexRequest.php index 8b5e82a3..0b52df4d 100644 --- a/app/Http/Requests/ActivityLog/ActivityLogIndexRequest.php +++ b/app/Http/Requests/ActivityLog/ActivityLogIndexRequest.php @@ -47,6 +47,10 @@ class ActivityLogIndexRequest extends FormRequest // 조용히 기본 정렬로 되돌리지 않도록 한다 (service-repository.md "정렬 컬럼 화이트리스트"). 'sort_by' => ['nullable', 'string', Rule::in(['created_at', 'action', 'log_type'])], 'sort_order' => ['nullable', 'string', Rule::in(['asc', 'desc'])], + // 커서를 주면 목록이 키셋 방식으로 응답한다. 로그는 계속 쌓이기만 하므로 깊은 + // 페이지를 OFFSET 으로 훑으면 건너뛸 행을 실제로 읽어야 한다. + // 형식이 깨진 값은 KeysetPaginator 가 첫 페이지로 되돌리므로 여기서는 길이만 본다. + 'cursor' => ['nullable', 'string', 'max:500'], ]; return HookManager::applyFilters('core.activity_log.index_validation_rules', $rules, $this); diff --git a/app/Http/Requests/NotificationLog/NotificationLogIndexRequest.php b/app/Http/Requests/NotificationLog/NotificationLogIndexRequest.php index f322ca67..ac691f3d 100644 --- a/app/Http/Requests/NotificationLog/NotificationLogIndexRequest.php +++ b/app/Http/Requests/NotificationLog/NotificationLogIndexRequest.php @@ -40,6 +40,10 @@ class NotificationLogIndexRequest extends FormRequest // 게이트가 더 좁으면 화면 정렬 셀렉트가 제공하는 옵션(수신자명순/제목순)이 422 로 막힌다. 'sort_by' => ['nullable', 'string', 'in:id,channel,notification_type,status,sent_at,created_at,recipient_name,subject'], 'sort_order' => ['nullable', 'string', 'in:asc,desc'], + // 커서를 주면 목록이 키셋 방식으로 응답한다. 로그는 계속 쌓이기만 하므로 깊은 + // 페이지를 OFFSET 으로 훑으면 건너뛸 행을 실제로 읽어야 한다. + // 형식이 깨진 값은 KeysetPaginator 가 첫 페이지로 되돌리므로 여기서는 길이만 본다. + 'cursor' => ['nullable', 'string', 'max:500'], ]; return HookManager::applyFilters( diff --git a/app/Http/Requests/Public/SearchRequest.php b/app/Http/Requests/Public/SearchRequest.php index 00fc8f22..54582355 100644 --- a/app/Http/Requests/Public/SearchRequest.php +++ b/app/Http/Requests/Public/SearchRequest.php @@ -3,6 +3,8 @@ namespace App\Http\Requests\Public; use App\Extension\HookManager; +use App\Support\Query\PaginationLimits; +use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Foundation\Http\FormRequest; /** @@ -23,10 +25,30 @@ class SearchRequest extends FormRequest return true; } + /** + * 검증 전에 쿼리 파라미터를 정규화합니다. + * + * 쿼리스트링은 언제나 문자열로 도착하므로 `with_counts=false` 가 `boolean` 규칙에 + * 걸려 422 가 된다. 해석 가능한 값만 캐스팅하고, 그 외(오타 등)는 손대지 않아 + * 규칙이 그대로 걸러내게 둔다 — null 로 바꾸면 오타가 "미지정" 으로 통과한다. + */ + protected function prepareForValidation(): void + { + if (! $this->has('with_counts')) { + return; + } + + $normalized = filter_var($this->input('with_counts'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + + if ($normalized !== null) { + $this->merge(['with_counts' => $normalized]); + } + } + /** * Get the validation rules that apply to the request. * - * @return array|string> + * @return array|string> */ public function rules(): array { @@ -35,8 +57,21 @@ class SearchRequest extends FormRequest 'q' => ['nullable', 'string', 'min:2', 'max:200'], 'type' => ['nullable', 'string'], 'sort' => ['nullable', 'string'], - 'page' => ['nullable', 'integer', 'min:1'], + // 페이지 상한은 남용 차단용이다. 정상 탐색은 has_more_pages 로 계속 열려 있고, + // 임의의 큰 페이지 번호를 직접 던져 초대형 OFFSET 을 만드는 것만 막는다. + 'page' => array_filter([ + 'nullable', + 'integer', + 'min:1', + ($maxPage = PaginationLimits::maxPage('search')) !== null ? 'max:'.$maxPage : null, + ]), 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + // 깊은 페이지를 OFFSET 없이 넘기기 위한 커서. 형식이 깨진 값은 + // KeysetPaginator 가 첫 페이지로 되돌리므로 여기서는 길이만 본다. + 'cursor' => ['nullable', 'string', 'max:500'], + // 특정 탭만 볼 때 다른 탭의 배지 건수를 세지 않게 하는 스위치. + // 배지를 그리지 않는 화면에서 그 집계 비용을 없앤다 (기본값은 세는 쪽). + 'with_counts' => ['nullable', 'boolean'], ]; // 모듈/플러그인이 validation rules를 동적으로 추가할 수 있도록 훅 제공 @@ -59,6 +94,7 @@ class SearchRequest extends FormRequest 'q.max' => __('search.validation.q_max'), 'page.integer' => __('search.validation.page_integer'), 'page.min' => __('search.validation.page_min'), + 'page.max' => __('search.validation.page_max'), 'per_page.integer' => __('search.validation.per_page_integer'), 'per_page.min' => __('search.validation.per_page_min'), 'per_page.max' => __('search.validation.per_page_max'), diff --git a/app/Http/Requests/Settings/SaveSettingsRequest.php b/app/Http/Requests/Settings/SaveSettingsRequest.php index b176c9c5..ae6fdbc0 100644 --- a/app/Http/Requests/Settings/SaveSettingsRequest.php +++ b/app/Http/Requests/Settings/SaveSettingsRequest.php @@ -308,6 +308,10 @@ class SaveSettingsRequest extends FormRequest 'advanced.geoip_license_key' => ['nullable', 'string', 'max:200', 'regex:/^[A-Za-z0-9_]+$/'], 'advanced.geoip_auto_update_enabled' => ['nullable', 'boolean'], + // 목록 한계값 (advanced 탭) — 0 은 무제한 + 'advanced.pagination_result_cap' => ['nullable', 'integer', 'min:'.config('core.settings_limits.advanced_pagination_result_cap_min', 0), 'max:'.config('core.settings_limits.advanced_pagination_result_cap_max', 1000000)], + 'advanced.pagination_max_page' => ['nullable', 'integer', 'min:'.config('core.settings_limits.advanced_pagination_max_page_min', 0), 'max:'.config('core.settings_limits.advanced_pagination_max_page_max', 100000)], + // 드라이버 설정 (drivers 탭) 'drivers.storage_driver' => $this->getTabRules($tab, 'drivers', [Rule::in(self::SUPPORTED_STORAGE_DRIVERS)]), 'drivers.s3_bucket' => ['nullable', 'string', 'max:255'], @@ -713,6 +717,14 @@ class SaveSettingsRequest extends FormRequest 'advanced.sql_query_log.required' => __('validation.settings.sql_query_log_required'), 'advanced.sql_query_log.boolean' => __('validation.settings.sql_query_log_boolean'), + // 목록 한계값 + 'advanced.pagination_result_cap.integer' => __('validation.settings.pagination_result_cap_integer'), + 'advanced.pagination_result_cap.min' => __('validation.settings.pagination_result_cap_min'), + 'advanced.pagination_result_cap.max' => __('validation.settings.pagination_result_cap_max'), + 'advanced.pagination_max_page.integer' => __('validation.settings.pagination_max_page_integer'), + 'advanced.pagination_max_page.min' => __('validation.settings.pagination_max_page_min'), + 'advanced.pagination_max_page.max' => __('validation.settings.pagination_max_page_max'), + // 코어 업데이트 설정 'advanced.core_update_github_url.url' => __('validation.settings.core_update_github_url_invalid'), 'advanced.core_update_github_url.max' => __('validation.settings.core_update_github_url_max'), diff --git a/app/Http/Resources/ActivityLogCollection.php b/app/Http/Resources/ActivityLogCollection.php index d17d5014..79930efa 100644 --- a/app/Http/Resources/ActivityLogCollection.php +++ b/app/Http/Resources/ActivityLogCollection.php @@ -3,7 +3,6 @@ namespace App\Http\Resources; use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; /** * 활동 로그 컬렉션 리소스 @@ -36,15 +35,10 @@ class ActivityLogCollection extends BaseApiCollection 'data' => $this->mapWithRowNumber(function ($activityLog) { return (new ActivityLogResource($activityLog))->toArray(request()); }), - 'pagination' => $this->resource instanceof LengthAwarePaginator ? [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ] : null, + // 메타를 손으로 조립하면 정확도 필드(total_relation·total_is_exact·result_cap)가 + // 빠진다. 그러면 상한에 걸려 잘린 건수가 화면에서 정확한 값처럼 읽힌다 + // (실측: 실제 88,792 건이 "10000건" 으로 표기). 표준 메타 한 곳만 쓴다. + ...$this->paginationMeta(), ...($abilities ? ['abilities' => $abilities] : []), ]; } diff --git a/app/Http/Resources/Admin/Identity/IdentityMessageDefinitionCollection.php b/app/Http/Resources/Admin/Identity/IdentityMessageDefinitionCollection.php index 55e43ca4..4afb8622 100644 --- a/app/Http/Resources/Admin/Identity/IdentityMessageDefinitionCollection.php +++ b/app/Http/Resources/Admin/Identity/IdentityMessageDefinitionCollection.php @@ -36,15 +36,8 @@ class IdentityMessageDefinitionCollection extends BaseApiCollection 'data' => $this->mapWithRowNumber(function ($definition) use ($request) { return (new IdentityMessageDefinitionResource($definition))->toArray($request); }, $sortOrder), - 'pagination' => [ - 'current_page' => $this->currentPage(), - 'last_page' => $this->lastPage(), - 'per_page' => $this->perPage(), - 'total' => $this->total(), - 'from' => $this->firstItem(), - 'to' => $this->lastItem(), - 'has_more_pages' => $this->hasMorePages(), - ], + // 표준 메타를 쓴다 — 페이지 결과의 형태를 스스로 판정해 그 형태가 아는 값만 낸다. + ...$this->paginationMeta(), ...($abilities ? ['abilities' => $abilities] : []), ]; } diff --git a/app/Http/Resources/BaseApiCollection.php b/app/Http/Resources/BaseApiCollection.php index 132a15b2..2d3bab97 100644 --- a/app/Http/Resources/BaseApiCollection.php +++ b/app/Http/Resources/BaseApiCollection.php @@ -2,10 +2,14 @@ namespace App\Http\Resources; +use App\Contracts\Pagination\BoundedTotalAware; use App\Helpers\PermissionHelper; use App\Http\Resources\Traits\HasRowNumber; +use App\Support\Query\BoundedPage; +use Illuminate\Contracts\Pagination\LengthAwarePaginator as LengthAwarePaginatorContract; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; +use Illuminate\Pagination\CursorPaginator; /** * API 컬렉션 리소스 기본 클래스 @@ -61,24 +65,71 @@ abstract class BaseApiCollection extends ResourceCollection * 정확히 판정할 수 있도록 노출합니다. 전체 조회(get) 등 paginator 가 아닌 * 경우에는 빈 배열을 반환하므로 toArray 에서 array_merge 로 안전하게 합칠 수 있습니다. * + * 페이지 결과는 세 형태 중 하나이며, 형태를 스스로 판정해 그 형태가 실제로 아는 + * 값만 내보냅니다. 모르는 값을 0 이나 1 로 채우면 화면이 그것을 사실로 읽습니다. + * + * | 형태 | 예 | total | last_page | 추가 | + * |---|---|---|---|---| + * | 전체건수형 | `paginate()` | 정확 | 정확 | — | + * | 상한형 | {@see BoundedPage} | 정확 또는 하한 | 하한이면 null | `total_relation` `total_is_exact` `result_cap` | + * | 단순형 | `simplePaginate()` / `cursorPaginate()` | 없음 | 없음 | 커서면 `next_cursor` `prev_cursor` | + * + * 기존 필드는 하나도 제거하지 않으므로, 표준 `paginate()` 를 쓰는 기존 컬렉션의 + * 응답은 이전과 완전히 동일합니다. + * * @return array ['pagination' => [...]] 또는 빈 배열 */ protected function paginationMeta(): array { - if (! method_exists($this->resource, 'currentPage')) { + $resource = $this->resource; + + if ($resource instanceof CursorPaginator) { + return ['pagination' => $this->cursorPaginationMeta($resource)]; + } + + if (! method_exists($resource, 'currentPage')) { return []; } + $meta = [ + 'current_page' => $resource->currentPage(), + 'per_page' => $resource->perPage(), + 'from' => $resource->firstItem(), + 'to' => $resource->lastItem(), + 'has_more_pages' => $resource->hasMorePages(), + ]; + + // 단순형(simplePaginate)은 총 건수를 아예 세지 않으므로 total/last_page 를 내보내지 않는다. + if ($resource instanceof LengthAwarePaginatorContract) { + $meta['last_page'] = $resource->lastPage(); + $meta['total'] = $resource->total(); + } + + if ($resource instanceof BoundedTotalAware) { + $meta['total_relation'] = $resource->totalRelation()->value; + $meta['total_is_exact'] = $resource->totalRelation()->isExact(); + $meta['result_cap'] = $resource->resultCap(); + } + + return ['pagination' => $meta]; + } + + /** + * 커서 페이지의 pagination 메타를 만듭니다. + * + * 커서 방식은 총 건수와 페이지 번호를 계산하지 않습니다. 대신 앞뒤 이동 커서를 + * 그대로 실어 보내며, 화면은 이 값으로 이전/다음 버튼을 만듭니다. + * + * @param CursorPaginator $resource 커서 페이지 결과 + * @return array pagination 메타 + */ + private function cursorPaginationMeta(CursorPaginator $resource): array + { return [ - 'pagination' => [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ], + 'per_page' => $resource->perPage(), + 'has_more_pages' => $resource->hasMorePages(), + 'next_cursor' => $resource->nextCursor()?->encode(), + 'prev_cursor' => $resource->previousCursor()?->encode(), ]; } } diff --git a/app/Http/Resources/NotificationDefinitionCollection.php b/app/Http/Resources/NotificationDefinitionCollection.php index 95868d79..aa65961f 100644 --- a/app/Http/Resources/NotificationDefinitionCollection.php +++ b/app/Http/Resources/NotificationDefinitionCollection.php @@ -19,7 +19,7 @@ class NotificationDefinitionCollection extends BaseApiCollection /** * 컬렉션을 배열로 변환합니다. * - * @param Request $request + * @param Request $request * @return array */ public function toArray(Request $request): array @@ -31,15 +31,8 @@ class NotificationDefinitionCollection extends BaseApiCollection 'data' => $this->mapWithRowNumber(function ($definition) use ($request) { return (new NotificationDefinitionResource($definition))->toArray($request); }, $sortOrder), - 'pagination' => [ - 'current_page' => $this->currentPage(), - 'last_page' => $this->lastPage(), - 'per_page' => $this->perPage(), - 'total' => $this->total(), - 'from' => $this->firstItem(), - 'to' => $this->lastItem(), - 'has_more_pages' => $this->hasMorePages(), - ], + // 표준 메타를 쓴다 — 페이지 결과의 형태를 스스로 판정해 그 형태가 아는 값만 낸다. + ...$this->paginationMeta(), ...($abilities ? ['abilities' => $abilities] : []), ]; } diff --git a/app/Http/Resources/NotificationLogCollection.php b/app/Http/Resources/NotificationLogCollection.php index c0047cc3..651fed46 100644 --- a/app/Http/Resources/NotificationLogCollection.php +++ b/app/Http/Resources/NotificationLogCollection.php @@ -19,7 +19,7 @@ class NotificationLogCollection extends BaseApiCollection /** * 컬렉션을 배열로 변환합니다. * - * @param Request $request + * @param Request $request * @return array */ public function toArray(Request $request): array @@ -31,15 +31,10 @@ class NotificationLogCollection extends BaseApiCollection 'data' => $this->mapWithRowNumber(function ($log) use ($request) { return (new NotificationLogResource($log))->toArray($request); }, $sortOrder), - 'pagination' => [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ], + // 표준 메타를 쓴다. 이 목록은 커서 요청이면 CursorPaginator 로, 그 외에는 상한형 + // 페이지로 온다. 손으로 조립하면 커서 결과에 없는 total()/lastPage() 를 불러 + // 그 요청만 500 이 되고, 상한형에서는 정확도 메타가 빠진다. + ...$this->paginationMeta(), ...($abilities ? ['abilities' => $abilities] : []), ]; } diff --git a/app/Http/Resources/Traits/HasRowNumber.php b/app/Http/Resources/Traits/HasRowNumber.php index e6ad272b..fd9fd304 100644 --- a/app/Http/Resources/Traits/HasRowNumber.php +++ b/app/Http/Resources/Traits/HasRowNumber.php @@ -2,6 +2,7 @@ namespace App\Http\Resources\Traits; +use App\Contracts\Pagination\BoundedTotalAware; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -57,7 +58,7 @@ trait HasRowNumber * @param int $total 전체 항목 수 * @param int $perPage 페이지당 항목 수 * @param int $currentPage 현재 페이지 번호 - * @return int 계산된 순번 + * @return int|null 계산된 순번 (총 건수가 잘린 내림차순이면 null) */ protected function calculateRowNumber( int $index, @@ -65,13 +66,22 @@ trait HasRowNumber int $total, int $perPage, int $currentPage - ): int { + ): ?int { if ($sortOrder === 'asc') { // 오름차순: 1, 2, 3, ... (페이지별 연속) // 예: 2페이지 → ((2-1) * 10) + 0 + 1 = 11, 12, 13, ... + // offset 만으로 정해지므로 총 건수가 잘려도 그대로 정확하다. return (($currentPage - 1) * $perPage) + $index + 1; } + // 내림차순 순번은 "전체 몇 건 중 몇 번째" 라 총 건수를 알아야 나온다. 총 건수가 + // 상한에 걸려 잘렸으면 그 값으로 역산한 순번은 첫 페이지부터 이미 틀리고, 상한을 + // 넘어선 페이지에서는 0 과 음수까지 내려간다. 틀린 숫자를 내보내는 것보다 + // 내보내지 않는 편이 낫으므로 `last_page` 와 같은 원칙으로 null 을 돌려준다. + if ($this->resource instanceof BoundedTotalAware && ! $this->resource->totalRelation()->isExact()) { + return null; + } + // 내림차순: total, total-1, ... (역순) // 예: 총 30개, 2페이지 → 30 - ((2-1) * 10) - 0 = 20, 19, 18, ... return $total - (($currentPage - 1) * $perPage) - $index; diff --git a/app/Http/Resources/UserNotificationCollection.php b/app/Http/Resources/UserNotificationCollection.php index ecd1da1c..6da48dcd 100644 --- a/app/Http/Resources/UserNotificationCollection.php +++ b/app/Http/Resources/UserNotificationCollection.php @@ -5,14 +5,16 @@ namespace App\Http\Resources; use App\Contracts\Repositories\NotificationDefinitionRepositoryInterface; use Illuminate\Http\Request; +/** + * 알림함 컬렉션 + */ class UserNotificationCollection extends BaseApiCollection { - /** * 컬렉션을 배열로 변환합니다. * - * @param Request $request - * @return array + * @param Request $request HTTP 요청 + * @return array 변환된 배열 */ public function toArray(Request $request): array { @@ -29,15 +31,10 @@ class UserNotificationCollection extends BaseApiCollection ->withTypeLabelMap($typeLabelMap) ->toArray($request); }, $sortOrder), - 'pagination' => [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ], + // 표준 메타를 쓴다. 손으로 조립하면 페이지 결과의 형태(전체건수형/상한형/단순형)를 + // 구분하지 못해, 상한에 걸려 잘린 총 건수가 정확한 값처럼 나가고 마지막 페이지도 + // 계산할 수 없는데 숫자가 채워진다. + ...$this->paginationMeta(), ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index 7a7dfd2b..6e365a0e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -51,6 +51,22 @@ class User extends Authenticatable implements HasLocalePreference */ protected array $effectiveScopeCache = []; + /** + * 보유 권한 부여 내역 캐시 (인스턴스 레벨) + * + * 한 요청에서 권한 판정은 수십~수백 번 일어난다 — 레이아웃 노드마다, 목록 행마다, + * 리소스 필드마다 부른다. 판정마다 DB 에 물으면 노드/행 수에 비례해 쿼리가 늘어난다. + * 첫 판정에서 `roles.permissions` 를 한 번 적재해 두고 이후로는 배열만 본다. + * + * 캐시 수명은 **모델 인스턴스**, 즉 요청 스코프다. 크로스 요청 캐시를 두지 않으므로 + * 권한을 바꾸면 다음 요청부터 즉시 반영된다. + * + * 구조: 권한 식별자 ⇒ [{type, scope_type}, ...] (같은 권한을 여러 역할이 주면 여러 건) + * + * @var array>|null + */ + protected ?array $permissionGrantsCache = null; + /** * 테이블명 * @@ -244,14 +260,79 @@ class User extends Authenticatable implements HasLocalePreference */ public function hasPermission(string $permission, ?PermissionType $type = null): bool { - return $this->roles() - ->whereHas('permissions', function ($query) use ($permission, $type) { - $query->where('identifier', $permission); - if ($type !== null) { - $query->where('type', $type); - } - }) - ->exists(); + $grants = $this->permissionGrants()[$permission] ?? null; + + if ($grants === null) { + return false; + } + + if ($type === null) { + return true; + } + + foreach ($grants as $grant) { + if ($grant['type'] === $type->value) { + return true; + } + } + + return false; + } + + /** + * 보유 권한 부여 내역을 반환합니다 (인스턴스 캐시). + * + * 역할 → 권한을 한 번만 적재하고, 이후 판정은 전부 이 배열에서 이루어집니다. + * + * @return array> 권한 식별자 ⇒ 부여 내역 + */ + protected function permissionGrants(): array + { + if ($this->permissionGrantsCache !== null) { + return $this->permissionGrantsCache; + } + + $grants = []; + + foreach ($this->roles()->with('permissions')->get() as $role) { + foreach ($role->permissions as $permission) { + $grants[$permission->identifier][] = [ + 'type' => $this->enumValue($permission->type), + 'scope_type' => $this->enumValue($permission->pivot->scope_type ?? null), + ]; + } + } + + return $this->permissionGrantsCache = $grants; + } + + /** + * Enum 또는 원시 값을 문자열로 정규화합니다. + * + * 캐스팅 설정 유무에 따라 Enum 인스턴스와 문자열이 섞여 들어오므로 한 형태로 맞춥니다. + * + * @param mixed $value Enum 인스턴스 · 문자열 · null + * @return string|null 정규화된 문자열 (부재 시 null) + */ + private function enumValue(mixed $value): ?string + { + if ($value instanceof \BackedEnum) { + return (string) $value->value; + } + + return $value === null ? null : (string) $value; + } + + /** + * 권한 판정 캐시를 비웁니다. + * + * 같은 인스턴스에서 역할을 바꾼 직후 다시 판정해야 하는 경우에 호출합니다. + * (역할 동기화 서비스가 호출하며, 일반 조회 경로에서는 필요하지 않습니다) + */ + public function flushPermissionCaches(): void + { + $this->permissionGrantsCache = null; + $this->effectiveScopeCache = []; } /** @@ -263,27 +344,17 @@ class User extends Authenticatable implements HasLocalePreference */ public function hasPermissions(array $permissions, bool $requireAll = true, ?PermissionType $type = null): bool { - $userPermissions = $this->roles() - ->whereHas('permissions', function ($query) use ($permissions, $type) { - $query->whereIn('identifier', $permissions); - if ($type !== null) { - $query->where('type', $type); - } - }) - ->with(['permissions' => function ($query) use ($permissions, $type) { - $query->whereIn('identifier', $permissions); - if ($type !== null) { - $query->where('type', $type); - } - }]) - ->get() - ->pluck('permissions') - ->flatten() - ->pluck('identifier') - ->unique() - ->count(); + // 같은 권한 집합을 hasPermission 과 공유한다 — 권한 개수만큼 쿼리가 늘지 않는다. + // 비교 기준(고유 매칭 수 vs 인자 개수)은 종전과 동일하게 유지한다. + $matched = 0; - return $requireAll ? $userPermissions === count($permissions) : $userPermissions > 0; + foreach (array_unique($permissions) as $permission) { + if ($this->hasPermission($permission, $type)) { + $matched++; + } + } + + return $requireAll ? $matched === count($permissions) : $matched > 0; } /** @@ -303,33 +374,21 @@ class User extends Authenticatable implements HasLocalePreference return $this->effectiveScopeCache[$identifier]; } - $scopeTypes = $this->roles() - ->whereHas('permissions', function ($query) use ($identifier) { - $query->where('identifier', $identifier); - }) - ->with(['permissions' => function ($query) use ($identifier) { - $query->where('identifier', $identifier); - }]) - ->get() - ->pluck('permissions') - ->flatten() - ->pluck('pivot.scope_type'); + // 같은 권한 집합에서 scope_type 만 뽑는다 — 권한마다 쿼리를 다시 내지 않는다. + $values = array_column($this->permissionGrants()[$identifier] ?? [], 'scope_type'); // 권한 미보유 시 null 반환 (기본값: 전체 접근) - if ($scopeTypes->isEmpty()) { + if ($values === []) { return $this->effectiveScopeCache[$identifier] = null; } // union 정책: 하나라도 null → 전체 접근 - if ($scopeTypes->contains(null)) { + if (in_array(null, $values, true)) { return $this->effectiveScopeCache[$identifier] = null; } - // ScopeType Enum 값을 문자열로 변환하여 비교 - $values = $scopeTypes->map(fn ($scope) => $scope instanceof ScopeType ? $scope->value : $scope); - // 하나라도 'role' → role 적용 - if ($values->contains('role')) { + if (in_array(ScopeType::Role->value, $values, true)) { return $this->effectiveScopeCache[$identifier] = 'role'; } @@ -369,11 +428,16 @@ class User extends Authenticatable implements HasLocalePreference */ public function isAdmin(): bool { - return $this->roles() - ->whereHas('permissions', function ($query) { - $query->where('type', PermissionType::Admin); - }) - ->exists(); + // 같은 권한 집합을 재사용한다 — 관리자 판정은 미들웨어·리소스·레이아웃에서 반복 호출된다. + foreach ($this->permissionGrants() as $grants) { + foreach ($grants as $grant) { + if ($grant['type'] === PermissionType::Admin->value) { + return true; + } + } + } + + return false; } /** diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 29867b2b..452bd1b5 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -4,6 +4,7 @@ namespace App\Providers; use App\Models\Role; use App\Models\User; +use App\Support\GuestRoleResolver; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Gate; @@ -11,11 +12,6 @@ use Illuminate\Support\Facades\Log; class AuthServiceProvider extends ServiceProvider { - /** - * guest role 캐시 - */ - protected static ?Role $guestRoleCache = null; - /** * Register any authentication / authorization services. */ @@ -236,9 +232,9 @@ class AuthServiceProvider extends ServiceProvider return null; // guest role 없으면 다음 Gate 정의로 위임 } - return $guestRole->permissions() - ->where('identifier', $ability) - ->exists() ? true : null; + // 적재해 둔 권한 컬렉션에서 판정한다. 빌더를 다시 열면 캐시가 무의미해져 + // Gate 호출마다 쿼리가 나간다. + return GuestRoleResolver::hasPermission($ability) ? true : null; } /** @@ -246,13 +242,7 @@ class AuthServiceProvider extends ServiceProvider */ protected static function getGuestRole(): ?Role { - if (self::$guestRoleCache === null) { - self::$guestRoleCache = Role::where('identifier', 'guest') - ->with('permissions') - ->first(); - } - - return self::$guestRoleCache; + return GuestRoleResolver::resolve(); } /** @@ -260,7 +250,7 @@ class AuthServiceProvider extends ServiceProvider */ public static function clearGuestRoleCache(): void { - self::$guestRoleCache = null; + GuestRoleResolver::flush(); } /** @@ -275,16 +265,14 @@ class AuthServiceProvider extends ServiceProvider $hasPermission = false; if ($guestRole) { - $hasPermission = $guestRole->permissions() - ->where('identifier', $ability) - ->exists(); + $hasPermission = GuestRoleResolver::hasPermission($ability); } return [ 'result' => self::checkGuestPermission($ability), 'guestRole' => $guestRole, 'hasPermission' => $hasPermission, - 'cachedRoleId' => self::$guestRoleCache?->id, + 'cachedRoleId' => $guestRole?->id, ]; } } diff --git a/app/Providers/CoreServiceProvider.php b/app/Providers/CoreServiceProvider.php index 874976be..a7064352 100644 --- a/app/Providers/CoreServiceProvider.php +++ b/app/Providers/CoreServiceProvider.php @@ -1019,7 +1019,7 @@ class CoreServiceProvider extends ServiceProvider $listener = app($listenerClass); $listener->registerDynamicHooks(); - Log::info('동적 훅 리스너 등록 완료', ['listener' => $listenerClass]); + // 등록 성공은 로그로 남기지 않는다 — 요청마다 부팅되는 경로다. 실패만 아래에 남긴다. } catch (\Throwable $e) { Log::warning('동적 훅 리스너 등록 실패', [ 'listener' => $listenerClass, diff --git a/app/Providers/ModuleRouteServiceProvider.php b/app/Providers/ModuleRouteServiceProvider.php index 767ebf09..d134a558 100644 --- a/app/Providers/ModuleRouteServiceProvider.php +++ b/app/Providers/ModuleRouteServiceProvider.php @@ -2,10 +2,9 @@ namespace App\Providers; -use App\Enums\ExtensionStatus; use App\Extension\ExtensionManager; use App\Extension\Testing\ExtensionTestAllowlist; -use App\Models\Module; +use App\Extension\Traits\CachesModuleStatus; use App\Support\InstallerContext; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Support\Facades\File; @@ -14,6 +13,8 @@ use Illuminate\Support\Facades\Schema; class ModuleRouteServiceProvider extends ServiceProvider { + use CachesModuleStatus; + /** * The path to the "home" route for your application. * @@ -74,10 +75,10 @@ class ModuleRouteServiceProvider extends ServiceProvider } } - // 활성화된 모듈 identifier 목록 가져오기 - $activeModuleIdentifiers = Module::where('status', ExtensionStatus::Active->value) - ->pluck('identifier') - ->toArray(); + // 활성화된 모듈 identifier 목록 가져오기. + // 같은 목록을 ModuleManager·ModuleServiceProvider 가 이미 캐시(TTL 기본 하루)해 두므로 + // 여기서 다시 조회하지 않고 그 캐시를 공유한다. 상태 변경 시 무효화도 같이 따라온다. + $activeModuleIdentifiers = self::getActiveModuleIdentifiers(); $modules = File::directories($modulesPath); $allowlistActive = ExtensionTestAllowlist::isActive(); diff --git a/app/Providers/SettingsServiceProvider.php b/app/Providers/SettingsServiceProvider.php index 94a49f6d..678f4846 100644 --- a/app/Providers/SettingsServiceProvider.php +++ b/app/Providers/SettingsServiceProvider.php @@ -30,6 +30,7 @@ class SettingsServiceProvider extends ServiceProvider 'geoip', 'seo', 'identity', + 'pagination', ]; /** diff --git a/app/Repositories/ActivityLogRepository.php b/app/Repositories/ActivityLogRepository.php index 9eabb8c7..15418f77 100644 --- a/app/Repositories/ActivityLogRepository.php +++ b/app/Repositories/ActivityLogRepository.php @@ -5,10 +5,14 @@ namespace App\Repositories; use App\Contracts\Repositories\ActivityLogRepositoryInterface; use App\Helpers\PermissionHelper; use App\Helpers\TimezoneHelper; +use App\Http\Resources\BaseApiCollection; use App\Models\ActivityLog; use App\Repositories\Concerns\HasMultipleSearchFilters; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; +use App\Support\Query\KeysetPaginator; +use App\Support\Query\PaginationLimits; +use Illuminate\Contracts\Pagination\CursorPaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -65,16 +69,22 @@ class ActivityLogRepository implements ActivityLogRepositoryInterface columns: ['*'], sort: [['column' => 'created_at', 'direction' => $sortOrder]], perPage: (int) ($filters['per_page'] ?? 15), + // 로그 테이블은 계속 쌓이기만 한다. 총 건수는 상한까지만 세고 "다음" 이동은 + // per_page + 1 실측으로 끝까지 열어 둔다 (계산 불가는 마지막 페이지 번호 하나뿐). + resultCap: PaginationLimits::resultCap('admin.activity_logs'), ); } /** * 활동 로그 목록을 페이지네이션하여 조회합니다. * + * 요청에 `cursor` 가 있으면 키셋(커서) 방식으로, 없으면 페이지 번호 방식으로 응답합니다. + * 두 방식의 응답 봉투 차이는 {@see BaseApiCollection} 이 흡수합니다. + * * @param array $filters 필터 조건 - * @return LengthAwarePaginator 페이지네이션된 로그 목록 + * @return LengthAwarePaginator|CursorPaginator 페이지네이션된 로그 목록 */ - public function getPaginated(array $filters = []): LengthAwarePaginator + public function getPaginated(array $filters = []): LengthAwarePaginator|CursorPaginator { // 관계는 지연 조인의 outer 에서만 로드한다 (inner 는 키 컬럼만 조회한다) $query = ActivityLog::query(); @@ -122,6 +132,26 @@ class ActivityLogRepository implements ActivityLogRepositoryInterface } $sort = $this->resolveSortSpec($filters, self::SORTABLE_COLUMNS, 'created_at'); + $perPage = (int) ($filters['per_page'] ?? 15); + + // 커서를 받은 요청은 키셋으로 응답한다. 로그는 계속 쌓이기만 해서 깊은 페이지를 + // OFFSET 으로 훑으면 건너뛸 행을 실제로 읽어야 하지만, 커서는 직전 페이지의 정렬 + // 키를 WHERE 경계로 삼아 깊이와 무관하게 일정하다. + // 커서 모드에서는 OFFSET 자체가 없으므로 지연 조인이 해결하려던 문제도 함께 사라진다. + $sortKeys = array_map( + static fn (array $spec): array => [$spec['column'], $spec['direction']], + $sort + ); + + if (! empty($filters['cursor']) && KeysetPaginator::supports($sortKeys, self::SORTABLE_COLUMNS)) { + return KeysetPaginator::paginate( + query: $query->with('user:id,uuid,name,email'), + perPage: $perPage, + sortKeys: $sortKeys, + uniqueKey: 'id', + cursor: (string) $filters['cursor'], + ); + } // 목록 컬럼을 좁히지 않는 이유: 활동 로그 리소스는 변경 내역(mediumText `changes`)과 // 부가 정보(`properties`)까지 그대로 노출하므로 컬럼을 빼면 응답 계약이 바뀐다. @@ -130,8 +160,11 @@ class ActivityLogRepository implements ActivityLogRepositoryInterface query: $query, columns: ['*'], sort: $sort, - perPage: (int) ($filters['per_page'] ?? 15), + perPage: $perPage, relations: ['user:id,uuid,name,email'], + // 로그 테이블은 계속 쌓이기만 한다. 총 건수는 상한까지만 세고 "다음" 이동은 + // per_page + 1 실측으로 끝까지 열어 둔다 (계산 불가는 마지막 페이지 번호 하나뿐). + resultCap: PaginationLimits::resultCap('admin.activity_logs'), ); } diff --git a/app/Repositories/Concerns/FiltersByDateRange.php b/app/Repositories/Concerns/FiltersByDateRange.php new file mode 100644 index 00000000..c5e48108 --- /dev/null +++ b/app/Repositories/Concerns/FiltersByDateRange.php @@ -0,0 +1,72 @@ += 하루 시작` / `<= 하루 끝` 범위 조건으로 바꾸면 인덱스가 그대로 쓰인다. + * + * 경계 처리를 각 저장소가 따로 적으면 한쪽이 종료일 하루를 통째로 빠뜨리기 쉽다 + * (`<= '2026-08-02'` 는 그날 00:00:00 까지만 포함한다). 경계는 이 Trait 한 곳에서만 정한다. + */ +trait FiltersByDateRange +{ + /** + * 기간(시작일~종료일) 필터를 범위 조건으로 적용합니다. + * + * 종료일은 그날 23:59:59.999999 까지 포함하므로 `whereDate(..., '<=', ...)` 와 + * 같은 결과를 냅니다. + * + * @param Builder|\Illuminate\Database\Query\Builder $query 대상 쿼리 + * @param string $column 날짜 컬럼 (테이블 한정자 포함 가능) + * @param string|null $startDate 시작일 (빈 값이면 미적용) + * @param string|null $endDate 종료일 (빈 값이면 미적용) + */ + protected function applyDateRangeFilter($query, string $column, ?string $startDate, ?string $endDate): void + { + if (! empty($startDate)) { + $query->where($column, '>=', CarbonImmutable::parse($startDate)->startOfDay()); + } + + if (! empty($endDate)) { + $query->where($column, '<=', CarbonImmutable::parse($endDate)->endOfDay()); + } + } + + /** + * 특정 하루에 해당하는 행만 남기는 필터를 범위 조건으로 적용합니다. + * + * @param Builder|\Illuminate\Database\Query\Builder $query 대상 쿼리 + * @param string $column 날짜 컬럼 (테이블 한정자 포함 가능) + * @param \DateTimeInterface|string $day 대상 날짜 + */ + protected function applyDayFilter($query, string $column, \DateTimeInterface|string $day): void + { + $target = CarbonImmutable::parse($day instanceof \DateTimeInterface ? $day->format('Y-m-d H:i:s') : $day); + + $query->whereBetween($column, [$target->startOfDay(), $target->endOfDay()]); + } + + /** + * 특정 연·월에 해당하는 행만 남기는 필터를 범위 조건으로 적용합니다. + * + * `whereYear` + `whereMonth` 조합은 컬럼에 함수를 두 번 씌워 인덱스를 완전히 막습니다. + * + * @param Builder|\Illuminate\Database\Query\Builder $query 대상 쿼리 + * @param string $column 날짜 컬럼 (테이블 한정자 포함 가능) + * @param int $year 연도 + * @param int $month 월 (1~12) + */ + protected function applyMonthFilter($query, string $column, int $year, int $month): void + { + $start = CarbonImmutable::create($year, $month, 1)->startOfDay(); + + $query->whereBetween($column, [$start, $start->endOfMonth()]); + } +} diff --git a/app/Repositories/Concerns/PaginatesWithDeferredJoin.php b/app/Repositories/Concerns/PaginatesWithDeferredJoin.php index 4587908f..45f1b696 100644 --- a/app/Repositories/Concerns/PaginatesWithDeferredJoin.php +++ b/app/Repositories/Concerns/PaginatesWithDeferredJoin.php @@ -2,10 +2,14 @@ namespace App\Repositories\Concerns; +use App\Enums\TotalRelation; +use App\Support\Query\BoundedPage; +use App\Support\Query\BoundedPaginator; use Closure; use Illuminate\Contracts\Database\Query\Expression; use Illuminate\Contracts\Pagination\Paginator as PaginatorContract; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\Paginator; @@ -66,10 +70,11 @@ trait PaginatesWithDeferredJoin * @param bool $preserveIdOrder true = inner 가 정한 ID 순서를 outer 에서 그대로 복원 * @param string $pageName 페이지 쿼리 파라미터명 * @param Closure|null $outerUsing outer 에만 적용할 조인/집계 (inner 에서는 실행되지 않는다) - * @return PaginatorContract 페이지네이터 ($simple=true 면 Paginator, 아니면 LengthAwarePaginator) + * @param int|null $resultCap 총 건수 집계 상한 (null = 항상 정확한 COUNT) + * @return PaginatorContract 페이지네이터 ($simple=true 면 Paginator, $resultCap 지정 시 BoundedPage, 아니면 LengthAwarePaginator) */ protected function paginateWithDeferredJoin( - Builder $query, + Builder|Relation $query, array $columns, array $sort, int $perPage, @@ -82,7 +87,14 @@ trait PaginatesWithDeferredJoin bool $preserveIdOrder = false, string $pageName = 'page', ?Closure $outerUsing = null, + ?int $resultCap = null, ): PaginatorContract { + // 관계(`$model->items()`)도 받는다 — 표준 `paginate()` 가 되는 자리는 이 계약으로 + // 바꿔도 되어야 한다. 관계의 소속 조건은 그 밑 빌더에 이미 들어가 있어 보존된다. + // (쿼리 빌더는 받지 않는다. 이 계약은 모델의 키 컬럼·eager load 를 다루므로 + // Eloquent 가 전제다 — 넓힐 수 있는 범위와 없는 범위를 구분한다.) + $query = $query instanceof Relation ? $query->getQuery() : $query; + $page = $page ?: Paginator::resolveCurrentPage($pageName); $page = max(1, $page); @@ -94,7 +106,16 @@ trait PaginatesWithDeferredJoin // whereHas 는 결과 집합을 결정하는 조건이라 setEagerLoads 로 지워지지 않는다 (의도된 구분). $inner = (clone $query)->setEagerLoads([]); - if (! $simple && $total === null) { + // 상한이 지정되면 총 건수를 상한까지만 센다. 다음 페이지 판정은 총 건수와 무관하게 + // per_page + 1 실측으로 하므로, 마지막 페이지 번호 하나만 계산 불가가 된다. + $bounded = ! $simple && $resultCap !== null && $resultCap > 0; + $relation = null; + + if ($bounded && $total === null) { + // Eloquent 빌더를 그대로 넘긴다 — countWithCap 이 내부에서 toBase() 로 + // 글로벌 스코프까지 적용한 기반 쿼리를 만든다. + [$total, $relation] = BoundedPaginator::countWithCap(clone $inner, $resultCap); + } elseif (! $simple && $total === null) { // Laravel 의 paginate() 와 같은 집계 경로를 쓴다. count() 는 groupBy/having 이 있는 // 쿼리에서 `select count(*) ... group by ...` 를 그대로 실행해 **첫 그룹의 행 수**를 // 총 건수로 돌려준다. getCountForPagination() 은 그룹 쿼리를 서브쿼리로 감싸므로 @@ -105,10 +126,11 @@ trait PaginatesWithDeferredJoin $this->applySortSpec($inner, $sort); - // simple 모드는 다음 페이지 유무 판정을 위해 한 건을 더 읽는다 (Paginator 가 잘라낸다). + // simple/bounded 모드는 다음 페이지 유무 판정을 위해 한 건을 더 읽는다. // forPage() 를 쓰면 offset 이 (page-1) * (perPage+1) 로 계산돼 페이지가 넘어갈수록 // 건너뛰는 행이 어긋나므로, offset 은 perPage 기준으로 직접 지정한다. - $fetchCount = $simple ? $perPage + 1 : $perPage; + $probesNextPage = $simple || $bounded; + $fetchCount = $probesNextPage ? $perPage + 1 : $perPage; $ids = $inner ->select($inner->getModel()->qualifyColumn($keyName)) @@ -116,6 +138,12 @@ trait PaginatesWithDeferredJoin ->limit($fetchCount) ->pluck($keyName); + $hasMorePages = $probesNextPage && $ids->count() > $perPage; + + if ($bounded && $hasMorePages) { + $ids = $ids->slice(0, $perPage)->values(); + } + $items = $ids->isEmpty() ? new Collection : $this->fetchDeferredJoinRows($query, $columns, $sort, $ids->all(), $relations, $withCount, $keyName, $preserveIdOrder, $outerUsing); @@ -129,6 +157,22 @@ trait PaginatesWithDeferredJoin return new Paginator($items, $perPage, $page, $options); } + if ($bounded) { + // 동시 삽입으로 집계 시점과 조회 시점의 모수가 달라질 수 있다 + $seenSoFar = ($page - 1) * $perPage + $items->count(); + + return new BoundedPage( + items: $items, + total: max((int) $total, $seenSoFar), + perPage: $perPage, + currentPage: $page, + totalRelation: $relation ?? TotalRelation::Exact, + resultCap: $resultCap, + hasMorePages: $hasMorePages, + options: $options, + ); + } + return new LengthAwarePaginator($items, $total ?? $items->count(), $perPage, $page, $options); } diff --git a/app/Repositories/IdentityPolicyRepository.php b/app/Repositories/IdentityPolicyRepository.php index 410494d5..54889b7f 100644 --- a/app/Repositories/IdentityPolicyRepository.php +++ b/app/Repositories/IdentityPolicyRepository.php @@ -263,6 +263,7 @@ class IdentityPolicyRepository implements IdentityPolicyRepositoryInterface */ public function allEnabled(): Collection { + // audit:allow query-unbounded-get reason: 본인인증 정책은 운영자가 등록한 수만큼만 존재한다 (사용량과 무관) return IdentityPolicy::query()->where('enabled', true)->get(); } @@ -347,6 +348,7 @@ class IdentityPolicyRepository implements IdentityPolicyRepositoryInterface return []; } + // audit:allow query-unbounded-get reason: 본인인증 정책은 운영자가 등록한 수만큼만 존재한다 (사용량과 무관) return IdentityPolicy::query() ->where('scope', 'hook') ->distinct() diff --git a/app/Repositories/IdentityVerificationLogRepository.php b/app/Repositories/IdentityVerificationLogRepository.php index 52c4c917..d62ec2f3 100644 --- a/app/Repositories/IdentityVerificationLogRepository.php +++ b/app/Repositories/IdentityVerificationLogRepository.php @@ -8,6 +8,7 @@ use App\Helpers\TimezoneHelper; use App\Models\IdentityPolicy; use App\Models\IdentityVerificationLog; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Carbon; @@ -219,6 +220,9 @@ class IdentityVerificationLogRepository implements IdentityVerificationLogReposi columns: ['*'], sort: [['column' => $sortBy, 'direction' => $sortOrder]], perPage: $perPage, + // 로그 테이블은 계속 쌓이기만 한다. 총 건수는 상한까지만 세고 "다음" 이동은 + // per_page + 1 실측으로 끝까지 열어 둔다 (계산 불가는 마지막 페이지 번호 하나뿐). + resultCap: PaginationLimits::resultCap('admin.identity_logs'), ); } diff --git a/app/Repositories/JsonConfigRepository.php b/app/Repositories/JsonConfigRepository.php index ed63319d..3087f26e 100644 --- a/app/Repositories/JsonConfigRepository.php +++ b/app/Repositories/JsonConfigRepository.php @@ -43,6 +43,17 @@ class JsonConfigRepository implements ConfigRepositoryInterface */ private ?array $cache = null; + /** + * 카테고리별 메모리 캐시 + * + * 부팅 한 번에 같은 카테고리를 여러 곳에서 읽는다 — SettingsServiceProvider 의 + * apply*Config 9종과 loadCoreSettingsToConfig 가 각각 조회하므로, 캐시가 없으면 + * 요청마다 카테고리 수만큼 곱한 횟수로 파일 존재 확인·읽기·JSON 파싱이 반복된다. + * + * @var array> + */ + private array $categoryCache = []; + /** * 모든 카테고리의 설정을 조회합니다. * @@ -67,9 +78,27 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 특정 카테고리의 설정을 조회합니다. * - * @return array + * @param string $category 카테고리명 + * @return array 기본값과 병합된 설정 */ public function getCategory(string $category): array + { + if (array_key_exists($category, $this->categoryCache)) { + return $this->categoryCache[$category]; + } + + return $this->categoryCache[$category] = $this->readCategory($category); + } + + /** + * 카테고리 설정을 파일에서 읽어 기본값과 병합합니다. + * + * 캐시를 거치지 않는 실제 읽기 경로입니다. + * + * @param string $category 카테고리명 + * @return array + */ + private function readCategory(string $category): array { if (! $this->categoryExists($category)) { return $this->getDefaultsForCategory($category); @@ -77,11 +106,17 @@ class JsonConfigRepository implements ConfigRepositoryInterface $path = $this->getCategoryPath($category); + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. if (! Storage::disk(self::STORAGE_DISK)->exists($path)) { return $this->getDefaultsForCategory($category); } try { + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $content = Storage::disk(self::STORAGE_DISK)->get($path); $data = json_decode($content, true); @@ -113,6 +148,8 @@ class JsonConfigRepository implements ConfigRepositoryInterface * 도트 노테이션으로 특정 설정값을 조회합니다. * * @param string $key 예: 'mail.host', 'general.site_name' + * @param mixed $default 값이 없을 때 돌려줄 기본값 + * @return mixed 설정값 (없으면 $default) */ public function get(string $key, mixed $default = null): mixed { @@ -131,6 +168,10 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 도트 노테이션으로 특정 설정값을 저장합니다. + * + * @param string $key 설정 키 (카테고리.항목) + * @param mixed $value 저장할 값 + * @return bool 저장 성공 여부 */ public function set(string $key, mixed $value): bool { @@ -150,7 +191,8 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 여러 설정을 일괄 저장합니다. * - * @param array $settings + * @param array $settings 도트 노테이션 키 ⇒ 값 + * @return bool 저장 성공 여부 */ public function setMany(array $settings): bool { @@ -171,7 +213,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 특정 카테고리의 설정을 저장합니다. * - * @param array $settings + * @param string $category 카테고리명 + * @param array $settings 저장할 설정 + * @return bool 저장 성공 여부 */ public function saveCategory(string $category, array $settings): bool { @@ -195,6 +239,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface try { // 파일 잠금으로 동시 쓰기 방지 + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $fullPath = Storage::disk(self::STORAGE_DISK)->path($path); $handle = fopen($fullPath, 'c'); @@ -211,8 +258,10 @@ class JsonConfigRepository implements ConfigRepositoryInterface fclose($handle); - // 캐시 무효화 + // 캐시 무효화 — 저장한 카테고리와 전체 맵 양쪽을 비운다. + // 한쪽만 비우면 저장 직후 조회가 이전 값을 돌려준다. $this->cache = null; + unset($this->categoryCache[$category]); return true; } catch (\Exception $e) { @@ -226,6 +275,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 설정 키 존재 여부를 확인합니다. + * + * @param string $key 설정 키 (카테고리.항목) + * @return bool 존재 여부 */ public function has(string $key): bool { @@ -234,6 +286,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 특정 설정을 삭제합니다. + * + * @param string $key 설정 키 (카테고리.항목) + * @return bool 삭제 성공 여부 */ public function delete(string $key): bool { @@ -269,6 +324,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 카테고리 존재 여부를 확인합니다. + * + * @param string $category 카테고리명 + * @return bool 존재 여부 */ public function categoryExists(string $category): bool { @@ -278,7 +336,8 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 설정 파일을 초기화합니다. * - * @param array> $settings + * @param array> $settings 기본값 위에 덮어쓸 설정 + * @return bool 초기화 성공 여부 */ public function initialize(array $settings = []): bool { @@ -310,6 +369,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface $backupPath = self::BACKUP_DIR.'/'.$backupName; $zip = new \ZipArchive; + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $fullBackupPath = Storage::disk(self::STORAGE_DISK)->path($backupPath); if ($zip->open($fullBackupPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { @@ -318,6 +380,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface foreach ($this->getCategories() as $category) { $categoryPath = $this->getCategoryPath($category); + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $fullCategoryPath = Storage::disk(self::STORAGE_DISK)->path($categoryPath); if (file_exists($fullCategoryPath)) { @@ -332,9 +397,15 @@ class JsonConfigRepository implements ConfigRepositoryInterface /** * 백업에서 설정을 복원합니다. + * + * @param string $backupPath 백업 파일 경로 (disk 기준) + * @return bool 복원 성공 여부 */ public function restore(string $backupPath): bool { + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $fullBackupPath = Storage::disk(self::STORAGE_DISK)->path($backupPath); if (! file_exists($fullBackupPath)) { @@ -356,12 +427,16 @@ class JsonConfigRepository implements ConfigRepositoryInterface if ($this->categoryExists($category)) { $content = $zip->getFromIndex($i); $categoryPath = $this->getCategoryPath($category); + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. Storage::disk(self::STORAGE_DISK)->put($categoryPath, $content); } } $zip->close(); $this->cache = null; + $this->categoryCache = []; return true; } @@ -457,6 +532,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface */ private function ensureDirectoryExists(): void { + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $disk = Storage::disk(self::STORAGE_DISK); if (! $disk->exists('.')) { @@ -469,6 +547,9 @@ class JsonConfigRepository implements ConfigRepositoryInterface */ private function ensureBackupDirectoryExists(): void { + // audit:allow no-storage-disk-direct reason: 이 저장소는 SettingsServiceProvider::register() 에서 + // `new` 로 직접 생성된다 — 컨테이너 바인딩 이전이라 StorageInterface 를 해석할 수 없다. + // 설정 JSON 은 부팅 자체가 의존하는 자료라 드라이버 추상화보다 부팅 순서가 우선한다. $disk = Storage::disk(self::STORAGE_DISK); if (! $disk->exists(self::BACKUP_DIR)) { diff --git a/app/Repositories/LanguagePackRepository.php b/app/Repositories/LanguagePackRepository.php index 338b1c34..426408b1 100644 --- a/app/Repositories/LanguagePackRepository.php +++ b/app/Repositories/LanguagePackRepository.php @@ -50,6 +50,7 @@ class LanguagePackRepository implements LanguagePackRepositoryInterface */ public function getActivePacks(): Collection { + // audit:allow query-unbounded-get reason: 언어팩은 설치된 팩 × 로케일 수만큼만 존재한다 (사용량과 무관) return LanguagePack::query() ->where('status', LanguagePackStatus::Active->value) ->get(); @@ -96,6 +97,7 @@ class LanguagePackRepository implements LanguagePackRepositoryInterface ?string $targetIdentifier, string $locale ): Collection { + // audit:allow query-unbounded-get reason: 언어팩은 설치된 팩 × 로케일 수만큼만 존재한다 (사용량과 무관) return LanguagePack::query() ->where('scope', $scope) ->where('target_identifier', $targetIdentifier) @@ -112,6 +114,7 @@ class LanguagePackRepository implements LanguagePackRepositoryInterface */ public function getActiveCoreLocales(): array { + // audit:allow query-unbounded-get reason: 언어팩은 설치된 팩 × 로케일 수만큼만 존재한다 (사용량과 무관) return LanguagePack::query() ->where('scope', LanguagePackScope::Core->value) ->where('status', LanguagePackStatus::Active->value) @@ -246,6 +249,7 @@ class LanguagePackRepository implements LanguagePackRepositoryInterface */ public function getPacksForTarget(string $scope, string $targetIdentifier): Collection { + // audit:allow query-unbounded-get reason: 언어팩은 설치된 팩 × 로케일 수만큼만 존재한다 (사용량과 무관) return LanguagePack::query() ->where('scope', $scope) ->where('target_identifier', $targetIdentifier) @@ -260,6 +264,7 @@ class LanguagePackRepository implements LanguagePackRepositoryInterface */ public function getPacksForLocale(string $locale): Collection { + // audit:allow query-unbounded-get reason: 언어팩은 설치된 팩 × 로케일 수만큼만 존재한다 (사용량과 무관) return LanguagePack::query()->where('locale', $locale)->get(); } diff --git a/app/Repositories/NotificationLogRepository.php b/app/Repositories/NotificationLogRepository.php index 735b8fb5..52415dc6 100644 --- a/app/Repositories/NotificationLogRepository.php +++ b/app/Repositories/NotificationLogRepository.php @@ -8,8 +8,11 @@ use App\Models\NotificationLog; use App\Models\User; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; +use App\Support\Query\KeysetPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; class NotificationLogRepository implements NotificationLogRepositoryInterface @@ -101,9 +104,9 @@ class NotificationLogRepository implements NotificationLogRepositoryInterface * @param array $filters 필터 조건 * @param int $perPage 페이지당 건수 * @param User|null $scopeUser 스코프 적용 대상 사용자 (null이면 스코프 미적용) - * @return LengthAwarePaginator 페이지네이션 결과 + * @return LengthAwarePaginator|CursorPaginator 페이지네이션 결과 (커서 요청 시 키셋) */ - public function getPaginated(array $filters = [], int $perPage = 20, ?User $scopeUser = null): LengthAwarePaginator + public function getPaginated(array $filters = [], int $perPage = 20, ?User $scopeUser = null): LengthAwarePaginator|CursorPaginator { // 관계는 지연 조인의 outer 에서만 로드한다 (inner 는 키 컬럼만 조회한다) $query = NotificationLog::query(); @@ -152,6 +155,24 @@ class NotificationLogRepository implements NotificationLogRepositoryInterface $sort = $this->resolveSortSpec($filters, self::SORTABLE_COLUMNS, 'sent_at'); + // 커서를 받은 요청은 키셋으로 응답한다. 로그는 계속 쌓이기만 해서 깊은 페이지를 + // OFFSET 으로 훑으면 건너뛸 행을 실제로 읽어야 하지만, 커서는 직전 페이지의 정렬 + // 키를 WHERE 경계로 삼아 깊이와 무관하게 일정하다. + $sortKeys = array_map( + static fn (array $spec): array => [$spec['column'], $spec['direction']], + $sort + ); + + if (! empty($filters['cursor']) && KeysetPaginator::supports($sortKeys, self::SORTABLE_COLUMNS)) { + return KeysetPaginator::paginate( + query: $query->with(['senderUser', 'recipientUser']), + perPage: $perPage, + sortKeys: $sortKeys, + uniqueKey: 'id', + cursor: (string) $filters['cursor'], + ); + } + // 목록 컬럼을 좁히지 않는 이유: 이 목록의 리소스는 렌더링된 본문(longText `body`)까지 // 그대로 노출하므로 컬럼을 빼면 응답 계약이 바뀐다. 지연 조인만으로도 본문을 읽는 // 행 수가 OFFSET 과 무관하게 이번 페이지 분량으로 고정된다. @@ -161,6 +182,9 @@ class NotificationLogRepository implements NotificationLogRepositoryInterface sort: $sort, perPage: $perPage, relations: ['senderUser', 'recipientUser'], + // 로그 테이블은 계속 쌓이기만 한다. 총 건수는 상한까지만 세고 "다음" 이동은 + // per_page + 1 실측으로 끝까지 열어 둔다 (계산 불가는 마지막 페이지 번호 하나뿐). + resultCap: PaginationLimits::resultCap('admin.notification_logs'), ); } diff --git a/app/Repositories/NotificationRepository.php b/app/Repositories/NotificationRepository.php index b7e102aa..07a4cb10 100644 --- a/app/Repositories/NotificationRepository.php +++ b/app/Repositories/NotificationRepository.php @@ -4,6 +4,8 @@ namespace App\Repositories; use App\Contracts\Repositories\NotificationRepositoryInterface; use App\Models\User; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Notifications\DatabaseNotification; use Illuminate\Pagination\LengthAwarePaginator; @@ -32,7 +34,14 @@ class NotificationRepository implements NotificationRepositoryInterface // 걷어낼 넓은 컬럼이 없다 // 정렬 마지막의 기본키는 전순서 보장용이다 — created_at 동률에서 페이지 경계가 // 흔들려 인접 페이지가 같은 알림을 중복 노출하고 다른 알림을 누락하는 것을 막는다. - return $query->orderBy('created_at', 'desc')->orderBy('id', 'desc')->paginate($perPage); + // + // 알림함은 한 사용자에 묶이지만 시간이 지날수록 계속 쌓인다(설정성 테이블이 아니다). + // 총 건수는 상한까지만 세고, 뒤쪽 페이지 이동은 실측으로 끝까지 열어 둔다. + return BoundedPaginator::paginate( + $query->orderBy('created_at', 'desc')->orderBy('id', 'desc'), + perPage: $perPage, + resultCap: PaginationLimits::resultCap('user.notifications'), + ); } /** diff --git a/app/Repositories/RoleRepository.php b/app/Repositories/RoleRepository.php index d453c882..05275a46 100644 --- a/app/Repositories/RoleRepository.php +++ b/app/Repositories/RoleRepository.php @@ -17,6 +17,7 @@ class RoleRepository implements RoleRepositoryInterface */ public function getAll(): Collection { + // audit:allow query-unbounded-get reason: 역할은 운영자가 정의한 수만큼만 존재한다 (회원 수와 무관) return Role::with(['permissions']) ->orderBy('id') ->get(); @@ -29,6 +30,7 @@ class RoleRepository implements RoleRepositoryInterface */ public function getActiveRoles(): Collection { + // audit:allow query-unbounded-get reason: 역할은 운영자가 정의한 수만큼만 존재한다 (회원 수와 무관) return Role::where('is_active', true) ->orderBy('id') ->get(); @@ -142,6 +144,7 @@ class RoleRepository implements RoleRepositoryInterface */ public function getByExtension(ExtensionOwnerType $extensionType, string $extensionIdentifier): Collection { + // audit:allow query-unbounded-get reason: 역할은 운영자가 정의한 수만큼만 존재한다 (회원 수와 무관) return Role::where('extension_type', $extensionType) ->where('extension_identifier', $extensionIdentifier) ->get(); diff --git a/app/Repositories/ScheduleHistoryRepository.php b/app/Repositories/ScheduleHistoryRepository.php index f2e858cb..10cc4909 100644 --- a/app/Repositories/ScheduleHistoryRepository.php +++ b/app/Repositories/ScheduleHistoryRepository.php @@ -6,6 +6,7 @@ use App\Contracts\Repositories\ScheduleHistoryRepositoryInterface; use App\Models\ScheduleHistory; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; +use App\Support\Query\PaginationLimits; use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; @@ -116,6 +117,9 @@ class ScheduleHistoryRepository implements ScheduleHistoryRepositoryInterface sort: $sort, perPage: $perPage, relations: ['triggeredBy'], + // 로그 테이블은 계속 쌓이기만 한다. 총 건수는 상한까지만 세고 "다음" 이동은 + // per_page + 1 실측으로 끝까지 열어 둔다 (계산 불가는 마지막 페이지 번호 하나뿐). + resultCap: PaginationLimits::resultCap('admin.schedule_histories'), ); } diff --git a/app/Repositories/ScheduleRepository.php b/app/Repositories/ScheduleRepository.php index 129ebfd8..88d0e41c 100644 --- a/app/Repositories/ScheduleRepository.php +++ b/app/Repositories/ScheduleRepository.php @@ -8,6 +8,7 @@ use App\Enums\ScheduleResultStatus; use App\Enums\ScheduleType; use App\Helpers\PermissionHelper; use App\Models\Schedule; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\HasMultipleSearchFilters; use App\Repositories\Concerns\ResolvesSortSpec; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -16,6 +17,7 @@ use Illuminate\Database\Eloquent\Collection; class ScheduleRepository implements ScheduleRepositoryInterface { + use FiltersByDateRange; use HasMultipleSearchFilters; use ResolvesSortSpec; @@ -198,13 +200,13 @@ class ScheduleRepository implements ScheduleRepositoryInterface */ private function applyDateFilters(Builder $query, array $filters): void { - if (! empty($filters['created_from'])) { - $query->whereDate('created_at', '>=', $filters['created_from']); - } - - if (! empty($filters['created_to'])) { - $query->whereDate('created_at', '<=', $filters['created_to']); - } + // whereDate 는 컬럼에 DATE() 를 씌워 인덱스를 무력화한다 — 범위 조건으로 준다. + $this->applyDateRangeFilter( + $query, + 'created_at', + $filters['created_from'] ?? null, + $filters['created_to'] ?? null + ); } /** diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 3d031993..14f29798 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -5,9 +5,11 @@ namespace App\Repositories; use App\Contracts\Repositories\UserRepositoryInterface; use App\Helpers\PermissionHelper; use App\Models\User; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\HasMultipleSearchFilters; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -16,6 +18,7 @@ use Laravel\Sanctum\PersonalAccessToken; class UserRepository implements UserRepositoryInterface { + use FiltersByDateRange; use HasMultipleSearchFilters; use PaginatesWithDeferredJoin; use ResolvesSortSpec; @@ -125,6 +128,8 @@ class UserRepository implements UserRepositoryInterface sort: $sort, perPage: $perPage, relations: ['roles'], + // 회원 수가 커져도 목록 첫 화면 비용이 총 건수 COUNT 에 끌려가지 않도록 상한을 건다. + resultCap: PaginationLimits::resultCap('admin.users'), ); } @@ -153,13 +158,13 @@ class UserRepository implements UserRepositoryInterface */ private function applyDateFilters(Builder $query, array $filters): void { - if (! empty($filters['start_date'])) { - $query->whereDate('created_at', '>=', $filters['start_date']); - } - - if (! empty($filters['end_date'])) { - $query->whereDate('created_at', '<=', $filters['end_date']); - } + // whereDate 는 컬럼에 DATE() 를 씌워 인덱스를 무력화한다 — 범위 조건으로 준다. + $this->applyDateRangeFilter( + $query, + 'created_at', + $filters['start_date'] ?? null, + $filters['end_date'] ?? null + ); // 기본 날짜 필터 (전체가 아닌 경우) if (empty($filters['start_date']) && empty($filters['end_date']) && diff --git a/app/Search/Contracts/KeywordPredicateProvider.php b/app/Search/Contracts/KeywordPredicateProvider.php new file mode 100644 index 00000000..0cf66aa4 --- /dev/null +++ b/app/Search/Contracts/KeywordPredicateProvider.php @@ -0,0 +1,78 @@ +lookupIds($keyword, limit: $context->keyCap); + * $method = $boolean === 'or' ? 'orWhereIn' : 'whereIn'; + * $query->$method($query->getModel()->getQualifiedKeyName(), $ids); + * } + * } + * ``` + */ +interface KeywordPredicateProvider +{ + /** + * 쿼리에 키워드 술어를 붙입니다. + * + * 컬럼 목록은 **하나의 조건으로 함께 평가**해 달라는 요청입니다. 엔진이 그 조합을 + * 다룰 수 없으면(예: MySQL 이 그 컬럼 조합의 복합 FULLTEXT 인덱스를 요구하는 경우) + * 호출자가 {@see KeywordSearch::applyAny()} 로 컬럼별 OR 을 요청합니다. + * + * 매칭이 하나도 없을 수 있는 검색어(예: 연산자만 입력)에는 **항상 거짓인 조건**을 + * 붙여야 합니다. 아무 조건도 붙이지 않으면 필터 없는 전체 목록이 검색 결과로 나갑니다. + * + * **페이지네이션은 호출자(DB)가 담당합니다.** 엔진에게 페이지 번호를 넘기지 않는 이유는, + * 엔진이 자기 순서로 한 페이지 분량만 돌려주면 그 뒤에 적용되는 DB 필터(분류·전시상태· + * 조인)에 일부가 탈락해 페이지가 비고, DB 정렬과 엔진의 관련도 순서가 달라 페이지 경계도 + * 어긋나기 때문입니다. 엔진이 책임지는 것은 **"얼마까지 돌려줄 것인가"** 하나입니다. + * + * 키 집합을 만들어 조건으로 붙이는 구현은 `$context->keyCap` 을 반드시 지켜야 합니다. + * 지키지 않으면 매칭이 큰 검색어에서 그 집합 자체가 메모리 폭발이 됩니다. 상한에 걸려 + * 잘린 경우 그 이상은 도달 불가이며, 총 건수는 "이상" 으로 보고됩니다 + * ({@see PaginationLimits} 와 같은 의미). + * + * @param Builder $query 조건을 붙일 Eloquent 쿼리 빌더 + * @param array $columns 검색 대상 컬럼명 + * @param string $keyword 검색어 원문 (정제는 엔진이 수행) + * @param string $boolean 기존 조건과의 결합 방식 ('and' 또는 'or') + * @param KeywordSearchContext $context 술어 생성 조건 (키 집합 상한 등) + */ + public function applyKeywordPredicate( + Builder $query, + array $columns, + string $keyword, + string $boolean, + KeywordSearchContext $context + ): void; +} diff --git a/app/Search/DTO/KeywordSearchContext.php b/app/Search/DTO/KeywordSearchContext.php new file mode 100644 index 00000000..f0278fb1 --- /dev/null +++ b/app/Search/DTO/KeywordSearchContext.php @@ -0,0 +1,36 @@ +keyCap !== null && $this->keyCap > 0; + } +} diff --git a/app/Search/Engines/DatabaseFulltextEngine.php b/app/Search/Engines/DatabaseFulltextEngine.php index f77f8cdf..5984ddda 100644 --- a/app/Search/Engines/DatabaseFulltextEngine.php +++ b/app/Search/Engines/DatabaseFulltextEngine.php @@ -2,7 +2,13 @@ namespace App\Search\Engines; +use App\Enums\TotalRelation; use App\Search\Contracts\FulltextSearchable; +use App\Search\Contracts\KeywordPredicateProvider; +use App\Search\DTO\KeywordSearchContext; +use App\Search\KeywordSearch; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; @@ -18,7 +24,7 @@ use Laravel\Scout\Engines\Engine; * * MySQL 자체가 인덱스 소스이므로 update/delete/flush는 no-op입니다. */ -class DatabaseFulltextEngine extends Engine +class DatabaseFulltextEngine extends Engine implements KeywordPredicateProvider { /** * MariaDB 감지 결과 캐시 (프로세스 수명 동안 유지) @@ -53,20 +59,52 @@ class DatabaseFulltextEngine extends Engine * FULLTEXT 검색을 수행합니다. * * @param Builder $builder Scout 빌더 - * @return array{query: \Illuminate\Database\Eloquent\Builder, total: int} + * @return array{query: \Illuminate\Database\Eloquent\Builder|null, total: int|null, total_relation: TotalRelation|null, result_cap: int|null} */ public function search(Builder $builder): array { return $this->performSearch($builder); } + /** + * 검색 결과의 키 목록만 조회합니다. + * + * 총 건수를 쓰지 않는 경로이므로 COUNT 를 아예 실행하지 않고, SELECT 도 키 컬럼과 + * 정렬용 스코어로만 좁힙니다. 좁히지 않으면 매칭된 전 행의 모든 컬럼(본문 포함)을 + * 읽고 나서 ID 만 뽑아내게 됩니다. + * + * @param Builder $builder Scout 빌더 + * @return \Illuminate\Support\Collection 키 컬렉션 + */ + public function keys(Builder $builder): \Illuminate\Support\Collection + { + return $this->mapIds($this->performSearch($builder, withTotal: false, keysOnly: true)); + } + + /** + * 검색 결과를 모델 컬렉션으로 조회합니다. + * + * 총 건수를 쓰지 않는 경로이므로 COUNT 를 실행하지 않습니다. + * + * @param Builder $builder Scout 빌더 + * @return Collection 모델 컬렉션 + */ + public function get(Builder $builder): Collection + { + return $this->map( + $builder, + $builder->applyAfterRawSearchCallback($this->performSearch($builder, withTotal: false)), + $builder->model + ); + } + /** * 페이지네이션 적용 FULLTEXT 검색을 수행합니다. * * @param Builder $builder Scout 빌더 * @param int $perPage 페이지당 결과 수 * @param int $page 페이지 번호 - * @return array{query: \Illuminate\Database\Eloquent\Builder, total: int} + * @return array{query: \Illuminate\Database\Eloquent\Builder|null, total: int|null, total_relation: TotalRelation|null, result_cap: int|null} */ public function paginate(Builder $builder, $perPage, $page): array { @@ -85,7 +123,13 @@ class DatabaseFulltextEngine extends Engine return collect(); } - return $query->pluck($query->getModel()->getKeyName()); + // `pluck()` 은 이미 걸려 있는 SELECT 를 보존한다(onceWithColumns). 검색 쿼리는 + // 관련도 점수를 위해 `table.*` 를 선택해 두므로, 그대로 두면 ID 만 필요한 자리에서 + // 전 컬럼을 읽는다. 사본에서 키 컬럼만 남겨 좁힌다. + $model = $query->getModel(); + $key = $model->getQualifiedKeyName(); + + return $query->clone()->select($key)->pluck($model->getKeyName()); } /** @@ -129,7 +173,31 @@ class DatabaseFulltextEngine extends Engine */ public function getTotalCount($results): int { - return $results['total'] ?? 0; + return (int) ($results['total'] ?? 0); + } + + /** + * 검색 결과의 총 건수 정확도를 반환합니다. + * + * 상한을 넘겨 정확히 세지 않은 경우 `AtLeast` 입니다. + * + * @param array $results 검색 결과 + * @return TotalRelation 총 건수 정확도 + */ + public function getTotalRelation($results): TotalRelation + { + return $results['total_relation'] ?? TotalRelation::Exact; + } + + /** + * 총 건수 집계에 적용된 상한을 반환합니다. + * + * @param array $results 검색 결과 + * @return int|null 상한 (무제한이면 null) + */ + public function getResultCap($results): ?int + { + return $results['result_cap'] ?? null; } /** @@ -239,6 +307,14 @@ class DatabaseFulltextEngine extends Engine * MySQL 파싱 오류(ER_PARSE_ERROR)를 유발하므로, 사용자 입력을 일반 검색어로만 * 취급하도록 연산자를 제거하고 남은 토큰을 따옴표 구문으로 묶습니다. * + * 토큰은 공백으로 잇습니다 — BOOLEAN MODE 에서 공백 결합은 OR 입니다. 각 토큰에 `+` 를 + * 붙이면 AND 가 되어 매칭 집합이 줄고 대용량에서 검색 시간이 짧아질 여지가 있지만, + * **의도적으로 OR 을 유지합니다**. 입력한 단어 중 하나만 든 문서가 결과에서 통째로 + * 사라지는 손실이 성능 이득보다 크기 때문입니다. 한글은 ngram 파서가 2글자 단위로 + * 토큰을 쪼개므로 AND 전환의 결과 축소 폭이 특히 큽니다. 성능 축이 문제가 되면 결합 + * 방식이 아니라 전용 검색 엔진(core.search.engine_drivers)으로 해결합니다. + * 배경·실측: docs/backend/search-system.md, docs/backend/pagination.md + * * @param string $keyword 원본 검색어 * @return string 안전하게 정제된 BOOLEAN MODE 검색식 (정제 결과가 없으면 빈 문자열) */ @@ -267,32 +343,114 @@ class DatabaseFulltextEngine extends Engine * * DBMS별로 MATCH...AGAINST 또는 LIKE fallback을 자동 적용합니다. * + * 검색어는 반드시 이 헬퍼를 거쳐야 합니다. BOOLEAN MODE 는 `+ - * " ( )` 등을 + * 연산자로 해석하므로, 원문 키워드를 그대로 바인딩하면 사용자가 `+` 하나만 입력해도 + * 파싱 오류로 500 이 됩니다. 정제는 sanitizeBooleanModeKeyword 한 곳에서만 합니다. + * + * **컬럼을 배열로 넘기면 하나의 `MATCH(a, b)` 가 됩니다.** MySQL 은 이 형태에 정확히 + * 그 컬럼 조합의 **복합 FULLTEXT 인덱스**를 요구하며, 없으면 + * `Can't find FULLTEXT index matching the column list` 오류가 납니다. + * 컬럼별 단일 인덱스만 있는 테이블은 {@see self::whereFulltextAny()} 를 쓰세요. + * * @param \Illuminate\Database\Eloquent\Builder $query Eloquent 쿼리 빌더 - * @param string $column 검색 대상 컬럼명 - * @param string $keyword 검색어 + * @param string|array $columns 검색 대상 컬럼명 (배열이면 복합 인덱스 필요) + * @param string $keyword 검색어 (원문 — 정제는 이 메서드가 수행) * @param string $boolean 조건 결합 방식 ('and' 또는 'or') */ public static function whereFulltext( \Illuminate\Database\Eloquent\Builder $query, - string $column, + string|array $columns, string $keyword, string $boolean = 'and' ): void { - if (static::supportsFulltext()) { - $ftKeyword = static::sanitizeBooleanModeKeyword($keyword); - if ($ftKeyword === '') { - // 정제 결과 없음 → 항상 false 조건으로 빈 결과 (void 반환 계약 유지) - $falseMethod = $boolean === 'or' ? 'orWhereRaw' : 'whereRaw'; - $query->$falseMethod('1 = 0'); + // 활성 엔진 해석을 거친다 — 다른 엔진이 켜져 있으면 그 엔진이 조건을 만든다. + // 이 정적 헬퍼를 직접 부르던 코드도 이 경유로 엔진 교체 혜택을 받는다. + KeywordSearch::apply($query, $columns, $keyword, $boolean); + } - return; - } - $method = $boolean === 'or' ? 'orWhereRaw' : 'whereRaw'; - $query->$method("MATCH(`{$column}`) AGAINST(? IN BOOLEAN MODE)", [$ftKeyword]); - } else { - $method = $boolean === 'or' ? 'orWhere' : 'where'; - $query->$method($column, 'LIKE', "%{$keyword}%"); + /** + * {@inheritDoc} + * + * 이 엔진의 술어는 `MATCH ... AGAINST IN BOOLEAN MODE` 입니다. DBMS 가 FULLTEXT 를 + * 지원하지 않으면 같은 자리에서 부분일치로 내려갑니다. + * + * `$context->keyCap` 은 쓰지 않습니다 — 이 엔진의 술어는 SQL 조건 그 자체라 중간 + * 키 집합을 만들지 않으므로 상한을 적용할 대상이 없습니다. 상한은 키 집합을 만들어 + * 조건으로 붙이는 엔진(외부 검색 서버 등)을 위한 것입니다. + */ + public function applyKeywordPredicate( + \Illuminate\Database\Eloquent\Builder $query, + array $columns, + string $keyword, + string $boolean, + KeywordSearchContext $context + ): void { + static::applyFulltextPredicate($query, $columns, $keyword, $boolean); + } + + /** + * FULLTEXT 조건을 실제로 조립합니다. + * + * @param \Illuminate\Database\Eloquent\Builder $query Eloquent 쿼리 빌더 + * @param array $columns 검색 대상 컬럼명 + * @param string $keyword 검색어 원문 + * @param string $boolean 조건 결합 방식 ('and' 또는 'or') + */ + protected static function applyFulltextPredicate( + \Illuminate\Database\Eloquent\Builder $query, + array $columns, + string $keyword, + string $boolean = 'and' + ): void { + $columns = array_values($columns); + + if (! static::supportsFulltext()) { + // 전문검색을 제공하지 않는 DBMS 로 설치된 사이트에서는 부분일치가 정상 경로다. + // 연산자 선택과 와일드카드 escape 는 코어 해석기가 단독으로 수행한다 — + // 여기서 다시 조립하면 DBMS 가 늘 때마다 고쳐야 할 곳이 둘이 된다. + KeywordSearch::applyLikeMatch($query, $columns, $keyword, $boolean); + + return; } + + $ftKeyword = static::sanitizeBooleanModeKeyword($keyword); + + if ($ftKeyword === '') { + // 정제 결과 없음(연산자만 입력) → 빈 결과. 빌더가 바인딩까지 처리하도록 + // 빈 whereIn 을 쓴다 (raw '1 = 0' 은 쓰지 않는다). + $method = $boolean === 'or' ? 'orWhereIn' : 'whereIn'; + $query->$method($query->getModel()->getQualifiedKeyName(), []); + + return; + } + + $match = 'MATCH(`'.implode('`, `', $columns).'`)'; + $method = $boolean === 'or' ? 'orWhereRaw' : 'whereRaw'; + $query->$method($match.' AGAINST(? IN BOOLEAN MODE)', [$ftKeyword]); + } + + /** + * 여러 컬럼 중 하나라도 매칭하면 되는 FULLTEXT 조건을 추가합니다. + * + * 컬럼마다 별도의 `MATCH(col)` 를 만들어 OR 로 묶습니다. 컬럼별 단일 FULLTEXT 인덱스만 + * 있는 테이블(대부분의 경우)에서 쓰는 형태이며, 복합 인덱스가 없어도 동작합니다. + * + * 복합 인덱스가 있어 `MATCH(a, b)` 한 번으로 끝내야 하는 테이블은 + * {@see self::whereFulltext()} 에 배열을 넘기세요. + * + * @param \Illuminate\Database\Eloquent\Builder $query Eloquent 쿼리 빌더 + * @param array $columns 검색 대상 컬럼명 목록 + * @param string $keyword 검색어 (원문 — 정제는 내부에서 수행) + * @param string $boolean 바깥 쿼리와의 결합 방식 ('and' 또는 'or') + */ + public static function whereFulltextAny( + \Illuminate\Database\Eloquent\Builder $query, + array $columns, + string $keyword, + string $boolean = 'and' + ): void { + // 활성 엔진 해석을 거친다 ({@see self::whereFulltext()} 와 동일한 이유). + KeywordSearch::applyAny($query, $columns, $keyword, $boolean); } /** @@ -329,39 +487,37 @@ class DatabaseFulltextEngine extends Engine * @param Builder $builder Scout 빌더 * @param int|null $perPage 페이지당 결과 수 * @param int|null $page 페이지 번호 - * @return array{query: \Illuminate\Database\Eloquent\Builder, total: int} + * @param bool $withTotal 총 건수를 계산할지 여부 (쓰지 않는 경로에서는 false) + * @param bool $keysOnly 키 컬럼과 정렬용 스코어만 조회할지 여부 + * @return array{query: \Illuminate\Database\Eloquent\Builder|null, total: int|null, total_relation: TotalRelation|null, result_cap: int|null} */ - protected function performSearch(Builder $builder, ?int $perPage = null, ?int $page = null): array - { + protected function performSearch( + Builder $builder, + ?int $perPage = null, + ?int $page = null, + bool $withTotal = true, + bool $keysOnly = false + ): array { $model = $builder->model; $keyword = $builder->query; // 빈 검색어인 경우 빈 결과 반환 if (empty(trim($keyword))) { - return [ - 'query' => null, - 'total' => 0, - ]; + return self::emptyResult(); } $query = $model->newQuery(); // FulltextSearchable 인터페이스 구현 확인 if (! ($model instanceof FulltextSearchable)) { - return [ - 'query' => null, - 'total' => 0, - ]; + return self::emptyResult(); } $columns = $model->searchableColumns(); $weights = $model->searchableWeights(); if (empty($columns)) { - return [ - 'query' => null, - 'total' => 0, - ]; + return self::emptyResult(); } $useFulltext = static::supportsFulltext(); @@ -375,10 +531,7 @@ class DatabaseFulltextEngine extends Engine $ftKeyword = static::sanitizeBooleanModeKeyword($keyword); if ($ftKeyword === '') { // 연산자만 입력 → 500 대신 빈 결과 (빈 검색어와 동일 반환 형태) - return [ - 'query' => null, - 'total' => 0, - ]; + return self::emptyResult(); } // MATCH...AGAINST WHERE 조건 생성 (MySQL, MariaDB) @@ -399,7 +552,7 @@ class DatabaseFulltextEngine extends Engine $scoreBindings[] = $ftKeyword; } $scoreRaw = '('.implode(' + ', $scoreExpressions).') as _ft_score'; - $query->selectRaw($qualifiedTable.'.*, '.$scoreRaw, $scoreBindings); + $this->applySelect($query, $model, $qualifiedTable, $scoreRaw, $scoreBindings, $keysOnly); } else { // LIKE fallback (PostgreSQL, SQLite 등) $query->where(function ($q) use ($columns, $keyword) { @@ -409,7 +562,7 @@ class DatabaseFulltextEngine extends Engine }); // 스코어 고정 0 (관련성 순위 불가) - $query->selectRaw($qualifiedTable.'.*, 0 as _ft_score'); + $this->applySelect($query, $model, $qualifiedTable, '0 as _ft_score', [], $keysOnly); } // Scout Builder 콜백 적용 (추가 where 조건 등) @@ -459,8 +612,16 @@ class DatabaseFulltextEngine extends Engine $query->orderByDesc('_ft_score'); } - // 전체 건수 계산 (페이지네이션 전) - $total = $query->toBase()->getCountForPagination(); + // 전체 건수 계산 (페이지네이션 전). + // 총 건수를 쓰지 않는 경로에서는 아예 세지 않는다 — 대용량 매칭에서 COUNT 한 번이 + // 조회 본체보다 비쌀 수 있다. + $resultCap = PaginationLimits::resultCap('search'); + $total = null; + $relation = null; + + if ($withTotal) { + [$total, $relation] = BoundedPaginator::countWithCap($query, $resultCap); + } // 페이지네이션 적용 if ($perPage !== null) { @@ -468,11 +629,62 @@ class DatabaseFulltextEngine extends Engine $query->limit($perPage)->offset($offset); } elseif ($builder->limit !== null) { $query->limit($builder->limit); + } elseif ($resultCap !== null) { + // perPage 도 limit 도 없으면 LIMIT 이 아예 붙지 않아 매칭 전량을 PHP 로 끌어온다. + // 상한을 걸어 한 요청이 읽는 행 수에 천장을 둔다. + $query->limit($resultCap); } return [ 'query' => $query, 'total' => $total, + 'total_relation' => $relation, + 'result_cap' => $resultCap, + ]; + } + + /** + * 조회 컬럼을 적용합니다. + * + * 키만 필요한 경로에서는 키 컬럼과 정렬용 스코어로 좁힙니다. 스코어는 `ORDER BY` 가 + * 참조하므로 좁힐 때도 함께 남겨야 합니다. + * + * @param \Illuminate\Database\Eloquent\Builder $query 대상 쿼리 + * @param Model $model 기준 모델 + * @param string $qualifiedTable 프리픽스 포함 테이블명 + * @param string $scoreRaw 스코어 SELECT 표현식 (`... as _ft_score`) + * @param array $scoreBindings 스코어 표현식 바인딩 + * @param bool $keysOnly 키 컬럼만 조회할지 여부 + */ + protected function applySelect( + $query, + Model $model, + string $qualifiedTable, + string $scoreRaw, + array $scoreBindings, + bool $keysOnly + ): void { + if ($keysOnly) { + $query->select($model->getQualifiedKeyName())->selectRaw($scoreRaw, $scoreBindings); + + return; + } + + $query->selectRaw($qualifiedTable.'.*, '.$scoreRaw, $scoreBindings); + } + + /** + * 검색이 성립하지 않을 때의 빈 결과 형태를 반환합니다. + * + * @return array{query: null, total: int, total_relation: TotalRelation, result_cap: null} + */ + protected static function emptyResult(): array + { + return [ + 'query' => null, + 'total' => 0, + 'total_relation' => TotalRelation::Exact, + 'result_cap' => null, ]; } } diff --git a/app/Search/KeywordSearch.php b/app/Search/KeywordSearch.php new file mode 100644 index 00000000..9d958ab9 --- /dev/null +++ b/app/Search/KeywordSearch.php @@ -0,0 +1,280 @@ + + */ + private static array $warnedDrivers = []; + + /** + * 쿼리에 키워드 술어를 붙입니다. + * + * 컬럼을 배열로 넘기면 **하나의 조건으로 함께 평가**해 달라는 뜻입니다. 엔진이 그 조합을 + * 다룰 수 없는 경우(MySQL FULLTEXT 는 그 컬럼 조합의 복합 인덱스를 요구한다)에는 + * {@see self::applyAny()} 로 컬럼별 OR 을 요청하세요. + * + * @param Builder $query 조건을 붙일 Eloquent 쿼리 빌더 + * @param string|array $columns 검색 대상 컬럼명 + * @param string $keyword 검색어 원문 + * @param string $boolean 기존 조건과의 결합 방식 ('and' 또는 'or') + * @param string|null $limitContext 상한 해석에 쓸 목록 컨텍스트 이름 (예: 'search') + */ + public static function apply( + Builder $query, + string|array $columns, + string $keyword, + string $boolean = 'and', + ?string $limitContext = null + ): void { + $columns = array_values((array) $columns); + + if ($columns === []) { + return; + } + + $provider = self::provider(); + + if ($provider !== null) { + $provider->applyKeywordPredicate( + $query, + $columns, + $keyword, + $boolean, + self::context($limitContext) + ); + + return; + } + + self::applyLikeMatch($query, $columns, $keyword, $boolean); + } + + /** + * 엔진에게 넘길 술어 생성 조건을 만듭니다. + * + * 키 집합 상한은 목록 총 건수 상한과 같은 값을 쓴다 — 두 상한이 갈라지면 "엔진이 + * 돌려준 건수" 와 "화면이 보고하는 총 건수" 가 서로 다른 근거를 갖게 된다. + * + * @param string|null $limitContext 상한 해석에 쓸 목록 컨텍스트 이름 + * @return KeywordSearchContext 술어 생성 조건 + */ + private static function context(?string $limitContext): KeywordSearchContext + { + return new KeywordSearchContext( + keyCap: PaginationLimits::resultCap($limitContext), + ); + } + + /** + * 컬럼별로 따로 평가해 OR 로 묶은 키워드 술어를 붙입니다. + * + * 컬럼마다 개별 인덱스만 있는 테이블에서 씁니다. 바깥은 하나의 그룹으로 감싸므로 + * 호출자의 기존 조건과 섞이지 않습니다. + * + * @param Builder $query 조건을 붙일 Eloquent 쿼리 빌더 + * @param array $columns 검색 대상 컬럼명 + * @param string $keyword 검색어 원문 + * @param string $boolean 기존 조건과의 결합 방식 ('and' 또는 'or') + * @param string|null $limitContext 상한 해석에 쓸 목록 컨텍스트 이름 (예: 'search') + */ + public static function applyAny( + Builder $query, + array $columns, + string $keyword, + string $boolean = 'and', + ?string $limitContext = null + ): void { + $columns = array_values($columns); + + if ($columns === []) { + return; + } + + $method = $boolean === 'or' ? 'orWhere' : 'where'; + + $query->$method(function ($group) use ($columns, $keyword, $limitContext) { + foreach ($columns as $index => $column) { + self::apply($group, $column, $keyword, $index === 0 ? 'and' : 'or', $limitContext); + } + }); + } + + /** + * 활성 엔진이 키워드 술어를 제공하는지 확인하고 그 엔진을 반환합니다. + * + * 제공하지 않으면 null 을 돌려주고, 그 사실을 드라이버당 한 번 기록합니다. + * 기록하지 않으면 "검색이 느리고 관련도가 이상하다" 는 증상만 남고 원인을 찾을 단서가 + * 없어진다 — 폴백은 정상 동작처럼 보이기 때문이다. + * + * @return KeywordPredicateProvider|null 술어를 제공하는 엔진 (없으면 null) + */ + private static function provider(): ?KeywordPredicateProvider + { + try { + $engine = app(EngineManager::class)->engine(); + } catch (Throwable) { + // 부팅 초기 등 엔진 해석이 불가한 상황 — 경고 없이 폴백한다. + return null; + } + + if ($engine instanceof KeywordPredicateProvider) { + return $engine; + } + + self::warnFallbackOnce(); + + return null; + } + + /** + * 폴백 사용 사실을 드라이버당 한 번 기록합니다. + */ + private static function warnFallbackOnce(): void + { + $driver = (string) config('scout.driver', 'mysql-fulltext'); + + if (isset(self::$warnedDrivers[$driver])) { + return; + } + + self::$warnedDrivers[$driver] = true; + + Log::warning('활성 검색 엔진이 키워드 술어를 제공하지 않아 LIKE 로 검색합니다. 관련도 정렬이 적용되지 않고 전체 스캔이 발생합니다.', [ + 'driver' => $driver, + 'contract' => KeywordPredicateProvider::class, + ]); + } + + /** + * 부분일치(LIKE) 조건을 붙입니다. + * + * 이 경로는 "엔진이 없을 때의 임시방편" 이 아니다. 전문검색을 제공하지 않는 DBMS 로 + * 설치된 사이트에서는 **이것이 정상 검색 경로**이므로 이식성을 갖춰야 한다. + * + * - 대소문자: PostgreSQL 의 `LIKE` 는 대소문자를 구분한다. MySQL 은 기본 collation 이 + * 구분하지 않으므로, 드라이버별로 연산자를 갈라 **같은 검색어가 DBMS 에 따라 다른 + * 결과를 내지 않도록** 한다. + * - 와일드카드: 검색어의 `%` `_` `\` 를 escape 해 이용자가 입력한 글자 그대로 찾는다. + * escape 하지 않으면 `50%` 검색이 `50` 으로 시작하는 모든 행을 반환한다. + * + * @param Builder $query 조건을 붙일 Eloquent 쿼리 빌더 + * @param array $columns 검색 대상 컬럼명 + * @param string $keyword 검색어 원문 + * @param string $boolean 기존 조건과의 결합 방식 + */ + public static function applyLikeMatch( + Builder $query, + array $columns, + string $keyword, + string $boolean = 'and' + ): void { + $columns = array_values($columns); + + if ($columns === []) { + return; + } + + $pattern = '%'.self::escapeLikeWildcards($keyword).'%'; + $operator = self::caseInsensitiveLikeOperator(); + $method = $boolean === 'or' ? 'orWhere' : 'where'; + + $query->$method(function ($nested) use ($columns, $pattern, $operator) { + foreach ($columns as $index => $column) { + $nested->{$index === 0 ? 'where' : 'orWhere'}($column, $operator, $pattern); + } + }); + } + + /** + * 대소문자를 구분하지 않는 부분일치 연산자를 드라이버별로 반환합니다. + * + * 드라이버명을 코드에 적지 않는다 — 앞으로 어떤 DBMS 가 공식 지원될지 정해져 있지 + * 않으므로, 코드에 박으면 DBMS 가 늘 때마다 코어를 고쳐야 한다. 표는 선언형 config + * (`core.search.like_operators`)에 있고, 확장은 필터 훅으로 조정한다. + * + * @return string 사용할 비교 연산자 + */ + private static function caseInsensitiveLikeOperator(): string + { + $default = (string) config('core.search.like_operator_default', 'like'); + + $operators = config('core.search.like_operators', []); + $operators = HookManager::applyFilters(self::LIKE_OPERATORS_FILTER, $operators); + + if (! is_array($operators)) { + return $default; + } + + try { + $driver = DB::getDriverName(); + } catch (Throwable) { + return $default; + } + + $operator = $operators[$driver] ?? $default; + + return is_string($operator) && $operator !== '' ? $operator : $default; + } + + /** + * `LIKE` 패턴에서 특수 의미를 갖는 문자를 escape 합니다. + * + * @param string $keyword 검색어 원문 + * @return string escape 된 검색어 + */ + public static function escapeLikeWildcards(string $keyword): string + { + return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $keyword); + } + + /** + * 폴백 경고 기록을 초기화합니다 (테스트 전용). + */ + public static function forgetFallbackWarnings(): void + { + self::$warnedDrivers = []; + } +} diff --git a/app/Search/SearchCategoryPayload.php b/app/Search/SearchCategoryPayload.php new file mode 100644 index 00000000..2a72fc16 --- /dev/null +++ b/app/Search/SearchCategoryPayload.php @@ -0,0 +1,100 @@ + $items 화면에 실을 항목 (가공 완료 상태) + * @param array $extra 도메인 고유 필드 (available_boards 등) + * @return array 카테고리 페이로드 + */ + public static function fromBounded(BoundedPage $page, array $items, array $extra = []): array + { + return array_merge([ + 'total' => $page->total(), + 'total_relation' => $page->totalRelation()->value, + 'total_is_exact' => $page->totalRelation()->isExact(), + 'result_cap' => $page->resultCap(), + 'last_page' => $page->lastPage(), + 'has_more_pages' => $page->hasMorePages(), + // offset 응답에는 커서가 없다. 키 자체는 남겨 화면이 분기 없이 읽게 한다. + 'next_cursor' => null, + 'prev_cursor' => null, + 'items' => $items, + ], $extra); + } + + /** + * 커서(키셋) 방식 결과로 페이로드를 만듭니다. + * + * 커서 응답에는 총 건수가 없으므로 건수는 별도 집계로 받습니다. 마지막 페이지 번호는 + * 커서 방식에 존재하지 않는 개념이라 언제나 null 이며, 화면은 그때 마지막 페이지 + * 점프만 감추고 "다음" 이동은 그대로 유지합니다. + * + * @param CursorPaginator $page 커서 페이지 결과 + * @param BoundedCount|null $count 총 건수 집계 (배지를 그리지 않으면 null) + * @param array $items 화면에 실을 항목 (가공 완료 상태) + * @param array $extra 도메인 고유 필드 + * @return array 카테고리 페이로드 + */ + public static function fromCursor( + CursorPaginator $page, + ?BoundedCount $count, + array $items, + array $extra = [] + ): array { + $relation = $count?->totalRelation() ?? TotalRelation::AtLeast; + + return array_merge([ + 'total' => $count?->total() ?? count($items), + 'total_relation' => $relation->value, + 'total_is_exact' => $count !== null && $relation->isExact(), + 'result_cap' => $count?->resultCap(), + // 커서 방식에는 마지막 페이지 번호가 없다 (총 건수를 알아도 계산하지 않는다). + 'last_page' => null, + 'has_more_pages' => $page->hasMorePages(), + 'next_cursor' => KeysetPaginator::nextCursor($page), + 'prev_cursor' => KeysetPaginator::previousCursor($page), + 'items' => $items, + ], $extra); + } + + /** + * 목록 없이 건수만 필요한 자리(비활성 탭 배지)의 페이로드를 만듭니다. + * + * @param BoundedCount $count 총 건수 집계 + * @param array $extra 도메인 고유 필드 + * @return array 카테고리 페이로드 + */ + public static function fromCountOnly(BoundedCount $count, array $extra = []): array + { + return array_merge($count->toArray(), [ + 'last_page' => null, + 'has_more_pages' => false, + 'next_cursor' => null, + 'prev_cursor' => null, + 'items' => [], + ], $extra); + } +} diff --git a/app/Search/SearchPagePolicy.php b/app/Search/SearchPagePolicy.php new file mode 100644 index 00000000..abb6887a --- /dev/null +++ b/app/Search/SearchPagePolicy.php @@ -0,0 +1,68 @@ + $sortKeys [[컬럼, 방향], ...] + * @param array $cursorColumns 이 도메인에서 커서로 허용한 실제 컬럼 + * @param int $page 요청이 지목한 페이지 번호 (없으면 1) + * @return bool 커서로 응답할 수 있으면 true + */ + public static function usesCursor(?string $cursor, array $sortKeys, array $cursorColumns, int $page = 1): bool + { + if (! KeysetPaginator::supports($sortKeys, $cursorColumns)) { + return false; + } + + if ($cursor !== null && $cursor !== '') { + return true; + } + + return $page <= 1; + } + + /** + * 정렬 이름을 정렬 키 배열로 바꿉니다. + * + * 확장이 선언한 `정렬이름 => [컬럼, 방향]` 맵을 코어가 해석합니다. 맵에 없는 이름은 + * 커서로 처리할 수 없는 정렬(관련도순 등)로 보고 빈 배열을 돌려주며, + * {@see self::usesCursor()} 가 이를 "커서 불가" 로 판정합니다. + * + * @param string $sort 요청이 보낸 정렬 이름 + * @param array $sortMap 정렬 이름 → [컬럼, 방향] + * @return array 정렬 키 배열 (해석 불가 시 빈 배열) + */ + public static function sortKeys(string $sort, array $sortMap): array + { + if (! array_key_exists($sort, $sortMap)) { + return []; + } + + return [$sortMap[$sort]]; + } +} diff --git a/app/Services/ActivityLogService.php b/app/Services/ActivityLogService.php index 5a42b153..9516aeed 100644 --- a/app/Services/ActivityLogService.php +++ b/app/Services/ActivityLogService.php @@ -4,6 +4,7 @@ namespace App\Services; use App\Contracts\Repositories\ActivityLogRepositoryInterface; use App\Extension\HookManager; +use Illuminate\Contracts\Pagination\CursorPaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Model; @@ -18,7 +19,7 @@ class ActivityLogService /** * ActivityLogService 생성자 * - * @param ActivityLogRepositoryInterface $repository 활동 로그 리포지토리 + * @param ActivityLogRepositoryInterface $repository 활동 로그 리포지토리 */ public function __construct( private ActivityLogRepositoryInterface $repository @@ -27,8 +28,8 @@ class ActivityLogService /** * 특정 모델의 활동 로그 목록을 조회합니다. * - * @param Model $model 대상 모델 - * @param array $filters 필터 조건 + * @param Model $model 대상 모델 + * @param array $filters 필터 조건 * @return LengthAwarePaginator 페이지네이션된 로그 목록 */ public function getLogsForModel(Model $model, array $filters = []): LengthAwarePaginator @@ -39,10 +40,12 @@ class ActivityLogService /** * 활동 로그 목록을 조회합니다. * - * @param array $filters 필터 조건 - * @return LengthAwarePaginator 페이지네이션된 로그 목록 + * 요청에 `cursor` 가 있으면 키셋(커서) 방식으로 응답합니다. + * + * @param array $filters 필터 조건 + * @return LengthAwarePaginator|CursorPaginator 페이지네이션된 로그 목록 */ - public function getList(array $filters = []): LengthAwarePaginator + public function getList(array $filters = []): LengthAwarePaginator|CursorPaginator { return $this->repository->getPaginated($filters); } @@ -50,7 +53,7 @@ class ActivityLogService /** * 활동 로그를 삭제합니다. * - * @param int $id 삭제할 활동 로그 ID + * @param int $id 삭제할 활동 로그 ID * @return bool 삭제 성공 여부 */ public function delete(int $id): bool @@ -67,7 +70,7 @@ class ActivityLogService /** * 여러 활동 로그를 일괄 삭제합니다. * - * @param array $ids 삭제할 활동 로그 ID 목록 + * @param array $ids 삭제할 활동 로그 ID 목록 * @return int 삭제된 건수 */ public function deleteMany(array $ids): int diff --git a/app/Services/AuthService.php b/app/Services/AuthService.php index eb69fc7f..1987d3a9 100644 --- a/app/Services/AuthService.php +++ b/app/Services/AuthService.php @@ -356,6 +356,7 @@ class AuthService $userRole = $this->roleRepository->findByIdentifier('user'); if ($userRole) { $user->roles()->sync([$userRole->id]); + $user->flushPermissionCaches(); } $token = $user->createToken('auth-token', ['*'], $this->getTokenExpiresAt())->plainTextToken; diff --git a/app/Services/LanguagePack/LanguagePackRegistry.php b/app/Services/LanguagePack/LanguagePackRegistry.php index b5ce4eed..927deaaf 100644 --- a/app/Services/LanguagePack/LanguagePackRegistry.php +++ b/app/Services/LanguagePack/LanguagePackRegistry.php @@ -56,8 +56,14 @@ class LanguagePackRegistry return $this->activeCoreLocalesCache; } - $fromDb = $this->repository->getActiveCoreLocales(); - $merged = array_values(array_unique(array_merge(self::BUNDLED_CORE_LOCALES, $fromDb))); + // 활성 언어팩 전체는 getActivePacks() 가 이미 한 번 적재해 캐시한다. 코어 로케일은 + // 그 컬렉션의 부분집합이므로 DB 를 다시 부르지 않고 여기서 걸러 낸다. + // (부팅 경로에서 두 메서드가 모두 호출되므로, 재조회하면 요청마다 쿼리가 하나 더 는다) + $fromPacks = $this->getActivePacks(LanguagePackScope::Core->value) + ->pluck('locale') + ->all(); + + $merged = array_values(array_unique(array_merge(self::BUNDLED_CORE_LOCALES, $fromPacks))); return $this->activeCoreLocalesCache = $merged; } diff --git a/app/Services/NotificationLogService.php b/app/Services/NotificationLogService.php index 49b27022..cc8a6c58 100644 --- a/app/Services/NotificationLogService.php +++ b/app/Services/NotificationLogService.php @@ -7,8 +7,12 @@ use App\Enums\NotificationLogStatus; use App\Extension\HookManager; use App\Models\NotificationLog; use App\Models\User; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; +/** + * 알림 발송 이력 서비스 + */ class NotificationLogService { public function __construct( @@ -17,6 +21,9 @@ class NotificationLogService /** * 발송 성공 로그를 기록합니다. + * + * @param array $data 로그 데이터 + * @return NotificationLog 기록된 로그 */ public function logSent(array $data): NotificationLog { @@ -33,6 +40,9 @@ class NotificationLogService /** * 발송 실패 로그를 기록합니다. + * + * @param array $data 로그 데이터 + * @return NotificationLog 기록된 로그 */ public function logFailed(array $data): NotificationLog { @@ -49,6 +59,9 @@ class NotificationLogService /** * 발송 건너뜀 로그를 기록합니다. + * + * @param array $data 로그 데이터 + * @return NotificationLog 기록된 로그 */ public function logSkipped(array $data): NotificationLog { @@ -65,6 +78,9 @@ class NotificationLogService /** * 로그를 삭제합니다. + * + * @param NotificationLog $log 삭제할 로그 + * @return bool 삭제 성공 여부 */ public function deleteLog(NotificationLog $log): bool { @@ -79,6 +95,9 @@ class NotificationLogService /** * 다건 삭제합니다. + * + * @param array $ids 삭제할 로그 ID 목록 + * @return int 삭제된 건수 */ public function bulkDelete(array $ids): int { @@ -94,9 +113,12 @@ class NotificationLogService /** * 페이지네이션 목록을 조회합니다. * + * @param array $filters 필터 조건 + * @param int $perPage 페이지당 건수 * @param User|null $user 스코프 적용 대상 사용자 (null이면 스코프 미적용) + * @return LengthAwarePaginator|CursorPaginator 페이지 결과 (커서 요청 시 키셋) */ - public function getLogs(array $filters = [], int $perPage = 20, ?User $user = null): LengthAwarePaginator + public function getLogs(array $filters = [], int $perPage = 20, ?User $user = null): LengthAwarePaginator|CursorPaginator { return $this->repository->getPaginated($filters, $perPage, $user); } diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 4075254e..a4444465 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -88,6 +88,7 @@ class UserService // 역할 동기화 if ($roleIds !== null && count($roleIds) > 0) { $user->roles()->sync($roleIds); + $user->flushPermissionCaches(); } // After 훅: 사용자 객체와 원본 데이터 전달 @@ -196,6 +197,7 @@ class UserService // 역할 동기화 if ($roleIds !== null) { $user->roles()->sync($roleIds); + $user->flushPermissionCaches(); } // After 훅: 사용자 객체와 원본 데이터, 스냅샷 전달 @@ -313,6 +315,7 @@ class UserService // 역할 연결 해제 (명시적 삭제 - CASCADE 의존 금지) $user->roles()->detach(); + $user->flushPermissionCaches(); // 약관 동의 이력 삭제 $user->consents()->delete(); diff --git a/app/Support/GuestRoleResolver.php b/app/Support/GuestRoleResolver.php new file mode 100644 index 00000000..2941b22e --- /dev/null +++ b/app/Support/GuestRoleResolver.php @@ -0,0 +1,108 @@ +attributes->has(self::CACHE_KEY)) { + return $request->attributes->get(self::CACHE_KEY); + } + + $role = Role::where('identifier', 'guest') + ->with('permissions') + ->first(); + + $request->attributes->set(self::CACHE_KEY, $role); + + return $role; + } + + /** + * guest 역할이 특정 권한을 보유하는지 판정합니다. + * + * 적재해 둔 권한 컬렉션에서 판정하므로 판정마다 쿼리가 나가지 않습니다. + * + * @param string $identifier 권한 식별자 + * @param PermissionType|null $type 권한 타입 (null 이면 타입 무관) + * @return bool 보유 여부 + */ + public static function hasPermission(string $identifier, ?PermissionType $type = null): bool + { + $role = self::resolve(); + + if (! $role) { + return false; + } + + foreach ($role->permissions as $permission) { + if ($permission->identifier !== $identifier) { + continue; + } + + if ($type === null || self::permissionType($permission) === $type) { + return true; + } + } + + return false; + } + + /** + * 캐시를 비웁니다. + * + * 권한 재시드(코어 업데이트 / 확장 설치) 직후처럼 같은 요청 안에서 권한 구성이 + * 바뀌는 경우에 호출합니다. + */ + public static function flush(): void + { + request()->attributes->remove(self::CACHE_KEY); + } + + /** + * 권한의 타입을 Enum 으로 정규화합니다. + * + * @param Permission $permission 권한 모델 + * @return PermissionType|null 정규화된 타입 (해석 불가 시 null) + */ + private static function permissionType($permission): ?PermissionType + { + $type = $permission->type; + + if ($type instanceof PermissionType) { + return $type; + } + + return $type === null ? null : PermissionType::tryFrom((string) $type); + } +} diff --git a/app/Support/Query/BoundedCount.php b/app/Support/Query/BoundedCount.php new file mode 100644 index 00000000..5b9b18a1 --- /dev/null +++ b/app/Support/Query/BoundedCount.php @@ -0,0 +1,89 @@ +relation; + } + + /** + * 집계에 적용된 상한을 반환합니다. + * + * @return int|null 상한 (무제한이면 null) + */ + public function resultCap(): ?int + { + return $this->resultCap; + } + + /** + * 상한에 걸려 잘렸는지 여부를 반환합니다. + * + * @return bool 잘렸으면 true + */ + public function isTruncated(): bool + { + return ! $this->relation->isExact(); + } + + /** + * 총 건수를 반환합니다. + * + * @return int 총 건수 + */ + public function total(): int + { + return $this->total; + } + + /** + * 응답에 실을 정확도 필드 묶음을 반환합니다. + * + * 배지·요약 자리마다 같은 키를 손으로 조립하면 한 군데만 빠져도 + * 그 화면에서만 잘린 값이 정확한 것처럼 나간다. + * + * @return array{total: int, total_relation: string, total_is_exact: bool, result_cap: int|null} + */ + public function toArray(): array + { + return [ + 'total' => $this->total, + 'total_relation' => $this->relation->value, + 'total_is_exact' => $this->relation->isExact(), + 'result_cap' => $this->resultCap, + ]; + } +} diff --git a/app/Support/Query/BoundedPage.php b/app/Support/Query/BoundedPage.php new file mode 100644 index 00000000..9da061c6 --- /dev/null +++ b/app/Support/Query/BoundedPage.php @@ -0,0 +1,125 @@ +|array $items 현재 페이지 항목 + * @param int $total 총 건수 (상한 초과 시 상한값) + * @param int $perPage 페이지당 건수 + * @param int $currentPage 현재 페이지 번호 + * @param TotalRelation $totalRelation 총 건수 정확도 + * @param int|null $resultCap 적용된 상한 (무제한이면 null) + * @param bool $hasMorePages 다음 페이지 존재 여부 (per_page + 1 실측) + * @param array $options 페이지네이터 옵션 (path, pageName 등) + */ + public function __construct( + $items, + int $total, + int $perPage, + int $currentPage, + TotalRelation $totalRelation, + ?int $resultCap, + bool $hasMorePages, + array $options = [] + ) { + parent::__construct($items, $total, $perPage, $currentPage, $options); + + $this->totalRelation = $totalRelation; + $this->resultCap = $resultCap; + $this->probedHasMorePages = $hasMorePages; + } + + /** + * 총 건수와 실제 매칭 건수의 관계를 반환합니다. + * + * @return TotalRelation 정확(Exact) 또는 하한(AtLeast) + */ + public function totalRelation(): TotalRelation + { + return $this->totalRelation; + } + + /** + * 총 건수 집계에 적용된 상한을 반환합니다. + * + * @return int|null 상한 (무제한이면 null) + */ + public function resultCap(): ?int + { + return $this->resultCap; + } + + /** + * 총 건수가 상한에 걸려 잘렸는지 여부를 반환합니다. + * + * @return bool 잘렸으면 true + */ + public function isTruncated(): bool + { + return ! $this->totalRelation->isExact(); + } + + /** + * 마지막 페이지 번호를 반환합니다. + * + * 총 건수가 부정확하면 마지막 페이지를 계산할 수 없으므로 null 을 반환합니다. + * 화면은 이 값이 null 이면 마지막 페이지 점프 버튼을 감춥니다. + * + * @return int|null 마지막 페이지 번호 (총 건수 부정확 시 null) + */ + public function lastPage(): ?int + { + return $this->isTruncated() ? null : $this->lastPage; + } + + /** + * 다음 페이지 존재 여부를 반환합니다. + * + * 총 건수를 몰라도 `per_page + 1` 조회로 정확히 판정되므로, 상한에 걸린 상태에서도 + * "다음" 이동은 끝까지 열려 있습니다. + * + * @return bool 다음 페이지가 있으면 true + */ + public function hasMorePages(): bool + { + return $this->probedHasMorePages; + } +} diff --git a/app/Support/Query/BoundedPaginator.php b/app/Support/Query/BoundedPaginator.php new file mode 100644 index 00000000..229966ea --- /dev/null +++ b/app/Support/Query/BoundedPaginator.php @@ -0,0 +1,209 @@ +notifications()`)도 받는다. 관계에서 `->paginate()` 가 + * 되는 이유는 관계가 빌더로 호출을 전달해 주기 때문인데, 정적 메서드는 그 전달을 받지 + * 못한다. 그래서 여기서 직접 이해한다 — 이 입구가 표준보다 좁으면, 표준을 이 계약으로 + * 바꾸는 것만으로 멀쩡하던 목록이 TypeError 로 죽는다. + */ +class BoundedPaginator +{ + /** + * 관계를 그 밑의 빌더로 환원합니다. + * + * 관계의 소속 조건(`where user_id = ?` 등)은 관계 생성 시점에 빌더에 이미 들어가 있어 + * 그대로 보존된다. + * + * @param EloquentBuilder|QueryBuilder|Relation $query 대상 + * @return EloquentBuilder|QueryBuilder 빌더 + */ + private static function toBuilder(EloquentBuilder|QueryBuilder|Relation $query): EloquentBuilder|QueryBuilder + { + return $query instanceof Relation ? $query->getQuery() : $query; + } + + /** + * 한 페이지를 조회하고 상한 총 건수를 함께 계산합니다. + * + * @param EloquentBuilder|QueryBuilder|Relation $query 대상 빌더/관계 (정렬·필터가 이미 적용된 상태) + * @param int $perPage 페이지당 건수 + * @param int|null $page 현재 페이지 번호 (null 이면 요청에서 해석) + * @param int|null $resultCap 총 건수 집계 상한 (null 이면 무제한 = 정확한 COUNT) + * @param array $columns 조회 컬럼 + * @param string $pageName 페이지 쿼리 파라미터 이름 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) + */ + public static function paginate( + EloquentBuilder|QueryBuilder|Relation $query, + int $perPage, + ?int $page = null, + ?int $resultCap = null, + array $columns = ['*'], + string $pageName = 'page' + ): BoundedPage { + $query = self::toBuilder($query); + + $page = $page ?: Paginator::resolveCurrentPage($pageName); + $page = max(1, $page); + $perPage = max(1, $perPage); + + // per_page + 1 건을 읽어 다음 페이지 존재 여부를 실측한다. + // 총 건수 상한과 무관하게 항상 정확하다. 호출자의 빌더는 건드리지 않는다. + // + // offset 은 per_page 기준으로 계산한다. forPage($page, $perPage + 1) 을 쓰면 + // offset 까지 per_page + 1 배수가 되어 페이지가 깊어질수록 경계가 밀린다. + $items = (clone $query) + ->offset(($page - 1) * $perPage) + ->limit($perPage + 1) + ->get($columns); + + $hasMorePages = $items->count() > $perPage; + + if ($hasMorePages) { + $items = $items->slice(0, $perPage)->values(); + } + + [$total, $relation] = self::countWithCap($query, $resultCap); + + // 상한 이하인데 이번 페이지가 상한 경계를 넘겨 조회된 경우를 방어한다. + // (동시 삽입으로 카운트 시점과 조회 시점의 모수가 다를 수 있다) + $seenSoFar = ($page - 1) * $perPage + $items->count(); + if ($total < $seenSoFar) { + $total = $seenSoFar; + } + + return new BoundedPage( + items: $items, + total: $total, + perPage: $perPage, + currentPage: $page, + totalRelation: $relation, + resultCap: $resultCap, + hasMorePages: $hasMorePages, + options: [ + 'path' => Paginator::resolveCurrentPath(), + 'pageName' => $pageName, + ], + ); + } + + /** + * 목록 조회 없이 상한을 건 총 건수만 셉니다. + * + * 탭 배지처럼 건수만 필요한 자리에서 쓴다. {@see countWithCap()} 과 같은 쿼리를 + * 실행하지만 정확도를 값에 담아 돌려주므로, 잘린 값이 정확한 것처럼 화면에 + * 나가는 일이 생기지 않는다. + * + * @param EloquentBuilder|QueryBuilder|Relation $query 대상 빌더/관계 + * @param int|null $resultCap 상한 (null 이면 무제한 = 정확한 COUNT) + * @return BoundedCount 건수 + 정확도 + */ + public static function count(EloquentBuilder|QueryBuilder|Relation $query, ?int $resultCap): BoundedCount + { + [$total, $relation] = self::countWithCap($query, $resultCap); + + return new BoundedCount($total, $relation, $resultCap); + } + + /** + * 상한을 건 총 건수를 셉니다. + * + * 상한을 넘는지만 알면 되므로 `LIMIT cap + 1` 로 감싼 파생 테이블을 센다. + * 결과가 `cap + 1` 이면 실제 건수는 그 이상이므로 상한값을 하한으로 보고한다. + * + * `GROUP BY` 가 걸린 쿼리도 파생 테이블 안에서 그룹이 만들어지므로 그룹 수가 + * 그대로 센다 (표준 `count()` 가 그룹별 건수를 돌려주는 문제가 없다). + * + * @param EloquentBuilder|QueryBuilder|Relation $query 대상 빌더/관계 + * @param int|null $resultCap 상한 (null 이면 무제한 = 정확한 COUNT) + * @return array{0: int, 1: TotalRelation} [총 건수, 정확도] + */ + public static function countWithCap(EloquentBuilder|QueryBuilder|Relation $query, ?int $resultCap): array + { + $base = self::toCountableBase(self::toBuilder($query)); + + if ($resultCap === null || $resultCap <= 0) { + return [$base->getCountForPagination(), TotalRelation::Exact]; + } + + $bounded = $base->limit($resultCap + 1); + + // newQuery() 는 같은 커넥션의 빈 빌더를 만든다. fromSub 가 바인딩까지 옮겨 주므로 + // 서브쿼리 SQL 을 문자열로 조립하거나 mergeBindings 를 부를 필요가 없다. + $counted = (int) $bounded->newQuery() + ->fromSub($bounded, 'g7_bounded_total') + ->count(); + + return $counted > $resultCap + ? [$resultCap, TotalRelation::AtLeast] + : [$counted, TotalRelation::Exact]; + } + + /** + * 총 건수 집계용 기본 빌더를 만듭니다. + * + * 정렬은 건수에 영향을 주지 않으므로 제거하고, `DISTINCT`·`GROUP BY` 가 없으면 + * SELECT 목록을 상수로 좁혀 불필요한 컬럼 읽기를 없앤다. + * + * @param EloquentBuilder|QueryBuilder $query 대상 빌더 + * @return QueryBuilder 집계용 기본 빌더 (원본 불변) + */ + private static function toCountableBase(EloquentBuilder|QueryBuilder $query): QueryBuilder + { + // toBase() 는 Eloquent 빌더에만 있다. 이 클래스는 쿼리 빌더도 받겠다고 선언했으므로 + // (supports() 가 그것을 허용한다) 종류를 보고 갈라야 한다 — 그러지 않으면 + // `DB::table(...)` 을 그대로 넘긴 호출이 BadMethodCallException 으로 죽는다. + $clone = clone $query; + $base = $clone instanceof EloquentBuilder ? $clone->toBase() : $clone; + + $base->reorder(); + $base->limit(null)->offset(null); + + // DISTINCT / GROUP BY 는 어떤 컬럼을 세는지가 결과를 좌우하므로 SELECT 를 건드리지 않는다. + if (! $base->distinct && empty($base->groups)) { + $base->select(DB::raw('1')); + } + + return $base; + } + + /** + * 빌더가 상한 집계에 쓸 수 있는 형태인지 확인합니다. + * + * @param mixed $query 검사 대상 + * @return bool 사용 가능하면 true + */ + public static function supports(mixed $query): bool + { + return $query instanceof EloquentBuilder + || $query instanceof QueryBuilder + || $query instanceof Relation + || $query instanceof BuilderContract; + } +} diff --git a/app/Support/Query/KeysetPaginator.php b/app/Support/Query/KeysetPaginator.php new file mode 100644 index 00000000..fc942147 --- /dev/null +++ b/app/Support/Query/KeysetPaginator.php @@ -0,0 +1,149 @@ + $sortKeys [[컬럼, 방향], ...] 순서대로 적용 + * @param string $uniqueKey 전순서 보장용 고유 컬럼 (보통 기본키) + * @param string|null $cursor 인코딩된 커서 문자열 (첫 페이지면 null) + * @param array $columns 조회 컬럼 + * @return CursorPaginator 커서 페이지 결과 + */ + public static function paginate( + EloquentBuilder|QueryBuilder $query, + int $perPage, + array $sortKeys, + string $uniqueKey, + ?string $cursor = null, + array $columns = ['*'] + ): CursorPaginator { + $perPage = max(1, $perPage); + + $applied = []; + + foreach ($sortKeys as [$column, $direction]) { + $query->orderBy($column, self::normalizeDirection($direction)); + $applied[] = $column; + } + + // 전순서 보장 — 마지막 정렬 키가 고유하지 않으면 페이지 경계에서 행이 새거나 겹친다. + if (! in_array($uniqueKey, $applied, true)) { + $lastDirection = $sortKeys === [] ? 'desc' : self::normalizeDirection(end($sortKeys)[1]); + $query->orderBy($uniqueKey, $lastDirection); + } + + return $query->cursorPaginate($perPage, $columns, self::CURSOR_PARAM, self::decode($cursor)); + } + + /** + * 정렬 키 전부가 커서로 쓸 수 있는 실제 컬럼인지 판정합니다. + * + * 계산값·표현식·별칭은 WHERE 절 경계로 쓸 수 없습니다. + * + * @param array $sortKeys [[컬럼, 방향], ...] + * @param array $columnWhitelist 커서로 허용된 실제 컬럼 목록 + * @return bool 전부 허용 컬럼이면 true + */ + public static function supports(array $sortKeys, array $columnWhitelist): bool + { + if ($sortKeys === []) { + return false; + } + + foreach ($sortKeys as [$column]) { + if (! in_array($column, $columnWhitelist, true)) { + return false; + } + } + + return true; + } + + /** + * 커서 문자열을 디코딩합니다. + * + * 형식이 깨진 값은 예외 대신 null 로 취급해 첫 페이지로 되돌립니다. 사용자가 URL 을 + * 손으로 고쳤다는 이유로 목록 화면이 오류를 띄우게 두지 않습니다. + * + * @param string|null $cursor 인코딩된 커서 문자열 + * @return Cursor|null 디코딩된 커서 (부재·해석 실패 시 null) + */ + public static function decode(?string $cursor): ?Cursor + { + if ($cursor === null || $cursor === '') { + return null; + } + + try { + return Cursor::fromEncoded($cursor); + } catch (\Throwable) { + return null; + } + } + + /** + * 커서 페이지에서 다음 페이지 커서 문자열을 뽑아냅니다. + * + * @param CursorPaginator $page 커서 페이지 결과 + * @return string|null 다음 페이지 커서 (마지막 페이지면 null) + */ + public static function nextCursor(CursorPaginator $page): ?string + { + return $page->nextCursor()?->encode(); + } + + /** + * 커서 페이지에서 이전 페이지 커서 문자열을 뽑아냅니다. + * + * @param CursorPaginator $page 커서 페이지 결과 + * @return string|null 이전 페이지 커서 (첫 페이지면 null) + */ + public static function previousCursor(CursorPaginator $page): ?string + { + return $page->previousCursor()?->encode(); + } + + /** + * 정렬 방향을 asc/desc 로 정규화합니다. + * + * @param string $direction 입력 방향 + * @return string 'asc' 또는 'desc' + */ + private static function normalizeDirection(string $direction): string + { + return strtolower($direction) === 'asc' ? 'asc' : 'desc'; + } +} diff --git a/app/Support/Query/PaginationLimits.php b/app/Support/Query/PaginationLimits.php new file mode 100644 index 00000000..09541dd1 --- /dev/null +++ b/app/Support/Query/PaginationLimits.php @@ -0,0 +1,80 @@ + 0 ? $value : null; + } + + /** + * 페이지 번호 상한을 반환합니다. + * + * 정상 탐색은 `has_more_pages` 와 커서로 열려 있고, 이 값은 임의의 큰 페이지 번호를 + * 직접 던지는 남용을 막기 위한 것입니다. + * + * @param string|null $context 상한을 조정할 확장이 구분에 쓸 컨텍스트 키 + * @return int|null 상한 (무제한이면 null) + */ + public static function maxPage(?string $context = null): ?int + { + $value = self::setting('max_page', self::FALLBACK_MAX_PAGE); + $value = (int) HookManager::applyFilters('core.pagination.filter_max_page', $value, $context); + + return $value > 0 ? $value : null; + } + + /** + * 관리자 환경설정 값을 읽고, 없으면 config 기본값으로 폴백합니다. + * + * @param string $key pagination 하위 키 + * @param int $fallback 둘 다 부재할 때 쓸 값 + * @return int 해석된 값 + */ + private static function setting(string $key, int $fallback): int + { + $configured = config('g7_settings.core.pagination.'.$key); + + if ($configured === null || $configured === '') { + $configured = config('core.pagination.'.$key, $fallback); + } + + return (int) $configured; + } +} diff --git a/config/core.php b/config/core.php index 79349ccb..e132f002 100644 --- a/config/core.php +++ b/config/core.php @@ -75,6 +75,54 @@ return [ 'identity_challenge_ttl_minutes_max' => 1440, 'identity_max_attempts_min' => 1, 'identity_max_attempts_max' => 20, + + // 목록 한계값 (0 = 무제한) + 'advanced_pagination_result_cap_min' => 0, + 'advanced_pagination_result_cap_max' => 1000000, + 'advanced_pagination_max_page_min' => 0, + 'advanced_pagination_max_page_max' => 100000, + ], + + /* + |-------------------------------------------------------------------------- + | 목록 한계값 기본값 + |-------------------------------------------------------------------------- + | 관리자 환경설정(`pagination` 카테고리)이 비어 있을 때 쓰는 코드 기본값입니다. + | 실제 해석은 App\Support\Query\PaginationLimits 가 단독으로 수행하며, 확장은 + | 이 값을 리터럴로 다시 적지 않고 필터 훅으로만 조정합니다. + | + | - result_cap: 총 건수를 정확히 세는 상한. 이 값을 넘는 매칭은 "이상" 으로만 보고합니다. + | 페이지 이동은 상한과 무관하게 끝까지 열려 있고, 계산이 불가능해지는 것은 + | 마지막 페이지 번호 하나뿐입니다. + | - max_page: 직접 요청할 수 있는 페이지 번호 상한 (남용 차단용). + | + | 둘 다 0 이면 무제한입니다. + */ + 'pagination' => [ + 'result_cap' => 10000, + 'max_page' => 1000, + ], + + /* + |-------------------------------------------------------------------------- + | 검색 — DBMS 별 부분일치 연산자 + |-------------------------------------------------------------------------- + | 전문검색을 제공하지 않는 DBMS 로 설치된 사이트에서는 부분일치(LIKE)가 정상 검색 + | 경로입니다. 그런데 "대소문자를 구분하지 않는 부분일치" 를 어떤 연산자로 쓰는지는 + | DBMS 마다 다릅니다 — 대부분 기본 비교가 구분하지 않아 `like` 로 충분하지만, + | 그렇지 않은 DBMS 는 전용 연산자를 씁니다. + | + | 코어 코드에 드라이버명을 적지 않기 위해 이 표에 선언합니다. 새 DBMS 를 공식 지원할 + | 때는 여기에 한 줄을 더하면 되고, 확장은 `core.search.like_operators` 필터 훅으로 + | 조정합니다. 표에 없는 드라이버는 `like_operator_default` 를 씁니다. + | + | 키는 `DB::getDriverName()` 이 돌려주는 값입니다. + */ + 'search' => [ + 'like_operators' => [ + 'pgsql' => 'ilike', + ], + 'like_operator_default' => 'like', ], /* diff --git a/config/settings/defaults.json b/config/settings/defaults.json index 650098e9..2d57483e 100644 --- a/config/settings/defaults.json +++ b/config/settings/defaults.json @@ -2,7 +2,7 @@ "_meta": { "version": "1.0.0", "description": "그누보드7 환경설정 기본값 및 프론트엔드 스키마", - "categories": ["general", "security", "mail", "upload", "seo", "cache", "debug", "drivers", "core_update", "geoip", "notifications", "identity"] + "categories": ["general", "security", "mail", "upload", "seo", "cache", "debug", "drivers", "core_update", "geoip", "notifications", "identity", "pagination"] }, "defaults": { "general": { @@ -112,6 +112,10 @@ "auto_update_enabled": true, "last_updated_at": "" }, + "pagination": { + "result_cap": 10000, + "max_page": 1000 + }, "drivers": { "storage_driver": "local", "s3_bucket": "", @@ -282,6 +286,15 @@ "last_updated_at": { "type": "string", "sensitive": false, "frontend_key": "geoip_last_updated_at" } } }, + "pagination": { + "expose": true, + "merge_into": "advanced", + "_comment": "목록 한계값 — advanced 카테고리에 병합", + "fields": { + "result_cap": { "type": "integer", "sensitive": false, "frontend_key": "pagination_result_cap" }, + "max_page": { "type": "integer", "sensitive": false, "frontend_key": "pagination_max_page" } + } + }, "drivers": { "expose": false, "_comment": "드라이버 설정 — 프론트엔드 미노출 (admin_settings에서 API 원본값 기반 비교로 변경)", diff --git a/docs/README.md b/docs/README.md index 28b14c7a..c3833f11 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@ | 카테고리 | 문서 수 | 링크 상태 | |----------|---------|----------| -| [백엔드](backend/) | 34개 | 정상 | +| [백엔드](backend/) | 35개 | 정상 | | [프론트엔드](frontend/) | 51개 | 정상 | | [확장 시스템](extension/) | 31개 | 정상 | | 공통 | 20개 | 정상 | @@ -125,7 +125,7 @@ G7 이 제공하는 REST API 의 엔드포인트별 요청 파라미터·응답 ## 카테고리별 전체 문서 목록 -### 백엔드 (34개) +### 백엔드 (35개) | 문서 | 제목 | |------|------| @@ -152,6 +152,7 @@ G7 이 제공하는 REST API 의 엔드포인트별 요청 파라미터·응답 | [language-pack-service.md](backend/language-pack-service.md) | LanguagePackService (백엔드 Service 레이어) | | [middleware.md](backend/middleware.md) | 미들웨어 등록 규칙 | | [notification-system.md](backend/notification-system.md) | 알림 시스템 (Notification System) | +| [pagination.md](backend/pagination.md) | 대용량 목록 페이지네이션 (Pagination) | | [README.md](backend/README.md) | 백엔드 개발 가이드 | | [response-helper.md](backend/response-helper.md) | API 응답 규칙 (ResponseHelper) | | [routing.md](backend/routing.md) | 라우트 네이밍 및 경로 | diff --git a/docs/backend/README.md b/docs/backend/README.md index b5d78a24..30984336 100644 --- a/docs/backend/README.md +++ b/docs/backend/README.md @@ -53,6 +53,7 @@ | [language-pack-service.md](language-pack-service.md) | LanguagePackService (백엔드 Service 레이어) | LanguagePackService 가 install/activate/deactiva... | | [middleware.md](middleware.md) | 미들웨어 등록 규칙 | 인증 필요 미들웨어 → 전역 등록 금지! | | [notification-system.md](notification-system.md) | 알림 시스템 (Notification System) | GenericNotification 범용 클래스 1개로 모든 알림 처리 (개별 클래스... | +| [pagination.md](pagination.md) | 대용량 목록 페이지네이션 (Pagination) | 총 건수만 상한을 받는다 — 상한 이하면 정확, 초과면 "이상"(total_relat... | | [response-helper.md](response-helper.md) | API 응답 규칙 (ResponseHelper) | 모든 API 응답은 ResponseHelper 사용 | | [routing.md](routing.md) | 라우트 네이밍 및 경로 | 모든 라우트는 name() 필수: ->name('api.users.index') | | [search-system.md](search-system.md) | Scout 검색 엔진 시스템 (Search System) | Laravel Scout + DatabaseFulltextEngine: MySQL F... | diff --git a/docs/backend/api/README.md b/docs/backend/api/README.md index 4ce65bb6..9052c085 100644 --- a/docs/backend/api/README.md +++ b/docs/backend/api/README.md @@ -80,6 +80,33 @@ Authorization: Bearer {YOUR_TOKEN} } ``` +#### 총 건수 정확도 (대용량 목록) + +매칭이 아주 많을 수 있는 목록(검색 등)은 총 건수를 상한까지만 셉니다. 그런 목록은 위 필드에 +더해 정확도를 함께 내보내며, 세지 않은 값을 정확한 것처럼 말하지 않습니다. + +| 필드 | 타입 | 의미 | +| --- | --- | --- | +| `total_relation` | string | `exact`(정확) 또는 `at_least`(그 이상) | +| `total_is_exact` | boolean | 총 건수가 정확한지 여부 | +| `result_cap` | integer\|null | 집계에 적용된 상한 (무제한이면 `null`) | + +상한을 넘긴 경우 동작은 이렇습니다. + +- `total` 은 상한값이며 **그 이상**이라는 뜻입니다 (화면은 "10,000건 이상" 으로 표기) +- `last_page` 는 **`null`** 입니다 — 총 건수를 알아야 계산되는 유일한 값이라 계산할 수 없습니다 +- `has_more_pages` 는 그대로 정확합니다. 다음 페이지 이동은 끝까지 열려 있습니다 + +즉 상한에 걸려도 막히는 것은 마지막 페이지 점프 하나뿐입니다. + +#### 단순형·커서형 응답 + +총 건수를 아예 세지 않는 목록(`simplePaginate`)은 `total` 과 `last_page` 를 **내보내지 않습니다**. +커서 방식 목록은 대신 `next_cursor` / `prev_cursor` 를 실어 보냅니다. 없는 필드를 0 이나 1 로 +채우지 않으므로, 화면은 필드 존재 여부로 목록의 종류를 구분할 수 있습니다. + +> 상한·커서 규약 상세: [pagination.md](../pagination.md) + 일부 목록은 `data.abilities` 에 컬렉션 레벨 권한(`can_create`, `can_delete` 등)을 함께 반환합니다. 화면의 버튼 노출 여부를 이 값으로 판정하세요. diff --git a/docs/backend/api/activity-logs.md b/docs/backend/api/activity-logs.md index 95e50267..b216f880 100644 --- a/docs/backend/api/activity-logs.md +++ b/docs/backend/api/activity-logs.md @@ -39,6 +39,7 @@ | per_page | query | integer | 아니오 | min 1, max 100 | 페이지당 항목 수 | | sort_by | query | string | 아니오 | — | 정렬 기준 필드명 | | sort_order | query | string | 아니오 | — | 정렬 방향 (asc 오름차순 / desc 내림차순) | +| cursor | query | string | 아니오 | max 500 | 이어보기 커서. 주면 페이지 번호 대신 키셋 방식으로 응답합니다 (기록이 많이 쌓인 사이트에서 뒤쪽 페이지가 느려지지 않음). 형식이 깨진 값은 오류 없이 첫 페이지로 해석됩니다 | > 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.activity_log.index_validation_rules`). @@ -169,6 +170,27 @@ HTTP/1.1 200 } ``` +**커서 방식 응답 (cursor 파라미터를 준 경우)** + +페이지 번호 대신 앞뒤 커서를 싣습니다. 총 건수를 세지 않으므로 `total` 과 `last_page` 는 없습니다. + +```json +{ + "success": true, + "data": { + "data": [], + "pagination": { + "per_page": 25, + "next_cursor": "eyJpZCI6MTIzLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9", + "prev_cursor": null, + "has_more_pages": true + } + } +} +``` + +총 건수가 상한을 넘겨 정확히 세지 못한 경우(페이지 번호 방식)에는 `pagination` 에 `total_relation`·`total_is_exact`·`result_cap` 이 함께 실리고 `last_page` 가 `null` 이 됩니다. 상세는 [pagination.md](../pagination.md). + **에러 응답** | 상태코드 | 의미 | 발생 조건 | diff --git a/docs/backend/api/search.md b/docs/backend/api/search.md index 022a19dd..c3666f8a 100644 --- a/docs/backend/api/search.md +++ b/docs/backend/api/search.md @@ -30,8 +30,9 @@ | q | query | string | 아니오 | min 2, max 200 | 검색어 (부분 일치) | | type | query | string | 아니오 | — | 유형 필터 (해당 유형의 항목만 조회) | | sort | query | string | 아니오 | `relevance`, `latest`, `oldest`, `views`, `popular`, `price_asc`, `price_desc` | 정렬 기준 (필드명, `-` 접두 시 내림차순) | -| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) | +| page | query | integer | 아니오 | min 1, max = 목록 한계값 설정 | 조회할 페이지 번호 (1부터 시작). 상한은 관리자 > 환경설정 > 고급의 «페이지 번호 상한» 이며 남용 차단용입니다 — 정상 탐색은 `has_more_pages` 로 계속 열려 있습니다 | | per_page | query | integer | 아니오 | min 1, max 100 | 페이지당 항목 수 | +| cursor | query | string | 아니오 | max 500 | 커서(키셋) 페이지 이동용 커서. 응답의 `next_cursor` / `prev_cursor` 를 그대로 돌려보냅니다. 특정 탭을 볼 때만 유효하며(전체 탭은 무시), 실제 컬럼 기준 정렬(`latest`·`oldest`·`views`·`popular`·`price_asc`·`price_desc`)에서만 적용됩니다. 관련도순은 계산값 정렬이라 커서를 쓸 수 없어 `page` 기반 이동을 유지합니다. 형식이 깨진 값은 오류 없이 첫 페이지로 처리됩니다 | | board_slug | query | string | 아니오 | max 100 | 검색 범위를 특정 게시판으로 한정 (게시판 모듈이 `core.search.index_validation_rules` 훅으로 추가하는 파라미터, 해당 slug의 게시판 글만 검색) | | category_id | query | integer | 아니오 | — | category 식별자 | @@ -50,7 +51,22 @@ _단건 응답: `data` 객체의 필드._ | 필드 | 타입 | 실측 예시값 | 용도/설명 | | --- | --- | --- | --- | | q | string | `` | 실제 검색에 사용된 검색어 (요청 `q` 를 trim 하여 에코, 검색어가 비어 있으면 빈 문자열) | -| total | integer | `0` | 전체 개수 (집계) | +| total | integer | `0` | 전체 개수 (집계). 상한을 넘기면 상한값이며 «그 이상» 을 뜻합니다 | +| total_relation | string | `exact` | 총 건수 정확도 (`exact` 정확 / `at_least` 그 이상) | +| total_is_exact | boolean | `true` | 총 건수가 정확한지 여부. `false` 면 화면이 "N건 이상" 으로 표기합니다 | +| result_cap | integer\|null | `10000` | 총 건수 집계에 적용된 상한 (무제한이면 `null`) | +| all_count | integer | `0` | 전체 탭 기준 합계 (탭 배지 표기용) | +| all_count_is_exact | boolean | `true` | 전체 합계가 정확한지 여부. 카테고리 중 하나라도 상한에 걸리면 `false` 입니다 | +| last_page | integer\|null | `1` | 특정 탭 조회 시의 마지막 페이지. **총 건수가 부정확하면 `null`** | +| has_more_pages | boolean | `false` | 다음 페이지 존재 여부 (총 건수를 몰라도 정확) | +| next_cursor | string\|null | `null` | 다음 페이지 커서. 커서 방식으로 응답했을 때만 채워지며, `page` 방식 응답에서는 `null` 입니다 | +| prev_cursor | string\|null | `null` | 이전 페이지 커서. 위와 같습니다 | +| counts_are_exact | object | `{}` | 카테고리별 총 건수 정확도 (`{"posts": true, "products": false}`). 탭 배지가 카테고리마다 그려지므로 정확도도 카테고리마다 제공됩니다 — 정확하지 않은 배지는 화면에서 "이상" 으로 표기됩니다 | + +카테고리(탭) 중 하나라도 상한에 걸리면 합계도 정확하지 않습니다 — 정확한 카테고리 몇 개를 +더해 봐야 전체가 정확해지지 않기 때문입니다. 그 경우 `total_is_exact` 는 `false` 가 됩니다. + +> 상한·페이지 이동 규약 상세: [pagination.md](../pagination.md) **응답 예시** @@ -64,7 +80,10 @@ HTTP/1.1 200 "message": "검색어를 입력해주세요.", "data": { "q": "", - "total": 0 + "total": 0, + "total_relation": "exact", + "total_is_exact": true, + "result_cap": 10000 } } ``` diff --git a/docs/backend/api/settings.md b/docs/backend/api/settings.md index e1530ecc..d8be601f 100644 --- a/docs/backend/api/settings.md +++ b/docs/backend/api/settings.md @@ -4,6 +4,22 @@ --- + +## 목록 한계값 (`advanced` 탭) + +대용량 목록에서 총 건수를 세는 범위와 직접 요청할 수 있는 페이지 번호의 상한입니다. +저장 경로는 다른 고급 설정과 같은 `advanced` 탭이며, 저장소에는 `pagination` 카테고리로 남습니다. + +| 필드 | 타입 | 범위 | 의미 | +| --- | --- | --- | --- | +| `advanced.pagination_result_cap` | integer | 0 ~ 1,000,000 | 총 건수를 정확히 세는 상한. 0 이면 항상 전부 셉니다 | +| `advanced.pagination_max_page` | integer | 0 ~ 100,000 | 주소로 직접 요청할 수 있는 최대 페이지 번호. 0 이면 제한하지 않습니다 | + +경계값은 설정 응답의 `_meta.limits` 로 함께 내려오며(`advanced_pagination_result_cap_min` 등), +화면 입력 칸의 min/max 와 저장 검증이 같은 값을 공유합니다. + +상한을 넘긴 목록의 응답 형태는 [pagination.md](../pagination.md) 를 참고하세요. + ## TL;DR (5초 요약) ```text diff --git a/docs/backend/core-config.md b/docs/backend/core-config.md index 2abd44be..02178263 100644 --- a/docs/backend/core-config.md +++ b/docs/backend/core-config.md @@ -29,16 +29,30 @@ ## 파일 구조 개요 -`config/core.php`는 3개 최상위 키로 구성됩니다: +`config/core.php`는 최상위 키로 구성되며, 이 문서는 그중 권한·역할·메뉴를 다룹니다. +나머지 키는 각자의 규정 문서가 소유합니다. ```php return [ - 'permissions' => [...], // 코어 권한 정의 - 'roles' => [...], // 코어 역할 정의 - 'menus' => [...], // 코어 메뉴 정의 + // 이 문서가 다루는 범위 + 'permissions' => [...], // 코어 권한 정의 + 'roles' => [...], // 코어 역할 정의 + 'menus' => [...], // 코어 메뉴 정의 + + // 다른 문서가 소유하는 키 + 'settings_limits' => [...], // 관리자 환경설정 입력 한계값 + 'pagination' => [...], // 목록 한계값 기본값 → pagination.md + 'search' => [...], // DBMS 별 부분일치 연산자 → search-system.md + 'notification_definitions' => [...], // 알림 정의 → notification-system.md + 'identity_policies' => [...], // 본인인증 정책 → identity-policies.md + 'identity_messages' => [...], // 본인인증 메시지 → identity-messages.md + 'identity_purposes' => [...], // 본인인증 목적 → identity-policies.md ]; ``` +키를 새로 추가할 때는 그 키를 설명하는 규정 문서를 함께 지정하고 이 목록에 한 줄을 더합니다. +목록이 실제 구조와 어긋나면 "여기 없으니 없는 값" 으로 읽혀 중복 키가 생깁니다. + > **변경 이력 (7.0.0-beta.2)**: `mail_templates` 키는 알림 시스템 통합(#146)으로 제거되었습니다. 코어 메일 템플릿은 `notification_definitions` + `notification_templates` 로 이전되었으며, `database/seeders/NotificationDefinitionSeeder` 가 시드 데이터를 직접 보유합니다. --- diff --git a/docs/backend/pagination.md b/docs/backend/pagination.md new file mode 100644 index 00000000..4bb2058a --- /dev/null +++ b/docs/backend/pagination.md @@ -0,0 +1,344 @@ +# 대용량 목록 페이지네이션 (Pagination) + +> **관련 문서**: [Service-Repository 패턴](./service-repository.md) | [API 리소스](./api-resources.md) | [검색 시스템](./search-system.md) + +--- + +## TL;DR (5초 요약) + +```text +1. 총 건수만 상한을 받는다 — 상한 이하면 정확, 초과면 "이상"(total_relation=at_least) +2. "다음" 이동은 상한과 무관하게 끝까지 열려 있다 (per_page + 1 실측) +3. 계산이 불가능해지는 것은 마지막 페이지 번호 하나뿐 → last_page = null +4. 최신순 등 실제 컬럼 정렬은 커서(키셋)로 전환 가능, 관련도순은 offset 유지 +5. 한계값은 관리자 환경설정 > 고급이 SSoT — 확장은 필터 훅으로만 조정 +``` + +--- + +## 목차 + +- [왜 상한을 두는가](#왜-상한을-두는가) +- [상한과 페이지 이동의 관계](#상한과-페이지-이동의-관계) +- [구성 요소](#구성-요소) +- [저장소에서 쓰는 법](#저장소에서-쓰는-법) +- [상한 없는 전량 조회](#상한-없는-전량-조회) +- [건수만 필요할 때](#건수만-필요할-때) +- [응답 계약](#응답-계약) +- [커서(키셋) 페이지네이션](#커서키셋-페이지네이션) +- [한계값 설정](#한계값-설정) +- [화면 쪽 규약](#화면-쪽-규약) +- [체크리스트](#체크리스트) + +--- + +## 왜 상한을 두는가 + +목록 화면은 보통 한 페이지 분량만 보여 주면서 총 건수를 함께 표시한다. 그 총 건수를 구하는 +`COUNT(*)` 는 조건에 맞는 행을 **전부** 세야 하므로, 매칭이 많을수록 한 페이지를 그리는 데 +드는 비용이 커진다. 검색처럼 매칭이 수십만 건에 이를 수 있는 목록에서는 페이지 조회보다 +건수 집계가 더 비싸지는 역전이 일어난다. + +한편 사용자가 총 건수에서 실제로 얻는 정보는 "많다/적다" 와 "마지막 페이지가 어디쯤인가" +정도다. 12,842건과 "10,000건 이상" 은 실질적으로 같은 정보를 준다. 그래서 **세는 범위에만 +상한을 두고**, 상한을 넘으면 정확한 수 대신 하한을 보고한다. + +### 상한이 줄여 주지 못하는 것 — FULLTEXT 술어 + +상한은 **세는 행 수**를 줄인다. 그러나 조건을 만족하는 행을 찾는 일 자체는 줄이지 못한다. +FULLTEXT(`MATCH ... AGAINST`) 검색에서 이 차이가 크게 드러난다. + +30만 행 전부가 한 토큰에 매칭되는 조건으로 실측한 결과다. + +| 쿼리 | 소요 | +|------|------| +| 상한 COUNT (`LIMIT 10001` 로 감싼 파생 테이블) | 588.96초 | +| 상한 없는 COUNT | 560.48초 | +| 페이지 조회 (`LIMIT 21`) | 594.76초 | + +세 경우가 사실상 같다. InnoDB 전문 검색은 매칭 집합을 먼저 구성하고 그 뒤에 `LIMIT` 을 +적용하므로, 몇 건을 돌려주든 매칭 집합을 만드는 비용은 동일하게 든다. 같은 조건에서 +실행계획을 보면 상한 COUNT 는 파생 테이블로 감싸는 과정에서 FULLTEXT 인덱스 사용 +계획을 잃고 전체 스캔이 되기도 한다. + +따라서 검색 목록에서 상한이 주는 이득은 **PHP 메모리·전송량·중복 실행 제거**이며, +데이터베이스가 매칭을 찾는 시간은 상한으로 해결되지 않는다. 이 축이 문제라면 전용 검색 +엔진(`core.search.engine_drivers` 훅으로 교체)을 쓰는 것이 남은 경로다. + +토큰 결합을 OR 에서 AND 로 바꿔 매칭 집합 자체를 줄이는 안은 **채택하지 않는다**. 입력한 +단어 중 하나만 든 문서가 결과에서 통째로 사라지는 손실이 성능 이득보다 크다고 판단했다 +(한글은 ngram 파서가 2글자 단위로 토큰을 쪼개 축소 폭이 특히 크다). 상세는 +[search-system.md "검색어 토큰은 OR 로 결합한다"](search-system.md) 참조. + +측정 한계도 함께 적는다. 위 수치는 한 토큰이 전 행에 걸리도록 만든 인위적 조건이고, +단일 개발 장비 기준이다. 매칭이 적은 일반적인 검색어에는 해당하지 않는다. +`innodb_ft_result_cache_limit` 에 대한 메모리 증가분은 기준선을 확보하지 못해 확정하지 +못했다 — 확정된 것은 위 시간 축뿐이다. + +--- + +## 상한과 페이지 이동의 관계 + +총 건수 상한과 페이지 이동 범위는 **별개 결정**이다. 묶으면 필요 없이 기능이 깎인다. + +| 항목 | 상한을 넘었을 때 | +|---|---| +| 총 건수 | "N건 이상" 으로 표기 (`total_relation = at_least`) | +| 다음 페이지 이동 | **그대로 가능** — `per_page + 1` 조회로 정확히 판정 | +| 이전 페이지 이동 | 그대로 가능 | +| 마지막 페이지 점프 | **감춤** — 총 건수를 알아야 계산되는 유일한 값 | + +마지막 페이지 점프를 감추는 것은 기능 축소가 아니라 **계산 불가 사실의 정직한 표시**다. +그 버튼은 대량 매칭에서 초대형 OFFSET 을 실행하므로, 눌러도 정상 응답이 오지 않는 경우가 많다. + +--- + +## 구성 요소 + +| 구성 요소 | 위치 | 역할 | +|---|---|---| +| `TotalRelation` | `app/Enums/TotalRelation.php` | 총 건수 정확도 (`Exact` / `AtLeast`) | +| `BoundedPaginator` | `app/Support/Query/BoundedPaginator.php` | `per_page + 1` 조회 + 상한 파생 테이블 COUNT | +| `BoundedPage` | `app/Support/Query/BoundedPage.php` | 페이지 결과. 표준 페이지네이터 인터페이스를 그대로 만족 | +| `KeysetPaginator` | `app/Support/Query/KeysetPaginator.php` | 커서 인코딩/디코딩 표준 | +| `PaginationLimits` | `app/Support/Query/PaginationLimits.php` | 한계값 해석 (설정 → 필터 훅) | +| `BoundedTotalAware` | `app/Contracts/Pagination/BoundedTotalAware.php` | 정확도를 밝히는 페이지 결과 계약 | + +`BoundedPage` 는 `LengthAwarePaginator` 를 상속한다. 기존 컬렉션·리소스·컨트롤러가 전부 +페이지네이터 인터페이스에 맞춰져 있으므로, 별도 타입을 새로 정의하면 소비 지점마다 언랩 +코드가 생긴다. 상속하면 기존 코드가 그대로 받고 정확도 메타까지 자동으로 얻는다. + +--- + +## 저장소에서 쓰는 법 + +```php +use App\Support\Query\BoundedPaginator; +use App\Support\Query\PaginationLimits; + +public function searchByKeyword(string $keyword, int $perPage = 10, int $page = 1): BoundedPage +{ + $query = $this->model->newQuery() + ->where('published', true) + ->orderBy('created_at', 'desc') + // 전순서 보장 — 정렬 컬럼이 비고유면 페이지 경계에서 행이 겹치거나 샌다 + ->orderBy('id', 'desc'); + + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + page: $page, + resultCap: PaginationLimits::resultCap('search'), + // 목록이 실제로 쓰는 컬럼만 명시 (프루닝) + columns: ['id', 'slug', 'title', 'created_at'], + ); +} +``` + +건수만 필요한 경우(탭 배지 등)는 조회 없이 집계만 한다. + +```php +[$total, $relation] = BoundedPaginator::countWithCap($query, PaginationLimits::resultCap('search')); +``` + +`GROUP BY` 가 걸린 쿼리도 파생 테이블 안에서 그룹이 만들어지므로 **그룹 수**가 그대로 센다. +표준 `count()` 가 그룹별 건수를 돌려주는 문제가 없다. + +### 받는 입력은 표준 `paginate()` 와 같다 + +Eloquent 빌더뿐 아니라 쿼리 빌더(`DB::table(...)`)와 **관계**(`$user->notifications()`)도 +그대로 넘길 수 있다. + +```php +// 관계를 그대로 넘겨도 된다 — 소속 조건은 그 밑 빌더에 이미 들어가 있어 보존된다 +BoundedPaginator::paginate($user->notifications(), perPage: 15, resultCap: $cap); +``` + +이 폭은 **좁히면 안 된다.** 이 계약은 기존 `->paginate()` 호출을 대체하려고 만든 것이라, +받는 형태가 표준보다 좁으면 바꾸는 것만으로 멀쩡하던 목록이 죽는다. 관계에서 `->paginate()` +가 되는 이유는 관계가 빌더로 호출을 전달해 주기 때문인데, 정적 메서드는 그 전달을 받지 +못하므로 계약이 직접 이해해야 한다. + +`PaginatesWithDeferredJoin` 은 빌더와 관계를 받는다. 이쪽은 모델의 키 컬럼과 eager load 를 +다루므로 Eloquent 가 전제이며, 쿼리 빌더는 받지 않는다. + +### 하지 말 것 + +| ❌ 금지 | ✅ 올바른 사용 | +|---|---| +| 같은 술어로 `count()` 한 번, `get()` 한 번 | `BoundedPaginator::paginate()` 한 번 | +| `paginate(PHP_INT_MAX)` 후 PHP `array_slice` | 실제 `page`/`per_page` 를 저장소까지 하달 | +| `forPage($page, $perPage + 1)` | offset 은 `per_page` 기준으로 따로 계산 | +| 총 건수를 모르는데 `last_page` 를 1 로 채움 | `null` 로 내보내 화면이 감추게 한다 | +| 정렬 마지막이 비고유 컬럼 | 기본키를 덧붙여 전순서 보장 | +| 컬렉션에서 `'pagination' => [...]` 를 손으로 조립 | `...$this->paginationMeta()` — 형태를 스스로 판정한다 | + +--- + +## 상한 없는 전량 조회 + +`->get()` / `->pluck()` 자체가 문제인 것은 아니다. 문제는 **결과 크기가 데이터 증가에 +비례해 커지는데 아무 상한도 없는** 경우다. 그런 조회는 개발 데이터에서 늘 빠르고, 운영 +데이터가 쌓인 뒤에야 메모리와 응답 시간을 함께 무너뜨린다. 예외도 경고도 없이 느려지기만 +하므로 테스트로는 드러나지 않는다. + +한 문장 안에 결과 크기를 묶는 근거가 하나는 있어야 한다. + +| 상황 | 쓸 것 | +|---|---| +| 화면에 목록으로 보여 준다 | `BoundedPaginator::paginate()` / `paginateWithDeferredJoin()` | +| 전량을 순회해 처리한다 | `chunkById()` / `lazyById()` (키셋 — OFFSET 밀림이 없다) | +| 몇 건만 필요하다 | `limit()` / `take()` | +| 이미 좁혀진 키 집합이다 | `whereIn($key, $ids)` / `find()` | + +행 수가 사용량이 아니라 **운영자의 등록 수**에 묶이는 설정성 테이블(역할·언어팩·정책 등)은 +예외다. 그 경우 근거를 코드에 남긴다. + +```php +// audit:allow query-unbounded-get reason: 역할은 운영자가 정의한 수만큼만 존재한다 (회원 수와 무관) +return Role::where('is_active', true)->orderBy('id')->get(); +``` + +--- + +## 건수만 필요할 때 + +목록을 조회하지 않고 건수만 필요한 자리(탭 배지, 요약 수치)는 `BoundedPaginator::count()` +를 쓴다. `int` 하나만 돌려주면 **상한에 걸려 잘린 값과 정확히 센 값이 구분되지 않는다** — +잘린 10,000 이 "정확히 10,000 건" 으로 화면에 나가는 것은 오류로 드러나지 않고 그냥 틀린 +숫자로만 보인다. + +```php +$count = BoundedPaginator::count($query, PaginationLimits::resultCap('search')); + +return $count->toArray(); +// ['total' => 10000, 'total_relation' => 'at_least', 'total_is_exact' => false, 'result_cap' => 10000] +``` + +여러 카테고리의 건수를 합칠 때는 **하나라도 부정확하면 합계도 부정확**하다. 정확한 +카테고리 몇 개를 더해 봐야 전체가 정확해지지 않는다. 반대로 특정 탭 하나만 보는 +중이라면 그 카테고리의 정확도만 본다 — 다른 카테고리가 잘렸다는 이유로 정확한 값을 +"이상" 이라고 말하지 않는다. + +--- + +## 응답 계약 + +`BaseApiCollection::paginationMeta()` 가 페이지 결과의 형태를 스스로 판정해 그 형태가 실제로 +아는 값만 내보낸다. 모르는 값을 0 이나 1 로 채우면 화면이 그것을 사실로 읽는다. + +| 형태 | 예 | `total` | `last_page` | 추가 필드 | +|---|---|---|---|---| +| 전체건수형 | `paginate()` | 정확 | 정확 | — | +| 상한형 | `BoundedPage` | 정확 또는 하한 | 하한이면 `null` | `total_relation` `total_is_exact` `result_cap` | +| 단순형 | `simplePaginate()` | 없음 | 없음 | — | +| 커서형 | `cursorPaginate()` | 없음 | 없음 | `next_cursor` `prev_cursor` | + +표준 `paginate()` 를 쓰는 기존 컬렉션의 응답은 필드 단위로 이전과 완전히 같다. + +컬렉션이 이 블록을 손으로 조립하면 두 가지가 함께 깨진다. 커서 결과에는 `total()` 과 +`lastPage()` 가 없어 **그 요청만 500** 이 되고, 상한형에서는 정확도 필드가 빠져 잘린 건수가 +정확한 값처럼 화면에 나간다. 둘 다 표준 `paginate()` 만 쓰던 시절에는 드러나지 않던 형태라, +목록 하나를 상한형이나 커서형으로 바꾸는 순간 처음 나타난다. + +```json +{ + "pagination": { + "current_page": 3, + "per_page": 20, + "from": 41, + "to": 60, + "has_more_pages": true, + "last_page": null, + "total": 10000, + "total_relation": "at_least", + "total_is_exact": false, + "result_cap": 10000 + } +} +``` + +--- + +## 커서(키셋) 페이지네이션 + +OFFSET 방식은 건너뛸 행을 실제로 읽어야 해서 페이지가 깊어질수록 느려진다. 커서 방식은 +직전 페이지 마지막 행의 정렬 키를 WHERE 절 경계로 삼으므로 깊이와 무관하게 일정하다. + +```php +use App\Support\Query\KeysetPaginator; + +$page = KeysetPaginator::paginate( + $query, + perPage: 20, + sortKeys: [['created_at', 'desc']], + uniqueKey: 'id', + cursor: $request->query(KeysetPaginator::CURSOR_PARAM), +); +``` + +### 관련도순에는 적용할 수 없다 + +커서는 정렬 키를 WHERE 절 경계로 쓴다. 따라서 정렬 키가 **실제 컬럼**이어야 한다. +FULLTEXT 관련도 점수(`_ft_score`)는 행마다 계산되는 값이고 컬럼이 아니므로 경계로 쓸 수 없다. + +`sort=relevance` 는 이 제약 때문에 OFFSET 을 유지한다. 판정은 +`KeysetPaginator::supports($sortKeys, $columnWhitelist)` 가 담당하며, 허용 컬럼 목록 밖의 +정렬 키가 하나라도 있으면 `false` 를 돌려준다. + +깨진 커서 문자열은 예외 대신 첫 페이지로 처리한다. 사용자가 주소를 손으로 고쳤다는 이유로 +목록 화면이 오류를 띄우게 두지 않는다. + +--- + +## 한계값 설정 + +| 설정 | 기본값 | 의미 | +|---|---|---| +| `pagination.result_cap` | 10000 | 총 건수를 정확히 세는 상한 (0 = 무제한) | +| `pagination.max_page` | 1000 | 직접 요청할 수 있는 페이지 번호 상한 (0 = 무제한) | + +해석 순서는 **관리자 환경설정 > 고급 → `config/core.php` 기본값 → 확장 필터 훅** 이다. +해석은 `PaginationLimits` 한 곳에서만 하며, 확장은 값을 리터럴로 다시 적지 않고 훅으로 조정한다. + +```php +HookManager::addFilter('core.pagination.filter_result_cap', function (int $cap, ?string $context) { + return $context === 'search' ? 50000 : $cap; +}); +``` + +`max_page` 는 남용 차단용이다. 정상 탐색은 `has_more_pages` 와 커서로 계속 열려 있고, +이 값은 임의의 큰 페이지 번호를 직접 던져 초대형 OFFSET 을 만드는 것만 막는다. + +--- + +## 화면 쪽 규약 + +`Pagination` 컴포넌트는 세 가지 입력으로 동작한다. + +| Prop | 의미 | +|---|---| +| `totalPages` | 마지막 페이지. **`null` 이면 페이지 번호 목록과 마지막 페이지 점프가 사라진다** | +| `hasMorePages` | 다음 페이지 존재 여부. 총 건수를 몰라도 정확하다 | +| `showFirst` / `showLast` | 첫/마지막 버튼을 따로 제어 (미지정 시 `showFirstLast` 를 따름) | + +총 건수 표기는 정확도에 따라 문구가 갈린다. + +```text +정확: 총 1,234건 +하한: 총 10,000건 이상 (+ 검색어를 더 구체적으로 입력하면 정확한 건수를 볼 수 있다는 안내) +``` + +세지 않은 값을 정확한 것처럼 말하지 않는다. + +--- + +## 체크리스트 + +- [ ] 목록 조회가 같은 술어를 `count()` + `get()` 으로 두 번 실행하지 않는가 +- [ ] `paginate()` 에 조회 컬럼을 명시했는가 (`['*']` 방치 금지) +- [ ] 정렬 마지막에 기본키를 덧붙여 전순서를 보장했는가 +- [ ] 총 건수를 모를 때 `last_page` 를 `null` 로 내보내는가 +- [ ] 화면이 `total_is_exact` 를 보고 문구를 바꾸는가 +- [ ] 상한값을 리터럴로 적지 않고 `PaginationLimits` 를 거치는가 +- [ ] 커서를 쓴다면 정렬 키가 전부 실제 컬럼인가 diff --git a/docs/backend/search-system.md b/docs/backend/search-system.md index e8a8c479..c807f327 100644 --- a/docs/backend/search-system.md +++ b/docs/backend/search-system.md @@ -24,10 +24,11 @@ 2. [FulltextSearchable 인터페이스](#fulltextsearchable-인터페이스) 3. [검색 엔진 드라이버](#검색-엔진-드라이버) 4. [확장 포인트](#확장-포인트) -5. [마이그레이션](#마이그레이션) -6. [AsUnicodeJson 캐스트](#asunicodejson-캐스트) -7. [환경설정](#환경설정) -8. [관련 문서](#관련-문서) +5. [검색 목록의 페이지 이동](#검색-목록의-페이지-이동) +6. [마이그레이션](#마이그레이션) +7. [AsUnicodeJson 캐스트](#asunicodejson-캐스트) +8. [환경설정](#환경설정) +9. [관련 문서](#관련-문서) --- @@ -188,6 +189,93 @@ DBMS에 따라 자동 분기: - MySQL/MariaDB: `WHERE MATCH(\`content\`) AGAINST(? IN BOOLEAN MODE)` - 그 외: `WHERE content LIKE '%keyword%'` +### 키워드 술어는 활성 엔진이 만든다 + +Scout 의 `Model::search()` 는 "엔진이 결과를 돌려준다" 모델입니다. 그래서 결과를 받은 뒤 그 ID +로 DB 를 다시 조회해야 하고, 매칭이 많으면 ID 전량을 메모리에 올려 무제한 `IN (...)` 을 만들게 +됩니다. 페이지네이션·조인·필터를 DB 에 남긴 채 키워드 조건만 얹으려면 **엔진에게 술어를 달라고 +요청할 통로**가 따로 필요합니다. + +그 통로가 `App\Search\Contracts\KeywordPredicateProvider` 이고, 해석은 +`App\Search\KeywordSearch` 가 단독으로 수행합니다. + +```php +use App\Search\KeywordSearch; + +// 컬럼들을 하나의 조건으로 함께 평가 (복합 인덱스가 있는 테이블) +KeywordSearch::apply($query, ['title', 'content'], $keyword); + +// 컬럼마다 따로 평가해 OR 로 묶기 (컬럼별 단일 인덱스만 있는 테이블) +KeywordSearch::applyAny($query, ['name', 'description'], $keyword); +``` + +**저장소는 구체 엔진 클래스를 지목하지 않습니다.** 지목하는 순간 플러그인이 등록한 검색 엔진은 +호출될 기회 자체를 잃고, 오류도 경고도 없이 그 사이트의 검색만 조용히 다른 방식으로 동작합니다. + +활성 엔진이 이 계약을 구현하지 않으면 부분일치로 내려가며, 그 사실이 기록에 남습니다 — 폴백은 +정상 동작처럼 보이므로 기록하지 않으면 "검색이 느리고 관련도가 이상하다" 는 증상만 남습니다. + +#### 페이지네이션은 DB 가, 상한은 엔진이 + +엔진에게 페이지 번호를 넘기지 않습니다. 엔진이 자기 순서로 한 페이지 분량만 돌려주면, 그 뒤에 +적용되는 DB 필터(분류·전시상태·조인)에 일부가 탈락해 페이지가 비고, DB 정렬과 엔진의 관련도 +순서가 달라 페이지 경계도 어긋나기 때문입니다. + +엔진이 책임지는 것은 **"얼마까지 돌려줄 것인가"** 하나이며, 그 값은 `KeywordSearchContext` 로 +전달됩니다. 외부 검색 서버를 쓰는 엔진은 자기 서버에서 키 집합을 받아 조건으로 붙이는데, +상한을 지키지 않으면 매칭이 큰 검색어에서 그 집합 자체가 메모리 폭발이 됩니다. + +```php +KeywordSearch::applyAny($query, ['name', 'description'], $keyword, 'and', 'search'); +// ↑ 상한 해석 컨텍스트 +``` + +상한은 목록 총 건수 상한(`PaginationLimits::resultCap()`)과 **같은 값**을 씁니다. 두 기준이 +갈라지면 "엔진이 돌려준 건수" 와 "화면이 보고하는 총 건수" 가 서로 다른 근거를 갖게 됩니다. +상한에 걸려 잘린 경우 그 이상은 도달 불가이고 총 건수는 "이상" 으로 보고됩니다. + +이 의무는 정적 검사로 강제할 수 없습니다 — 외부 엔진 코드는 이 저장소 밖입니다. 코어가 할 수 +있는 것은 **값을 손에 쥐어 주는 것**까지이며, 그 값이 실제로 도달하는지는 계약 테스트가 +고정합니다. + +#### 전문검색을 제공하지 않는 DBMS + +부분일치 경로는 임시방편이 아닙니다. 전문검색을 제공하지 않는 DBMS 로 설치된 사이트에서는 +**이것이 정상 검색 경로**이므로 이식성을 갖춥니다. + +- 검색어의 `%` `_` `\` 를 escape 해 이용자가 입력한 글자 그대로 찾습니다. +- "대소문자를 구분하지 않는 부분일치" 를 어떤 연산자로 쓰는지는 DBMS 마다 다릅니다. 그 표는 + `config('core.search.like_operators')` 에 **선언형으로** 있고, 표에 없는 드라이버는 + `like_operator_default` 를 씁니다. 코어 코드에는 드라이버명을 적지 않습니다 — 적으면 + DBMS 가 공식 지원 목록에 추가될 때마다 코어를 고쳐야 합니다. +- 확장은 `core.search.like_operators` 필터 훅으로 새 DBMS 의 연산자를 선언합니다. + +```php +HookManager::addFilter('core.search.like_operators', function (array $operators) { + $operators['somedb'] = 'imatch'; + + return $operators; +}); +``` + +해당 DBMS 의 진짜 전문검색(예: PostgreSQL `tsvector`)은 그 엔진을 `core.search.engine_drivers` +로 등록하고 `KeywordPredicateProvider` 를 구현하면 코어 수정 없이 이 경로를 그대로 탑니다. + +### 검색어 토큰은 OR 로 결합한다 (확정) + +`sanitizeBooleanModeKeyword()` 는 BOOLEAN MODE 연산자를 제거한 뒤 남은 토큰을 각각 +따옴표로 묶어 **공백으로 잇는다**. BOOLEAN MODE 에서 공백 결합은 OR 이므로, "빨간 운동화" +는 "빨간" 이 든 행과 "운동화" 가 든 행을 **모두** 매치한다. + +각 토큰에 `+` 를 붙여 AND 로 바꾸면 매칭 집합 자체가 줄어 대용량에서 검색 시간이 줄어들 +여지가 있다. 그럼에도 **OR 을 유지한다** — 이용자가 입력한 단어 중 하나만 든 문서가 +결과에서 통째로 사라지는 것은 성능과 맞바꿀 수 없는 손실이기 때문이다. 한글은 ngram 파서가 +2글자 단위로 토큰을 쪼개므로 AND 전환의 결과 축소 폭이 특히 크다. + +성능 축이 문제가 되면 토큰 결합 방식이 아니라 전용 검색 엔진(`core.search.engine_drivers` +훅으로 교체)으로 해결한다. 상한 COUNT 가 매칭 시간을 줄이지 못하는 이유와 실측치는 +[pagination.md](pagination.md) 참조. + --- ## 확장 포인트 @@ -481,8 +569,77 @@ SCOUT_QUEUE=false --- +## 검색 목록의 페이지 이동 + +검색 결과는 컬렉션 리소스를 거치지 않고 리스너가 배열을 만들어 넘긴다. 그 조립과 판정을 +도메인마다 손으로 하면 검색 모듈 수만큼 같은 코드가 복제되고, 나중에 필드가 하나 늘 때 +어떤 화면은 받고 어떤 화면은 못 받는다. **코어가 계약을 소유하고 확장은 선언만 한다.** + +| 코어가 소유하는 것 | 위치 | +|---|---| +| 커서 적용 가능 여부 판정 | `App\Search\SearchPagePolicy` | +| 카테고리 응답 페이로드 조립 | `App\Search\SearchCategoryPayload` | +| 커서 인코딩·디코딩 | `App\Support\Query\KeysetPaginator` | + +확장이 선언하는 것은 **정렬 이름이 어떤 실제 컬럼인가** 둘뿐이다. + +```php +/** 정렬 이름 → [실제 컬럼, 방향]. 여기에 없는 이름은 커서를 쓰지 않는다. */ +public const SEARCH_SORT_MAP = [ + 'latest' => ['created_at', 'desc'], + 'oldest' => ['created_at', 'asc'], +]; + +/** 커서 경계로 쓸 수 있는 실제 컬럼 */ +public const SEARCH_CURSOR_COLUMNS = ['created_at']; +``` + +서비스는 규칙을 다시 쓰지 않고 코어에 판정을 위임한다. 적용할 수 없는 정렬이면 `null` 을 +돌려주고, 호출자는 기존 offset 경로를 그대로 쓴다. + +```php +$sortKeys = SearchPagePolicy::sortKeys($sort, self::SEARCH_SORT_MAP); + +if (! SearchPagePolicy::usesCursor($cursor, $sortKeys, self::SEARCH_CURSOR_COLUMNS)) { + return null; +} + +return $this->repository->searchByKeywordWithCursor($keyword, $sortKeys, $perPage, $cursor); +``` + +### 관련도순에는 커서를 쓸 수 없다 + +커서는 정렬 키를 WHERE 절 경계로 삼으므로 정렬 키가 **실제 컬럼**이어야 한다. 관련도순은 +전문 검색 점수라는 계산값으로 정렬하므로 경계로 쓸 수 없고, 그 정렬에서는 offset 을 +유지한다. 이 판정은 선언에 그 이름이 없는 것으로 자연히 이루어진다. + +### 응답 키 집합은 세 형태가 모두 같다 + +offset·커서·건수전용 세 응답은 채워지는 값만 다르고 키 구성은 동일하다. 화면이 분기 없이 +같은 키를 읽을 수 있어야 하기 때문이다. + +| 키 | offset | 커서 | 건수전용 | +|---|---|---|---| +| `total` / `total_relation` / `total_is_exact` / `result_cap` | 채움 | 채움(별도 집계) | 채움 | +| `last_page` | 상한 초과 시 `null` | 언제나 `null` | `null` | +| `has_more_pages` | 실측 | 실측 | `false` | +| `next_cursor` / `prev_cursor` | `null` | 채움 | `null` | +| `items` | 채움 | 채움 | 빈 배열 | + +커서 응답에 `last_page` 가 없는 것은 결함이 아니라 **커서 방식에 마지막 페이지 번호라는 +개념이 없기 때문**이다. 화면은 그때 마지막 페이지 점프만 감추고 "다음" 이동은 유지한다. + +### 탭 배지에도 정확도가 따라간다 + +배지는 숫자 하나만 그리므로, 상한에 걸려 잘린 값이 정확한 것처럼 나가도 오류로 드러나지 +않고 그냥 틀린 숫자로만 보인다. 코어가 카테고리별 정확도를 `counts_are_exact` 로 일괄 +제공하며, 화면은 정확하지 않은 배지에 "이상" 표시를 붙인다. + +--- + ## 관련 문서 +- [대용량 목록 페이지네이션](pagination.md) - 상한 COUNT·커서 적용 기준 - [Service-Repository 패턴](service-repository.md) - Repository에서 whereFulltext() 사용 - [훅 시스템](../extension/hooks.md) - core.search.engine_drivers 필터 훅 - [데이터베이스 가이드](../database-guide.md) - 마이그레이션 규칙 diff --git a/docs/backend/service-repository.md b/docs/backend/service-repository.md index 8206d4cc..9cb844f3 100644 --- a/docs/backend/service-repository.md +++ b/docs/backend/service-repository.md @@ -942,11 +942,25 @@ class OrderRepository implements OrderRepositoryInterface `paginate` / `simplePaginate` / 캐시 total 주입은 인자로 구분한다 — inner 쿼리는 동일하고 COUNT 수행 여부와 반환 클래스만 달라진다. -| 호출자 상황 | `$simple` | `$total` | 반환 | -| ------ | ------ | ------ | ------ | -| 일반 목록 | `false` | `null` | `LengthAwarePaginator` (COUNT 1회) | -| COUNT 를 피하고 다음 페이지 유무만 필요 | `true` | — | `Paginator` | -| 총 건수를 이미 캐시해 둠 | `false` | 캐시값 | `LengthAwarePaginator` (COUNT 없음) | +| 호출자 상황 | `$simple` | `$total` | `$resultCap` | 반환 | +| ------ | ------ | ------ | ------ | ------ | +| 일반 목록 | `false` | `null` | `null` | `LengthAwarePaginator` (COUNT 1회) | +| COUNT 를 피하고 다음 페이지 유무만 필요 | `true` | — | — | `Paginator` | +| 총 건수를 이미 캐시해 둠 | `false` | 캐시값 | `null` | `LengthAwarePaginator` (COUNT 없음) | +| 계속 쌓이기만 하는 목록 (로그·회원 등) | `false` | `null` | 상한 | `BoundedPage` (상한 COUNT + `per_page + 1` 실측) | + +상한을 지정하면 총 건수는 그 값까지만 세고, 다음 페이지 판정은 `per_page + 1` 실측으로 따로 한다. 계산이 불가능해지는 것은 마지막 페이지 번호 하나뿐이다. 상세는 [pagination.md](pagination.md). + +```php +return $this->paginateWithDeferredJoin( + query: $query, + columns: ['*'], + sort: $sort, + perPage: $perPage, + relations: ['user:id,uuid,name,email'], + resultCap: PaginationLimits::resultCap('admin.activity_logs'), +); +``` 정렬 순서 복원은 `whereIn` + 같은 정렬 스펙 재적용으로 한다. 키 컬럼이 정렬에 포함돼 전순서가 성립하므로 결과가 inner 순서와 동일하다. MySQL 전용 `FIELD()` 는 쓰지 않는다. outer 에 존재하지 않는 표현식(집계·조인 서브쿼리)으로 정렬해 재현이 불가능한 경우에만 `$preserveIdOrder = true` 로 표준 SQL `CASE WHEN` 경로를 쓴다. diff --git a/docs/requirements.md b/docs/requirements.md index 047a326e..9cfd6a97 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -8,7 +8,7 @@ 1. PHP 8.2+ 필수 2. MySQL 8.0+ 또는 MariaDB 10.3+ (utf8mb4, utf8mb4_unicode_ci) 3. PHP 필수 모듈 30개 (ctype, curl, gd, intl, redis, imagick 등) -4. 디스크 용량: 최소 700MB / 권장 2GB+ +4. 하드웨어: 최소 2 vCPU·2GB / 권장 4 vCPU·8GB, 디스크 최소 700MB / 권장 2GB+ 5. 프로덕션: HTTPS 필수, Redis 권장, 큐 워커/스케줄러/Reverb 데몬 필요 ``` @@ -174,7 +174,39 @@ sudo chmod -R 755 storage bootstrap/cache vendor modules plugins templates --- -## 3. 디스크 용량 +## 3. 하드웨어 사양 + +### 3.1 CPU · 메모리 + +| 수준 | CPU | 메모리 | 상정 환경 | +|------|-----|--------|----------| +| 최소 | 2 vCPU | 2GB | 설치·기능 확인용. 동시 접속이 거의 없는 개발/검토 환경 | +| 권장 | 4 vCPU | 8GB | 웹서버 + PHP-FPM + MySQL + Redis 를 한 대에 올린 소규모 운영 | +| 분리 구성 | 웹 2 vCPU / DB 2 vCPU | 각 4GB 이상 | 트래픽이 늘면 DB 를 먼저 분리 | + +메모리 배분은 어떤 구성 요소를 같은 서버에 올렸는지에 따라 달라진다. 한 대에 모두 올린 +경우 MySQL 의 `innodb_buffer_pool_size` 가 전체 메모리의 절반을 넘지 않도록 두고, PHP-FPM +자식 프로세스 수 × `memory_limit` 가 남은 메모리를 넘지 않는지 확인한다. + +위 수치는 코어와 번들 확장을 기본 설정으로 운영할 때의 출발점이다. 실제 필요량은 데이터 +규모·동시 접속·설치한 확장에 따라 달라지므로, 운영 전 대상 트래픽으로 직접 측정할 것을 +권한다(`php artisan g7:bench` 로 목록·화면·쓰기·배치 4축을 잰다). + +**이 수치의 근거** + +- **권장 4 vCPU · 8GB** — 사용자가 4 vCPU · 8GB 가상머신에서 수행한 운영 환경 측정 보고 + (`gnuboard/g7#82`)에서 코어와 번들 확장을 함께 올린 구성이 동작한 사양이다. 같은 보고에서 + 대량 데이터의 검색·목록 조회는 이 사양에서도 메모리 압박이 관측되었으므로, "이 사양이면 + 어떤 규모든 충분하다" 는 뜻이 아니라 **한 대 구성의 하한선**으로 읽어야 한다. +- **최소 2 vCPU · 2GB** — 설치와 기능 확인이 가능한 수준으로, 위 보고의 측정 대상이 아니다. + 동시 접속이 있는 운영에는 적합하지 않다. +- **분리 구성** — 위 보고에서 부하가 먼저 걸린 지점이 데이터베이스였다는 관측에 따른 순서 + 제안이다. 분리 시점의 트래픽 임계값은 측정하지 않았다. + +우리가 직접 측정한 범위는 쿼리 실행 횟수와 조회 구조이며, 특정 사양에서의 동시 접속 한계나 +응답 시간은 측정하지 않았다. 그 값이 필요하면 대상 환경에서 직접 재야 한다. + +### 3.2 디스크 용량 | 수준 | 용량 | 포함 범위 | |------|------|----------| diff --git a/lang-packs/_bundled/g7-core-ja/CHANGELOG.md b/lang-packs/_bundled/g7-core-ja/CHANGELOG.md index 347a529f..dcb0e9a2 100644 --- a/lang-packs/_bundled/g7-core-ja/CHANGELOG.md +++ b/lang-packs/_bundled/g7-core-ja/CHANGELOG.md @@ -26,6 +26,9 @@ - 레이아웃 편집기 버전 이력의 "더 보기" 버튼과 변경내용이 너무 클 때의 안내 문구 일본어 번역 추가. - 버전 목록을 한 번에 몇 개까지 가져올지 정하는 값의 입력 안내 문구 일본어 번역 추가. - 통신이 끊겼을 때 표시되는 네트워크 오류 안내 문구 일본어 번역 추가 — 종전에는 내부 식별 문구가 영문 그대로 노출됐습니다. +- 목록·검색의 총 건수 정확도 안내 문구(`pagination.*`) 일본어 번역 추가 — 총 건수를 상한까지만 센 경우의 "N건 이상" 표기와 검색어를 좁히라는 안내가 일본어 로케일에서 자연스럽게 표시됩니다. +- 검색 결과 건수 안내와 검색어 구체화 안내(`search.results_found_at_least`, `search.refine_query_hint`) 일본어 번역 추가. +- 페이지 번호 상한 초과 시의 검증 오류 문구(`validation.page_max`) 일본어 번역 추가. ## [1.0.8] - 2026-08-01 diff --git a/lang-packs/_bundled/g7-core-ja/backend/ja/pagination.php b/lang-packs/_bundled/g7-core-ja/backend/ja/pagination.php new file mode 100644 index 00000000..7e776f7c --- /dev/null +++ b/lang-packs/_bundled/g7-core-ja/backend/ja/pagination.php @@ -0,0 +1,12 @@ + [ + 'exact' => '正確', + 'at_least' => '以上', + ], + 'total_exact' => '合計 :count件', + 'total_at_least' => '合計 :count件以上', + 'result_cap_notice' => '一致する項目が :cap件を超えているため、正確な件数を数えていません。次のページに続けて移動できます。', + 'refine_query_hint' => '検索語をより具体的に入力すると、正確な件数と最後のページを表示できます。', +]; diff --git a/lang-packs/_bundled/g7-core-ja/backend/ja/search.php b/lang-packs/_bundled/g7-core-ja/backend/ja/search.php index 16730df9..a633d797 100644 --- a/lang-packs/_bundled/g7-core-ja/backend/ja/search.php +++ b/lang-packs/_bundled/g7-core-ja/backend/ja/search.php @@ -13,6 +13,7 @@ return [ 'per_page_integer' => 'ページあたりの項目数は数字である必要があります。', 'per_page_min' => 'ページあたりの項目数は1以上である必要があります。', 'per_page_max' => 'ページあたりの項目数は最大100個まで可能です。', + 'page_max' => 'ページ番号は :max 以下である必要があります。検索キーワードをより具体的に入力してください。', ], 'index' => [ 'status' => [ @@ -56,4 +57,7 @@ return [ ], ], ], + 'results_found_at_least' => ':count件以上の検索結果が見つかりました。', + 'result_cap_notice' => '一致する項目が :cap件を超えており、合計件数を正確にカウントしていません。次のページに続行できます。', + 'refine_query_hint' => '検索キーワードをより具体的に入力すると、正確な件数と最後のページを確認できます。', ]; diff --git a/lang-packs/_bundled/g7-core-ja/backend/ja/validation.php b/lang-packs/_bundled/g7-core-ja/backend/ja/validation.php index cba83bff..00d53917 100644 --- a/lang-packs/_bundled/g7-core-ja/backend/ja/validation.php +++ b/lang-packs/_bundled/g7-core-ja/backend/ja/validation.php @@ -977,6 +977,12 @@ return [ 'identity_max_attempts_min' => '最大試行回数は最小1回以上である必要があります。', 'identity_max_attempts_max' => '最大試行回数は最大20回を超えることはできません。', 'sitemap_hreflang_enabled_boolean' => 'Sitemap 多言語代替リンク(hreflang) 設定は true または false 値である必要があります。', + 'pagination_result_cap_integer' => '総件数集計上限は数値である必要があります。', + 'pagination_result_cap_min' => '総件数集計上限は:min以上である必要があります。(0 = 無制限)', + 'pagination_result_cap_max' => '総件数集計上限は:maxを超えることはできません。', + 'pagination_max_page_integer' => 'ページ番号上限は数値である必要があります。', + 'pagination_max_page_min' => 'ページ番号上限は:min以上である必要があります。(0 = 無制限)', + 'pagination_max_page_max' => 'ページ番号上限は:maxを超えることはできません。', ], 'identity_policy' => [ 'key_required' => 'ポリシーキーを入力してください。', diff --git a/lang/en/pagination.php b/lang/en/pagination.php new file mode 100644 index 00000000..c9956cee --- /dev/null +++ b/lang/en/pagination.php @@ -0,0 +1,21 @@ + [ + 'exact' => 'exact', + 'at_least' => 'at least', + ], + + 'total_exact' => ':count results', + 'total_at_least' => 'more than :count results', + + 'result_cap_notice' => 'More than :cap items matched, so the exact total was not counted. You can still move to the next page.', + 'refine_query_hint' => 'Narrow your search terms to see the exact total and jump to the last page.', +]; diff --git a/lang/en/search.php b/lang/en/search.php index 1e7d5ea6..d69b66d8 100644 --- a/lang/en/search.php +++ b/lang/en/search.php @@ -7,6 +7,9 @@ return [ 'empty_keyword' => 'Please enter a search keyword.', 'results_found' => ':count results found.', + 'results_found_at_least' => 'More than :count results found.', + 'result_cap_notice' => 'More than :cap items matched, so the exact total was not counted. You can still move to the next page.', + 'refine_query_hint' => 'Narrow your search terms to see the exact total and jump to the last page.', 'no_results' => 'No results found.', 'view_more' => 'View more', @@ -16,6 +19,7 @@ return [ 'q_max' => 'Search keyword must not exceed 200 characters.', 'page_integer' => 'Page number must be a number.', 'page_min' => 'Page number must be at least 1.', + 'page_max' => 'Page number must not exceed :max. Please narrow your search terms.', 'per_page_integer' => 'Items per page must be a number.', 'per_page_min' => 'Items per page must be at least 1.', 'per_page_max' => 'Items per page must not exceed 100.', diff --git a/lang/en/validation.php b/lang/en/validation.php index 24bd97de..9b9f1dd5 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -906,6 +906,14 @@ return [ 'sql_query_log_required' => 'Please select the SQL query log setting.', 'sql_query_log_boolean' => 'SQL query log must be true or false.', + // List limits + 'pagination_result_cap_integer' => 'The total count cap must be a number.', + 'pagination_result_cap_min' => 'The total count cap must be at least :min. (0 = unlimited)', + 'pagination_result_cap_max' => 'The total count cap may not be greater than :max.', + 'pagination_max_page_integer' => 'The maximum page number must be a number.', + 'pagination_max_page_min' => 'The maximum page number must be at least :min. (0 = unlimited)', + 'pagination_max_page_max' => 'The maximum page number may not be greater than :max.', + // Core update settings 'core_update_github_url_invalid' => 'The GitHub repository URL format is invalid.', 'core_update_github_url_max' => 'The GitHub repository URL may not be greater than 500 characters.', diff --git a/lang/ko/pagination.php b/lang/ko/pagination.php new file mode 100644 index 00000000..d653c6f2 --- /dev/null +++ b/lang/ko/pagination.php @@ -0,0 +1,23 @@ + [ + 'exact' => '정확', + 'at_least' => '이상', + ], + + // 건수 표기 — 상한 이하일 때는 정확한 건수, 초과할 때는 "이상" + 'total_exact' => '총 :count건', + 'total_at_least' => '총 :count건 이상', + + // 상한 초과 안내 — 검색어를 좁히면 정확한 건수를 볼 수 있다 + 'result_cap_notice' => '일치하는 항목이 :cap건을 넘어 총 건수를 정확히 세지 않았습니다. 다음 페이지로 계속 이동할 수 있습니다.', + 'refine_query_hint' => '검색어를 더 구체적으로 입력하면 정확한 건수와 마지막 페이지를 볼 수 있습니다.', +]; diff --git a/lang/ko/search.php b/lang/ko/search.php index 1e802d4c..e59b470d 100644 --- a/lang/ko/search.php +++ b/lang/ko/search.php @@ -7,6 +7,9 @@ return [ 'empty_keyword' => '검색어를 입력해주세요.', 'results_found' => ':count건의 검색 결과를 찾았습니다.', + 'results_found_at_least' => ':count건 이상의 검색 결과를 찾았습니다.', + 'result_cap_notice' => '일치하는 항목이 :cap건을 넘어 총 건수를 정확히 세지 않았습니다. 다음 페이지로 계속 이동할 수 있습니다.', + 'refine_query_hint' => '검색어를 더 구체적으로 입력하면 정확한 건수와 마지막 페이지를 볼 수 있습니다.', 'no_results' => '검색 결과가 없습니다.', 'view_more' => '더보기', @@ -16,6 +19,7 @@ return [ 'q_max' => '검색어는 최대 200자까지 입력 가능합니다.', 'page_integer' => '페이지 번호는 숫자여야 합니다.', 'page_min' => '페이지 번호는 1 이상이어야 합니다.', + 'page_max' => '페이지 번호는 :max 이하여야 합니다. 검색어를 더 구체적으로 입력해 주세요.', 'per_page_integer' => '페이지당 항목 수는 숫자여야 합니다.', 'per_page_min' => '페이지당 항목 수는 1 이상이어야 합니다.', 'per_page_max' => '페이지당 항목 수는 최대 100개까지 가능합니다.', diff --git a/lang/ko/validation.php b/lang/ko/validation.php index bcf3a393..b05a8700 100644 --- a/lang/ko/validation.php +++ b/lang/ko/validation.php @@ -989,6 +989,14 @@ return [ 'sql_query_log_required' => 'SQL 쿼리 로그 설정을 선택해주세요.', 'sql_query_log_boolean' => 'SQL 쿼리 로그는 true 또는 false 값이어야 합니다.', + // 목록 한계값 + 'pagination_result_cap_integer' => '총 건수 집계 상한은 숫자여야 합니다.', + 'pagination_result_cap_min' => '총 건수 집계 상한은 :min 이상이어야 합니다. (0 = 무제한)', + 'pagination_result_cap_max' => '총 건수 집계 상한은 :max 를 초과할 수 없습니다.', + 'pagination_max_page_integer' => '페이지 번호 상한은 숫자여야 합니다.', + 'pagination_max_page_min' => '페이지 번호 상한은 :min 이상이어야 합니다. (0 = 무제한)', + 'pagination_max_page_max' => '페이지 번호 상한은 :max 를 초과할 수 없습니다.', + // 코어 업데이트 설정 'core_update_github_url_invalid' => 'GitHub 저장소 URL 형식이 올바르지 않습니다.', 'core_update_github_url_max' => 'GitHub 저장소 URL은 500자를 초과할 수 없습니다.', diff --git a/tests/Concerns/CountsQueries.php b/tests/Concerns/CountsQueries.php new file mode 100644 index 00000000..f9239558 --- /dev/null +++ b/tests/Concerns/CountsQueries.php @@ -0,0 +1,81 @@ + 실행된 SQL 목록 + */ + protected function captureQueries(\Closure $callback): array + { + $queries = []; + + DB::listen(function (QueryExecuted $query) use (&$queries) { + $queries[] = $query->sql; + }); + + try { + $callback(); + } finally { + // 리스너는 커넥션에 누적되므로, 다음 측정이 이전 기록을 물려받지 않도록 끊는다. + DB::getEventDispatcher()?->forget(QueryExecuted::class); + } + + return $queries; + } + + /** + * 클로저를 실행하며 발생한 쿼리 수를 셉니다. + * + * @param \Closure $callback 측정 대상 + * @return int 실행된 쿼리 수 + */ + protected function countQueries(\Closure $callback): int + { + return count($this->captureQueries($callback)); + } + + /** + * 데이터 규모를 바꿔도 쿼리 수가 늘지 않는지 단언합니다. + * + * 행을 늘리는 일(`$grow`)은 측정 밖에서 수행하고, 측정은 조회(`$measure`)만 감쌉니다. + * + * @param \Closure $measure 측정할 조회 (쿼리 수를 재는 대상) + * @param \Closure $grow 데이터를 늘리는 작업 (측정에 포함되지 않음) + * @param string $context 실패 메시지에 붙일 설명 + */ + protected function assertQueryCountStableAsDataGrows(\Closure $measure, \Closure $grow, string $context = ''): void + { + $before = $this->countQueries($measure); + + $grow(); + + $after = $this->countQueries($measure); + + $label = $context !== '' ? $context.': ' : ''; + + $this->assertLessThanOrEqual( + $before, + $after, + $label."행 수를 늘렸더니 쿼리 수가 {$before} → {$after} 로 늘었다. " + .'조회가 행마다 쿼리를 내고 있다(N+1) — 관계를 eager load 하거나 일괄 조회로 바꾼다' + ); + } +} diff --git a/tests/Feature/Api/Admin/ActivityLogControllerTest.php b/tests/Feature/Api/Admin/ActivityLogControllerTest.php index 1562a911..a8fbb31a 100644 --- a/tests/Feature/Api/Admin/ActivityLogControllerTest.php +++ b/tests/Feature/Api/Admin/ActivityLogControllerTest.php @@ -224,9 +224,21 @@ class ActivityLogControllerTest extends TestCase 'from', 'to', 'has_more_pages', + // 활동 로그는 상한을 건 집계라 총 건수가 잘릴 수 있다. 정확도 필드가 + // 빠지면 잘린 값이 화면에서 정확한 건수로 읽힌다 (실측: 실제 88,792 건이 + // "10000건" 으로 표기). 메타를 손으로 조립하면 이 필드들이 사라지므로 + // 표준 메타(BaseApiCollection::paginationMeta)를 쓰는지 여기서 고정한다. + 'total_relation', + 'total_is_exact', + 'result_cap', ], ], ]); + + $this->assertTrue( + $response->json('data.pagination.total_is_exact'), + '작은 표본에서는 총 건수가 정확해야 합니다.' + ); } public function test_index_data_items_have_correct_fields(): void diff --git a/tests/Feature/Api/Admin/NotificationLogControllerTest.php b/tests/Feature/Api/Admin/NotificationLogControllerTest.php index 42a3977d..cdbacc8f 100644 --- a/tests/Feature/Api/Admin/NotificationLogControllerTest.php +++ b/tests/Feature/Api/Admin/NotificationLogControllerTest.php @@ -67,6 +67,65 @@ class NotificationLogControllerTest extends TestCase ->assertJsonPath('data.pagination.total', 1); } + /** + * 커서 요청이 정상 응답하는지 확인 (#519 회귀) + * + * 이 목록은 커서를 주면 키셋 페이지로 응답한다. 커서 결과에는 총 건수와 마지막 페이지가 + * 없는데, 컬렉션이 페이지네이션 블록을 손으로 조립하면 없는 값을 불러 그 요청만 500 이 + * 된다. 응답 형태를 스스로 판정하는 표준 메타를 쓰는지 여기서 고정한다. + */ + public function test_index_accepts_cursor_request(): void + { + foreach (range(1, 5) as $i) { + NotificationLog::create([ + 'channel' => 'mail', + 'notification_type' => 'test', + 'recipient_identifier' => "c{$i}@test.com", + 'status' => 'sent', + ]); + } + + // 커서 파라미터가 있어야 키셋 경로로 들어간다. 형식이 깨진 값은 첫 페이지로 + // 되돌려 주므로(KeysetPaginator::decode), 진입에는 임의 문자열로 충분하다. + $first = $this->authRequest() + ->getJson('/api/admin/notification-logs?per_page=2&cursor=first'); + + $first->assertStatus(200); + $this->assertCount(2, $first->json('data.data')); + + $pagination = $first->json('data.pagination'); + + // 커서 결과는 총 건수를 세지 않는다 — 없는 값을 채워 내보내지 않아야 한다. + $this->assertArrayNotHasKey('total', $pagination); + $this->assertArrayNotHasKey('last_page', $pagination); + $this->assertArrayHasKey('next_cursor', $pagination, '커서 응답에 다음 커서가 없다'); + + // 받은 커서로 실제 다음 페이지까지 이동되는지 확인 + $second = $this->authRequest() + ->getJson('/api/admin/notification-logs?per_page=2&cursor='.urlencode($pagination['next_cursor'])); + + $second->assertStatus(200); + $this->assertNotEmpty($second->json('data.data')); + } + + /** + * 상한형 목록이 정확도 메타를 함께 싣는지 확인 (#519 — 성능 개선 유지) + */ + public function test_index_carries_total_accuracy_meta(): void + { + NotificationLog::create(['channel' => 'mail', 'notification_type' => 'test', 'recipient_identifier' => 'm@test.com', 'status' => 'sent']); + + $response = $this->authRequest()->getJson('/api/admin/notification-logs?per_page=15'); + + $response->assertStatus(200); + + $pagination = $response->json('data.pagination'); + + $this->assertArrayHasKey('total_relation', $pagination, '상한 계약의 정확도 메타가 사라졌다'); + $this->assertArrayHasKey('total_is_exact', $pagination); + $this->assertArrayHasKey('result_cap', $pagination); + } + /** * 단건 삭제 */ @@ -111,7 +170,7 @@ class NotificationLogControllerTest extends TestCase private function authRequest(): static { return $this->withHeaders([ - 'Authorization' => 'Bearer ' . $this->token, + 'Authorization' => 'Bearer '.$this->token, 'Accept' => 'application/json', ]); } diff --git a/tests/Feature/Middleware/PermissionMiddlewareTest.php b/tests/Feature/Middleware/PermissionMiddlewareTest.php index 38f9af39..9f73b39f 100644 --- a/tests/Feature/Middleware/PermissionMiddlewareTest.php +++ b/tests/Feature/Middleware/PermissionMiddlewareTest.php @@ -7,6 +7,7 @@ use App\Enums\PermissionType; use App\Enums\ScheduleType; use App\Enums\ScopeType; use App\Helpers\PermissionHelper; +use App\Http\Middleware\PermissionMiddleware; use App\Models\Menu; use App\Models\Permission; use App\Models\Role; @@ -48,10 +49,7 @@ class PermissionMiddlewareTest extends TestCase $prop->setValue(null, []); // PermissionMiddleware guest role 캐시 초기화 - $middlewareReflection = new \ReflectionClass(\App\Http\Middleware\PermissionMiddleware::class); - $guestProp = $middlewareReflection->getProperty('guestRoleCache'); - $guestProp->setAccessible(true); - $guestProp->setValue(null, null); + PermissionMiddleware::clearGuestRoleCache(); // 테스트용 admin 권한 생성 $this->adminPermission = Permission::create([ diff --git a/tests/Feature/Notifications/AdminNotificationListTest.php b/tests/Feature/Notifications/AdminNotificationListTest.php new file mode 100644 index 00000000..4e495515 --- /dev/null +++ b/tests/Feature/Notifications/AdminNotificationListTest.php @@ -0,0 +1,150 @@ +notifications()` 라는 **관계**를 페이지네이션한다. 상한 페이지네이션 + * 계약으로 옮기면서 그 계약이 빌더만 받도록 좁게 선언돼 있어, 화면 진입만으로 500 이 + * 났다. 이 경로에 테스트가 하나도 없어 전 계층 테스트가 green 인 채로 통과했다. + * + * 그래서 여기서는 두 가지를 함께 못박는다. + * (1) 관계 입력이 정상 응답으로 이어진다 (회귀 차단) + * (2) 그러면서도 상한 계약의 정확도 메타가 살아 있다 (성능 개선이 되돌아가지 않음) + */ +class AdminNotificationListTest extends TestCase +{ + use RefreshDatabase; + + /** + * 알림 읽기 권한을 가진 관리자를 만듭니다. + * + * @return User 관리자 + */ + private function adminWithNotificationRead(): User + { + $user = User::factory()->create(); + + $role = Role::create([ + 'identifier' => 'notif-admin-'.$user->id, + 'name' => ['ko' => '알림 관리자', 'en' => 'Notification Admin'], + ]); + + foreach (['admin.access', 'core.notifications.read'] as $identifier) { + $permission = Permission::firstOrCreate( + ['identifier' => $identifier], + [ + 'name' => ['ko' => $identifier, 'en' => $identifier], + 'type' => PermissionType::Admin, + ] + ); + $role->permissions()->syncWithoutDetaching([$permission->id]); + } + + $user->roles()->attach($role->id); + + return User::findOrFail($user->id); + } + + /** + * 알림을 만듭니다. + * + * @param User $user 수신자 + * @param int $count 건수 + * @param bool $read 읽음 여부 + */ + private function seedNotifications(User $user, int $count, bool $read = false): void + { + foreach (range(1, $count) as $i) { + $user->notifications()->create([ + 'id' => (string) Str::uuid(), + 'type' => 'test', + 'data' => ['message' => 'n'.$i], + 'read_at' => $read ? now() : null, + ]); + } + } + + /** + * 목록이 200 으로 응답하는지 확인 (관계 입력 회귀 차단) + */ + public function test_admin_notification_list_returns_ok(): void + { + $admin = $this->adminWithNotificationRead(); + $this->seedNotifications($admin, 3); + + $response = $this->actingAs($admin, 'sanctum') + ->getJson('/api/admin/notifications?per_page=15&read=unread'); + + $response->assertOk(); + $this->assertCount(3, $response->json('data.data')); + } + + /** + * 읽음 필터가 적용되는지 확인 + */ + public function test_unread_filter_is_applied(): void + { + $admin = $this->adminWithNotificationRead(); + $this->seedNotifications($admin, 2, read: false); + $this->seedNotifications($admin, 4, read: true); + + $response = $this->actingAs($admin, 'sanctum') + ->getJson('/api/admin/notifications?per_page=15&read=unread'); + + $response->assertOk(); + $this->assertCount(2, $response->json('data.data')); + } + + /** + * 다른 사람의 알림이 섞이지 않는지 확인 (관계의 소속 조건 보존) + */ + public function test_other_users_notifications_are_not_listed(): void + { + $admin = $this->adminWithNotificationRead(); + $other = User::factory()->create(); + + $this->seedNotifications($admin, 2); + $this->seedNotifications($other, 5); + + $response = $this->actingAs($admin, 'sanctum') + ->getJson('/api/admin/notifications?per_page=15'); + + $response->assertOk(); + $this->assertCount(2, $response->json('data.data'), '관계의 소속 조건이 사라져 남의 알림이 섞였다'); + } + + /** + * 상한 계약의 정확도 메타가 응답에 실리는지 확인 (성능 개선 유지) + * + * 회귀를 고치면서 표준 집계로 되돌리면 이 필드가 사라진다. 오류가 없어졌다는 것만으로는 + * 개선이 유지됐다고 말할 수 없다. + */ + public function test_response_carries_total_accuracy_meta(): void + { + $admin = $this->adminWithNotificationRead(); + $this->seedNotifications($admin, 3); + + $response = $this->actingAs($admin, 'sanctum') + ->getJson('/api/admin/notifications?per_page=2'); + + $response->assertOk(); + + $pagination = $response->json('data.pagination'); + + $this->assertIsArray($pagination, '페이지네이션 메타가 없다'); + $this->assertArrayHasKey('total_relation', $pagination, '상한 계약의 정확도 메타가 사라졌다'); + $this->assertArrayHasKey('total_is_exact', $pagination); + $this->assertArrayHasKey('result_cap', $pagination); + $this->assertTrue($pagination['has_more_pages']); + } +} diff --git a/tests/Feature/Performance/DeferredJoinBoundedTotalTest.php b/tests/Feature/Performance/DeferredJoinBoundedTotalTest.php new file mode 100644 index 00000000..f4127ded --- /dev/null +++ b/tests/Feature/Performance/DeferredJoinBoundedTotalTest.php @@ -0,0 +1,194 @@ + $page); + } + + /** + * 테스트 종료 시 페이지 해석기를 되돌립니다. + */ + protected function tearDown(): void + { + Paginator::currentPageResolver(static fn () => 1); + + parent::tearDown(); + } + + /** + * 활동 로그를 원하는 건수만큼 만듭니다. + * + * @param int $count 생성할 건수 + */ + private function seedLogs(int $count): void + { + $user = User::factory()->create(); + + for ($i = 0; $i < $count; $i++) { + ActivityLog::create([ + 'log_type' => 'admin', + 'action' => 'test.action', + 'user_id' => $user->id, + 'description_key' => 'test', + 'ip_address' => '127.0.0.1', + ]); + } + } + + /** + * 상한을 넘긴 목록이 BoundedPage 로 응답하며 마지막 페이지를 감추는지 확인 + */ + public function test_deferred_join_returns_bounded_page_when_over_cap(): void + { + $this->seedLogs(9); + config(['g7_settings.core.pagination.result_cap' => 4]); + + $page = app(ActivityLogRepositoryInterface::class)->getPaginated(['per_page' => 2]); + + $this->assertInstanceOf(BoundedPage::class, $page); + $this->assertSame(4, $page->total()); + $this->assertSame(TotalRelation::AtLeast, $page->totalRelation()); + // 총 건수를 모르면 마지막 페이지 번호는 계산할 수 없다 + $this->assertNull($page->lastPage()); + // 그래도 "다음" 이동은 열려 있어야 한다 + $this->assertTrue($page->hasMorePages()); + $this->assertCount(2, $page->items()); + } + + /** + * 상한 이하면 정확한 총 건수와 마지막 페이지가 그대로 나오는지 확인 + */ + public function test_deferred_join_stays_exact_under_cap(): void + { + $this->seedLogs(5); + config(['g7_settings.core.pagination.result_cap' => 100]); + + $page = app(ActivityLogRepositoryInterface::class)->getPaginated(['per_page' => 2]); + + $this->assertSame(5, $page->total()); + $this->assertSame(3, $page->lastPage()); + $this->assertTrue($page->hasMorePages()); + } + + /** + * 마지막 페이지에서는 "다음" 이 닫히는지 확인 + * + * per_page + 1 실측이 경계에서 어긋나면 마지막 페이지에서도 다음 버튼이 남는다. + */ + public function test_last_page_closes_next_navigation(): void + { + $this->seedLogs(5); + config(['g7_settings.core.pagination.result_cap' => 3]); + + $this->onPage(3); + $page = app(ActivityLogRepositoryInterface::class)->getPaginated(['per_page' => 2]); + + $this->assertCount(1, $page->items()); + $this->assertFalse($page->hasMorePages()); + } + + /** + * 깊은 페이지에서도 offset 이 밀리지 않는지 확인 + * + * offset 을 per_page + 1 배수로 계산하면 페이지가 깊어질수록 경계가 밀려 + * 뒤쪽 페이지에서 행이 통째로 사라진다. + */ + public function test_deep_page_offset_does_not_drift(): void + { + $this->seedLogs(10); + config(['g7_settings.core.pagination.result_cap' => 4]); + + $repository = app(ActivityLogRepositoryInterface::class); + + $seen = []; + for ($p = 1; $p <= 5; $p++) { + $this->onPage($p); + foreach ($repository->getPaginated(['per_page' => 2])->items() as $row) { + $seen[] = $row->id; + } + } + + $this->assertCount(10, $seen); + $this->assertSame(count($seen), count(array_unique($seen))); + } + + /** + * 커서를 주면 키셋 방식으로 응답하는지 확인 + */ + public function test_cursor_request_switches_to_keyset(): void + { + $this->seedLogs(6); + + $repository = app(ActivityLogRepositoryInterface::class); + + // 첫 페이지는 페이지 번호 방식 — 여기서 다음 커서를 얻을 수 없으므로 + // 커서 모드 첫 진입은 빈 문자열이 아닌 "임의의 유효하지 않은 값" 으로 확인한다. + $page = $repository->getPaginated(['per_page' => 2, 'cursor' => 'invalid-cursor']); + + $this->assertInstanceOf(CursorPaginator::class, $page); + // 형식이 깨진 커서는 첫 페이지로 되돌린다 (URL 을 손으로 고쳤다고 오류를 띄우지 않는다) + $this->assertCount(2, $page->items()); + } + + /** + * 커서로 끝까지 훑으면 모든 행이 정확히 한 번씩 나오는지 확인 + */ + public function test_cursor_round_trip_covers_every_row_once(): void + { + $this->seedLogs(7); + + $repository = app(ActivityLogRepositoryInterface::class); + + $seen = []; + $cursor = 'invalid-cursor'; // 첫 진입 (첫 페이지로 해석된다) + + for ($i = 0; $i < 10; $i++) { + /** @var CursorPaginator $page */ + $page = $repository->getPaginated(['per_page' => 2, 'cursor' => $cursor]); + + foreach ($page->items() as $row) { + $seen[] = $row->id; + } + + $next = KeysetPaginator::nextCursor($page); + if ($next === null) { + break; + } + $cursor = $next; + } + + $this->assertCount(7, $seen); + $this->assertSame(count($seen), count(array_unique($seen))); + } +} diff --git a/tests/Feature/Performance/ListQueryCountRegressionTest.php b/tests/Feature/Performance/ListQueryCountRegressionTest.php new file mode 100644 index 00000000..1fabf61a --- /dev/null +++ b/tests/Feature/Performance/ListQueryCountRegressionTest.php @@ -0,0 +1,221 @@ +count(5)->create(); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => User::query()->with('roles')->paginate(50, ['id', 'uuid', 'name', 'email', 'status', 'created_at']), + grow: fn () => User::factory()->count(10)->create(), + context: '관리자 회원 목록', + ); + } + + /** + * 권한 판정: 판정 횟수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 레이아웃 노드 필터링은 노드마다 권한을 묻는다. 판정마다 DB 를 보면 + * 노드 수에 비례해 쿼리가 늘어난다. + */ + public function test_permission_checks_do_not_grow_with_check_count(): void + { + $user = $this->userWithPermissions(['a.read', 'b.read', 'c.read']); + + $tenChecks = $this->countQueries(function () use ($user) { + for ($i = 0; $i < 10; $i++) { + $user->hasPermission('a.read'); + $user->hasPermission('b.read'); + $user->hasPermission('missing.read'); + } + }); + + $freshUser = User::find($user->id); + + $hundredChecks = $this->countQueries(function () use ($freshUser) { + for ($i = 0; $i < 100; $i++) { + $freshUser->hasPermission('a.read'); + $freshUser->hasPermission('b.read'); + $freshUser->hasPermission('missing.read'); + } + }); + + $this->assertLessThanOrEqual( + $tenChecks, + $hundredChecks, + "판정 횟수를 10배로 늘렸더니 쿼리가 {$tenChecks} → {$hundredChecks} 로 늘었다 — 권한 집합이 재사용되지 않는다" + ); + } + + /** + * 관리자 판정도 같은 권한 집합을 재사용하는지 확인 + */ + public function test_is_admin_reuses_permission_set(): void + { + $user = $this->userWithPermissions(['x.read']); + + $queries = $this->countQueries(function () use ($user) { + $user->hasPermission('x.read'); + $user->isAdmin(); + $user->hasPermissions(['x.read', 'y.read'], requireAll: false); + $user->getEffectiveScopeForPermission('x.read'); + }); + + // 집합 적재는 역할 조회 + 권한 eager load 2회로 끝난다. + // 판정 종류가 늘어도 이 2회를 넘지 않아야 한다. + $this->assertLessThanOrEqual( + 2, + $queries, + "권한 판정 4종이 쿼리를 {$queries}회 실행했다 — 한 번 적재한 집합을 공유해야 한다" + ); + } + + /** + * 레이아웃 컴포넌트 트리 권한 필터링이 노드 수와 무관한지 확인 + */ + public function test_layout_permission_filter_does_not_grow_with_node_count(): void + { + $user = $this->userWithPermissions(['layout.read']); + $service = app(LayoutService::class); + + $smallTree = $this->componentTree(5); + $largeTree = $this->componentTree(50); + + $smallQueries = $this->countQueries(fn () => $this->filterTree($service, $smallTree, $user)); + + $freshUser = User::find($user->id); + $largeQueries = $this->countQueries(fn () => $this->filterTree($service, $largeTree, $freshUser)); + + $this->assertLessThanOrEqual( + $smallQueries, + $largeQueries, + "노드를 10배로 늘렸더니 쿼리가 {$smallQueries} → {$largeQueries} 로 늘었다 — 노드마다 권한을 다시 묻고 있다" + ); + } + + /** + * 통합검색: 매칭 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 통합검색은 각 모듈 리스너로 팬아웃하는 구조라, 어느 한 리스너가 결과 항목마다 + * 조회를 내면 그 자리에서 쿼리가 매칭 수에 비례한다. 응답을 통째로 재야 드러난다. + * + * 활성 검색 모듈이 하나도 없는 환경에서도 이 단언은 유효하다 — 그 경우 코어의 + * 응답 조립만 재게 되며, 리스너가 붙는 순간부터 그 리스너가 판정 대상이 된다. + */ + public function test_unified_search_query_count_is_stable(): void + { + $this->seedSearchableUsers(5, 'search-a'); + + $this->assertQueryCountStableAsDataGrows( + measure: function () { + $response = $this->getJson('/api/search?q=g7search'); + $response->assertOk(); + }, + grow: fn () => $this->seedSearchableUsers(10, 'search-b'), + context: '통합검색', + ); + } + + /** + * 검색 대상이 될 만한 데이터를 만듭니다. + * + * 어떤 모듈이 설치돼 있든 모수가 늘어나도록 코어 테이블(users)을 늘린다. + * + * @param int $count 생성할 수 + * @param string $prefix 이름 접두 + */ + private function seedSearchableUsers(int $count, string $prefix): void + { + User::factory()->count($count)->create([ + 'name' => $prefix.' g7search', + ]); + } + + /** + * 권한을 가진 사용자를 만듭니다. + * + * @param array $identifiers 권한 식별자 목록 + * @return User 생성된 사용자 + */ + private function userWithPermissions(array $identifiers): User + { + $role = Role::factory()->create(); + + foreach ($identifiers as $identifier) { + $permission = Permission::factory()->create([ + 'identifier' => $identifier, + 'type' => 'user', + ]); + $role->permissions()->syncWithoutDetaching([$permission->id]); + } + + $user = User::factory()->create(); + $user->roles()->syncWithoutDetaching([$role->id]); + + return User::find($user->id); + } + + /** + * 권한 조건이 붙은 컴포넌트 트리를 만듭니다. + * + * @param int $nodeCount 노드 수 + * @return array> 컴포넌트 배열 + */ + private function componentTree(int $nodeCount): array + { + $components = []; + + for ($i = 0; $i < $nodeCount; $i++) { + $components[] = [ + 'type' => 'basic', + 'name' => 'Div', + 'permissions' => ['layout.read'], + 'children' => [ + ['type' => 'basic', 'name' => 'Span', 'permissions' => ['layout.read']], + ], + ]; + } + + return $components; + } + + /** + * 컴포넌트 트리에 권한 필터를 적용합니다. + * + * @param LayoutService $service 레이아웃 서비스 + * @param array> $components 컴포넌트 배열 + * @param User $user 판정 대상 사용자 + * @return array 필터링 결과 + */ + private function filterTree(LayoutService $service, array $components, User $user): array + { + $reflection = new \ReflectionMethod($service, 'filterComponentTree'); + $reflection->setAccessible(true); + + return ['components' => $reflection->invoke($service, $components, $user)]; + } +} diff --git a/tests/Feature/Search/SearchAccuracyContractTest.php b/tests/Feature/Search/SearchAccuracyContractTest.php new file mode 100644 index 00000000..03d6c58b --- /dev/null +++ b/tests/Feature/Search/SearchAccuracyContractTest.php @@ -0,0 +1,152 @@ +count(7)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 3, page: 1, resultCap: 100); + + $this->assertSame(TotalRelation::Exact, $page->totalRelation()); + $this->assertTrue($page->totalRelation()->isExact()); + $this->assertSame(User::query()->count(), $page->total()); + $this->assertNotNull($page->lastPage(), '정확한 총 건수에서는 마지막 페이지가 계산된다'); + } + + /** + * 상한 초과 시 하한으로 보고하고 마지막 페이지를 비우는지 확인 + * + * @effects bounded_total_reports_at_least, bounded_total_hides_last_page + */ + public function test_bounded_total_reports_at_least_and_hides_last_page(): void + { + User::factory()->count(12)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 3, page: 1, resultCap: 5); + + $this->assertSame(TotalRelation::AtLeast, $page->totalRelation()); + $this->assertFalse($page->totalRelation()->isExact()); + $this->assertSame(5, $page->total(), '상한을 넘으면 상한값을 하한으로 보고한다'); + $this->assertNull($page->lastPage(), '마지막 페이지는 계산할 수 없다'); + } + + /** + * 상한을 넘겨도 "다음" 이동이 끝까지 열려 있는지 확인 + * + * @effects bounded_total_keeps_next + */ + public function test_bounded_total_keeps_next_navigation_open(): void + { + User::factory()->count(12)->create(); + + $reached = []; + + for ($pageNumber = 1; $pageNumber <= 5; $pageNumber++) { + $page = BoundedPaginator::paginate(User::query(), perPage: 3, page: $pageNumber, resultCap: 5); + + foreach ($page->items() as $user) { + $reached[] = $user->id; + } + + if (! $page->hasMorePages()) { + break; + } + } + + $this->assertSame( + User::query()->orderBy('id')->pluck('id')->all(), + collect($reached)->sort()->values()->all(), + '상한과 무관하게 마지막 행까지 도달할 수 있어야 한다' + ); + } + + /** + * 통합검색 응답이 정확도 메타를 함께 내보내는지 확인 + * + * @effects search_message_switches_on_accuracy + */ + public function test_search_response_carries_accuracy_meta(): void + { + $response = $this->getJson('/api/search?'.http_build_query(['q' => '검색어테스트'])); + + $response->assertStatus(200); + + $data = $response->json('data'); + + $this->assertArrayHasKey('total_relation', $data, '정확도를 응답에 실어야 화면이 문구를 고를 수 있다'); + $this->assertArrayHasKey('total_is_exact', $data); + $this->assertArrayHasKey('result_cap', $data); + $this->assertSame(TotalRelation::Exact->value, $data['total_relation']); + $this->assertTrue($data['total_is_exact']); + } + + /** + * 남용 방지용 페이지 상한이 적용되는지 확인 + * + * 정상 탐색은 has_more_pages 로 열려 있고, 상한은 임의의 큰 페이지 번호를 직접 + * 던져 초대형 OFFSET 을 만드는 것만 막는다. + * + * @effects page_limit_rejects_abusive_page + */ + public function test_page_number_upper_bound_is_enforced(): void + { + config(['g7_settings.core.pagination.max_page' => 50]); + + $maxPage = PaginationLimits::maxPage('search'); + $this->assertSame(50, $maxPage); + + $response = $this->getJson('/api/search?'.http_build_query(['q' => '검색어테스트', 'page' => 999999])); + + $response->assertStatus(422); + $response->assertJsonValidationErrors(['page']); + } + + /** + * 상한이 0 이면 무제한으로 해석되는지 확인 + */ + public function test_zero_cap_means_unlimited(): void + { + config(['g7_settings.core.pagination.result_cap' => 0]); + + $this->assertNull(PaginationLimits::resultCap('search')); + + User::factory()->count(4)->create(); + + [$total, $relation] = BoundedPaginator::countWithCap(User::query(), PaginationLimits::resultCap('search')); + + $this->assertSame(User::query()->count(), $total); + $this->assertSame(TotalRelation::Exact, $relation); + } +} diff --git a/tests/Feature/Search/SearchBadgeAccuracyTest.php b/tests/Feature/Search/SearchBadgeAccuracyTest.php new file mode 100644 index 00000000..a32079d2 --- /dev/null +++ b/tests/Feature/Search/SearchBadgeAccuracyTest.php @@ -0,0 +1,89 @@ + 3, + 'total_relation' => TotalRelation::Exact->value, + 'total_is_exact' => true, + 'items' => [], + ]; + $results['beta'] = [ + 'total' => 10000, + 'total_relation' => TotalRelation::AtLeast->value, + 'total_is_exact' => false, + 'items' => [], + ]; + + return $results; + }, 5); + + // When: 통합 검색을 호출 + $response = $this->getJson('/api/search?q=테스트'); + + // Then: 카테고리별 정확도가 각각 실린다 + $response->assertOk(); + $response->assertJsonPath('data.counts_are_exact.alpha', true); + $response->assertJsonPath('data.counts_are_exact.beta', false); + } + + /** + * 정확도를 싣지 않은 카테고리는 정확한 것으로 보되 키는 존재하는지 확인 + * + * 키 자체가 없으면 화면이 "정확도 정보 없음" 과 "정확함" 을 구분하지 못한다. + * + * @effects badge_accuracy_defaults_to_exact + */ + public function test_category_without_accuracy_defaults_to_exact(): void + { + // Given: 정확도를 싣지 않은 카테고리 + HookManager::addFilter('core.search.results', function (array $results): array { + $results['legacy'] = ['total' => 7, 'items' => []]; + + return $results; + }, 5); + + // When: 통합 검색을 호출 + $response = $this->getJson('/api/search?q=테스트'); + + // Then: 키는 존재하고 값은 정확으로 해석된다 + $response->assertOk(); + $response->assertJsonPath('data.counts_are_exact.legacy', true); + } +} diff --git a/tests/Playwright/specs/pagination/bounded-total-and-storefront.spec.ts b/tests/Playwright/specs/pagination/bounded-total-and-storefront.spec.ts new file mode 100644 index 00000000..862dc8d3 --- /dev/null +++ b/tests/Playwright/specs/pagination/bounded-total-and-storefront.spec.ts @@ -0,0 +1,270 @@ +/** + * E2E: 총 건수 상한 표기 계약 + 쇼핑 첫 화면 단일 요청 (#519) + * + * 두 변경 모두 브라우저에서만 드러나는 성질을 갖는다. + * + * (1) 총 건수가 상한을 넘으면 서버가 `last_page: null` 을 보낸다. 화면이 그 값을 + * 숫자로 잘못 해석하면 페이저가 통째로 사라지거나 "1 / 1" 로 굳는다 — + * 응답은 정상이고 콘솔 에러도 없어 API 테스트로는 드러나지 않는다. + * (2) 쇼핑 첫 화면은 다섯 묶음을 한 번에 받도록 바꿨다. 레이아웃 바인딩을 한 곳이라도 + * 옛 경로에 남겨 두면 그 영역만 조용히 비어 렌더된다. + * + * @scenario case=bounded_total_and_single_storefront_request + * + * @effects search_pager_survives_null_last_page, + * search_count_marks_inexact_total, + * storefront_uses_single_request, + * storefront_sections_render + */ +import type { Page } from '@playwright/test'; +import { test, expect } from '../../fixtures/auth'; + +/** + * 화면 진입 공통 대기. + * + * `networkidle` 은 폴링이 있는 화면에서 idle 이 되지 않으므로 쓰지 않는다. + * + * @param page 대상 페이지 + * @param path 이동할 경로 + * @returns void + */ +async function gotoAndSettle(page: Page, path: string): Promise { + await page.goto(path); + await page.waitForLoadState('domcontentloaded'); + await acceptCookieConsent(page); + await page.waitForTimeout(1200); +} + +/** + * 쿠키 동의 배너를 처리한다. + * + * GDPR 사전 차단(preblocker)이 동의 전까지 데이터 요청을 막으므로, 배너를 남겨 둔 채로는 + * "화면이 어떤 API 를 부르는가" 를 관찰할 수 없다. 배너가 없는 사이트에서는 아무 일도 하지 않는다. + * + * @param page 대상 페이지 + * @returns void + */ +async function acceptCookieConsent(page: Page): Promise { + const accept = page.getByRole('button', { name: /모두 동의|Accept all/i }).first(); + + if (await accept.isVisible({ timeout: 3_000 }).catch(() => false)) { + await accept.click(); + await page.waitForTimeout(500); + } +} + +// 쇼핑 목록 라우트는 '/shop' 이 아니라 '/shop/products' 다 (템플릿 routes.json 기준). +const SHOP_LIST_PATH = '/shop/products'; + +test.describe('총 건수 상한과 페이지 이동', () => { + // @scenario case=search_response_carries_accuracy_meta + // @effects search_count_marks_inexact_total + test('검색 응답이 총 건수 정확도를 함께 싣는다', async ({ page }) => { + const responsePromise = page.waitForResponse( + (r) => r.url().includes('/api/search?') && r.status() === 200, + { timeout: 20_000 }, + ); + + await gotoAndSettle(page, '/search?q=' + encodeURIComponent('테스트')); + + const body = await (await responsePromise).json(); + const data = body?.data; + + expect(data, '검색 응답에 data 가 없다').toBeTruthy(); + expect(data).toHaveProperty('total_is_exact'); + expect(data).toHaveProperty('total_relation'); + + // 정확도가 true 면 종전 표기 그대로, false 면 "이상" 표기가 붙어야 한다. + // 표기 문구는 로케일에 따라 다르므로 둘 중 하나가 화면에 있는지로 판정한다. + // 본문 전체를 대상으로 하므로 앵커(`$`)를 쓰지 않는다 — 뒤에 다른 문구가 따라온다. + const suffix = data.total_is_exact + ? /\d+\s*건|\d+\s*results/i + : /건 이상|\+ results|more than/i; + + await expect(page.locator('body')).toContainText(suffix, { timeout: 15_000 }); + }); + + // @scenario case=search_pager_survives_null_last_page + // @effects search_pager_survives_null_last_page + test('마지막 페이지를 모르는 목록에서도 페이저가 사라지지 않는다', async ({ page }) => { + const errors: string[] = []; + page.on('pageerror', (error) => errors.push(error.message)); + + // 상한 초과 상태는 실데이터로 재현하기 어려우므로 응답을 가로채 주입한다. + // last_page: null + has_more_pages: true 가 이 시나리오의 핵심 입력이다. + await page.route('**/api/search?**', async (route) => { + const response = await route.fetch(); + const body = await response.json(); + + if (body?.data) { + body.data.last_page = null; + body.data.has_more_pages = true; + body.data.total_relation = 'at_least'; + body.data.total_is_exact = false; + } + + await route.fulfill({ response, json: body }); + }); + + await gotoAndSettle(page, '/search?q=' + encodeURIComponent('테스트')); + + expect(errors, '검색 화면 렌더 중 자바스크립트 오류가 발생했다').toEqual([]); + + // "다음" 이동 수단이 실제로 화면에 남아 있어야 한다. 이것이 사라지면 상한 초과 + // 검색에서 2페이지 이후에 도달할 방법이 없어진다. + const nextControl = page + .locator('button:has(i.fa-chevron-right), a:has(i.fa-chevron-right)') + .first(); + + await expect(nextControl, '마지막 페이지를 모른다는 이유로 "다음" 이동이 사라졌다') + .toBeVisible({ timeout: 15_000 }); + }); +}); + +test.describe('쇼핑 첫 화면 단일 요청', () => { + // @scenario case=storefront_uses_single_request + // @effects storefront_uses_single_request + test('분류·상품·진열 묶음을 한 번의 요청으로 받는다', async ({ page }) => { + const productApiCalls: string[] = []; + + page.on('request', (request) => { + const url = request.url(); + if (/\/api\/modules\/sirsoft-ecommerce\/(products|categories|storefront)/.test(url)) { + productApiCalls.push(url); + } + }); + + const storefrontPromise = page.waitForResponse( + (r) => r.url().includes('/api/modules/sirsoft-ecommerce/storefront') && r.status() === 200, + { timeout: 20_000 }, + ); + + await gotoAndSettle(page, SHOP_LIST_PATH); + + const body = await (await storefrontPromise).json(); + const data = body?.data; + + expect(data, 'storefront 응답에 data 가 없다').toBeTruthy(); + for (const key of ['categories', 'products', 'recent_products', 'popular_products', 'new_products']) { + expect(data, `storefront 응답에 ${key} 묶음이 없다`).toHaveProperty(key); + } + + // 개별 엔드포인트를 함께 부르고 있으면 통합의 의미가 없다. + const legacyCalls = productApiCalls.filter( + (url) => /\/(products\/(popular|new|recent)|categories)(\?|$)/.test(url), + ); + expect(legacyCalls, `쇼핑 첫 화면이 개별 엔드포인트를 여전히 호출한다: ${legacyCalls.join(', ')}`).toEqual([]); + }); + + // @scenario case=storefront_sections_render + // @effects storefront_sections_render + test('첫 화면의 각 영역이 비어 있지 않게 렌더된다', async ({ page }) => { + const errors: string[] = []; + page.on('pageerror', (error) => errors.push(error.message)); + + const storefrontPromise = page.waitForResponse( + (r) => r.url().includes('/api/modules/sirsoft-ecommerce/storefront') && r.status() === 200, + { timeout: 20_000 }, + ); + + await gotoAndSettle(page, SHOP_LIST_PATH); + + expect(errors, '쇼핑 첫 화면 렌더 중 자바스크립트 오류가 발생했다').toEqual([]); + await expect(page.locator('h1')).toBeVisible({ timeout: 15_000 }); + + // 통합 응답이 실제로 담아 온 묶음은 화면에도 나타나야 한다. 바인딩 경로가 + // 하나라도 옛 이름에 남아 있으면 그 영역만 조용히 비므로, 응답에 항목이 있는 + // 묶음에 한해 그 이름이 화면에 렌더됐는지 본다 (빈 사이트에서는 건너뛴다). + const data = (await (await storefrontPromise).json())?.data; + + const sections: Array<[string, unknown]> = [ + ['recent_products', data?.recent_products], + ['popular_products', data?.popular_products], + ['new_products', data?.new_products], + ]; + + for (const [key, items] of sections) { + const list = Array.isArray(items) ? items : (items as { data?: unknown[] })?.data; + + if (!Array.isArray(list) || list.length === 0) { + continue; + } + + const firstName = (list[0] as { name_localized?: string; name?: string })?.name_localized + ?? (list[0] as { name?: string })?.name; + + if (typeof firstName !== 'string' || firstName.trim() === '') { + continue; + } + + await expect( + page.getByText(firstName, { exact: false }).first(), + `${key} 묶음이 응답에는 있는데 화면에 렌더되지 않았다 — 바인딩 경로가 어긋났다`, + ).toBeVisible({ timeout: 15_000 }); + } + }); +}); + +test.describe('공유 partial 을 쓰는 다른 화면 (#519 회귀)', () => { + // 쇼핑 첫 화면을 단일 요청으로 통합하면서, 같은 partial 을 쓰는 분류 화면과 상품 + // 상세의 인기 상품 영역이 조용히 비었던 회귀다. `?? []` 폴백 때문에 예외도 404 도 + // 나지 않아 브라우저에서 눈으로 보는 것 말고는 드러나지 않는다. + + // @scenario case=shared_partial_reads_parent_declared_names + // @effects shared_partial_reads_parent_declared_names + test('분류 화면이 상품 그리드 데이터를 실제로 받는다', async ({ page }) => { + const errors: string[] = []; + page.on('pageerror', (error) => errors.push(error.message)); + + const listRequests: string[] = []; + page.on('request', (request) => { + const url = request.url(); + if (/\/api\/modules\/sirsoft-ecommerce\/(products|storefront)(\?|$|\/)/.test(url)) { + listRequests.push(url); + } + }); + + // 분류 슬러그가 없는 사이트에서는 목록 화면으로 대체 확인한다 + await gotoAndSettle(page, '/shop/products'); + + expect(errors, '분류/목록 화면 렌더 중 자바스크립트 오류가 발생했다').toEqual([]); + + // 이 화면은 공유 partial 로 상품 그리드를 그린다. partial 이 부모가 선언한 이름을 + // 읽지 못하면 데이터 요청 자체가 나가지 않고 그리드만 조용히 빈다 — 요청이 실제로 + // 있었는지가 그 회귀를 잡는 신호다. + expect( + listRequests, + '상품 목록/통합 응답 요청이 한 번도 나가지 않았다 — 공유 partial 이 부모가 선언한 데이터소스 이름을 읽지 못한다', + ).not.toEqual([]); + + await expect(page.locator('h1')).toBeVisible({ timeout: 15_000 }); + }); + + // @scenario case=storefront_grid_pager_uses_has_more_pages + // @effects storefront_grid_pager_uses_has_more_pages + test('상품 그리드 페이저가 마지막 페이지를 몰라도 접히지 않는다', async ({ page }) => { + await page.route('**/api/modules/sirsoft-ecommerce/storefront**', async (route) => { + const response = await route.fetch(); + const body = await response.json().catch(() => null); + + if (!body?.data?.products?.pagination) { + return route.fulfill({ response }); + } + + // 총 건수를 상한까지만 센 응답을 흉내 낸다 — last_page 는 계산할 수 없다 + body.data.products.pagination.last_page = null; + body.data.products.pagination.has_more_pages = true; + body.data.products.pagination.total_relation = 'at_least'; + body.data.products.pagination.total_is_exact = false; + + return route.fulfill({ response, body: JSON.stringify(body) }); + }); + + await gotoAndSettle(page, SHOP_LIST_PATH); + + // last_page 가 null 이라고 페이저가 통째로 사라지면 1페이지 밖 상품에 도달할 방법이 없다 + const nextButton = page.locator('button:has(i.fa-chevron-right)').last(); + await expect(nextButton).toBeVisible({ timeout: 15_000 }); + await expect(nextButton).toBeEnabled(); + }); +}); diff --git a/tests/Unit/Http/Resources/HasRowNumberBoundedTotalTest.php b/tests/Unit/Http/Resources/HasRowNumberBoundedTotalTest.php new file mode 100644 index 00000000..855f3b90 --- /dev/null +++ b/tests/Unit/Http/Resources/HasRowNumberBoundedTotalTest.php @@ -0,0 +1,113 @@ + 순번 목록 + */ + public function numbers(string $sortOrder): array + { + return $this->mapWithRowNumber(fn ($item) => ['id' => $item], $sortOrder) + ->pluck('number') + ->all(); + } + }; + } + + /** + * 상한형 페이지를 만듭니다. + */ + private function boundedPage(int $page, int $perPage, int $total, TotalRelation $relation): BoundedPage + { + return new BoundedPage( + items: new Collection(range(1, $perPage)), + total: $total, + perPage: $perPage, + currentPage: $page, + totalRelation: $relation, + resultCap: 10000, + hasMorePages: true, + ); + } + + public function test_총건수가_정확하면_순번은_총건수부터_내림차순이다(): void + { + $paginator = new LengthAwarePaginator(new Collection(range(1, 5)), 120, 5, 1); + + $this->assertSame( + [120, 119, 118, 117, 116], + $this->collectionFor($paginator)->numbers('desc') + ); + } + + public function test_총건수가_잘리면_순번은_null_이다(): void + { + $page = $this->boundedPage(1, 5, 10000, TotalRelation::AtLeast); + + $this->assertSame( + [null, null, null, null, null], + $this->collectionFor($page)->numbers('desc') + ); + } + + public function test_상한을_넘은_페이지에서_0이나_음수_순번이_없다(): void + { + foreach ([501, 502, 601] as $pageNumber) { + $page = $this->boundedPage($pageNumber, 20, 10000, TotalRelation::AtLeast); + + foreach ($this->collectionFor($page)->numbers('desc') as $number) { + $this->assertFalse( + is_int($number), + "{$pageNumber} 페이지에서 상한 총 건수로 역산한 순번이 그대로 나왔습니다." + ); + } + } + } + + public function test_오름차순_순번은_총건수가_잘려도_유지된다(): void + { + $page = $this->boundedPage(3, 5, 10000, TotalRelation::AtLeast); + + $this->assertSame( + [11, 12, 13, 14, 15], + $this->collectionFor($page)->numbers('asc') + ); + } +} diff --git a/tests/Unit/Layouts/PaginationLastPageCollapseTest.php b/tests/Unit/Layouts/PaginationLastPageCollapseTest.php new file mode 100644 index 00000000..2b4c16ab --- /dev/null +++ b/tests/Unit/Layouts/PaginationLastPageCollapseTest.php @@ -0,0 +1,181 @@ + 1` → 페이저가 통째로 사라져 **뒤쪽 페이지로 갈 방법 자체가 없어진다** + * + * 둘 다 예외도 404 도 내지 않는다. 화면은 정상으로 보이고 기능만 없다. + * + * 템플릿별 가드는 각 템플릿 스위트에 이미 있지만 자기 `layouts/` 디렉토리만 훑는다. + * 모듈·플러그인이 소유한 관리자 레이아웃은 어느 스위트에도 잡히지 않아 33 지점이 남아 + * 있었고, 그중 주문·신고·페이지 관리자 목록은 저장소가 실제로 상한을 적용한 목록이었다. + * 이 테스트는 레이아웃을 소유한 **모든** 디렉토리를 훑어 그 사각을 없앤다. + * + * 개별 파일을 열거하지 않는 이유는 다음에 추가되는 레이아웃이 또 빠지기 때문이다. + */ +class PaginationLastPageCollapseTest extends TestCase +{ + /** + * `last_page` 를 1 로 채우는 형태 + */ + private const COLLAPSE_PATTERN = '/last_page\s*(?:\?\?|\|\|)\s*1(?![0-9])/'; + + /** + * 레이아웃을 소유하는 디렉토리 전부 + * + * @return array 절대 경로 목록 + */ + private function layoutRoots(): array + { + return [ + base_path('resources/layouts'), + base_path('templates/_bundled'), + base_path('modules/_bundled'), + base_path('plugins/_bundled'), + ]; + } + + /** + * 레이아웃 JSON 파일을 모읍니다. + * + * `_bundled` 밑에서는 `layouts/` 경로에 든 것만 본다 (컴포넌트 매니페스트·언어 파일 제외). + * + * @return array 파일 경로 목록 + */ + private function collectLayoutFiles(): array + { + $files = []; + + foreach ($this->layoutRoots() as $root) { + if (! is_dir($root)) { + continue; + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS) + ); + + foreach ($iterator as $file) { + if ($file->getExtension() !== 'json') { + continue; + } + + $path = str_replace('\\', '/', $file->getPathname()); + + if (! str_contains($path, '/layouts/')) { + continue; + } + + if (str_contains($path, '/node_modules/') || str_contains($path, '/dist/')) { + continue; + } + + $files[] = $path; + } + } + + sort($files); + + return $files; + } + + /** + * 한 파일의 위반 줄을 찾습니다. + * + * @param string $path 레이아웃 경로 + * @return array "경로:줄 내용" 목록 + */ + private function violationsIn(string $path): array + { + $violations = []; + $lines = explode("\n", (string) file_get_contents($path)); + $relative = str_replace(str_replace('\\', '/', base_path()).'/', '', $path); + + foreach ($lines as $index => $line) { + if (str_contains($line, 'audit:allow layout-last-page-null-collapse')) { + continue; + } + + if (preg_match(self::COLLAPSE_PATTERN, $line) === 1) { + $violations[] = $relative.':'.($index + 1).' '.trim($line); + + continue; + } + + // 페이저 노출 조건이 last_page 하나에만 의존하는 형태 + if (str_contains($line, '"if"') + && str_contains($line, 'last_page') + && ! str_contains($line, 'has_more_pages') + && ! str_contains($line, 'current_page')) { + $violations[] = $relative.':'.($index + 1).' '.trim($line); + } + } + + return $violations; + } + + /** + * 스캔 모집단이 비어 있지 않아야 한다 — 0 건을 훑고 통과하면 이 가드는 아무것도 지키지 않는다. + */ + public function test_레이아웃_스캔_모집단이_비어있지_않다(): void + { + $files = $this->collectLayoutFiles(); + + $this->assertGreaterThan( + 300, + count($files), + '레이아웃 스캔 대상이 비정상적으로 적다. 경로 규칙이 바뀌었는지 확인해야 한다.' + ); + } + + /** + * 어떤 레이아웃도 `last_page` 를 1 로 접지 않는다. + */ + public function test_어떤_레이아웃도_last_page를_1로_접지_않는다(): void + { + $violations = []; + + foreach ($this->collectLayoutFiles() as $path) { + $violations = array_merge($violations, $this->violationsIn($path)); + } + + $this->assertSame( + [], + $violations, + "상한 목록의 last_page(null) 를 1 로 접는 레이아웃이 있다:\n".implode("\n", $violations) + ); + } + + /** + * 합성 표본이 실제로 red 가 되는지 — 판정기가 살아 있음을 확인한다. + */ + public function test_판정기가_합성_위반을_실제로_잡는다(): void + { + $sample = base_path('storage/framework/testing/pagination_collapse_sample.json'); + @mkdir(dirname($sample), 0o775, true); + + file_put_contents($sample, implode("\n", [ + '{', + ' "props": {', + ' "totalPages": "{{items?.data?.pagination?.last_page ?? 1}}"', + ' },', + ' "if": "{{items?.data?.pagination?.last_page > 1}}"', + '}', + ])); + + try { + $this->assertCount(2, $this->violationsIn($sample)); + } finally { + @unlink($sample); + } + } +} diff --git a/tests/Unit/Listeners/CoreActivityLogListenerSignatureTest.php b/tests/Unit/Listeners/CoreActivityLogListenerSignatureTest.php index c697846a..175b2aa9 100644 --- a/tests/Unit/Listeners/CoreActivityLogListenerSignatureTest.php +++ b/tests/Unit/Listeners/CoreActivityLogListenerSignatureTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\Listeners; use App\Listeners\CoreActivityLogListener; +use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; /** @@ -22,6 +23,11 @@ use Tests\TestCase; */ class CoreActivityLogListenerSignatureTest extends TestCase { + // 아래 호출 가능 검증은 핸들러를 실제로 실행하므로 활동 로그가 기록된다. + // 격리가 없으면 그 행이 프로세스 내내 남아, 테이블 전체 건수를 단언하는 다른 테스트를 + // 실행 순서에 따라 실패시킨다 (단독 실행에서는 통과해 원인을 찾기 어렵다). + use RefreshDatabase; + public function test_handle_template_after_deactivate_accepts_string_identifier(): void { $listener = $this->app->make(CoreActivityLogListener::class); diff --git a/tests/Unit/Search/DatabaseFulltextEngineTest.php b/tests/Unit/Search/DatabaseFulltextEngineTest.php index d5edcf54..c1afaf69 100644 --- a/tests/Unit/Search/DatabaseFulltextEngineTest.php +++ b/tests/Unit/Search/DatabaseFulltextEngineTest.php @@ -389,7 +389,11 @@ class DatabaseFulltextEngineTest extends TestCase } /** - * whereFulltext()가 연산자만 입력 시 1=0 조건으로 빈 결과를 생성하는지 테스트합니다. + * whereFulltext()가 연산자만 입력 시 항상 거짓인 조건으로 빈 결과를 만드는지 테스트합니다. + * + * 조건이 어떤 문자열로 렌더되는지(`1 = 0` / `0 = 1`)는 빌더 구현 사항이므로 단언하지 + * 않는다. 여기서 고정할 것은 **매칭을 시도하지 않고(AGAINST 없음) 결과가 비어야 한다** + * 는 동작이다. 리터럴을 박아 두면 빈 whereIn 처럼 동등한 구현으로 바꿀 때 깨진다. */ public function test_where_fulltext_uses_false_condition_for_operators_only(): void { @@ -405,8 +409,16 @@ class DatabaseFulltextEngineTest extends TestCase DatabaseFulltextEngine::whereFulltext($query, 'name', '< >'); $sql = $query->toSql(); - $this->assertStringContainsString('1 = 0', $sql); + + // 매칭을 시도하지 않는다 $this->assertStringNotContainsString('AGAINST', $sql); + // 조건이 아예 없으면 전체 행이 나온다 — 조건은 반드시 붙어야 한다 + $this->assertStringContainsString('where', strtolower($sql)); + // 그 조건은 어떤 행도 통과시키지 않는 상수 거짓이어야 한다. + // `1 = 0`(raw)과 `0 = 1`(빈 whereIn) 은 같은 뜻이므로 둘 다 허용한다. + $this->assertMatchesRegularExpression('/\b(?:1\s*=\s*0|0\s*=\s*1)\b/', $sql); + // 상수 거짓 조건에는 바인딩이 없다 (사용자 입력이 새어 들어가지 않았다는 뜻) + $this->assertSame([], $query->getBindings()); } /** diff --git a/tests/Unit/Search/KeywordSearchTest.php b/tests/Unit/Search/KeywordSearchTest.php new file mode 100644 index 00000000..97f70774 --- /dev/null +++ b/tests/Unit/Search/KeywordSearchTest.php @@ -0,0 +1,289 @@ +newQuery(); + } + + /** + * 활성 엔진을 지정한 인스턴스로 교체합니다. + * + * @param Engine $engine 활성으로 만들 엔진 + */ + private function useEngine(Engine $engine): void + { + $manager = $this->mock(EngineManager::class); + $manager->shouldReceive('engine')->andReturn($engine); + $this->app->instance(EngineManager::class, $manager); + } + + /** + * 계약을 구현한 엔진이 실제로 호출되는지 확인 + * + * @effects keyword_predicate_delegates_to_active_engine + */ + #[Test] + public function delegates_to_active_engine_that_implements_the_contract(): void + { + $engine = $this->makeRecordingEngine(); + + $this->useEngine($engine); + + $query = $this->makeQuery(); + KeywordSearch::apply($query, ['title', 'content'], '검색어'); + + $this->assertSame( + [[['title', 'content'], '검색어', 'and']], + $engine->calls, + '활성 엔진이 호출되지 않았다 — 플러그인 엔진이 우회된다.' + ); + $this->assertStringNotContainsString('like', strtolower($query->toSql()), '엔진이 만든 조건 대신 폴백이 적용됐다.'); + } + + /** + * 호출 내역을 기록하는 계약 구현 엔진을 만듭니다. + */ + private function makeRecordingEngine(): Engine + { + return new class extends Engine implements KeywordPredicateProvider + { + public array $calls = []; + + public ?KeywordSearchContext $lastContext = null; + + public function applyKeywordPredicate(Builder $query, array $columns, string $keyword, string $boolean, KeywordSearchContext $context): void + { + $this->calls[] = [$columns, $keyword, $boolean]; + $this->lastContext = $context; + $query->whereIn($query->getModel()->getQualifiedKeyName(), [7]); + } + + public function update($models) {} + + public function delete($models) {} + + public function search(\Laravel\Scout\Builder $builder) {} + + public function paginate(\Laravel\Scout\Builder $builder, $perPage, $page) {} + + public function mapIds($results) {} + + public function map(\Laravel\Scout\Builder $builder, $results, $model) {} + + public function lazyMap(\Laravel\Scout\Builder $builder, $results, $model) {} + + public function getTotalCount($results) {} + + public function flush($model) {} + + public function createIndex($name, array $options = []) {} + + public function deleteIndex($name) {} + }; + } + + /** + * 엔진에게 키 집합 상한이 전달되는지 확인 + * + * 외부 검색 서버를 쓰는 엔진은 자기 서버에서 키 집합을 받아 조건으로 붙인다. 상한을 + * 손에 쥐어 주지 않으면 매칭이 큰 검색어에서 그 집합 자체가 메모리 폭발이 된다 — + * 이 프로젝트가 실제로 겪은 결함이다. 규정만으로는 강제할 수 없으므로 **값이 실제로 + * 도달하는지**를 고정한다. + * + * @effects keyword_predicate_passes_key_cap_to_engine + */ + #[Test] + public function passes_the_key_cap_to_the_engine(): void + { + $engine = $this->makeRecordingEngine(); + $this->useEngine($engine); + + $query = $this->makeQuery(); + KeywordSearch::apply($query, ['title'], '검색어', 'and', 'search'); + + $this->assertNotNull($engine->lastContext, '엔진이 술어 생성 조건을 받지 못했다.'); + $this->assertSame( + PaginationLimits::resultCap('search'), + $engine->lastContext->keyCap, + '엔진이 받은 키 집합 상한이 목록 총 건수 상한과 다르다 — 두 기준이 갈라지면 "엔진이 돌려준 건수" 와 "화면이 보고하는 총 건수" 가 어긋난다.' + ); + } + + /** + * 계약 미구현 엔진에서는 부분일치로 내려가는지 확인 + * + * @effects keyword_predicate_falls_back_when_engine_lacks_contract + */ + #[Test] + public function falls_back_to_partial_match_when_engine_lacks_the_contract(): void + { + $engine = new class extends Engine + { + public function update($models) {} + + public function delete($models) {} + + public function search(\Laravel\Scout\Builder $builder) {} + + public function paginate(\Laravel\Scout\Builder $builder, $perPage, $page) {} + + public function mapIds($results) {} + + public function map(\Laravel\Scout\Builder $builder, $results, $model) {} + + public function lazyMap(\Laravel\Scout\Builder $builder, $results, $model) {} + + public function getTotalCount($results) {} + + public function flush($model) {} + + public function createIndex($name, array $options = []) {} + + public function deleteIndex($name) {} + }; + + $this->useEngine($engine); + + $query = $this->makeQuery(); + KeywordSearch::apply($query, ['title'], '검색어'); + + $this->assertStringContainsString('like', strtolower($query->toSql())); + $this->assertContains('%검색어%', $query->getBindings()); + } + + /** + * 번들 엔진이 계약을 구현하는지 확인 + * + * 구현하지 않으면 MySQL 설치본조차 폴백으로 내려간다. + * + * @effects keyword_predicate_delegates_to_active_engine + */ + #[Test] + public function bundled_engine_implements_the_contract(): void + { + $this->assertInstanceOf( + KeywordPredicateProvider::class, + new DatabaseFulltextEngine, + '번들 엔진이 키워드 술어 계약을 구현하지 않는다.' + ); + } + + /** + * 부분일치 연산자가 선언형 표에서 오는지 확인 + * + * 코드에 드라이버명을 박으면 공식 지원 DBMS 가 늘 때마다 코어를 고쳐야 한다. + * 여기서는 표에 없던 드라이버를 훅으로 선언했을 때 그 연산자가 실제로 쓰이는지 본다. + * + * @effects like_operator_is_declarative_not_hardcoded + */ + #[Test] + public function like_operator_comes_from_the_declarative_table(): void + { + $driver = DB::getDriverName(); + + HookManager::addFilter(KeywordSearch::LIKE_OPERATORS_FILTER, function (array $operators) use ($driver) { + $operators[$driver] = 'ilike'; + + return $operators; + }, 10, ['type' => 'filter']); + + $query = $this->makeQuery(); + KeywordSearch::applyLikeMatch($query, ['title'], '검색어'); + + // `ilike` 는 문자열로 `like` 를 포함하므로 부분일치 단언은 두 연산자를 구분하지 + // 못한다 — 낱말 경계로 정확히 가른다. + $this->assertMatchesRegularExpression('/\bilike\b/i', $query->toSql()); + } + + /** + * 표에 없는 드라이버는 기본 연산자를 쓰는지 확인 + * + * @effects like_operator_is_declarative_not_hardcoded + */ + #[Test] + public function unknown_driver_uses_the_configured_default(): void + { + Config::set('core.search.like_operators', []); + + $query = $this->makeQuery(); + KeywordSearch::applyLikeMatch($query, ['title'], '검색어'); + $sql = $query->toSql(); + + $this->assertMatchesRegularExpression('/(?assertDoesNotMatchRegularExpression('/\bilike\b/i', $sql); + } + + /** + * 부분일치가 검색어의 와일드카드를 escape 하는지 확인 + * + * escape 하지 않으면 `50%` 검색이 `50` 으로 시작하는 모든 행을 반환한다. + * + * @effects like_fallback_escapes_wildcards + */ + #[Test] + public function partial_match_escapes_wildcards_in_the_keyword(): void + { + $query = $this->makeQuery(); + KeywordSearch::applyLikeMatch($query, ['title'], '50%_할인'); + + $this->assertContains('%50\\%\\_할인%', $query->getBindings()); + } +} diff --git a/tests/Unit/Search/SearchPagePolicyTest.php b/tests/Unit/Search/SearchPagePolicyTest.php new file mode 100644 index 00000000..9b4febd5 --- /dev/null +++ b/tests/Unit/Search/SearchPagePolicyTest.php @@ -0,0 +1,121 @@ + ['created_at', 'desc'], + 'views' => ['view_count', 'desc'], + ]; + + /** 커서 경계로 허용한 실제 컬럼 */ + private const CURSOR_COLUMNS = ['created_at', 'view_count']; + + /** + * 커서가 없어도 첫 페이지면 커서로 시작하는지 확인 + * + * 커서를 받은 요청만 커서로 처리하면 첫 커서가 만들어질 자리가 없다. 서버는 커서 + * 모드에서만 다음 커서를 내보내므로, 화면은 건넬 커서가 없어 영원히 offset 에 머문다. + * 첫 페이지는 커서가 없는 것이 정상이므로 이를 "커서 없음" 이 아니라 "시작점" 으로 읽는다. + * + * @effects cursor_starts_on_first_page + */ + public function test_first_page_starts_cursor_mode(): void + { + $sortKeys = SearchPagePolicy::sortKeys('latest', self::SORT_MAP); + + $this->assertTrue(SearchPagePolicy::usesCursor(null, $sortKeys, self::CURSOR_COLUMNS)); + $this->assertTrue(SearchPagePolicy::usesCursor('', $sortKeys, self::CURSOR_COLUMNS)); + $this->assertTrue(SearchPagePolicy::usesCursor(null, $sortKeys, self::CURSOR_COLUMNS, page: 1)); + } + + /** + * 커서 없이 깊은 페이지를 직접 지목한 요청은 offset 을 유지하는지 확인 + * + * 주소로 특정 페이지를 열어 둔 링크(딥링크·북마크)는 그 페이지를 그대로 보여줘야 한다. + * 커서로 바꿔 버리면 첫 페이지로 되돌아가 링크가 가리키던 자리를 잃는다. + * + * @effects deep_page_without_cursor_stays_on_offset + */ + public function test_deep_page_without_cursor_stays_on_offset(): void + { + $sortKeys = SearchPagePolicy::sortKeys('latest', self::SORT_MAP); + + $this->assertFalse(SearchPagePolicy::usesCursor(null, $sortKeys, self::CURSOR_COLUMNS, page: 2)); + $this->assertFalse(SearchPagePolicy::usesCursor('', $sortKeys, self::CURSOR_COLUMNS, page: 7)); + } + + /** + * 깊은 페이지라도 커서를 들고 왔으면 커서로 이어가는지 확인 + * + * @effects cursor_starts_on_first_page + */ + public function test_cursor_wins_over_page_number(): void + { + $sortKeys = SearchPagePolicy::sortKeys('latest', self::SORT_MAP); + + $this->assertTrue(SearchPagePolicy::usesCursor('encoded-cursor', $sortKeys, self::CURSOR_COLUMNS, page: 9)); + } + + /** + * 실제 컬럼 정렬 + 커서가 있으면 커서로 응답하는지 확인 + * + * @effects cursor_requires_real_columns + */ + public function test_real_column_sort_with_cursor_uses_cursor(): void + { + foreach (['latest', 'views'] as $sort) { + $sortKeys = SearchPagePolicy::sortKeys($sort, self::SORT_MAP); + + $this->assertTrue( + SearchPagePolicy::usesCursor('encoded-cursor', $sortKeys, self::CURSOR_COLUMNS), + $sort.' 정렬은 실제 컬럼이므로 커서를 쓸 수 있어야 한다' + ); + } + } + + /** + * 선언에 없는 정렬 이름(관련도순 등)은 offset 을 유지하는지 확인 + * + * 관련도순은 FULLTEXT 점수라는 계산값으로 정렬하므로 WHERE 절 경계로 쓸 수 없다. + * + * @effects unknown_sort_name_falls_back_to_offset + */ + public function test_unknown_sort_name_falls_back_to_offset(): void + { + $sortKeys = SearchPagePolicy::sortKeys('relevance', self::SORT_MAP); + + $this->assertSame([], $sortKeys); + $this->assertFalse(SearchPagePolicy::usesCursor('encoded-cursor', $sortKeys, self::CURSOR_COLUMNS)); + } + + /** + * 허용 목록에 없는 컬럼으로 정렬하면 커서를 쓰지 않는지 확인 + * + * @effects cursor_requires_real_columns + */ + public function test_column_outside_whitelist_falls_back_to_offset(): void + { + $sortKeys = SearchPagePolicy::sortKeys('score', ['score' => ['_ft_score', 'desc']]); + + $this->assertFalse(SearchPagePolicy::usesCursor('encoded-cursor', $sortKeys, self::CURSOR_COLUMNS)); + } +} diff --git a/tests/Unit/Support/Query/BoundedCountTest.php b/tests/Unit/Support/Query/BoundedCountTest.php new file mode 100644 index 00000000..f08fd1df --- /dev/null +++ b/tests/Unit/Support/Query/BoundedCountTest.php @@ -0,0 +1,100 @@ +count(6)->create(); + + $count = BoundedPaginator::count(User::query(), 100); + + $this->assertInstanceOf(BoundedCount::class, $count); + $this->assertSame(User::query()->count(), $count->total); + $this->assertSame(TotalRelation::Exact, $count->totalRelation()); + $this->assertFalse($count->isTruncated()); + $this->assertSame(100, $count->resultCap()); + } + + /** + * 상한을 넘으면 상한값 + AtLeast 를 보고하는지 확인 + */ + public function test_reports_at_least_when_over_cap(): void + { + User::factory()->count(9)->create(); + + $count = BoundedPaginator::count(User::query(), 4); + + $this->assertSame(4, $count->total); + $this->assertSame(TotalRelation::AtLeast, $count->totalRelation()); + $this->assertTrue($count->isTruncated()); + } + + /** + * 상한이 없으면(0 또는 null) 항상 정확한 건수를 세는지 확인 + */ + public function test_null_cap_means_unlimited(): void + { + User::factory()->count(5)->create(); + + $count = BoundedPaginator::count(User::query(), null); + + $this->assertSame(User::query()->count(), $count->total); + $this->assertFalse($count->isTruncated()); + $this->assertNull($count->resultCap()); + } + + /** + * 응답에 실을 필드 묶음이 한 곳에서 조립되는지 확인 + * + * 배지마다 키를 손으로 조립하면 한 군데만 빠져도 그 화면에서만 잘린 값이 + * 정확한 것처럼 나간다. + */ + public function test_to_array_carries_every_accuracy_field(): void + { + User::factory()->count(9)->create(); + + $payload = BoundedPaginator::count(User::query(), 4)->toArray(); + + $this->assertSame([ + 'total' => 4, + 'total_relation' => 'at_least', + 'total_is_exact' => false, + 'result_cap' => 4, + ], $payload); + } + + /** + * 필터가 걸린 쿼리에서도 그 술어 기준으로 세는지 확인 + */ + public function test_respects_query_filters(): void + { + User::factory()->count(4)->create(['status' => 'active']); + User::factory()->count(3)->create(['status' => 'inactive']); + + $count = BoundedPaginator::count(User::query()->where('status', 'active'), 100); + + $this->assertSame(4, $count->total); + $this->assertFalse($count->isTruncated()); + } +} diff --git a/tests/Unit/Support/Query/BoundedPaginatorTest.php b/tests/Unit/Support/Query/BoundedPaginatorTest.php new file mode 100644 index 00000000..714d5595 --- /dev/null +++ b/tests/Unit/Support/Query/BoundedPaginatorTest.php @@ -0,0 +1,333 @@ +count(7)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 3, page: 1, resultCap: 100); + + $this->assertSame(User::query()->count(), $page->total()); + $this->assertSame(TotalRelation::Exact, $page->totalRelation()); + $this->assertFalse($page->isTruncated()); + $this->assertSame(100, $page->resultCap()); + } + + /** + * 상한을 넘으면 총 건수가 하한(AtLeast)으로 보고되는지 확인 + */ + public function test_total_is_at_least_when_over_cap(): void + { + User::factory()->count(9)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 2, page: 1, resultCap: 4); + + $this->assertSame(4, $page->total()); + $this->assertSame(TotalRelation::AtLeast, $page->totalRelation()); + $this->assertTrue($page->isTruncated()); + } + + /** + * 총 건수가 잘려도 마지막 페이지 번호만 사라지고 "다음" 이동은 유지되는지 확인 + */ + public function test_truncated_hides_last_page_but_keeps_next(): void + { + User::factory()->count(9)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 2, page: 1, resultCap: 4); + + $this->assertNull($page->lastPage(), '총 건수가 부정확하면 마지막 페이지를 계산할 수 없다'); + $this->assertTrue($page->hasMorePages(), '상한과 무관하게 다음 페이지 이동은 열려 있어야 한다'); + } + + /** + * 상한을 넘긴 상태에서도 상한 뒤쪽 페이지까지 실제로 이동되는지 확인 + */ + public function test_can_navigate_past_the_cap(): void + { + User::factory()->count(9)->create(); + + // 상한 4 < 실제 9. 5페이지(9~10번째 행)는 상한 바깥이지만 조회되어야 한다. + $page = BoundedPaginator::paginate(User::query(), perPage: 2, page: 5, resultCap: 4); + + $this->assertCount(1, $page->items(), '상한 바깥 페이지도 실제 행을 돌려준다'); + $this->assertFalse($page->hasMorePages()); + } + + /** + * 다음 페이지 존재 여부가 per_page + 1 실측으로 정확한지 확인 + */ + public function test_has_more_pages_is_probed_exactly(): void + { + User::factory()->count(6)->create(); + + $notLast = BoundedPaginator::paginate(User::query(), perPage: 3, page: 1, resultCap: 100); + $last = BoundedPaginator::paginate(User::query(), perPage: 3, page: 2, resultCap: 100); + + $this->assertTrue($notLast->hasMorePages()); + $this->assertFalse($last->hasMorePages()); + $this->assertCount(3, $notLast->items(), 'per_page + 1 로 읽어도 per_page 만 돌려준다'); + $this->assertCount(3, $last->items()); + } + + /** + * 마지막 페이지가 정확히 채워졌을 때 다음 페이지가 없다고 판정하는지 확인 + */ + public function test_exactly_full_last_page_reports_no_more(): void + { + User::factory()->count(4)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 2, page: 2, resultCap: 100); + + $this->assertFalse($page->hasMorePages()); + $this->assertSame(2, $page->lastPage()); + } + + /** + * GROUP BY 쿼리의 총 건수가 그룹 수인지 확인 (행 수가 아님) + */ + public function test_group_by_counts_groups_not_rows(): void + { + User::factory()->count(3)->create(['status' => 'active']); + User::factory()->count(2)->create(['status' => 'inactive']); + + $query = User::query() + ->select('status', DB::raw('count(*) as c')) + ->groupBy('status'); + + [$total, $relation] = BoundedPaginator::countWithCap($query, 100); + + $this->assertSame(2, $total, 'status 그룹은 2개'); + $this->assertSame(TotalRelation::Exact, $relation); + } + + /** + * 상한이 null 이면 정확한 전량 COUNT 로 동작하는지 확인 + */ + public function test_null_cap_counts_everything_exactly(): void + { + User::factory()->count(5)->create(); + + [$total, $relation] = BoundedPaginator::countWithCap(User::query(), null); + + $this->assertSame(User::query()->count(), $total); + $this->assertSame(TotalRelation::Exact, $relation); + } + + /** + * 호출자의 빌더가 페이지 조회로 오염되지 않는지 확인 + */ + public function test_caller_builder_is_not_mutated(): void + { + User::factory()->count(5)->create(); + + $query = User::query(); + BoundedPaginator::paginate($query, perPage: 2, page: 1, resultCap: 100); + + $this->assertNull($query->toBase()->limit, '페이지 조회가 원본 빌더에 LIMIT 을 남기면 안 된다'); + $this->assertSame(5, $query->count()); + } + + /** + * 쿼리 빌더(`DB::table`)를 그대로 넘겨도 동작하는지 확인 + * + * 이 클래스는 "임의의 빌더" 를 받는다고 선언하고 supports() 도 쿼리 빌더를 허용한다. + * 그런데 집계 경로가 Eloquent 전용 메서드를 무조건 부르면, 모델을 거치지 않는 호출이 + * 예외로 죽는다. 계약이 연 입구는 실제로 통해야 한다. + */ + public function test_plain_query_builder_is_supported(): void + { + User::factory()->count(5)->create(); + + $this->assertTrue(BoundedPaginator::supports(DB::table('users'))); + + [$total, $relation] = BoundedPaginator::countWithCap(DB::table('users'), 100); + + $this->assertSame(5, $total); + $this->assertTrue($relation->isExact()); + + $page = BoundedPaginator::paginate(DB::table('users')->orderBy('id'), perPage: 2, page: 1, resultCap: 100); + + $this->assertCount(2, $page->items()); + $this->assertSame(5, $page->total()); + $this->assertTrue($page->hasMorePages()); + } + + /** + * 관계(`$model->relation()`)를 그대로 넘겨도 동작하는지 확인 + * + * Laravel 에서 `$user->notifications()->paginate()` 는 관계가 빌더로 호출을 넘겨 주기 + * 때문에 성립한다. 정적 메서드는 그 전달을 받지 못하므로 관계를 직접 이해해야 한다. + * 이 입구가 막히면 "회원 1명에 종속된 목록" 이라는 가장 흔한 형태가 통째로 500 이 된다. + */ + public function test_eloquent_relation_is_supported(): void + { + $user = User::factory()->create(); + + foreach (range(1, 3) as $i) { + $user->notifications()->create([ + 'id' => (string) Str::uuid(), + 'type' => 'test', + 'data' => ['message' => 'n'.$i], + ]); + } + + $this->assertTrue(BoundedPaginator::supports($user->notifications())); + + $page = BoundedPaginator::paginate($user->notifications(), perPage: 2, page: 1, resultCap: 100); + + $this->assertCount(2, $page->items()); + $this->assertSame(3, $page->total(), '관계의 소속 조건이 유지되어야 한다'); + $this->assertTrue($page->hasMorePages()); + + $count = BoundedPaginator::count($user->notifications(), 100); + $this->assertSame(3, $count->total()); + } + + /** + * 관계 입력에서도 상한이 실제로 걸리는지 확인 (회귀 수정이 성능을 되돌리지 않았는가) + * + * 관계를 받아들이게 넓히면서 "그냥 표준 paginate 로 흘려보내기" 로 고치면 타입 오류는 + * 사라지지만 상한이 함께 사라진다. 오류가 없어졌다는 것만으로는 이 계약이 지켜졌다고 + * 말할 수 없으므로, 관계 경로에서도 잘림이 보고되는지를 따로 못박는다. + */ + public function test_relation_still_honours_the_cap(): void + { + $user = User::factory()->create(); + + foreach (range(1, 6) as $i) { + $user->notifications()->create([ + 'id' => (string) Str::uuid(), + 'type' => 'test', + 'data' => ['message' => 'n'.$i], + ]); + } + + $page = BoundedPaginator::paginate($user->notifications(), perPage: 2, page: 1, resultCap: 3); + + $this->assertSame(3, $page->total(), '상한이 걸리지 않았다 — 관계 경로가 표준 집계로 새고 있다'); + $this->assertSame(TotalRelation::AtLeast, $page->totalRelation()); + $this->assertNull($page->lastPage(), '잘렸으면 마지막 페이지를 계산할 수 없다'); + $this->assertTrue($page->hasMorePages(), '상한과 무관하게 다음 이동은 열려 있어야 한다'); + } + + /** + * 쿼리 빌더에서도 상한 초과가 "이상" 으로 보고되는지 확인 + */ + public function test_plain_query_builder_reports_at_least_over_cap(): void + { + User::factory()->count(5)->create(); + + [$total, $relation] = BoundedPaginator::countWithCap(DB::table('users'), 2); + + $this->assertSame(2, $total); + $this->assertSame(TotalRelation::AtLeast, $relation); + } + + /** + * 이 계약의 입력 범위가 표준 `paginate()` 보다 좁지 않은지 확인 + * + * 이 계약은 기존 `->paginate()` 호출을 대체하려고 만들었다. 그런데 받는 타입이 표준보다 + * 좁으면, 바꾸는 것만으로 멀쩡하던 목록이 죽는다 — 실제로 그렇게 두 번 터졌다 + * (쿼리 빌더에서 BadMethodCallException, 관계에서 TypeError). + * + * 그래서 "표준에서 되는 형태는 여기서도 된다" 를 형태별로 고정한다. 새 형태를 지원하게 + * 넓힐 때 이 목록에 추가하고, 좁히는 변경은 여기서 걸린다. + */ + public function test_accepts_every_shape_standard_paginate_accepts(): void + { + $user = User::factory()->create(); + + $user->notifications()->create([ + 'id' => (string) Str::uuid(), + 'type' => 'test', + 'data' => ['message' => 'n'], + ]); + + $shapes = [ + 'Eloquent 빌더' => User::query(), + '쿼리 빌더' => DB::table('users'), + '관계' => $user->notifications(), + ]; + + foreach ($shapes as $label => $query) { + $this->assertTrue( + BoundedPaginator::supports($query), + "supports() 가 {$label} 를 거부한다 — 표준 paginate 는 받는 형태다" + ); + + // 실제로 통과하는지까지 본다. supports() 만 true 이고 본체가 죽는 경우가 있었다. + $page = BoundedPaginator::paginate($query, perPage: 1, page: 1, resultCap: 100); + + $this->assertInstanceOf( + BoundedPage::class, + $page, + "{$label} 입력이 페이지 결과를 만들지 못했다" + ); + $this->assertSame( + TotalRelation::Exact, + $page->totalRelation(), + "{$label} 입력에서 정확도 계약이 깨졌다" + ); + } + } + + /** + * 정렬이 걸린 빌더도 상한 집계가 성립하는지 확인 + */ + public function test_ordered_query_is_countable(): void + { + User::factory()->count(5)->create(); + + $page = BoundedPaginator::paginate( + User::query()->orderBy('id', 'desc'), + perPage: 2, + page: 1, + resultCap: 3 + ); + + $this->assertSame(3, $page->total()); + $this->assertSame(TotalRelation::AtLeast, $page->totalRelation()); + } + + /** + * BoundedPage 가 표준 페이지네이터 인터페이스를 그대로 만족하는지 확인 + */ + public function test_bounded_page_is_a_standard_paginator(): void + { + User::factory()->count(5)->create(); + + $page = BoundedPaginator::paginate(User::query(), perPage: 2, page: 2, resultCap: 100); + + $this->assertInstanceOf(BoundedPage::class, $page); + $this->assertInstanceOf(LengthAwarePaginator::class, $page); + $this->assertSame(2, $page->currentPage()); + $this->assertSame(2, $page->perPage()); + $this->assertSame(3, $page->firstItem()); + $this->assertSame(4, $page->lastItem()); + } +} diff --git a/tests/Unit/Support/Query/PaginationContractTest.php b/tests/Unit/Support/Query/PaginationContractTest.php new file mode 100644 index 00000000..a5b155a6 --- /dev/null +++ b/tests/Unit/Support/Query/PaginationContractTest.php @@ -0,0 +1,240 @@ +count(5)->create(); + + $meta = $this->metaFor(User::query()->paginate(2, ['*'], 'page', 1)); + + $this->assertSame( + ['current_page', 'per_page', 'from', 'to', 'has_more_pages', 'last_page', 'total'], + array_keys($meta), + '표준 paginate 응답에는 상한형 전용 필드가 붙지 않는다' + ); + $this->assertSame(1, $meta['current_page']); + $this->assertSame(2, $meta['per_page']); + $this->assertSame(3, $meta['last_page']); + $this->assertSame(5, $meta['total']); + $this->assertSame(1, $meta['from']); + $this->assertSame(2, $meta['to']); + $this->assertTrue($meta['has_more_pages']); + } + + /** + * 상한형 페이지가 정확도 메타를 함께 내보내는지 확인 (상한 이하) + */ + public function test_bounded_meta_reports_exact_total(): void + { + User::factory()->count(5)->create(); + + $meta = $this->metaFor(BoundedPaginator::paginate(User::query(), 2, 1, 100)); + + $this->assertSame('exact', $meta['total_relation']); + $this->assertTrue($meta['total_is_exact']); + $this->assertSame(100, $meta['result_cap']); + $this->assertSame(5, $meta['total']); + $this->assertSame(3, $meta['last_page']); + } + + /** + * 상한 초과 시 정확도 메타가 하한이고 마지막 페이지가 비는지 확인 + */ + public function test_bounded_meta_reports_at_least_and_null_last_page(): void + { + User::factory()->count(9)->create(); + + $meta = $this->metaFor(BoundedPaginator::paginate(User::query(), 2, 1, 4)); + + $this->assertSame('at_least', $meta['total_relation']); + $this->assertFalse($meta['total_is_exact']); + $this->assertSame(4, $meta['result_cap']); + $this->assertSame(4, $meta['total']); + $this->assertNull($meta['last_page'], '마지막 페이지 점프는 계산 불가'); + $this->assertTrue($meta['has_more_pages'], '"다음" 이동은 계속 열려 있다'); + } + + /** + * 단순형(simplePaginate)은 총 건수를 세지 않으므로 total/last_page 를 내보내지 않는지 확인 + */ + public function test_simple_paginator_meta_omits_total(): void + { + User::factory()->count(5)->create(); + + $meta = $this->metaFor(User::query()->simplePaginate(2, ['*'], 'page', 1)); + + $this->assertArrayNotHasKey('total', $meta, '세지 않은 값을 0 으로 채우면 화면이 그것을 사실로 읽는다'); + $this->assertArrayNotHasKey('last_page', $meta); + $this->assertTrue($meta['has_more_pages']); + } + + /** + * 커서형 페이지가 앞뒤 커서를 메타로 내보내는지 확인 + */ + public function test_cursor_paginator_meta_exposes_cursors(): void + { + User::factory()->count(5)->create(); + + $first = User::query()->orderBy('id')->cursorPaginate(2); + $meta = $this->metaFor($first); + + $this->assertArrayHasKey('next_cursor', $meta); + $this->assertArrayHasKey('prev_cursor', $meta); + $this->assertNotNull($meta['next_cursor']); + $this->assertNull($meta['prev_cursor'], '첫 페이지에는 이전 커서가 없다'); + $this->assertTrue($meta['has_more_pages']); + } + + /** + * 커서로 끝까지 이동해도 행이 새거나 겹치지 않는지 확인 + */ + public function test_keyset_cursor_round_trip_covers_every_row_once(): void + { + User::factory()->count(7)->create(); + + $seen = []; + $cursor = null; + + do { + $page = KeysetPaginator::paginate( + User::query(), + perPage: 2, + sortKeys: [['created_at', 'desc']], + uniqueKey: 'id', + cursor: $cursor + ); + + foreach ($page->items() as $user) { + $seen[] = $user->id; + } + + $cursor = KeysetPaginator::nextCursor($page); + } while ($cursor !== null); + + $expected = User::query()->pluck('id')->all(); + + sort($expected); + $sortedSeen = $seen; + sort($sortedSeen); + + $this->assertSame($expected, $sortedSeen, '모든 행이 정확히 한 번씩 나와야 한다'); + $this->assertSame(count($seen), count(array_unique($seen)), '중복 노출 금지'); + } + + /** + * 깨진 커서 문자열이 예외 대신 첫 페이지로 처리되는지 확인 + */ + public function test_malformed_cursor_falls_back_to_first_page(): void + { + User::factory()->count(3)->create(); + + $this->assertNull(KeysetPaginator::decode('not-a-valid-cursor')); + + $page = KeysetPaginator::paginate( + User::query(), + perPage: 2, + sortKeys: [['id', 'desc']], + uniqueKey: 'id', + cursor: 'not-a-valid-cursor' + ); + + $this->assertCount(2, $page->items()); + } + + /** + * 계산값 정렬은 커서 대상이 아님을 판정하는지 확인 + */ + public function test_supports_rejects_non_column_sort_keys(): void + { + $whitelist = ['created_at', 'id']; + + $this->assertTrue(KeysetPaginator::supports([['created_at', 'desc']], $whitelist)); + $this->assertFalse( + KeysetPaginator::supports([['_ft_score', 'desc']], $whitelist), + '계산값은 WHERE 절 경계로 쓸 수 없다' + ); + $this->assertFalse(KeysetPaginator::supports([], $whitelist)); + } + + /** + * 한계값이 config 기본값 → 환경설정 → 필터 훅 순으로 해석되는지 확인 + */ + public function test_pagination_limits_resolution_order(): void + { + config(['core.pagination.result_cap' => 5000, 'g7_settings.core.pagination' => []]); + $this->assertSame(5000, PaginationLimits::resultCap(), 'config 기본값'); + + config(['g7_settings.core.pagination.result_cap' => 777]); + $this->assertSame(777, PaginationLimits::resultCap(), '관리자 환경설정이 config 를 덮는다'); + + HookManager::addFilter('core.pagination.filter_result_cap', fn ($value) => 42); + $this->assertSame(42, PaginationLimits::resultCap(), '확장 필터 훅이 최종 결정'); + } + + /** + * 한계값 0 이 무제한(null)으로 해석되는지 확인 + */ + public function test_zero_limit_means_unlimited(): void + { + config(['g7_settings.core.pagination.result_cap' => 0, 'g7_settings.core.pagination.max_page' => 0]); + + $this->assertNull(PaginationLimits::resultCap()); + $this->assertNull(PaginationLimits::maxPage()); + } + + /** + * 페이지네이터의 pagination 메타를 뽑아냅니다. + * + * @param mixed $paginator 페이지 결과 + * @return array pagination 메타 + */ + private function metaFor(mixed $paginator): array + { + $collection = new PaginationMetaProbeCollection($paginator); + + return $collection->toArray(Request::create('/'))['pagination'] ?? []; + } +} + +/** + * paginationMeta() 결과만 확인하기 위한 테스트 전용 컬렉션 + */ +class PaginationMetaProbeCollection extends BaseApiCollection +{ + /** + * pagination 메타만 담은 배열을 반환합니다. + * + * @param Request $request HTTP 요청 객체 + * @return array pagination 메타 + */ + public function toArray(Request $request): array + { + return $this->paginationMeta(); + } +} diff --git a/tests/scenarios/bounded-total-pagination.yaml b/tests/scenarios/bounded-total-pagination.yaml new file mode 100644 index 00000000..250b3dbf --- /dev/null +++ b/tests/scenarios/bounded-total-pagination.yaml @@ -0,0 +1,164 @@ +# audit:allow test-scenario-coverage reason: 이슈 #519(공개 #82) 상한 총 건수·정확도 계약 SSoT. axes cross product 는 저장소별 호출처 전수에 걸쳐 있어 docblock 전수 @scenario 매핑이 비현실적이라 면제하고, 회귀 가드는 test_files(코어 계약 단위 + 도메인별 소비자 + 화면 소비단 + Playwright E2E)로 커버한다. + +feature: bounded_total_pagination + +description: | + 대용량 목록에서 총 건수를 세는 비용과 페이지를 이동하는 능력을 분리한다. + + 종전에는 둘이 묶여 있었다. 총 건수를 정확히 세려면 술어 전체를 훑어야 하고, 그 비용이 + 감당되지 않으면 목록 자체를 열지 못했다. 반대로 비용을 피하려고 총 건수를 포기하면 + "다음" 이동까지 함께 사라졌다. + + 핵심 설계: + - **총 건수만 상한을 받는다.** `SELECT COUNT(*) FROM (SELECT 1 FROM ... LIMIT cap+1) t` + 로 감싸므로 상한 이하면 표준 COUNT 와 값이 같고, 초과할 때만 "그 이상" 이 된다. + - **다음 페이지 판정은 총 건수와 무관하다.** `per_page + 1` 을 읽어 초과분 존재 + 여부로 판정하므로 총 건수를 몰라도 끝까지 정확하다. + - 계산이 불가능해지는 것은 **마지막 페이지 번호 하나뿐**이며, 그 사실은 `last_page: null` + 로 알린다. 1 로 채우면 화면이 "1페이지뿐" 이라고 잘못 말한다. + - 건수만 필요한 자리(탭 배지)는 `BoundedCount` 로 **건수와 정확도를 함께** 옮긴다. + `int` 하나만 돌려주면 잘린 10,000 이 "정확히 10,000 건" 으로 화면에 나간다. + - 최신순 목록은 커서(키셋)로 전환할 수 있다. 관련도순(`_ft_score`)은 계산값이라 + WHERE 경계로 쓸 수 없어 offset 을 유지한다. + - 상한값은 `PaginationLimits` 단일 해석 — 저장소·화면에 리터럴로 재기입하지 않는다. + +axes: + total_relation: [exact, at_least] + consumer: [list_page, tab_badge, aggregate_sum] + navigation: [offset_page_number, keyset_cursor] + position: [first, middle, last] + query_shape: [flat, grouped, deferred_join, fulltext] + cap_source: [settings, config_fallback, hook_override, unlimited] + surface: [core_repository, module_search, admin_datagrid, storefront_grid] + +exclusions: + - { navigation: keyset_cursor, query_shape: fulltext, reason: "관련도순은 계산값 정렬이라 커서 경계로 쓸 수 없다 — KeysetPaginator::supports 가 거부하고 offset 을 유지한다" } + - { navigation: keyset_cursor, consumer: aggregate_sum, reason: "커서 응답에는 총 건수가 없다 — 합산 대상이 아니다" } + - { total_relation: at_least, cap_source: unlimited, reason: "상한이 없으면 언제나 정확히 센다" } + - { consumer: tab_badge, position: middle, reason: "배지는 페이지 개념이 없다 — 건수 하나만 표시한다" } + +effects: + # ── 총 건수 정확도 (P0) ── + - total_is_exact_under_cap + - total_reports_at_least_over_cap + - unlimited_cap_always_counts_exactly + - grouped_query_counts_groups_not_first_group_rows + - bounded_count_carries_relation_and_cap + # ── 페이지 이동 (P0) ── + - next_navigation_stays_open_when_total_truncated + - last_page_is_null_when_total_truncated + - last_page_closes_next_navigation + - deep_page_offset_does_not_drift + - page_number_upper_bound_is_enforced + # ── 커서 (P0) ── + - cursor_request_switches_to_keyset + - cursor_round_trip_covers_every_row_once + - malformed_cursor_falls_back_to_first_page + - relevance_sort_rejects_cursor + # ── 응답 계약 (P0) ── + - standard_paginate_envelope_is_unchanged + - bounded_envelope_adds_accuracy_fields + - simple_paginator_omits_total_and_last_page + - cursor_paginator_exposes_cursors + # ── 검색 커서 (P0) ── + # 커서 판정과 응답 조립을 코어가 소유한다 — 도메인마다 복제되면 규칙이 갈라진다. + - cursor_requires_explicit_cursor_param + - cursor_requires_real_columns + - unknown_sort_name_falls_back_to_offset + - search_latest_sort_uses_cursor + - search_relevance_sort_stays_on_offset + - search_cursor_payload_keeps_offset_key_set + # ── 집계 정확도 (P1) ── + - aggregate_is_inexact_when_any_category_truncated + - single_tab_accuracy_ignores_other_categories + - badge_count_carries_accuracy_when_truncated + - badge_accuracy_is_emitted_per_category + - badge_accuracy_defaults_to_exact + # ── 일괄 변경 (P0) ── + # 단일 statement 로 합치는 과정에서 값이 실리는 경로가 깨지면 조용히 엉뚱한 값이 기록된다. + - bulk_price_increase_applies_exact_amount + - bulk_price_percent_applies_exact_ratio + - bulk_price_floors_at_zero + - bulk_stock_increase_applies_exact_amount + - bulk_stock_floors_at_zero + - bulk_adjustment_touches_only_selected_rows + # ── 화면 소비단 (P1) ── + - datagrid_preserves_null_last_page + - datagrid_shows_pager_when_only_has_more_pages_is_known + - storefront_grid_pager_uses_has_more_pages + - shared_partial_reads_parent_declared_names + # ── 절단 노출 (P1) ── + - comment_list_discloses_truncation + - comment_count_reports_at_least_over_cap + # ── 한계값 해석 (P1) ── + - limits_resolve_settings_then_config_then_hook + - zero_limit_means_unlimited + # ── 종단(브라우저) 확인 (P1) ── + # 위 항목들은 계약 단위이고, 아래 넷은 그 계약이 실제 화면까지 도달했는지를 본다. + # 응답이 정상이어도 바인딩 경로가 어긋나면 화면만 조용히 비므로 API 테스트로는 안 잡힌다. + - search_count_marks_inexact_total + - search_pager_survives_null_last_page + - storefront_uses_single_request + - storefront_sections_render + # ── 요청당 중복 조회 제거 (F3 / F4) ── + # 같은 행을 두 번 읽는 것은 응답이 정상이라 어떤 기능 테스트에도 걸리지 않는다. + # "몇 번 읽었는가" 를 직접 세야만 회귀가 드러난다. + - post_detail_reads_post_row_once + - post_detail_reads_board_row_once + - storefront_category_matches_single_endpoint + - storefront_without_slug_omits_category + - storefront_unknown_slug_returns_not_found + +# scale: 대용량 회귀 축. block-array of flow objects (내장 fallback 파서 제약 — flow 객체만 중첩 파싱) +scale: + - { n: 200000, dataset: activity_logs, assert: [total_reports_at_least_over_cap, next_navigation_stays_open_when_total_truncated, cursor_round_trip_covers_every_row_once] } + +test_files: + # 코어 — 상한 페이지네이터 계약 (총 건수 / 페이지 경계 / offset) + - tests/Unit/Support/Query/BoundedPaginatorTest.php + # 코어 — 건수 전용 값 객체 (배지 자리의 정확도 전달) + - tests/Unit/Support/Query/BoundedCountTest.php + # 코어 — 응답 봉투 4형태 + 한계값 해석 + 커서 왕복 + - tests/Unit/Support/Query/PaginationContractTest.php + # 코어 — 지연 조인 목록의 상한 계약 + 커서 전환 (검색 밖 재사용 증명) + - tests/Feature/Performance/DeferredJoinBoundedTotalTest.php + # 코어 — 통합 검색 응답의 정확도 메타 + 페이지 상한 + - tests/Feature/Search/SearchAccuracyContractTest.php + # 코어 — 커서 적용 판정 규칙 (도메인이 아니라 코어가 소유한다는 성질) + - tests/Unit/Search/SearchPagePolicyTest.php + # 코어 — 탭 배지 정확도가 카테고리마다 응답에 실리는지 + - tests/Feature/Search/SearchBadgeAccuracyTest.php + # 게시판 — 검색이 커서 경로를 타는지 + 세 응답 형태의 키 집합이 같은지 + - modules/_bundled/sirsoft-board/tests/Feature/SearchCursorPaginationTest.php + # 이커머스 — 일괄 가격·재고 증감의 결과값 (단일 statement 전환 회귀) + - modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/ProductBulkAdjustmentTest.php + # 화면 — 상한 목록의 last_page=null 을 레이아웃이 1 로 붕괴시키지 않는지 (전수 스캔) + - templates/_bundled/sirsoft-basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx + - templates/_bundled/sirsoft-admin_basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx + # 코어 — 행 수를 늘려도 쿼리 수가 늘지 않는지 (N+1 회귀) + - tests/Feature/Performance/ListQueryCountRegressionTest.php + # 게시판 — 댓글 절단 사실이 응답까지 도달하는지 + - modules/_bundled/sirsoft-board/tests/Feature/CommentTruncationDisclosureTest.php + # 게시판 — 목록·댓글 트리 쿼리 수 회귀 + - modules/_bundled/sirsoft-board/tests/Feature/ListQueryCountRegressionTest.php + # 이커머스 — 상품 목록·공개 목록·카테고리 트리 쿼리 수 회귀 + - modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/ListQueryCountRegressionTest.php + # 이커머스 — 집계 캐시와 무효화 (낡은 수치 방지) + - modules/_bundled/sirsoft-ecommerce/tests/Feature/ListCacheInvalidationTest.php + # 이커머스 — 진열·판매 집계 색인이 옵티마이저에 실제로 잡히는지 + - modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/StorefrontIndexCoverageTest.php + # 이커머스 — 비활성 탭 배지의 정확도 전달 + - modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/SearchProductsListenerTest.php + # 페이지 — 발행 목록 정렬 색인 + 검색 쿼리 수 회귀 + - modules/_bundled/sirsoft-page/tests/Feature/Performance/PublishedSortIndexCoverageTest.php + - modules/_bundled/sirsoft-page/tests/Feature/Performance/ListQueryCountRegressionTest.php + # 페이지 — 비활성 탭 배지의 정확도 전달 + - modules/_bundled/sirsoft-page/tests/Unit/Listeners/SearchPagesListenerTest.php + # 화면 — 공유 partial 이 부모가 선언한 이름을 읽는지 + 페이저가 has_more_pages 로 열리는지 + - templates/_bundled/sirsoft-basic/__tests__/layouts/shop-shared-partial-datasource-contract.test.tsx + # 중복 조회 — 상세에서 같은 글/게시판을 몇 번 읽는지 + - modules/_bundled/sirsoft-board/tests/Feature/PostDetailQueryCountRegressionTest.php + # 통합 조회의 단건 분류가 개별 엔드포인트와 같은 계약인지 + - modules/_bundled/sirsoft-ecommerce/tests/Feature/StorefrontCategoryContractTest.php + # 브라우저 — 상한 목록의 다음 이동 / 마지막 페이지 감춤 / 쇼핑 첫 화면 단일 요청 + - tests/Playwright/specs/pagination/bounded-total-and-storefront.spec.ts From 41fe812f2cf4c0a0d89ac6b71c16f7060417b830 Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:15:00 +0900 Subject: [PATCH 2/8] =?UTF-8?q?perf(board):=20=EB=AA=A9=EB=A1=9D=C2=B7?= =?UTF-8?q?=EA=B2=80=EC=83=89=C2=B7=EC=83=81=EC=84=B8=EC=9D=98=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=8B=A4=ED=96=89=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=81=ED=95=9C=20=EA=B3=84=EC=95=BD=20=EC=86=8C=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 검색 — 이 코어에 추가한 BOOLEAN MODE 정제기가 게시판에는 도달하지 않아 원문 키워드가 그대로 바인딩되고 있었다. + - * " < > 를 넣으면 오류 화면이 떴다. 코어 정제기 경유로 옮겼고, 같은 술어를 count 와 get 으로 두 번 실행하던 것을 페이지네이터 한 번으로 합쳤다. 구체 엔진 직접 호출도 해석기 경유로 바꿔 플러그인 검색엔진이 게시글·신고 검색에 적용된다. 상세 — 같은 글을 3회, 같은 게시판을 5회 읽고 있었다. 조회한 인스턴스를 넘겨 글 1회·게시판 1회로 줄였고, 조회수 증가를 권한 확인 뒤로 옮겨 열람 권한이 없는 요청이 조회수를 올리지 않게 했다. 목록 — 답변 트리의 무제한 조회에 상한과 순환 가드를 두고, O(n²) 병합을 한 패스로 바꿨다. 댓글도 무제한 조회에 상한을 두되 잘렸다는 사실과 전체 건수를 함께 알린다. 조용히 잘리면 "댓글이 그만큼뿐" 으로 보인다. 원댓글 단위 페이지네이션을 더해 답글이 부모와 떨어지지 않게 했다. 관리자 화면 — 총 건수가 잘린 목록에서 페이저가 "1페이지뿐" 으로 접히던 자리를 고쳤다. 저장소가 상한을 붙인 목록인데 화면이 그 값을 1 로 채우고 있어, 기록이 상한을 넘으면 2페이지에 도달할 방법이 없었다. 같은 파일에서 드러난 사전 결함도 함께 처리했다 — 반복 렌더 안의 정적 HTML id, Icon 의 박스 크기 지정, 단색 버튼의 다크 모드 대비. --- modules/_bundled/sirsoft-board/CHANGELOG.md | 21 ++ .../_bundled/sirsoft-board/docs/api/board.md | 19 ++ .../layouts/admin/admin_board_index.json | 14 +- .../admin/admin_board_posts_index.json | 9 +- .../admin/admin_board_reports_index.json | 3 +- .../_card_report_info.json | 36 +-- .../_tab_identity_policies.json | 8 +- .../_tab_notification_definitions.json | 3 +- .../Http/Controllers/User/PostController.php | 110 +++++++- .../src/Http/Resources/PostCollection.php | 58 ++++- .../src/Http/Resources/PostResource.php | 41 ++- .../src/Listeners/SearchPostsListener.php | 169 +++++++----- .../src/Repositories/CommentRepository.php | 179 ++++++++++++- .../Contracts/CommentRepositoryInterface.php | 44 ++++ .../Contracts/PostRepositoryInterface.php | 65 ++++- .../src/Repositories/PostRepository.php | 244 ++++++++++++------ .../src/Repositories/ReportRepository.php | 19 +- .../src/Services/CommentService.php | 89 +++++++ .../src/Services/PostService.php | 174 ++++++++++--- .../src/Support/BoardPermissionCacheKeys.php | 27 ++ .../src/Traits/ChecksBoardPermission.php | 8 +- .../sirsoft-board/tests/BoardTestCase.php | 6 +- .../CommentTruncationDisclosureTest.php | 121 +++++++++ .../Feature/ListQueryCountRegressionTest.php | 104 ++++++++ .../PostDetailQueryCountRegressionTest.php | 125 +++++++++ .../PostNumberBoundedTotalRegressionTest.php | 152 +++++++++++ .../PostSearchKeywordSanitizationTest.php | 181 +++++++++++++ .../Feature/SearchCursorPaginationTest.php | 205 +++++++++++++++ .../sirsoft-board/tests/ModuleTestCase.php | 78 +++--- .../Listeners/SearchPostsListenerTest.php | 112 ++++---- .../Unit/Traits/ChecksBoardPermissionTest.php | 40 ++- 31 files changed, 2092 insertions(+), 372 deletions(-) create mode 100644 modules/_bundled/sirsoft-board/src/Support/BoardPermissionCacheKeys.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/CommentTruncationDisclosureTest.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/ListQueryCountRegressionTest.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/PostDetailQueryCountRegressionTest.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/PostNumberBoundedTotalRegressionTest.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/PostSearchKeywordSanitizationTest.php create mode 100644 modules/_bundled/sirsoft-board/tests/Feature/SearchCursorPaginationTest.php diff --git a/modules/_bundled/sirsoft-board/CHANGELOG.md b/modules/_bundled/sirsoft-board/CHANGELOG.md index 17ee4e4e..81fda7fb 100644 --- a/modules/_bundled/sirsoft-board/CHANGELOG.md +++ b/modules/_bundled/sirsoft-board/CHANGELOG.md @@ -19,18 +19,36 @@ ### Changed +- 통합 검색의 게시글 결과를 최신순·오래된순·조회순으로 볼 때 이어보기 방식으로 뒤쪽 페이지를 이동할 수 있습니다. 관련도순은 종전의 페이지 번호 방식을 유지합니다. +- 플러그인으로 다른 검색엔진을 연결하면 게시글·신고 검색에도 그 엔진이 적용됩니다. 이전에는 연결한 엔진과 무관하게 기본 방식으로만 검색됐습니다. +- 게시글을 열 때 같은 글과 같은 게시판을 여러 번 다시 읽던 것을 정리했습니다. 글은 한 번만, 게시판도 한 번만 읽습니다. 화면에 보이는 내용은 동일하며 글이 열리는 속도가 빨라집니다. 조회수는 열람 권한을 확인한 뒤에만 올라갑니다. - 사이트맵을 만들 때 게시판과 게시글을 한 번에 모두 메모리에 올리지 않고 나눠서 읽도록 바꿨습니다. 게시글이 많은 사이트에서 사이트맵 생성 중 메모리가 부족해 실패하던 문제가 줄어듭니다. - 게시글 목록을 뒤쪽 페이지에서도 빠르게 열 수 있도록 조회 방식을 바꿨습니다. 예전에는 페이지가 뒤로 갈수록 건너뛰는 게시글의 본문 앞부분까지 함께 읽어 느려졌지만, 이제 현재 페이지의 게시글만 본문 미리보기를 읽습니다. 공지 노출·답글 표시·다음 페이지 버튼은 이전과 동일하게 동작합니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) - 신고 관리 목록도 같은 방식으로 개선했습니다. 신고 건수·대상 글 상태처럼 목록에 함께 표시되는 정보를 예전에는 건너뛰는 신고까지 모두 계산했지만, 이제 현재 페이지의 신고에 대해서만 계산합니다. 신고가 많이 쌓인 사이트에서 목록 페이지 이동이 빨라집니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) - 마이페이지의 「내가 쓴 댓글」·「내가 댓글 단 글」 목록도 같은 방식으로 개선했습니다. 각 글의 최근 댓글을 예전에는 건너뛰는 글까지 모두 찾아봤지만, 이제 현재 페이지의 글에 대해서만 찾습니다. 댓글 활동이 많은 회원의 목록 페이지 이동이 빨라집니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) - 게시글이 많은 게시판에서 목록 정렬이 더 빨라지도록 색인을 정비했습니다. 신고 관리 목록에도 같은 정비를 적용했습니다. 같은 시각에 등록된 게시글이 많은 구간에서 순서를 정하느라 생기던 추가 작업이 사라집니다. 기존 사이트도 업데이트 시 자동으로 반영되며, 게시글이 아주 많은 경우 이 과정에 수 분이 걸리고 그동안 글쓰기가 잠시 대기할 수 있습니다. - 관리자 게시판 목록이 게시판마다 매니저·스텝 역할과 그 역할에 속한 회원 명단(이름·이메일)을 함께 내려주지 않습니다. 목록 화면이 쓰지 않는 값이라 게시판 수만큼 늘어나던 조회와 전송량이 사라집니다. 역할 지정은 종전대로 게시판 상세·설정 화면에서 하며, 목록에 보이는 항목은 이전과 동일합니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) +- 한 글의 댓글이 아주 많아 목록이 일정 수에서 끊긴 경우, 끊겼다는 사실과 전체 건수를 함께 알려 줍니다. 예전에는 조용히 잘려 "댓글이 그만큼뿐" 으로 보였습니다. +- 댓글이 아주 많은 글에서도 뒤쪽 댓글을 볼 수 있도록 댓글 목록을 나눠 받을 수 있게 했습니다. 페이지 단위는 원댓글이며, 그 원댓글에 달린 답글은 함께 따라오므로 답글이 부모와 떨어지지 않습니다. 기존처럼 한 번에 받는 방식도 그대로 동작합니다. +- 게시글 검색 탭의 배지 숫자도 세지 못한 건수를 정확한 것처럼 표시하지 않습니다. 배지를 표시하지 않는 화면에서는 그 숫자를 세는 작업 자체를 건너뛸 수 있습니다. +- 게시글 목록의 총 건수도 일정 규모까지만 정확히 세고 그보다 많으면 "N건 이상" 으로 표시합니다. 페이지 이동은 끝까지 열려 있습니다. +- 신고 관리 목록의 총 건수에도 같은 방식을 적용했습니다. - 개선된 사이트맵 생성 기능을 사용하기 위해 이 모듈은 이제 코어 7.0.6 이상이 필요합니다. +- 게시판 검색이 조건에 맞는 글을 두 번 조회하던 것을 한 번으로 합쳤습니다. 글이 많은 게시판일수록 검색이 빨라집니다. +- 검색 결과가 아주 많으면 총 건수를 "N건 이상" 으로 표시하고, 다음 페이지 이동은 끝까지 열어 둡니다. 마지막 페이지로 바로 뛰는 버튼만 이때 감춰집니다. +- 관리자 게시판 목록이 삭제글을 포함해 열릴 때마다 전체 건수를 다시 세던 것을 저장해 두고 재사용하도록 바꿨습니다. +- 답변글이 아주 많은 글을 열 때 답변 전체를 한꺼번에 읽던 것에 상한을 두고, 답변을 트리로 묶는 처리도 개선했습니다. +- 한 글의 댓글을 무제한으로 읽던 것에 상한을 두고, 같은 댓글을 두 번 가져오던 부분을 없앴습니다. ### Fixed - 게시판 알림 설정 화면에서 모든 알림의 제목·수신자·활성 여부가 비어 보이고 「이 채널에 대한 템플릿이 없습니다」로만 표시되던 문제를 수정했습니다. 편집 창을 열어도 내용이 채워지지 않았습니다. 채널을 전환하거나 새로고침해도 마찬가지였습니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) - 게시판 설정의 '새 글 표시 시간'이 저장 직후에는 숫자가 아닌 형태로 다뤄져, 조회 시점에 따라 값의 형태가 달라지던 문제를 수정했습니다. 새 글 표시 여부 판정에 쓰이는 값이므로 항상 숫자로 처리합니다. +- 게시글이 아주 많은 게시판에서 목록의 글 번호가 0 이나 음수로 표시되던 문제를 수정했습니다. 총 건수를 끝까지 세지 못하는 경우에는 번호를 지어내지 않고 「-」로 표시합니다. +- 통합 검색 결과에서 작성자 자리에 번역되지 않은 내부 문구가 그대로 보이던 문제를 수정했습니다. 이제 「비회원」으로 표시됩니다. +- 통합 검색을 최신순으로 볼 때 첫 페이지부터 이어보기 방식이 적용되도록 수정했습니다. 이전에는 뒤쪽 페이지로 갈수록 느려지는 방식이 계속 쓰였습니다. 주소로 특정 페이지를 열어 둔 링크는 종전대로 그 페이지를 보여 줍니다. +- 게시판 목록에서 총 건수를 끝까지 세지 못했을 때 "1페이지뿐" 으로 표시되어 뒤쪽 페이지를 볼 수 없던 문제를 수정했습니다. +- 마이페이지의 내가 쓴 글·내가 쓴 댓글 목록에서 총 건수를 끝까지 세지 못했을 때 페이지 이동 막대가 사라지던 문제를 수정했습니다. - 글을 쓰면서 파일을 함께 첨부하면 글은 저장되는데 첨부파일만 사라지던 문제를 수정했습니다. 첨부 개수·용량·형식 검사와 권한 확인은 모두 통과한 뒤 저장 단계에서만 빠져, 등록된 글에 첨부가 하나도 남지 않았습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) - 게시판 신고 정책의 자동 숨김 기준 횟수를 0(자동 숨김 사용 안 함)으로 입력할 수 없던 문제를 수정했습니다. 서버는 0을 "사용 안 함"으로 처리하는데 화면에서만 1 이상을 요구해, 0을 입력하면 저장은 되면서도 입력칸이 계속 오류 상태로 남았습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) - 설정 화면의 선택 항목(라디오 버튼)을 키보드 방향키로 고를 때 선택이 저장되지 않던 문제를 수정했습니다. 마우스 클릭은 정상 동작했으나, 키보드만 사용하는 경우 화면 표시와 실제 저장 값이 어긋날 수 있었습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) @@ -68,6 +86,9 @@ - 관리자 화면 일부 아이콘이 의도한 크기보다 크거나 작게 보이던 문제를 수정했습니다. - 관리자 화면 일부 버튼·배지가 다크 모드에서 밝은 색 그대로 표시되던 문제를 수정했습니다. - 게시판 모듈을 제거할 때 게시판별로 자동 생성된 관리자·승인 단계 역할이 1,000개를 넘으면 일부가 삭제되지 않고 남던 문제를 수정했습니다. 남은 역할은 사용하지 않는 게시판의 것이면서도 회원 권한 목록에 계속 노출됐습니다. 이제 개수와 무관하게 전부 정리됩니다. (#84 @glitter-gim 님께서 제보해주셨습니다.) +- 검색어에 `+` `-` `*` `"` `<` `>` 같은 기호를 넣으면 게시판 검색이 오류 화면을 띄우던 문제를 수정했습니다. 이제 어떤 문자를 입력해도 검색이 정상 동작합니다. +- 게시글 상세를 열 때 같은 글과 게시판을 여러 번 다시 조회하던 것을 정리했습니다. +- 관리자 게시판 목록·신고 관리 목록과 신고 상세의 신고자 목록에서 기록이 아주 많아 총 건수를 끝까지 세지 못하면 페이지 이동이 막히던 문제를 수정했습니다. 마지막 페이지로 바로 뛰는 버튼만 감춰지고 이전·다음 이동은 그대로 동작합니다. ## [1.0.2] - 2026-07-14 diff --git a/modules/_bundled/sirsoft-board/docs/api/board.md b/modules/_bundled/sirsoft-board/docs/api/board.md index 3e201398..e9ee083a 100644 --- a/modules/_bundled/sirsoft-board/docs/api/board.md +++ b/modules/_bundled/sirsoft-board/docs/api/board.md @@ -16,6 +16,22 @@ --- +## 목록·검색의 총 건수와 답변·댓글 상한 + +게시판 목록에 `search` 를 얹으면 내부 검색이 수행됩니다. 매칭이 아주 많을 수 있으므로 총 +건수는 상한까지만 세며, 상한을 넘으면 응답의 `pagination` 에 정확도가 함께 실립니다 +(`total_relation` / `total_is_exact` / `result_cap`). 이때 `last_page` 는 `null` 이고 +`has_more_pages` 는 그대로 정확하므로, 마지막 페이지 점프만 감춰지고 다음 페이지 이동은 +끝까지 열려 있습니다. 상세 규약은 [pagination.md](../../../../../docs/backend/pagination.md) 를 참고하세요. + +검색어에 `+` `-` `*` `"` `<` `>` 같은 문자가 들어와도 오류가 나지 않습니다. 코어 정제기가 +FULLTEXT 연산자를 제거한 뒤 검색하며, 연산자만 입력한 경우에는 오류 대신 빈 결과를 돌려줍니다. + +게시글 상세 응답의 답변 트리와 댓글 목록에도 같은 상한이 적용됩니다. 한 글에 답변·댓글이 +극단적으로 많은 경우 그 지점에서 끊기며, 총 건수는 목록 응답의 집계로 확인할 수 있습니다. + +--- + ### POST /api/modules/sirsoft-board/admin/board/{slug}/attachments @@ -864,6 +880,9 @@ _단건 응답: `data` 객체의 필드._ | navigation | object | `{"prev":null,"next":null}` | 이전/다음 게시글 이동 정보. `prev`·`next` 키에 인접 게시글 요약(없으면 null)이 담기며, 상세 로드 시 함께 계산됩니다. | | parent | null | `null` | 상위 항목 객체 (parent 관계 파생) | | comments | array | `[{"id":760,"post_id":237,"parent_id":null,"content":"API …` | 게시글에 달린 댓글 목록(CommentResource 컬렉션). comments 관계가 로드된 경우에만 채워지며, 각 항목에 신고 여부가 사전 로드되어 담깁니다. | +| comments_truncated | boolean | `false` | 댓글 목록이 상한에서 끊겼는지 여부. `true` 면 `comments` 에 실린 것이 전부가 아닙니다 | +| comments_total | integer\|null | `12` | 댓글 총 건수. 끊기지 않았으면 `comments` 길이와 같고, 끊겼으면 상한값(그 이상)입니다 | +| comments_total_is_exact | boolean | `true` | 위 총 건수가 정확한지 여부. `false` 면 "N건 이상" 으로 표기합니다 | | attachments | array | `[{"id":155,"hash":"apidocsmpl1","original_filename":"apid…` | 게시글 첨부파일 목록(AttachmentResource 컬렉션). 비밀글은 열람 권한이 없으면 빈 배열, 삭제된 게시글은 관리 권한이 없으면 연쇄 삭제된 첨부만 노출됩니다. | | replies | array | `[]` | 이 게시글에 달린 답변글 목록(PostResource 컬렉션, 재귀). replies 관계가 로드된 경우에만 채워지며, 아니면 null. | | is_already_reported | boolean | `false` | already reported 여부 | diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_index.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_index.json index 76caecc7..6806aa2e 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_index.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_index.json @@ -86,7 +86,7 @@ "name": "Icon", "props": { "name": "refresh", - "className": "w-5 h-5" + "className": "text-xl" } } ] @@ -116,7 +116,7 @@ "name": "Icon", "props": { "name": "plus", - "className": "w-5 h-5" + "className": "text-xl" } }, { @@ -183,7 +183,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{boards?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{boards?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{boards?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{boards?.data?.pagination?.has_more_pages ?? false}}", "alwaysShowPagination": true, "emptyMessage": "$t:sirsoft-board.admin.board.index.empty_description", "cardClassName": "rounded-lg bg-white dark:bg-gray-800 shadow-md hover:shadow-lg transition-shadow border border-gray-200 dark:border-gray-700 p-6 space-y-4", @@ -457,12 +458,13 @@ "text": "$t:sirsoft-board.admin.board.index.categories_label" }, { - "id": "category_tag", + "id": "category_tag_{{categoryIdx}}", "type": "composite", "name": "StatusBadge", "iteration": { "source": "row.categories", - "item_var": "category" + "item_var": "category", + "index_var": "categoryIdx" }, "props": { "status": "default", @@ -749,7 +751,7 @@ "type": "basic", "name": "Button", "props": { - "className": "flex-center gap-2 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed", + "className": "flex-center gap-2 px-4 py-2 bg-red-600 dark:bg-red-700 text-white dark:text-white rounded-lg hover:bg-red-700 dark:hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed", "disabled": "{{_global.isDeleting}}" }, "actions": [ diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_posts_index.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_posts_index.json index 55f2f9a9..08029efe 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_posts_index.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_posts_index.json @@ -744,7 +744,7 @@ "props": { "className": "inline-flex items-center px-2.5 py-0.5 rounded text-xs font-bold bg-blue-600 text-white dark:bg-blue-500" }, - "text": "{{row.number}}", + "text": "{{row.number ?? '-'}}", "if": "{{row.row_type === 'notice'}}" }, { @@ -753,7 +753,7 @@ "props": { "className": "inline-flex items-center px-2.5 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200" }, - "text": "{{row.number}}", + "text": "{{row.number ?? '-'}}", "if": "{{row.row_type === 'reply'}}" }, { @@ -762,7 +762,7 @@ "props": { "className": "font-medium text-gray-900 dark:text-white" }, - "text": "{{row.number}}", + "text": "{{row.number ?? '-'}}", "if": "{{row.row_type === 'normal'}}" } ] @@ -1003,7 +1003,8 @@ "pageSize": 15, "serverSidePagination": true, "serverCurrentPage": "{{posts?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{posts?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{posts?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{posts?.data?.pagination?.has_more_pages ?? false}}", "selectable": false, "responsiveBreakpoint": 768, "showFirstLast": true, diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_reports_index.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_reports_index.json index 43d337e9..1288dd10 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_reports_index.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/admin_board_reports_index.json @@ -2096,7 +2096,8 @@ "pageSize": 20, "serverSidePagination": true, "serverCurrentPage": "{{reports?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{reports?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{reports?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{reports?.data?.pagination?.has_more_pages ?? false}}", "selectable": true, "selectedIds": "{{_global.selectedIds || []}}", "idField": "id", diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_reports_detail/_card_report_info.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_reports_detail/_card_report_info.json index 268defa6..e6a96272 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_reports_detail/_card_report_info.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_reports_detail/_card_report_info.json @@ -81,7 +81,7 @@ "name": "Icon", "props": { "name": "chart-bar", - "className": "w-4 h-4" + "className": "text-base" } }, { @@ -121,7 +121,7 @@ "name": "Icon", "props": { "name": "calendar", - "className": "w-4 h-4" + "className": "text-base" } }, { @@ -163,7 +163,7 @@ "name": "Icon", "props": { "name": "circle-info", - "className": "w-4 h-4" + "className": "text-base" } }, { @@ -336,7 +336,7 @@ "name": "Icon", "props": { "name": "arrow-up-right-from-square", - "className": "w-3 h-3" + "className": "text-xs" } } ] @@ -410,7 +410,7 @@ "name": "Icon", "props": { "name": "arrow-up-right-from-square", - "className": "w-3 h-3 transform translate-x-0 group-hover:translate-x-0.5 transition-all" + "className": "text-xs transform translate-x-0 group-hover:translate-x-0.5 transition-all" } } ] @@ -470,7 +470,7 @@ "name": "Icon", "props": { "name": "arrow-up-right-from-square", - "className": "w-3 h-3 transform translate-x-0 group-hover:translate-x-0.5 transition-all" + "className": "text-xs transform translate-x-0 group-hover:translate-x-0.5 transition-all" } } ] @@ -511,7 +511,7 @@ "name": "Icon", "props": { "name": "clock", - "className": "w-4 h-4 text-gray-500 dark:text-gray-400" + "className": "text-base text-gray-500 dark:text-gray-400" } }, { @@ -618,7 +618,7 @@ }, "children": [ { - "id": "reporters_list_container", + "id": "reporters_list_container_{{reporterIdx}}", "type": "basic", "name": "Div", "blur_until_loaded": "{{_local.reporters_loading}}", @@ -627,11 +627,12 @@ }, "iteration": { "source": "reporters_list?.data?.data ?? report_detail?.data?.reporters ?? []", - "item_var": "reporter" + "item_var": "reporter", + "index_var": "reporterIdx" }, "children": [ { - "id": "reporter_item_card", + "id": "reporter_item_card_{{reporterIdx}}", "type": "basic", "name": "Div", "props": { @@ -639,7 +640,7 @@ }, "children": [ { - "id": "card_inner_grid", + "id": "card_inner_grid_{{reporterIdx}}", "type": "basic", "name": "Div", "props": { @@ -647,7 +648,7 @@ }, "children": [ { - "id": "reporter_field", + "id": "reporter_field_{{reporterIdx}}", "type": "basic", "name": "Div", "children": [ @@ -713,7 +714,7 @@ ] }, { - "id": "reported_at_field", + "id": "reported_at_field_{{reporterIdx}}", "type": "basic", "name": "Div", "children": [ @@ -736,7 +737,7 @@ ] }, { - "id": "reason_type_field", + "id": "reason_type_field_{{reporterIdx}}", "type": "basic", "name": "Div", "children": [ @@ -759,7 +760,7 @@ ] }, { - "id": "reporter_item_reason", + "id": "reporter_item_reason_{{reporterIdx}}", "type": "basic", "name": "Div", "props": { @@ -794,10 +795,11 @@ "id": "reporters_pagination", "type": "composite", "name": "Pagination", - "if": "{{(reporters_list?.data?.pagination?.last_page ?? 1) > 1}}", + "if": "{{((reporters_list?.data?.pagination?.last_page ?? 0) > 1) || (reporters_list?.data?.pagination?.has_more_pages === true) || ((reporters_list?.data?.pagination?.current_page ?? 1) > 1)}}", "props": { "currentPage": "{{reporters_list?.data?.pagination?.current_page ?? 1}}", - "totalPages": "{{reporters_list?.data?.pagination?.last_page ?? 1}}", + "totalPages": "{{reporters_list?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{reporters_list?.data?.pagination?.has_more_pages ?? false}}", "className": "mt-4 justify-end" }, "actions": [ diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_identity_policies.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_identity_policies.json index d620642d..31eedfd4 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_identity_policies.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_identity_policies.json @@ -1045,7 +1045,7 @@ "props": { "className": "text-tertiary" }, - "text": "$t:admin.identity.policies.pagination_summary|total={{boardIdentityPolicies?.data?.meta?.total ?? 0}}|page={{boardIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{boardIdentityPolicies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.pagination_summary|total={{boardIdentityPolicies?.data?.meta?.total ?? 0}}|page={{boardIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{boardIdentityPolicies?.data?.meta?.last_page ?? '-'}}" }, { "type": "basic", @@ -1098,14 +1098,14 @@ "props": { "className": "px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200" }, - "text": "$t:admin.identity.policies.page_indicator|page={{boardIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{boardIdentityPolicies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.page_indicator|page={{boardIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{boardIdentityPolicies?.data?.meta?.last_page ?? '-'}}" }, { "type": "basic", "name": "Button", "props": { "type": "button", - "disabled": "{{(boardIdentityPolicies?.data?.meta?.current_page ?? 1) >= (boardIdentityPolicies?.data?.meta?.last_page ?? 1)}}", + "disabled": "{{boardIdentityPolicies?.data?.meta?.last_page ? ((boardIdentityPolicies?.data?.meta?.current_page ?? 1) >= boardIdentityPolicies?.data?.meta?.last_page) : (boardIdentityPolicies?.data?.meta?.has_more_pages !== true)}}", "className": "px-3 py-1.5 text-xs font-medium bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed" }, "actions": [ @@ -1118,7 +1118,7 @@ "mergeQuery": true, "query": { "tab": "identity_policies", - "page": "{{Math.min(boardIdentityPolicies?.data?.meta?.last_page ?? 1, (boardIdentityPolicies?.data?.meta?.current_page ?? 1) + 1)}}" + "page": "{{boardIdentityPolicies?.data?.meta?.last_page ? Math.min(boardIdentityPolicies?.data?.meta?.last_page, (boardIdentityPolicies?.data?.meta?.current_page ?? 1) + 1) : ((boardIdentityPolicies?.data?.meta?.current_page ?? 1) + 1)}}" }, "transition_overlay_target": "policy_table_card", "scroll": { diff --git a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_notification_definitions.json b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_notification_definitions.json index 403b57e7..952537ec 100644 --- a/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_notification_definitions.json +++ b/modules/_bundled/sirsoft-board/resources/layouts/admin/partials/admin_board_settings/_tab_notification_definitions.json @@ -713,7 +713,8 @@ "name": "Pagination", "props": { "currentPage": "{{boardNotificationDefinitions?.data?.pagination?.current_page || 1}}", - "totalPages": "{{boardNotificationDefinitions?.data?.pagination?.last_page || 1}}" + "totalPages": "{{boardNotificationDefinitions?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{boardNotificationDefinitions?.data?.pagination?.has_more_pages ?? false}}" }, "actions": [ { diff --git a/modules/_bundled/sirsoft-board/src/Http/Controllers/User/PostController.php b/modules/_bundled/sirsoft-board/src/Http/Controllers/User/PostController.php index 9b27420b..0be7a176 100644 --- a/modules/_bundled/sirsoft-board/src/Http/Controllers/User/PostController.php +++ b/modules/_bundled/sirsoft-board/src/Http/Controllers/User/PostController.php @@ -5,6 +5,7 @@ namespace Modules\Sirsoft\Board\Http\Controllers\User; use App\Enums\PermissionType; use App\Http\Controllers\Api\Base\PublicBaseController; use App\Models\User; +use App\Support\Query\PaginationLimits; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -140,8 +141,10 @@ class PostController extends PublicBaseController throw new BoardNotFoundException($slug); } - // 게시글 조회 (스코프 접근 검사 포함) - $post = $this->postService->getPost($slug, $id, context: 'user'); + // 게시글 조회 (댓글/첨부파일/답글 관계 + 스코프 접근 검사). + // 권한 판정과 응답 조립이 같은 인스턴스를 공유해 같은 행을 다시 읽지 않는다 (#519 F3). + // 이미 조회한 Board 를 넘겨 게시판 재조회와 board 관계 적재도 함께 생략한다. + $post = $this->postService->getPostWithCounts($slug, $id, board: $board, context: 'user'); // 삭제된 게시글은 manager 권한 필요 if ($post->trashed()) { @@ -150,21 +153,50 @@ class PostController extends PublicBaseController } } - // 조회수 증가 (캐시 기반 중복 방지) - $this->postService->incrementViewCountOnce($slug, $id); - - // 댓글/첨부파일/답글 포함하여 게시글 조회 (boardId 전달로 Board 재조회 방지) - $post = $this->postService->getPostWithCounts($slug, $id, $board->id); - - // board 관계 수동 설정 - $post->setRelation('board', $board); + // 조회수 증가 (캐시 기반 중복 방지). 권한 확인을 통과한 열람만 센다. + // 증가분은 이미 조회한 인스턴스에 반영해 조회수 하나 때문에 글을 다시 읽지 않는다. + if ($this->postService->incrementViewCountOnce($slug, $id, $board->id)) { + $post->view_count = (int) $post->view_count + 1; + } // manager 권한 체크 (삭제 게시글/댓글 포함 여부 결정) $canViewDeleted = $this->checkBoardPermission($slug, 'manager', PermissionType::User); // 댓글 로드 (게시판 comment_order 설정 적용, manager 권한 + 토글 ON 시 삭제 댓글 포함) $withTrashedComments = $canViewDeleted && $request->boolean('del_cmt'); - $comments = $this->commentService->getCommentsByPostId($slug, $id, context: 'user', withTrashed: $withTrashedComments, boardId: $board->id); + + // comment_page 가 오면 원댓글 기준 페이지네이션 경로를 쓴다. 댓글이 상한을 넘는 + // 글에서도 뒤쪽 댓글에 도달할 수 있어야 하기 때문이다. 파라미터가 없으면 + // 종전대로 상한까지 전량을 싣는다(기존 화면 응답 형태 불변). + $commentPage = $this->resolveCommentPage($request); + $commentPagination = null; + + if ($commentPage !== null) { + $paginated = $this->commentService->paginateCommentsByPostId( + $slug, + $id, + perPage: $commentPage['per_page'], + page: $commentPage['page'], + context: 'user', + withTrashed: $withTrashedComments, + boardId: $board->id, + board: $board, + ); + + $comments = $paginated->getCollection(); + $commentPagination = [ + 'current_page' => $paginated->currentPage(), + 'per_page' => $paginated->perPage(), + 'total' => $paginated->total(), + 'last_page' => $paginated->lastPage(), + 'has_more_pages' => $paginated->hasMorePages(), + 'total_relation' => $paginated->totalRelation()->value, + 'total_is_exact' => $paginated->totalRelation()->isExact(), + 'result_cap' => $paginated->resultCap(), + ]; + } else { + $comments = $this->commentService->getCommentsByPostId($slug, $id, context: 'user', withTrashed: $withTrashedComments, boardId: $board->id, board: $board); + } // 신고 여부 일괄 조회 (N+1 방지: 댓글별 개별 쿼리 → 1회 일괄 쿼리) $user = $request->user(); @@ -188,6 +220,29 @@ class PostController extends PublicBaseController // 정렬된 댓글을 post에 설정 $post->setRelation('comments', $comments); + // 댓글 목록은 상한에서 끊길 수 있다. 끊겼다면 그 사실을 화면에 알린다 — + // 조용히 잘라내면 사용자에게는 "댓글이 그만큼뿐" 으로 보인다. + // 상한 이하면 이미 전량을 받았으므로 세는 쿼리를 추가하지 않는다. + $commentCap = PaginationLimits::resultCap('board.comments'); + + if ($commentPagination !== null) { + // 페이지네이션 경로는 잘림 여부를 페이지 메타가 그대로 알린다. + $post->comments_pagination = $commentPagination; + $post->comments_total = $commentPagination['total']; + $post->comments_total_is_exact = $commentPagination['total_is_exact']; + } elseif ($commentCap !== null && $comments->count() >= $commentCap) { + $commentTotal = $this->commentService->countCommentsByPostId( + $slug, + $id, + $withTrashedComments, + $board->id + ); + + $post->comments_truncated = true; + $post->comments_total = $commentTotal->total; + $post->comments_total_is_exact = $commentTotal->totalRelation()->isExact(); + } + // 비밀글 권한 체크 및 content 필터링은 PostResource에서 처리 return $this->successWithResource( 'sirsoft-board::messages.posts.fetch_success', @@ -477,9 +532,8 @@ class PostController extends PublicBaseController throw new BoardNotFoundException($slug); } - // 게시글 조회 (첨부파일 포함) - $post = $this->postService->getPostWithCounts($slug, $id); - $post->setRelation('board', $board); + // 게시글 조회 (첨부파일 포함). 이미 조회한 Board 를 넘겨 재조회를 막는다. + $post = $this->postService->getPostWithCounts($slug, $id, board: $board); // 비밀번호 검증 (Service 사용) $password = $request->validated('password'); @@ -953,4 +1007,32 @@ class PostController extends PublicBaseController return $this->error('sirsoft-board::messages.posts.fetch_failed', 500, $e->getMessage()); } } + + /** + * 댓글 페이지네이션 요청 여부와 값을 해석합니다. + * + * `comment_page` 또는 `comment_per_page` 중 하나라도 오면 페이지네이션 경로를 씁니다. + * 상한은 페이지네이션 공통 상한(max_page)과 한 페이지 100건을 따릅니다. + * + * @param Request $request HTTP 요청 + * @return array{page: int, per_page: int}|null 페이지 정보 (미요청 시 null) + */ + private function resolveCommentPage(Request $request): ?array + { + if (! $request->has('comment_page') && ! $request->has('comment_per_page')) { + return null; + } + + $page = max(1, (int) $request->input('comment_page', 1)); + $maxPage = PaginationLimits::maxPage('board.comments'); + + if ($maxPage !== null) { + $page = min($page, $maxPage); + } + + $perPage = (int) $request->input('comment_per_page', 20); + $perPage = max(1, min($perPage, 100)); + + return ['page' => $page, 'per_page' => $perPage]; + } } diff --git a/modules/_bundled/sirsoft-board/src/Http/Resources/PostCollection.php b/modules/_bundled/sirsoft-board/src/Http/Resources/PostCollection.php index b055e984..7c9f9e31 100644 --- a/modules/_bundled/sirsoft-board/src/Http/Resources/PostCollection.php +++ b/modules/_bundled/sirsoft-board/src/Http/Resources/PostCollection.php @@ -3,7 +3,9 @@ namespace Modules\Sirsoft\Board\Http\Resources; use App\Enums\PermissionType; +use App\Enums\TotalRelation; use App\Http\Resources\BaseApiCollection; +use App\Support\Query\BoundedCount; use Illuminate\Http\Request; use Modules\Sirsoft\Board\Traits\ChecksBoardPermission; @@ -18,9 +20,9 @@ class PostCollection extends BaseApiCollection use ChecksBoardPermission; /** - * 전체 일반 게시글(원글) 수 + * 전체 일반 게시글(원글) 수 + 정확도 */ - private ?int $totalNormalPosts = null; + private ?BoundedCount $totalNormalPosts = null; /** * 정렬 방향 @@ -30,9 +32,9 @@ class PostCollection extends BaseApiCollection /** * 전체 일반 게시글 수를 설정합니다. * - * @param int $total 전체 일반 게시글 수 + * @param BoundedCount $total 전체 일반 게시글 수 + 정확도 */ - public function setTotalNormalPosts(int $total): void + public function setTotalNormalPosts(BoundedCount $total): void { $this->totalNormalPosts = $total; } @@ -56,7 +58,7 @@ class PostCollection extends BaseApiCollection */ public function toArray(Request $request): array { - $normalPostsTotal = $this->totalNormalPosts ?? 0; + $normalPostsTotal = $this->totalNormalPosts?->total() ?? 0; $currentPage = $this->currentPage(); $perPage = $this->perPage(); $isDescending = $this->orderDirection === 'desc'; @@ -114,15 +116,26 @@ class PostCollection extends BaseApiCollection /** * 시작 순번을 계산합니다. * + * 내림차순 순번은 "전체 몇 건 중 몇 번째" 라 총 건수를 알아야 나온다. 총 건수가 상한에 + * 걸려 잘렸으면 그 값으로 역산한 순번은 첫 페이지부터 이미 틀리고(12,027 건인데 10000 + * 부터 시작), 상한을 넘어선 페이지에서는 0 과 음수까지 내려간다. 틀린 숫자를 내보내는 + * 것보다 내보내지 않는 편이 낫으므로 `last_page` 와 같은 원칙으로 null 을 돌려준다. + * + * 오름차순은 offset 만으로 정해지므로 총 건수가 잘려도 그대로 정확하다. + * * @param int $total 전체 게시글 수 * @param int $currentPage 현재 페이지 * @param int $perPage 페이지당 항목 수 * @param bool $isDescending 내림차순 여부 - * @return int 시작 순번 + * @return int|null 시작 순번 (총 건수가 잘린 내림차순이면 null) */ - private function calculateStartNumber(int $total, int $currentPage, int $perPage, bool $isDescending): int + private function calculateStartNumber(int $total, int $currentPage, int $perPage, bool $isDescending): ?int { if ($isDescending) { + if ($this->totalNormalPosts?->isTruncated()) { + return null; + } + // 내림차순: 큰 숫자부터 (28, 27, 26, ...) return $total - (($currentPage - 1) * $perPage); } @@ -154,11 +167,11 @@ class PostCollection extends BaseApiCollection * 게시글의 순번을 반환합니다. * * @param mixed $post 게시글 모델 - * @param int &$currentNumber 현재 순번 (참조) + * @param int|null &$currentNumber 현재 순번 (참조, 총 건수가 잘린 내림차순이면 null) * @param bool $isDescending 내림차순 여부 - * @return string|int 순번 또는 라벨 + * @return string|int|null 순번 또는 라벨 (순번을 알 수 없으면 null) */ - private function getRowNumber($post, int &$currentNumber, bool $isDescending): string|int + private function getRowNumber($post, ?int &$currentNumber, bool $isDescending): string|int|null { if ($post->is_notice) { return __('sirsoft-board::messages.post.notice'); @@ -168,6 +181,12 @@ class PostCollection extends BaseApiCollection return __('sirsoft-board::messages.post.reply'); } + // 총 건수를 모르면 순번도 없다. 여기서 임의의 값을 채우면 화면에는 그럴듯한 숫자가 + // 나가지만 실제 위치와 어긋난 값이라 그 사실이 드러나지 않는다. + if ($currentNumber === null) { + return null; + } + $number = $currentNumber; $currentNumber = $isDescending ? $currentNumber - 1 : $currentNumber + 1; @@ -190,7 +209,14 @@ class PostCollection extends BaseApiCollection */ private function buildPagination(int $total, int $currentPage, int $perPage, bool $isDescending, int $currentPageItemCount): array { - $lastPage = $total > 0 ? (int) ceil($total / $perPage) : 1; + $isTruncated = (bool) $this->totalNormalPosts?->isTruncated(); + + // 총 건수가 상한에 걸려 잘렸으면 마지막 페이지 번호를 계산할 수 없다. 1 이나 어림값을 + // 채우면 화면이 "여기가 끝" 으로 읽어 그 뒤 행에 도달할 방법이 사라진다. null 로 + // 내보내 마지막 페이지 점프만 감추게 하고, "다음" 은 has_more_pages 로 계속 열어 둔다. + $lastPage = $isTruncated + ? null + : ($total > 0 ? (int) ceil($total / $perPage) : 1); [$from, $to] = $this->calculateFromTo($total, $currentPage, $perPage, $isDescending); @@ -204,6 +230,9 @@ class PostCollection extends BaseApiCollection 'from' => $from, 'to' => $to, 'has_more_pages' => $this->hasMorePages(), + 'total_relation' => $this->totalNormalPosts?->totalRelation()->value ?? TotalRelation::Exact->value, + 'total_is_exact' => ! $isTruncated, + 'result_cap' => $this->totalNormalPosts?->resultCap(), ]; } @@ -214,7 +243,7 @@ class PostCollection extends BaseApiCollection * @param int $currentPage 현재 페이지 * @param int $perPage 페이지당 항목 수 * @param bool $isDescending 내림차순 여부 - * @return array{int, int} [from, to] + * @return array{int|null, int|null} [from, to] */ private function calculateFromTo(int $total, int $currentPage, int $perPage, bool $isDescending): array { @@ -223,6 +252,11 @@ class PostCollection extends BaseApiCollection } if ($isDescending) { + // 순번과 같은 역산이므로 총 건수가 잘리면 이 값도 성립하지 않는다. + if ($this->totalNormalPosts?->isTruncated()) { + return [null, null]; + } + // 내림차순: 큰 숫자 → 작은 숫자 $from = $total - (($currentPage - 1) * $perPage); $to = max($from - $perPage + 1, 1); diff --git a/modules/_bundled/sirsoft-board/src/Http/Resources/PostResource.php b/modules/_bundled/sirsoft-board/src/Http/Resources/PostResource.php index 926a51ce..e50baa6b 100644 --- a/modules/_bundled/sirsoft-board/src/Http/Resources/PostResource.php +++ b/modules/_bundled/sirsoft-board/src/Http/Resources/PostResource.php @@ -11,6 +11,7 @@ use Modules\Sirsoft\Board\Enums\PostStatus; use Modules\Sirsoft\Board\Enums\ReportReasonType; use Modules\Sirsoft\Board\Enums\TriggerType; use Modules\Sirsoft\Board\Repositories\Contracts\ReportRepositoryInterface; +use Modules\Sirsoft\Board\Support\BoardPermissionCacheKeys; use Modules\Sirsoft\Board\Traits\ChecksBoardPermission; use Modules\Sirsoft\Board\Traits\FormatsBoardDate; @@ -62,6 +63,14 @@ class PostResource extends BaseApiResource 'navigation' => $this->navigation ?? null, 'parent' => $this->relationLoaded('parent') ? $this->getParentInfo() : null, 'comments' => $this->relationLoaded('comments') ? CommentResource::collection($this->comments) : null, + + // 댓글이 상한에서 끊겼는지. 끊기지 않았으면 false 이고 총 건수는 실린 목록 길이와 같다. + // 조용히 잘라내면 사용자에게는 "댓글이 그만큼뿐" 으로 보이므로 사실을 함께 내보낸다. + 'comments_truncated' => (bool) ($this->comments_truncated ?? false), + 'comments_total' => $this->comments_total ?? ($this->relationLoaded('comments') ? $this->comments->count() : null), + 'comments_total_is_exact' => (bool) ($this->comments_total_is_exact ?? true), + // 댓글 페이지네이션을 요청한 경우에만 실린다 (미요청 시 null — 기존 응답 형태 유지) + 'comments_pagination' => $this->comments_pagination ?? null, 'attachments' => $this->getAttachmentsForResponse($request, $slug), 'replies' => $this->relationLoaded('replies') ? static::collection($this->replies) : null, @@ -580,7 +589,35 @@ class PostResource extends BaseApiResource return []; } - $permissionMap = $this->isAdminRequest($request) + // 이 권한 맵은 게시글이 아니라 (게시판, 화면 종류, 사용자) 로만 정해진다. + // 상세 화면의 답글 트리는 같은 게시판의 게시글을 N개 직렬화하므로, 메모가 없으면 + // 같은 답을 노드 수만큼 다시 만든다. 캐시는 요청 인스턴스에 두어 요청 단위로 격리된다. + $isAdminRequest = $this->isAdminRequest($request); + $cacheKey = $slug.'|'.($isAdminRequest ? 'admin' : 'user').'|'.(Auth::id() ?? 'guest'); + $cache = $request->attributes->get(BoardPermissionCacheKeys::ABILITIES, []); + + if (isset($cache[$cacheKey])) { + return $cache[$cacheKey]; + } + + $abilities = $this->buildAbilities($slug, $isAdminRequest); + + $cache[$cacheKey] = $abilities; + $request->attributes->set(BoardPermissionCacheKeys::ABILITIES, $cache); + + return $abilities; + } + + /** + * 게시판 권한 맵을 실제로 해석합니다. + * + * @param string $slug 게시판 슬러그 + * @param bool $isAdminRequest 관리자 화면 요청 여부 + * @return array 통합 권한 정보 + */ + private function buildAbilities(string $slug, bool $isAdminRequest): array + { + $permissionMap = $isAdminRequest ? [ 'can_read' => "sirsoft-board.{$slug}.admin.posts.read", 'can_write' => "sirsoft-board.{$slug}.admin.posts.write", @@ -610,7 +647,7 @@ class PostResource extends BaseApiResource // 유저 상세 화면에서만: 관리자 게시판 화면 진입 게이트(Admin 타입 admin.manage). // Admin 요청에서는 can_manage 가 이미 admin.manage 이므로 중복 노출 불필요. - if (! $this->isAdminRequest($request)) { + if (! $isAdminRequest) { $abilities['can_access_admin'] = $this->checkPermissionByIdentifier("sirsoft-board.{$slug}.admin.manage"); } diff --git a/modules/_bundled/sirsoft-board/src/Listeners/SearchPostsListener.php b/modules/_bundled/sirsoft-board/src/Listeners/SearchPostsListener.php index 4e87a04a..d0400174 100644 --- a/modules/_bundled/sirsoft-board/src/Listeners/SearchPostsListener.php +++ b/modules/_bundled/sirsoft-board/src/Listeners/SearchPostsListener.php @@ -4,6 +4,7 @@ namespace Modules\Sirsoft\Board\Listeners; use App\Contracts\Extension\HookListenerInterface; use App\Helpers\PermissionHelper; +use App\Search\SearchCategoryPayload; use Illuminate\Support\Facades\Log; use Modules\Sirsoft\Board\Services\BoardService; use Modules\Sirsoft\Board\Services\PostService; @@ -54,7 +55,7 @@ class SearchPostsListener implements HookListenerInterface /** * 훅 이벤트를 처리합니다. * - * @param mixed ...$args 훅에서 전달된 인수들 + * @param mixed ...$args 훅에서 전달된 인수들 * @return void */ public function handle(...$args): void @@ -66,7 +67,7 @@ class SearchPostsListener implements HookListenerInterface /** * 검색 파라미터 validation rules 추가 * - * @param array $rules 기존 validation rules + * @param array $rules 기존 validation rules * @return array 게시판 모듈 파라미터가 추가된 rules */ public function addValidationRules(array $rules): array @@ -80,8 +81,8 @@ class SearchPostsListener implements HookListenerInterface /** * 게시글 검색을 수행하고 결과를 반환합니다. * - * @param array $results 기존 검색 결과 - * @param array $context 검색 컨텍스트 (q, type, sort, page, per_page, user, request) + * @param array $results 기존 검색 결과 + * @param array $context 검색 컨텍스트 (q, type, sort, page, per_page, user, request) * @return array 게시글이 추가된 검색 결과 */ public function searchPosts(array $results, array $context): array @@ -97,7 +98,7 @@ class SearchPostsListener implements HookListenerInterface try { $boardSlug = ($context['request'] ?? null)?->input('board_slug', '') ?? ''; $boards = $this->boardService->getActiveBoardsForSearch( - !empty($boardSlug) ? $boardSlug : null + ! empty($boardSlug) ? $boardSlug : null ); // 게시판별 읽기 권한 필터링 — 권한 없는 게시판 제외 @@ -110,8 +111,11 @@ class SearchPostsListener implements HookListenerInterface return $isRelevantTab ? $results : $this->withEmptyPostsResult($results); } - if (!$isRelevantTab) { - return $this->withCountOnlyResult($results, $boards, $q); + if (! $isRelevantTab) { + // 배지를 그리지 않는 화면은 건수 자체를 요청하지 않는다 — 그 경우 집계를 생략한다. + return ($context['include_inactive_counts'] ?? true) + ? $this->withCountOnlyResult($results, $boards, $q) + : $this->withEmptyPostsResult($results); } $results['posts'] = $this->buildSearchResult($boards, $q, $context); @@ -125,14 +129,14 @@ class SearchPostsListener implements HookListenerInterface /** * 게시글 검색 결과를 프론트엔드 응답 구조로 변환합니다. * - * @param array $response 기존 응답 구조 - * @param array $results 검색 결과 (core.search.results에서 반환된 데이터) - * @param array $context 검색 컨텍스트 + * @param array $response 기존 응답 구조 + * @param array $results 검색 결과 (core.search.results에서 반환된 데이터) + * @param array $context 검색 컨텍스트 * @return array 게시글 응답이 추가된 구조 */ public function buildPostsResponse(array $response, array $results, array $context): array { - if (!isset($results['posts'])) { + if (! isset($results['posts'])) { return $response; } @@ -159,7 +163,7 @@ class SearchPostsListener implements HookListenerInterface /** * 빈 게시글 결과를 설정합니다. * - * @param array $results 기존 검색 결과 + * @param array $results 기존 검색 결과 * @return array 빈 posts가 추가된 결과 */ private function withEmptyPostsResult(array $results): array @@ -172,17 +176,20 @@ class SearchPostsListener implements HookListenerInterface /** * 다른 탭에서 게시글 건수만 조회합니다 (단일 쿼리). * - * @param array $results 기존 검색 결과 - * @param iterable $boards 게시판 목록 - * @param string $keyword 검색어 + * @param array $results 기존 검색 결과 + * @param iterable $boards 게시판 목록 + * @param string $keyword 검색어 * @return array count만 포함된 결과 */ private function withCountOnlyResult(array $results, iterable $boards, string $keyword): array { + // 다른 탭을 보는 중이라도 탭 배지에는 건수가 필요하다. 상한을 건 집계라 + // 대량 매칭에서도 비용이 일정하다. + // 잘린 값을 정확한 것처럼 내보내지 않도록 정확도를 함께 싣는다. $boardIds = collect($boards)->pluck('id')->all(); - $totalCount = $this->postService->countAcrossBoards($boardIds, $keyword); + $count = $this->postService->countAcrossBoards($boardIds, $keyword); - $results['posts'] = ['total' => $totalCount, 'items' => [], 'available_boards' => []]; + $results['posts'] = SearchCategoryPayload::fromCountOnly($count, ['available_boards' => []]); return $results; } @@ -190,9 +197,9 @@ class SearchPostsListener implements HookListenerInterface /** * 전체 게시판을 단일 쿼리로 검색하고 결과 구조를 생성합니다. * - * @param iterable $boards 게시판 목록 - * @param string $keyword 검색어 - * @param array $context 검색 컨텍스트 + * @param iterable $boards 게시판 목록 + * @param string $keyword 검색어 + * @param array $context 검색 컨텍스트 * @return array posts 결과 구조 */ private function buildSearchResult(iterable $boards, string $keyword, array $context): array @@ -203,22 +210,45 @@ class SearchPostsListener implements HookListenerInterface $type = $context['type'] ?? 'all'; $boardIds = collect($boards)->pluck('id')->all(); + $extra = ['available_boards' => $this->boardService->getActiveBoardsListForFilter()]; - // 전체 탭은 최신 N개만, posts 탭은 DB 페이지네이션 + /** 조회 결과 항목을 화면 형태로 가공한다. */ + $format = fn (iterable $items): array => collect($items) + ->map(fn ($post) => $this->formatPostResult($post, $keyword)) + ->all(); + + // 전체 탭은 최신 N개만 보여 주므로 깊은 페이지 자체가 없다. if ($type === 'all') { - $allTabLimit = $context['all_tab_limit'] ?? 5; - $searchResult = $this->postService->searchAcrossBoards($boardIds, $keyword, $sort, $allTabLimit, 1); - } else { - $searchResult = $this->postService->searchAcrossBoards($boardIds, $keyword, $sort, $perPage, $page); + $allTabLimit = (int) ($context['all_tab_limit'] ?? 5); + $searchPage = $this->postService->searchAcrossBoards($boardIds, $keyword, $sort, $allTabLimit, 1); + + return SearchCategoryPayload::fromBounded($searchPage, $format($searchPage->items()), $extra); } - return [ - 'total' => $searchResult['total'], - 'items' => $searchResult['items']->map( - fn ($post) => $this->formatPostResult($post, $keyword) - )->all(), - 'available_boards' => $this->boardService->getActiveBoardsListForFilter(), - ]; + // 커서로 처리할 수 있는 정렬이면 키셋으로 응답한다. 첫 페이지는 커서가 없는 것이 + // 정상이며 그때 다음 커서가 발급된다. 커서 없이 깊은 페이지를 직접 지목한 요청 + // (딥링크)만 offset 을 유지한다 — 판정은 코어가 하고 이 리스너는 결과 유무만 본다. + $cursorPage = $this->postService->searchAcrossBoardsByCursor( + $boardIds, + $keyword, + $sort, + (int) $perPage, + $context['cursor'] ?? null, + (int) $page + ); + + if ($cursorPage !== null) { + return SearchCategoryPayload::fromCursor( + $cursorPage, + $this->postService->countAcrossBoards($boardIds, $keyword), + $format($cursorPage->items()), + $extra + ); + } + + $searchPage = $this->postService->searchAcrossBoards($boardIds, $keyword, $sort, (int) $perPage, (int) $page); + + return SearchCategoryPayload::fromBounded($searchPage, $format($searchPage->items()), $extra); } // ─── buildPostsResponse 헬퍼 ───────────────────────── @@ -226,14 +256,17 @@ class SearchPostsListener implements HookListenerInterface /** * 전체 탭 응답 구조를 생성합니다. * - * @param array $postsData 게시글 데이터 - * @param array $context 검색 컨텍스트 + * @param array $postsData 게시글 데이터 + * @param array $context 검색 컨텍스트 * @return array 전체 탭용 응답 */ private function buildAllTabResponse(array $postsData, array $context): array { return [ 'total' => $postsData['total'] ?? 0, + 'total_relation' => $postsData['total_relation'] ?? null, + 'total_is_exact' => $postsData['total_is_exact'] ?? true, + 'result_cap' => $postsData['result_cap'] ?? null, 'items' => $postsData['items'] ?? [], ]; } @@ -241,24 +274,34 @@ class SearchPostsListener implements HookListenerInterface /** * posts 탭 응답 구조를 생성합니다 (DB 페이지네이션). * - * @param array $response 기존 응답 - * @param array $postsData 게시글 데이터 - * @param array $context 검색 컨텍스트 + * @param array $response 기존 응답 + * @param array $postsData 게시글 데이터 + * @param array $context 검색 컨텍스트 * @return array 페이지네이션이 추가된 응답 */ private function buildPostsTabResponse(array $response, array $postsData, array $context): array { - $page = $context['page'] ?? 1; - $perPage = $context['per_page'] ?? 10; + $page = (int) ($context['page'] ?? 1); + $perPage = (int) ($context['per_page'] ?? 10); $totalItems = $postsData['total'] ?? 0; $response['posts'] = [ 'total' => $totalItems, + 'total_relation' => $postsData['total_relation'] ?? null, + 'total_is_exact' => $postsData['total_is_exact'] ?? true, + 'result_cap' => $postsData['result_cap'] ?? null, 'items' => $postsData['items'] ?? [], ]; $response['current_page'] = $page; $response['per_page'] = $perPage; - $response['last_page'] = max(1, (int) ceil($totalItems / $perPage)); + // 총 건수가 상한에 걸리면 마지막 페이지를 계산할 수 없다 — null 로 알린다. + // 화면은 이 값이 null 이면 마지막 페이지 점프만 감추고, "다음" 은 계속 노출한다. + $response['last_page'] = $postsData['last_page'] ?? null; + $response['has_more_pages'] = $postsData['has_more_pages'] ?? false; + // 커서 응답이면 다음/이전 커서를 함께 실어 화면이 깊은 페이지를 OFFSET 없이 넘긴다. + // offset 응답에서는 두 값이 null 이라 화면이 분기 없이 같은 키를 읽는다. + $response['next_cursor'] = $postsData['next_cursor'] ?? null; + $response['prev_cursor'] = $postsData['prev_cursor'] ?? null; return $response; } @@ -268,8 +311,8 @@ class SearchPostsListener implements HookListenerInterface /** * 게시글을 검색 결과 형식으로 변환합니다. * - * @param object $post 게시글 (board relation 로드 필수) - * @param string $keyword 검색어 + * @param object $post 게시글 (board relation 로드 필수) + * @param string $keyword 검색어 * @return array 변환된 게시글 데이터 */ private function formatPostResult(object $post, string $keyword): array @@ -288,32 +331,34 @@ class SearchPostsListener implements HookListenerInterface } return [ - 'id' => $post->id, - 'title' => $post->title, - 'title_highlighted' => $this->highlightKeyword($post->title, $keyword), - 'content_preview' => $contentPreview, + 'id' => $post->id, + 'title' => $post->title, + 'title_highlighted' => $this->highlightKeyword($post->title, $keyword), + 'content_preview' => $contentPreview, 'content_preview_highlighted' => $contentPreviewHighlighted, - 'content_mode' => $contentMode, - 'board' => [ + 'content_mode' => $contentMode, + 'board' => [ 'slug' => $boardSlug, 'name' => $post->board?->getLocalizedName() ?? '', ], - 'board_name' => $post->board?->getLocalizedName() ?? '', - 'board_slug' => $boardSlug, - 'author_name' => $post->author_name ?? $post->user?->name ?? __('board.anonymous'), - 'created_at' => $this->formatCreatedAt($post->created_at), - 'created_at_formatted' => $this->formatCreatedAtFormat($post->created_at, g7_module_settings('sirsoft-board', 'display.date_display_format', 'standard')), - 'view_count' => $post->view_count ?? 0, - 'comment_count' => $post->comments_count ?? 0, - 'url' => "/board/{$boardSlug}/{$post->id}", + 'board_name' => $post->board?->getLocalizedName() ?? '', + 'board_slug' => $boardSlug, + // 모듈 번역 키는 네임스페이스를 붙여야 해석된다. 붙이지 않으면 해석에 실패해도 + // 예외 없이 키 문자열이 그대로 화면에 나간다 (실측: 작성자에 "board.anonymous"). + 'author_name' => $post->author_name ?? $post->user?->name ?? __('sirsoft-board::messages.common.guest'), + 'created_at' => $this->formatCreatedAt($post->created_at), + 'created_at_formatted' => $this->formatCreatedAtFormat($post->created_at, g7_module_settings('sirsoft-board', 'display.date_display_format', 'standard')), + 'view_count' => $post->view_count ?? 0, + 'comment_count' => $post->comments_count ?? 0, + 'url' => "/board/{$boardSlug}/{$post->id}", ]; } /** * 텍스트에서 검색어를 하이라이트 처리합니다. * - * @param string|null $text 원본 텍스트 - * @param string $keyword 검색어 + * @param string|null $text 원본 텍스트 + * @param string $keyword 검색어 * @return string 하이라이트 처리된 텍스트 */ private function highlightKeyword(?string $text, string $keyword): string @@ -333,10 +378,10 @@ class SearchPostsListener implements HookListenerInterface * HTML 모드: strip_tags로 태그 제거 후 평문 추출 * 텍스트 모드: 태그 문자열을 그대로 보존 (실제 게시글 표시와 동일하게) * - * @param string|null $content 본문 내용 - * @param string $keyword 검색어 - * @param int $length 추출할 최대 길이 - * @param string $contentMode 콘텐츠 모드 (text|html) + * @param string|null $content 본문 내용 + * @param string $keyword 검색어 + * @param int $length 추출할 최대 길이 + * @param string $contentMode 콘텐츠 모드 (text|html) * @return string 추출된 미리보기 텍스트 */ private function extractContentPreview(?string $content, string $keyword, int $length = 150, string $contentMode = 'text'): string diff --git a/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php b/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php index 0ce04015..671fe972 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php @@ -4,6 +4,10 @@ namespace Modules\Sirsoft\Board\Repositories; use App\Helpers\PermissionHelper; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\PaginationLimits; use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -47,7 +51,9 @@ class CommentRepository implements CommentRepositoryInterface $query = Comment::query() ->where('board_id', $resolvedBoardId) - ->with(['user', 'user.avatarAttachment', 'parent']) + // parent 는 eager load 하지 않는다 — 부모 댓글도 같은 글의 댓글이라 이 결과셋에 + // 이미 들어 있다. 관계로 다시 부르면 같은 집합을 한 번 더 가져온다. + ->with(['user', 'user.avatarAttachment']) ->where('post_id', $postId); // 권한 스코프 필터링 (Service에서 컨텍스트 기반으로 전달) @@ -68,7 +74,15 @@ class CommentRepository implements CommentRepositoryInterface }); } - $comments = $query->orderBy('created_at', $orderDirection)->get(); + // 한 글의 댓글 전량을 무제한으로 읽지 않는다. 상한을 넘으면 그 지점에서 끊고, + // 화면은 더 있다는 사실을 총 건수로 알 수 있다({@see self::countByPostId()}). + $commentCap = PaginationLimits::resultCap('board.comments'); + + $comments = $query + ->orderBy('created_at', $orderDirection) + ->orderBy('id', $orderDirection) + ->when($commentCap !== null, fn ($q) => $q->limit($commentCap)) + ->get(); // 일반 조회(withTrashed=false)에서 부모가 삭제되어 빠진 경우, // 살아있는 자식이 트리에서 통째로 누락된다. @@ -240,6 +254,166 @@ class CommentRepository implements CommentRepositoryInterface return $count; } + /** + * {@inheritDoc} + */ + public function paginateRootsByPostId( + string $slug, + int $postId, + int $perPage, + int $page = 1, + bool $withTrashed = false, + string $orderDirection = 'DESC', + ?string $scopePermission = null, + ?int $boardId = null + ): BoundedPage { + $resolvedBoardId = $boardId ?? Board::where('slug', $slug)->value('id'); + $orderDirection = strtoupper($orderDirection) === 'ASC' ? 'asc' : 'desc'; + + // 원댓글(최상위)만 페이지네이션한다. 답글은 그 원댓글에 딸린 것이라 페이지 경계로 + // 갈라 놓으면 트리가 끊긴다 — 이번 페이지 원댓글의 자손은 아래에서 통째로 읽는다. + $rootQuery = $this->buildVisibleCommentQuery($resolvedBoardId, $postId, $withTrashed, $slug, $scopePermission) + ->whereNull('parent_id') + ->with(['user', 'user.avatarAttachment']) + ->orderBy('created_at', $orderDirection) + // 전순서 보장 — created_at 은 초 단위라 동률에서 페이지 경계가 흔들린다 + ->orderBy('id', $orderDirection); + + $rootPage = BoundedPaginator::paginate( + $rootQuery, + perPage: $perPage, + page: $page, + resultCap: PaginationLimits::resultCap('board.comments'), + ); + + $rootIds = collect($rootPage->items())->pluck('id')->all(); + + if (empty($rootIds)) { + return $rootPage; + } + + $descendants = $this->loadDescendantsOfRoots($resolvedBoardId, $postId, $rootIds, $withTrashed, $slug, $scopePermission); + + $comments = collect($rootPage->items())->concat($descendants); + + if (! $withTrashed && $resolvedBoardId) { + $comments = $this->restoreTombstoneParents($comments, $resolvedBoardId, $postId); + } + + $sorted = $this->sortByParentChild($comments, $orderDirection); + $this->recalculateDescendantCounts($sorted); + + $rootPage->setCollection($sorted); + + return $rootPage; + } + + /** + * 이번 페이지 원댓글들의 모든 자손 댓글을 깊이별로 읽습니다. + * + * 상한을 두어 답글이 많은 글에서도 한 페이지를 여는 비용이 고정됩니다. + * + * @param int|null $boardId 게시판 ID + * @param int $postId 게시글 ID + * @param array $rootIds 이번 페이지 원댓글 ID + * @param bool $withTrashed 삭제 포함 여부 + * @param string $slug 게시판 슬러그 + * @param string|null $scopePermission 권한 스코프 식별자 + * @return Collection 자손 댓글 컬렉션 + */ + private function loadDescendantsOfRoots(?int $boardId, int $postId, array $rootIds, bool $withTrashed, string $slug, ?string $scopePermission): Collection + { + $cap = PaginationLimits::resultCap('board.comments'); + $collected = new Collection; + $currentLevel = $rootIds; + $seen = array_flip($rootIds); + + while (! empty($currentLevel)) { + $remaining = $cap === null ? null : max(0, $cap - $collected->count()); + + if ($remaining === 0) { + break; + } + + $level = $this->buildVisibleCommentQuery($boardId, $postId, $withTrashed, $slug, $scopePermission) + ->whereIn('parent_id', $currentLevel) + ->with(['user', 'user.avatarAttachment']) + ->orderBy('id', 'asc') + ->when($remaining !== null, fn ($q) => $q->limit($remaining)) + ->get(); + + if ($level->isEmpty()) { + break; + } + + $collected = $collected->concat($level); + + // 방문 집합으로 유한 종료를 보장한다 (오염된 parent_id 가 순환을 만들어도 멈춘다) + $currentLevel = $level->pluck('id') + ->reject(fn ($id) => isset($seen[$id])) + ->each(fn ($id) => $seen[$id] = true) + ->all(); + } + + return $collected; + } + + /** + * 노출 대상 댓글 쿼리를 구성합니다 (권한 스코프 + 삭제 표시 규칙 공통). + * + * @param int|null $boardId 게시판 ID + * @param int $postId 게시글 ID + * @param bool $withTrashed 삭제 포함 여부 + * @param string $slug 게시판 슬러그 + * @param string|null $scopePermission 권한 스코프 식별자 + * @return Builder 구성된 쿼리 + */ + private function buildVisibleCommentQuery(?int $boardId, int $postId, bool $withTrashed, string $slug, ?string $scopePermission) + { + $query = Comment::query() + ->where('board_id', $boardId) + ->where('post_id', $postId); + + PermissionHelper::applyPermissionScope($query, $scopePermission ?? "sirsoft-board.{$slug}.admin.comments.read"); + + if ($withTrashed) { + $query->withTrashed(); + } else { + // 게시글 삭제로 함께 숨겨진(cascade) 댓글은 사용자가 지운 것이 아니므로 노출한다. + $query->withTrashed() + ->where(function ($q) { + $q->whereNull('deleted_at') + ->orWhere('trigger_type', TriggerType::Cascade->value); + }); + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function countByPostId(string $slug, int $postId, bool $withTrashed = false, ?int $boardId = null): BoundedCount + { + $resolvedBoardId = $boardId ?? Board::where('slug', $slug)->value('id'); + + $query = Comment::query() + ->where('board_id', $resolvedBoardId) + ->where('post_id', $postId); + + if ($withTrashed) { + $query->withTrashed(); + } else { + $query->withTrashed() + ->where(function ($q) { + $q->whereNull('deleted_at') + ->orWhere('trigger_type', TriggerType::Cascade->value); + }); + } + + return BoundedPaginator::count($query, PaginationLimits::resultCap('board.comments')); + } + /** * 댓글을 생성합니다. * @@ -547,6 +721,7 @@ class CommentRepository implements CommentRepositoryInterface $orderDirection = $sort === 'oldest' ? 'asc' : 'desc'; // 비활성 게시판 제외 — JOIN 대신 whereNotIn으로 인덱스 활용 + // audit:allow query-unbounded-get reason: 게시판은 운영자가 만든 수만큼만 존재한다 (글·댓글 수와 무관) $inactiveBoardIds = Board::where('is_active', false)->pluck('id')->all(); // 관계/정렬은 지연 조인이 담당한다 (inner 는 키 컬럼만 조회) diff --git a/modules/_bundled/sirsoft-board/src/Repositories/Contracts/CommentRepositoryInterface.php b/modules/_bundled/sirsoft-board/src/Repositories/Contracts/CommentRepositoryInterface.php index c350c479..e2eb7458 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/Contracts/CommentRepositoryInterface.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/Contracts/CommentRepositoryInterface.php @@ -2,6 +2,8 @@ namespace Modules\Sirsoft\Board\Repositories\Contracts; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Pagination\LengthAwarePaginator; @@ -25,6 +27,48 @@ interface CommentRepositoryInterface */ public function getByPostId(string $slug, int $postId, bool $withTrashed = false, string $orderDirection = 'DESC', ?string $scopePermission = null, ?int $boardId = null): Collection; + /** + * 게시글의 원댓글을 페이지네이션하고, 이번 페이지 원댓글의 답글까지 함께 조립합니다. + * + * 페이지 단위는 **원댓글**입니다. 답글까지 잘라 세면 트리가 페이지 경계에서 끊겨 + * 부모 없는 답글이 화면에 남습니다. 총 건수는 상한까지만 세며, 다음 페이지 이동은 + * 총 건수와 무관하게 실측으로 판정됩니다. + * + * @param string $slug 게시판 슬러그 + * @param int $postId 게시글 ID + * @param int $perPage 페이지당 원댓글 수 + * @param int $page 현재 페이지 + * @param bool $withTrashed 삭제된 댓글 포함 여부 + * @param string $orderDirection 정렬 방향 (ASC 또는 DESC) + * @param string|null $scopePermission 스코프 권한 식별자 + * @param int|null $boardId 게시판 ID (전달 시 재조회 생략) + * @return BoundedPage 원댓글 기준 페이지 (컬렉션은 트리 정렬된 댓글 전체) + */ + public function paginateRootsByPostId( + string $slug, + int $postId, + int $perPage, + int $page = 1, + bool $withTrashed = false, + string $orderDirection = 'DESC', + ?string $scopePermission = null, + ?int $boardId = null + ): BoundedPage; + + /** + * 게시글의 댓글 총 건수를 조회합니다. + * + * 목록 조회는 상한에 걸려 잘릴 수 있으므로, 화면이 "더 있다" 를 알 수 있도록 + * 총 건수를 따로 제공합니다. 집계에도 같은 상한이 걸려 대량 댓글에서 비용이 일정합니다. + * + * @param string $slug 게시판 슬러그 + * @param int $postId 게시글 ID + * @param bool $withTrashed 삭제 댓글 포함 여부 + * @param int|null $boardId 게시판 ID (전달 시 재조회 생략) + * @return BoundedCount 댓글 총 건수 (상한 초과 시 상한값 + AtLeast 정확도) + */ + public function countByPostId(string $slug, int $postId, bool $withTrashed = false, ?int $boardId = null): BoundedCount; + /** * 댓글을 생성합니다. * diff --git a/modules/_bundled/sirsoft-board/src/Repositories/Contracts/PostRepositoryInterface.php b/modules/_bundled/sirsoft-board/src/Repositories/Contracts/PostRepositoryInterface.php index 751c172d..128721f8 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/Contracts/PostRepositoryInterface.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/Contracts/PostRepositoryInterface.php @@ -2,9 +2,12 @@ namespace Modules\Sirsoft\Board\Repositories\Contracts; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Modules\Sirsoft\Board\Models\Board; use Modules\Sirsoft\Board\Models\Post; @@ -108,9 +111,10 @@ interface PostRepositoryInterface * * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID + * @param int|null $boardId 게시판 ID (전달 시 슬러그 재조회 생략) * @return int 증가된 조회수 */ - public function incrementViewCount(string $slug, int $id): int; + public function incrementViewCount(string $slug, int $id, ?int $boardId = null): int; /** * 해당 게시판의 게시글이 공지글인지 여부만 경량 조회합니다. @@ -154,9 +158,10 @@ interface PostRepositoryInterface * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID * @param int|null $boardId 게시판 ID (전달 시 Board 재조회 생략) + * @param Board|null $board 이미 조회한 게시판 모델 (전달 시 board 관계 적재까지 생략) * @return Post|null 게시글 모델 (카운트 포함) */ - public function findWithCounts(string $slug, int $id, ?int $boardId = null): ?Post; + public function findWithCounts(string $slug, int $id, ?int $boardId = null, ?Board $board = null): ?Post; /** * 전체 일반 게시글(원글) 수를 조회합니다. @@ -165,9 +170,9 @@ interface PostRepositoryInterface * @param string $slug 게시판 슬러그 * @param array $filters 필터 조건 * @param bool $withTrashed 삭제된 게시글 포함 여부 - * @return int 일반 게시글 수 (답글, 공지 제외) + * @return BoundedCount 일반 게시글 수 + 정확도 (답글, 공지 제외) */ - public function countNormalPosts(string $slug, array $filters = [], bool $withTrashed = false): int; + public function countNormalPosts(string $slug, array $filters = [], bool $withTrashed = false): BoundedCount; /** * 이전/다음 게시글을 조회합니다. @@ -198,19 +203,27 @@ interface PostRepositoryInterface * @param string $keyword 검색 키워드 * @param string $orderBy 정렬 컬럼 * @param string $direction 정렬 방향 (asc, desc) - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $slug, string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $limit = 10): array; + public function searchByKeyword( + string $slug, + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage; /** * 게시판에서 키워드와 일치하는 게시글 수를 조회합니다. * * @param string $slug 게시판 슬러그 * @param string $keyword 검색 키워드 - * @return int 일치하는 게시글 수 + * @return BoundedCount 일치하는 게시글 수 (정확도 포함) */ - public function countByKeyword(string $slug, string $keyword): int; + public function countByKeyword(string $slug, string $keyword): BoundedCount; /** * 여러 게시판에서 키워드로 게시글을 검색합니다 (단일 쿼리, DB 페이지네이션). @@ -223,16 +236,44 @@ interface PostRepositoryInterface * @param int $page 페이지 번호 * @return array{total: int, items: Collection} */ - public function searchAcrossBoards(array $boardIds, string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $perPage = 10, int $page = 1): array; + public function searchAcrossBoards( + array $boardIds, + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage; + + /** + * 여러 게시판에서 키워드로 게시글을 커서(키셋)로 검색합니다. + * + * 커서 적용 가능 여부 판정은 코어가 담당하므로, 이 메서드는 이미 검증된 정렬 키를 + * 받아 조회만 수행합니다. + * + * @param array $boardIds 검색 대상 게시판 ID 목록 + * @param string $keyword 검색 키워드 + * @param array $sortKeys [[컬럼, 방향], ...] + * @param int $perPage 페이지당 항목 수 + * @param string|null $cursor 인코딩된 커서 (첫 페이지면 null) + * @return CursorPaginator 커서 페이지 결과 + */ + public function searchAcrossBoardsByCursor( + array $boardIds, + string $keyword, + array $sortKeys, + int $perPage = 10, + ?string $cursor = null + ): CursorPaginator; /** * 여러 게시판에서 키워드와 일치하는 게시글 수를 조회합니다 (단일 쿼리). * * @param array $boardIds 검색 대상 게시판 ID 목록 * @param string $keyword 검색 키워드 - * @return int 키워드와 일치하는 게시글 수 + * @return BoundedCount 키워드와 일치하는 게시글 수 (정확도 포함) */ - public function countAcrossBoards(array $boardIds, string $keyword): int; + public function countAcrossBoards(array $boardIds, string $keyword): BoundedCount; /** * 사용자의 게시글 활동 통계를 조회합니다. diff --git a/modules/_bundled/sirsoft-board/src/Repositories/PostRepository.php b/modules/_bundled/sirsoft-board/src/Repositories/PostRepository.php index d360a09f..58b4b0b3 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/PostRepository.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/PostRepository.php @@ -5,11 +5,17 @@ namespace Modules\Sirsoft\Board\Repositories; use App\Enums\PermissionType; use App\Helpers\PermissionHelper; use App\Repositories\Concerns\PaginatesWithDeferredJoin; -use App\Search\Engines\DatabaseFulltextEngine; +use App\Search\KeywordSearch; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\KeysetPaginator; +use App\Support\Query\PaginationLimits; use Carbon\CarbonImmutable; use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; @@ -88,26 +94,25 @@ class PostRepository implements PostRepositoryInterface { // 검색 if (! empty($filters['search'])) { - $keyword = $this->escapeLikeKeyword($filters['search']); + // FULLTEXT 와 LIKE 는 이스케이프 규칙이 다르다. LIKE 용으로 이스케이프한 문자열을 + // MATCH 에 그대로 넘기면 백슬래시가 검색어의 일부로 들어간다. + $rawKeyword = (string) $filters['search']; + $likeKeyword = $this->escapeLikeKeyword($rawKeyword); $searchField = $filters['search_field'] ?? 'all'; - $query->where(function ($q) use ($keyword, $searchField) { + $query->where(function ($q) use ($rawKeyword, $likeKeyword, $searchField) { // 제목+내용 검색: FULLTEXT 활용 (all, title_content) + // 코어 헬퍼를 거쳐야 BOOLEAN MODE 연산자(+ - * " 등) 입력이 500 이 되지 않는다. if ($searchField === 'all' || $searchField === 'title_content') { - if (DatabaseFulltextEngine::supportsFulltext()) { - $q->orWhereRaw('MATCH(`title`, `content`) AGAINST(? IN BOOLEAN MODE)', [$keyword]); - } else { - $q->orWhere('title', 'like', "%{$keyword}%") - ->orWhere('content', 'like', "%{$keyword}%"); - } + KeywordSearch::apply($q, ['title', 'content'], $rawKeyword, 'or'); } // 작성자 검색 if ($searchField === 'all' || $searchField === 'author' || $searchField === 'author_name') { - $q->orWhere('author_name', 'like', "%{$keyword}%") - ->orWhereHas('user', function ($uq) use ($keyword) { - $uq->where('name', 'like', "%{$keyword}%") - ->orWhere('email', 'like', "%{$keyword}%"); + $q->orWhere('author_name', 'like', "%{$likeKeyword}%") + ->orWhereHas('user', function ($uq) use ($likeKeyword) { + $uq->where('name', 'like', "%{$likeKeyword}%") + ->orWhere('email', 'like', "%{$likeKeyword}%"); }); } }); @@ -301,19 +306,27 @@ class PostRepository implements PostRepositoryInterface * * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID + * @param int|null $boardId 게시판 ID (전달 시 슬러그 재조회 생략) * @return int 증가된 조회수 */ - public function incrementViewCount(string $slug, int $id): int + public function incrementViewCount(string $slug, int $id, ?int $boardId = null): int { - $board = Board::where('slug', $slug)->first(); + // 상세 화면은 이미 게시판을 조회한 뒤 여기로 온다. 슬러그로 다시 찾으면 같은 요청에서 + // 게시판을 두 번 읽는다 — 호출자가 알고 있으면 그대로 받는다. + if ($boardId === null) { + $boardId = Board::where('slug', $slug)->value('id'); + } - Post::where('board_id', $board?->id) + Post::where('board_id', $boardId) ->where('id', $id) ->increment('view_count'); - $post = $this->find($slug, $id); - - return $post?->view_count ?? 0; + // 증가된 값만 필요하다. find() 는 게시판을 또 찾고 user 관계까지 적재하므로 + // 조회수 하나 읽자고 쓰기에는 과하다. + return (int) (Post::withTrashed() + ->where('board_id', $boardId) + ->where('id', $id) + ->value('view_count') ?? 0); } /** @@ -417,11 +430,16 @@ class PostRepository implements PostRepositoryInterface * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID * @param int|null $boardId 게시판 ID (전달 시 Board 재조회 생략) + * @param Board|null $board 이미 조회한 게시판 모델 (전달 시 board 관계 적재까지 생략) * @return Post|null 게시글 모델 (카운트 포함) */ - public function findWithCounts(string $slug, int $id, ?int $boardId = null): ?Post + public function findWithCounts(string $slug, int $id, ?int $boardId = null, ?Board $board = null): ?Post { - // boardId가 전달되면 Board 모델 재조회 없이 직접 사용 + // 호출자가 이미 손에 쥔 Board 를 넘기면 그 인스턴스를 그대로 쓴다 (#519 F3). + // $board 는 아래 답글 로딩에서 참조하므로 어느 경로로 오든 정의돼 있어야 한다 + // (초기화가 없으면 boardId 를 받은 경로에서 미정의 변수 경고가 난다). + $boardId = $board?->id ?? $boardId; + if (! $boardId) { $board = Board::where('slug', $slug)->first(); $boardId = $board?->id; @@ -434,12 +452,13 @@ class PostRepository implements PostRepositoryInterface || $this->checkBoardPermission($slug, 'admin.manage') || $this->checkBoardPermission($slug, 'manager', PermissionType::User); + // Board 를 이미 받았으면 관계로 같은 행을 다시 읽지 않는다 (조회 후 setRelation 으로 부착). + $relations = $board ? ['user', 'user.avatarAttachment'] : ['user', 'user.avatarAttachment', 'board']; + $post = Post::withTrashed() ->where('board_id', $boardId) ->with([ - 'user', - 'user.avatarAttachment', - 'board', + ...$relations, 'parent' => function ($query) { $query->withTrashed() ->with('user'); @@ -462,6 +481,11 @@ class PostRepository implements PostRepositoryInterface // 모든 하위 답글을 재귀적으로 로드하여 트리 구조로 설정 if ($post) { + // 호출자가 넘긴 Board 는 관계로 적재하지 않았으므로 여기서 부착한다. + if ($board) { + $post->setRelation('board', $board); + } + // loadAllDescendantReplies는 board_id만 필요하므로 Board 모델 대신 조회된 board 사용 $board = $board ?? $post->board; $allReplies = $this->loadAllDescendantReplies($post->id, $board, $hasDeletePermission); @@ -529,9 +553,9 @@ class PostRepository implements PostRepositoryInterface * @param string $slug 게시판 슬러그 * @param array $filters 필터 조건 * @param bool $withTrashed 삭제된 게시글 포함 여부 - * @return int 일반 게시글 수 (답글, 공지 제외) + * @return BoundedCount 일반 게시글 수 + 정확도 (답글, 공지 제외) */ - public function countNormalPosts(string $slug, array $filters = [], bool $withTrashed = false): int + public function countNormalPosts(string $slug, array $filters = [], bool $withTrashed = false): BoundedCount { $board = Board::where('slug', $slug)->first(); @@ -554,7 +578,11 @@ class PostRepository implements PostRepositoryInterface // 필터 적용 $this->applyFilters($query, $filters); - return $query->count(); + // 총 건수는 상한까지만 센다. 검색어가 걸린 목록은 매칭 수가 데이터 증가에 비례하고, + // 이 값은 목록 화면이 열릴 때마다 계산된다. 상한 이하면 지금과 값이 같고, 초과할 + // 때만 "이상" 으로 보고한다 — 페이지 이동은 simplePaginate 의 per_page + 1 실측이 + // 담당하므로 상한과 무관하게 끝까지 열려 있다. + return BoundedPaginator::count($query, PaginationLimits::resultCap('board.posts')); } /** @@ -862,11 +890,16 @@ class PostRepository implements PostRepositoryInterface } // 3단계: 모든 하위 답글 조회 (모든 depth 처리 — depth-1만이 아닌 depth-2+ 포함) + // + // 깊이별로 한 번씩 조회하되 누적 건수에 상한을 둔다. 상한이 없으면 답글이 많은 + // 게시판에서 한 페이지를 여는 것만으로 그 게시판의 답글 전량을 메모리에 올린다. $parentIds = $parents->pluck('id')->toArray(); + $replyCap = PaginationLimits::resultCap('board.reply_tree'); $allReplies = collect([]); if (! empty($parentIds)) { $currentLevelIds = $parentIds; + $seenIds = array_flip($parentIds); while (! empty($currentLevelIds)) { $levelQuery = Post::query() @@ -884,6 +917,10 @@ class PostRepository implements PostRepositoryInterface $levelQuery->with($relations); } + if ($replyCap !== null) { + $levelQuery->limit(max(1, $replyCap - $allReplies->count())); + } + $levelReplies = $levelQuery->get($columns); if ($levelReplies->isEmpty()) { @@ -892,17 +929,37 @@ class PostRepository implements PostRepositoryInterface $allReplies = $allReplies->merge($levelReplies); $currentLevelIds = $levelReplies->pluck('id')->toArray(); + + // 오염된 데이터(순환 참조)에서도 유한 종료를 보장한다. + $currentLevelIds = array_values(array_filter( + $currentLevelIds, + function ($id) use (&$seenIds) { + if (isset($seenIds[$id])) { + return false; + } + $seenIds[$id] = true; + + return true; + } + )); + + if ($replyCap !== null && $allReplies->count() >= $replyCap) { + break; + } } } $replies = $allReplies; // 4단계: 병합 (원글 + 모든 하위 답글을 깊이 우선 순으로) + // + // 부모별로 한 번만 그룹지어 둔다. 노드마다 전체 컬렉션을 where 로 훑으면 + // 답글 수의 제곱에 비례해 비교가 늘어난다. + $repliesByParent = $replies->sortBy('id')->groupBy('parent_id'); $mergedItems = collect([]); - $appendReplies = function (int $postId) use (&$appendReplies, &$mergedItems, $replies): void { - $directReplies = $replies->where('parent_id', $postId)->sortBy('id'); - foreach ($directReplies as $reply) { + $appendReplies = function (int $postId) use (&$appendReplies, &$mergedItems, $repliesByParent): void { + foreach ($repliesByParent->get($postId) ?? [] as $reply) { $mergedItems->push($reply); $appendReplies($reply->id); } @@ -1273,23 +1330,32 @@ class PostRepository implements PostRepositoryInterface * @param string $keyword 검색 키워드 * @param string $orderBy 정렬 컬럼 * @param string $direction 정렬 방향 (asc, desc) - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: \Illuminate\Database\Eloquent\Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $slug, string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $limit = 10): array - { - $query = $this->buildPublicSearchQuery($slug, $keyword); - $total = $query->count(); - - $items = $query->with('user') + public function searchByKeyword( + string $slug, + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage { + // 종전에는 같은 FULLTEXT 술어를 count() 로 한 번, get() 으로 또 한 번 실행했다. + // 페이지네이터 한 번으로 합치고, 총 건수에는 상한을 건다. + $query = $this->buildPublicSearchQuery($slug, $keyword) + ->with('user') ->orderBy($orderBy, $direction) - ->limit($limit) - ->get(); + // 전순서 보장 — 정렬 컬럼이 비고유라 페이지 경계에서 행이 겹치거나 샐 수 있다 + ->orderBy('id', $direction === 'asc' ? 'asc' : 'desc'); - return [ - 'total' => $total, - 'items' => $items, - ]; + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + page: $page, + resultCap: PaginationLimits::resultCap('search'), + ); } /** @@ -1297,11 +1363,14 @@ class PostRepository implements PostRepositoryInterface * * @param string $slug 게시판 슬러그 * @param string $keyword 검색 키워드 - * @return int 일치하는 게시글 수 + * @return BoundedCount 일치하는 게시글 수 (정확도 포함) */ - public function countByKeyword(string $slug, string $keyword): int + public function countByKeyword(string $slug, string $keyword): BoundedCount { - return $this->buildPublicSearchQuery($slug, $keyword)->count(); + return BoundedPaginator::count( + $this->buildPublicSearchQuery($slug, $keyword), + PaginationLimits::resultCap('search') + ); } /** @@ -1313,23 +1382,53 @@ class PostRepository implements PostRepositoryInterface * @param string $direction 정렬 방향 (asc, desc) * @param int $perPage 페이지당 항목 수 * @param int $page 페이지 번호 - * @return array{total: int, items: \Illuminate\Database\Eloquent\Collection} + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchAcrossBoards(array $boardIds, string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $perPage = 10, int $page = 1): array - { - $query = $this->buildPublicSearchQueryByIds($boardIds, $keyword); - $total = $query->count(); - - $items = (clone $query) + public function searchAcrossBoards( + array $boardIds, + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage { + // count() + get() 이중 실행을 페이지네이터 한 번으로 합친다. + // 다른 탭 조회 시 배지용 COUNT 까지 더해 같은 술어가 3회 실행되던 경로다. + $query = $this->buildPublicSearchQueryByIds($boardIds, $keyword) ->with('user', 'board') ->orderBy($orderBy, $direction) - ->forPage($page, $perPage) - ->get(); + ->orderBy('id', $direction === 'asc' ? 'asc' : 'desc'); - return [ - 'total' => $total, - 'items' => $items, - ]; + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + page: $page, + resultCap: PaginationLimits::resultCap('search'), + ); + } + + /** + * {@inheritDoc} + */ + public function searchAcrossBoardsByCursor( + array $boardIds, + string $keyword, + array $sortKeys, + int $perPage = 10, + ?string $cursor = null + ): CursorPaginator { + // 커서 모드에는 OFFSET 이 없다. 깊은 페이지에서 건너뛸 행을 실제로 읽던 비용이 + // 사라지므로 상한 COUNT 도 이 경로에서는 하지 않는다 (총 건수는 배지 집계 담당). + $query = $this->buildPublicSearchQueryByIds($boardIds, $keyword) + ->with('user', 'board'); + + return KeysetPaginator::paginate( + query: $query, + perPage: $perPage, + sortKeys: $sortKeys, + uniqueKey: 'id', + cursor: $cursor, + ); } /** @@ -1337,11 +1436,14 @@ class PostRepository implements PostRepositoryInterface * * @param array $boardIds 검색 대상 게시판 ID 목록 * @param string $keyword 검색 키워드 - * @return int 키워드와 일치하는 게시글 수 + * @return BoundedCount 키워드와 일치하는 게시글 수 (정확도 포함) */ - public function countAcrossBoards(array $boardIds, string $keyword): int + public function countAcrossBoards(array $boardIds, string $keyword): BoundedCount { - return $this->buildPublicSearchQueryByIds($boardIds, $keyword)->count(); + return BoundedPaginator::count( + $this->buildPublicSearchQueryByIds($boardIds, $keyword), + PaginationLimits::resultCap('search') + ); } /** @@ -1387,22 +1489,17 @@ class PostRepository implements PostRepositoryInterface /** * 키워드 검색 조건을 쿼리에 적용합니다. * - * FULLTEXT 인덱스가 지원되면 MATCH...AGAINST를, 아니면 LIKE fallback을 사용합니다. + * 어떤 조건이 붙는지는 활성 검색 엔진이 정합니다. 저장소는 "이 컬럼들로 이 키워드를 + * 걸어라" 만 말하고 엔진 종류를 알지 않습니다 — 구체 엔진을 여기서 지목하면 플러그인이 + * 등록한 검색 엔진이 호출될 기회 자체를 잃습니다. 정제·폴백(LIKE, 와일드카드 escape + * 포함)은 모두 코어 해석기가 단독으로 수행합니다. * * @param Builder $query 쿼리 빌더 * @param string $keyword 검색 키워드 */ private function applyKeywordSearch(Builder $query, string $keyword): void { - if (DatabaseFulltextEngine::supportsFulltext()) { - $query->whereRaw('MATCH(`title`, `content`) AGAINST(? IN BOOLEAN MODE)', [$keyword]); - } else { - $escapedKeyword = $this->escapeLikeKeyword($keyword); - $query->where(function ($q) use ($escapedKeyword) { - $q->where('title', 'like', "%{$escapedKeyword}%") - ->orWhere('content', 'like', "%{$escapedKeyword}%"); - }); - } + KeywordSearch::apply($query, ['title', 'content'], $keyword); } /** @@ -1595,6 +1692,7 @@ class PostRepository implements PostRepositoryInterface */ private function getInactiveBoardIds(): array { + // audit:allow query-unbounded-get reason: 게시판은 운영자가 만든 수만큼만 존재한다 (글 수와 무관) return Board::where('is_active', false)->pluck('id')->all(); } diff --git a/modules/_bundled/sirsoft-board/src/Repositories/ReportRepository.php b/modules/_bundled/sirsoft-board/src/Repositories/ReportRepository.php index 38730611..bc373e19 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/ReportRepository.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/ReportRepository.php @@ -3,9 +3,11 @@ namespace Modules\Sirsoft\Board\Repositories; use App\Helpers\PermissionHelper; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; -use App\Search\Engines\DatabaseFulltextEngine; +use App\Search\KeywordSearch; +use App\Support\Query\PaginationLimits; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; @@ -29,6 +31,7 @@ use Modules\Sirsoft\Board\Repositories\Contracts\ReportRepositoryInterface; */ class ReportRepository implements ReportRepositoryInterface { + use FiltersByDateRange; use PaginatesWithDeferredJoin; use ResolvesSortSpec; @@ -92,6 +95,7 @@ class ReportRepository implements ReportRepositoryInterface sort: [['column' => 'created_at', 'direction' => 'desc']], perPage: $perPage, relations: ['board', 'author', 'processor'], + resultCap: PaginationLimits::resultCap('admin.reports'), ); } @@ -358,6 +362,7 @@ class ReportRepository implements ReportRepositoryInterface // 대상 글 ID — 게시글 신고는 target_id 자체, 댓글 신고는 그 댓글이 달린 글 $outer->addSelect(['target_post_id_comment' => $targetColumn(Comment::class, 'comment', 'post_id')]); }, + resultCap: PaginationLimits::resultCap('admin.reports'), ); // 갈래별로 나눠 읽은 대상 정보를 화면이 쓰는 단일 속성으로 합친다. @@ -398,7 +403,7 @@ class ReportRepository implements ReportRepositoryInterface // 추가 필드: post_title (paginateGrouped 전용 — logs 첫 번째 snapshot 기준) if (in_array('post_title', $extraFields) && ($searchField === 'all' || $searchField === 'post_title')) { $q->orWhereHas('logs', function ($lq) use ($keyword) { - DatabaseFulltextEngine::whereFulltext($lq, 'snapshot', $keyword); + KeywordSearch::apply($lq, 'snapshot', $keyword); $lq->oldest(); }); } @@ -406,7 +411,7 @@ class ReportRepository implements ReportRepositoryInterface // 게시판명 검색 if ($searchField === 'all' || $searchField === 'board_name') { $q->orWhereHas('board', function ($bq) use ($keyword) { - DatabaseFulltextEngine::whereFulltext($bq, 'name', $keyword); + KeywordSearch::apply($bq, 'name', $keyword); }); } @@ -463,9 +468,11 @@ class ReportRepository implements ReportRepositoryInterface */ public function countTodayReportsByUser(int $userId): int { - return ReportLog::where('reporter_id', $userId) - ->whereDate('created_at', today()) - ->count(); + // whereDate 는 컬럼에 DATE() 를 씌워 인덱스를 무력화한다 — 범위 조건으로 준다. + $query = ReportLog::where('reporter_id', $userId); + $this->applyDayFilter($query, 'created_at', today()); + + return $query->count(); } /** diff --git a/modules/_bundled/sirsoft-board/src/Services/CommentService.php b/modules/_bundled/sirsoft-board/src/Services/CommentService.php index 0c343050..c7f68546 100644 --- a/modules/_bundled/sirsoft-board/src/Services/CommentService.php +++ b/modules/_bundled/sirsoft-board/src/Services/CommentService.php @@ -5,6 +5,8 @@ namespace Modules\Sirsoft\Board\Services; use App\Contracts\Extension\CacheInterface; use App\Enums\PermissionType; use App\Extension\HookManager; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Pagination\LengthAwarePaginator; @@ -86,6 +88,66 @@ class CommentService return $this->commentRepository->getByPostId($slug, $postId, $withTrashed, $orderDirection, $scopePermission, $boardId); } + /** + * 특정 게시글의 댓글을 원댓글 기준으로 페이지네이션해 조회합니다. + * + * 댓글이 상한을 넘는 글에서도 뒤쪽 댓글에 도달할 수 있게 하는 경로입니다. + * 정렬 방향·권한 스코프 해석은 전량 조회와 동일합니다. + * + * @param string $slug 게시판 슬러그 + * @param int $postId 게시글 ID + * @param int $perPage 페이지당 원댓글 수 + * @param int $page 현재 페이지 + * @param string $context 컨텍스트 (admin 또는 user) + * @param bool|null $withTrashed 삭제된 댓글 포함 여부 (null이면 권한으로 결정) + * @param int|null $boardId 게시판 ID + * @param Board|null $board 게시판 모델 + * @return BoundedPage 원댓글 기준 페이지 (트리 정렬된 댓글 컬렉션) + */ + public function paginateCommentsByPostId( + string $slug, + int $postId, + int $perPage, + int $page = 1, + string $context = 'admin', + ?bool $withTrashed = null, + ?int $boardId = null, + ?Board $board = null + ): BoundedPage { + if ($withTrashed === null) { + $withTrashed = $this->checkBoardPermission($slug, 'admin.control') + || $this->checkBoardPermission($slug, 'admin.manage') + || $this->checkBoardPermission($slug, 'manager', PermissionType::User); + } + + if (! $board) { + $board = $boardId + ? $this->boardRepository->find($boardId) + : $this->boardRepository->findBySlug($slug); + } + $boardId = $boardId ?? $board?->id; + $commentOrder = $board?->comment_order; + + $orderDirection = $commentOrder instanceof \BackedEnum + ? $commentOrder->value + : ($commentOrder ?? 'DESC'); + + $scopePermission = $context === 'admin' + ? "sirsoft-board.{$slug}.admin.comments.read" + : "sirsoft-board.{$slug}.comments.read"; + + return $this->commentRepository->paginateRootsByPostId( + $slug, + $postId, + $perPage, + $page, + $withTrashed, + $orderDirection, + $scopePermission, + $boardId + ); + } + /** * ID로 댓글을 조회합니다. * @@ -96,6 +158,33 @@ class CommentService * * @throws ModelNotFoundException */ + /** + * 특정 게시글의 댓글 총 건수를 조회합니다. + * + * 목록이 상한에서 끊긴 경우에만 부르도록 설계돼 있습니다 — 상한 이하면 이미 전량을 + * 받았으므로 세는 쿼리를 다시 실행할 이유가 없습니다. + * + * @param string $slug 게시판 슬러그 + * @param int $postId 게시글 ID + * @param bool $withTrashed 삭제 댓글 포함 여부 + * @param int|null $boardId 게시판 ID (전달 시 Board 재조회 생략) + * @return BoundedCount 댓글 총 건수 (정확도 포함) + */ + public function countCommentsByPostId(string $slug, int $postId, bool $withTrashed = false, ?int $boardId = null): BoundedCount + { + return $this->commentRepository->countByPostId($slug, $postId, $withTrashed, $boardId); + } + + /** + * 댓글 하나를 조회합니다. + * + * @param string $slug 게시판 슬러그 + * @param int $id 댓글 ID + * @param int|null $postId 게시글 ID (전달 시 상위 스코프까지 검사) + * @return Comment 조회된 댓글 + * + * @throws ModelNotFoundException 댓글이 없거나 상위 스코프가 다를 때 + */ public function getComment(string $slug, int $id, ?int $postId = null): Comment { return $this->commentRepository->findOrFail($slug, $id, $postId); diff --git a/modules/_bundled/sirsoft-board/src/Services/PostService.php b/modules/_bundled/sirsoft-board/src/Services/PostService.php index aaa4acac..676b41a4 100644 --- a/modules/_bundled/sirsoft-board/src/Services/PostService.php +++ b/modules/_bundled/sirsoft-board/src/Services/PostService.php @@ -3,12 +3,16 @@ namespace Modules\Sirsoft\Board\Services; use App\Contracts\Extension\CacheInterface; +use App\Enums\TotalRelation; use App\Extension\HookManager; use App\Helpers\PermissionHelper; +use App\Search\SearchPagePolicy; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Contracts\Pagination\Paginator; -use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\UploadedFile; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -30,6 +34,26 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; */ class PostService { + /** + * 검색 정렬 이름 → [실제 컬럼, 방향] 선언 + * + * 코어({@see SearchPagePolicy})가 이 선언을 읽어 커서 적용 여부를 판정한다. + * 여기에 없는 정렬 이름(관련도순 등)은 커서로 처리하지 않고 offset 을 유지한다. + */ + public const SEARCH_SORT_MAP = [ + 'latest' => ['created_at', 'desc'], + 'oldest' => ['created_at', 'asc'], + 'views' => ['view_count', 'desc'], + 'popular' => ['view_count', 'desc'], + ]; + + /** + * 커서(키셋) 경계로 쓸 수 있는 실제 컬럼 선언 + * + * 커서는 정렬 키를 WHERE 절 경계로 삼으므로 계산값·별칭은 넣을 수 없다. + */ + public const SEARCH_CURSOR_COLUMNS = ['created_at', 'view_count']; + /** * PostService 생성자 * @@ -86,11 +110,11 @@ class PostService * @param array $filters 필터 조건 * @param bool $withTrashed 삭제된 게시글 포함 여부 * @param string $context 호출 컨텍스트 (admin/user — 권한 스코프 판정용) - * @return int 일반 게시글 수 (답글, 공지 제외) + * @return BoundedCount 일반 게시글 수 + 정확도 (답글, 공지 제외) * * @throws ModelNotFoundException 게시판을 찾을 수 없는 경우 */ - public function getTotalNormalPosts(string $slug, array $filters = [], bool $withTrashed = false, string $context = 'admin'): int + public function getTotalNormalPosts(string $slug, array $filters = [], bool $withTrashed = false, string $context = 'admin'): BoundedCount { // 게시판 존재성 검증 $this->validateBoardExists($slug); @@ -115,9 +139,9 @@ class PostService * @param array $filters 필터 조건 * @param bool $withTrashed 삭제된 게시글 포함 여부 * @param string $context 컨텍스트 (admin 또는 user) - * @return int 일반 게시글 수 + * @return BoundedCount 일반 게시글 수 + 정확도 */ - public function getCachedNormalPostCount(string $slug, int $boardId, array $filters = [], bool $withTrashed = false, string $context = 'admin'): int + public function getCachedNormalPostCount(string $slug, int $boardId, array $filters = [], bool $withTrashed = false, string $context = 'admin'): BoundedCount { // 필터가 적용된 경우 캐시 미사용 — 실제 COUNT 실행 $hasActiveFilters = ! empty($filters['search']) @@ -127,18 +151,32 @@ class PostService || ! empty($filters['created_at_from']) || ! empty($filters['created_at_to']); - if ($hasActiveFilters || $withTrashed) { + if ($hasActiveFilters) { return $this->getTotalNormalPosts($slug, $filters, $withTrashed, $context); } - $cacheKey = "board_normal_count_{$boardId}"; + // 관리자 목록은 삭제글 포함(withTrashed=true)이 기본이라, 이 조건에서 캐시를 건너뛰면 + // 관리자가 목록을 열 때마다 전체 COUNT 가 실행된다. 포함 여부는 결과가 달라지는 + // 축이므로 캐시를 우회하는 대신 **키를 나눈다** — 두 값이 서로를 덮어쓰지 않는다. + $cacheKey = $withTrashed + ? "board_normal_count_{$boardId}_with_trashed" + : "board_normal_count_{$boardId}"; - return $this->cache->remember( + // 캐시에는 값 객체가 아니라 원시 배열을 담는다. 드라이버마다 직렬화 방식이 달라 + // 객체를 그대로 넣으면 복원 시 클래스 정의에 묶이고, 정확도 필드가 하나라도 + // 빠지면 잘린 건수가 정확한 것처럼 되살아난다. + $cached = $this->cache->remember( $cacheKey, - fn () => $this->getTotalNormalPosts($slug, $filters, $withTrashed, $context), + fn () => $this->getTotalNormalPosts($slug, $filters, $withTrashed, $context)->toArray(), (int) g7_core_settings('cache.default_ttl', 86400), tags: ['board-stats'] ); + + return new BoundedCount( + (int) ($cached['total'] ?? 0), + TotalRelation::tryFrom($cached['total_relation'] ?? '') ?? TotalRelation::Exact, + $cached['result_cap'] ?? null, + ); } /** @@ -484,11 +522,12 @@ class PostService * * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID + * @param int|null $boardId 게시판 ID (전달 시 슬러그 재조회 생략) * @return int 증가된 조회수 */ - public function incrementViewCount(string $slug, int $id): int + public function incrementViewCount(string $slug, int $id, ?int $boardId = null): int { - return $this->postRepository->incrementViewCount($slug, $id); + return $this->postRepository->incrementViewCount($slug, $id, $boardId); } /** @@ -499,9 +538,10 @@ class PostService * * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID + * @param int|null $boardId 게시판 ID (전달 시 슬러그 재조회 생략) * @return bool 조회수가 증가했으면 true, 이미 조회한 경우 false */ - public function incrementViewCountOnce(string $slug, int $id): bool + public function incrementViewCountOnce(string $slug, int $id, ?int $boardId = null): bool { $identifier = Auth::id() ?? request()->ip(); $key = "post_view_{$slug}_{$id}_{$identifier}"; @@ -510,7 +550,7 @@ class PostService return false; } - $this->incrementViewCount($slug, $id); + $this->incrementViewCount($slug, $id, $boardId); $ttl = (int) g7_module_settings('sirsoft-board', 'spam_security.view_count_cache_ttl', 86400); $this->cache->put($key, true, $ttl); @@ -523,23 +563,45 @@ class PostService * @param string $slug 게시판 슬러그 * @param int $id 게시글 ID * @param int|null $boardId 이미 검증된 게시판 ID (중복 조회 방지용) + * @param Board|null $board 이미 조회한 게시판 모델 (넘기면 관계 적재까지 생략) + * @param string|null $context 스코프 검사 컨텍스트 ('user' | 'admin'). null 이면 검사하지 않는다 * @return Post 게시글 모델 (카운트 포함) * * @throws ModelNotFoundException 게시판 또는 게시글을 찾을 수 없는 경우 + * @throws AccessDeniedHttpException 스코프 접근이 거부된 경우 ($context 지정 시) */ - public function getPostWithCounts(string $slug, int $id, ?int $boardId = null): Post - { + public function getPostWithCounts( + string $slug, + int $id, + ?int $boardId = null, + ?Board $board = null, + ?string $context = null + ): Post { + $boardId = $board?->id ?? $boardId; + // boardId가 전달되면 이미 검증된 것이므로 중복 조회 방지 if (! $boardId) { $this->validateBoardExists($slug); } - $post = $this->postRepository->findWithCounts($slug, $id, $boardId); + $post = $this->postRepository->findWithCounts($slug, $id, $boardId, $board); if (! $post) { throw new ModelNotFoundException(__('sirsoft-board::messages.errors.post_not_found')); } + // 컨텍스트가 주어지면 스코프 접근 검사를 이 인스턴스로 수행한다. + // 종전에는 권한 판정 전용으로 같은 행을 한 번 더 읽었다 (#519 F3). + if ($context !== null) { + $scopePermission = $context === 'admin' + ? "sirsoft-board.{$slug}.admin.posts.read" + : "sirsoft-board.{$slug}.posts.read"; + + if (! PermissionHelper::checkScopeAccess($post, $scopePermission)) { + throw new AccessDeniedHttpException(__('auth.scope_denied')); + } + } + return $post; } @@ -647,23 +709,12 @@ class PostService throw new ModelNotFoundException(__('sirsoft-board::messages.errors.board_not_found')); } - // 조회수 증가 (캐시 기반 중복 방지) - $this->incrementViewCountOnce($slug, $id); + // 조회수 증가 (캐시 기반 중복 방지) — 이미 조회한 게시판 ID 를 넘겨 재조회를 막는다 + $this->incrementViewCountOnce($slug, $id, $board->id); - // 댓글/첨부파일 카운트 포함하여 게시글 조회 (boardId 전달로 Board 중복 조회 방지) - $post = $this->getPostWithCounts($slug, $id, $board->id); - - // 컨텍스트 기반 스코프 접근 검사 - $scopePermission = $context === 'admin' - ? "sirsoft-board.{$slug}.admin.posts.read" - : "sirsoft-board.{$slug}.posts.read"; - - if (! PermissionHelper::checkScopeAccess($post, $scopePermission)) { - throw new AccessDeniedHttpException(__('auth.scope_denied')); - } - - // board 관계 수동 설정 - $post->setRelation('board', $board); + // 댓글/첨부파일 카운트 포함하여 게시글 조회 + 컨텍스트 기반 스코프 접근 검사. + // 이미 조회한 Board 를 넘겨 게시판 재조회와 board 관계 적재를 함께 생략한다. + $post = $this->getPostWithCounts($slug, $id, board: $board, context: $context); // 댓글 로드 (게시판 comment_order 설정 적용, Board 객체 전달로 중복 조회 방지) $comments = $this->commentService->getCommentsByPostId($slug, $id, boardId: $board->id, board: $board); @@ -1118,14 +1169,15 @@ class PostService * @param string $slug 게시판 슬러그 * @param string $keyword 검색 키워드 * @param string $sort 정렬 옵션 - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $slug, string $keyword, string $sort = 'latest', int $limit = 10): array + public function searchByKeyword(string $slug, string $keyword, string $sort = 'latest', int $perPage = 10, int $page = 1): BoundedPage { [$orderBy, $direction] = $this->resolveSortColumn($sort); - return $this->postRepository->searchByKeyword($slug, $keyword, $orderBy, $direction, $limit); + return $this->postRepository->searchByKeyword($slug, $keyword, $orderBy, $direction, $perPage, $page); } /** @@ -1133,9 +1185,9 @@ class PostService * * @param string $slug 게시판 슬러그 * @param string $keyword 검색 키워드 - * @return int 일치하는 게시글 수 + * @return BoundedCount 일치하는 게시글 수 (정확도 포함) */ - public function countByKeyword(string $slug, string $keyword): int + public function countByKeyword(string $slug, string $keyword): BoundedCount { return $this->postRepository->countByKeyword($slug, $keyword); } @@ -1148,23 +1200,61 @@ class PostService * @param string $sort 정렬 옵션 * @param int $perPage 페이지당 항목 수 * @param int $page 페이지 번호 - * @return array{total: int, items: Collection} + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchAcrossBoards(array $boardIds, string $keyword, string $sort = 'latest', int $perPage = 10, int $page = 1): array + public function searchAcrossBoards(array $boardIds, string $keyword, string $sort = 'latest', int $perPage = 10, int $page = 1): BoundedPage { [$orderBy, $direction] = $this->resolveSortColumn($sort); return $this->postRepository->searchAcrossBoards($boardIds, $keyword, $orderBy, $direction, $perPage, $page); } + /** + * 여러 게시판에서 키워드로 게시글을 커서(키셋)로 검색합니다. + * + * 커서 적용 가능 여부는 코어({@see SearchPagePolicy})가 판정한다. 이 서비스는 + * 정렬 선언({@see self::SEARCH_SORT_MAP})만 제공하고 규칙을 다시 쓰지 않는다. + * 적용할 수 없는 정렬(관련도순 등)이면 null 을 돌려주고 호출자는 offset 경로를 쓴다. + * + * @param array $boardIds 검색 대상 게시판 ID 목록 + * @param string $keyword 검색 키워드 + * @param string $sort 정렬 옵션 + * @param int $perPage 페이지당 항목 수 + * @param string|null $cursor 인코딩된 커서 (첫 페이지면 null) + * @param int $page 요청이 지목한 페이지 번호 (딥링크 판정용, 없으면 1) + * @return CursorPaginator|null 커서 페이지 결과 (커서 적용 불가 시 null) + */ + public function searchAcrossBoardsByCursor( + array $boardIds, + string $keyword, + string $sort = 'latest', + int $perPage = 10, + ?string $cursor = null, + int $page = 1 + ): ?CursorPaginator { + $sortKeys = SearchPagePolicy::sortKeys($sort, self::SEARCH_SORT_MAP); + + if (! SearchPagePolicy::usesCursor($cursor, $sortKeys, self::SEARCH_CURSOR_COLUMNS, $page)) { + return null; + } + + return $this->postRepository->searchAcrossBoardsByCursor( + $boardIds, + $keyword, + $sortKeys, + $perPage, + $cursor + ); + } + /** * 여러 게시판에서 키워드와 일치하는 게시글 수를 조회합니다 (단일 쿼리). * * @param array $boardIds 검색 대상 게시판 ID 목록 * @param string $keyword 검색 키워드 - * @return int 일치하는 게시글 수 + * @return BoundedCount 일치하는 게시글 수 (정확도 포함) */ - public function countAcrossBoards(array $boardIds, string $keyword): int + public function countAcrossBoards(array $boardIds, string $keyword): BoundedCount { return $this->postRepository->countAcrossBoards($boardIds, $keyword); } diff --git a/modules/_bundled/sirsoft-board/src/Support/BoardPermissionCacheKeys.php b/modules/_bundled/sirsoft-board/src/Support/BoardPermissionCacheKeys.php new file mode 100644 index 00000000..ce99c420 --- /dev/null +++ b/modules/_bundled/sirsoft-board/src/Support/BoardPermissionCacheKeys.php @@ -0,0 +1,27 @@ +attributes->remove('_board_permission_cache'); + request()->attributes->remove(BoardPermissionCacheKeys::PERMISSION); + request()->attributes->remove(BoardPermissionCacheKeys::ABILITIES); } /** @@ -145,7 +147,7 @@ trait ChecksBoardPermission $cacheKey = $userId.'|'.$permission.'|'.$type->value.'|'.($requireAll ? '1' : '0'); // 요청 인스턴스에 권한 캐시를 저장하여 요청 단위로 자동 격리 - $cache = $request->attributes->get('_board_permission_cache', []); + $cache = $request->attributes->get(BoardPermissionCacheKeys::PERMISSION, []); if (isset($cache[$cacheKey])) { return $cache[$cacheKey]; @@ -167,7 +169,7 @@ trait ChecksBoardPermission ); $cache[$cacheKey] = $passed; - $request->attributes->set('_board_permission_cache', $cache); + $request->attributes->set(BoardPermissionCacheKeys::PERMISSION, $cache); return $passed; } diff --git a/modules/_bundled/sirsoft-board/tests/BoardTestCase.php b/modules/_bundled/sirsoft-board/tests/BoardTestCase.php index f6a7129e..615f6c24 100644 --- a/modules/_bundled/sirsoft-board/tests/BoardTestCase.php +++ b/modules/_bundled/sirsoft-board/tests/BoardTestCase.php @@ -2,6 +2,7 @@ namespace Modules\Sirsoft\Board\Tests; +use App\Http\Middleware\PermissionMiddleware; use App\Models\Permission; use App\Models\Role; use Illuminate\Support\Facades\DB; @@ -180,10 +181,7 @@ abstract class BoardTestCase extends ModuleTestCase */ protected function resetPermissionMiddlewareCache(): void { - $reflection = new \ReflectionClass(\App\Http\Middleware\PermissionMiddleware::class); - $property = $reflection->getProperty('guestRoleCache'); - $property->setAccessible(true); - $property->setValue(null, null); + PermissionMiddleware::clearGuestRoleCache(); } /** diff --git a/modules/_bundled/sirsoft-board/tests/Feature/CommentTruncationDisclosureTest.php b/modules/_bundled/sirsoft-board/tests/Feature/CommentTruncationDisclosureTest.php new file mode 100644 index 00000000..3b9b9496 --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/CommentTruncationDisclosureTest.php @@ -0,0 +1,121 @@ + $this->board->id, + 'title' => '댓글 절단 테스트', + 'content' => '본문', + 'author_name' => '작성자', + 'status' => 'published', + 'ip_address' => '127.0.0.1', + ]); + + for ($i = 0; $i < $commentCount; $i++) { + Comment::create([ + 'board_id' => $this->board->id, + 'post_id' => $post->id, + 'content' => '댓글 '.$i, + 'author_name' => '댓글쓴이', + 'ip_address' => '127.0.0.1', + ]); + } + + return $post; + } + + /** + * 상한 이하면 댓글 전량이 나오고 절단 표시가 없는지 확인 + */ + public function test_comments_are_not_truncated_under_cap(): void + { + config(['g7_settings.core.pagination.result_cap' => 100]); + $post = $this->seedPostWithComments(5); + + $repository = app(CommentRepositoryInterface::class); + $comments = $repository->getByPostId($this->board->slug, $post->id, boardId: $this->board->id); + + $this->assertCount(5, $comments); + + $count = $repository->countByPostId($this->board->slug, $post->id, boardId: $this->board->id); + $this->assertInstanceOf(BoundedCount::class, $count); + $this->assertSame(5, $count->total); + $this->assertFalse($count->isTruncated()); + } + + /** + * 상한을 넘으면 목록이 그 지점에서 끊기고 총 건수가 "이상" 으로 보고되는지 확인 + */ + public function test_comment_count_reports_truncation_over_cap(): void + { + config(['g7_settings.core.pagination.result_cap' => 3]); + $post = $this->seedPostWithComments(7); + + $repository = app(CommentRepositoryInterface::class); + + $comments = $repository->getByPostId($this->board->slug, $post->id, boardId: $this->board->id); + $this->assertLessThanOrEqual(3, $comments->count(), '상한을 넘겨 조회됐다 — 목록이 끊기지 않았다'); + + $count = $repository->countByPostId($this->board->slug, $post->id, boardId: $this->board->id); + $this->assertSame(3, $count->total); + $this->assertSame(TotalRelation::AtLeast, $count->totalRelation()); + $this->assertTrue($count->isTruncated()); + } + + /** + * 상세 응답이 절단 사실을 필드로 알리는지 확인 + * + * 저장소가 정확도를 돌려줘도 화면까지 도달하지 않으면 아무 소용이 없다. + */ + public function test_post_detail_response_discloses_truncation(): void + { + config(['g7_settings.core.pagination.result_cap' => 3]); + $post = $this->seedPostWithComments(7); + + $response = $this->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$post->id}"); + + $response->assertOk(); + $response->assertJsonPath('data.comments_truncated', true); + $response->assertJsonPath('data.comments_total', 3); + $response->assertJsonPath('data.comments_total_is_exact', false); + } + + /** + * 끊기지 않은 경우에는 절단 플래그가 서지 않는지 확인 + */ + public function test_post_detail_response_reports_no_truncation_when_under_cap(): void + { + config(['g7_settings.core.pagination.result_cap' => 100]); + $post = $this->seedPostWithComments(4); + + $response = $this->getJson("/api/modules/sirsoft-board/boards/{$this->board->slug}/posts/{$post->id}"); + + $response->assertOk(); + $response->assertJsonPath('data.comments_truncated', false); + $response->assertJsonPath('data.comments_total_is_exact', true); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/Feature/ListQueryCountRegressionTest.php b/modules/_bundled/sirsoft-board/tests/Feature/ListQueryCountRegressionTest.php new file mode 100644 index 00000000..1f94c465 --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/ListQueryCountRegressionTest.php @@ -0,0 +1,104 @@ + $this->board->id, + 'title' => $prefix.' '.$i, + 'content' => '본문', + 'author_name' => '작성자', + 'status' => 'published', + 'ip_address' => '127.0.0.1', + ]); + } + } + + /** + * 게시글 목록: 글 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * @effects post_list_query_count_stable + */ + public function test_post_list_query_count_is_stable(): void + { + $repository = app(PostRepositoryInterface::class); + $this->seedPosts(5, '초기'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->paginate($this->board->slug, [], 50, false, $this->board), + grow: fn () => $this->seedPosts(10, '추가'), + context: '게시글 목록', + ); + } + + /** + * 댓글 트리: 댓글 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 종전에는 부모 댓글을 관계로 다시 불러 같은 집합을 한 번 더 가져왔다. + * + * @effects comment_tree_query_count_stable + */ + public function test_comment_tree_query_count_is_stable(): void + { + $repository = app(CommentRepositoryInterface::class); + + $post = Post::create([ + 'board_id' => $this->board->id, + 'title' => '댓글 트리', + 'content' => '본문', + 'author_name' => '작성자', + 'status' => 'published', + 'ip_address' => '127.0.0.1', + ]); + + $seedComments = function (int $count, string $prefix) use ($post) { + for ($i = 0; $i < $count; $i++) { + Comment::create([ + 'board_id' => $this->board->id, + 'post_id' => $post->id, + 'content' => $prefix.' '.$i, + 'author_name' => '댓글쓴이', + 'ip_address' => '127.0.0.1', + ]); + } + }; + + $seedComments(5, '초기'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->getByPostId($this->board->slug, $post->id, boardId: $this->board->id), + grow: fn () => $seedComments(10, '추가'), + context: '댓글 트리', + ); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/Feature/PostDetailQueryCountRegressionTest.php b/modules/_bundled/sirsoft-board/tests/Feature/PostDetailQueryCountRegressionTest.php new file mode 100644 index 00000000..c6ce8cac --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/PostDetailQueryCountRegressionTest.php @@ -0,0 +1,125 @@ +post = Post::create([ + 'board_id' => $this->board->id, + 'title' => '상세 중복 조회 점검', + 'content' => '본문', + 'author_name' => '작성자', + 'status' => 'published', + 'ip_address' => '127.0.0.1', + ]); + } + + /** + * 상세 조회 중 실행된 SQL 을 수집합니다. + * + * @return array 실행된 SQL 목록 + */ + private function captureDetailQueries(): array + { + return $this->captureQueries(function () { + $response = $this->getJson( + '/api/modules/sirsoft-board/boards/'.$this->board->slug.'/posts/'.$this->post->id + ); + + $response->assertOk(); + }); + } + + /** + * 특정 테이블의 전체 컬럼(`select *`) 조회만 셉니다. + * + * 한 컬럼만 읽는 조회(조회수 갱신 후의 `select view_count` 등)는 목적이 다른 경량 + * 질의이므로 중복 읽기 판정에서 제외한다. 여기서 잡고 싶은 것은 같은 행 전체를 + * 두 번 가져오는 낭비다. + * + * @param array $queries 수집된 SQL 목록 + * @param string $table 테이블명 (프리픽스 제외) + * @return int 해당 테이블 전체 컬럼 SELECT 수 + */ + private function countFullRowSelectsOn(array $queries, string $table): int + { + $prefixed = DB::getTablePrefix().$table; + + return count(array_filter( + $queries, + fn (string $sql) => str_starts_with(strtolower(trim($sql)), 'select *') + && str_contains($sql, '`'.$prefixed.'`') + )); + } + + /** + * 글 행을 한 번만 읽는지 확인 + * + * 권한 판정과 응답 조립이 같은 인스턴스를 공유해야 한다. + * + * @effects post_detail_reads_post_row_once + */ + public function test_post_row_is_read_once(): void + { + $queries = $this->captureDetailQueries(); + + $postSelects = $this->countFullRowSelectsOn($queries, 'board_posts'); + + // 본문 1회 + 답글 트리 탐색 1회. 권한 판정용으로 같은 행을 한 번 더 읽던 + // 종전 동작(3회)을 차단한다. + $this->assertLessThanOrEqual( + 2, + $postSelects, + '게시글 상세에서 글 전체 조회가 '.$postSelects.'회 — 같은 행을 중복으로 읽고 있다.' + ); + } + + /** + * 게시판 행을 한 번만 읽는지 확인 + * + * 컨트롤러가 이미 조회한 게시판을 서비스·저장소가 slug 로 다시 찾지 않아야 한다. + * + * @effects post_detail_reads_board_row_once + */ + public function test_board_row_is_read_once(): void + { + $queries = $this->captureDetailQueries(); + + $boardSelects = $this->countFullRowSelectsOn($queries, 'boards'); + + $this->assertLessThanOrEqual( + 1, + $boardSelects, + '게시글 상세에서 게시판 테이블 SELECT 가 '.$boardSelects.'회 — 이미 조회한 게시판을 다시 찾고 있다.' + ); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/Feature/PostNumberBoundedTotalRegressionTest.php b/modules/_bundled/sirsoft-board/tests/Feature/PostNumberBoundedTotalRegressionTest.php new file mode 100644 index 00000000..e41c62c9 --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/PostNumberBoundedTotalRegressionTest.php @@ -0,0 +1,152 @@ + 게시글 컬렉션 + */ + private function makePosts(int $count): Collection + { + return collect(range(1, $count))->map(function (int $i) { + $post = new Post; + $post->id = 1000 + $i; + $post->is_notice = false; + $post->parent_id = null; + + return $post; + }); + } + + /** + * 컬렉션을 렌더링해 순번만 뽑아냅니다. + * + * @param int $page 현재 페이지 + * @param int $perPage 페이지당 건수 + * @param BoundedCount $total 총 건수 + 정확도 + * @param string $direction 정렬 방향 + * @return array 순번 목록 + */ + private function numbersFor(int $page, int $perPage, BoundedCount $total, string $direction = 'desc'): array + { + $paginator = new LengthAwarePaginator( + $this->makePosts($perPage), + max($total->total(), $page * $perPage), + $perPage, + $page, + ); + + $collection = new PostCollection($paginator); + $collection->setTotalNormalPosts($total); + $collection->setOrderDirection($direction); + + $rendered = $collection->toArray(request()); + + return collect($rendered['data'])->pluck('number')->all(); + } + + /** + * 총 건수가 잘리지 않았으면 순번은 지금까지처럼 정확해야 합니다. + */ + public function test_총건수가_정확하면_순번은_총건수부터_내림차순이다(): void + { + $numbers = $this->numbersFor( + page: 1, + perPage: 5, + total: new BoundedCount(120, TotalRelation::Exact, 10000), + ); + + $this->assertSame([120, 119, 118, 117, 116], $numbers); + } + + /** + * 총 건수가 잘렸으면 순번을 지어내지 않아야 합니다. + */ + public function test_총건수가_잘리면_순번은_null_이다(): void + { + $numbers = $this->numbersFor( + page: 1, + perPage: 5, + total: new BoundedCount(10000, TotalRelation::AtLeast, 10000), + ); + + $this->assertSame([null, null, null, null, null], $numbers); + } + + /** + * 상한을 넘어선 깊은 페이지에서도 0·음수가 나오지 않아야 합니다. + */ + public function test_상한을_넘은_페이지에서_0이나_음수_순번이_없다(): void + { + foreach ([501, 502, 601] as $page) { + $numbers = $this->numbersFor( + page: $page, + perPage: 20, + total: new BoundedCount(10000, TotalRelation::AtLeast, 10000), + ); + + foreach ($numbers as $number) { + $this->assertNotIsInt( + $number, + "{$page} 페이지에서 상한 총 건수로 역산한 순번이 그대로 나왔습니다." + ); + } + } + } + + /** + * 오름차순은 offset 기반이라 총 건수가 잘려도 그대로 정확해야 합니다. + */ + public function test_오름차순_순번은_총건수가_잘려도_유지된다(): void + { + $numbers = $this->numbersFor( + page: 3, + perPage: 5, + total: new BoundedCount(10000, TotalRelation::AtLeast, 10000), + direction: 'asc', + ); + + $this->assertSame([11, 12, 13, 14, 15], $numbers); + } + + /** + * PHPUnit 11 에는 assertNotIsInt 가 없으므로 의미를 그대로 옮겨 둡니다. + * + * @param mixed $value 검사 대상 + * @param string $message 실패 메시지 + */ + private function assertNotIsInt(mixed $value, string $message = ''): void + { + $this->assertFalse(is_int($value), $message); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/Feature/PostSearchKeywordSanitizationTest.php b/modules/_bundled/sirsoft-board/tests/Feature/PostSearchKeywordSanitizationTest.php new file mode 100644 index 00000000..94de3d5d --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/PostSearchKeywordSanitizationTest.php @@ -0,0 +1,181 @@ + @` 를 연산자로 해석한다. + * 원문 키워드를 그대로 바인딩하면 사용자가 그 문자를 입력하는 것만으로 파싱 오류가 나고 + * 목록 화면 전체가 500 이 된다. `#407` 이 코어에 정제기를 넣었지만 게시판은 자기 raw 쿼리를 + * 들고 있어 그 수정이 닿지 않았다. + * + * 여기서 단언하는 것은 "검색이 무엇을 찾는가" 가 아니라 **"어떤 입력에도 화면이 죽지 않는가"** 다. + * + * @scenario board-search-keyword-sanitization + * + * @effects boolean_mode_operator_does_not_500, + * html_like_keyword_does_not_500, + * operator_only_keyword_returns_empty, + * normal_keyword_still_matches + */ +class PostSearchKeywordSanitizationTest extends BoardTestCase +{ + /** + * 테스트 게시판 slug + * + * @return string 게시판 슬러그 + */ + protected function getTestBoardSlug(): string + { + return 'search-sanitize-test'; + } + + /** + * 테스트 사전 준비를 수행합니다. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->grantDefaultGuestPermissions(); + $this->resetPermissionMiddlewareCache(); + + $this->createTestPost([ + 'title' => '정상 게시글', + 'content' => '검색으로 찾을 수 있는 본문입니다.', + 'status' => 'published', + ]); + } + + /** + * BOOLEAN MODE 연산자를 포함한 검색어가 500 을 내지 않는지 확인 + * + * @effects boolean_mode_operator_does_not_500 + */ + #[Test] + public function boolean_mode_operators_do_not_break_search(): void + { + foreach (['+', '-', '*', '"', '(', ')', '~', '<', '>', '@', '+검색 -제외', '"닫히지 않은'] as $keyword) { + $response = $this->searchPosts($keyword); + + $response->assertStatus(200, "검색어 [{$keyword}] 가 500 을 냈다 — BOOLEAN MODE 정제가 적용되지 않았다"); + } + } + + /** + * HTML 형태 검색어가 500 을 내지 않는지 확인 + * + * `'); + + $response->assertStatus(200); + } + + /** + * 연산자만 입력하면 오류 대신 빈 결과가 나오는지 확인 + * + * 정제 후 남는 토큰이 없으면 "찾을 것이 없다" 이지 "오류" 가 아니다. + * + * @effects operator_only_keyword_returns_empty + */ + #[Test] + public function operator_only_keyword_returns_empty_result(): void + { + $response = $this->searchPosts('+++'); + + $response->assertStatus(200); + $this->assertSame([], $response->json('data.data') ?? [], '연산자만 입력하면 매칭이 없어야 한다'); + } + + /** + * 정제가 정상 검색어의 토큰을 보존하는지 확인 + * + * FULLTEXT 인덱스는 커밋된 행만 색인하므로, 트랜잭션 안에서 만든 게시글은 MATCH 로 + * 찾을 수 없다. 그래서 "정제가 정상 검색어를 뭉개지 않는다" 는 명제는 정제기 자체의 + * 출력으로 확인하고, 실제 매칭은 아래 LIKE 경로 테스트가 담당한다. + * + * @effects normal_keyword_still_matches + */ + #[Test] + public function sanitizer_preserves_normal_keyword_tokens(): void + { + $this->assertSame( + '"정상"', + DatabaseFulltextEngine::sanitizeBooleanModeKeyword('정상'), + '정제가 정상 검색어까지 걸러 내면 안 된다' + ); + + $this->assertSame( + '"정상" "게시글"', + DatabaseFulltextEngine::sanitizeBooleanModeKeyword('정상 게시글'), + '여러 토큰은 각각 보존되어야 한다' + ); + + $this->assertSame( + '"검색" "제외"', + DatabaseFulltextEngine::sanitizeBooleanModeKeyword('+검색 -제외'), + '연산자만 제거하고 토큰은 남긴다' + ); + } + + /** + * FULLTEXT 를 타지 않는 검색 경로(작성자 LIKE)가 정상 동작하는지 확인 + * + * 정제 변경이 목록 검색 전체를 망가뜨리지 않았음을 실제 매칭으로 확인한다. + * + * @effects normal_keyword_still_matches + */ + #[Test] + public function like_path_search_still_matches(): void + { + $this->createTestPost([ + 'title' => '작성자 검색 대상', + 'content' => '본문', + 'author_name' => '홍길동', + 'status' => 'published', + ]); + + $this->resetPermissionMiddlewareCache(); + + $url = "/api/modules/sirsoft-board/boards/{$this->board->slug}/posts?" + .http_build_query(['search' => '홍길동', 'search_field' => 'author']); + + $response = $this->getJson($url); + $response->assertStatus(200); + + $titles = collect($response->json('data.data') ?? [])->pluck('title')->all(); + + $this->assertContains('작성자 검색 대상', $titles, 'LIKE 경로 검색이 매칭되어야 한다'); + } + + /** + * 게시판 목록에 검색어를 얹어 조회합니다. + * + * @param string $keyword 검색어 + * @return TestResponse 응답 + */ + private function searchPosts(string $keyword) + { + $this->resetPermissionMiddlewareCache(); + + $url = "/api/modules/sirsoft-board/boards/{$this->board->slug}/posts?" + .http_build_query(['search' => $keyword, 'search_field' => 'title_content']); + + return $this->getJson($url); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/Feature/SearchCursorPaginationTest.php b/modules/_bundled/sirsoft-board/tests/Feature/SearchCursorPaginationTest.php new file mode 100644 index 00000000..5336ebdc --- /dev/null +++ b/modules/_bundled/sirsoft-board/tests/Feature/SearchCursorPaginationTest.php @@ -0,0 +1,205 @@ +postService = app(PostService::class); + } + + /** + * 검색 대상 게시글을 만듭니다. + * + * @param int $count 생성할 수 + */ + private function seedPosts(int $count): void + { + for ($i = 0; $i < $count; $i++) { + Post::create([ + 'board_id' => $this->board->id, + 'author_name' => '작성자', + 'ip_address' => '127.0.0.1', + 'title' => '커서키워드 게시글 '.$i, + 'content' => '커서키워드 본문 '.$i, + 'status' => 'published', + 'is_secret' => false, + ]); + } + } + + /** + * 실제 컬럼 기준 정렬은 커서 경로를 타는지 확인 + * + * @effects search_latest_sort_uses_cursor + */ + public function test_real_column_sorts_use_cursor(): void + { + // Given: 검색 대상 게시글 + $this->seedPosts(3); + + // When/Then: 선언된 정렬 전부가 커서 페이지네이터를 돌려준다 + foreach (array_keys(PostService::SEARCH_SORT_MAP) as $sort) { + $page = $this->postService->searchAcrossBoardsByCursor( + [$this->board->id], + '커서키워드', + $sort, + 4, + 'encoded-cursor' + ); + + $this->assertNotNull($page, $sort.' 정렬은 실제 컬럼이므로 커서를 써야 한다'); + } + } + + /** + * 관련도순은 커서를 쓰지 않고 offset 을 유지하는지 확인 + * + * FULLTEXT 점수는 계산값이라 WHERE 절 경계로 쓸 수 없다. + * + * @effects search_relevance_sort_stays_on_offset + */ + public function test_relevance_sort_stays_on_offset(): void + { + // Given: 검색 대상 게시글 + $this->seedPosts(3); + + // When: 관련도순으로 커서를 요청 + $page = $this->postService->searchAcrossBoardsByCursor( + [$this->board->id], + '커서키워드', + 'relevance', + 4, + 'encoded-cursor' + ); + + // Then: 커서를 쓰지 않는다 (호출자는 offset 경로로 떨어진다) + $this->assertNull($page); + } + + /** + * 커서가 없어도 첫 페이지면 커서 경로로 시작하는지 확인 + * + * 첫 페이지에 커서가 없는 것은 정상이다. 이를 "커서 없음" 으로 읽어 offset 으로 돌리면 + * 다음 커서가 발급될 자리가 사라져 화면이 영원히 offset 에 머문다. + * + * @effects search_latest_sort_uses_cursor + */ + public function test_first_page_without_cursor_starts_cursor_path(): void + { + // Given: 검색 대상 게시글 + $this->seedPosts(3); + + // When: 커서 없이 최신순 첫 페이지 요청 + $page = $this->postService->searchAcrossBoardsByCursor( + [$this->board->id], + '커서키워드', + 'latest', + 4, + null + ); + + // Then: 커서 경로로 처리한다 + $this->assertNotNull($page); + } + + /** + * 커서 없이 깊은 페이지를 직접 지목한 요청은 offset 을 유지하는지 확인 + * + * 주소로 특정 페이지를 열어 둔 링크는 그 페이지를 그대로 보여줘야 한다. + * + * @effects search_relevance_sort_stays_on_offset + */ + public function test_deep_page_without_cursor_stays_on_offset(): void + { + // Given: 검색 대상 게시글 + $this->seedPosts(3); + + // When: 커서 없이 2 페이지를 직접 지목 + $page = $this->postService->searchAcrossBoardsByCursor( + [$this->board->id], + '커서키워드', + 'latest', + 4, + null, + page: 2 + ); + + // Then: offset 으로 처리한다 + $this->assertNull($page); + } + + /** + * 커서·offset·건수전용 세 응답의 키 집합이 같은지 확인 + * + * 화면이 세 형태를 분기 없이 그릴 수 있어야 한다. 응답 조립을 도메인마다 손으로 하면 + * 이 형태가 조용히 갈라진다. + * + * @effects search_cursor_payload_keeps_offset_key_set + */ + public function test_cursor_and_offset_payloads_share_key_set(): void + { + // Given: 검색 대상 게시글과 세 형태의 페이지 결과 + $this->seedPosts(3); + $boardIds = [$this->board->id]; + + $offsetPage = $this->postService->searchAcrossBoards($boardIds, '커서키워드', 'latest', 4, 1); + $cursorPage = $this->postService->searchAcrossBoardsByCursor($boardIds, '커서키워드', 'latest', 4, 'first'); + $count = new BoundedCount(3, TotalRelation::Exact, 10000); + + $this->assertNotNull($cursorPage); + + // When: 세 형태의 페이로드를 만든다 + $payloads = [ + 'offset' => SearchCategoryPayload::fromBounded($offsetPage, []), + 'cursor' => SearchCategoryPayload::fromCursor($cursorPage, $count, []), + 'count_only' => SearchCategoryPayload::fromCountOnly($count), + ]; + + // Then: 키 집합이 완전히 같다 + $reference = array_keys($payloads['offset']); + sort($reference); + + foreach ($payloads as $label => $payload) { + $keys = array_keys($payload); + sort($keys); + $this->assertSame($reference, $keys, $label.' 응답의 키 집합이 offset 과 달라졌다'); + } + + // 커서 응답은 마지막 페이지를 계산하지 않는다 (그 값만 계산 불가하다) + $this->assertNull($payloads['cursor']['last_page']); + + // offset 응답에도 커서 키가 존재한다 — 값만 null 이라 화면이 분기하지 않는다 + $this->assertNull($payloads['offset']['next_cursor']); + $this->assertNull($payloads['offset']['prev_cursor']); + } +} diff --git a/modules/_bundled/sirsoft-board/tests/ModuleTestCase.php b/modules/_bundled/sirsoft-board/tests/ModuleTestCase.php index ab52c8dd..6c6890a0 100644 --- a/modules/_bundled/sirsoft-board/tests/ModuleTestCase.php +++ b/modules/_bundled/sirsoft-board/tests/ModuleTestCase.php @@ -2,9 +2,21 @@ namespace Modules\Sirsoft\Board\Tests; +use App\Contracts\Extension\HookListenerInterface; +use App\Enums\ExtensionStatus; +use App\Extension\HookListenerRegistrar; +use App\Extension\HookManager; +use App\Extension\ModuleManager; +use App\Http\Middleware\PermissionMiddleware; +use App\Models\Module; +use App\Models\Permission; +use App\Models\Role; +use App\Models\User; use Illuminate\Foundation\Testing\DatabaseTransactions; +use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Schema; use Modules\Sirsoft\Board\Models\Board; +use Modules\Sirsoft\Board\Providers\BoardServiceProvider; use Tests\TestCase; /** @@ -62,7 +74,7 @@ abstract class ModuleTestCase extends TestCase $this->registerModuleAutoload(); // 모듈 ServiceProvider 등록 (Repository 바인딩) - $this->app->register(\Modules\Sirsoft\Board\Providers\BoardServiceProvider::class); + $this->app->register(BoardServiceProvider::class); // 모듈 마이그레이션 실행 (boards 테이블 등) $this->runModuleMigrationIfNeeded(); @@ -73,7 +85,7 @@ abstract class ModuleTestCase extends TestCase // ModuleManager 메모리 맵에 sirsoft-board 로드 (테스트 환경) // CoreServiceProvider::boot()에서 loadModules()가 호출되지만, // 테스트 컨테이너에서 ModuleManager 싱글톤이 비어있는 경우를 대비해 명시적으로 재로드 - $this->app->make(\App\Extension\ModuleManager::class)->loadModules(); + $this->app->make(ModuleManager::class)->loadModules(); // _bundled 디렉토리 모듈은 loadModules() 가 스캔하지 않아 // 모듈 인스턴스 등록 + 훅 리스너 자동 등록이 누락된다. @@ -91,11 +103,7 @@ abstract class ModuleTestCase extends TestCase // PermissionMiddleware::$guestRoleCache 초기화 — 이전 테스트에서 로드된 guest role/permissions 캐시가 // DatabaseTransactions 롤백 후에도 남아있어 다음 테스트의 새 permission 설정이 반영되지 않는 문제 회피. - $middlewareRef = new \ReflectionClass(\App\Http\Middleware\PermissionMiddleware::class); - if ($middlewareRef->hasProperty('guestRoleCache')) { - $prop = $middlewareRef->getProperty('guestRoleCache'); - $prop->setValue(null, null); - } + PermissionMiddleware::clearGuestRoleCache(); } /** @@ -113,7 +121,7 @@ abstract class ModuleTestCase extends TestCase */ private function snapshotHookManager(): void { - $ref = new \ReflectionClass(\App\Extension\HookManager::class); + $ref = new \ReflectionClass(HookManager::class); $this->hookSnapshot = [ 'hooks' => $ref->getProperty('hooks')->getValue(), 'filters' => $ref->getProperty('filters')->getValue(), @@ -130,7 +138,7 @@ abstract class ModuleTestCase extends TestCase return; } - $ref = new \ReflectionClass(\App\Extension\HookManager::class); + $ref = new \ReflectionClass(HookManager::class); $ref->getProperty('hooks')->setValue(null, $this->hookSnapshot['hooks']); $ref->getProperty('filters')->setValue(null, $this->hookSnapshot['filters']); $ref->getProperty('dispatching')->setValue(null, $this->hookSnapshot['dispatching']); @@ -159,7 +167,7 @@ abstract class ModuleTestCase extends TestCase // 모듈 마이그레이션 실행 (코어 테이블 생성 후) $this->artisan('migrate', [ - '--path' => $this->getModuleBasePath() . '/database/migrations', + '--path' => $this->getModuleBasePath().'/database/migrations', '--realpath' => true, ]); @@ -179,13 +187,13 @@ abstract class ModuleTestCase extends TestCase $moduleClass = \Modules\Sirsoft\Board\Module::class; if (! class_exists($moduleClass)) { - require_once $this->getModuleBasePath() . '/module.php'; + require_once $this->getModuleBasePath().'/module.php'; } - $module = new $moduleClass(); + $module = new $moduleClass; - /** @var \App\Extension\ModuleManager $manager */ - $manager = $this->app->make(\App\Extension\ModuleManager::class); + /** @var ModuleManager $manager */ + $manager = $this->app->make(ModuleManager::class); // ModuleManager.modules 에 인스턴스 주입 $reflection = new \ReflectionClass($manager); @@ -203,11 +211,11 @@ abstract class ModuleTestCase extends TestCase if (! class_exists($listenerClass)) { continue; } - if (! in_array(\App\Contracts\Extension\HookListenerInterface::class, class_implements($listenerClass), true)) { + if (! in_array(HookListenerInterface::class, class_implements($listenerClass), true)) { continue; } try { - \App\Extension\HookListenerRegistrar::register($listenerClass, 'sirsoft-board'); + HookListenerRegistrar::register($listenerClass, 'sirsoft-board'); } catch (\Throwable $e) { // 중복 등록 등 무해한 예외는 무시 (snapshot/restore 패턴이 정리) } @@ -221,15 +229,15 @@ abstract class ModuleTestCase extends TestCase protected function registerModuleAsActive(): void { // 이미 등록되어 있으면 스킵 - if (\App\Models\Module::where('identifier', 'sirsoft-board')->exists()) { + if (Module::where('identifier', 'sirsoft-board')->exists()) { return; } - \App\Models\Module::create([ + Module::create([ 'identifier' => 'sirsoft-board', 'vendor' => 'sirsoft', 'name' => ['ko' => '게시판', 'en' => 'Board'], - 'status' => \App\Enums\ExtensionStatus::Active->value, + 'status' => ExtensionStatus::Active->value, 'version' => '1.0.0', 'config' => [], ]); @@ -240,7 +248,7 @@ abstract class ModuleTestCase extends TestCase */ protected function registerModuleAutoload(): void { - $moduleBasePath = $this->getModuleBasePath() . '/src/'; + $moduleBasePath = $this->getModuleBasePath().'/src/'; spl_autoload_register(function ($class) use ($moduleBasePath) { $prefix = 'Modules\\Sirsoft\\Board\\'; @@ -264,10 +272,10 @@ abstract class ModuleTestCase extends TestCase */ protected function registerModuleRoutes(): void { - $apiRoutesFile = $this->getModuleBasePath() . '/src/routes/api.php'; + $apiRoutesFile = $this->getModuleBasePath().'/src/routes/api.php'; if (file_exists($apiRoutesFile)) { - \Illuminate\Support\Facades\Route::prefix('api/modules/sirsoft-board') + Route::prefix('api/modules/sirsoft-board') ->name('api.modules.sirsoft-board.') ->middleware('api') ->group($apiRoutesFile); @@ -279,17 +287,17 @@ abstract class ModuleTestCase extends TestCase */ protected function createDefaultRoles(): void { - \App\Models\Role::firstOrCreate( + Role::firstOrCreate( ['identifier' => 'admin'], ['name' => ['ko' => '관리자', 'en' => 'Administrator']] ); - \App\Models\Role::firstOrCreate( + Role::firstOrCreate( ['identifier' => 'user'], ['name' => ['ko' => '일반 사용자', 'en' => 'User']] ); - \App\Models\Role::firstOrCreate( + Role::firstOrCreate( ['identifier' => 'guest'], ['name' => ['ko' => '비회원', 'en' => 'Guest']] ); @@ -300,18 +308,18 @@ abstract class ModuleTestCase extends TestCase * 관리자 역할을 가진 사용자를 생성합니다. * * @param array $permissions 추가 권한 목록 - * @return \App\Models\User + * @return User */ - protected function createAdminUser(array $permissions = []): \App\Models\User + protected function createAdminUser(array $permissions = []): User { - $adminRole = \App\Models\Role::where('identifier', 'admin')->first(); - $user = \App\Models\User::factory()->create(); + $adminRole = Role::where('identifier', 'admin')->first(); + $user = User::factory()->create(); $user->roles()->attach($adminRole->id); // 추가 권한이 있으면 생성 및 할당 if (! empty($permissions)) { foreach ($permissions as $permissionIdentifier) { - $permission = \App\Models\Permission::firstOrCreate( + $permission = Permission::firstOrCreate( ['identifier' => $permissionIdentifier], [ 'name' => ['ko' => $permissionIdentifier, 'en' => $permissionIdentifier], @@ -328,14 +336,14 @@ abstract class ModuleTestCase extends TestCase /** * 일반 사용자를 생성합니다. * - * @return \App\Models\User + * @return User */ - protected function createUser(): \App\Models\User + protected function createUser(): User { - $userRole = \App\Models\Role::where('identifier', 'user')->first(); - $user = \App\Models\User::factory()->create(); + $userRole = Role::where('identifier', 'user')->first(); + $user = User::factory()->create(); $user->roles()->attach($userRole->id); return $user; } -} \ No newline at end of file +} diff --git a/modules/_bundled/sirsoft-board/tests/Unit/Listeners/SearchPostsListenerTest.php b/modules/_bundled/sirsoft-board/tests/Unit/Listeners/SearchPostsListenerTest.php index d74a9b76..35e3724c 100644 --- a/modules/_bundled/sirsoft-board/tests/Unit/Listeners/SearchPostsListenerTest.php +++ b/modules/_bundled/sirsoft-board/tests/Unit/Listeners/SearchPostsListenerTest.php @@ -2,7 +2,9 @@ namespace Modules\Sirsoft\Board\Tests\Unit\Listeners; +use App\Enums\TotalRelation; use App\Models\User; +use App\Support\Query\BoundedPage; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Gate; use Modules\Sirsoft\Board\Listeners\SearchPostsListener; @@ -15,6 +17,28 @@ use Tests\TestCase; */ class SearchPostsListenerTest extends TestCase { + /** + * 검색 결과 페이지(BoundedPage)를 만듭니다. + * + * 저장소가 반환하는 계약과 같은 형태를 테스트에서도 그대로 씁니다. + * + * @param Collection $items 페이지 항목 + * @param int $total 총 건수 + * @return BoundedPage 페이지 결과 + */ + private function boundedPage(Collection $items, int $total): BoundedPage + { + return new BoundedPage( + items: $items, + total: $total, + perPage: 5, + currentPage: 1, + totalRelation: TotalRelation::Exact, + resultCap: 10000, + hasMorePages: false, + ); + } + private SearchPostsListener $listener; private PostService $postService; @@ -32,7 +56,7 @@ class SearchPostsListenerTest extends TestCase /** * getSubscribedHooks()가 올바른 훅 목록을 반환하는지 확인 */ - public function test_getSubscribedHooks_returns_correct_hooks(): void + public function test_get_subscribed_hooks_returns_correct_hooks(): void { $hooks = SearchPostsListener::getSubscribedHooks(); @@ -48,7 +72,7 @@ class SearchPostsListenerTest extends TestCase /** * 권한 있는 게시판만 검색 결과에 포함되는지 확인 */ - public function test_searchPosts_filters_boards_by_permission(): void + public function test_search_posts_filters_boards_by_permission(): void { $user = User::factory()->make(['id' => 1001]); @@ -78,25 +102,22 @@ class SearchPostsListenerTest extends TestCase $this->postService ->method('searchAcrossBoards') ->with([1], '테스트', $this->anything(), $this->anything(), $this->anything()) - ->willReturn([ - 'total' => 1, - 'items' => new Collection([ - $this->createPostStub(1, 'notice', '공지사항'), - ]), - ]); + ->willReturn($this->boundedPage(new Collection([ + $this->createPostStub(1, 'notice', '공지사항'), + ]), 1)); $this->boardService ->method('getActiveBoardsListForFilter') ->willReturn([]); $context = [ - 'type' => 'all', - 'q' => '테스트', - 'sort' => 'relevance', - 'page' => 1, + 'type' => 'all', + 'q' => '테스트', + 'sort' => 'relevance', + 'page' => 1, 'per_page' => 10, - 'user' => $user, - 'request' => null, + 'user' => $user, + 'request' => null, ]; $result = $this->listener->searchPosts([], $context); @@ -108,7 +129,7 @@ class SearchPostsListenerTest extends TestCase /** * 모든 게시판 권한이 없을 때 빈 결과를 반환하는지 확인 */ - public function test_searchPosts_returns_empty_when_all_boards_denied(): void + public function test_search_posts_returns_empty_when_all_boards_denied(): void { $user = User::factory()->make(); @@ -123,9 +144,9 @@ class SearchPostsListenerTest extends TestCase $results = []; $context = [ - 'type' => 'all', - 'q' => '테스트', - 'user' => $user, + 'type' => 'all', + 'q' => '테스트', + 'user' => $user, 'request' => null, ]; @@ -141,7 +162,7 @@ class SearchPostsListenerTest extends TestCase /** * 빈 검색어일 때 스킵하는지 확인 */ - public function test_searchPosts_skips_when_keyword_is_empty(): void + public function test_search_posts_skips_when_keyword_is_empty(): void { $results = []; $context = ['type' => 'all', 'q' => '']; @@ -154,7 +175,7 @@ class SearchPostsListenerTest extends TestCase /** * formatPostResult()가 created_at(Y-m-d H:i:s 포맷)과 created_at_formatted(표시용) 필드를 반환하는지 확인 */ - public function test_formatPostResult_includes_created_at_and_created_at_formatted(): void + public function test_format_post_result_includes_created_at_and_created_at_formatted(): void { $user = User::factory()->make(['id' => 9999]); @@ -170,23 +191,20 @@ class SearchPostsListenerTest extends TestCase $this->postService ->method('searchAcrossBoards') - ->willReturn([ - 'total' => 1, - 'items' => new Collection([ - $this->createPostStub(1, 'notice', '공지사항'), - ]), - ]); + ->willReturn($this->boundedPage(new Collection([ + $this->createPostStub(1, 'notice', '공지사항'), + ]), 1)); Gate::before(fn ($u) => $u->id === 9999 ? true : null); $context = [ - 'type' => 'all', - 'q' => '테스트', - 'sort' => 'relevance', - 'page' => 1, + 'type' => 'all', + 'q' => '테스트', + 'sort' => 'relevance', + 'page' => 1, 'per_page' => 10, - 'user' => $user, - 'request' => null, + 'user' => $user, + 'request' => null, ]; $result = $this->listener->searchPosts([], $context); @@ -209,9 +227,9 @@ class SearchPostsListenerTest extends TestCase /** * id를 포함하는 Board 스텁 생성 * - * @param int $id 게시판 ID - * @param string $slug 게시판 슬러그 - * @param string $name 게시판 이름 + * @param int $id 게시판 ID + * @param string $slug 게시판 슬러그 + * @param string $name 게시판 이름 * @return object */ private function createBoardStub(int $id, string $slug, string $name): object @@ -241,9 +259,9 @@ class SearchPostsListenerTest extends TestCase /** * board relation이 포함된 Post 스텁 생성 * - * @param int $id 게시글 ID - * @param string $boardSlug 게시판 슬러그 - * @param string $boardName 게시판 이름 + * @param int $id 게시글 ID + * @param string $boardSlug 게시판 슬러그 + * @param string $boardName 게시판 이름 * @return object */ private function createPostStub(int $id, string $boardSlug, string $boardName): object @@ -267,16 +285,16 @@ class SearchPostsListenerTest extends TestCase }; return (object) [ - 'id' => $id, - 'title' => '테스트 게시글', - 'content' => '테스트 내용', - 'content_mode' => 'text', - 'author_name' => '작성자', - 'created_at' => now(), - 'view_count' => 5, + 'id' => $id, + 'title' => '테스트 게시글', + 'content' => '테스트 내용', + 'content_mode' => 'text', + 'author_name' => '작성자', + 'created_at' => now(), + 'view_count' => 5, 'comments_count' => 2, - 'user' => null, - 'board' => $boardStub, + 'user' => null, + 'board' => $boardStub, ]; } } diff --git a/modules/_bundled/sirsoft-board/tests/Unit/Traits/ChecksBoardPermissionTest.php b/modules/_bundled/sirsoft-board/tests/Unit/Traits/ChecksBoardPermissionTest.php index 2f34b9b3..bd599147 100644 --- a/modules/_bundled/sirsoft-board/tests/Unit/Traits/ChecksBoardPermissionTest.php +++ b/modules/_bundled/sirsoft-board/tests/Unit/Traits/ChecksBoardPermissionTest.php @@ -24,7 +24,6 @@ use PHPUnit\Framework\Attributes\Test; */ class ChecksBoardPermissionTest extends ModuleTestCase { - /** * Trait을 사용하는 테스트용 클래스 */ @@ -48,12 +47,12 @@ class ChecksBoardPermissionTest extends ModuleTestCase { use ChecksBoardPermission; - public function testCheckPermissionByIdentifier(string $identifier): bool + public function invokeCheckPermissionByIdentifier(string $identifier): bool { return $this->checkPermissionByIdentifier($identifier); } - public function testCheckBoardPermission(string $slug, string $action, PermissionType $type = PermissionType::User): bool + public function invokeCheckBoardPermission(string $slug, string $action, PermissionType $type = PermissionType::User): bool { return $this->checkBoardPermission($slug, $action, $type); } @@ -65,16 +64,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase */ private function resetPermissionMiddlewareCache(): void { - try { - $reflection = new \ReflectionClass(PermissionMiddleware::class); - if ($reflection->hasProperty('guestRoleCache')) { - $prop = $reflection->getProperty('guestRoleCache'); - $prop->setAccessible(true); - $prop->setValue(null, null); - } - } catch (\ReflectionException $e) { - // 무시 - } + PermissionMiddleware::clearGuestRoleCache(); } /** @@ -111,7 +101,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase request()->setUserResolver(fn () => $user); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: true 반환 $this->assertTrue($result); @@ -140,7 +130,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::login($user); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.write'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.write'); // Then: false 반환 $this->assertFalse($result); @@ -168,7 +158,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: true 반환 $this->assertTrue($result); @@ -195,7 +185,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 다른 권한 체크 (guest role에 없는 권한) - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.delete'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.delete'); // Then: false 반환 $this->assertFalse($result); @@ -211,7 +201,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: false 반환 (전체 허용도 없음) $this->assertFalse($result); @@ -233,7 +223,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: false 반환 (역할 할당 필수) $this->assertFalse($result); @@ -243,7 +233,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase * checkBoardPermission 메서드가 올바른 권한 식별자를 생성하는지 테스트 */ #[Test] - public function checkBoardPermission_메서드_올바른_식별자_생성(): void + public function check_board_permission_메서드_올바른_식별자_생성(): void { // Given: 권한 생성 + guest role에 할당 $permission = Permission::factory()->create([ @@ -259,7 +249,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: checkBoardPermission 호출 - $result = $this->traitObject->testCheckBoardPermission('notice', 'posts.read'); + $result = $this->traitObject->invokeCheckBoardPermission('notice', 'posts.read'); // Then: 올바른 식별자로 권한 체크됨 (guest role 권한 보유) $this->assertTrue($result); @@ -275,7 +265,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 존재하지 않는 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.nonexistent.permission'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.nonexistent.permission'); // Then: false 반환 $this->assertFalse($result); @@ -285,7 +275,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase * 회원 권한 체크 우선순위 테스트 (회원이 로그인하면 Gate 먼저 체크) */ #[Test] - public function 회원_로그인_시_Gate_우선_체크(): void + public function 회원_로그인_시_gate_우선_체크(): void { // Given: guest role에도 권한 부여, 회원에게도 권한 부여 $guestRole = Role::firstOrCreate( @@ -312,7 +302,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase request()->setUserResolver(fn () => $user); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: true 반환 (회원 권한으로 체크됨) $this->assertTrue($result); @@ -339,7 +329,7 @@ class ChecksBoardPermissionTest extends ModuleTestCase Auth::logout(); // When: 권한 체크 - $result = $this->traitObject->testCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); + $result = $this->traitObject->invokeCheckPermissionByIdentifier('sirsoft-board.notice.posts.read'); // Then: false 반환 (guest role에 권한 미할당) $this->assertFalse($result); From 4c62dbe64980a307997f0371142a51bce311f44f Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:25:00 +0900 Subject: [PATCH 3/8] =?UTF-8?q?perf(ecommerce):=20=EC=A7=84=EC=97=B4=C2=B7?= =?UTF-8?q?=EC=A7=91=EA=B3=84=C2=B7=EA=B2=80=EC=83=89=EC=9D=98=20=EB=B0=98?= =?UTF-8?q?=EB=B3=B5=20=EC=8B=A4=ED=96=89=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=81=ED=95=9C=20=EA=B3=84=EC=95=BD=20=EC=86=8C=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 집계 — 판매량 정렬과 인기 상품이 ORDER BY 안의 상관 서브쿼리라 LIMIT 이 평가 범위를 막지 못했다. 전 상품을 평가한 뒤 정렬하던 것을 파생 테이블 조인으로 바꿨다. 리뷰 개수·평점도 상품마다 리뷰 표를 두 번씩 훑던 것을 한 번의 그룹 집계로 합쳤다. 대표 이미지는 이미 적재된 관계를 쓰지 않고 매번 관계 빌더로 조회해 상품당 2회씩 쿼리를 더 내고 있었다. 루프 안 쿼리 — 체크아웃의 항목별 옵션 조회, 게스트 장바구니 병합의 항목별 2쿼리, 주문 옵션의 항목별 find 를 일괄 조회로 옮겼다. 상품 대량 가격·재고 수정은 행마다 저장을 반복했는데, 두 번째 인자를 넘기던 오용 때문에 실제로는 바인딩이 어긋나 실행 자체가 실패하고 있었다. 이 두 메서드를 호출하는 테스트가 0건이라 전 스위트 green 상태로 통과하고 있었다. 카테고리 트리는 노드마다 집계 쿼리를 재귀 발행하던 것을 평면 1쿼리 + 조립으로 바꾸고 캐시했다. 거의 모든 쇼핑 화면이 읽는 값이라 분류·상품 변경 시 무효화한다. 검색 — 조건에 맞는 ID 전량을 PHP 로 적재한 뒤 무제한 whereIn 으로 다시 조회하던 경로를 없애고 키워드 술어를 페이지 쿼리에 직접 붙였다. 구체 엔진 직접 호출도 해석기 경유로 옮겨 플러그인 검색엔진이 상품·쿠폰·브랜드 검색에 적용된다. 쇼핑 첫 화면과 분류 화면은 각각 5요청·3요청을 한 번으로 합쳤다. 분류 화면이 함께 받던 분류 트리는 그 화면의 어떤 표현식도 읽지 않는 순수 낭비였다. 없는 분류 주소는 빈 화면 대신 404 로 처리해 검색엔진이 정상 페이지로 수집하지 않게 했다. 색인 — 전시 조건으로 좁힌 뒤 등록일·판매가로 정렬하는 목록과 주문 옵션 판매량 집계에 필요한 색인을 더했다. 신규 설치는 마이그레이션이, 기존 사이트는 업그레이드 스텝이 적용한다. 관리자 화면 — 총 건수가 잘린 목록에서 페이저가 접히던 자리와, 쿠폰 사용조건의 상품 검색이 추가 로드되지 않던 조건을 고쳤다. 같은 파일의 사전 결함(반복 렌더 안의 정적 id, Icon 박스 크기, 다크 모드 대비, 반복 변수 선언 오탈자)도 함께 처리했다. --- .../_bundled/sirsoft-ecommerce/CHANGELOG.md | 25 +- ...storefront_indexes_to_ecommerce_tables.php | 80 +++ .../sirsoft-ecommerce/docs/api/README.md | 1 + .../sirsoft-ecommerce/docs/api/storefront.md | 138 ++++ modules/_bundled/sirsoft-ecommerce/module.php | 2 + .../admin_ecommerce_product_review_index.json | 5 +- .../_transactions_table.json | 3 +- .../_partial_activity_log.json | 5 +- .../_partial_order_datagrid.json | 3 +- .../_partial_activity_log.json | 11 +- .../_partial_product_datagrid.json | 3 +- .../_partial_usage_conditions.json | 8 +- .../_modal_issue_history.json | 3 +- .../_partial_coupon_datagrid.json | 3 +- .../_tab_identity_policies.json | 8 +- .../_tab_notification_definitions.json | 3 +- .../_partial_datagrid.json | 3 +- .../Public/StorefrontController.php | 181 +++++ .../Public/PublicStorefrontRequest.php | 85 +++ .../src/Http/Resources/ProductCollection.php | 56 +- .../Http/Resources/ProductListResource.php | 23 +- .../Listeners/CategoryTreeCacheListener.php | 78 +++ .../src/Listeners/SearchProductsListener.php | 113 +++- .../sirsoft-ecommerce/src/Models/Category.php | 211 +++++- .../src/Repositories/BrandRepository.php | 4 +- .../src/Repositories/CartRepository.php | 46 ++ .../src/Repositories/CategoryRepository.php | 38 +- .../Contracts/CartRepositoryInterface.php | 23 + .../Contracts/CategoryRepositoryInterface.php | 17 +- .../Contracts/OrderRepositoryInterface.php | 19 + ...ditionalOptionValueRepositoryInterface.php | 11 + .../ProductOptionRepositoryInterface.php | 9 + .../Contracts/ProductRepositoryInterface.php | 27 +- .../Repositories/CouponIssueRepository.php | 2 + .../src/Repositories/CouponRepository.php | 48 +- .../MileageTransactionRepository.php | 9 + .../Repositories/OrderOptionRepository.php | 6 +- .../src/Repositories/OrderRepository.php | 137 +++- ...ProductAdditionalOptionValueRepository.php | 21 +- .../Repositories/ProductInquiryRepository.php | 5 + .../Repositories/ProductOptionRepository.php | 25 + .../src/Repositories/ProductRepository.php | 636 +++++++++++++----- .../Repositories/ProductReviewRepository.php | 3 + .../AdditionalOptionSelectionService.php | 86 ++- .../src/Services/CartService.php | 46 +- .../src/Services/CategoryService.php | 25 +- .../src/Services/ProductService.php | 57 +- .../src/Services/TempOrderService.php | 49 +- .../sirsoft-ecommerce/src/routes/api.php | 8 + .../Feature/EcommerceUserAbilitiesTest.php | 6 +- .../Feature/EcommerceUserPermissionTest.php | 6 +- .../Public/PublicProductControllerTest.php | 5 +- .../Feature/ListCacheInvalidationTest.php | 189 ++++++ .../ListQueryCountRegressionTest.php | 205 ++++++ .../StorefrontIndexCoverageTest.php | 269 ++++++++ .../StorefrontCategoryContractTest.php | 107 +++ .../Listeners/SearchProductsListenerTest.php | 136 +++- .../ProductBulkAdjustmentTest.php | 220 ++++++ .../tests/Unit/Services/CartServiceTest.php | 82 +-- .../Unit/Services/CategoryServiceTest.php | 14 +- .../upgrades/Upgrade_1_0_5.php | 4 + .../migrations/06_AddStorefrontIndexes.php | 77 +++ 62 files changed, 3283 insertions(+), 445 deletions(-) create mode 100644 modules/_bundled/sirsoft-ecommerce/database/migrations/2026_08_02_000001_add_storefront_indexes_to_ecommerce_tables.php create mode 100644 modules/_bundled/sirsoft-ecommerce/docs/api/storefront.md create mode 100644 modules/_bundled/sirsoft-ecommerce/src/Http/Controllers/Public/StorefrontController.php create mode 100644 modules/_bundled/sirsoft-ecommerce/src/Http/Requests/Public/PublicStorefrontRequest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/src/Listeners/CategoryTreeCacheListener.php create mode 100644 modules/_bundled/sirsoft-ecommerce/tests/Feature/ListCacheInvalidationTest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/ListQueryCountRegressionTest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/StorefrontIndexCoverageTest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/tests/Feature/StorefrontCategoryContractTest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/ProductBulkAdjustmentTest.php create mode 100644 modules/_bundled/sirsoft-ecommerce/upgrades/data/1.0.5/migrations/06_AddStorefrontIndexes.php diff --git a/modules/_bundled/sirsoft-ecommerce/CHANGELOG.md b/modules/_bundled/sirsoft-ecommerce/CHANGELOG.md index 256c59b5..76bf76db 100644 --- a/modules/_bundled/sirsoft-ecommerce/CHANGELOG.md +++ b/modules/_bundled/sirsoft-ecommerce/CHANGELOG.md @@ -32,6 +32,25 @@ - 관리자 주문 목록이 주문마다 배송 정보 전체를 함께 내려주지 않습니다. 목록은 대표 배송 1건(배송유형·배송방법·택배사·송장번호)만 표시하므로 그 1건만 읽으며, 택배사 이름도 함께 읽어 행마다 다시 조회하지 않습니다. 분할 배송이 많은 쇼핑몰일수록 목록이 가벼워집니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) - 배송정책 목록이 정책마다 국가별 배송 설정을 통째로 내려주지 않습니다. 목록에는 화면이 실제로 표시하는 값(국가·배송방법·부과정책·배송비·무료배송 기준액·도서산간 사용 여부·사용 여부)만 실리고, 구간별 배송비 표·도서산간 지역 설정·배송비 계산 API 설정은 빠집니다. 정책 하나에 국가 설정이 수백 건까지 달릴 수 있어 목록을 여는 것만으로 그만큼의 상세 설정이 전송됐습니다. 배송정책 관리 목록과 상품 등록/수정 화면의 표시 내용은 종전과 동일하며, 전체 설정이 필요한 외부 연동은 주소 뒤에 `?with_country_settings=1` 을 붙이면 됩니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) - 마이페이지 주문내역 조회가 기본으로 주문마다 주문상품 전체를 내려주지 않습니다. 기본 응답에는 대표 상품 1건과 전체 개수만 실리며, 주문상품을 전부 나열하는 마이페이지 화면은 전량을 함께 요청하므로 보이는 내용은 종전과 동일합니다. 주문내역 조회를 직접 사용하는 외부 연동에서 상품 전체가 필요하면 주소 뒤에 `?with_items=1` 을 붙이면 됩니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) +- 통합 검색의 상품 결과를 최신순·가격순으로 볼 때 이어보기 방식으로 뒤쪽 페이지를 이동할 수 있습니다. 결과가 많아도 페이지를 넘길수록 느려지지 않습니다. +- 분류 단건 조회가 화면에서 쓰지 않는 상품 수·하위 분류 수 집계를 기본으로 계산하지 않도록 바꿨습니다. 분류 순서 변경처럼 그 값을 읽지 않는 작업이 빨라집니다. +- 쇼핑 첫 화면이 분류·상품목록·최근 본·인기·신상품을 각각 따로 요청하던 것을 한 번에 받도록 바꿨습니다. 화면에 보이는 내용은 동일하며 첫 화면이 뜨는 속도가 빨라집니다. +- 상품 분류 화면도 같은 방식으로 바꿨습니다. 예전에는 분류 정보·분류 목록·상품 목록을 각각 요청했고 그중 분류 목록은 화면에서 쓰지 않았습니다. 이제 한 번의 요청으로 필요한 것만 받습니다. 없는 분류 주소로 들어오면 빈 화면 대신 없는 페이지로 처리됩니다. +- 상품 목록의 판매량 정렬과 인기 상품 조회 방식을 개선했습니다. 예전에는 순서를 정하기 위해 전체 상품의 판매량을 매번 계산했지만, 이제 미리 묶어 둔 집계를 사용합니다. 상품이 많을수록 차이가 큽니다. +- 상품 목록의 리뷰 개수·평점도 같은 방식으로 한 번에 집계합니다. 예전에는 상품 한 건마다 리뷰 표를 두 번씩 훑었습니다. +- 주문·마일리지·쿠폰 발급내역·리뷰·1:1 문의 목록도 총 건수가 아주 많으면 "N건 이상" 으로 표시합니다. 페이지 이동은 끝까지 열려 있습니다. +- 상품·쿠폰 검색이 조건에 맞는 항목 전체를 먼저 메모리에 올린 뒤 다시 조회하던 방식을 없앴습니다. 검색 결과가 많아도 메모리 사용량이 늘지 않습니다. (#82 @jiwonpapa 님께서 제보해주셨습니다.) +- 플러그인으로 다른 검색엔진을 연결하면 상품·쿠폰·브랜드 검색에도 그 엔진이 적용됩니다. 이전에는 연결한 엔진과 무관하게 기본 방식으로만 검색됐습니다. +- 상품 검색 결과의 총 건수가 아주 많으면 "N건 이상" 으로 표시하고, 다음 페이지 이동은 끝까지 열어 둡니다. 마지막 페이지로 바로 뛰는 버튼만 이때 감춰집니다. +- 기간으로 걸러 보는 목록(주문·쿠폰)의 날짜 조건을 색인을 활용하는 방식으로 바꿨습니다. +- 주문 관리 화면 상단의 통계를 짧게 저장해 두고 재사용합니다. 주문이 바뀌면 즉시 새로 계산됩니다. +- 상품 대량 수정(가격·재고)이 상품 수만큼 저장을 반복하던 것을 한 번에 처리하도록 바꿨습니다. +- 결제 진행과 비회원 장바구니 병합에서 항목마다 반복하던 조회를 한 번에 처리하도록 바꿨습니다. 장바구니에 담긴 상품이 많을수록 차이가 큽니다. +- 카테고리 트리를 만들 때 하위 분류마다 조회를 반복하던 것을 한 번에 읽어 조립하도록 바꿨습니다. 거의 모든 쇼핑 화면이 읽는 값이라 결과를 짧게 저장해 두고 재사용하며, 분류나 상품이 바뀌면 즉시 새로 계산됩니다. +- 상품 관리 화면 상단의 통계를 짧게 저장해 두고 재사용합니다. 상품·재고가 바뀌면 즉시 새로 계산됩니다. +- 상품 검색 탭의 배지 숫자도 세지 못한 건수를 정확한 것처럼 표시하지 않습니다. +- 상품 목록의 분류 경로 표시가 같은 조상 분류를 반복 조회하던 것을 한 번으로 줄였습니다. +- 상품 목록·주문 옵션 집계에 필요한 색인을 추가했습니다. 기존 사이트도 업데이트 시 자동으로 반영되며, 데이터가 아주 많은 경우 이 과정에 수 분이 걸리고 그동안 해당 데이터의 저장이 잠시 대기할 수 있습니다. - 실제로 사용되지 않던 설정 항목(상품·주문 목록의 페이지당 개수, 결제 자동취소 기한 중복 항목)을 정리했습니다. 미입금 자동취소 기한은 관리자 환경설정의 값 하나만 사용합니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) - 사이트맵을 만들 때 상품과 카테고리를 한 번에 모두 메모리에 올리지 않고 나눠서 읽도록 바꿨습니다. 상품이 많은 쇼핑몰에서 사이트맵 생성 중 메모리가 부족해 실패하던 문제가 줄어듭니다. - 상품 리뷰·마일리지 내역·쿠폰 발급 내역 목록도 같은 방식으로 뒤쪽 페이지 조회를 개선했습니다. 상품 리뷰는 작성일 정렬용 인덱스를 추가하고 기간 검색 조건을 인덱스가 활용되는 형태로 바꿔, 리뷰가 많은 쇼핑몰에서 목록과 기간 검색이 함께 빨라집니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) @@ -57,6 +76,9 @@ - 리뷰 작성 기한 판정이 화면 표시와 실제 저장 가능 여부에서 서로 다른 기준을 쓸 수 있던 문제를 수정했습니다. 이제 두 곳이 같은 설정값을 사용하므로 "작성 가능"으로 보이는데 저장이 거부되는 일이 없습니다. - 관리자 환경설정에서 미입금 자동취소 기한을 한 번이라도 저장하면, 이후 모든 무통장입금·가상계좌 주문이 결제 처리 오류로 실패하던 문제를 수정했습니다. 저장된 기한 값의 형태 때문에 입금기한을 계산하지 못한 것으로, 이제 저장 형태와 무관하게 주문이 정상 생성됩니다. 기한이 비어 있거나 허용 범위(1~30일)를 벗어난 값이면 기본값 3일로 처리합니다. (#85 @hwaryeon1234 님께서 제보해주셨습니다.) - 설정 화면에서 숫자 항목(기한·수량·금액 등)을 저장할 때 값이 숫자가 아닌 형태로 보관되던 문제를 수정했습니다. 화면 표시는 같았지만 저장된 형태가 달라, 그 값을 사용하는 기능에서 오류가 날 수 있었습니다. +- 관리자 상품 목록의 일괄 가격 변경(정액·정률 증액/감액)과 일괄 재고 증감이 실행되지 않던 문제를 수정했습니다. 지정한 금액이나 수량이 반영되지 않고 오류가 발생했습니다. 값을 그대로 지정하는 방식(가격/재고를 특정 값으로 설정)은 영향이 없었습니다. +- 상품 리뷰·상품 문의 목록에서 총 건수를 끝까지 세지 못했을 때 "1페이지뿐" 으로 표시되어 뒤쪽 페이지를 볼 수 없던 문제를 수정했습니다. +- 마이페이지의 주문 내역·마일리지 내역·위시리스트에서 총 건수를 끝까지 세지 못했을 때 페이지 이동 막대가 통째로 사라지던 문제를 수정했습니다. - 설정 화면의 선택 항목(라디오 버튼)을 키보드 방향키로 고를 때 선택이 저장되지 않던 문제를 수정했습니다. 마우스 클릭은 정상 동작했으나, 키보드만 사용하는 경우 화면 표시와 실제 저장 값이 어긋날 수 있었습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) - 로그인하지 않은 상태로 장바구니에 담아 둔 상품이 로그인 후 사라지던 문제를 수정했습니다. 로그인 시 장바구니를 회원 계정으로 옮기는 처리가 실행되지 않아, 담아 둔 상품이 아무 안내 없이 없어졌습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) - 설정 화면이 허용하는 값과 저장 규칙이 서로 달라 저장이 거부되거나 그 반대가 되던 항목들을 맞췄습니다. 리뷰 이미지 최대 개수(최대 20개), 리뷰 이미지 최대 크기(정수 MB, 최대 50MB), 마일리지 기본 적립률(최대 100%), 미입금 자동취소 기한(최소 1일 — 0일은 주문 즉시 만료라 사용할 수 없는 값)이 대상입니다. 정수만 받는 항목은 입력칸의 증감 단위도 1로 맞춰, 화살표로 값을 올리다 소수가 되어 저장이 거부되는 일이 없습니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) @@ -110,7 +132,8 @@ - 쿠폰의 다른 항목만 고치는 경우에도 "1인당 사용 제한"을 함께 보내지 않으면 저장이 거절되던 문제를 수정했습니다. (#78 @glitter-gim 님께서 제보해주셨습니다.) - 배송지를 수정할 때 주소를 통째로 비운 채 저장할 수 있던 문제를 수정했습니다. 국내 배송지는 우편번호와 주소가, 해외 배송지는 주소·도시·우편번호가 갖춰져야 저장됩니다. 기본 배송지 지정처럼 주소를 건드리지 않는 수정은 이전과 동일하게 저장됩니다. (#78 @glitter-gim 님께서 제보해주셨습니다.) - 해외 배송지를 수정하면 도시·주·우편번호가 안내 없이 비워지던 문제를 수정했습니다. 이번 수정에서 다시 입력하지 않은 항목은 저장된 값이 그대로 유지됩니다. (#78 @glitter-gim 님께서 제보해주셨습니다.) - +- 관리자 목록에서 기록이 아주 많아 총 건수를 끝까지 세지 못하면 "1페이지뿐" 으로 표시되어 뒤쪽 페이지를 볼 수 없던 문제를 수정했습니다. 주문·상품·마일리지·쿠폰·쿠폰 발급 이력·상품 리뷰·배송 정책·활동 이력 목록이 해당하며, 마지막 페이지로 바로 뛰는 버튼만 감춰지고 이전·다음 이동은 그대로 동작합니다. +- 쿠폰 사용조건의 상품 검색에서 결과가 많을 때 목록 끝까지 스크롤해도 다음 묶음이 더 불러와지지 않던 문제를 수정했습니다. ## [1.0.4] - 2026-07-16 ### Fixed diff --git a/modules/_bundled/sirsoft-ecommerce/database/migrations/2026_08_02_000001_add_storefront_indexes_to_ecommerce_tables.php b/modules/_bundled/sirsoft-ecommerce/database/migrations/2026_08_02_000001_add_storefront_indexes_to_ecommerce_tables.php new file mode 100644 index 00000000..9a5a8267 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/database/migrations/2026_08_02_000001_add_storefront_indexes_to_ecommerce_tables.php @@ -0,0 +1,80 @@ + [테이블, 컬럼]] + * + * @var array}> + */ + private const TARGETS = [ + 'idx_products_display_created_id' => ['ecommerce_products', ['display_status', 'created_at', 'id']], + 'idx_products_display_price_id' => ['ecommerce_products', ['display_status', 'selling_price', 'id']], + 'idx_order_options_product_created_qty' => ['ecommerce_order_options', ['product_id', 'created_at', 'quantity']], + ]; + + /** + * Run the migrations. + */ + public function up(): void + { + foreach (self::TARGETS as $indexName => [$table, $columns]) { + if (! Schema::hasTable($table)) { + continue; + } + + $existing = array_column(Schema::getIndexes($table), 'name'); + + if (in_array($indexName, $existing, true)) { + continue; + } + + Schema::table($table, function (Blueprint $blueprint) use ($columns, $indexName) { + $blueprint->index($columns, $indexName); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + foreach (self::TARGETS as $indexName => [$table, $columns]) { + if (! Schema::hasTable($table)) { + continue; + } + + $existing = array_column(Schema::getIndexes($table), 'name'); + + if (! in_array($indexName, $existing, true)) { + continue; + } + + Schema::table($table, function (Blueprint $blueprint) use ($indexName) { + $blueprint->dropIndex($indexName); + }); + } + } +}; diff --git a/modules/_bundled/sirsoft-ecommerce/docs/api/README.md b/modules/_bundled/sirsoft-ecommerce/docs/api/README.md index ea170343..73bc5334 100644 --- a/modules/_bundled/sirsoft-ecommerce/docs/api/README.md +++ b/modules/_bundled/sirsoft-ecommerce/docs/api/README.md @@ -39,6 +39,7 @@ | [shipping-carriers.md](shipping-carriers.md) | `shipping-carriers` | 7 | | [shipping-country.md](shipping-country.md) | `shipping-country` | 2 | | [shipping-policies.md](shipping-policies.md) | `shipping-policies` | 11 | +| [storefront.md](storefront.md) | `storefront` | 1 | | [users.md](users.md) | `users` | 2 | | [wishlist.md](wishlist.md) | `wishlist` | 3 | diff --git a/modules/_bundled/sirsoft-ecommerce/docs/api/storefront.md b/modules/_bundled/sirsoft-ecommerce/docs/api/storefront.md new file mode 100644 index 00000000..d71f64ee --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/docs/api/storefront.md @@ -0,0 +1,138 @@ +# Storefront API 레퍼런스 + +> **소유**: module `sirsoft-ecommerce` · 쇼핑 첫 화면이 필요한 데이터를 한 번에 돌려주는 통합 엔드포인트입니다. + +--- + +## TL;DR (5초 요약) + +```text +1. GET /api/modules/sirsoft-ecommerce/storefront — 분류·상품목록·최근본·인기·신상품 한 번에 (`category_slug` 지정 시 그 분류의 상세까지) +2. 각 묶음의 응답 형태는 개별 엔드포인트와 완전히 동일 (조립 위치만 서버로 이동) +3. 개별 엔드포인트도 그대로 남아 다른 화면에서 계속 사용 +4. products 묶음은 총 건수 정확도 메타(total_relation / total_is_exact / result_cap) 포함 +5. 진열 묶음 개수를 0 으로 주면 그 묶음은 조회하지 않음 +``` + +--- + +## 왜 통합 엔드포인트인가 + +쇼핑 첫 화면은 분류·상품 목록·최근 본·인기·신상품 다섯 묶음을 한 화면에 함께 그립니다. +이를 각각의 엔드포인트로 부르면 화면 하나에 요청이 다섯 번 나가고, 그 다섯 번이 전부 같은 +부팅(라우팅·미들웨어·권한 판정·설정 적재)을 되풀이합니다. + +각 묶음의 응답 형태(리소스 계약)는 개별 엔드포인트와 같습니다 — 옮긴 것은 조립 위치뿐이므로, +개별 엔드포인트를 쓰는 다른 화면은 영향을 받지 않습니다. + +분류 화면도 같은 이유로 이 엔드포인트를 씁니다. 종전에는 분류 상세·분류 트리·상품 목록으로 +요청이 세 번 나갔고, 그중 분류 트리는 그 화면의 어떤 표현식도 읽지 않는 낭비였습니다. +`category_slug` 를 주면 `category` 묶음에 그 분류의 상세가 함께 담겨 요청 한 번으로 끝납니다. + +--- + +### GET /api/modules/sirsoft-ecommerce/storefront + +- **라우트명**: `api.modules.sirsoft-ecommerce.storefront.index` +- **컨트롤러**: `Modules\Sirsoft\Ecommerce\Http\Controllers\Public\StorefrontController@index` +- **인증/권한**: `optional.sanctum` (선택적 인증: 회원/비회원 모두 접근) + `permission:sirsoft-ecommerce.user-products.read` + +**요청 파라미터** + +| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 | +| --- | --- | --- | --- | --- | --- | +| category_id | query | integer | 아니오 | — | 상품 목록 묶음의 카테고리 필터 (category 식별자) | +| category_slug | query | string | 아니오 | max 100 | 상품 목록 묶음의 카테고리 필터 (URL 친화 식별자). **지정하면 `category` 묶음에 그 분류의 상세가 함께 담깁니다** — 없는 분류면 404 | +| brand_id | query | integer | 아니오 | — | 상품 목록 묶음의 브랜드 필터 | +| search | query | string | 아니오 | max 200 | 상품 목록 묶음의 검색어 | +| sort | query | string | 아니오 | `latest`, `sales`, `price_asc`, `price_desc` | 상품 목록 묶음의 정렬 기준 | +| min_price | query | integer | 아니오 | min 0 | 판매가 하한 | +| max_price | query | integer | 아니오 | min 0 | 판매가 상한 | +| per_page | query | integer | 아니오 | min 1, max 100 | 상품 목록 묶음의 페이지당 항목 수 | +| page | query | integer | 아니오 | min 1 | 상품 목록 묶음의 페이지 번호 | +| popular_limit | query | integer | 아니오 | min 0, max 50 | 인기 상품 개수 (0 이면 조회하지 않음, 미지정 시 8) | +| new_limit | query | integer | 아니오 | min 0, max 50 | 신상품 개수 (0 이면 조회하지 않음, 미지정 시 8) | +| recent_ids | query | string | 아니오 | max 500 | 최근 본 상품 ID 목록 (쉼표 구분, 비면 조회하지 않음) | + +> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product.public_storefront_validation_rules`, `sirsoft-ecommerce.product.public_storefront_validation_messages`). + +**요청 예시** + +```http +GET /api/modules/sirsoft-ecommerce/storefront?page=1&per_page=12&sort=latest&popular_limit=8&new_limit=8&recent_ids=12%2C34 HTTP/1.1 +Host: api.example.com +Accept: application/json +Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능) +``` + +**응답 필드** (`data` 내부) + +| 필드 | 타입 | 용도/설명 | +| --- | --- | --- | +| category | object\|null | 지금 보고 있는 분류의 상세. `GET /categories/{slug}` 의 `data` 와 동일한 형태. `category_slug` 를 주지 않으면 `null` | +| categories | array | 공개 카테고리 트리. `GET /categories` 의 `data` 와 동일한 형태 | +| products | object | 상품 목록. `GET /products` 의 `data` 와 동일한 형태 (`data[]` + `pagination`) | +| recent_products | array | 최근 본 상품. `GET /products/recent` 의 `data` 와 동일한 형태 (`recent_ids` 가 비면 `[]`) | +| popular_products | array | 인기 상품. `GET /products/popular` 의 `data` 와 동일한 형태 (`popular_limit=0` 이면 `[]`) | +| new_products | array | 신상품. `GET /products/new` 의 `data` 와 동일한 형태 (`new_limit=0` 이면 `[]`) | + +`products.pagination` 은 표준 페이지네이션 메타에 총 건수 정확도가 더해진 형태입니다. + +| 필드 | 타입 | 용도/설명 | +| --- | --- | --- | +| current_page | integer | 현재 페이지 번호 | +| per_page | integer | 페이지당 항목 수 | +| from / to | integer\|null | 현재 페이지의 첫/마지막 행 번호 | +| has_more_pages | boolean | 다음 페이지 존재 여부 (총 건수를 몰라도 정확) | +| total | integer | 총 건수 (상한 초과 시 상한값 = 하한 보증) | +| last_page | integer\|null | 마지막 페이지 번호. **총 건수가 부정확하면 `null`** | +| total_relation | string | `exact` 또는 `at_least` | +| total_is_exact | boolean | 총 건수가 정확한지 여부 | +| result_cap | integer\|null | 총 건수 집계에 적용된 상한 (무제한이면 `null`) | + +> 총 건수 상한과 페이지 이동의 관계는 [pagination.md](../../../../../docs/backend/pagination.md) 참조. + +**응답 예시** + +```http +HTTP/1.1 200 +``` + +```json +{ + "success": true, + "message": "상품 목록을 조회했습니다.", + "data": { + "category": null, + "categories": [], + "products": { + "data": [], + "pagination": { + "current_page": 1, + "per_page": 12, + "from": null, + "to": null, + "has_more_pages": false, + "total": 0, + "last_page": 1, + "total_relation": "exact", + "total_is_exact": true, + "result_cap": 10000 + } + }, + "recent_products": [], + "popular_products": [], + "new_products": [] + } +} +``` + +**오류 응답** + +| 상태 | 상황 | +| --- | --- | +| 404 | `category_slug` 가 가리키는 분류가 없거나 비활성 (200 + 빈 목록으로 내보내면 검색엔진이 없는 주소를 정상 페이지로 수집합니다) | +| 401 | 비회원에게 `sirsoft-ecommerce.user-products.read` 권한이 없음 | +| 403 | 인증 사용자에게 해당 권한이 없음 | +| 422 | 파라미터 검증 실패 (범위를 벗어난 `popular_limit` 등) | +| 500 | 조회 중 예외 — 응답 형태는 개별 상품 엔드포인트와 동일 | diff --git a/modules/_bundled/sirsoft-ecommerce/module.php b/modules/_bundled/sirsoft-ecommerce/module.php index 37a4f155..97604235 100644 --- a/modules/_bundled/sirsoft-ecommerce/module.php +++ b/modules/_bundled/sirsoft-ecommerce/module.php @@ -18,6 +18,7 @@ use Modules\Sirsoft\Ecommerce\Listeners\ActivityLogDescriptionResolver; use Modules\Sirsoft\Ecommerce\Listeners\AssignDefaultCurrencyOnRegisterListener; use Modules\Sirsoft\Ecommerce\Listeners\AssignDefaultShippingCountryOnRegisterListener; use Modules\Sirsoft\Ecommerce\Listeners\CategoryActivityLogListener; +use Modules\Sirsoft\Ecommerce\Listeners\CategoryTreeCacheListener; use Modules\Sirsoft\Ecommerce\Listeners\CouponActivityLogListener; use Modules\Sirsoft\Ecommerce\Listeners\CouponRestoreListener; use Modules\Sirsoft\Ecommerce\Listeners\CouponUseListener; @@ -2087,6 +2088,7 @@ class Module extends AbstractModule CouponUseListener::class, SeoProductCacheListener::class, SeoCategoryCacheListener::class, + CategoryTreeCacheListener::class, SeoSettingsCacheListener::class, MileageTransactionListener::class, UserMileageInfoListener::class, diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/admin_ecommerce_product_review_index.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/admin_ecommerce_product_review_index.json index 3c326c9b..fd68d536 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/admin_ecommerce_product_review_index.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/admin_ecommerce_product_review_index.json @@ -2902,10 +2902,11 @@ { "type": "composite", "name": "Pagination", - "if": "{{(reviews.data?.meta?.last_page ?? 1) > 1}}", + "if": "{{((reviews.data?.meta?.last_page ?? 0) > 1) || (reviews.data?.meta?.has_more_pages === true) || ((reviews.data?.meta?.current_page ?? 1) > 1)}}", "props": { "currentPage": "{{reviews.data?.meta?.current_page ?? 1}}", - "totalPages": "{{reviews.data?.meta?.last_page ?? 1}}", + "totalPages": "{{reviews.data?.meta?.last_page ?? null}}", + "hasMorePages": "{{reviews.data?.meta?.has_more_pages ?? false}}", "className": "flex justify-center mt-4" }, "actions": [ diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_mileage_transaction_index/_transactions_table.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_mileage_transaction_index/_transactions_table.json index 0f85fa98..c1d797fd 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_mileage_transaction_index/_transactions_table.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_mileage_transaction_index/_transactions_table.json @@ -31,7 +31,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{transactions?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{transactions?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{transactions?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{transactions?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{query.per_page || 20}}", "alwaysShowPagination": true, "columns": [ diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_partial_activity_log.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_partial_activity_log.json index e3b65495..1f3bc051 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_partial_activity_log.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_partial_activity_log.json @@ -216,7 +216,7 @@ "text": "$t:sirsoft-ecommerce.admin.order.detail.activity_log.empty" }, { - "id": "log_rows", + "id": "log_rows_{{logIdx}}", "type": "basic", "name": "Div", "iteration": { @@ -889,7 +889,8 @@ "name": "Pagination", "props": { "currentPage": "{{order_logs.data?.meta?.current_page ?? 1}}", - "totalPages": "{{order_logs.data?.meta?.last_page ?? 1}}", + "totalPages": "{{order_logs.data?.meta?.last_page ?? null}}", + "hasMorePages": "{{order_logs.data?.meta?.has_more_pages ?? false}}", "showFirstLast": true }, "actions": [ diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_list/_partial_order_datagrid.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_list/_partial_order_datagrid.json index cbd6948e..b6b07b15 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_list/_partial_order_datagrid.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_list/_partial_order_datagrid.json @@ -740,7 +740,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{orders?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{orders?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{orders?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{orders?.data?.pagination?.has_more_pages ?? false}}", "pageSize": 20, "alwaysShowPagination": true, "rowActions": [ diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_form/_partial_activity_log.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_form/_partial_activity_log.json index cd534b55..a16a7d8c 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_form/_partial_activity_log.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_form/_partial_activity_log.json @@ -330,7 +330,7 @@ }, "children": [ { - "id": "activity_log_row", + "id": "activity_log_row_{{logIdx}}", "type": "basic", "name": "Tr", "iteration": { @@ -671,7 +671,7 @@ "name": "Icon", "props": { "name": "clipboard-list", - "className": "w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-600" + "className": "text-3xl mx-auto mb-2 text-gray-300 dark:text-gray-600" } }, { @@ -703,7 +703,7 @@ }, "children": [ { - "id": "activity_log_card_item", + "id": "activity_log_card_item_{{logIdx}}", "type": "basic", "name": "Div", "iteration": { @@ -1039,7 +1039,7 @@ "name": "Icon", "props": { "name": "clipboard-list", - "className": "w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-600" + "className": "text-3xl mx-auto mb-2 text-gray-300 dark:text-gray-600" } }, { @@ -1064,7 +1064,8 @@ "name": "Pagination", "props": { "currentPage": "{{activity_logs.data?.meta?.current_page ?? 1}}", - "totalPages": "{{activity_logs.data?.meta?.last_page ?? 1}}", + "totalPages": "{{activity_logs.data?.meta?.last_page ?? null}}", + "hasMorePages": "{{activity_logs.data?.meta?.has_more_pages ?? false}}", "showFirstLast": true }, "actions": [ diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_list/_partial_product_datagrid.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_list/_partial_product_datagrid.json index f7430ad6..0e692e63 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_list/_partial_product_datagrid.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_product_list/_partial_product_datagrid.json @@ -557,7 +557,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{products?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{products?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{products?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{products?.data?.pagination?.has_more_pages ?? false}}", "pageSize": 20, "alwaysShowPagination": true, "showColumnSelector": false, diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_form/_partial_usage_conditions.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_form/_partial_usage_conditions.json index afcb70f5..87e58a8a 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_form/_partial_usage_conditions.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_form/_partial_usage_conditions.json @@ -282,7 +282,8 @@ "target": "_local", "productSearchResults": "{{response?.data?.data ?? response?.data ?? []}}", "productSearchPage": "{{response?.data?.pagination?.current_page ?? 1}}", - "productSearchLastPage": "{{response?.data?.pagination?.last_page ?? 1}}", + "productSearchLastPage": "{{response?.data?.pagination?.last_page ?? null}}", + "productSearchHasMore": "{{response?.data?.pagination?.has_more_pages ?? false}}", "productSearchTotal": "{{response?.data?.pagination?.total ?? 0}}" } } @@ -402,7 +403,7 @@ { "handler": "switch", "params": { - "value": "{{$event.target.scrollHeight - $event.target.scrollTop <= $event.target.clientHeight + 100 && (_local.productSearchPage ?? 1) < (_local.productSearchLastPage ?? 1) && !_local.productSearchIsLoadingMore}}" + "value": "{{$event.target.scrollHeight - $event.target.scrollTop <= $event.target.clientHeight + 100 && ((_local.productSearchHasMore === true) || ((_local.productSearchPage ?? 1) < (_local.productSearchLastPage ?? 0))) && !_local.productSearchIsLoadingMore}}" }, "cases": { "true": { @@ -446,7 +447,8 @@ "target": "_local", "productSearchResults": "{{[...(_local.productSearchResults ?? []), ...(response?.data?.data ?? response?.data ?? [])]}}", "productSearchPage": "{{response?.data?.pagination?.current_page ?? 1}}", - "productSearchLastPage": "{{response?.data?.pagination?.last_page ?? 1}}", + "productSearchLastPage": "{{response?.data?.pagination?.last_page ?? null}}", + "productSearchHasMore": "{{response?.data?.pagination?.has_more_pages ?? false}}", "productSearchTotal": "{{response?.data?.pagination?.total ?? 0}}", "productSearchIsLoadingMore": false } diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_modal_issue_history.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_modal_issue_history.json index f2415111..52da3941 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_modal_issue_history.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_modal_issue_history.json @@ -385,7 +385,8 @@ "name": "Pagination", "props": { "currentPage": "{{couponIssues?.data?.pagination?.current_page || 1}}", - "totalPages": "{{couponIssues?.data?.pagination?.last_page || 1}}", + "totalPages": "{{couponIssues?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{couponIssues?.data?.pagination?.has_more_pages ?? false}}", "totalItems": "{{couponIssues?.data?.pagination?.total || 0}}", "perPage": "{{couponIssues?.data?.pagination?.per_page || 10}}" }, diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_partial_coupon_datagrid.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_partial_coupon_datagrid.json index 768dd69e..44296856 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_partial_coupon_datagrid.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_promotion_coupon_list/_partial_coupon_datagrid.json @@ -232,7 +232,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{coupons?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{coupons?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{coupons?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{coupons?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 10)}}", "alwaysShowPagination": true, "selectable": true, diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_identity_policies.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_identity_policies.json index d62033f9..f82bfecc 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_identity_policies.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_identity_policies.json @@ -1047,7 +1047,7 @@ "props": { "className": "text-tertiary" }, - "text": "$t:admin.identity.policies.pagination_summary|total={{ecommerceIdentityPolicies?.data?.meta?.total ?? 0}}|page={{ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{ecommerceIdentityPolicies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.pagination_summary|total={{ecommerceIdentityPolicies?.data?.meta?.total ?? 0}}|page={{ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{ecommerceIdentityPolicies?.data?.meta?.last_page ?? '-'}}" }, { "type": "basic", @@ -1100,14 +1100,14 @@ "props": { "className": "px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200" }, - "text": "$t:admin.identity.policies.page_indicator|page={{ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{ecommerceIdentityPolicies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.page_indicator|page={{ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1}}|last={{ecommerceIdentityPolicies?.data?.meta?.last_page ?? '-'}}" }, { "type": "basic", "name": "Button", "props": { "type": "button", - "disabled": "{{(ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1) >= (ecommerceIdentityPolicies?.data?.meta?.last_page ?? 1)}}", + "disabled": "{{ecommerceIdentityPolicies?.data?.meta?.last_page ? ((ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1) >= ecommerceIdentityPolicies?.data?.meta?.last_page) : (ecommerceIdentityPolicies?.data?.meta?.has_more_pages !== true)}}", "className": "px-3 py-1.5 text-xs font-medium bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed" }, "actions": [ @@ -1120,7 +1120,7 @@ "mergeQuery": true, "query": { "tab": "identity_policies", - "page": "{{Math.min(ecommerceIdentityPolicies?.data?.meta?.last_page ?? 1, (ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1) + 1)}}" + "page": "{{ecommerceIdentityPolicies?.data?.meta?.last_page ? Math.min(ecommerceIdentityPolicies?.data?.meta?.last_page, (ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1) + 1) : ((ecommerceIdentityPolicies?.data?.meta?.current_page ?? 1) + 1)}}" }, "transition_overlay_target": "policy_table_card", "scroll": { diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_notification_definitions.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_notification_definitions.json index 39dd4012..9bc531cf 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_notification_definitions.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_settings/_tab_notification_definitions.json @@ -713,7 +713,8 @@ "name": "Pagination", "props": { "currentPage": "{{ecommerceNotificationDefinitions?.data?.pagination?.current_page || 1}}", - "totalPages": "{{ecommerceNotificationDefinitions?.data?.pagination?.last_page || 1}}" + "totalPages": "{{ecommerceNotificationDefinitions?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{ecommerceNotificationDefinitions?.data?.pagination?.has_more_pages ?? false}}" }, "actions": [ { diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_shipping_policy_list/_partial_datagrid.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_shipping_policy_list/_partial_datagrid.json index bb33be43..e9ca5a27 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_shipping_policy_list/_partial_datagrid.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_shipping_policy_list/_partial_datagrid.json @@ -17,7 +17,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{shipping_policies?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{shipping_policies?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{shipping_policies?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{shipping_policies?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "subRowClassName": "px-4 py-3 rounded-lg bg-gray-100 dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-300 whitespace-normal shadow-sm", diff --git a/modules/_bundled/sirsoft-ecommerce/src/Http/Controllers/Public/StorefrontController.php b/modules/_bundled/sirsoft-ecommerce/src/Http/Controllers/Public/StorefrontController.php new file mode 100644 index 00000000..15b5f321 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/src/Http/Controllers/Public/StorefrontController.php @@ -0,0 +1,181 @@ +logApiUsage('storefront.index'); + + $validated = $request->validated(); + + $category = $this->resolveFocusedCategory($validated); + + if ($category === false) { + return ResponseHelper::notFound( + 'messages.categories.not_found', + [], + 'sirsoft-ecommerce' + ); + } + + return ResponseHelper::moduleSuccess( + 'sirsoft-ecommerce', + 'messages.products.fetch_success', + [ + 'category' => $category, + 'categories' => PublicCategoryResource::collection( + $this->categoryService->getPublicCategoryTree() + )->resolve(), + 'products' => $this->resolveProductList($request, $validated), + 'recent_products' => $this->resolveRecentProducts($validated), + 'popular_products' => $this->resolveDisplayGroup( + (int) ($validated['popular_limit'] ?? 8), + fn (int $limit) => $this->productService->getPopularProducts($limit) + ), + 'new_products' => $this->resolveDisplayGroup( + (int) ($validated['new_limit'] ?? 8), + fn (int $limit) => $this->productService->getNewProducts($limit) + ), + ] + ); + } catch (Exception $e) { + return ResponseHelper::moduleError( + 'sirsoft-ecommerce', + 'messages.products.fetch_failed', + 500 + ); + } + } + + /** + * 상품 목록 묶음을 조립합니다. + * + * 개별 상품 목록 엔드포인트와 같은 컬렉션을 쓰므로 페이지네이션 메타까지 형태가 같습니다. + * + * @param PublicStorefrontRequest $request 요청 객체 + * @param array $validated 검증된 입력 + * @return array 상품 목록 응답 데이터 + */ + private function resolveProductList(PublicStorefrontRequest $request, array $validated): array + { + $listKeys = ['category_id', 'category_slug', 'brand_id', 'search', 'sort', 'min_price', 'max_price']; + $filters = array_filter( + $validated, + fn ($value, $key) => in_array($key, $listKeys, true) && $value !== null && $value !== '', + ARRAY_FILTER_USE_BOTH + ); + + $perPage = (int) ($validated['per_page'] ?? 20); + $collection = new ProductCollection($this->productService->getPublicList($filters, $perPage)); + + return $collection->toArray($request); + } + + /** + * 특정 분류 화면이 필요로 하는 단건 분류를 조립합니다. + * + * 분류 화면은 "지금 보고 있는 분류" 하나와 전체 분류 트리, 그 분류의 상품 목록을 함께 + * 그린다. 단건 분류를 여기서 함께 돌려주면 그 화면도 요청 한 번으로 끝난다. + * `category_slug` 가 없으면(쇼핑 첫 화면) 조회하지 않는다. + * + * 응답 형태는 단건 분류 엔드포인트와 같은 리소스를 써서 완전히 일치시킨다 — + * 옮긴 것은 조립 위치뿐이므로 화면 바인딩이 그대로 동작해야 한다. + * + * 반환값이 false 면 "분류를 지정했는데 그런 분류가 없다" 는 뜻이다. 이 경우 호출자가 + * 404 로 끊는다 — 200 으로 빈 화면을 내보내면 검색엔진이 존재하지 않는 분류 주소를 + * 정상 페이지로 수집한다. + * + * @param array $validated 검증된 입력 + * @return array|null|false 분류 응답 데이터 / 미지정 시 null / 대상 없음 시 false + */ + private function resolveFocusedCategory(array $validated): array|null|false + { + $slug = trim((string) ($validated['category_slug'] ?? '')); + + if ($slug === '') { + return null; + } + + $category = $this->categoryService->getPublicCategoryBySlug($slug); + + return $category ? (new PublicCategoryDetailResource($category))->resolve() : false; + } + + /** + * 최근 본 상품 묶음을 조립합니다. + * + * 최근 본 상품은 클라이언트가 보관한 ID 목록으로만 정해지므로, 비어 있으면 조회하지 않습니다. + * + * @param array $validated 검증된 입력 + * @return array 상품 목록 (없으면 빈 배열) + */ + private function resolveRecentProducts(array $validated): array + { + $ids = array_values(array_filter(array_map( + 'intval', + explode(',', (string) ($validated['recent_ids'] ?? '')) + ))); + + if (empty($ids)) { + return []; + } + + return ProductListResource::collection( + $this->productService->getProductsByIds($ids) + )->resolve(); + } + + /** + * 진열 묶음(인기·신상품)을 조립합니다. + * + * 개수가 0 이면 그 묶음을 쓰지 않겠다는 뜻이므로 조회하지 않습니다. + * + * @param int $limit 조회 개수 + * @param callable $resolver 상품 조회 클로저 + * @return array 상품 목록 (없으면 빈 배열) + */ + private function resolveDisplayGroup(int $limit, callable $resolver): array + { + if ($limit <= 0) { + return []; + } + + return ProductListResource::collection($resolver($limit))->resolve(); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/src/Http/Requests/Public/PublicStorefrontRequest.php b/modules/_bundled/sirsoft-ecommerce/src/Http/Requests/Public/PublicStorefrontRequest.php new file mode 100644 index 00000000..4d51dbcf --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/src/Http/Requests/Public/PublicStorefrontRequest.php @@ -0,0 +1,85 @@ + ['nullable', 'integer'], + 'category_slug' => ['nullable', 'string', 'max:100'], + 'brand_id' => ['nullable', 'integer'], + 'search' => ['nullable', 'string', 'max:200'], + 'sort' => ['nullable', 'string', 'in:latest,sales,price_asc,price_desc'], + 'min_price' => ['nullable', 'integer', 'min:0'], + 'max_price' => ['nullable', 'integer', 'min:0'], + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + + // 진열 묶음 + 'popular_limit' => ['nullable', 'integer', 'min:0', 'max:50'], + 'new_limit' => ['nullable', 'integer', 'min:0', 'max:50'], + 'recent_ids' => ['nullable', 'string', 'max:500'], + ]; + + return HookManager::applyFilters('sirsoft-ecommerce.product.public_storefront_validation_rules', $rules, $this); + } + + /** + * 검증 에러 메시지 정의 + * + * @return array 검증 메시지 + */ + public function messages(): array + { + $messages = [ + 'category_id.integer' => __('sirsoft-ecommerce::validation.public_product.category_id.integer'), + 'category_slug.string' => __('sirsoft-ecommerce::validation.public_product.category_slug.string'), + 'category_slug.max' => __('sirsoft-ecommerce::validation.public_product.category_slug.max'), + 'brand_id.integer' => __('sirsoft-ecommerce::validation.public_product.brand_id.integer'), + 'search.string' => __('sirsoft-ecommerce::validation.public_product.search.string'), + 'search.max' => __('sirsoft-ecommerce::validation.public_product.search.max'), + 'sort.in' => __('sirsoft-ecommerce::validation.public_product.sort.in'), + 'min_price.integer' => __('sirsoft-ecommerce::validation.public_product.min_price.integer'), + 'min_price.min' => __('sirsoft-ecommerce::validation.public_product.min_price.min'), + 'max_price.integer' => __('sirsoft-ecommerce::validation.public_product.max_price.integer'), + 'max_price.min' => __('sirsoft-ecommerce::validation.public_product.max_price.min'), + 'per_page.integer' => __('sirsoft-ecommerce::validation.public_product.per_page.integer'), + 'per_page.min' => __('sirsoft-ecommerce::validation.public_product.per_page.min'), + 'per_page.max' => __('sirsoft-ecommerce::validation.public_product.per_page.max'), + 'popular_limit.integer' => __('sirsoft-ecommerce::validation.public_product.limit.integer'), + 'popular_limit.max' => __('sirsoft-ecommerce::validation.public_product.limit.max'), + 'new_limit.integer' => __('sirsoft-ecommerce::validation.public_product.limit.integer'), + 'new_limit.max' => __('sirsoft-ecommerce::validation.public_product.limit.max'), + 'recent_ids.string' => __('sirsoft-ecommerce::validation.public_product.ids.string'), + 'recent_ids.max' => __('sirsoft-ecommerce::validation.public_product.ids.max'), + ]; + + return HookManager::applyFilters('sirsoft-ecommerce.product.public_storefront_validation_messages', $messages, $this); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductCollection.php b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductCollection.php index 9f1e7a03..ea3f2571 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductCollection.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductCollection.php @@ -5,8 +5,8 @@ namespace Modules\Sirsoft\Ecommerce\Http\Resources; use App\Http\Resources\BaseApiCollection; use App\Http\Resources\Traits\HasAbilityCheck; use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; use Modules\Sirsoft\Ecommerce\Http\Resources\Traits\HasMultiCurrencyPrices; +use Modules\Sirsoft\Ecommerce\Models\Category; /** * 상품 컬렉션 리소스 @@ -40,26 +40,33 @@ class ProductCollection extends BaseApiCollection */ public function toArray(Request $request): array { - $result = [ + $this->prefetchCategoryAncestors(); + + return [ 'data' => $this->mapWithRowNumber(function ($product) { return (new ProductListResource($product))->resolve(request()); }), 'abilities' => $this->resolveAbilitiesFromMap($this->abilityMap(), $request->user()), + // 표준 메타를 쓴다. 상한형 페이지에서는 total_relation/result_cap 이 함께 실리고, + // 총 건수를 정확히 알 수 없으면 last_page 가 null 로 나간다. + ...$this->paginationMeta(), ]; + } - if ($this->resource instanceof LengthAwarePaginator) { - $result['pagination'] = [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ]; - } - - return $result; + /** + * 이번 응답이 그릴 분류 경로의 조상 카테고리를 한 번에 예열합니다. + * + * 상품마다 예열하면 예열 쿼리가 상품 수만큼 반복됩니다. 카테고리는 이미 적재돼 + * 있으므로 path 를 메모리에서 읽어 조상 조회를 응답당 1회로 고정합니다. + */ + private function prefetchCategoryAncestors(): void + { + Category::prefetchAncestorsFor( + $this->collection + ->filter(fn ($product) => $product->relationLoaded('categories')) + ->flatMap(fn ($product) => $product->categories) + ->unique('id') + ); } /** @@ -70,27 +77,16 @@ class ProductCollection extends BaseApiCollection */ public function withStatistics(array $statistics = []): array { - $result = [ + $this->prefetchCategoryAncestors(); + + return [ 'data' => $this->mapWithRowNumber(function ($product) { return (new ProductListResource($product))->resolve(request()); }), 'abilities' => $this->resolveAbilitiesFromMap($this->abilityMap(), request()->user()), 'statistics' => $statistics, + ...$this->paginationMeta(), ]; - - if ($this->resource instanceof LengthAwarePaginator) { - $result['pagination'] = [ - 'current_page' => $this->resource->currentPage(), - 'last_page' => $this->resource->lastPage(), - 'per_page' => $this->resource->perPage(), - 'total' => $this->resource->total(), - 'from' => $this->resource->firstItem(), - 'to' => $this->resource->lastItem(), - 'has_more_pages' => $this->resource->hasMorePages(), - ]; - } - - return $result; } /** diff --git a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductListResource.php b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductListResource.php index 30883bc7..b09edbdc 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductListResource.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/ProductListResource.php @@ -64,12 +64,23 @@ class ProductListResource extends BaseApiResource ])), 'primary_category' => $this->whenLoaded('categories', fn () => $this->categories->firstWhere('pivot.is_primary', true)?->getLocalizedName() ), - 'categories_with_path' => $this->whenLoaded('categories', fn () => $this->categories->map(fn ($cat) => [ - 'id' => $cat->id, - 'path' => $cat->getBreadcrumb(), - 'path_string' => collect($cat->getBreadcrumb())->pluck('name')->implode(' > '), - 'is_primary' => $cat->pivot->is_primary, - ])), + // 경로는 카테고리당 한 번만 만든다. 두 번 부르면 조상 조회도 두 번 나간다. + // + // 조상 예열은 여기가 아니라 ProductCollection 이 응답 단위로 한 번 수행한다. + // 상품마다 예열하면 예열 쿼리가 상품 수만큼 반복된다. 단건 사용처(컬렉션을 + // 거치지 않는 경로)에서는 getBreadcrumb() 이 스스로 필요한 조상만 읽는다. + 'categories_with_path' => $this->whenLoaded('categories', function () { + return $this->categories->map(function ($cat) { + $breadcrumb = $cat->getBreadcrumb(); + + return [ + 'id' => $cat->id, + 'path' => $breadcrumb, + 'path_string' => collect($breadcrumb)->pluck('name')->implode(' > '), + 'is_primary' => $cat->pivot->is_primary, + ]; + }); + }), // 브랜드 (다국어) 'brand_name' => $this->whenLoaded('brand', fn () => $this->brand?->getLocalizedName()), diff --git a/modules/_bundled/sirsoft-ecommerce/src/Listeners/CategoryTreeCacheListener.php b/modules/_bundled/sirsoft-ecommerce/src/Listeners/CategoryTreeCacheListener.php new file mode 100644 index 00000000..d4467fc8 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/src/Listeners/CategoryTreeCacheListener.php @@ -0,0 +1,78 @@ + 'flush', 'priority' => 10]; + } + + // 상품 변경 — 트리에 실리는 상품 수 집계가 달라진다 + foreach ([ + 'sirsoft-ecommerce.product.after_create', + 'sirsoft-ecommerce.product.after_update', + 'sirsoft-ecommerce.product.after_delete', + 'sirsoft-ecommerce.product.after_bulk_update', + ] as $hook) { + $hooks[$hook] = ['method' => 'flush', 'priority' => 10]; + } + + return $hooks; + } + + /** + * 기본 훅 핸들러 (HookListenerInterface 필수 메서드) + * + * @param mixed ...$args 훅 인자 + */ + public function handle(...$args): void + { + $this->flush(); + } + + /** + * 카테고리 트리 캐시를 비웁니다. + * + * @param mixed ...$args 훅 인자 (사용하지 않음 — 어떤 변경이든 트리 전체가 대상) + */ + public function flush(...$args): void + { + try { + Category::flushTreeCache(); + } catch (\Throwable $e) { + // 캐시 무효화 실패가 본 작업(상품/카테고리 저장)을 되돌리게 두지 않는다. + // TTL 이 안전망으로 남아 있으므로 낡은 트리는 스스로 만료된다. + Log::warning('카테고리 트리 캐시 무효화 실패', ['message' => $e->getMessage()]); + } + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/src/Listeners/SearchProductsListener.php b/modules/_bundled/sirsoft-ecommerce/src/Listeners/SearchProductsListener.php index 4e81f347..0b636843 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Listeners/SearchProductsListener.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Listeners/SearchProductsListener.php @@ -3,6 +3,9 @@ namespace Modules\Sirsoft\Ecommerce\Listeners; use App\Contracts\Extension\HookListenerInterface; +use App\Enums\TotalRelation; +use App\Search\SearchCategoryPayload; +use App\Support\Query\BoundedCount; use App\Helpers\PermissionHelper; use Illuminate\Support\Facades\Log; use Modules\Sirsoft\Ecommerce\Http\Resources\Traits\HasMultiCurrencyPrices; @@ -53,7 +56,7 @@ class SearchProductsListener implements HookListenerInterface * 훅 이벤트를 처리합니다. * Filter Hook은 getSubscribedHooks에서 지정한 메서드를 직접 호출하므로 이 메서드는 사용되지 않습니다. * - * @param mixed ...$args 훅에서 전달된 인수들 + * @param mixed ...$args 훅에서 전달된 인수들 * @return void */ public function handle(...$args): void @@ -65,7 +68,7 @@ class SearchProductsListener implements HookListenerInterface /** * 검색 파라미터 validation rules 추가 * - * @param array $rules 기존 validation rules + * @param array $rules 기존 validation rules * @return array 이커머스 모듈 파라미터가 추가된 rules */ public function addValidationRules(array $rules): array @@ -80,8 +83,8 @@ class SearchProductsListener implements HookListenerInterface /** * 상품 검색을 수행하고 결과를 반환합니다. * - * @param array $results 기존 검색 결과 - * @param array $context 검색 컨텍스트 (q, type, sort, page, per_page, user, request) + * @param array $results 기존 검색 결과 + * @param array $context 검색 컨텍스트 (q, type, sort, page, per_page, user, request) * @return array 상품이 추가된 검색 결과 */ public function searchProducts(array $results, array $context): array @@ -107,27 +110,62 @@ class SearchProductsListener implements HookListenerInterface $perPage = $context['per_page'] ?? 10; try { - // 다른 탭인 경우 count만 반환 - if (!$isRelevantTab) { - $results['products'] = [ - 'total' => $this->productService->countByKeyword($q, $categoryId), - 'items' => [], - ]; + // 다른 탭인 경우 count만 반환. + // 배지에도 정확도를 함께 싣는다 — 상한에 걸려 잘린 값을 정확한 것처럼 + // 내보내면 화면에는 그냥 틀린 숫자로만 보인다. + if (! $isRelevantTab) { + // 배지를 그리지 않는 화면은 건수를 요청하지 않는다 — 그 경우 집계를 생략한다. + if (! ($context['include_inactive_counts'] ?? true)) { + $results['products'] = SearchCategoryPayload::fromCountOnly( + new BoundedCount(0, TotalRelation::Exact, null) + ); + + return $results; + } + + $results['products'] = SearchCategoryPayload::fromCountOnly( + $this->productService->countByKeyword($q, $categoryId) + ); return $results; } - $offset = ($page - 1) * $perPage; - $searchResult = $this->productService->searchByKeyword($q, $sort, $categoryId, $offset, $perPage); + // 전체 탭은 미리보기 몇 건만, 상품 탭은 실제 요청 페이지를 그대로 조회한다. + // 종전에는 전체 탭에서도 페이지 분량을 받아 PHP 에서 다시 잘랐다. + $isAllTab = ($type === 'all'); + $fetchPerPage = $isAllTab ? (int) ($context['all_tab_limit'] ?? 5) : (int) $perPage; + $offset = $isAllTab ? 0 : (((int) $page - 1) * (int) $perPage); - $items = $searchResult['items']->map( - fn ($product) => $this->formatProductResult($product, $q) - )->toArray(); + /** 조회 결과 항목을 화면 형태로 가공한다. */ + $format = fn (iterable $items): array => collect($items) + ->map(fn ($product) => $this->formatProductResult($product, $q)) + ->toArray(); - $results['products'] = [ - 'total' => $searchResult['total'], - 'items' => $items, - ]; + // 커서를 받았고 그 정렬을 커서로 처리할 수 있으면 키셋으로 응답한다. + // 전체 탭은 미리보기 몇 건뿐이라 깊은 페이지가 없어 대상이 아니다. + $cursorPage = $isAllTab + ? null + : $this->productService->searchByKeywordWithCursor( + $q, + $sort, + $categoryId, + $fetchPerPage, + $context['cursor'] ?? null + ); + + if ($cursorPage !== null) { + $results['products'] = SearchCategoryPayload::fromCursor( + $cursorPage, + $this->productService->countByKeyword($q, $categoryId), + $format($cursorPage->items()) + ); + + return $results; + } + + $searchPage = $this->productService->searchByKeyword($q, $sort, $categoryId, $offset, $fetchPerPage); + + $results['products'] = SearchCategoryPayload::fromBounded($searchPage, $format($searchPage->items())); } catch (\Exception $e) { Log::error('Search products error', [ 'message' => $e->getMessage(), @@ -141,14 +179,14 @@ class SearchProductsListener implements HookListenerInterface /** * 상품 검색 결과를 프론트엔드 응답 구조로 변환합니다. * - * @param array $response 기존 응답 구조 - * @param array $results 검색 결과 (core.search.results에서 반환된 데이터) - * @param array $context 검색 컨텍스트 + * @param array $response 기존 응답 구조 + * @param array $results 검색 결과 (core.search.results에서 반환된 데이터) + * @param array $context 검색 컨텍스트 * @return array 상품 응답이 추가된 구조 */ public function buildProductsResponse(array $response, array $results, array $context): array { - if (!isset($results['products'])) { + if (! isset($results['products'])) { return $response; } @@ -157,20 +195,27 @@ class SearchProductsListener implements HookListenerInterface $page = $context['page'] ?? 1; $perPage = $context['per_page'] ?? 10; + // 항목은 이미 요청한 분량만 조회돼 있다 — 여기서 다시 자르지 않는다. $items = $productsData['items'] ?? []; $total = $productsData['total'] ?? 0; if ($type === 'all') { - $allTabLimit = $context['all_tab_limit'] ?? 5; - $response['products'] = array_slice($items, 0, $allTabLimit); + $response['products'] = $items; } elseif ($type === 'products') { $response['products'] = $items; $response['current_page'] = $page; $response['per_page'] = $perPage; - $response['last_page'] = max(1, (int) ceil($total / $perPage)); + // 총 건수가 상한에 걸리면 마지막 페이지를 계산할 수 없다 — null 로 알린다. + $response['last_page'] = $productsData['last_page'] ?? null; + $response['has_more_pages'] = $productsData['has_more_pages'] ?? false; + // 커서 응답이면 다음/이전 커서를 함께 실어 깊은 페이지를 OFFSET 없이 넘긴다. + $response['next_cursor'] = $productsData['next_cursor'] ?? null; + $response['prev_cursor'] = $productsData['prev_cursor'] ?? null; } $response['products_count'] = $total; + $response['products_total_is_exact'] = $productsData['total_is_exact'] ?? true; + $response['products_total_relation'] = $productsData['total_relation'] ?? null; return $response; } @@ -180,8 +225,8 @@ class SearchProductsListener implements HookListenerInterface /** * 상품을 검색 결과 형식으로 변환합니다. * - * @param object $product 상품 모델 - * @param string $keyword 검색어 + * @param object $product 상품 모델 + * @param string $keyword 검색어 * @return array 변환된 상품 데이터 */ private function formatProductResult(object $product, string $keyword): array @@ -229,8 +274,8 @@ class SearchProductsListener implements HookListenerInterface /** * 텍스트에서 검색어를 하이라이트 처리합니다. * - * @param string|null $text 원본 텍스트 - * @param string $keyword 검색어 + * @param string|null $text 원본 텍스트 + * @param string $keyword 검색어 * @return string 하이라이트 처리된 텍스트 */ private function highlightKeyword(?string $text, string $keyword): string @@ -247,9 +292,9 @@ class SearchProductsListener implements HookListenerInterface /** * 본문에서 키워드 주변 텍스트를 추출합니다. * - * @param string|null $content 본문 내용 - * @param string $keyword 검색어 - * @param int $length 추출할 최대 길이 + * @param string|null $content 본문 내용 + * @param string $keyword 검색어 + * @param int $length 추출할 최대 길이 * @return string 추출된 미리보기 텍스트 */ private function extractContentPreview(?string $content, string $keyword, int $length = 150): string @@ -274,4 +319,4 @@ class SearchProductsListener implements HookListenerInterface return $preview.(mb_strlen($plainText) > $length ? '...' : ''); } -} \ No newline at end of file +} diff --git a/modules/_bundled/sirsoft-ecommerce/src/Models/Category.php b/modules/_bundled/sirsoft-ecommerce/src/Models/Category.php index ee1bdcce..a91e28e5 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Models/Category.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Models/Category.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Models; use App\Casts\AsUnicodeJson; +use App\Contracts\Extension\CacheInterface; use App\Extension\HookManager; use App\Search\Contracts\FulltextSearchable; use Illuminate\Database\Eloquent\Builder; @@ -176,6 +177,20 @@ class Category extends Model implements FulltextSearchable return $ids; } + /** + * 조상 카테고리 예열 캐시 (요청 스코프) + * + * @var array + */ + private static array $ancestorPrefetch = []; + + /** + * 트리 캐시 TTL (초) + * + * 정상 경로의 무효화는 훅이 담당하므로 이 값은 훅이 닿지 않는 변경에 대한 안전망이다. + */ + private const TREE_CACHE_TTL = 600; + /** * 조상 카테고리들 조회 * @@ -189,10 +204,95 @@ class Category extends Model implements FulltextSearchable return new Collection; } - $ancestors = self::whereIn('id', $ancestorIds)->get(); - $orderedIds = array_flip($ancestorIds); + // 한 응답에서 여러 카테고리의 조상을 그리는 경우(상품 목록의 분류 경로 등) + // 같은 조상을 반복해 조회하지 않도록, 미리 예열해 둔 맵을 먼저 본다. + $prefetched = self::$ancestorPrefetch; + $missing = array_values(array_filter($ancestorIds, fn ($id) => ! isset($prefetched[$id]))); - return $ancestors->sortBy(fn ($item) => $orderedIds[$item->id] ?? PHP_INT_MAX)->values(); + if (! empty($missing)) { + foreach (self::whereIn('id', $missing)->get() as $fetched) { + self::$ancestorPrefetch[$fetched->id] = $fetched; + } + } + + $ancestors = new Collection; + + foreach ($ancestorIds as $id) { + if (isset(self::$ancestorPrefetch[$id])) { + $ancestors->push(self::$ancestorPrefetch[$id]); + } + } + + return $ancestors->values(); + } + + /** + * 조상 카테고리를 미리 한 번에 적재합니다. + * + * 한 응답이 여러 카테고리의 경로를 그릴 때, 조상 조회가 카테고리 수만큼 반복되지 않도록 + * 호출자가 먼저 예열합니다. 예열하지 않아도 동작은 같고 쿼리 수만 달라집니다. + * + * @param array $categoryIds 경로를 그릴 카테고리 ID 목록 + */ + public static function prefetchAncestors(array $categoryIds): void + { + $categoryIds = array_values(array_unique(array_filter($categoryIds))); + + if (empty($categoryIds)) { + return; + } + + self::warmAncestorsFromPaths( + self::whereIn('id', $categoryIds)->get(['id', 'path']) + ); + } + + /** + * 이미 적재된 카테고리 모델들로 조상을 예열합니다. + * + * `path` 가 메모리에 이미 있으므로 그 값을 다시 읽는 쿼리를 내지 않습니다. + * 상품 목록처럼 응답 전체가 같은 조상 집합을 공유하는 자리에서는 컬렉션 단위로 + * 한 번만 부르면 되며, 그러면 조상 조회가 응답당 1회로 고정됩니다. + * + * @param iterable $categories path 가 적재된 카테고리들 + */ + public static function prefetchAncestorsFor(iterable $categories): void + { + self::warmAncestorsFromPaths(new Collection($categories instanceof Collection ? $categories->all() : $categories)); + } + + /** + * path 문자열에서 조상 ID 를 뽑아 한 번에 적재합니다. + * + * @param Collection $categories path 가 있는 카테고리들 + */ + private static function warmAncestorsFromPaths(Collection $categories): void + { + $ancestorIds = $categories + ->flatMap(fn ($category) => array_filter(array_map('intval', explode('/', (string) $category->path)))) + ->unique() + ->reject(fn ($id) => isset(self::$ancestorPrefetch[$id])) + ->values() + ->all(); + + if (empty($ancestorIds)) { + return; + } + + foreach (self::whereIn('id', $ancestorIds)->get() as $ancestor) { + self::$ancestorPrefetch[$ancestor->id] = $ancestor; + } + } + + /** + * 조상 예열 캐시를 비웁니다. + * + * 캐시 수명은 요청 스코프입니다. 같은 프로세스에서 카테고리를 바꾼 뒤 다시 경로를 + * 그려야 하는 경우(테스트·콘솔 작업)에 호출합니다. + */ + public static function flushAncestorPrefetch(): void + { + self::$ancestorPrefetch = []; } /** @@ -300,20 +400,91 @@ class Category extends Model implements FulltextSearchable } /** - * 트리 구조로 카테고리 조회 (재귀) + * 트리 구조로 카테고리 조회 + * + * 종전에는 노드마다 자식을 다시 조회해, 카테고리 수만큼 쿼리(각각 상품 수 집계 서브쿼리 + * 포함)가 발행됐다. 지금은 대상 전체를 한 번에 읽고 PHP 에서 부모–자식을 잇는다. + * + * 결과는 캐시한다 — 카테고리는 자주 바뀌지 않는데 거의 모든 쇼핑 화면이 이 트리를 읽는다. + * 무효화는 카테고리·상품 변경 지점이 담당한다({@see self::flushTreeCache()}). + * 트리에 상품 수 집계가 함께 실리므로 상품 변경도 무효화 대상이다. * * @param int|null $parentId 부모 ID (null이면 루트부터) * @param bool $onlyActive 활성 카테고리만 조회할지 여부 - * @return Collection + * @return Collection 트리 구조 카테고리 컬렉션 */ public static function getTree(?int $parentId = null, bool $onlyActive = false): Collection + { + $key = self::treeCacheKey($parentId, $onlyActive); + + $cached = app(CacheInterface::class)->get($key); + + if ($cached instanceof Collection) { + return $cached; + } + + $tree = self::buildTree($parentId, $onlyActive); + + // TTL 은 무효화 훅이 닿지 않는 경로(직접 SQL 수정 등)에 대한 안전망일 뿐, + // 정상 경로의 반영 지연은 flushTreeCache() 가 없앤다. + app(CacheInterface::class)->put($key, $tree, self::TREE_CACHE_TTL); + + return $tree; + } + + /** + * 카테고리 트리 캐시를 전부 비웁니다. + * + * 캐시 키는 (부모, 활성여부) 조합이라 변경 하나가 어느 조합에 영향을 주는지 + * 특정하기 어렵다 — 조합 수가 적으므로 전량을 비운다. + */ + public static function flushTreeCache(): void + { + $cache = app(CacheInterface::class); + + foreach ([true, false] as $onlyActive) { + $cache->forget(self::treeCacheKey(null, $onlyActive)); + } + + // 부모 지정 트리는 그 부모 ID 로 키가 갈린다. 태그를 지원하지 않는 드라이버가 + // 있으므로 존재하는 카테고리 ID 만 훑어 지운다 (카테고리 수는 수백 단위). + foreach (self::query()->pluck('id') as $id) { + foreach ([true, false] as $onlyActive) { + $cache->forget(self::treeCacheKey((int) $id, $onlyActive)); + } + } + } + + /** + * 트리 캐시 키를 만듭니다. + * + * @param int|null $parentId 부모 ID + * @param bool $onlyActive 활성만 여부 + * @return string 캐시 키 + */ + private static function treeCacheKey(?int $parentId, bool $onlyActive): string + { + return sprintf( + 'sirsoft-ecommerce.category.tree.%s.%s', + $parentId === null ? 'root' : $parentId, + $onlyActive ? 'active' : 'all' + ); + } + + /** + * 대상 전체를 한 번에 읽어 트리를 조립합니다. + * + * @param int|null $parentId 루트로 삼을 부모 ID + * @param bool $onlyActive 활성 카테고리만 포함할지 여부 + * @return Collection 트리 구조 카테고리 컬렉션 + */ + private static function buildTree(?int $parentId, bool $onlyActive): Collection { $query = self::with([ 'images', 'parent:id,name,slug', // parent에서 필요한 필드만 선택 ]) ->withCount('products') - ->where('parent_id', $parentId) ->orderBy('sort_order') ->orderBy('id'); @@ -321,13 +492,31 @@ class Category extends Model implements FulltextSearchable $query->where('is_active', true); } - $categories = $query->get(); + $all = $query->get(); + $byParent = $all->groupBy(fn (self $category) => $category->parent_id === null ? '' : (string) $category->parent_id); - foreach ($categories as $category) { - $category->setRelation('children', self::getTree($category->id, $onlyActive)); - } + // 방문 집합으로 유한 종료를 보장한다 — 오염된 데이터(순환 참조)에서도 멈춘다. + $visited = []; - return $categories; + $attach = function (?int $currentParentId) use (&$attach, $byParent, &$visited): Collection { + $children = $byParent->get($currentParentId === null ? '' : (string) $currentParentId) ?? new Collection; + + $resolved = new Collection; + + foreach ($children as $child) { + if (isset($visited[$child->id])) { + continue; + } + + $visited[$child->id] = true; + $child->setRelation('children', $attach($child->id)); + $resolved->push($child); + } + + return $resolved; + }; + + return $attach($parentId); } /** diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/BrandRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/BrandRepository.php index b484aa41..a046d562 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/BrandRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/BrandRepository.php @@ -3,7 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Helpers\PermissionHelper; -use App\Search\Engines\DatabaseFulltextEngine; +use App\Search\KeywordSearch; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Modules\Sirsoft\Ecommerce\Models\Brand; @@ -47,7 +47,7 @@ class BrandRepository implements BrandRepositoryInterface $keyword = $filters['search']; $query->where(function ($q) use ($keyword) { - DatabaseFulltextEngine::whereFulltext($q, 'name', $keyword, 'and'); + KeywordSearch::apply($q, 'name', $keyword, 'and'); $q->orWhere('slug', 'like', "%{$keyword}%") ->orWhere('website', 'like', "%{$keyword}%"); }); diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CartRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CartRepository.php index c50c80d6..64fb4cbb 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CartRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CartRepository.php @@ -4,6 +4,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection as SupportCollection; use Modules\Sirsoft\Ecommerce\Models\Cart; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\CartRepositoryInterface; @@ -128,6 +129,32 @@ class CartRepository implements CartRepositoryInterface ->get(); } + /** + * 회원 장바구니에서 여러 옵션의 라인을 한 번에 조회해 옵션별로 묶어 반환합니다. + * + * 비회원 장바구니 병합처럼 여러 항목을 한꺼번에 다루는 경로에서, 항목마다 조회하면 + * 로그인 한 번에 항목 수만큼 쿼리가 난다. + * + * @param int $userId 회원 ID + * @param array $productOptionIds 옵션 ID 목록 + * @return SupportCollection 옵션 ID ⇒ 장바구니 라인 컬렉션 + */ + public function findAllByUserAndOptions(int $userId, array $productOptionIds): SupportCollection + { + $productOptionIds = array_values(array_unique(array_filter($productOptionIds))); + + if (empty($productOptionIds)) { + return new SupportCollection; + } + + // groupBy 는 Support\Collection 을 돌려준다 (Eloquent\Collection 이 아니다). + return $this->model + ->where('user_id', $userId) + ->whereIn('product_option_id', $productOptionIds) + ->get() + ->groupBy('product_option_id'); + } + /** * {@inheritDoc} */ @@ -226,6 +253,25 @@ class CartRepository implements CartRepositoryInterface return (int) $query->sum('quantity'); } + /** + * {@inheritDoc} + */ + public function sumQuantityByProducts(array $productIds, int $userId): array + { + if (empty($productIds)) { + return []; + } + + return $this->model + ->whereIn('product_id', $productIds) + ->where('user_id', $userId) + ->groupBy('product_id') + ->selectRaw('product_id, SUM(quantity) as total_quantity') + ->pluck('total_quantity', 'product_id') + ->map(fn ($total) => (int) $total) + ->all(); + } + /** * {@inheritDoc} */ diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CategoryRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CategoryRepository.php index b0357fd8..374a0388 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CategoryRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CategoryRepository.php @@ -90,7 +90,7 @@ class CategoryRepository implements CategoryRepositoryInterface /** * {@inheritDoc} */ - public function findById(int $id, array $with = []): ?Category + public function findById(int $id, array $with = [], bool $withCounts = false): ?Category { $query = $this->model->newQuery(); @@ -98,12 +98,29 @@ class CategoryRepository implements CategoryRepositoryInterface $query->with($with); } - // 상품 수 및 자식 수 카운트 추가 - $query->withCount(['products', 'children']); + // 상품 수 및 자식 수 카운트는 화면에서만 쓴다. 재정렬 같은 쓰기 루프는 이 값을 + // 읽지 않으므로, 무조건 계산하면 항목마다 집계 서브쿼리가 두 개씩 따라붙는다. + if ($withCounts) { + $query->withCount(['products', 'children']); + } return $query->find($id); } + /** + * {@inheritDoc} + */ + public function findByIdsKeyed(array $ids): Collection + { + $ids = array_values(array_unique(array_filter($ids))); + + if (empty($ids)) { + return new Collection; + } + + return $this->model->newQuery()->whereIn('id', $ids)->get()->keyBy('id'); + } + /** * {@inheritDoc} */ @@ -117,7 +134,9 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function update(int $id, array $data): Category { - $category = $this->findById($id); + // 재정렬은 항목 수만큼 이 메서드를 부른다. 집계를 켜 두면 항목마다 상품 수·자식 수 + // 서브쿼리가 두 개씩 붙지만 쓰기 경로는 그 값을 읽지 않는다. + $category = $this->findById($id, withCounts: false); $category->update($data); return $category->fresh(); @@ -128,7 +147,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function delete(int $id): bool { - $category = $this->findById($id); + $category = $this->findById($id, withCounts: false); return $category->delete(); } @@ -146,7 +165,8 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function getProductCount(int $id): int { - $category = $this->findById($id); + // 아래에서 products()->count() 를 직접 세므로 withCount 집계는 버려진다 + $category = $this->findById($id, withCounts: false); return $category->products()->count(); } @@ -180,7 +200,7 @@ class CategoryRepository implements CategoryRepositoryInterface /** * {@inheritDoc} */ - public function findBySlug(string $slug, array $with = []): ?Category + public function findBySlug(string $slug, array $with = [], bool $withCounts = false): ?Category { $query = $this->model->newQuery(); @@ -188,7 +208,9 @@ class CategoryRepository implements CategoryRepositoryInterface $query->with($with); } - $query->withCount(['products', 'children']); + if ($withCounts) { + $query->withCount(['products', 'children']); + } return $query->where('slug', $slug)->first(); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CartRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CartRepositoryInterface.php index 69abe685..b252ddb6 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CartRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CartRepositoryInterface.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories\Contracts; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection as SupportCollection; use Modules\Sirsoft\Ecommerce\Models\Cart; /** @@ -89,6 +90,15 @@ interface CartRepositoryInterface */ public function findAllByUserAndOption(int $userId, int $productOptionId): Collection; + /** + * 회원 장바구니에서 여러 옵션의 라인을 한 번에 조회해 옵션별로 묶어 반환합니다. + * + * @param int $userId 회원 ID + * @param array $productOptionIds 옵션 ID 목록 + * @return SupportCollection 옵션 ID ⇒ 장바구니 라인 컬렉션 + */ + public function findAllByUserAndOptions(int $userId, array $productOptionIds): SupportCollection; + /** * cart_key와 옵션 ID로 동일 옵션 비회원 장바구니 아이템 전체 조회 * @@ -160,6 +170,19 @@ interface CartRepositoryInterface */ public function sumQuantityByProduct(int $productId, ?int $userId, ?string $cartKey, ?int $excludeCartId = null): int; + /** + * 회원 장바구니에서 여러 상품의 수량 합계를 한 번에 조회합니다. + * + * 여러 항목을 한꺼번에 다루는 경로(비회원 장바구니 병합 등)에서 항목마다 합계를 + * 조회하면 항목 수만큼 쿼리가 납니다. 시작 시점의 합계를 한 번 읽어 오고, 이후 + * 변동분은 호출자가 메모리에서 누적합니다. + * + * @param array $productIds 상품 ID 목록 + * @param int $userId 회원 ID + * @return array 상품 ID ⇒ 수량 합계 (없는 상품은 키 자체가 없음) + */ + public function sumQuantityByProducts(array $productIds, int $userId): array; + /** * 보관기간이 지난 장바구니 항목을 삭제합니다 (마지막 활동 updated_at 기준). * diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CategoryRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CategoryRepositoryInterface.php index 6762b532..ec246f24 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CategoryRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/CategoryRepositoryInterface.php @@ -24,9 +24,10 @@ interface CategoryRepositoryInterface * * @param int $id 카테고리 ID * @param array $with Eager loading 관계 + * @param bool $withCounts 상품 수·자식 수 집계 포함 여부 (쓰기 경로는 false) * @return Category|null 카테고리 모델 (없으면 null) */ - public function findById(int $id, array $with = []): ?Category; + public function findById(int $id, array $with = [], bool $withCounts = false): ?Category; /** * 카테고리 생성 @@ -91,9 +92,21 @@ interface CategoryRepositoryInterface * * @param string $slug 카테고리 slug * @param array $with Eager loading 관계 + * @param bool $withCounts 상품 수·자식 수 집계 포함 여부 (쓰기 경로는 false) * @return Category|null 카테고리 모델 (없으면 null) */ - public function findBySlug(string $slug, array $with = []): ?Category; + public function findBySlug(string $slug, array $with = [], bool $withCounts = false): ?Category; + + /** + * ID 목록으로 카테고리를 조회하고 ID 키 맵으로 반환합니다. + * + * 재정렬처럼 여러 항목을 한꺼번에 다루는 경로에서 항목마다 조회하지 않도록 합니다. + * 상품 수·자식 수 집계는 포함하지 않습니다 (쓰기 경로는 읽지 않음). + * + * @param array $ids 카테고리 ID 목록 + * @return Collection 카테고리 ID ⇒ 카테고리 + */ + public function findByIdsKeyed(array $ids): Collection; /** * 평면 리스트로 카테고리 목록 조회 (TagInput 등에 사용) diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/OrderRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/OrderRepositoryInterface.php index 540f407e..8ab419d6 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/OrderRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/OrderRepositoryInterface.php @@ -113,6 +113,14 @@ interface OrderRepositoryInterface */ public function getStatistics(): array; + /** + * 주문 통계 캐시를 무효화합니다. + * + * 주문이 바뀌면 통계도 곧바로 달라져야 하므로, 저장소 밖에서 주문을 변경하는 + * 경로도 이 메서드로 캐시를 비웁니다. + */ + public function forgetStatisticsCache(): void; + /** * 사용자별 주문상태 통계 조회 * @@ -164,6 +172,17 @@ interface OrderRepositoryInterface */ public function findByIdsKeyed(array $ids): Collection; + /** + * ID 목록으로 주문을 관계와 함께 조회하고 ID 키 맵으로 반환합니다. + * + * 주문 ID 를 하나씩 돌며 조회하면 대상 수만큼 쿼리가 납니다. + * + * @param array $ids 주문 ID 목록 + * @param array $with eager load 할 관계 + * @return Collection 주문 ID ⇒ 주문 + */ + public function findByIdsWithRelationsKeyed(array $ids, array $with = []): Collection; + /** * ID 목록으로 주문 스냅샷(배열)을 조회하고 ID 키 맵으로 반환합니다 (ChangeDetector용). * diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductAdditionalOptionValueRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductAdditionalOptionValueRepositoryInterface.php index 15459498..dfbf3fb3 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductAdditionalOptionValueRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductAdditionalOptionValueRepositoryInterface.php @@ -30,6 +30,17 @@ interface ProductAdditionalOptionValueRepositoryInterface */ public function getActiveByProductKeyed(int $productId): Collection; + /** + * 여러 상품의 활성 선택지를 상품 ID → (value_id 키 맵) 형태로 한 번에 조회합니다. + * + * 장바구니·바로구매처럼 항목이 여러 개인 경로에서 상품마다 조회하지 않기 위한 + * 일괄 진입점입니다. + * + * @param array $productIds 상품 ID 목록 + * @return Collection> 상품 ID 키 맵 + */ + public function getActiveByProductIdsKeyed(array $productIds): Collection; + /** * 추가옵션 그룹 ID 목록에 속한 모든 선택지를 삭제합니다. * diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductOptionRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductOptionRepositoryInterface.php index eead86d9..248224e4 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductOptionRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductOptionRepositoryInterface.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories\Contracts; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection as SupportCollection; use Modules\Sirsoft\Ecommerce\Models\ProductOption; /** @@ -45,6 +46,14 @@ interface ProductOptionRepositoryInterface */ public function getByProductId(int $productId): Collection; + /** + * 여러 상품의 옵션을 한 번에 조회해 상품별로 묶어 반환합니다. + * + * @param array $productIds 상품 ID 목록 + * @return SupportCollection 상품 ID ⇒ 옵션 컬렉션 + */ + public function getByProductIds(array $productIds): SupportCollection; + /** * 옵션 판매가 일괄 변경 * diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductRepositoryInterface.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductRepositoryInterface.php index 4a3e65b8..592717bd 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductRepositoryInterface.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/Contracts/ProductRepositoryInterface.php @@ -2,6 +2,9 @@ namespace Modules\Sirsoft\Ecommerce\Repositories\Contracts; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Modules\Sirsoft\Ecommerce\Models\Product; @@ -149,6 +152,13 @@ interface ProductRepositoryInterface */ public function getStatistics(): array; + /** + * 상품 통계 캐시를 무효화합니다. + * + * 상품이 바뀌면 통계도 곧바로 달라져야 합니다. + */ + public function forgetStatisticsCache(): void; + /** * 상품 코드로 상품 조회 * @@ -218,6 +228,17 @@ interface ProductRepositoryInterface */ public function findByIds(array $ids): Collection; + /** + * ID 목록으로 상품을 관계와 함께 조회해 ID 키 맵으로 반환합니다. + * + * 항목마다 `find()` 를 부르는 루프를 없애기 위한 일괄 진입점입니다. + * + * @param array $ids 상품 ID 배열 + * @param array $relations 함께 적재할 관계 + * @return Collection 상품 ID 키 맵 + */ + public function findByIdsWithRelationsKeyed(array $ids, array $relations = []): Collection; + /** * ID 목록으로 상품을 조회하고 ID 키 맵으로 반환합니다 (bulk activity log lookup). * @@ -246,16 +267,16 @@ interface ProductRepositoryInterface * @param int $limit 조회할 최대 항목 수 * @return array{total: int, items: Collection} */ - public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', ?int $categoryId = null, int $offset = 0, int $limit = 10): array; + public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', ?int $categoryId = null, int $offset = 0, int $limit = 10): BoundedPage; /** * 키워드와 일치하는 공개 상품 수를 조회합니다. * * @param string $keyword 검색 키워드 * @param int|null $categoryId 카테고리 필터 (null이면 전체) - * @return int 일치하는 상품 수 + * @return BoundedCount 일치하는 상품 수 (정확도 포함) */ - public function countByKeyword(string $keyword, ?int $categoryId = null): int; + public function countByKeyword(string $keyword, ?int $categoryId = null): BoundedCount; /** * 상품 재고를 옵션 재고 합계와 동기화 diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponIssueRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponIssueRepository.php index 874dd4b5..734efe08 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponIssueRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponIssueRepository.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\PaginationLimits; use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -179,6 +180,7 @@ class CouponIssueRepository implements CouponIssueRepositoryInterface // 제3자 확장이 쿠폰 원본 정의(쿠폰명·할인율·사용조건)를 읽고 있을 수 있어 현행 유지 // (PO 결정 2026-08-04). 번들 템플릿에는 아직 대응 마이페이지 화면이 없다. relations: ['coupon'], + resultCap: PaginationLimits::resultCap('ecommerce.coupon_issues'), ); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponRepository.php index ca45dd6b..33745ee8 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/CouponRepository.php @@ -3,8 +3,12 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Helpers\PermissionHelper; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; +use App\Search\KeywordSearch; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -20,6 +24,7 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\CouponRepositoryInterface; */ class CouponRepository implements CouponRepositoryInterface { + use FiltersByDateRange; use PaginatesWithDeferredJoin; use ResolvesSortSpec; @@ -55,20 +60,19 @@ class CouponRepository implements CouponRepositoryInterface // all → FULLTEXT(name+description) + creator 보조필드 union (Scout queryCallback total // 재계산 시 orWhereHas('creator') 가 MATCH 절 없이 재적용되어 total=0 이 되는 결함 회피) if ($searchField === 'all') { - $ftIds = Coupon::search($keyword)->keys()->all(); + // FULLTEXT 매칭과 작성자 보조 매칭을 하나의 WHERE 그룹으로 묶어 페이지 쿼리에 + // 직접 넣는다. 종전에는 매칭 ID 전량을 PHP 로 끌어와 whereIn 으로 되먹여, + // 매칭이 많을수록 메모리와 IN(...) 목록이 함께 커졌다. + $query->where(function ($group) use ($keyword) { + // 정제는 코어 헬퍼가 단독 수행 — BOOLEAN MODE 연산자 입력이 500 이 되지 않는다. + // 쿠폰 테이블도 컬럼별 단일 FULLTEXT 인덱스다 — 복합 MATCH 는 쓸 수 없다. + KeywordSearch::applyAny($group, ['name', 'description'], $keyword); - $auxIds = $this->model->newQuery() - ->whereHas('creator', function ($creatorQuery) use ($keyword) { + $group->orWhereHas('creator', function ($creatorQuery) use ($keyword) { $creatorQuery->where('name', 'like', "%{$keyword}%") ->orWhere('email', 'like', "%{$keyword}%"); - }) - ->pluck('id') - ->all(); - - $matchedIds = array_values(array_unique([...$ftIds, ...$auxIds])); - - // 매칭 ID 로 조건 한정 (빈 매칭은 존재하지 않는 ID 로 빈 결과 → total=0 이 정상) - $query->whereIn('id', $matchedIds ?: [0]); + }); + }); } elseif ($searchField === 'name') { // 컬럼 한정 검색 — Scout(FULLTEXT)는 컬럼 한정 미지원이므로 단일컬럼 LIKE $query->where('name', 'like', "%{$keyword}%"); @@ -111,7 +115,14 @@ class CouponRepository implements CouponRepositoryInterface // audit:allow repository-paginate-column-pruning reason: 쿠폰 "정의" 목록 — // 행 수가 운영자가 만든 쿠폰 수에 묶인다(발급 이력은 별도 테이블). description 외에 넓은 컬럼이 없다 - return $query->paginate($perPage); + // + // 키워드 검색은 FULLTEXT + creator 보조 매칭이라 매칭 수가 데이터 증가에 비례한다. + // 총 건수는 상한까지만 세고, "다음" 이동은 per_page + 1 실측으로 끝까지 열어 둔다. + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + resultCap: PaginationLimits::resultCap('ecommerce.coupons'), + ); } /** @@ -199,6 +210,7 @@ class CouponRepository implements CouponRepositoryInterface perPage: $perPage, // 관계 로드 (사용처 주문번호 표시를 위해 order 도 로드) relations: ['user:id,uuid,name', 'order:id,order_number'], + resultCap: PaginationLimits::resultCap('ecommerce.coupon_issues'), ); } @@ -331,26 +343,26 @@ class CouponRepository implements CouponRepositoryInterface // 등록일 필터 if (! empty($filters['created_start_date'])) { - $query->whereDate('created_at', '>=', $filters['created_start_date']); + $this->applyDateRangeFilter($query, 'created_at', $filters['created_start_date'], null); } if (! empty($filters['created_end_date'])) { - $query->whereDate('created_at', '<=', $filters['created_end_date']); + $this->applyDateRangeFilter($query, 'created_at', null, $filters['created_end_date']); } // 유효기간 필터 if (! empty($filters['valid_start_date'])) { - $query->whereDate('valid_from', '>=', $filters['valid_start_date']); + $this->applyDateRangeFilter($query, 'valid_from', $filters['valid_start_date'], null); } if (! empty($filters['valid_end_date'])) { - $query->whereDate('valid_to', '<=', $filters['valid_end_date']); + $this->applyDateRangeFilter($query, 'valid_to', null, $filters['valid_end_date']); } // 발급기간 필터 if (! empty($filters['issue_start_date'])) { - $query->whereDate('issue_from', '>=', $filters['issue_start_date']); + $this->applyDateRangeFilter($query, 'issue_from', $filters['issue_start_date'], null); } if (! empty($filters['issue_end_date'])) { - $query->whereDate('issue_to', '<=', $filters['issue_end_date']); + $this->applyDateRangeFilter($query, 'issue_to', null, $filters['issue_end_date']); } // 등록자 필터 (UUID → 정수 FK 변환) diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/MileageTransactionRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/MileageTransactionRepository.php index 76b8a6e9..d7dbd913 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/MileageTransactionRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/MileageTransactionRepository.php @@ -4,6 +4,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Models\User; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\PaginationLimits; use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; @@ -62,6 +63,9 @@ class MileageTransactionRepository implements MileageTransactionRepositoryInterf public function getActiveLotsForUpdate(int $userId, string $currency): Collection { // 잠금 순서 고정 — 항상 트랜잭션 내에서 호출 (만료 임박 순) + // audit:allow query-unbounded-get reason: 차감 대상 lot 을 만료 임박 순으로 소진해야 + // 하므로 잔액이 있는 활성 lot 전량이 한 트랜잭션에 필요하다. 상한을 걸면 뒤쪽 lot 이 + // 차감에서 누락돼 잔액이 틀어진다. 모수는 한 사용자·한 통화의 미소진 lot 으로 한정된다 return MileageTransaction::query() ->forUserCurrency($userId, $currency) ->active() @@ -261,6 +265,7 @@ class MileageTransactionRepository implements MileageTransactionRepositoryInterf outerUsing: fn (Builder $outer) => $outer ->addSelect('*') ->addSelect(['expired_amount' => $expiredAmount]), + resultCap: PaginationLimits::resultCap('admin.mileage_transactions'), ); } @@ -318,6 +323,7 @@ class MileageTransactionRepository implements MileageTransactionRepositoryInterf columns: ['*'], sort: [['column' => 'id', 'direction' => 'desc']], perPage: $perPage, + resultCap: PaginationLimits::resultCap('admin.mileage_transactions'), ); } @@ -447,6 +453,9 @@ class MileageTransactionRepository implements MileageTransactionRepositoryInterf */ public function getActiveLotsForUser(int $userId): Collection { + // audit:allow query-unbounded-get reason: 한 사용자의 잔액 산정용 활성 lot 전량 — + // 잔액은 미소진 lot 의 합이라 일부만 읽으면 값이 틀린다. 소진된 lot 은 active() + // 스코프에서 빠지므로 모수가 사용 이력에 비례해 늘지 않는다 return MileageTransaction::query() ->where('user_id', $userId) ->active() diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderOptionRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderOptionRepository.php index af17e316..ee80dc99 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderOptionRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderOptionRepository.php @@ -2,6 +2,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; +use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Modules\Sirsoft\Ecommerce\Enums\OrderStatusEnum; @@ -220,6 +221,9 @@ class OrderOptionRepository implements OrderOptionRepositoryInterface return $this->model->newQuery() ->join($ordersTable, "{$ordersTable}.id", '=', "{$optionsTable}.order_id") ->whereNull("{$ordersTable}.deleted_at") - ->whereDate("{$ordersTable}.ordered_at", $date); + ->whereBetween("{$ordersTable}.ordered_at", [ + CarbonImmutable::parse($date)->startOfDay(), + CarbonImmutable::parse($date)->endOfDay(), + ]); } } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderRepository.php index 0f367e31..9abf1c48 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/OrderRepository.php @@ -2,12 +2,15 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; +use App\Contracts\Extension\CacheInterface; use App\Helpers\PermissionHelper; use App\Models\ActivityLog; use App\Models\User; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; use App\Repositories\Concerns\SortsByRelatedColumn; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -26,6 +29,17 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\OrderRepositoryInterface; */ class OrderRepository implements OrderRepositoryInterface { + /** + * 주문 통계 캐시 키 + */ + private const STATISTICS_CACHE_KEY = 'ecommerce.orders.statistics'; + + /** + * 주문 통계 캐시 태그 + */ + private const STATISTICS_CACHE_TAG = 'ecommerce.orders'; + + use FiltersByDateRange; use PaginatesWithDeferredJoin; use ResolvesSortSpec; use SortsByRelatedColumn; @@ -262,12 +276,14 @@ class OrderRepository implements OrderRepositoryInterface if (! empty($filters['date_type']) && (! empty($filters['start_date']) || ! empty($filters['end_date']))) { $dateField = $filters['date_type']; // ordered_at, paid_at, etc. - if (! empty($filters['start_date'])) { - $query->whereDate($dateField, '>=', $filters['start_date']); - } - if (! empty($filters['end_date'])) { - $query->whereDate($dateField, '<=', $filters['end_date']); - } + // whereDate 는 컬럼에 DATE() 를 씌워 인덱스를 무력화한다 — 같은 결과를 내는 + // 범위 조건으로 준다 (종료일은 그날 끝까지 포함). + $this->applyDateRangeFilter( + $query, + $dateField, + $filters['start_date'] ?? null, + $filters['end_date'] ?? null + ); } // 주문상태 필터 (다중 선택 가능) @@ -410,6 +426,7 @@ class OrderRepository implements OrderRepositoryInterface OrderStatusEnum::CANCELLED->value, ), ], + resultCap: PaginationLimits::resultCap('admin.orders'), ); } @@ -418,7 +435,10 @@ class OrderRepository implements OrderRepositoryInterface */ public function create(array $data): Order { - return $this->model->create($data); + $order = $this->model->create($data); + $this->forgetStatisticsCache(); + + return $order; } /** @@ -427,6 +447,7 @@ class OrderRepository implements OrderRepositoryInterface public function update(Order $order, array $data): Order { $order->update($data); + $this->forgetStatisticsCache(); return $order->fresh(); } @@ -436,7 +457,10 @@ class OrderRepository implements OrderRepositoryInterface */ public function delete(Order $order): bool { - return $order->delete(); + $deleted = $order->delete(); + $this->forgetStatisticsCache(); + + return $deleted; } /** @@ -444,12 +468,16 @@ class OrderRepository implements OrderRepositoryInterface */ public function bulkUpdateStatus(array $ids, string $status): int { - return $this->model + $affected = $this->model ->whereIn('id', $ids) ->update([ 'order_status' => $status, 'updated_at' => now(), ]); + + $this->forgetStatisticsCache(); + + return $affected; } /** @@ -521,6 +549,48 @@ class OrderRepository implements OrderRepositoryInterface * {@inheritDoc} */ public function getStatistics(): array + { + // 이 통계는 주문 목록 화면과 함께 매 페이지 조회된다. 값이 초 단위로 달라질 필요는 + // 없으므로 짧게 캐시해 같은 집계가 페이지를 넘길 때마다 다시 돌지 않게 한다. + // 주문이 바뀌면 즉시 무효화되므로 화면이 낡은 수치를 보여 주지 않는다. + if (! g7_core_settings('cache.stats_enabled', true)) { + return $this->computeStatistics(); + } + + $ttl = (int) g7_core_settings('cache.stats_ttl', 1800); + + if ($ttl <= 0) { + return $this->computeStatistics(); + } + + return app(CacheInterface::class)->remember( + self::STATISTICS_CACHE_KEY, + fn () => $this->computeStatistics(), + $ttl, + [self::STATISTICS_CACHE_TAG] + ); + } + + /** + * 주문 통계 캐시를 무효화합니다. + * + * 주문이 생성·수정·삭제되면 통계도 곧바로 달라져야 합니다. 쓰기 경로가 이 메서드를 + * 부르지 않으면 화면이 TTL 이 끝날 때까지 이전 수치를 보여 줍니다. + */ + public function forgetStatisticsCache(): void + { + app(CacheInterface::class)->forget(self::STATISTICS_CACHE_KEY); + } + + /** + * 주문 통계를 실제로 집계합니다. + * + * 날짜 조건은 전부 범위 조건으로 준다 — whereDate/whereYear/whereMonth 는 컬럼에 + * 함수를 씌워 ordered_at 인덱스를 쓸 수 없게 만든다. + * + * @return array 주문 통계 + */ + private function computeStatistics(): array { // 숨김 상태(PENDING_ORDER 등) 제외한 전체 통계 (OrderStatusEnum::listHiddenValues SSoT) $total = $this->model @@ -536,22 +606,21 @@ class OrderRepository implements OrderRepositoryInterface ->toArray(); // 오늘 주문 수 - $todayCount = $this->model - ->whereDate('ordered_at', today()) - ->count(); + $todayCountQuery = $this->model->newQuery(); + $this->applyDayFilter($todayCountQuery, 'ordered_at', today()); + $todayCount = $todayCountQuery->count(); // 오늘 매출액 - $todayRevenue = $this->model - ->whereDate('ordered_at', today()) - ->whereNotIn('order_status', [OrderStatusEnum::CANCELLED->value]) - ->sum('total_paid_amount'); + $todayRevenueQuery = $this->model->newQuery() + ->whereNotIn('order_status', [OrderStatusEnum::CANCELLED->value]); + $this->applyDayFilter($todayRevenueQuery, 'ordered_at', today()); + $todayRevenue = $todayRevenueQuery->sum('total_paid_amount'); // 이번 달 매출액 - $monthlyRevenue = $this->model - ->whereYear('ordered_at', now()->year) - ->whereMonth('ordered_at', now()->month) - ->whereNotIn('order_status', [OrderStatusEnum::CANCELLED->value]) - ->sum('total_paid_amount'); + $monthlyRevenueQuery = $this->model->newQuery() + ->whereNotIn('order_status', [OrderStatusEnum::CANCELLED->value]); + $this->applyMonthFilter($monthlyRevenueQuery, 'ordered_at', (int) now()->year, (int) now()->month); + $monthlyRevenue = $monthlyRevenueQuery->sum('total_paid_amount'); return [ 'total' => $total, @@ -666,12 +735,13 @@ class OrderRepository implements OrderRepositoryInterface if (! empty($filters['date_type']) && (! empty($filters['start_date']) || ! empty($filters['end_date']))) { $dateField = $filters['date_type']; - if (! empty($filters['start_date'])) { - $query->whereDate($dateField, '>=', $filters['start_date']); - } - if (! empty($filters['end_date'])) { - $query->whereDate($dateField, '<=', $filters['end_date']); - } + // whereDate 는 인덱스를 무력화한다 — 범위 조건으로 준다. + $this->applyDateRangeFilter( + $query, + $dateField, + $filters['start_date'] ?? null, + $filters['end_date'] ?? null + ); } // 주문상태 필터 @@ -740,6 +810,18 @@ class OrderRepository implements OrderRepositoryInterface return Order::whereIn('id', $ids)->get()->keyBy('id'); } + /** + * {@inheritDoc} + */ + public function findByIdsWithRelationsKeyed(array $ids, array $with = []): Collection + { + if (empty($ids)) { + return new Collection; + } + + return Order::with($with)->whereIn('id', $ids)->get()->keyBy('id'); + } + /** * {@inheritDoc} */ @@ -790,6 +872,7 @@ class OrderRepository implements OrderRepositoryInterface columns: ['*'], sort: [['column' => 'created_at', 'direction' => $sortOrder]], perPage: $perPage, + resultCap: PaginationLimits::resultCap('admin.activity_logs'), ); } } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductAdditionalOptionValueRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductAdditionalOptionValueRepository.php index a5b4ca8b..a6cd3d3a 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductAdditionalOptionValueRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductAdditionalOptionValueRepository.php @@ -38,12 +38,29 @@ class ProductAdditionalOptionValueRepository implements ProductAdditionalOptionV */ public function getActiveByProductKeyed(int $productId): Collection { + return $this->getActiveByProductIdsKeyed([$productId])->get($productId) ?? new Collection; + } + + /** + * {@inheritDoc} + */ + public function getActiveByProductIdsKeyed(array $productIds): Collection + { + $productIds = array_values(array_unique(array_filter(array_map('intval', $productIds)))); + + if (empty($productIds)) { + return new Collection; + } + + // 상품별로 한 번씩 조회하면 장바구니 항목 수만큼 쿼리가 난다. 한 번에 읽고 + // 상품 ID 로 묶어 돌려준다 — 호출자는 상품별 맵을 그대로 쓴다. return $this->model ->with('additionalOption') ->where('is_active', true) - ->whereHas('additionalOption', fn ($query) => $query->where('product_id', $productId)) + ->whereHas('additionalOption', fn ($query) => $query->whereIn('product_id', $productIds)) ->get() - ->keyBy('id'); + ->groupBy(fn ($value) => (int) $value->additionalOption?->product_id) + ->map(fn (Collection $values) => $values->keyBy('id')); } /** diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductInquiryRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductInquiryRepository.php index 7472d009..37bd4fd0 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductInquiryRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductInquiryRepository.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Carbon; @@ -33,6 +34,9 @@ class ProductInquiryRepository implements ProductInquiryRepositoryInterface */ public function findByProductId(int $productId): Collection { + // audit:allow query-unbounded-get reason: 상품 삭제 시 딸린 문의를 함께 정리하기 위한 + // 내부 조회다. 상한을 걸면 남은 문의가 고아로 남는다. 화면용 목록은 같은 클래스의 + // paginateByProductId() 가 담당한다 return $this->model->newQuery() ->where('product_id', $productId) ->orderBy('created_at', 'desc') @@ -144,6 +148,7 @@ class ProductInquiryRepository implements ProductInquiryRepositoryInterface sort: [['column' => 'created_at', 'direction' => 'desc']], perPage: $perPage, relations: ['product', 'user'], + resultCap: PaginationLimits::resultCap('admin.product_inquiries'), ); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductOptionRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductOptionRepository.php index 7d803785..3d26338b 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductOptionRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductOptionRepository.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection as SupportCollection; use Modules\Sirsoft\Ecommerce\Models\ProductOption; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductOptionRepositoryInterface; @@ -47,6 +48,30 @@ class ProductOptionRepository implements ProductOptionRepositoryInterface return $this->model->where('product_id', $productId)->get(); } + /** + * 여러 상품의 옵션을 한 번에 조회해 상품별로 묶어 반환합니다. + * + * 항목마다 getByProductId 를 부르면 항목 수만큼 쿼리가 난다. 체크아웃처럼 여러 + * 항목을 한꺼번에 다루는 경로는 이 메서드로 한 번에 읽는다. + * + * @param array $productIds 상품 ID 목록 + * @return SupportCollection 상품 ID ⇒ 옵션 컬렉션 + */ + public function getByProductIds(array $productIds): SupportCollection + { + $productIds = array_values(array_unique(array_filter($productIds))); + + if (empty($productIds)) { + return new SupportCollection; + } + + // groupBy 는 Support\Collection 을 돌려준다 (Eloquent\Collection 이 아니다). + return $this->model + ->whereIn('product_id', $productIds) + ->get() + ->groupBy('product_id'); + } + /** * 옵션 가격 조정액(price_adjustment) 일괄 변경 * diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductRepository.php index 9e22da56..7939163f 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductRepository.php @@ -2,20 +2,30 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; +use App\Contracts\Extension\CacheInterface; use App\Helpers\PermissionHelper; use App\Models\ActivityLog; +use App\Repositories\Concerns\FiltersByDateRange; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; -use App\Search\Engines\DatabaseFulltextEngine; +use App\Search\KeywordSearch; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\KeysetPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Support\Facades\DB; use Modules\Sirsoft\Ecommerce\Enums\ProductDisplayStatus; +use Modules\Sirsoft\Ecommerce\Enums\ReviewStatus; use Modules\Sirsoft\Ecommerce\Models\Category; use Modules\Sirsoft\Ecommerce\Models\OrderOption; use Modules\Sirsoft\Ecommerce\Models\Product; use Modules\Sirsoft\Ecommerce\Models\ProductOption; +use Modules\Sirsoft\Ecommerce\Models\ProductReview; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductRepositoryInterface; /** @@ -23,12 +33,19 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductRepositoryInterface; */ class ProductRepository implements ProductRepositoryInterface { + use FiltersByDateRange; use PaginatesWithDeferredJoin; use ResolvesSortSpec; /** 허용 정렬 컬럼 (ProductListRequest 와 동일 집합) */ private const ADMIN_SORTABLE_COLUMNS = ['created_at', 'updated_at', 'selling_price', 'stock_quantity', 'name']; + /** 상품 통계 캐시 키 */ + private const STATISTICS_CACHE_KEY = 'ecommerce.products.statistics'; + + /** 상품 통계 캐시 태그 */ + private const STATISTICS_CACHE_TAG = 'ecommerce.products'; + public function __construct( protected Product $model ) {} @@ -118,36 +135,15 @@ class ProductRepository implements ProductRepositoryInterface $keyword = $filters['search_keyword']; $field = $filters['search_field'] ?? 'all'; - // FULLTEXT 대상 필드(all/name/description)는 Scout 검색 ID 와 보조필드 LIKE 매칭 ID 의 - // 합집합(union)을 미리 산출한 뒤 plain Eloquent whereIn 으로 조회한다. - // (total 계산 경로와 결과 조회 경로가 동일한 검색 조건을 보장 — Scout queryCallback - // total 재계산 시 보조필드 OR 가 MATCH 절 없이 재적용되어 total=0 이 되는 결함 회피) + // FULLTEXT 대상 필드(all/name/description)는 FULLTEXT 매칭과 보조필드 LIKE 를 + // 하나의 WHERE 그룹으로 묶어 페이지 쿼리에 직접 넣는다. + // + // 종전에는 매칭 ID 전량을 PHP 로 끌어와 whereIn 으로 되먹였다. 그 방식은 매칭이 + // 많을수록 PHP 메모리와 IN(...) 목록이 함께 커지고, 조회 전에 전 매칭을 한 번 + // 훑는 왕복이 추가된다. 술어를 그대로 밀어넣으면 왕복도 목록 폭발도 없고, + // total 계산 경로와 결과 조회 경로가 같은 술어를 공유한다는 성질도 유지된다. if (in_array($field, ['all', 'name', 'description'])) { - // FULLTEXT 매칭 ID (Scout 엔진 순수 검색 — queryCallback 없음) - $ftIds = Product::search($keyword)->keys()->all(); - - // 보조필드(id/product_code/sku/barcode) LIKE 매칭 ID (all 일 때만) - $auxIds = []; - if ($field === 'all') { - $auxIds = $this->model->newQuery() - ->where(function ($q) use ($keyword) { - $q->where('product_code', 'like', "%{$keyword}%") - ->orWhere('sku', 'like', "%{$keyword}%") - ->orWhere('barcode', 'like', "%{$keyword}%"); - - // 숫자 키워드는 상품 ID 정확 매칭도 포함 (예: "99" → id=99) - if (ctype_digit($keyword)) { - $q->orWhere('id', (int) $keyword); - } - }) - ->pluck('id') - ->all(); - } - - $matchedIds = array_values(array_unique([...$ftIds, ...$auxIds])); - - // 매칭 ID 로 조건 한정 (빈 매칭은 존재하지 않는 ID 로 빈 결과 → total=0 이 정상) - $query->whereIn('id', $matchedIds ?: [0]); + $this->applyKeywordMatch($query, $keyword, includeAuxFields: $field === 'all'); // 필터 적용 $this->applyAdminFilters($query, $filters); @@ -160,6 +156,7 @@ class ProductRepository implements ProductRepositoryInterface relations: $listRelations, withCount: $listWithCount, outerUsing: $listOuterUsing, + resultCap: PaginationLimits::resultCap('admin.products'), ); } @@ -188,6 +185,7 @@ class ProductRepository implements ProductRepositoryInterface relations: $listRelations, withCount: $listWithCount, outerUsing: $listOuterUsing, + resultCap: PaginationLimits::resultCap('admin.products'), ); } @@ -243,7 +241,10 @@ class ProductRepository implements ProductRepositoryInterface */ public function create(array $data): Product { - return $this->model->create($data); + $product = $this->model->create($data); + $this->forgetStatisticsCache(); + + return $product; } /** @@ -252,6 +253,7 @@ class ProductRepository implements ProductRepositoryInterface public function update(Product $product, array $data): Product { $product->update($data); + $this->forgetStatisticsCache(); return $product->fresh(); } @@ -261,7 +263,10 @@ class ProductRepository implements ProductRepositoryInterface */ public function delete(Product $product): bool { - return $product->delete(); + $deleted = $product->delete(); + $this->forgetStatisticsCache(); + + return $deleted; } /** @@ -269,7 +274,10 @@ class ProductRepository implements ProductRepositoryInterface */ public function forceDelete(Product $product): bool { - return (bool) $product->forceDelete(); + $deleted = (bool) $product->forceDelete(); + $this->forgetStatisticsCache(); + + return $deleted; } /** @@ -289,13 +297,17 @@ class ProductRepository implements ProductRepositoryInterface */ public function bulkUpdateStatus(array $ids, string $field, string $value): int { - return $this->model + $affected = $this->model ->whereIn('id', $ids) ->update([ $field => $value, 'updated_by' => auth()->id(), 'updated_at' => now(), ]); + + $this->forgetStatisticsCache(); + + return $affected; } /** @@ -303,24 +315,59 @@ class ProductRepository implements ProductRepositoryInterface */ public function bulkUpdatePrice(array $ids, string $method, float $value, string $unit): int { - $updatedCount = 0; + if (empty($ids)) { + return 0; + } - DB::transaction(function () use ($ids, $method, $value, $unit, &$updatedCount) { - $products = $this->model->whereIn('id', $ids)->get(); - - foreach ($products as $product) { - $currentPrice = $product->selling_price; - $newPrice = $this->calculateNewPrice($currentPrice, $method, $value, $unit); - - $product->update([ - 'selling_price' => max(0, $newPrice), + // 'set' 은 새 값이 행마다 같으므로 단일 statement 로 끝난다. + if ($method === 'set') { + $affected = $this->model + ->whereIn('id', $ids) + ->update([ + 'selling_price' => max(0, $value), 'updated_by' => auth()->id(), + 'updated_at' => now(), ]); - $updatedCount++; - } - }); - return $updatedCount; + $this->forgetStatisticsCache(); + + return $affected; + } + + // 증감·비율은 현재 값에 의존하지만 그 계산도 SQL 이 할 수 있다. + // 행마다 update() 를 부르면 대상 수만큼 UPDATE 문이 나가므로, 표현식 하나로 합친다. + // (같은 파일의 bulkUpdateFields 가 쓰는 단일 statement 방식과 같은 결) + // + // 표현식 안의 값은 플레이스홀더로 두지 않는다 — update() 는 값 배열 하나만 받고 + // 표현식에 딸린 바인딩을 실을 통로가 없다. 남겨 두면 WHERE 바인딩이 SET 절로 + // 밀려 엉뚱한 컬럼에 기록되거나 파라미터 수 불일치로 실패한다. + $amount = $this->sqlDecimal($value); + + $expression = match ($method) { + 'increase' => $unit === 'percent' + ? 'selling_price * (1 + '.$amount.' / 100)' + : 'selling_price + '.$amount, + 'decrease' => $unit === 'percent' + ? 'selling_price * (1 - '.$amount.' / 100)' + : 'selling_price - '.$amount, + default => null, + }; + + if ($expression === null) { + return 0; + } + + $affected = $this->model + ->whereIn('id', $ids) + ->update([ + 'selling_price' => DB::raw('GREATEST(0, '.$expression.')'), + 'updated_by' => auth()->id(), + 'updated_at' => now(), + ]); + + $this->forgetStatisticsCache(); + + return $affected; } /** @@ -328,29 +375,51 @@ class ProductRepository implements ProductRepositoryInterface */ public function bulkUpdateStock(array $ids, string $method, int $value): int { - $updatedCount = 0; + if (empty($ids)) { + return 0; + } - DB::transaction(function () use ($ids, $method, $value, &$updatedCount) { - $products = $this->model->whereIn('id', $ids)->get(); + // 행마다 조회 후 update() 를 부르면 대상 수만큼 UPDATE 문이 나간다. + // 재고 증감은 SQL 표현식으로 표현되므로 단일 statement 로 합친다. + // 수량은 정수로 강제되므로 표현식에 그대로 넣는다 (바인딩 불가 사유는 bulkUpdatePrice 참조). + $quantity = (int) $value; - foreach ($products as $product) { - $currentStock = $product->stock_quantity; - $newStock = match ($method) { - 'increase' => $currentStock + $value, - 'decrease' => $currentStock - $value, - 'set' => $value, - default => $currentStock, - }; + $newStock = match ($method) { + 'increase' => DB::raw('GREATEST(0, stock_quantity + '.$quantity.')'), + 'decrease' => DB::raw('GREATEST(0, stock_quantity - '.$quantity.')'), + 'set' => max(0, $value), + default => null, + }; - $product->update([ - 'stock_quantity' => max(0, $newStock), - 'updated_by' => auth()->id(), - ]); - $updatedCount++; - } - }); + if ($newStock === null) { + return 0; + } - return $updatedCount; + $affected = $this->model + ->whereIn('id', $ids) + ->update([ + 'stock_quantity' => $newStock, + 'updated_by' => auth()->id(), + 'updated_at' => now(), + ]); + + $this->forgetStatisticsCache(); + + return $affected; + } + + /** + * 실수를 SQL 표현식에 넣을 십진 리터럴로 만듭니다. + * + * 인자가 float 로 강제되므로 주입 여지는 없고, 지수 표기(1.0E-5)로 변환되면 + * SQL 이 해석하지 못하므로 고정 소수점으로 고정합니다. + * + * @param float $value 변환할 값 + * @return string 십진 리터럴 문자열 + */ + private function sqlDecimal(float $value): string + { + return number_format($value, 4, '.', ''); } /** @@ -371,6 +440,45 @@ class ProductRepository implements ProductRepositoryInterface * {@inheritDoc} */ public function getStatistics(): array + { + // 상품 목록 화면과 함께 매 페이지 조회되는 집계 5종이다. 값이 초 단위로 달라질 + // 필요는 없으므로 짧게 캐시하고, 상품이 바뀌면 즉시 무효화한다 + // (주문 통계와 같은 규약 — cache.stats_enabled / cache.stats_ttl). + if (! g7_core_settings('cache.stats_enabled', true)) { + return $this->computeStatistics(); + } + + $ttl = (int) g7_core_settings('cache.stats_ttl', 1800); + + if ($ttl <= 0) { + return $this->computeStatistics(); + } + + return app(CacheInterface::class)->remember( + self::STATISTICS_CACHE_KEY, + fn () => $this->computeStatistics(), + $ttl, + [self::STATISTICS_CACHE_TAG] + ); + } + + /** + * 상품 통계 캐시를 무효화합니다. + * + * 상품이 생성·수정·삭제되면 통계도 곧바로 달라져야 합니다. 쓰기 경로가 이 메서드를 + * 부르지 않으면 화면이 TTL 이 끝날 때까지 이전 수치를 보여 줍니다. + */ + public function forgetStatisticsCache(): void + { + app(CacheInterface::class)->forget(self::STATISTICS_CACHE_KEY); + } + + /** + * 상품 통계를 실제로 집계합니다. + * + * @return array 상품 통계 + */ + private function computeStatistics(): array { $total = $this->model->count(); @@ -485,9 +593,13 @@ class ProductRepository implements ProductRepositoryInterface $fields['updated_at'] = now(); $fields['updated_by'] = auth()->id(); - return $this->model + $affected = $this->model ->whereIn('id', $ids) ->update($fields); + + $this->forgetStatisticsCache(); + + return $affected; } /** @@ -495,9 +607,9 @@ class ProductRepository implements ProductRepositoryInterface */ public function getPublicList(array $filters, int $perPage = 20): LengthAwarePaginator { + // 리뷰 건수·평점은 파생 테이블 조인으로 한 번에 얻는다. 목록 경로는 지연 조인을 + // 쓰므로 집계를 outer 에서만 붙인다 (아래 outerUsing). $query = $this->model->newQuery() - ->withCount('visibleReviews as review_count') - ->withAvg('visibleReviews as rating_avg', 'rating') ->where('display_status', 'visible'); // 지연 조인 outer 전용 관계 (inner 는 id 만 훑으므로 with() 를 걸지 않는다) @@ -528,7 +640,7 @@ class ProductRepository implements ProductRepositoryInterface // 키워드 검색 (FULLTEXT) if (! empty($filters['search'])) { $keyword = $filters['search']; - DatabaseFulltextEngine::whereFulltext($query, 'name', $keyword); + KeywordSearch::apply($query, 'name', $keyword); } // 가격 범위 필터 (소수 통화 대응 — float 비교) @@ -548,19 +660,32 @@ class ProductRepository implements ProductRepositoryInterface // 정렬 $sort = $filters['sort'] ?? 'latest'; - // 판매량 정렬은 집계 서브쿼리(total_sold) 를 select 에 올려 두고 그 별칭으로 정렬한다. - // 지연 조인의 inner 는 select 를 id 하나로 교체하므로 별칭이 사라져 성립하지 않는다. + // 판매량 정렬. + // + // 종전에는 상관 서브쿼리를 SELECT 에 올리고 그 별칭으로 정렬했다. 상관 서브쿼리가 + // ORDER BY 에 걸리면 LIMIT 이 평가 범위를 막지 못한다 — 정렬 순서를 정하려면 모든 + // 후보 행에 대해 서브쿼리를 한 번씩 돌려야 하므로, 한 페이지만 필요해도 전 상품에 + // 대해 집계가 실행된다. 미리 묶은 파생 테이블을 LEFT JOIN 하면 집계가 한 번만 + // 수행되고 조인 결과를 정렬한다. + // + // 파생 테이블은 product_id 로 그룹지어 상품당 한 행이므로 원 행이 부풀지 않는다. if ($sort === 'sales') { - $query - ->addSelect(['total_sold' => OrderOption::selectRaw('COALESCE(SUM(quantity), 0)') - ->whereColumn('product_id', 'ecommerce_products.id'), - ]) - ->orderByDesc('total_sold') + // 판매량 정렬은 지연 조인을 쓰지 않으므로 리뷰 집계도 여기서 함께 붙인다. + // 두 파생 테이블 모두 product_id 로 그룹지어 상품당 한 행이라 원 행이 부풀지 않는다. + $this->joinReviewAggregate($query); + + $this->joinSalesAggregate($query) + ->orderByDesc($this->salesAlias().'.total_sold') ->with($listRelations); - // audit:allow repository-paginate-column-pruning reason: 판매량 정렬은 집계 서브쿼리 - // 별칭 기준이라 지연 조인 inner(=id 단일 select)로 옮길 수 없다. 나머지 정렬은 지연 조인 적용 - return $query->paginate($perPage); + // audit:allow repository-paginate-column-pruning reason: 판매량 정렬은 조인한 + // 집계 별칭 기준이라 지연 조인 inner(=id 단일 select)로 옮길 수 없다. + // 나머지 정렬은 지연 조인 적용 + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + resultCap: PaginationLimits::resultCap('shop.products'), + ); } $sortSpec = match ($sort) { @@ -575,6 +700,8 @@ class ProductRepository implements ProductRepositoryInterface sort: $sortSpec, perPage: $perPage, relations: $listRelations, + outerUsing: $this->reviewAggregateForOuter(), + resultCap: PaginationLimits::resultCap('shop.products'), ); } @@ -585,31 +712,175 @@ class ProductRepository implements ProductRepositoryInterface { $thirtyDaysAgo = now()->subDays(30); - return $this->model->newQuery() + // 판매량 정렬과 같은 이유로 파생 테이블 조인을 쓴다 — 상관 서브쿼리를 ORDER BY 에 + // 두면 LIMIT 이 있어도 전 상품에 대해 집계가 실행된다. + $query = $this->model->newQuery() ->with(['images', 'categories', 'activeLabelAssignments.label']) - ->withCount('visibleReviews as review_count') - ->withAvg('visibleReviews as rating_avg', 'rating') - ->where('display_status', 'visible') - ->addSelect(['recent_sold' => OrderOption::selectRaw('COALESCE(SUM(quantity), 0)') - ->whereColumn('product_id', 'ecommerce_products.id') - ->where('created_at', '>=', $thirtyDaysAgo), - ]) - ->orderByDesc('recent_sold') + ->where('display_status', 'visible'); + + $this->joinReviewAggregate($query); + + return $this->joinSalesAggregate($query, $thirtyDaysAgo) + ->orderByDesc($this->salesAlias().'.total_sold') ->limit($limit) ->get(); } + /** + * 상품별 판매 수량 집계를 파생 테이블로 조인합니다. + * + * 상관 서브쿼리 대신 미리 그룹지은 결과를 LEFT JOIN 합니다. 집계가 상품 수만큼 + * 반복 실행되지 않고, product_id 기준 그룹이라 원 행이 부풀지 않습니다. + * + * 판매 이력이 없는 상품은 조인 결과가 없으므로 정렬에서 자연스럽게 뒤로 밀립니다. + * + * @param Builder $query 대상 쿼리 + * @param \DateTimeInterface|null $since 집계 시작 시점 (null 이면 전체 기간) + * @return Builder 조인이 적용된 쿼리 + */ + private function joinSalesAggregate($query, ?\DateTimeInterface $since = null) + { + $alias = $this->salesAlias(); + $productsTable = $this->model->getTable(); + + $aggregate = OrderOption::query() + ->select('product_id') + ->selectRaw('SUM(quantity) as total_sold') + ->when($since !== null, fn ($q) => $q->where('created_at', '>=', $since)) + ->groupBy('product_id'); + + return $this->ensureBaseSelect($query) + ->leftJoinSub( + $aggregate, + $alias, + fn ($join) => $join->on($alias.'.product_id', '=', $productsTable.'.id') + ) + ->addSelect($alias.'.total_sold'); + } + + /** + * {@inheritDoc} + */ + public function findByIdsWithRelationsKeyed(array $ids, array $relations = []): Collection + { + $ids = array_values(array_unique(array_filter(array_map('intval', $ids)))); + + if (empty($ids)) { + return $this->model->newCollection(); + } + + // 항목마다 find() 를 부르면 항목 수만큼 쿼리가 난다. 한 번에 읽고 ID 로 키를 잡는다. + return $this->model->newQuery() + ->when(! empty($relations), fn ($q) => $q->with($relations)) + ->whereIn('id', $ids) + ->get() + ->keyBy('id'); + } + + /** + * 조인 전에 상품 테이블 기준 SELECT 를 한 번만 고정합니다. + * + * 조인이 붙은 쿼리에서 `*` 는 조인 대상의 컬럼까지 끌어와 별칭이 충돌합니다. + * 그렇다고 집계 헬퍼마다 `select()` 를 부르면 나중에 부른 쪽이 앞서 쌓은 + * `addSelect()` 를 지웁니다 — 판매 집계와 리뷰 집계를 함께 붙이는 경로에서 + * 한쪽 별칭이 조용히 사라지므로, 이미 지정돼 있으면 건드리지 않습니다. + * + * @param Builder $query 대상 쿼리 + * @return Builder SELECT 가 고정된 쿼리 + */ + private function ensureBaseSelect($query) + { + if (empty($query->getQuery()->columns)) { + $query->select($this->model->getTable().'.*'); + } + + return $query; + } + + /** + * 판매 집계 파생 테이블의 별칭을 반환합니다. + * + * @return string 별칭 + */ + private function salesAlias(): string + { + return 'g7_product_sales'; + } + + /** + * 상품별 리뷰 건수·평점 집계를 파생 테이블로 조인합니다. + * + * `withCount()` + `withAvg()` 를 겹쳐 쓰면 같은 리뷰 테이블을 훑는 상관 서브쿼리가 + * 행마다 **두 번** 실행됩니다. 건수와 평균은 한 번의 그룹 집계로 함께 얻을 수 있으므로 + * 미리 그룹지은 파생 테이블을 LEFT JOIN 해 스캔을 1회로 줄입니다. + * + * product_id 기준 그룹이라 상품당 한 행이므로 원 행이 부풀지 않습니다. 리뷰가 없는 + * 상품은 조인 결과가 없어 건수 0 / 평점 null 이 되며, 이는 종전 동작과 같습니다. + * + * @param Builder $query 대상 쿼리 + * @return Builder 조인이 적용된 쿼리 + */ + private function joinReviewAggregate($query) + { + $alias = $this->reviewAlias(); + $productsTable = $this->model->getTable(); + + $aggregate = ProductReview::query() + ->select('product_id') + ->selectRaw('COUNT(*) as review_count') + ->selectRaw('AVG(rating) as rating_avg') + ->where('status', ReviewStatus::VISIBLE->value) + ->groupBy('product_id'); + + return $this->ensureBaseSelect($query) + ->leftJoinSub( + $aggregate, + $alias, + fn ($join) => $join->on($alias.'.product_id', '=', $productsTable.'.id') + ) + // 별칭은 빌더가 만들게 둔다 — grammar 가 테이블 프리픽스를 별칭에도 붙이므로 + // raw 문자열로 별칭을 조립하면 `g7_g7_...` 처럼 이중 프리픽스가 된다. + // 리뷰가 없는 상품은 여기서 null 이 오고, 리소스가 0 으로 표기한다. + ->addSelect([ + $alias.'.review_count', + $alias.'.rating_avg', + ]); + } + + /** + * 지연 조인 목록의 outer 에만 리뷰 집계를 붙이는 클로저를 반환합니다. + * + * inner 에 두면 OFFSET 이 건너뛰는 행 전체에 대해 집계가 실행되므로, + * 이번 페이지 행에만 적용되도록 outer 전용으로 넘깁니다. + * + * @return \Closure outer 적용 클로저 + */ + private function reviewAggregateForOuter(): \Closure + { + return fn ($outer) => $this->joinReviewAggregate($outer); + } + + /** + * 리뷰 집계 파생 테이블의 별칭을 반환합니다. + * + * @return string 별칭 + */ + private function reviewAlias(): string + { + return 'g7_product_reviews_agg'; + } + /** * {@inheritDoc} */ public function getNewProducts(int $limit = 10): Collection { - return $this->model->newQuery() + $query = $this->model->newQuery() ->with(['images', 'categories', 'activeLabelAssignments.label']) - ->withCount('visibleReviews as review_count') - ->withAvg('visibleReviews as rating_avg', 'rating') - ->where('display_status', 'visible') - ->orderBy('created_at', 'desc') + ->where('display_status', 'visible'); + + return $this->joinReviewAggregate($query) + ->orderBy($this->model->getTable().'.created_at', 'desc') ->limit($limit) ->get(); } @@ -638,77 +909,129 @@ class ProductRepository implements ProductRepositoryInterface /** * {@inheritDoc} */ - public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', ?int $categoryId = null, int $offset = 0, int $limit = 10): array + public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', ?int $categoryId = null, int $offset = 0, int $limit = 10): BoundedPage { - $page = (int) floor($offset / $limit) + 1; + $page = (int) floor($offset / max(1, $limit)) + 1; // FULLTEXT(name/description) 매칭 + 보조필드(product_code/id) 매칭의 합집합. // 통합검색도 관리자 검색과 동일하게 상품코드/ID 로 상품을 찾을 수 있어야 한다. - $matchedIds = $this->resolveKeywordMatchedProductIds($keyword); - - $paginator = $this->model->newQuery() - ->whereIn('id', $matchedIds ?: [0]) + $query = $this->model->newQuery() ->where('display_status', ProductDisplayStatus::VISIBLE->value) // audit:allow list-repository-eager-load-vs-resource reason: 통합검색 결과는 Resource 가 // 아니라 SearchProductsListener 가 직렬화한다 — 그 안에서 primaryCategory 를 행마다 // 읽으므로(`$product->primaryCategory->first()`) 여기서 미리 로드하지 않으면 N+1 이 된다 ->with(['images', 'primaryCategory', 'brand', 'activeLabelAssignments.label']) - ->withCount('visibleReviews as review_count') - ->withAvg('visibleReviews as rating_avg', 'rating') - ->when($categoryId !== null, fn ($q) => $q->whereHas('categories', fn ($c) => $c->where('ecommerce_categories.id', $categoryId))) - ->orderBy($orderBy, $direction) + ->when($categoryId !== null, fn ($q) => $q->whereHas('categories', fn ($c) => $c->where('ecommerce_categories.id', $categoryId))); + + // 리뷰 건수·평점은 파생 테이블 조인 1회로 얻는다 (상관 서브쿼리 2회 아님) + $this->joinReviewAggregate($query); + + $productsTable = $this->model->getTable(); + $query->orderBy($productsTable.'.'.$orderBy, $direction) // 전순서 보장 — 정렬 컬럼(등록일/판매량 등)이 비고유라 페이지 경계가 흔들릴 수 있다 - ->orderBy('id', $direction === 'asc' ? 'asc' : 'desc') - // audit:allow repository-paginate-column-pruning reason: 통합검색 — - // whereIn(id, $matchedIds) 로 FULLTEXT 매칭 ID 집합에 먼저 한정되므로 스캔 대상이 - // 상품 전체가 아니라 매칭 건수로 묶인다(OFFSET 이 훑는 범위도 그 안). 지연 조인의 - // inner 가 하는 일을 매칭 ID 산출이 이미 수행하는 구조다 - ->paginate($limit, ['*'], 'page', $page); + ->orderBy($productsTable.'.id', $direction === 'asc' ? 'asc' : 'desc'); - return ['total' => $paginator->total(), 'items' => $paginator->getCollection()]; - } + $this->applyKeywordMatch($query, $keyword, limitContext: 'search'); - /** - * 키워드로 매칭되는 상품 ID 집합을 반환합니다. - * - * FULLTEXT(name/description) Scout 매칭과 보조필드(product_code / 숫자 ID) 매칭의 - * 합집합. 통합검색의 결과 조회와 total 계산이 동일한 조건을 쓰도록 단일 경로로 산출한다. - * - * @param string $keyword 검색 키워드 - * @return array 매칭된 상품 ID 배열 - */ - private function resolveKeywordMatchedProductIds(string $keyword): array - { - $ftIds = Product::search($keyword)->keys()->all(); - - $auxIds = $this->model->newQuery() - ->where(function ($q) use ($keyword) { - $q->where('product_code', 'like', "%{$keyword}%"); - - // 숫자 키워드는 상품 ID 정확 매칭도 포함 - if (ctype_digit($keyword)) { - $q->orWhere('id', (int) $keyword); - } - }) - ->pluck('id') - ->all(); - - return array_values(array_unique([...$ftIds, ...$auxIds])); + // audit:allow repository-paginate-column-pruning reason: 통합검색 결과 카드가 + // 이미지·카테고리·브랜드·라벨 관계를 함께 그리므로 모델 전체가 필요하다. 한 페이지 + // 분량만 조회되고, 총 건수에는 상한이 걸려 있다 + return BoundedPaginator::paginate( + $query, + perPage: $limit, + page: $page, + resultCap: PaginationLimits::resultCap('search'), + ); } /** * {@inheritDoc} */ - public function countByKeyword(string $keyword, ?int $categoryId = null): int + public function searchByKeywordWithCursor( + string $keyword, + array $sortKeys, + ?int $categoryId = null, + int $perPage = 10, + ?string $cursor = null + ): CursorPaginator { + $query = $this->model->newQuery() + ->where('display_status', ProductDisplayStatus::VISIBLE->value) + ->with(['images', 'primaryCategory', 'brand', 'activeLabelAssignments.label']) + ->when($categoryId !== null, fn ($q) => $q->whereHas('categories', fn ($c) => $c->where('ecommerce_categories.id', $categoryId))); + + $this->joinReviewAggregate($query); + $this->applyKeywordMatch($query, $keyword, limitContext: 'search'); + + // 정렬 컬럼은 상품 테이블 소속임을 명시한다 — 파생 조인이 붙어 있어 모호해질 수 있다. + $productsTable = $this->model->getTable(); + $qualified = array_map( + static fn (array $spec): array => [$productsTable.'.'.$spec[0], $spec[1]], + $sortKeys + ); + + return KeysetPaginator::paginate( + query: $query, + perPage: $perPage, + sortKeys: $qualified, + uniqueKey: $productsTable.'.id', + cursor: $cursor, + ); + } + + /** + * 키워드 매칭 조건을 쿼리에 직접 적용합니다. + * + * FULLTEXT(name/description) 매칭과 보조필드(product_code / sku / barcode / 숫자 ID) + * 매칭의 합집합을 하나의 WHERE 그룹으로 만듭니다. 매칭 ID 를 PHP 로 실어 나르지 않으므로 + * 매칭 건수가 아무리 커도 메모리와 SQL 길이가 늘지 않습니다. + * + * 결과 조회와 total 계산이 이 메서드 하나를 공유하므로 두 경로의 조건이 갈라지지 않습니다. + * + * @param Builder $query 대상 쿼리 + * @param string $keyword 검색 키워드 + * @param bool $includeAuxFields 상품코드·SKU·바코드·ID 보조 매칭 포함 여부 + * @param string|null $limitContext 키 집합 상한 해석에 쓸 목록 컨텍스트 이름 + */ + private function applyKeywordMatch($query, string $keyword, bool $includeAuxFields = true, ?string $limitContext = null): void + { + $query->where(function ($group) use ($keyword, $includeAuxFields, $limitContext) { + // 정제는 코어 헬퍼가 단독 수행 — BOOLEAN MODE 연산자 입력이 500 이 되지 않는다. + // 상품 테이블은 name·description 에 각각 단일 FULLTEXT 인덱스를 갖는다. + // 복합 MATCH(name, description) 는 그 조합의 복합 인덱스를 요구해 오류가 난다. + // 상한 컨텍스트를 함께 넘긴다 — 키 집합을 만드는 엔진이 총 건수 상한과 같은 + // 기준으로 잘라 내야 "엔진이 돌려준 건수" 와 "화면이 보고하는 총 건수" 가 어긋나지 않는다. + KeywordSearch::applyAny($group, ['name', 'description'], $keyword, 'and', $limitContext); + + if (! $includeAuxFields) { + return; + } + + $group->orWhere('product_code', 'like', "%{$keyword}%") + ->orWhere('sku', 'like', "%{$keyword}%") + ->orWhere('barcode', 'like', "%{$keyword}%"); + + // 숫자 키워드는 상품 ID 정확 매칭도 포함 (예: "99" → id=99) + if (ctype_digit($keyword)) { + $group->orWhere('id', (int) $keyword); + } + }); + } + + /** + * {@inheritDoc} + */ + public function countByKeyword(string $keyword, ?int $categoryId = null): BoundedCount { // searchByKeyword 와 동일한 매칭 조건(FULLTEXT ∪ 보조필드)으로 total 산출. - $matchedIds = $this->resolveKeywordMatchedProductIds($keyword); - - return $this->model->newQuery() - ->whereIn('id', $matchedIds ?: [0]) + $query = $this->model->newQuery() ->where('display_status', ProductDisplayStatus::VISIBLE->value) - ->when($categoryId !== null, fn ($q) => $q->whereHas('categories', fn ($c) => $c->where('ecommerce_categories.id', $categoryId))) - ->count(); + ->when($categoryId !== null, fn ($q) => $q->whereHas('categories', fn ($c) => $c->where('ecommerce_categories.id', $categoryId))); + + $this->applyKeywordMatch($query, $keyword); + + // 배지용 건수 — 상한을 걸어 대량 매칭에서도 비용이 일정하다. + // 상한에 걸리면 값이 잘리므로 정확도를 함께 돌려준다. + return BoundedPaginator::count($query, PaginationLimits::resultCap('search')); } /** @@ -736,12 +1059,13 @@ class ProductRepository implements ProductRepositoryInterface if (! empty($filters['date_type']) && (! empty($filters['start_date']) || ! empty($filters['end_date']))) { $dateField = $filters['date_type']; - if (! empty($filters['start_date'])) { - $query->whereDate($dateField, '>=', $filters['start_date']); - } - if (! empty($filters['end_date'])) { - $query->whereDate($dateField, '<=', $filters['end_date']); - } + // whereDate 는 인덱스를 무력화한다 — 범위 조건으로 준다. + $this->applyDateRangeFilter( + $query, + $dateField, + $filters['start_date'] ?? null, + $filters['end_date'] ?? null + ); } // 판매상태 필터 (다중 선택 가능) @@ -872,7 +1196,12 @@ class ProductRepository implements ProductRepositoryInterface */ public function updateStockQuantity(int $productId, int $stock): int { - return $this->model->where('id', $productId)->update(['stock_quantity' => $stock]); + $affected = $this->model->where('id', $productId)->update(['stock_quantity' => $stock]); + + // 통계에 품절·재고부족 건수가 들어 있으므로 재고 변경도 무효화 대상이다 + $this->forgetStatisticsCache(); + + return $affected; } /** @@ -908,6 +1237,7 @@ class ProductRepository implements ProductRepositoryInterface columns: ['*'], sort: [['column' => 'created_at', 'direction' => $sortOrder]], perPage: $perPage, + resultCap: PaginationLimits::resultCap('admin.activity_logs'), ); } } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductReviewRepository.php b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductReviewRepository.php index d7ad1c5f..80dbd285 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductReviewRepository.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Repositories/ProductReviewRepository.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Repositories; use App\Repositories\Concerns\PaginatesWithDeferredJoin; +use App\Support\Query\PaginationLimits; use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; @@ -145,6 +146,7 @@ class ProductReviewRepository implements ProductReviewRepositoryInterface 'orderOption.order', 'replyAdmin', ], + resultCap: PaginationLimits::resultCap('admin.product_reviews'), ); } @@ -222,6 +224,7 @@ class ProductReviewRepository implements ProductReviewRepositoryInterface // 개수는 배열 길이 대신 DB 집계를 그대로 쓴다(Resource 가 집계를 우선한다). relations: ['user', 'images'], withCount: ['images as image_count'], + resultCap: PaginationLimits::resultCap('shop.product_reviews'), ); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/AdditionalOptionSelectionService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/AdditionalOptionSelectionService.php index b3209800..48b7278c 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/AdditionalOptionSelectionService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/AdditionalOptionSelectionService.php @@ -2,7 +2,9 @@ namespace Modules\Sirsoft\Ecommerce\Services; +use Illuminate\Support\Collection; use Modules\Sirsoft\Ecommerce\Exceptions\CartUnavailableException; +use Modules\Sirsoft\Ecommerce\Models\Product; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductAdditionalOptionValueRepositoryInterface; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductRepositoryInterface; @@ -14,11 +16,56 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductRepositoryInterface; */ class AdditionalOptionSelectionService { + /** + * 상품 ID → 활성 선택지 맵 (요청 스코프) + * + * @var array + */ + private array $activeValuesCache = []; + + /** + * 상품 ID → 필수 그룹 판정용 상품 모델 (요청 스코프) + * + * @var array + */ + private array $productCache = []; + public function __construct( protected ProductAdditionalOptionValueRepositoryInterface $additionalOptionValueRepository, protected ProductRepositoryInterface $productRepository ) {} + /** + * 여러 상품의 검증 자료를 한 번에 적재합니다. + * + * 장바구니 병합·바로구매처럼 항목이 여러 개인 경로는 검증 전에 이 메서드를 부릅니다. + * 부르지 않아도 동작은 같지만, 그 경우 상품마다 조회가 발생합니다. + * + * @param array $productIds 상품 ID 목록 + */ + public function prefetchForProducts(array $productIds): void + { + $productIds = array_values(array_unique(array_filter(array_map('intval', $productIds)))); + $missing = array_values(array_diff($productIds, array_keys($this->activeValuesCache))); + + if (empty($missing)) { + return; + } + + $valuesByProduct = $this->additionalOptionValueRepository->getActiveByProductIdsKeyed($missing); + + foreach ($missing as $productId) { + $this->activeValuesCache[$productId] = $valuesByProduct->get($productId) ?? collect(); + } + + // 필수 그룹 판정에 쓰는 상품 + 그룹도 한 번에 읽는다. + $products = $this->productRepository->findByIdsWithRelationsKeyed($missing, ['additionalOptions']); + + foreach ($missing as $productId) { + $this->productCache[$productId] = $products->get($productId); + } + } + /** * 추가옵션 선택을 검증하고 정규화합니다. * @@ -39,7 +86,8 @@ class AdditionalOptionSelectionService $selections = $selections ?? []; // 상품에 정의된 활성 선택지 lookup (value_id 키) - $activeValues = $this->additionalOptionValueRepository->getActiveByProductKeyed($productId); + // prefetchForProducts() 를 먼저 부른 경로는 여기서 쿼리가 나가지 않는다. + $activeValues = $this->resolveActiveValues($productId); // 그룹당 1개로 정규화 (라디오) — 값과 직접입력 텍스트를 함께 보관 $byGroup = []; @@ -77,9 +125,8 @@ class AdditionalOptionSelectionService } // 필수 그룹 미선택 + 필수 그룹의 직접입력 빈값 검증 (D9/D12/Q-E1) - $product = $this->productRepository->find($productId); + $product = $this->resolveProduct($productId); if ($product) { - $product->loadMissing('additionalOptions'); foreach ($product->additionalOptions as $group) { // 필수 그룹 미선택 차단 if ($group->is_required && ! isset($byGroup[$group->id])) { @@ -122,4 +169,37 @@ class AdditionalOptionSelectionService return $normalized; } + + /** + * 상품의 활성 선택지 맵을 반환합니다 (요청 스코프 캐시). + * + * @param int $productId 상품 ID + * @return Collection value_id 키 맵 + */ + private function resolveActiveValues(int $productId) + { + if (! array_key_exists($productId, $this->activeValuesCache)) { + $this->activeValuesCache[$productId] = $this->additionalOptionValueRepository + ->getActiveByProductKeyed($productId); + } + + return $this->activeValuesCache[$productId]; + } + + /** + * 필수 그룹 판정용 상품을 반환합니다 (요청 스코프 캐시, additionalOptions 적재). + * + * @param int $productId 상품 ID + * @return Product|null 상품 모델 + */ + private function resolveProduct(int $productId) + { + if (! array_key_exists($productId, $this->productCache)) { + $product = $this->productRepository->find($productId); + $product?->loadMissing('additionalOptions'); + $this->productCache[$productId] = $product; + } + + return $this->productCache[$productId]; + } } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/CartService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/CartService.php index 07b36981..255eca44 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/CartService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/CartService.php @@ -554,15 +554,33 @@ class CartService $guestItems = $this->cartRepository->findByCartKeyWithoutUser($cartKey); $count = 0; + // 병합할 항목이 없으면 사전 조회도 하지 않는다 (빈 cart_key·존재하지 않는 키) + if ($guestItems->isEmpty()) { + return $count; + } + + // 항목마다 옵션과 회원 장바구니 라인을 조회하면 로그인 한 번에 항목 수 × 2 만큼 + // 쿼리가 난다. 루프 밖에서 한 번씩만 읽어 맵으로 들고 간다. + $guestOptionIds = $guestItems->pluck('product_option_id')->all(); + $optionsById = $this->productOptionRepository->findByIds($guestOptionIds)->keyBy('id'); + $memberLinesByOption = $this->cartRepository->findAllByUserAndOptions($userId, $guestOptionIds); + + // 구매 수량 한도 판정에 쓰는 상품별 합계도 시작 시점 값을 한 번만 읽는다. + // 루프가 회원 장바구니를 바꾸므로 그 변동분은 아래에서 메모리로 누적한다 — + // 매 항목 합계를 다시 조회하면 항목 수만큼 쿼리가 더 난다. + $memberTotalByProduct = $this->cartRepository->sumQuantityByProducts( + $guestItems->pluck('product_id')->all(), + $userId + ); + foreach ($guestItems as $guestItem) { // 재고 확인 - $productOption = $this->productOptionRepository->findById($guestItem->product_option_id); + $productOption = $optionsById->get($guestItem->product_option_id); $availableStock = $productOption?->stock_quantity ?? 0; // 회원 장바구니에 동일 옵션 + 동일 추가옵션 조합이 있는지 확인 (D3) $guestHash = $guestItem->getAdditionalOptionSelectionHash(); - $existingItem = $this->cartRepository - ->findAllByUserAndOption($userId, $guestItem->product_option_id) + $existingItem = ($memberLinesByOption->get($guestItem->product_option_id) ?? collect()) ->first(fn ($candidate) => $candidate->getAdditionalOptionSelectionHash() === $guestHash); if ($existingItem) { @@ -573,12 +591,9 @@ class CartService // 로그인 시 자동 병합이라 예외를 던지면 로그인 자체가 실패한다 → // 상한까지 클램프하고 조정 내역을 반환값에 담아 화면이 안내하게 한다. // 같은 상품의 다른 라인 수량까지 합산해 판정한다 (옵션 분할 우회 차단). - $otherLinesTotal = $this->cartRepository->sumQuantityByProduct( - $guestItem->product_id, - $userId, - null, - $existingItem->id, - ); + // 지금 합치는 라인 자신은 빼야 하므로 그 수량만 제외한다. + $productTotal = $memberTotalByProduct[$guestItem->product_id] ?? 0; + $otherLinesTotal = max(0, $productTotal - (int) $existingItem->quantity); $clamped = $this->clampToPurchaseQuantityLimit( $guestItem->product_id, @@ -599,6 +614,9 @@ class CartService ]; } + // 이 라인의 수량이 바뀐 만큼 상품 합계도 옮겨 둔다 (다음 항목 판정용). + $memberTotalByProduct[$guestItem->product_id] = $otherLinesTotal + $clamped; + $this->cartRepository->update($existingItem, [ 'quantity' => $clamped, ]); @@ -607,11 +625,8 @@ class CartService // 동일 옵션 없음: 재고 초과 시 조정 후 user_id 업데이트 (cart_key 유지) $finalQuantity = $availableStock > 0 ? min($guestItem->quantity, $availableStock) : $guestItem->quantity; - $otherLinesTotal = $this->cartRepository->sumQuantityByProduct( - $guestItem->product_id, - $userId, - null, - ); + // 회원 장바구니에 같은 옵션이 없으므로 기존 합계 전부가 '다른 라인' 이다. + $otherLinesTotal = $memberTotalByProduct[$guestItem->product_id] ?? 0; $clamped = $this->clampToPurchaseQuantityLimit( $guestItem->product_id, @@ -637,6 +652,9 @@ class CartService 'user_id' => $userId, 'quantity' => $clamped, ]); + + // 이 라인이 회원 장바구니로 넘어왔으므로 상품 합계에 더한다. + $memberTotalByProduct[$guestItem->product_id] = $otherLinesTotal + $clamped; } } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/CategoryService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/CategoryService.php index 0562ca8c..3fa89184 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/CategoryService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/CategoryService.php @@ -89,7 +89,7 @@ class CategoryService $query->withCount('products')->orderBy('sort_order'); }, 'images', - ]); + ], withCounts: true); if ($category && ! $category->is_active) { return null; @@ -118,7 +118,7 @@ class CategoryService 'images', 'children', 'parent:id,name,slug', // parent에서 필요한 필드만 선택 - ]); + ], withCounts: true); if ($category) { // 필터 훅 - 조회 결과 변형 @@ -186,7 +186,8 @@ class CategoryService */ public function updateCategory(int $id, array $data): Category { - $category = $this->repository->findById($id); + // 쓰기 경로는 상품 수·자식 수 집계를 읽지 않는다 — 계산하면 조회마다 서브쿼리 2개가 붙는다. + $category = $this->repository->findById($id, withCounts: false); if (! $category) { throw new CategoryOperationException('sirsoft-ecommerce::exceptions.category_not_found', ['category_id' => $id]); @@ -245,7 +246,7 @@ class CategoryService */ public function deleteCategory(int $id): array { - $category = $this->repository->findById($id); + $category = $this->repository->findById($id, withCounts: false); if (! $category) { throw new CategoryOperationException('sirsoft-ecommerce::exceptions.category_not_found', ['category_id' => $id]); @@ -299,7 +300,7 @@ class CategoryService return ['depth' => 0, 'path' => '']; } - $parent = $this->repository->findById($parentId); + $parent = $this->repository->findById($parentId, withCounts: false); if (! $parent) { return ['depth' => 0, 'path' => '']; @@ -317,7 +318,7 @@ class CategoryService private function updatePath(Category $category): void { if ($category->parent_id) { - $parent = $this->repository->findById($category->parent_id); + $parent = $this->repository->findById($category->parent_id, withCounts: false); if ($parent) { $newPath = $parent->path ? $parent->path.'/'.$category->id : (string) $category->id; $category->update(['path' => $newPath]); @@ -365,7 +366,7 @@ class CategoryService */ public function toggleStatus(int $id): Category { - $category = $this->repository->findById($id); + $category = $this->repository->findById($id, withCounts: false); if (! $category) { throw new CategoryOperationException('sirsoft-ecommerce::exceptions.category_not_found', ['category_id' => $id]); @@ -394,9 +395,15 @@ class CategoryService // Before 훅 HookManager::doAction('sirsoft-ecommerce.category.before_reorder', $orders); - DB::transaction(function () use ($orders) { + // 항목마다 조회하면 재정렬 한 번에 항목 수만큼 쿼리가 난다. 한 번에 읽어 맵으로 들고 간다. + // 집계(상품 수·자식 수)는 재정렬이 읽지 않으므로 계산하지 않는다. + $categoriesById = $this->repository->findByIdsKeyed( + array_map(fn ($order) => (int) $order['id'], $orders) + ); + + DB::transaction(function () use ($orders, $categoriesById) { foreach ($orders as $order) { - $category = $this->repository->findById($order['id']); + $category = $categoriesById->get((int) $order['id']); if (! $category) { continue; } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/ProductService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/ProductService.php index bf208622..ba29276b 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/ProductService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/ProductService.php @@ -3,6 +3,10 @@ namespace Modules\Sirsoft\Ecommerce\Services; use App\Extension\HookManager; +use App\Search\SearchPagePolicy; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Auth; @@ -24,6 +28,24 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\ProductRepositoryInterface; */ class ProductService { + /** + * 검색 정렬 이름 → [실제 컬럼, 방향] 선언 + * + * 코어({@see SearchPagePolicy})가 이 선언을 읽어 커서 적용 여부를 판정한다. + * 여기에 없는 정렬 이름(관련도순 등)은 커서로 처리하지 않고 offset 을 유지한다. + */ + public const SEARCH_SORT_MAP = [ + 'latest' => ['created_at', 'desc'], + 'oldest' => ['created_at', 'asc'], + 'price_asc' => ['selling_price', 'asc'], + 'price_desc' => ['selling_price', 'desc'], + ]; + + /** + * 커서(키셋) 경계로 쓸 수 있는 실제 컬럼 선언 + */ + public const SEARCH_CURSOR_COLUMNS = ['created_at', 'selling_price']; + /** * HTMLPurifier 인스턴스 (지연 생성) */ @@ -1829,21 +1851,50 @@ class ProductService * @param int $limit 조회할 최대 항목 수 * @return array{total: int, items: Collection} */ - public function searchByKeyword(string $keyword, string $sort = 'latest', ?int $categoryId = null, int $offset = 0, int $limit = 10): array + public function searchByKeyword(string $keyword, string $sort = 'latest', ?int $categoryId = null, int $offset = 0, int $limit = 10): BoundedPage { [$orderBy, $direction] = $this->resolveSortColumn($sort); return $this->repository->searchByKeyword($keyword, $orderBy, $direction, $categoryId, $offset, $limit); } + /** + * 키워드로 상품을 커서(키셋)로 검색합니다. + * + * 커서 적용 가능 여부는 코어({@see SearchPagePolicy})가 판정한다. 이 서비스는 + * 정렬 선언({@see self::SEARCH_SORT_MAP})만 제공하고 규칙을 다시 쓰지 않는다. + * + * @param string $keyword 검색 키워드 + * @param string $sort 정렬 옵션 + * @param int|null $categoryId 카테고리 필터 + * @param int $perPage 페이지당 항목 수 + * @param string|null $cursor 인코딩된 커서 (첫 페이지면 null) + * @return CursorPaginator|null 커서 페이지 결과 (커서 적용 불가 시 null) + */ + public function searchByKeywordWithCursor( + string $keyword, + string $sort = 'latest', + ?int $categoryId = null, + int $perPage = 10, + ?string $cursor = null + ): ?CursorPaginator { + $sortKeys = SearchPagePolicy::sortKeys($sort, self::SEARCH_SORT_MAP); + + if (! SearchPagePolicy::usesCursor($cursor, $sortKeys, self::SEARCH_CURSOR_COLUMNS)) { + return null; + } + + return $this->repository->searchByKeywordWithCursor($keyword, $sortKeys, $categoryId, $perPage, $cursor); + } + /** * 키워드와 일치하는 공개 상품 수를 조회합니다. * * @param string $keyword 검색 키워드 * @param int|null $categoryId 카테고리 필터 - * @return int 일치하는 상품 수 + * @return BoundedCount 일치하는 상품 수 (정확도 포함) */ - public function countByKeyword(string $keyword, ?int $categoryId = null): int + public function countByKeyword(string $keyword, ?int $categoryId = null): BoundedCount { return $this->repository->countByKeyword($keyword, $categoryId); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/TempOrderService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/TempOrderService.php index d9279ea6..f3187f56 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/TempOrderService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/TempOrderService.php @@ -215,13 +215,18 @@ class TempOrderService { $cartItems = new Collection; - foreach ($items as $item) { + // 항목마다 옵션을 두 번 조회하면 체크아웃 한 번에 항목 수 × 2 만큼 쿼리가 난다. + // 상품별 옵션과 확정된 옵션 상세를 각각 한 번씩만 읽어 맵으로 들고 간다. + $optionsByProduct = $this->productOptionRepository->getByProductIds( + array_map(fn ($item) => (int) ($item['product_id'] ?? 0), $items) + ); + + $resolvedOptionIds = []; + + foreach ($items as $index => $item) { $productId = (int) ($item['product_id'] ?? 0); $requestedOptionId = $item['product_option_id'] ?? null; - $quantity = max(1, (int) ($item['quantity'] ?? 1)); - - // 상품 옵션 매칭 (옵션 ID 기준, 없으면 기본 옵션) - $productOptions = $this->productOptionRepository->getByProductId($productId); + $productOptions = $optionsByProduct->get($productId) ?? new Collection; if (! empty($requestedOptionId)) { $matchedOption = $productOptions->firstWhere('id', (int) $requestedOptionId); @@ -230,18 +235,34 @@ class TempOrderService throw new CartOperationException('option_not_found'); } - $optionId = $matchedOption->id; - } else { - $defaultOption = $productOptions->first(); - if (! $defaultOption) { - throw new CartOperationException('option_not_found'); - } + $resolvedOptionIds[$index] = $matchedOption->id; - $optionId = $defaultOption->id; + continue; } - // product/images 관계를 포함해 옵션 재조회 (검증·계산에 필요) - $option = $this->productOptionRepository->findByIdsWithProduct([$optionId])->first(); + $defaultOption = $productOptions->first(); + if (! $defaultOption) { + throw new CartOperationException('option_not_found'); + } + + $resolvedOptionIds[$index] = $defaultOption->id; + } + + // product/images 관계를 포함해 확정 옵션을 한 번에 재조회 (검증·계산에 필요) + $optionsById = $this->productOptionRepository + ->findByIdsWithProduct(array_values($resolvedOptionIds)) + ->keyBy('id'); + + // 추가옵션 검증 자료도 항목 루프 전에 한 번에 적재한다. 이걸 빼면 아래 루프가 + // 항목마다 선택지·상품·그룹을 다시 읽는다(항목당 3쿼리). + $this->additionalOptionSelectionService->prefetchForProducts( + $optionsById->pluck('product_id')->all() + ); + + foreach ($items as $index => $item) { + $quantity = max(1, (int) ($item['quantity'] ?? 1)); + + $option = $optionsById->get($resolvedOptionIds[$index]); if (! $option || ! $option->product) { throw new CartOperationException('option_not_found'); } diff --git a/modules/_bundled/sirsoft-ecommerce/src/routes/api.php b/modules/_bundled/sirsoft-ecommerce/src/routes/api.php index 327427c1..f9614af0 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/routes/api.php +++ b/modules/_bundled/sirsoft-ecommerce/src/routes/api.php @@ -33,6 +33,7 @@ use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\ProductImageController; use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\ProductInquiryController as PublicProductInquiryController; use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\ProductReviewController as PublicProductReviewController; use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\PublicCouponController; +use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\StorefrontController; use Modules\Sirsoft\Ecommerce\Http\Controllers\Public\WishlistController; use Modules\Sirsoft\Ecommerce\Http\Controllers\Shop\PaymentConfigController; use Modules\Sirsoft\Ecommerce\Http\Controllers\User\OrderController as UserOrderController; @@ -73,6 +74,13 @@ Route::prefix('categories')->group(function () { ->name('categories.show'); }); +// 쇼핑 첫 화면 통합 조회 API +// GET /api/modules/sirsoft-ecommerce/storefront - 분류·상품목록·최근본·인기·신상품 한 번에 조회 +// (각 묶음의 응답 형태는 개별 엔드포인트와 동일 — 조립 위치만 서버로 옮긴 것) +Route::get('storefront', [StorefrontController::class, 'index']) + ->middleware(['optional.sanctum', 'permission:user,sirsoft-ecommerce.user-products.read']) + ->name('storefront.index'); + // 공개 상품 API // GET /api/modules/sirsoft-ecommerce/products - 공개 상품 목록 조회 // GET /api/modules/sirsoft-ecommerce/products/popular - 인기 상품 조회 diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserAbilitiesTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserAbilitiesTest.php index 719f3fa3..58702a86 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserAbilitiesTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserAbilitiesTest.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Tests\Feature; use App\Enums\PermissionType; +use App\Http\Middleware\PermissionMiddleware; use App\Models\Permission; use App\Models\Role; use App\Models\User; @@ -86,10 +87,7 @@ class EcommerceUserAbilitiesTest extends ModuleTestCase // 블랙리스트에는 권한 할당 없음 // PermissionMiddleware의 guest role 정적 캐시 초기화 - $reflection = new \ReflectionClass(\App\Http\Middleware\PermissionMiddleware::class); - $prop = $reflection->getProperty('guestRoleCache'); - $prop->setAccessible(true); - $prop->setValue(null, null); + PermissionMiddleware::clearGuestRoleCache(); // 사용자 생성 $this->permittedUser = User::factory()->create(); diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserPermissionTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserPermissionTest.php index 1cfa216f..a27c2bcc 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserPermissionTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/EcommerceUserPermissionTest.php @@ -3,6 +3,7 @@ namespace Modules\Sirsoft\Ecommerce\Tests\Feature; use App\Enums\PermissionType; +use App\Http\Middleware\PermissionMiddleware; use App\Models\Permission; use App\Models\Role; use App\Models\User; @@ -88,10 +89,7 @@ class EcommerceUserPermissionTest extends ModuleTestCase $guestRole->permissions()->syncWithoutDetaching([$productReadPerm->id]); // PermissionMiddleware의 guest role 정적 캐시 초기화 - $reflection = new \ReflectionClass(\App\Http\Middleware\PermissionMiddleware::class); - $prop = $reflection->getProperty('guestRoleCache'); - $prop->setAccessible(true); - $prop->setValue(null, null); + PermissionMiddleware::clearGuestRoleCache(); // 사용자 생성 $this->permittedUser = User::factory()->create(); diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Http/Controllers/Public/PublicProductControllerTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Http/Controllers/Public/PublicProductControllerTest.php index f70bb53c..67e4f405 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Http/Controllers/Public/PublicProductControllerTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Http/Controllers/Public/PublicProductControllerTest.php @@ -33,10 +33,7 @@ class PublicProductControllerTest extends ModuleTestCase // PermissionMiddleware의 guest role 정적 캐시 초기화 // (TestingSeeder에서 guest 역할을 생성하지만, 정적 캐시가 null로 초기화되지 않을 수 있음) - $reflection = new \ReflectionClass(PermissionMiddleware::class); - $prop = $reflection->getProperty('guestRoleCache'); - $prop->setAccessible(true); - $prop->setValue(null, null); + PermissionMiddleware::clearGuestRoleCache(); } // ======================================== diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/ListCacheInvalidationTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/ListCacheInvalidationTest.php new file mode 100644 index 00000000..41818819 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/ListCacheInvalidationTest.php @@ -0,0 +1,189 @@ + ['ko' => $name], + 'slug' => $slug, + 'depth' => 0, + 'sort_order' => $sortOrder, + 'is_active' => true, + 'path' => '', + ]); + + $category->update(['path' => (string) $category->id]); + + return $category; + } + + /** + * 테스트용 상품을 만듭니다. + * + * @param string $code 상품 코드 + * @return Product 생성된 상품 + */ + private function makeProduct(string $code) + { + return app(ProductRepositoryInterface::class)->create([ + 'product_code' => $code, + 'name' => ['ko' => '캐시 테스트 상품 '.$code], + 'selling_price' => 1000, + 'stock_quantity' => 5, + 'sales_status' => 'on_sale', + 'display_status' => 'visible', + ]); + } + + /** + * 카테고리 트리가 두 번째 호출에서 캐시로 응답하는지 확인 + */ + public function test_category_tree_is_cached_between_calls(): void + { + $this->makeCategory('appliance', '가전', 0); + + $this->assertCount(1, Category::getTree()); + + // 무효화 훅을 거치지 않고 행을 늘렸는데도 결과가 그대로라면, 두 번째 호출이 + // 캐시였다는 뜻이다 (Model::create 는 Service 훅을 발화하지 않는다). + $this->makeCategory('clothing', '의류', 1); + + $this->assertCount(1, Category::getTree()); + } + + /** + * 트리 캐시를 비우면 다음 호출이 다시 계산하는지 확인 + */ + public function test_flush_makes_tree_recompute(): void + { + $this->makeCategory('appliance', '가전', 0); + Category::getTree(); + + $this->makeCategory('clothing', '의류', 1); + Category::flushTreeCache(); + + $this->assertCount(2, Category::getTree()); + } + + /** + * 상품 변경 훅에서도 트리 캐시가 비워지는지 확인 + * + * 트리에는 카테고리 구조뿐 아니라 상품 수 집계가 함께 실린다. 상품 훅을 빠뜨리면 + * 카테고리 배지의 상품 수가 오류 없이 낡은 값으로 남는다. + */ + public function test_product_hooks_also_invalidate_tree_cache(): void + { + $hooks = CategoryTreeCacheListener::getSubscribedHooks(); + + foreach ([ + 'sirsoft-ecommerce.product.after_create', + 'sirsoft-ecommerce.product.after_update', + 'sirsoft-ecommerce.product.after_delete', + 'sirsoft-ecommerce.product.after_bulk_update', + ] as $hook) { + $this->assertArrayHasKey($hook, $hooks, "상품 훅 {$hook} 가 트리 캐시 무효화에 연결돼 있지 않다"); + } + + $this->makeCategory('appliance', '가전', 0); + Category::getTree(); + + $this->makeCategory('clothing', '의류', 1); + (new CategoryTreeCacheListener)->flush(); + + $this->assertCount(2, Category::getTree()); + } + + /** + * 상품 통계가 쓰기 경로에서 즉시 무효화되는지 확인 + */ + public function test_product_statistics_cache_is_invalidated_on_write(): void + { + $repository = app(ProductRepositoryInterface::class); + + $before = $repository->getStatistics()['total']; + + $product = $this->makeProduct('TEST-CACHE-1'); + + $this->assertSame( + $before + 1, + $repository->getStatistics()['total'], + '상품을 만들었는데 통계가 그대로다 — 쓰기 경로에서 캐시가 비워지지 않았다' + ); + + $repository->delete($product); + + $this->assertSame($before, $repository->getStatistics()['total']); + } + + /** + * 재고 변경도 통계 무효화 대상인지 확인 + * + * 통계에 품절·재고부족 건수가 들어 있으므로 재고만 바뀌어도 값이 달라진다. + */ + public function test_stock_change_invalidates_statistics(): void + { + $repository = app(ProductRepositoryInterface::class); + + $product = $this->makeProduct('TEST-CACHE-2'); + $before = $repository->getStatistics()['out_of_stock_count']; + + $repository->updateStockQuantity($product->id, 0); + + $this->assertSame( + $before + 1, + $repository->getStatistics()['out_of_stock_count'], + '재고를 0 으로 바꿨는데 품절 건수가 그대로다 — 재고 경로에서 캐시가 비워지지 않았다' + ); + } + + /** + * 통계 캐시를 끄면 캐시를 거치지 않고 매번 집계하는지 확인 + */ + public function test_statistics_cache_can_be_disabled(): void + { + config(['g7_settings.core.cache.stats_enabled' => false]); + + $repository = app(ProductRepositoryInterface::class); + $before = $repository->getStatistics()['total']; + + // 캐시가 꺼져 있으면 무효화 호출 없이도 다음 조회에 곧바로 반영된다 + Category::query()->getConnection()->table('ecommerce_products')->insert([ + 'product_code' => 'TEST-CACHE-3', + 'name' => json_encode(['ko' => '직접 삽입'], JSON_UNESCAPED_UNICODE), + 'selling_price' => 1000, + 'stock_quantity' => 5, + 'sales_status' => 'on_sale', + 'display_status' => 'visible', + 'created_at' => now(), + 'updated_at' => now(), + ]); + + $this->assertSame($before + 1, $repository->getStatistics()['total']); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/ListQueryCountRegressionTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/ListQueryCountRegressionTest.php new file mode 100644 index 00000000..3b6c95cb --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/ListQueryCountRegressionTest.php @@ -0,0 +1,205 @@ + $prefix.'-'.$i, + 'name' => ['ko' => '상품 '.$prefix.$i], + 'selling_price' => 1000 + $i, + 'stock_quantity' => 10, + 'sales_status' => 'on_sale', + 'display_status' => 'visible', + ]); + } + } + + /** + * 관리자 상품 목록: 상품 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * @effects product_list_query_count_stable + */ + public function test_admin_product_list_query_count_is_stable(): void + { + $repository = app(ProductRepositoryInterface::class); + $this->seedProducts(5, 'ADMIN-A'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->getListWithFilters([], 50), + grow: fn () => $this->seedProducts(10, 'ADMIN-B'), + context: '관리자 상품 목록', + ); + } + + /** + * 공개 상품 목록: 상품 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * @effects public_list_query_count_stable + */ + public function test_public_product_list_query_count_is_stable(): void + { + $repository = app(ProductRepositoryInterface::class); + $this->seedProducts(5, 'PUB-A'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->getPublicList([], 50), + grow: fn () => $this->seedProducts(10, 'PUB-B'), + context: '공개 상품 목록', + ); + } + + /** + * 카테고리 트리: 카테고리 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 종전에는 노드마다 자식을 다시 조회해 카테고리 수만큼 쿼리가 나갔다. + * + * @effects category_tree_query_count_stable + */ + public function test_category_tree_query_count_is_stable(): void + { + $this->seedCategories(3, 'tree-a'); + + $this->assertQueryCountStableAsDataGrows( + measure: function () { + // 캐시가 있으면 두 번째 측정이 0 쿼리가 되어 판정이 무의미해진다. + // 여기서 재는 것은 "조립 쿼리 수가 노드 수에 비례하지 않는다" 이므로 매번 비운다. + Category::flushTreeCache(); + + return Category::getTree(); + }, + grow: fn () => $this->seedCategories(6, 'tree-b'), + context: '카테고리 트리', + ); + } + + /** + * 쇼핑 첫 화면(storefront): 상품 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 이 엔드포인트는 분류·상품·최근·인기·신상품 5묶음을 한 응답으로 합친다. 묶음마다 + * 관계가 달려 있어 어느 한 묶음이 관계 eager load 를 놓치면 그 자리에서 N+1 이 된다. + * 응답을 통째로 재야 그 사실이 드러난다. + * + * @effects storefront_query_count_stable + */ + public function test_storefront_query_count_is_stable(): void + { + $this->seedProducts(5, 'SHOP-A'); + + // storefront 는 `permission:user,sirsoft-ecommerce.user-products.read` 로 가드된다. + // 시드된 'user' 역할에 그 권한이 붙어 있는지는 앞선 테스트가 무엇을 했는지에 따라 + // 달라지므로(단독 실행은 통과, 연속 실행은 403), 전용 역할에 권한을 직접 부여한다. + $this->actingAs($this->userWithProductReadPermission(), 'sanctum'); + + $this->assertQueryCountStableAsDataGrows( + measure: function () { + // 카테고리 트리 캐시가 살아 있으면 두 번째 측정이 그만큼 적게 나와 + // 판정이 무의미해진다. 매 측정 조건을 같게 맞춘다. + Category::flushTreeCache(); + + $response = $this->getJson('/api/modules/sirsoft-ecommerce/storefront'); + $response->assertOk(); + }, + grow: fn () => $this->seedProducts(10, 'SHOP-B'), + context: '쇼핑 첫 화면(storefront)', + ); + } + + /** + * 공개 상품 조회 권한을 가진 사용자를 만듭니다. + * + * 전용 역할에 권한을 직접 붙여, 시드된 공용 역할의 상태에 의존하지 않게 한다. + * + * @return User 권한을 가진 사용자 + */ + private function userWithProductReadPermission(): User + { + $user = User::factory()->create(); + + $role = Role::create([ + 'identifier' => 'storefront-probe-'.$user->id, + 'name' => ['ko' => '진열 조회 테스트', 'en' => 'Storefront Probe'], + ]); + + $permission = Permission::firstOrCreate( + ['identifier' => 'sirsoft-ecommerce.user-products.read'], + [ + 'name' => ['ko' => '공개 상품 조회', 'en' => 'Read public products'], + 'type' => PermissionType::User, + ] + ); + + $role->permissions()->syncWithoutDetaching([$permission->id]); + $user->roles()->attach($role->id); + + return User::findOrFail($user->id); + } + + /** + * 카테고리를 계층으로 만듭니다 (루트 1 + 자식 N-1). + * + * @param int $count 생성할 수 + * @param string $prefix 슬러그 접두 + */ + private function seedCategories(int $count, string $prefix): void + { + $root = Category::create([ + 'name' => ['ko' => '루트 '.$prefix], + 'slug' => $prefix.'-root', + 'depth' => 0, + 'sort_order' => 0, + 'is_active' => true, + 'path' => '', + ]); + $root->update(['path' => (string) $root->id]); + + for ($i = 1; $i < $count; $i++) { + $child = Category::create([ + 'name' => ['ko' => '자식 '.$prefix.$i], + 'slug' => $prefix.'-child-'.$i, + 'parent_id' => $root->id, + 'depth' => 1, + 'sort_order' => $i, + 'is_active' => true, + 'path' => '', + ]); + $child->update(['path' => $root->id.'/'.$child->id]); + } + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/StorefrontIndexCoverageTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/StorefrontIndexCoverageTest.php new file mode 100644 index 00000000..79ed85c2 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Performance/StorefrontIndexCoverageTest.php @@ -0,0 +1,269 @@ +getDriverName(), ['mysql', 'mariadb'], true)) { + $this->markTestSkipped('EXPLAIN 판정은 MySQL 계열에서만 수행합니다.'); + } + } + + /** + * 전시 조건 + 정렬을 덮는 색인이 존재하는지 확인 + * + * @effects display_sort_index_exists + */ + #[Test] + public function display_sort_indexes_exist(): void + { + $names = array_column(Schema::getIndexes('ecommerce_products'), 'name'); + + $this->assertContains('idx_products_display_created_id', $names, '등록일 정렬을 덮는 색인이 없다'); + $this->assertContains('idx_products_display_price_id', $names, '판매가 정렬을 덮는 색인이 없다'); + } + + /** + * 판매 집계용 커버링 색인이 존재하는지 확인 + * + * @effects sales_aggregate_index_exists + */ + #[Test] + public function sales_aggregate_index_exists(): void + { + $names = array_column(Schema::getIndexes('ecommerce_order_options'), 'name'); + + $this->assertContains( + 'idx_order_options_product_created_qty', + $names, + '판매 집계가 테이블 본문을 읽지 않도록 하는 색인이 없다' + ); + } + + /** + * 색인 컬럼 순서가 조건·정렬 순서와 일치하는지 확인 + * + * 컬럼 순서가 어긋나면 색인은 존재해도 정렬에 쓰이지 않는다. + * + * @effects product_list_uses_display_sort_index + */ + #[Test] + public function index_column_order_matches_query_shape(): void + { + $this->assertIndexColumns( + 'ecommerce_products', + 'idx_products_display_created_id', + ['display_status', 'created_at', 'id'] + ); + + $this->assertIndexColumns( + 'ecommerce_products', + 'idx_products_display_price_id', + ['display_status', 'selling_price', 'id'] + ); + + $this->assertIndexColumns( + 'ecommerce_order_options', + 'idx_order_options_product_created_qty', + ['product_id', 'created_at', 'quantity'] + ); + } + + /** + * 등록일 정렬 목록이 실제로 이 색인을 고르는지 EXPLAIN 으로 확인 + * + * @effects product_list_uses_display_sort_index + */ + #[Test] + public function query_plan_uses_the_display_sort_index(): void + { + $this->seedProductsForPlanning(); + + $query = DB::table('ecommerce_products') + ->where('display_status', 'visible') + ->orderBy('created_at', 'desc') + ->orderBy('id', 'desc') + ->limit(10); + + $this->assertQueryPlanUsesIndex($query, 'idx_products_display_created_id'); + } + + /** + * 판매가 정렬 목록이 실제로 이 색인을 고르는지 EXPLAIN 으로 확인 + * + * @effects product_list_uses_price_sort_index + */ + #[Test] + public function query_plan_uses_the_price_sort_index(): void + { + $this->seedProductsForPlanning(); + + $query = DB::table('ecommerce_products') + ->where('display_status', 'visible') + ->orderBy('selling_price', 'asc') + ->orderBy('id', 'asc') + ->limit(10); + + $this->assertQueryPlanUsesIndex($query, 'idx_products_display_price_id'); + } + + /** + * 판매 집계 파생 테이블이 실제로 이 색인을 고르는지 EXPLAIN 으로 확인 + * + * 이 색인은 `(product_id, created_at, quantity)` 커버링이라, 집계가 원본 행을 읽지 + * 않고 색인만으로 끝나야 한다. + * + * @effects sales_aggregate_uses_covering_index + */ + #[Test] + public function query_plan_uses_the_sales_aggregate_index(): void + { + $this->seedOrderOptionsForPlanning(); + + $query = DB::table('ecommerce_order_options') + ->select('product_id') + ->selectRaw('SUM(quantity) as total_sold') + ->where('created_at', '>=', now()->subDays(30)) + ->groupBy('product_id'); + + $this->assertQueryPlanUsesIndex($query, 'idx_order_options_product_created_qty'); + } + + /** + * 옵티마이저가 실제로 그 색인을 **선택**했는지 단언합니다. + * + * `possible_keys` 는 "쓸 수 있었다" 일 뿐이다. 후보에만 올라 있고 실제로는 전체 + * 스캔을 골라도 통과해 버리므로, 판정은 실행계획이 확정한 `key` 로만 한다. + * + * @param Builder $query 판정 대상 쿼리 + * @param string $indexName 선택되어야 하는 색인명 + */ + private function assertQueryPlanUsesIndex($query, string $indexName): void + { + $plan = DB::select('EXPLAIN '.$query->toSql(), $query->getBindings()); + + $this->assertNotEmpty($plan, 'EXPLAIN 결과가 비어 있다'); + + $row = (array) $plan[0]; + $chosen = (string) ($row['key'] ?? ''); + + $this->assertStringContainsString( + $indexName, + $chosen, + "옵티마이저가 {$indexName} 을 고르지 않았다 (선택: ".($chosen !== '' ? $chosen : '없음(전체 스캔)'). + ', 후보: '.($row['possible_keys'] ?? '없음').') — 컬럼 순서나 조건 형태가 어긋났다' + ); + } + + /** + * 실행계획이 색인을 고를 만큼 상품 행을 채웁니다. + * + * 행이 몇 건뿐이면 전체 스캔이 정상 선택이라 `key` 단언이 성립하지 않는다. + */ + private function seedProductsForPlanning(): void + { + $rows = []; + + for ($i = 0; $i < 300; $i++) { + $rows[] = [ + 'product_code' => 'PLAN-'.$i, + 'name' => json_encode(['ko' => '계획 '.$i], JSON_UNESCAPED_UNICODE), + 'selling_price' => 1000 + $i, + 'stock_quantity' => 10, + 'sales_status' => 'on_sale', + 'display_status' => $i % 10 === 0 ? 'hidden' : 'visible', + 'created_at' => now()->subMinutes($i), + 'updated_at' => now(), + ]; + } + + DB::table('ecommerce_products')->insert($rows); + DB::statement('ANALYZE TABLE '.DB::getTablePrefix().'ecommerce_products'); + } + + /** + * 실행계획이 색인을 고를 만큼 주문 옵션 행을 채웁니다. + * + * order_id / product_id 에 외래키가 걸려 있어 실제 부모 행이 먼저 있어야 한다. + */ + private function seedOrderOptionsForPlanning(): void + { + // 컬럼이 많고 필수 항목이 대부분이라 팩토리로 한 건을 만들어 형태를 얻은 뒤, + // 색인 판정에 필요한 축(product_id / created_at / quantity)만 바꿔 복제한다. + $seed = OrderOption::factory()->create(); + $template = $seed->getAttributes(); + unset($template['id']); + + $productIds = DB::table('ecommerce_products')->limit(30)->pluck('id')->all(); + + if (empty($productIds)) { + $this->seedProductsForPlanning(); + $productIds = DB::table('ecommerce_products')->limit(30)->pluck('id')->all(); + } + + $rows = []; + + for ($i = 0; $i < 300; $i++) { + $rows[] = array_merge($template, [ + // 상품 FK 는 실제 존재하는 행이어야 한다 + 'product_id' => $productIds[$i % count($productIds)], + 'quantity' => ($i % 5) + 1, + 'created_at' => now()->subDays($i % 40), + 'updated_at' => now(), + ]); + } + + DB::table('ecommerce_order_options')->insert($rows); + DB::statement('ANALYZE TABLE '.DB::getTablePrefix().'ecommerce_order_options'); + } + + /** + * 색인의 컬럼 구성을 단언합니다. + * + * @param string $table 테이블명 (프리픽스 제외) + * @param string $indexName 색인명 + * @param array $expected 기대 컬럼 순서 + */ + private function assertIndexColumns(string $table, string $indexName, array $expected): void + { + $index = collect(Schema::getIndexes($table))->firstWhere('name', $indexName); + + $this->assertNotNull($index, "색인이 없다: {$indexName}"); + $this->assertSame( + $expected, + array_values($index['columns']), + "색인 컬럼 순서가 조건·정렬 순서와 다르다: {$indexName}" + ); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/StorefrontCategoryContractTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/StorefrontCategoryContractTest.php new file mode 100644 index 00000000..d3f8ab10 --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/StorefrontCategoryContractTest.php @@ -0,0 +1,107 @@ +category = Category::create([ + 'name' => ['ko' => '통합조회 분류'], + 'slug' => 'storefront-contract', + 'depth' => 0, + 'sort_order' => 1, + 'is_active' => true, + 'path' => '', + ]); + + $this->category->update(['path' => (string) $this->category->id]); + } + + /** + * 통합 조회의 분류가 단건 엔드포인트와 같은 키 집합이어야 함 + * + * @effects storefront_category_matches_single_endpoint + */ + #[Test] + public function storefront_category_matches_single_endpoint(): void + { + $single = $this->getJson('/api/modules/sirsoft-ecommerce/categories/'.$this->category->slug); + $single->assertOk(); + + $combined = $this->getJson( + '/api/modules/sirsoft-ecommerce/storefront?category_slug='.$this->category->slug + ); + $combined->assertOk(); + + $expected = $single->json('data'); + $actual = $combined->json('data.category'); + + $this->assertIsArray($actual, '통합 조회 응답에 분류가 없다 — 분류 화면이 제목조차 못 그린다.'); + + $missing = array_diff(array_keys($expected), array_keys($actual)); + $this->assertSame( + [], + $missing, + '단건 엔드포인트에는 있고 통합 조회에는 없는 필드: '.implode(', ', $missing) + ); + + $this->assertSame($expected, $actual, '같은 분류인데 두 경로의 응답이 다르다.'); + } + + /** + * 분류를 지정하지 않으면 분류 키가 비어야 함 (쇼핑 첫 화면 경로) + * + * @effects storefront_without_slug_omits_category + */ + #[Test] + public function storefront_without_slug_omits_category(): void + { + $response = $this->getJson('/api/modules/sirsoft-ecommerce/storefront'); + + $response->assertOk(); + $this->assertNull($response->json('data.category')); + } + + /** + * 없는 분류를 가리키면 404 여야 함 + * + * 200 으로 빈 화면을 내보내면 검색엔진이 존재하지 않는 분류 주소를 정상 페이지로 수집한다. + * + * @effects storefront_unknown_slug_returns_not_found + */ + #[Test] + public function storefront_unknown_slug_returns_not_found(): void + { + $response = $this->getJson( + '/api/modules/sirsoft-ecommerce/storefront?category_slug=no-such-category' + ); + + $response->assertNotFound(); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/SearchProductsListenerTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/SearchProductsListenerTest.php index a3ebee74..a264101e 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/SearchProductsListenerTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/SearchProductsListenerTest.php @@ -2,7 +2,12 @@ namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Listeners; +use App\Enums\TotalRelation; +use App\Models\Permission; +use App\Models\Role; use App\Models\User; +use App\Support\GuestRoleResolver; +use App\Support\Query\BoundedCount; use Illuminate\Support\Facades\Gate; use Modules\Sirsoft\Ecommerce\Listeners\SearchProductsListener; use Modules\Sirsoft\Ecommerce\Services\ProductService; @@ -17,20 +22,47 @@ class SearchProductsListenerTest extends TestCase private ProductService $productService; + /** + * 권한 허용 여부 (권한 거부 케이스에서 false 로 바꾼다) + * + * Gate::before 는 **먼저 등록된** 콜백이 non-null 을 돌려주면 그 값으로 확정된다. + * setUp 에서 곧바로 true 를 등록하면 개별 테스트가 뒤에 false 를 등록해도 절대 + * 이기지 못하므로, 판정을 이 플래그로 미룬다. + */ + private bool $permissionsAllowed = true; + protected function setUp(): void { parent::setUp(); $this->productService = $this->createMock(ProductService::class); $this->listener = new SearchProductsListener($this->productService); - // 기본적으로 권한 허용 (개별 테스트에서 오버라이드 가능) - Gate::before(fn () => true); + // 기본적으로 권한 허용 (개별 테스트에서 $this->permissionsAllowed 로 오버라이드) + Gate::before(fn () => $this->permissionsAllowed ? true : false); + } + + /** + * guest 역할에서 상품 조회 권한을 떼어냅니다. + * + * 비회원 판정은 Gate 가 아니라 guest 역할의 권한 행을 보므로, 거부 케이스를 만들려면 + * 권한 자체를 없애야 합니다. 요청 스코프 캐시도 함께 비웁니다. + */ + private function revokeGuestProductReadPermission(): void + { + $permission = Permission::where('identifier', 'sirsoft-ecommerce.user-products.read')->first(); + $guestRole = Role::where('identifier', 'guest')->first(); + + if ($permission && $guestRole) { + $guestRole->permissions()->detach($permission->id); + } + + GuestRoleResolver::flush(); } /** * getSubscribedHooks()가 올바른 훅 목록을 반환하는지 확인 */ - public function test_getSubscribedHooks_returns_correct_hooks(): void + public function test_get_subscribed_hooks_returns_correct_hooks(): void { $hooks = SearchProductsListener::getSubscribedHooks(); @@ -52,7 +84,7 @@ class SearchProductsListenerTest extends TestCase /** * addValidationRules()가 sort에 가격순 옵션을 추가하는지 확인 */ - public function test_addValidationRules_adds_price_sort_and_category_id(): void + public function test_add_validation_rules_adds_price_sort_and_category_id(): void { $rules = ['q' => ['nullable', 'string', 'max:200']]; @@ -74,11 +106,11 @@ class SearchProductsListenerTest extends TestCase /** * searchProducts()가 type이 products가 아닐 때 count만 반환하는지 확인 */ - public function test_searchProducts_returns_count_only_when_type_is_not_products_or_all(): void + public function test_search_products_returns_count_only_when_type_is_not_products_or_all(): void { $this->productService ->method('countByKeyword') - ->willReturn(3); + ->willReturn(new BoundedCount(3, TotalRelation::Exact, 10000)); $results = []; $context = ['type' => 'posts', 'q' => '테스트', 'user' => User::factory()->make()]; @@ -90,10 +122,34 @@ class SearchProductsListenerTest extends TestCase $this->assertEmpty($result['products']['items']); } + /** + * 비활성 탭 배지도 잘린 건수를 정확한 것처럼 보고하지 않는지 확인 (#519) + * + * 배지는 오류를 내지 않고 그냥 틀린 숫자로만 보이므로, 정확도가 함께 실리지 않으면 + * 상한에 걸린 10,000 이 "정확히 10,000 건" 으로 화면에 나간다. + */ + public function test_count_only_badge_carries_accuracy_when_truncated(): void + { + $this->productService + ->method('countByKeyword') + ->willReturn(new BoundedCount(10000, TotalRelation::AtLeast, 10000)); + + $result = $this->listener->searchProducts([], [ + 'type' => 'posts', + 'q' => '테스트', + 'user' => User::factory()->make(), + ]); + + $this->assertSame(10000, $result['products']['total']); + $this->assertSame('at_least', $result['products']['total_relation']); + $this->assertFalse($result['products']['total_is_exact']); + $this->assertSame(10000, $result['products']['result_cap']); + } + /** * searchProducts()가 빈 검색어일 때 스킵하는지 확인 */ - public function test_searchProducts_skips_when_keyword_is_empty(): void + public function test_search_products_skips_when_keyword_is_empty(): void { $results = []; $context = ['type' => 'all', 'q' => '']; @@ -106,10 +162,10 @@ class SearchProductsListenerTest extends TestCase /** * searchProducts()가 권한 없을 때 빈 결과를 반환하는지 확인 */ - public function test_searchProducts_returns_empty_when_no_permission(): void + public function test_search_products_returns_empty_when_no_permission(): void { // 모든 권한 거부 - Gate::before(fn () => false); + $this->permissionsAllowed = false; $results = []; $context = ['type' => 'all', 'q' => '테스트', 'user' => User::factory()->make()]; @@ -123,25 +179,25 @@ class SearchProductsListenerTest extends TestCase /** * searchProducts()가 비회원(user=null)이고 guest 권한 없을 때 빈 결과를 반환하는지 확인 */ - public function test_searchProducts_returns_empty_for_guest_without_permission(): void + public function test_search_products_returns_empty_for_guest_without_permission(): void { - // Gate::before 해제 — guest는 Gate를 거치지 않으므로 DB 기반 체크 - // guest role에 권한이 없으면 거부됨 - Gate::before(fn () => null); // null = Gate::before가 판단하지 않음 + // 비회원 판정은 Gate 가 아니라 guest 역할의 권한 행을 본다. + // 기본 설치의 guest 는 상품 조회 권한을 가지므로, 그 권한을 실제로 떼고 확인한다. + $this->revokeGuestProductReadPermission(); $results = []; $context = ['type' => 'all', 'q' => '테스트', 'user' => null]; $result = $this->listener->searchProducts($results, $context); - // guest 권한이 DB에 없으므로 빈 결과 + // guest 권한이 없으므로 빈 결과 $this->assertArrayNotHasKey('products', $result); } /** * buildProductsResponse()가 products 결과가 없을 때 스킵하는지 확인 */ - public function test_buildProductsResponse_skips_when_no_products_results(): void + public function test_build_products_response_skips_when_no_products_results(): void { $response = ['posts_count' => 5]; $results = []; // products 키 없음 @@ -155,12 +211,15 @@ class SearchProductsListenerTest extends TestCase } /** - * buildProductsResponse()가 all 탭에서 5개로 제한하는지 확인 + * buildProductsResponse()가 all 탭에서 조회된 항목을 그대로 싣는지 확인 + * + * 미리보기 개수만큼만 조회해 오므로 여기서 다시 자르지 않는다. 종전에는 페이지 분량을 + * 받아 PHP 에서 잘랐고, 그만큼 쓰이지 않을 행까지 읽었다. */ - public function test_buildProductsResponse_limits_items_for_all_tab(): void + public function test_build_products_response_passes_through_items_for_all_tab(): void { $items = []; - for ($i = 0; $i < 10; $i++) { + for ($i = 0; $i < 5; $i++) { $items[] = ['id' => $i + 1, 'name' => "상품 {$i}"]; } @@ -177,7 +236,7 @@ class SearchProductsListenerTest extends TestCase /** * buildProductsResponse()가 products 탭에서 페이지네이션 정보를 추가하는지 확인 */ - public function test_buildProductsResponse_adds_pagination_for_products_tab(): void + public function test_build_products_response_adds_pagination_for_products_tab(): void { $items = []; for ($i = 0; $i < 10; $i++) { @@ -185,7 +244,12 @@ class SearchProductsListenerTest extends TestCase } $response = []; - $results = ['products' => ['total' => 25, 'items' => $items]]; + $results = ['products' => [ + 'total' => 25, + 'items' => $items, + 'last_page' => 3, + 'has_more_pages' => true, + ]]; $context = ['type' => 'products', 'page' => 1, 'per_page' => 10]; $result = $this->listener->buildProductsResponse($response, $results, $context); @@ -195,14 +259,40 @@ class SearchProductsListenerTest extends TestCase $this->assertArrayHasKey('last_page', $result); $this->assertEquals(1, $result['current_page']); $this->assertEquals(10, $result['per_page']); - $this->assertEquals(3, $result['last_page']); // ceil(25/10) = 3 + $this->assertEquals(3, $result['last_page']); + $this->assertTrue($result['has_more_pages']); $this->assertCount(10, $result['products']); } + /** + * 총 건수가 상한에 걸리면 마지막 페이지가 null 로 전달되는지 확인 + */ + public function test_build_products_response_omits_last_page_when_total_is_bounded(): void + { + $response = []; + $results = ['products' => [ + 'total' => 10000, + 'total_is_exact' => false, + 'total_relation' => 'at_least', + 'result_cap' => 10000, + 'items' => [['id' => 1]], + 'last_page' => null, + 'has_more_pages' => true, + ]]; + $context = ['type' => 'products', 'page' => 1, 'per_page' => 10]; + + $result = $this->listener->buildProductsResponse($response, $results, $context); + + $this->assertNull($result['last_page'], '마지막 페이지는 계산 불가'); + $this->assertTrue($result['has_more_pages'], '"다음" 이동은 계속 열려 있다'); + $this->assertFalse($result['products_total_is_exact']); + $this->assertEquals('at_least', $result['products_total_relation']); + } + /** * buildProductsResponse()가 products_count를 설정하는지 확인 */ - public function test_buildProductsResponse_sets_products_count(): void + public function test_build_products_response_sets_products_count(): void { $response = []; $results = ['products' => ['total' => 7, 'items' => [['id' => 1]]]]; @@ -216,7 +306,7 @@ class SearchProductsListenerTest extends TestCase /** * buildProductsResponse()가 rating_avg, review_count 필드를 유지하는지 확인 */ - public function test_buildProductsResponse_preserves_rating_fields(): void + public function test_build_products_response_preserves_rating_fields(): void { $items = [ ['id' => 1, 'name' => '상품1', 'rating_avg' => 4.5, 'review_count' => 10], diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/ProductBulkAdjustmentTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/ProductBulkAdjustmentTest.php new file mode 100644 index 00000000..a2ca878f --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/ProductBulkAdjustmentTest.php @@ -0,0 +1,220 @@ +repository = app(ProductRepositoryInterface::class); + } + + /** + * 상품 하나를 만듭니다. + * + * @param string $code 상품 코드 + * @param float $price 판매가 + * @param int $stock 재고 수량 + * @return Product 생성된 상품 + */ + private function makeProduct(string $code, float $price, int $stock = 10): Product + { + return Product::create([ + 'product_code' => $code, + 'name' => ['ko' => '상품 '.$code], + 'selling_price' => $price, + 'stock_quantity' => $stock, + 'sales_status' => 'on_sale', + 'display_status' => 'visible', + ]); + } + + /** + * 정액 증액이 지정한 금액만큼 정확히 오르는지 확인 + * + * @effects bulk_price_increase_applies_exact_amount + */ + public function test_bulk_price_increase_applies_exact_amount(): void + { + // Given: 판매가가 서로 다른 상품 2건 + $a = $this->makeProduct('BP-A', 1000); + $b = $this->makeProduct('BP-B', 2500); + + // When: 정액 1000원 증액 + $affected = $this->repository->bulkUpdatePrice([$a->id, $b->id], 'increase', 1000, 'amount'); + + // Then: 각 행이 정확히 1000원씩 오른다 + $this->assertSame(2, $affected); + $this->assertEquals(2000.0, (float) $a->fresh()->selling_price); + $this->assertEquals(3500.0, (float) $b->fresh()->selling_price); + } + + /** + * 정액 감액이 지정한 금액만큼 정확히 내리는지 확인 + * + * @effects bulk_price_increase_applies_exact_amount + */ + public function test_bulk_price_decrease_applies_exact_amount(): void + { + // Given: 판매가 5000원 상품 + $product = $this->makeProduct('BP-C', 5000); + + // When: 정액 1500원 감액 + $this->repository->bulkUpdatePrice([$product->id], 'decrease', 1500, 'amount'); + + // Then: 3500원 + $this->assertEquals(3500.0, (float) $product->fresh()->selling_price); + } + + /** + * 정률 증감이 지정한 비율만큼 정확히 반영되는지 확인 + * + * @effects bulk_price_percent_applies_exact_ratio + */ + public function test_bulk_price_percent_applies_exact_ratio(): void + { + // Given: 판매가 1000원 상품 2건 + $up = $this->makeProduct('BP-D', 1000); + $down = $this->makeProduct('BP-E', 1000); + + // When: 각각 10% 증액 / 10% 감액 + $this->repository->bulkUpdatePrice([$up->id], 'increase', 10, 'percent'); + $this->repository->bulkUpdatePrice([$down->id], 'decrease', 10, 'percent'); + + // Then: 1100 / 900 + $this->assertEquals(1100.0, (float) $up->fresh()->selling_price); + $this->assertEquals(900.0, (float) $down->fresh()->selling_price); + } + + /** + * 감액 결과가 음수가 되지 않고 0 에서 멈추는지 확인 + * + * @effects bulk_price_floors_at_zero + */ + public function test_bulk_price_decrease_floors_at_zero(): void + { + // Given: 판매가 500원 상품 + $product = $this->makeProduct('BP-F', 500); + + // When: 판매가보다 큰 금액을 감액 + $this->repository->bulkUpdatePrice([$product->id], 'decrease', 900, 'amount'); + + // Then: 음수가 아니라 0 + $this->assertEquals(0.0, (float) $product->fresh()->selling_price); + } + + /** + * 재고 증감이 지정한 수량만큼 정확히 반영되는지 확인 + * + * @effects bulk_stock_increase_applies_exact_amount + */ + public function test_bulk_stock_increase_and_decrease_apply_exact_amount(): void + { + // Given: 재고가 서로 다른 상품 2건 + $a = $this->makeProduct('BS-A', 1000, 10); + $b = $this->makeProduct('BS-B', 1000, 40); + + // When: 두 건을 함께 5 증가 + $this->repository->bulkUpdateStock([$a->id, $b->id], 'increase', 5); + + // Then: 각각 정확히 5씩 증가 + $this->assertSame(15, (int) $a->fresh()->stock_quantity); + $this->assertSame(45, (int) $b->fresh()->stock_quantity); + + // When: 두 건을 함께 3 감소 + $this->repository->bulkUpdateStock([$a->id, $b->id], 'decrease', 3); + + // Then: 각각 정확히 3씩 감소 + $this->assertSame(12, (int) $a->fresh()->stock_quantity); + $this->assertSame(42, (int) $b->fresh()->stock_quantity); + } + + /** + * 재고 감소 결과가 음수가 되지 않고 0 에서 멈추는지 확인 + * + * @effects bulk_stock_floors_at_zero + */ + public function test_bulk_stock_decrease_floors_at_zero(): void + { + // Given: 재고 2개 상품 + $product = $this->makeProduct('BS-C', 1000, 2); + + // When: 재고보다 많은 수량을 감소 + $this->repository->bulkUpdateStock([$product->id], 'decrease', 10); + + // Then: 음수가 아니라 0 + $this->assertSame(0, (int) $product->fresh()->stock_quantity); + } + + /** + * 지정하지 않은 행이 영향받지 않는지 확인 + * + * WHERE 바인딩이 SET 절로 밀리면 대상 선택 자체가 어긋난다. + * + * @effects bulk_adjustment_touches_only_selected_rows + */ + public function test_bulk_adjustment_touches_only_selected_rows(): void + { + // Given: 대상 2건 + 비대상 1건 + $target1 = $this->makeProduct('BT-1', 1000, 10); + $target2 = $this->makeProduct('BT-2', 1000, 10); + $untouched = $this->makeProduct('BT-3', 1000, 10); + + // When: 대상 2건만 지정해 증감 + $this->repository->bulkUpdatePrice([$target1->id, $target2->id], 'increase', 500, 'amount'); + $this->repository->bulkUpdateStock([$target1->id, $target2->id], 'increase', 7); + + // Then: 대상만 바뀌고 비대상은 그대로 + $this->assertEquals(1500.0, (float) $target1->fresh()->selling_price); + $this->assertEquals(1500.0, (float) $target2->fresh()->selling_price); + $this->assertEquals(1000.0, (float) $untouched->fresh()->selling_price); + $this->assertSame(17, (int) $target1->fresh()->stock_quantity); + $this->assertSame(10, (int) $untouched->fresh()->stock_quantity); + } + + /** + * 'set' 경로가 종전대로 동작하는지 확인 (회귀 방지) + * + * @effects bulk_price_increase_applies_exact_amount + */ + public function test_bulk_set_replaces_value(): void + { + // Given: 판매가 1000원 / 재고 10 상품 + $product = $this->makeProduct('BSET-1', 1000, 10); + + // When: set 으로 지정 + $this->repository->bulkUpdatePrice([$product->id], 'set', 7777, 'amount'); + $this->repository->bulkUpdateStock([$product->id], 'set', 33); + + // Then: 지정값 그대로 + $this->assertEquals(7777.0, (float) $product->fresh()->selling_price); + $this->assertSame(33, (int) $product->fresh()->stock_quantity); + } +} diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CartServiceTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CartServiceTest.php index f06c89e9..6fe8d088 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CartServiceTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CartServiceTest.php @@ -90,6 +90,12 @@ class CartServiceTest extends ModuleTestCase ->andReturn(0) ->byDefault(); + // 병합 경로는 상품별 합계를 시작 시점에 한 번만 읽는다 (항목마다 조회하지 않는다). + $this->mockCartRepository + ->shouldReceive('sumQuantityByProducts') + ->andReturn([]) + ->byDefault(); + $this->mockProductRepository ->shouldReceive('find') ->andReturnUsing(fn ($id) => Product::find($id)) @@ -729,17 +735,18 @@ class CartServiceTest extends ModuleTestCase ->once() ->andReturn($guestItems); - // Mock stock check in mergeGuestCartToUser + // 병합은 항목마다 조회하지 않고 루프 밖에서 한 번에 읽는다 + // (옵션 상세 1회 + 회원 장바구니 라인 1회) $this->mockProductOptionRepository - ->shouldReceive('findById') - ->with($option->id) - ->andReturn($option); + ->shouldReceive('findByIds') + ->once() + ->andReturn(new Collection([$option])); $this->mockCartRepository - ->shouldReceive('findAllByUserAndOption') - ->with($user->id, $option->id) + ->shouldReceive('findAllByUserAndOptions') + ->with($user->id, Mockery::any()) ->once() - ->andReturn(new Collection); + ->andReturn(collect()); $this->mockCartRepository ->shouldReceive('update') @@ -777,16 +784,18 @@ class CartServiceTest extends ModuleTestCase ->andReturn($guestItems); // Mock stock check in mergeGuestCartToUser + // 병합은 항목마다 조회하지 않고 루프 밖에서 한 번에 읽는다 + // (옵션 상세 1회 + 회원 장바구니 라인 1회) $this->mockProductOptionRepository - ->shouldReceive('findById') - ->with($option->id) - ->andReturn($option); + ->shouldReceive('findByIds') + ->once() + ->andReturn(new Collection([$option])); $this->mockCartRepository - ->shouldReceive('findAllByUserAndOption') - ->with($user->id, $option->id) + ->shouldReceive('findAllByUserAndOptions') + ->with($user->id, Mockery::any()) ->once() - ->andReturn(new Collection([$userCart])); + ->andReturn(collect([$option->id => new Collection([$userCart])])); $combinedCart = clone $userCart; $combinedCart->quantity = 5; @@ -1162,22 +1171,19 @@ class CartServiceTest extends ModuleTestCase ->once() ->andReturn($guestItems); - // Mock stock check in mergeGuestCartToUser + // 병합은 항목마다 조회하지 않고 루프 밖에서 한 번에 읽는다 + // (옵션 상세 1회 + 회원 장바구니 라인 1회) $this->mockProductOptionRepository - ->shouldReceive('findById') - ->with($optionA->id) - ->andReturn($optionA); - $this->mockProductOptionRepository - ->shouldReceive('findById') - ->with($optionB->id) - ->andReturn($optionB); - - // 옵션A: 회원 장바구니에 존재 → 합산 - $this->mockCartRepository - ->shouldReceive('findAllByUserAndOption') - ->with($user->id, $optionA->id) + ->shouldReceive('findByIds') ->once() - ->andReturn(new Collection([$userCartA])); + ->andReturn(new Collection([$optionA, $optionB])); + + // 옵션A 는 회원 장바구니에 존재(합산), 옵션B 는 없음(user_id 이관) + $this->mockCartRepository + ->shouldReceive('findAllByUserAndOptions') + ->with($user->id, Mockery::any()) + ->once() + ->andReturn(collect([$optionA->id => new Collection([$userCartA])])); $mergedCartA = clone $userCartA; $mergedCartA->quantity = 5; @@ -1195,13 +1201,6 @@ class CartServiceTest extends ModuleTestCase ->with($guestCartA) ->once(); - // 옵션B: 회원 장바구니에 없음 → user_id 업데이트 - $this->mockCartRepository - ->shouldReceive('findAllByUserAndOption') - ->with($user->id, $optionB->id) - ->once() - ->andReturn(new Collection); - $updatedGuestCartB = clone $guestCartB; $updatedGuestCartB->user_id = $user->id; $updatedGuestCartB->cart_key = null; @@ -1598,18 +1597,19 @@ class CartServiceTest extends ModuleTestCase ->once() ->andReturn(new Collection([$guestCart])); - // Mock stock check in mergeGuestCartToUser - 재고 5개 반환 + // 병합은 항목마다 조회하지 않고 루프 밖에서 한 번에 읽는다 + // (옵션 상세 1회 + 회원 장바구니 라인 1회) $this->mockProductOptionRepository - ->shouldReceive('findById') - ->with($option->id) - ->andReturn($option); + ->shouldReceive('findByIds') + ->once() + ->andReturn(new Collection([$option])); // 회원 장바구니에 같은 옵션 있음 $this->mockCartRepository - ->shouldReceive('findAllByUserAndOption') - ->with($user->id, $option->id) + ->shouldReceive('findAllByUserAndOptions') + ->with($user->id, Mockery::any()) ->once() - ->andReturn(new Collection([$userCart])); + ->andReturn(collect([$option->id => new Collection([$userCart])])); // 기대: 수량을 재고(5개)로 조정하여 업데이트 $updatedUserCart = clone $userCart; diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CategoryServiceTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CategoryServiceTest.php index 75e05833..3e551499 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CategoryServiceTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/CategoryServiceTest.php @@ -2,6 +2,7 @@ namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Services; +use Illuminate\Database\Eloquent\Relations\HasMany; use Mockery; use Modules\Sirsoft\Ecommerce\Models\Category; use Modules\Sirsoft\Ecommerce\Repositories\Contracts\CategoryImageRepositoryInterface; @@ -50,13 +51,14 @@ class CategoryServiceTest extends ModuleTestCase $category = Mockery::mock(Category::class)->makePartial(); $category->shouldReceive('getAttribute')->with('id')->andReturn(1); - $mockImages = Mockery::mock(\Illuminate\Database\Eloquent\Relations\HasMany::class); + $mockImages = Mockery::mock(HasMany::class); $mockImages->shouldReceive('delete')->once(); $category->shouldReceive('images')->once()->andReturn($mockImages); $this->mockRepository ->shouldReceive('findById') - ->with(1) + // 쓰기 경로는 상품 수·자식 수 집계를 읽지 않으므로 withCounts=false 로 조회한다 + ->with(1, [], false) ->once() ->andReturn($category); @@ -97,13 +99,14 @@ class CategoryServiceTest extends ModuleTestCase $category = Mockery::mock(Category::class)->makePartial(); $category->shouldReceive('getAttribute')->with('id')->andReturn(1); - $mockChildren = Mockery::mock(\Illuminate\Database\Eloquent\Relations\HasMany::class); + $mockChildren = Mockery::mock(HasMany::class); $mockChildren->shouldReceive('count')->andReturn(2); $category->shouldReceive('children')->andReturn($mockChildren); $this->mockRepository ->shouldReceive('findById') - ->with(1) + // 쓰기 경로는 상품 수·자식 수 집계를 읽지 않으므로 withCounts=false 로 조회한다 + ->with(1, [], false) ->once() ->andReturn($category); @@ -128,7 +131,8 @@ class CategoryServiceTest extends ModuleTestCase $this->mockRepository ->shouldReceive('findById') - ->with(1) + // 쓰기 경로는 상품 수·자식 수 집계를 읽지 않으므로 withCounts=false 로 조회한다 + ->with(1, [], false) ->once() ->andReturn($category); diff --git a/modules/_bundled/sirsoft-ecommerce/upgrades/Upgrade_1_0_5.php b/modules/_bundled/sirsoft-ecommerce/upgrades/Upgrade_1_0_5.php index d1f96c3c..548c961c 100644 --- a/modules/_bundled/sirsoft-ecommerce/upgrades/Upgrade_1_0_5.php +++ b/modules/_bundled/sirsoft-ecommerce/upgrades/Upgrade_1_0_5.php @@ -19,6 +19,10 @@ use App\Extension\AbstractUpgradeStep; * 반영되므로 업그레이드 시점에 동일 인덱스를 적용한다. 데이터가 많은 쇼핑몰에서는 * ALTER TABLE 이 수 분 걸리며 그동안 해당 테이블 쓰기가 대기한다. * + * 06_AddStorefrontIndexes 는 쇼핑 진열·판매 집계 색인이다 — 전시 조건으로 좁힌 뒤 + * 등록일/판매가로 정렬하는 공개 상품 목록과, 주문 옵션을 상품별로 묶어 수량을 더하는 + * 판매량 집계가 쓴다. + * * 3. 주문에 보관된 배송정책 스냅샷을 리스트+메타 구조로 정규화한다. * 항목 목록과 배송지 메타를 한 배열에 섞어 담아 json_encode 가 리스트가 아닌 객체로 * 직렬화했고, 배열을 전제한 주문 상세 화면에서 배송정책 줄이 통째로 사라졌다. diff --git a/modules/_bundled/sirsoft-ecommerce/upgrades/data/1.0.5/migrations/06_AddStorefrontIndexes.php b/modules/_bundled/sirsoft-ecommerce/upgrades/data/1.0.5/migrations/06_AddStorefrontIndexes.php new file mode 100644 index 00000000..1c1d09bc --- /dev/null +++ b/modules/_bundled/sirsoft-ecommerce/upgrades/data/1.0.5/migrations/06_AddStorefrontIndexes.php @@ -0,0 +1,77 @@ + [테이블, 컬럼]] + * + * @var array}> + */ + private const TARGETS = [ + 'idx_products_display_created_id' => ['ecommerce_products', ['display_status', 'created_at', 'id']], + 'idx_products_display_price_id' => ['ecommerce_products', ['display_status', 'selling_price', 'id']], + 'idx_order_options_product_created_qty' => ['ecommerce_order_options', ['product_id', 'created_at', 'quantity']], + ]; + + /** + * 마이그레이션 식별자를 반환합니다. + * + * @return string 마이그레이션 이름 + */ + public function name(): string + { + return 'AddStorefrontIndexes'; + } + + /** + * 진열·집계 색인을 적용합니다. + * + * @param UpgradeContext $context 업그레이드 컨텍스트 + */ + public function run(UpgradeContext $context): void + { + foreach (self::TARGETS as $indexName => [$table, $columns]) { + if (! Schema::hasTable($table)) { + $context->logger->info("[ecommerce:1.0.5] 테이블 부재 — 색인 적용 스킵: {$table}"); + + continue; + } + + if (in_array($indexName, array_column(Schema::getIndexes($table), 'name'), true)) { + $context->logger->info("[ecommerce:1.0.5] 이미 적용된 색인 — 스킵: {$indexName}"); + + continue; + } + + $context->logger->info("[ecommerce:1.0.5] 진열·집계 색인 적용 시작: {$table} (데이터가 많으면 수 분 걸릴 수 있고 그동안 해당 테이블 쓰기가 대기합니다)"); + + Schema::table($table, function ($blueprint) use ($columns, $indexName) { + $blueprint->index($columns, $indexName); + }); + + $context->logger->info("[ecommerce:1.0.5] 색인 적용 완료: {$indexName}"); + } + } +} From e619b21b2b743e3d6dc244d9ff0c75a40bafa35b Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:35:00 +0900 Subject: [PATCH 4/8] =?UTF-8?q?perf(page):=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=A0=84=EB=9F=89=20=EC=A0=81=EC=9E=AC=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=ED=95=9C=20=EA=B3=84=EC=95=BD=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 검색이 조건에 맞는 페이지 전체를 읽어 온 뒤 PHP 에서 화면 분량만 잘라 내고 있었다. 실제 page/per_page 를 저장소까지 하달해 필요한 분량만 조회한다. 구체 엔진 직접 호출도 해석기 경유로 옮겨 플러그인 검색엔진이 페이지 검색에 적용된다. 관리자 목록은 화면에 본문이 나오지 않는데도 항목마다 본문을 함께 읽고 있었다. 목록 조회에서 제외하고 총 건수에 상한 계약을 적용했다. 발행 페이지 목록과 통합검색의 페이지 탭 정렬에 필요한 색인을 더했다. 신규 설치는 마이그레이션이, 기존 사이트는 업그레이드 스텝이 적용한다. 관리자 목록의 페이저가 총 건수를 끝까지 세지 못한 경우 "1페이지뿐" 으로 접혀 뒤쪽 페이지에 도달할 수 없던 것을 고쳤다. --- modules/_bundled/sirsoft-page/CHANGELOG.md | 12 ++ ...dd_published_sort_index_to_pages_table.php | 72 +++++++++ .../layouts/admin/admin_page_list.json | 3 +- .../src/Listeners/SearchPagesListener.php | 92 ++++++++--- .../Contracts/PageRepositoryInterface.php | 40 ++++- .../src/Repositories/PageRepository.php | 103 ++++++++---- .../sirsoft-page/src/Services/PageService.php | 67 +++++++- .../ListQueryCountRegressionTest.php | 77 +++++++++ .../PublishedSortIndexCoverageTest.php | 117 ++++++++++++++ .../Repositories/PageRepositorySearchTest.php | 25 +-- .../Listeners/SearchPagesListenerTest.php | 146 ++++++++++++++---- .../sirsoft-page/upgrades/Upgrade_1_0_2.php | 3 + .../migrations/02_AddPublishedSortIndex.php | 78 ++++++++++ 13 files changed, 727 insertions(+), 108 deletions(-) create mode 100644 modules/_bundled/sirsoft-page/database/migrations/2026_08_02_000001_add_published_sort_index_to_pages_table.php create mode 100644 modules/_bundled/sirsoft-page/tests/Feature/Performance/ListQueryCountRegressionTest.php create mode 100644 modules/_bundled/sirsoft-page/tests/Feature/Performance/PublishedSortIndexCoverageTest.php create mode 100644 modules/_bundled/sirsoft-page/upgrades/data/1.0.2/migrations/02_AddPublishedSortIndex.php diff --git a/modules/_bundled/sirsoft-page/CHANGELOG.md b/modules/_bundled/sirsoft-page/CHANGELOG.md index 7508bc3c..8fc5c5e1 100644 --- a/modules/_bundled/sirsoft-page/CHANGELOG.md +++ b/modules/_bundled/sirsoft-page/CHANGELOG.md @@ -17,9 +17,14 @@ ### Changed +- 통합 검색의 페이지 결과를 최신순·오래된순으로 볼 때 이어보기 방식으로 뒤쪽 페이지를 이동할 수 있습니다. +- 플러그인으로 다른 검색엔진을 연결하면 페이지 검색에도 그 엔진이 적용됩니다. 이전에는 연결한 엔진과 무관하게 기본 방식으로만 검색됐습니다. - 사이트맵을 만들 때 페이지를 한 번에 모두 메모리에 올리지 않고 나눠서 읽도록 바꿨습니다. 페이지가 많은 사이트에서 사이트맵 생성 중 메모리가 부족해 실패하던 문제가 줄어듭니다. - 페이지 목록을 뒤쪽 페이지에서도 빠르게 열 수 있도록 조회 방식을 바꿨습니다. 예전에는 페이지가 뒤로 갈수록 건너뛰는 항목의 본문까지 함께 읽어 느려졌지만, 이제 현재 페이지의 항목만 본문을 읽습니다. 화면에 보이는 내용은 이전과 동일합니다. (#74 @jiwonpapa 님께서 제보해주셨습니다.) - 관리자 페이지 목록을 열 때 페이지 본문을 데이터베이스에서 읽지 않습니다. 목록은 제목·주소·발행 여부만 쓰는데 본문까지 함께 읽고 있었습니다. 본문이 긴 페이지가 많은 사이트일수록 목록을 여는 속도와 서버 메모리 사용이 개선되며, 화면에 보이는 내용은 이전과 동일합니다. (#76 @jordy-bitree 님께서 제보해주셨습니다.) +- 발행 페이지 목록과 통합검색의 페이지 탭 정렬에 필요한 색인을 추가했습니다. 기존 사이트도 업데이트 시 자동으로 반영됩니다. +- 페이지 검색 탭의 배지 숫자도 세지 못한 건수를 정확한 것처럼 표시하지 않습니다. 배지를 표시하지 않는 화면에서는 그 숫자를 세는 작업 자체를 건너뜁니다. +- 관리자 페이지 목록의 총 건수도 일정 규모까지만 정확히 세고 그보다 많으면 "N건 이상" 으로 표시합니다. 페이지 이동은 끝까지 열려 있습니다. - 개선된 사이트맵 생성 기능을 사용하기 위해 이 모듈은 이제 코어 7.0.6 이상이 필요합니다. - 첨부파일 저장 디스크 설정 항목을 제거했습니다. 설정 파일 자체가 그 디스크에 저장되는 구조라 값을 바꿔도 반영될 수 없는 항목이었습니다(기존에도 항상 기본값으로 동작). 저장 위치는 서버 파일 저장소 설정으로 관리합니다. (#81 @jiwonpapa 님께서 제보해주셨습니다.) @@ -35,6 +40,13 @@ - 통합검색의 페이지 결과에서 같은 시각에 등록된 페이지가 여러 건일 때 목록 순서가 흔들리던 문제를 수정했습니다. - 페이지 관리 목록에서 뒤쪽 페이지를 보던 중 정렬을 바꾸면 이전 페이지 번호에 그대로 머물러 엉뚱한 구간이 보이던 문제를 수정했습니다. 이제 정렬을 바꾸면 첫 페이지부터 보여줍니다. +- 페이지 관리 목록에서 페이지가 아주 많아 총 건수를 끝까지 세지 못하면 "1페이지뿐" 으로 표시되어 뒤쪽 페이지를 볼 수 없던 문제를 수정했습니다. +### Changed + +- 페이지 검색이 조건에 맞는 페이지 전체를 읽어 온 뒤 화면에 보일 만큼만 잘라 내던 방식을 바꿨습니다. 이제 필요한 페이지 분량만 조회하므로, 페이지가 많은 사이트에서 검색이 빨라지고 메모리 사용량이 줄어듭니다. +- 검색 결과가 아주 많으면 총 건수를 "N건 이상" 으로 표시하고, 다음 페이지 이동은 끝까지 열어 둡니다. +- 발행 페이지 목록의 정렬 색인을 추가했습니다. 기존 사이트도 업데이트 시 자동으로 반영됩니다. + ## [1.0.1] - 2026-07-08 ### Fixed diff --git a/modules/_bundled/sirsoft-page/database/migrations/2026_08_02_000001_add_published_sort_index_to_pages_table.php b/modules/_bundled/sirsoft-page/database/migrations/2026_08_02_000001_add_published_sort_index_to_pages_table.php new file mode 100644 index 00000000..166ea1b4 --- /dev/null +++ b/modules/_bundled/sirsoft-page/database/migrations/2026_08_02_000001_add_published_sort_index_to_pages_table.php @@ -0,0 +1,72 @@ + + */ + private const COLUMNS = ['published', 'created_at', 'id']; + + /** + * Run the migrations. + */ + public function up(): void + { + if (! Schema::hasTable(self::TABLE)) { + return; + } + + if (in_array(self::INDEX, array_column(Schema::getIndexes(self::TABLE), 'name'), true)) { + return; + } + + Schema::table(self::TABLE, function (Blueprint $blueprint) { + $blueprint->index(self::COLUMNS, self::INDEX); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + if (! Schema::hasTable(self::TABLE)) { + return; + } + + if (! in_array(self::INDEX, array_column(Schema::getIndexes(self::TABLE), 'name'), true)) { + return; + } + + Schema::table(self::TABLE, function (Blueprint $blueprint) { + $blueprint->dropIndex(self::INDEX); + }); + } +}; diff --git a/modules/_bundled/sirsoft-page/resources/layouts/admin/admin_page_list.json b/modules/_bundled/sirsoft-page/resources/layouts/admin/admin_page_list.json index 697e4f81..669df1e4 100644 --- a/modules/_bundled/sirsoft-page/resources/layouts/admin/admin_page_list.json +++ b/modules/_bundled/sirsoft-page/resources/layouts/admin/admin_page_list.json @@ -766,7 +766,8 @@ "pageSize": 20, "serverSidePagination": true, "serverCurrentPage": "{{pages?.data?.meta?.current_page ?? 1}}", - "serverTotalPages": "{{pages?.data?.meta?.last_page ?? 1}}", + "serverTotalPages": "{{pages?.data?.meta?.last_page ?? null}}", + "serverHasMorePages": "{{pages?.data?.meta?.has_more_pages ?? false}}", "selectable": true, "selectedCountText": "", "selectedIds": "{{_global.selectedIds_admin_pages || []}}", diff --git a/modules/_bundled/sirsoft-page/src/Listeners/SearchPagesListener.php b/modules/_bundled/sirsoft-page/src/Listeners/SearchPagesListener.php index 25196217..b547eae4 100644 --- a/modules/_bundled/sirsoft-page/src/Listeners/SearchPagesListener.php +++ b/modules/_bundled/sirsoft-page/src/Listeners/SearchPagesListener.php @@ -3,6 +3,9 @@ namespace Modules\Sirsoft\Page\Listeners; use App\Contracts\Extension\HookListenerInterface; +use App\Enums\TotalRelation; +use App\Search\SearchCategoryPayload; +use App\Support\Query\BoundedCount; use Illuminate\Support\Facades\Log; use Modules\Sirsoft\Page\Services\PageService; @@ -89,10 +92,22 @@ class SearchPagesListener implements HookListenerInterface try { if (! $isRelevantTab) { - $results['pages'] = [ - 'total' => $this->pageService->countByKeyword($q), - 'items' => [], - ]; + // 다른 탭을 보는 중이라도 탭 배지에는 건수가 필요하다. 다만 상한을 건 + // 집계라 대량 매칭에서도 비용이 일정하다. + // 잘린 값을 정확한 것처럼 내보내지 않도록 정확도를 함께 싣는다. + // + // 배지를 그리지 않는 화면은 건수를 요청하지 않는다 — 그 경우 집계를 생략한다. + if (! ($context['include_inactive_counts'] ?? true)) { + $results['pages'] = SearchCategoryPayload::fromCountOnly( + new BoundedCount(0, TotalRelation::Exact, null) + ); + + return $results; + } + + $results['pages'] = SearchCategoryPayload::fromCountOnly( + $this->pageService->countByKeyword($q) + ); return $results; } @@ -100,18 +115,38 @@ class SearchPagesListener implements HookListenerInterface $sort = $context['sort'] ?? 'relevance'; [$orderBy, $direction] = $this->resolveSortOrder($sort); - $limit = ($type === 'all') - ? ($context['all_tab_limit'] ?? 5) - : PHP_INT_MAX; + // 전체 탭은 미리보기 몇 건, 페이지 탭은 실제 요청 페이지를 그대로 하달한다. + // 종전에는 PHP_INT_MAX 로 매칭 전량을 읽고 PHP 에서 잘라냈다. + $isAllTab = ($type === 'all'); + $perPage = $isAllTab + ? (int) ($context['all_tab_limit'] ?? 5) + : (int) ($context['per_page'] ?? 10); + $pageNumber = $isAllTab ? 1 : (int) ($context['page'] ?? 1); - $searchResult = $this->pageService->searchByKeyword($q, $orderBy, $direction, $limit); + /** 조회 결과 항목을 화면 형태로 가공한다. */ + $format = fn (iterable $items): array => collect($items) + ->map(fn ($page) => $this->formatPageResult($page, $q)) + ->toArray(); - $results['pages'] = [ - 'total' => $searchResult['total'], - 'items' => $searchResult['items']->map( - fn ($page) => $this->formatPageResult($page, $q) - )->toArray(), - ]; + // 커서를 받았고 그 정렬을 커서로 처리할 수 있으면 키셋으로 응답한다. + // 전체 탭은 미리보기 몇 건뿐이라 깊은 페이지가 없어 대상이 아니다. + $cursorPage = $isAllTab + ? null + : $this->pageService->searchByKeywordWithCursor($q, $sort, $perPage, $context['cursor'] ?? null); + + if ($cursorPage !== null) { + $results['pages'] = SearchCategoryPayload::fromCursor( + $cursorPage, + $this->pageService->countByKeyword($q), + $format($cursorPage->items()) + ); + + return $results; + } + + $searchPage = $this->pageService->searchByKeyword($q, $orderBy, $direction, $perPage, $pageNumber); + + $results['pages'] = SearchCategoryPayload::fromBounded($searchPage, $format($searchPage->items())); } catch (\Exception $e) { Log::error('Search pages error', ['message' => $e->getMessage(), 'q' => $q]); } @@ -136,28 +171,35 @@ class SearchPagesListener implements HookListenerInterface $pagesData = $results['pages']; $type = $context['type'] ?? 'all'; $total = $pagesData['total'] ?? 0; + // 항목은 이미 요청한 페이지 분량만 조회돼 있다 — 여기서 다시 자르지 않는다. $items = $pagesData['items'] ?? []; $response['pages_count'] = $total; if ($type === 'all') { - $allTabLimit = $context['all_tab_limit'] ?? 5; $response['pages'] = [ 'total' => $total, - 'items' => array_slice($items, 0, $allTabLimit), + 'total_relation' => $pagesData['total_relation'] ?? null, + 'total_is_exact' => $pagesData['total_is_exact'] ?? true, + 'result_cap' => $pagesData['result_cap'] ?? null, + 'items' => $items, ]; } elseif ($type === 'pages') { - $page = $context['page'] ?? 1; - $perPage = $context['per_page'] ?? 10; - $offset = ($page - 1) * $perPage; - $response['pages'] = [ 'total' => $total, - 'items' => array_slice($items, $offset, $perPage), + 'total_relation' => $pagesData['total_relation'] ?? null, + 'total_is_exact' => $pagesData['total_is_exact'] ?? true, + 'result_cap' => $pagesData['result_cap'] ?? null, + 'items' => $items, ]; - $response['current_page'] = $page; - $response['per_page'] = $perPage; - $response['last_page'] = max(1, (int) ceil($total / $perPage)); + $response['current_page'] = (int) ($context['page'] ?? 1); + $response['per_page'] = (int) ($context['per_page'] ?? 10); + // 총 건수가 상한에 걸리면 마지막 페이지를 계산할 수 없다 — null 로 알린다. + $response['last_page'] = $pagesData['last_page'] ?? null; + $response['has_more_pages'] = $pagesData['has_more_pages'] ?? false; + // 커서 응답이면 다음/이전 커서를 함께 실어 깊은 페이지를 OFFSET 없이 넘긴다. + $response['next_cursor'] = $pagesData['next_cursor'] ?? null; + $response['prev_cursor'] = $pagesData['prev_cursor'] ?? null; } return $response; @@ -175,7 +217,7 @@ class SearchPagesListener implements HookListenerInterface { return match ($sort) { 'oldest' => ['created_at', 'asc'], - default => ['created_at', 'desc'], + default => ['created_at', 'desc'], }; } diff --git a/modules/_bundled/sirsoft-page/src/Repositories/Contracts/PageRepositoryInterface.php b/modules/_bundled/sirsoft-page/src/Repositories/Contracts/PageRepositoryInterface.php index 3635fd25..15eec6dd 100644 --- a/modules/_bundled/sirsoft-page/src/Repositories/Contracts/PageRepositoryInterface.php +++ b/modules/_bundled/sirsoft-page/src/Repositories/Contracts/PageRepositoryInterface.php @@ -2,8 +2,10 @@ namespace Modules\Sirsoft\Page\Repositories\Contracts; -use Illuminate\Database\Eloquent\Collection; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Modules\Sirsoft\Page\Models\Page; @@ -91,22 +93,48 @@ interface PageRepositoryInterface */ public function slugExists(string $slug, ?int $excludeId = null): bool; + /** + * 키워드로 페이지를 커서(키셋)로 검색합니다. + * + * 커서 적용 가능 여부 판정은 코어가 담당하므로, 이 메서드는 이미 검증된 정렬 키를 + * 받아 조회만 수행합니다. + * + * @param string $keyword 검색 키워드 + * @param array $sortKeys [[컬럼, 방향], ...] + * @param int $perPage 페이지당 항목 수 + * @param string|null $cursor 인코딩된 커서 (첫 페이지면 null) + * @return CursorPaginator 커서 페이지 결과 + */ + public function searchByKeywordWithCursor( + string $keyword, + array $sortKeys, + int $perPage = 10, + ?string $cursor = null + ): CursorPaginator; + /** * 키워드로 페이지를 검색합니다. * * @param string $keyword 검색 키워드 * @param string $orderBy 정렬 컬럼 * @param string $direction 정렬 방향 (asc, desc) - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $limit = 10): array; + public function searchByKeyword( + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage; /** * 키워드와 일치하는 발행된 페이지 수를 조회합니다. * * @param string $keyword 검색 키워드 - * @return int 일치하는 페이지 수 + * @return BoundedCount 일치하는 페이지 수 (정확도 포함) */ - public function countByKeyword(string $keyword): int; + public function countByKeyword(string $keyword): BoundedCount; } diff --git a/modules/_bundled/sirsoft-page/src/Repositories/PageRepository.php b/modules/_bundled/sirsoft-page/src/Repositories/PageRepository.php index 699300d0..44b8a150 100644 --- a/modules/_bundled/sirsoft-page/src/Repositories/PageRepository.php +++ b/modules/_bundled/sirsoft-page/src/Repositories/PageRepository.php @@ -7,9 +7,15 @@ use App\Repositories\Concerns\HasMultipleSearchFilters; use App\Repositories\Concerns\PaginatesWithDeferredJoin; use App\Repositories\Concerns\ResolvesSortSpec; use App\Search\Engines\DatabaseFulltextEngine; +use App\Search\KeywordSearch; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use App\Support\Query\BoundedPaginator; +use App\Support\Query\KeysetPaginator; +use App\Support\Query\PaginationLimits; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Modules\Sirsoft\Page\Models\Page; use Modules\Sirsoft\Page\Repositories\Contracts\PageRepositoryInterface; @@ -53,20 +59,20 @@ class PageRepository implements PageRepositoryInterface $this->applyFilters($query, $filters); - // 지연 조인: 본문 content(longText)는 목록에 쓰이지 않는데도 OFFSET 이 훑는 - // 모든 행에서 함께 읽힌다. inner 는 id 만 훑고 본문은 이번 페이지에서만 읽는다. - // 목록이 실제로 쓰는 컬럼만 읽는다. 본문(`content`)은 longText 인데 목록 표현 - // (`PageResource::toListArray`)이 출력하지 않으므로, 이번 페이지 행에서도 읽을 이유가 없다. + // 지연 조인: inner 는 id 만 훑는다. outer 도 목록 리소스가 실제로 쓰는 컬럼만 + // 읽는다 — 본문 content(mediumText)는 목록 표현(`PageResource::toListArray`)이 + // 출력하지 않으므로 한 페이지 분량이라도 읽을 이유가 없다 (오버플로 페이지 읽기 제거). // 응답 형태는 변하지 않고 DB/메모리 비용만 줄어든다. return $this->paginateWithDeferredJoin( query: $query, columns: [ - 'id', 'slug', 'title', 'published', 'published_at', 'current_version', - 'created_by', 'updated_by', 'created_at', 'updated_at', + 'id', 'slug', 'title', 'published', 'published_at', + 'current_version', 'created_by', 'updated_by', 'created_at', 'updated_at', ], sort: $this->resolveListSortSpec($filters), perPage: $perPage, relations: ['creator', 'updater'], + resultCap: PaginationLimits::resultCap('admin.pages'), ); } @@ -183,35 +189,75 @@ class PageRepository implements PageRepositoryInterface * @param string $keyword 검색 키워드 * @param string $orderBy 정렬 컬럼 * @param string $direction 정렬 방향 (asc, desc) - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $limit = 10): array - { - $results = $this->buildKeywordQuery($keyword) + public function searchByKeyword( + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage { + $query = $this->buildKeywordQuery($keyword) ->orderBy($orderBy, $direction) // 전순서 보장 — 정렬 컬럼이 비고유라 페이지 경계가 흔들릴 수 있다 - ->orderBy('id', $direction === 'asc' ? 'asc' : 'desc') - // audit:allow repository-paginate-column-pruning reason: 통합검색 전용 진입점 — - // 유일한 호출자(SearchPagesListener)가 전체 탭은 limit=5, 페이지 탭은 limit=PHP_INT_MAX 로 - // 1페이지에 결과를 받아가므로 OFFSET 이 발생하지 않는다 - ->paginate($limit); + ->orderBy('id', $direction === 'asc' ? 'asc' : 'desc'); - return [ - 'total' => $results->total(), - 'items' => $results->getCollection(), - ]; + // 검색 결과 카드가 실제로 쓰는 컬럼만 읽는다. + // + // content 만 예외로 남긴다. 검색 결과는 키워드 **주변** 문맥을 잘라 보여주므로 + // 본문 어디에 키워드가 있든 그 위치를 찾을 수 있어야 하고, 이 컬럼은 다국어 + // JSON 이라 DB 에서 `SUBSTRING` 으로 자르면 캐스팅이 깨진다. 관리자 페이지 목록 + // (위 paginate)은 본문을 전혀 쓰지 않으므로 그쪽에서는 제외했다. + // + // 비용 측면의 목적은 달성돼 있다 — 종전에는 매칭 전량의 본문을 끌어왔지만 + // 지금은 이번 페이지 분량(per_page + 1)만 읽는다. + return BoundedPaginator::paginate( + $query, + perPage: $perPage, + page: $page, + resultCap: PaginationLimits::resultCap('search'), + columns: ['id', 'slug', 'title', 'content', 'published_at', 'created_at'], + ); + } + + /** + * {@inheritDoc} + */ + public function searchByKeywordWithCursor( + string $keyword, + array $sortKeys, + int $perPage = 10, + ?string $cursor = null + ): CursorPaginator { + // 커서 모드에는 OFFSET 이 없다 — 건너뛸 행을 실제로 읽던 비용이 사라진다. + // 읽는 컬럼은 offset 경로와 같게 유지해 화면이 받는 항목 형태를 맞춘다. + return KeysetPaginator::paginate( + query: $this->buildKeywordQuery($keyword), + perPage: $perPage, + sortKeys: $sortKeys, + uniqueKey: 'id', + cursor: $cursor, + columns: ['id', 'slug', 'title', 'content', 'published_at', 'created_at'], + ); } /** * 키워드와 일치하는 발행된 페이지 수를 조회합니다. * * @param string $keyword 검색 키워드 - * @return int 일치하는 페이지 수 + * @return BoundedCount 일치하는 페이지 수 (정확도 포함) */ - public function countByKeyword(string $keyword): int + public function countByKeyword(string $keyword): BoundedCount { - return $this->buildKeywordQuery($keyword)->count(); + // 비활성 탭의 배지용 건수다. 상한을 걸어 대량 매칭에서도 비용이 일정하게 유지된다. + // 상한에 걸리면 값이 잘리므로 정확도를 함께 돌려준다. + return BoundedPaginator::count( + $this->buildKeywordQuery($keyword), + PaginationLimits::resultCap('search') + ); } /** @@ -225,8 +271,9 @@ class PageRepository implements PageRepositoryInterface * `WHERE published = ? OR slug LIKE ? AND id IN (...)` 형태로 연산자 우선순위가 깨져 * `published = ?` 만 유효해지고 total 이 모든 발행 페이지 수로 부풀려지는 회귀가 발생한다. * 이 Repository 의 applyTitleKeywordSearch 에서 이미 사용중인 - * `DatabaseFulltextEngine::whereFulltext` 정적 헬퍼 패턴과 동일하게 해결한다 - * (Scout 엔진은 indexing 및 다른 검색에서 계속 사용됨). + * `KeywordSearch::apply()` 패턴과 동일하게 해결한다 — 키워드 조건을 진행 중인 쿼리에 + * 직접 붙이므로 Scout 의 재계수 경로를 타지 않는다. 어떤 조건이 붙는지는 활성 검색 + * 엔진이 정하므로 플러그인 엔진도 이 경로를 그대로 탄다. * * @param string $keyword 검색 키워드 */ @@ -253,7 +300,7 @@ class PageRepository implements PageRepositoryInterface } // 본문은 순수 텍스트/HTML 이라 FULLTEXT 가 유효하다. - DatabaseFulltextEngine::whereFulltext($q, 'content', $keyword, 'or'); + KeywordSearch::apply($q, 'content', $keyword, 'or'); $q->orWhere('slug', 'like', '%'.$keyword.'%'); }); } @@ -332,7 +379,7 @@ class PageRepository implements PageRepositoryInterface $query->where('slug', 'like', "%{$keyword}%"); } - DatabaseFulltextEngine::whereFulltext($query, 'title', $keyword, 'or'); + KeywordSearch::apply($query, 'title', $keyword, 'or'); } /** diff --git a/modules/_bundled/sirsoft-page/src/Services/PageService.php b/modules/_bundled/sirsoft-page/src/Services/PageService.php index 6a5127ba..82582dcb 100644 --- a/modules/_bundled/sirsoft-page/src/Services/PageService.php +++ b/modules/_bundled/sirsoft-page/src/Services/PageService.php @@ -4,8 +4,12 @@ namespace Modules\Sirsoft\Page\Services; use App\Extension\HookManager; use App\Helpers\PermissionHelper; +use App\Search\SearchPagePolicy; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Pagination\CursorPaginator; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -22,6 +26,22 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; */ class PageService { + /** + * 검색 정렬 이름 → [실제 컬럼, 방향] 선언 + * + * 코어({@see SearchPagePolicy})가 이 선언을 읽어 커서 적용 여부를 판정한다. + * 여기에 없는 정렬 이름(관련도순 등)은 커서로 처리하지 않고 offset 을 유지한다. + */ + public const SEARCH_SORT_MAP = [ + 'latest' => ['created_at', 'desc'], + 'oldest' => ['created_at', 'asc'], + ]; + + /** + * 커서(키셋) 경계로 쓸 수 있는 실제 컬럼 선언 + */ + public const SEARCH_CURSOR_COLUMNS = ['created_at']; + public function __construct( private PageRepositoryInterface $pageRepository, private PageVersionRepositoryInterface $pageVersionRepository, @@ -380,21 +400,54 @@ class PageService * @param string $keyword 검색 키워드 * @param string $orderBy 정렬 컬럼 * @param string $direction 정렬 방향 (asc, desc) - * @param int $limit 조회할 최대 항목 수 - * @return array{total: int, items: Collection} + * @param int $perPage 페이지당 항목 수 + * @param int $page 페이지 번호 + * @return BoundedPage 페이지 결과 (총 건수 정확도 포함) */ - public function searchByKeyword(string $keyword, string $orderBy = 'created_at', string $direction = 'desc', int $limit = 10): array - { - return $this->pageRepository->searchByKeyword($keyword, $orderBy, $direction, $limit); + public function searchByKeyword( + string $keyword, + string $orderBy = 'created_at', + string $direction = 'desc', + int $perPage = 10, + int $page = 1 + ): BoundedPage { + return $this->pageRepository->searchByKeyword($keyword, $orderBy, $direction, $perPage, $page); + } + + /** + * 키워드로 페이지를 커서(키셋)로 검색합니다. + * + * 커서 적용 가능 여부는 코어({@see SearchPagePolicy})가 판정한다. 이 서비스는 + * 정렬 선언({@see self::SEARCH_SORT_MAP})만 제공하고 규칙을 다시 쓰지 않는다. + * + * @param string $keyword 검색 키워드 + * @param string $sort 정렬 옵션 + * @param int $perPage 페이지당 항목 수 + * @param string|null $cursor 인코딩된 커서 (첫 페이지면 null) + * @return CursorPaginator|null 커서 페이지 결과 (커서 적용 불가 시 null) + */ + public function searchByKeywordWithCursor( + string $keyword, + string $sort = 'latest', + int $perPage = 10, + ?string $cursor = null + ): ?CursorPaginator { + $sortKeys = SearchPagePolicy::sortKeys($sort, self::SEARCH_SORT_MAP); + + if (! SearchPagePolicy::usesCursor($cursor, $sortKeys, self::SEARCH_CURSOR_COLUMNS)) { + return null; + } + + return $this->pageRepository->searchByKeywordWithCursor($keyword, $sortKeys, $perPage, $cursor); } /** * 키워드와 일치하는 발행된 페이지 수를 조회합니다. * * @param string $keyword 검색 키워드 - * @return int 일치하는 페이지 수 + * @return BoundedCount 일치하는 페이지 수 (정확도 포함) */ - public function countByKeyword(string $keyword): int + public function countByKeyword(string $keyword): BoundedCount { return $this->pageRepository->countByKeyword($keyword); } diff --git a/modules/_bundled/sirsoft-page/tests/Feature/Performance/ListQueryCountRegressionTest.php b/modules/_bundled/sirsoft-page/tests/Feature/Performance/ListQueryCountRegressionTest.php new file mode 100644 index 00000000..65beee9d --- /dev/null +++ b/modules/_bundled/sirsoft-page/tests/Feature/Performance/ListQueryCountRegressionTest.php @@ -0,0 +1,77 @@ + $prefix.'-'.$i, + 'title' => ['ko' => '페이지 '.$prefix.$i], + 'content' => '본문 내용', + 'published' => true, + ]); + } + } + + /** + * 페이지 목록: 페이지 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * @effects page_list_query_count_stable + */ + public function test_page_list_query_count_is_stable(): void + { + $repository = app(PageRepositoryInterface::class); + $this->seedPages(5, 'list-a'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->paginate([], 50), + grow: fn () => $this->seedPages(10, 'list-b'), + context: '페이지 목록', + ); + } + + /** + * 페이지 검색: 매칭 수가 늘어도 쿼리 수가 늘지 않는지 확인 + * + * 종전에는 PHP_INT_MAX 로 매칭 전량을 읽고 PHP 에서 잘라냈다. + * + * @effects page_search_query_count_stable + */ + public function test_page_search_query_count_is_stable(): void + { + $repository = app(PageRepositoryInterface::class); + $this->seedPages(5, 'search-a'); + + $this->assertQueryCountStableAsDataGrows( + measure: fn () => $repository->searchByKeyword('페이지', 'created_at', 'desc', 10, 1), + grow: fn () => $this->seedPages(10, 'search-b'), + context: '페이지 검색', + ); + } +} diff --git a/modules/_bundled/sirsoft-page/tests/Feature/Performance/PublishedSortIndexCoverageTest.php b/modules/_bundled/sirsoft-page/tests/Feature/Performance/PublishedSortIndexCoverageTest.php new file mode 100644 index 00000000..bcac28fd --- /dev/null +++ b/modules/_bundled/sirsoft-page/tests/Feature/Performance/PublishedSortIndexCoverageTest.php @@ -0,0 +1,117 @@ +getDriverName(), ['mysql', 'mariadb'], true)) { + $this->markTestSkipped('EXPLAIN 판정은 MySQL 계열에서만 수행합니다.'); + } + } + + /** + * 정렬을 덮는 색인이 존재하는지 확인 + * + * @effects published_sort_index_exists + */ + public function test_published_sort_index_exists(): void + { + $names = array_column(Schema::getIndexes('pages'), 'name'); + + $this->assertContains( + 'idx_pages_published_created_id', + $names, + '발행 조건 + 등록일 정렬을 덮는 색인이 없다' + ); + } + + /** + * 색인 컬럼 순서가 조건·정렬 순서와 일치하는지 확인 + * + * @effects published_sort_index_column_order + */ + public function test_index_column_order_matches_query_shape(): void + { + $index = collect(Schema::getIndexes('pages'))->firstWhere('name', 'idx_pages_published_created_id'); + + $this->assertNotNull($index); + $this->assertSame( + ['published', 'created_at', 'id'], + array_values($index['columns']), + '색인 컬럼 순서가 조건·정렬 순서와 다르다 — 존재해도 정렬에 쓰이지 않는다' + ); + } + + /** + * 옵티마이저가 실제로 이 색인을 **선택**하는지 EXPLAIN 으로 확인 + * + * `possible_keys` 는 "쓸 수 있었다" 일 뿐이라, 후보에만 올라 있고 실제로는 전체 + * 스캔을 골라도 통과해 버린다. 판정은 실행계획이 확정한 `key` 로만 한다. + * + * @effects page_list_query_uses_published_sort_index + */ + public function test_query_plan_uses_the_index(): void + { + $rows = []; + + // 행이 몇 건뿐이면 전체 스캔이 정상 선택이라 key 단언이 성립하지 않는다. + for ($i = 0; $i < 300; $i++) { + $rows[] = [ + 'slug' => 'idx-test-'.$i, + 'title' => json_encode(['ko' => '색인 테스트 '.$i], JSON_UNESCAPED_UNICODE), + 'content' => json_encode(['ko' => '본문'], JSON_UNESCAPED_UNICODE), + 'published' => $i % 10 !== 0, + 'created_at' => now()->subMinutes($i), + 'updated_at' => now(), + ]; + } + + DB::table('pages')->insert($rows); + DB::statement('ANALYZE TABLE '.DB::getTablePrefix().'pages'); + + $query = Page::query() + ->where('published', true) + ->orderBy('created_at', 'desc') + ->orderBy('id', 'desc') + ->limit(10) + ->toBase(); + + $plan = DB::select('EXPLAIN '.$query->toSql(), $query->getBindings()); + + $this->assertNotEmpty($plan, 'EXPLAIN 결과가 비어 있다'); + + $row = (array) $plan[0]; + $chosen = (string) ($row['key'] ?? ''); + + $this->assertStringContainsString( + 'idx_pages_published_created_id', + $chosen, + '옵티마이저가 이 색인을 고르지 않았다 (선택: '.($chosen !== '' ? $chosen : '없음(전체 스캔)'). + ', 후보: '.($row['possible_keys'] ?? '없음').') — 컬럼 순서나 조건 형태가 어긋났다' + ); + } +} diff --git a/modules/_bundled/sirsoft-page/tests/Feature/Repositories/PageRepositorySearchTest.php b/modules/_bundled/sirsoft-page/tests/Feature/Repositories/PageRepositorySearchTest.php index dbfd37a5..850dbf09 100644 --- a/modules/_bundled/sirsoft-page/tests/Feature/Repositories/PageRepositorySearchTest.php +++ b/modules/_bundled/sirsoft-page/tests/Feature/Repositories/PageRepositorySearchTest.php @@ -120,8 +120,8 @@ class PageRepositorySearchTest extends TestCase $result = $this->repository->searchByKeyword('이용약관'); - $this->assertEquals(1, $result['total']); - $this->assertEquals('test-terms-search', $result['items']->first()->slug); + $this->assertEquals(1, $result->total()); + $this->assertEquals('test-terms-search', $result->getCollection()->first()->slug); } /** @@ -144,9 +144,9 @@ class PageRepositorySearchTest extends TestCase $koFull = $this->repository->searchByKeyword('이용약관'); $koPartial = $this->repository->searchByKeyword('약관'); - $this->assertEquals(1, $koFull['total'], '제목 전체 일치가 검색되지 않았다'); - $this->assertEquals('aaa-bbb-ccc', $koFull['items']->first()->slug); - $this->assertEquals(1, $koPartial['total'], '제목 부분 일치가 검색되지 않았다'); + $this->assertEquals(1, $koFull->total(), '제목 전체 일치가 검색되지 않았다'); + $this->assertEquals('aaa-bbb-ccc', $koFull->getCollection()->first()->slug); + $this->assertEquals(1, $koPartial->total(), '제목 부분 일치가 검색되지 않았다'); } /** @@ -160,8 +160,8 @@ class PageRepositorySearchTest extends TestCase $result = $this->repository->searchByKeyword('Terms'); - $this->assertEquals(1, $result['total'], '영문 제목으로 검색되지 않았다'); - $this->assertEquals('ddd-eee-fff', $result['items']->first()->slug); + $this->assertEquals(1, $result->total(), '영문 제목으로 검색되지 않았다'); + $this->assertEquals('ddd-eee-fff', $result->getCollection()->first()->slug); } /** @@ -174,8 +174,8 @@ class PageRepositorySearchTest extends TestCase $result = $this->repository->searchByKeyword('쿠키 정책'); - $this->assertEquals(1, $result['total']); - $this->assertEquals('test-guide-search', $result['items']->first()->slug); + $this->assertEquals(1, $result->total()); + $this->assertEquals('test-guide-search', $result->getCollection()->first()->slug); } /** @@ -188,7 +188,10 @@ class PageRepositorySearchTest extends TestCase $count = $this->repository->countByKeyword('사이트 점검'); - $this->assertEquals(1, $count); + // 건수만 세는 자리도 정확도를 함께 돌려준다 (#519) — 상한에 걸려 잘린 값이 + // 정확한 것처럼 화면에 나가지 않도록. + $this->assertSame(1, $count->total); + $this->assertTrue($count->totalRelation()->isExact()); } /** @@ -201,7 +204,7 @@ class PageRepositorySearchTest extends TestCase $result = $this->repository->searchByKeyword('독점 서비스'); - $this->assertEquals(0, $result['total']); + $this->assertEquals(0, $result->total()); } // ─── 헬퍼 ──────────────────────────────────────────── diff --git a/modules/_bundled/sirsoft-page/tests/Unit/Listeners/SearchPagesListenerTest.php b/modules/_bundled/sirsoft-page/tests/Unit/Listeners/SearchPagesListenerTest.php index e06da21d..9ab8c001 100644 --- a/modules/_bundled/sirsoft-page/tests/Unit/Listeners/SearchPagesListenerTest.php +++ b/modules/_bundled/sirsoft-page/tests/Unit/Listeners/SearchPagesListenerTest.php @@ -2,11 +2,15 @@ namespace Modules\Sirsoft\Page\Tests\Unit\Listeners; +use App\Enums\TotalRelation; +use App\Support\Query\BoundedCount; +use App\Support\Query\BoundedPage; +use Carbon\Carbon; use Illuminate\Support\Collection; +use Mockery; use Modules\Sirsoft\Page\Listeners\SearchPagesListener; use Modules\Sirsoft\Page\Services\PageService; use Modules\Sirsoft\Page\Tests\ModuleTestCase; -use Mockery; /** * SearchPagesListener 단위 테스트 @@ -28,6 +32,14 @@ class SearchPagesListenerTest extends ModuleTestCase parent::setUp(); $this->pageServiceMock = Mockery::mock(PageService::class); + + // 커서 경로는 코어 정책이 판정한다 — 커서를 보내지 않는 이 케이스들에서는 + // 서비스가 null 을 돌려주고 호출자는 offset 경로로 떨어진다. + $this->pageServiceMock + ->shouldReceive('searchByKeywordWithCursor') + ->andReturnNull() + ->byDefault(); + $this->listener = new SearchPagesListener($this->pageServiceMock); } @@ -92,6 +104,29 @@ class SearchPagesListenerTest extends ModuleTestCase $this->assertSame($original, $result); } + /** + * 검색 결과 페이지(BoundedPage)를 만듭니다. + * + * 저장소가 반환하는 계약과 같은 형태를 테스트에서도 그대로 씁니다. + * + * @param Collection $items 페이지 항목 + * @param int $total 총 건수 + * @param int $perPage 페이지당 건수 + * @return BoundedPage 페이지 결과 + */ + private function boundedPage(Collection $items, int $total, int $perPage = 5): BoundedPage + { + return new BoundedPage( + items: $items, + total: $total, + perPage: $perPage, + currentPage: 1, + totalRelation: TotalRelation::Exact, + resultCap: 10000, + hasMorePages: false, + ); + } + /** * 관련 없는 탭(posts)일 때 total만 반환하고 items는 빈 배열인지 확인 */ @@ -101,7 +136,7 @@ class SearchPagesListenerTest extends ModuleTestCase ->shouldReceive('countByKeyword') ->with('Laravel') ->once() - ->andReturn(5); + ->andReturn(new BoundedCount(5, TotalRelation::Exact, 10000)); $result = $this->listener->searchPages([], [ 'q' => 'Laravel', @@ -112,6 +147,28 @@ class SearchPagesListenerTest extends ModuleTestCase $this->assertEmpty($result['pages']['items']); } + /** + * 비활성 탭 배지도 잘린 건수를 정확한 것처럼 보고하지 않는지 확인 (#519) + */ + public function test_count_only_badge_carries_accuracy_when_truncated(): void + { + $this->pageServiceMock + ->shouldReceive('countByKeyword') + ->with('Laravel') + ->once() + ->andReturn(new BoundedCount(10000, TotalRelation::AtLeast, 10000)); + + $result = $this->listener->searchPages([], [ + 'q' => 'Laravel', + 'type' => 'posts', + ]); + + $this->assertSame(10000, $result['pages']['total']); + $this->assertSame('at_least', $result['pages']['total_relation']); + $this->assertFalse($result['pages']['total_is_exact']); + $this->assertSame(10000, $result['pages']['result_cap']); + } + /** * all 탭에서 검색 결과를 반환하는지 확인 */ @@ -121,12 +178,9 @@ class SearchPagesListenerTest extends ModuleTestCase $this->pageServiceMock ->shouldReceive('searchByKeyword') - ->with('약관', 'created_at', 'desc', 5) + ->with('약관', 'created_at', 'desc', 5, 1) ->once() - ->andReturn([ - 'total' => 1, - 'items' => new Collection([$fakePage]), - ]); + ->andReturn($this->boundedPage(new Collection([$fakePage]), 1)); $result = $this->listener->searchPages([], [ 'q' => '약관', @@ -147,12 +201,9 @@ class SearchPagesListenerTest extends ModuleTestCase $this->pageServiceMock ->shouldReceive('searchByKeyword') - ->with('개인정보', 'created_at', 'desc', PHP_INT_MAX) + ->with('개인정보', 'created_at', 'desc', 10, 1) ->once() - ->andReturn([ - 'total' => 1, - 'items' => new Collection([$fakePage]), - ]); + ->andReturn($this->boundedPage(new Collection([$fakePage]), 1)); $result = $this->listener->searchPages([], [ 'q' => '개인정보', @@ -170,9 +221,9 @@ class SearchPagesListenerTest extends ModuleTestCase { $this->pageServiceMock ->shouldReceive('searchByKeyword') - ->with('G7', 'created_at', 'asc', 5) + ->with('G7', 'created_at', 'asc', 5, 1) ->once() - ->andReturn(['total' => 0, 'items' => new Collection()]); + ->andReturn($this->boundedPage(new Collection, 0)); $result = $this->listener->searchPages([], [ 'q' => 'G7', @@ -192,9 +243,9 @@ class SearchPagesListenerTest extends ModuleTestCase { $this->pageServiceMock ->shouldReceive('searchByKeyword') - ->with('G7', 'created_at', 'desc', 5) + ->with('G7', 'created_at', 'desc', 5, 1) ->once() - ->andReturn(['total' => 0, 'items' => new Collection()]); + ->andReturn($this->boundedPage(new Collection, 0)); $result = $this->listener->searchPages([], [ 'q' => 'G7', @@ -217,10 +268,7 @@ class SearchPagesListenerTest extends ModuleTestCase $this->pageServiceMock ->shouldReceive('searchByKeyword') ->once() - ->andReturn([ - 'total' => 1, - 'items' => new Collection([$fakePage]), - ]); + ->andReturn($this->boundedPage(new Collection([$fakePage]), 1)); $result = $this->listener->searchPages([], [ 'q' => '약관', @@ -249,10 +297,7 @@ class SearchPagesListenerTest extends ModuleTestCase $this->pageServiceMock ->shouldReceive('searchByKeyword') ->once() - ->andReturn([ - 'total' => 1, - 'items' => new Collection([$fakePage]), - ]); + ->andReturn($this->boundedPage(new Collection([$fakePage]), 1)); $result = $this->listener->searchPages([], [ 'q' => '이용', @@ -298,11 +343,14 @@ class SearchPagesListenerTest extends ModuleTestCase } /** - * all 탭에서 pages_count와 pages 슬라이스가 반환되는지 확인 + * all 탭에서 pages_count 와 조회된 항목이 그대로 반환되는지 확인 + * + * 항목은 저장소가 이미 요청한 분량만 조회해 오므로 여기서 다시 자르지 않는다. + * 종전에는 전량을 받아 PHP 에서 잘랐고, 그래서 매칭이 많을수록 메모리가 함께 커졌다. */ public function test_build_pages_response_returns_summary_for_all_tab(): void { - $items = array_map(fn ($i) => ['id' => $i], range(1, 5)); + $items = array_map(fn ($i) => ['id' => $i], range(1, 3)); $results = [ 'pages' => ['total' => 10, 'items' => $items], ]; @@ -314,7 +362,7 @@ class SearchPagesListenerTest extends ModuleTestCase $this->assertEquals(10, $response['pages_count']); $this->assertEquals(10, $response['pages']['total']); - $this->assertCount(3, $response['pages']['items']); + $this->assertCount(3, $response['pages']['items'], '조회된 항목을 그대로 싣는다'); } /** @@ -322,9 +370,15 @@ class SearchPagesListenerTest extends ModuleTestCase */ public function test_build_pages_response_returns_pagination_for_pages_tab(): void { - $items = array_map(fn ($i) => ['id' => $i], range(1, 25)); + // 2페이지 분량(10건)만 조회돼 있는 상태 — 저장소가 이미 잘라서 준다 + $items = array_map(fn ($i) => ['id' => $i], range(11, 20)); $results = [ - 'pages' => ['total' => 25, 'items' => $items], + 'pages' => [ + 'total' => 25, + 'items' => $items, + 'last_page' => 3, + 'has_more_pages' => true, + ], ]; $response = $this->listener->buildPagesResponse([], $results, [ @@ -338,6 +392,38 @@ class SearchPagesListenerTest extends ModuleTestCase $this->assertEquals(2, $response['current_page']); $this->assertEquals(10, $response['per_page']); $this->assertEquals(3, $response['last_page']); + $this->assertTrue($response['has_more_pages']); + } + + /** + * 총 건수가 상한에 걸리면 마지막 페이지가 null 로 전달되는지 확인 + * + * 계산할 수 없는 값을 1 이나 임의 숫자로 채우면 화면이 그것을 사실로 읽는다. + */ + public function test_build_pages_response_omits_last_page_when_total_is_bounded(): void + { + $results = [ + 'pages' => [ + 'total' => 10000, + 'total_is_exact' => false, + 'total_relation' => 'at_least', + 'result_cap' => 10000, + 'items' => [['id' => 1]], + 'last_page' => null, + 'has_more_pages' => true, + ], + ]; + + $response = $this->listener->buildPagesResponse([], $results, [ + 'type' => 'pages', + 'page' => 1, + 'per_page' => 10, + ]); + + $this->assertNull($response['last_page'], '마지막 페이지는 계산 불가'); + $this->assertTrue($response['has_more_pages'], '"다음" 이동은 계속 열려 있다'); + $this->assertFalse($response['pages']['total_is_exact']); + $this->assertEquals('at_least', $response['pages']['total_relation']); } // ─── addValidationRules() ──────────────────────────── @@ -387,7 +473,7 @@ class SearchPagesListenerTest extends ModuleTestCase public array $content; - public ?\Carbon\Carbon $published_at; + public ?Carbon $published_at; public function __construct(string $title, string $slug, string $contentKo) { diff --git a/modules/_bundled/sirsoft-page/upgrades/Upgrade_1_0_2.php b/modules/_bundled/sirsoft-page/upgrades/Upgrade_1_0_2.php index a3c9d6dc..106a7696 100644 --- a/modules/_bundled/sirsoft-page/upgrades/Upgrade_1_0_2.php +++ b/modules/_bundled/sirsoft-page/upgrades/Upgrade_1_0_2.php @@ -11,6 +11,9 @@ use App\Extension\AbstractUpgradeStep; * `->constrained()` 뒤에 체인되어 컬럼이 아닌 외래키 정의에 부착되던 문제를 소스에서 * 교정했으나, 기설치본은 마이그레이션이 재실행되지 않아 그대로 남기 때문이다. * + * 함께 발행 페이지 목록의 정렬 색인을 기존 사이트에 반영한다(02). 마이그레이션만 추가하면 + * 신규 설치에만 반영되므로 업그레이드 시점에 동일 색인을 적용한다. + * * 모든 비즈니스 로직은 data/1.0.2/migrations/ 로 격리(AbstractUpgradeStep 규약). */ class Upgrade_1_0_2 extends AbstractUpgradeStep {} diff --git a/modules/_bundled/sirsoft-page/upgrades/data/1.0.2/migrations/02_AddPublishedSortIndex.php b/modules/_bundled/sirsoft-page/upgrades/data/1.0.2/migrations/02_AddPublishedSortIndex.php new file mode 100644 index 00000000..c9f83a74 --- /dev/null +++ b/modules/_bundled/sirsoft-page/upgrades/data/1.0.2/migrations/02_AddPublishedSortIndex.php @@ -0,0 +1,78 @@ + + */ + private const COLUMNS = ['published', 'created_at', 'id']; + + /** + * 마이그레이션 식별자를 반환합니다. + * + * @return string 마이그레이션 이름 + */ + public function name(): string + { + return 'AddPublishedSortIndex'; + } + + /** + * 정렬 색인을 적용합니다. + * + * @param UpgradeContext $context 업그레이드 컨텍스트 + */ + public function run(UpgradeContext $context): void + { + if (! Schema::hasTable(self::TABLE)) { + $context->logger->info('[page:1.0.2] 테이블 부재 — 색인 적용 스킵: '.self::TABLE); + + return; + } + + if (in_array(self::INDEX, array_column(Schema::getIndexes(self::TABLE), 'name'), true)) { + $context->logger->info('[page:1.0.2] 이미 적용된 색인 — 스킵: '.self::INDEX); + + return; + } + + $context->logger->info('[page:1.0.2] 목록 정렬 색인 적용 시작 (데이터가 많으면 수 분 걸릴 수 있고 그동안 해당 테이블 쓰기가 대기합니다)'); + + Schema::table(self::TABLE, function ($blueprint) { + $blueprint->index(self::COLUMNS, self::INDEX); + }); + + $context->logger->info('[page:1.0.2] 색인 적용 완료: '.self::INDEX); + } +} From 74a0a540162d30b90a54a904434df7590033eacd Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:45:00 +0900 Subject: [PATCH 5/8] =?UTF-8?q?fix(gdpr):=20=EC=83=81=ED=95=9C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=97=90=EC=84=9C=20=ED=8E=98=EC=9D=B4=EC=A0=80?= =?UTF-8?q?=EA=B0=80=20=EC=A0=91=ED=9E=88=EB=8D=98=20=EC=9E=90=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=95=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 동의 이력과 정책 버전 발행 이력의 페이저가 마지막 페이지 번호 하나에 의존하고 있었다. 총 건수를 끝까지 세지 못한 목록에서는 그 값이 없어, 페이저 노출 조건이 거짓이 되어 통째로 사라지거나 "다음" 이 첫 페이지부터 비활성이었다. 마지막 페이지를 아는 경우의 동작은 종전과 같고, 모르는 경우에만 다음 페이지 존재 여부로 판정한다. 표시용 마지막 페이지 번호는 지어내지 않는다. --- plugins/_bundled/sirsoft-gdpr/CHANGELOG.md | 1 + .../resources/layouts/admin/gdpr_consent_log.json | 3 ++- .../resources/layouts/admin/plugin_settings.json | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md b/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md index ddfde8af..b48f25f9 100644 --- a/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md +++ b/plugins/_bundled/sirsoft-gdpr/CHANGELOG.md @@ -46,6 +46,7 @@ - 비회원으로 쿠키 배너를 닫은 뒤 동의 이력이 없는 다른 계정으로 로그인하면 배너가 계속 숨겨져 있던 문제를 바로잡았습니다 — 이제 로그인한 계정 기준으로 다시 판단해 배너가 정상적으로 노출됩니다. - 관리자 환경설정의 「운영 주체」·「쿠키 동의 배너」·「자동 차단 정책」 카드 제목과 차단 도메인 안내 문구가 빨간색(경고 색상)으로 표시되던 문제를 바로잡았습니다 — 일반 안내에도 경고 색상이 쓰여 어색하게 강조되던 부분을 다른 관리자 화면과 동일한 색상으로 정리했습니다. +- 「GDPR 동의 이력」과 정책 버전 발행 이력에서 기록이 아주 많아 총 건수를 끝까지 세지 못하면 페이지 이동이 막히던 문제를 수정했습니다. 마지막 페이지로 바로 뛰는 버튼만 감춰지고 이전·다음 이동은 그대로 동작합니다. ## [1.0.1] - 2026-07-16 ### Changed diff --git a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/gdpr_consent_log.json b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/gdpr_consent_log.json index 4e11615e..9bebf3a7 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/gdpr_consent_log.json +++ b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/gdpr_consent_log.json @@ -1163,7 +1163,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{gdprConsentLog?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{gdprConsentLog?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{gdprConsentLog?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{gdprConsentLog?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "idField": "id", diff --git a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json index a043f990..570d1485 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json +++ b/plugins/_bundled/sirsoft-gdpr/resources/layouts/admin/plugin_settings.json @@ -1181,7 +1181,7 @@ "comment": "이력 페이저 — 총건수/현재 페이지를 함께 표시한다. 없으면 최신 20건만 보이고 나머지가 조용히 잘린다.", "type": "basic", "name": "Div", - "if": "{{(gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 1) > 1}}", + "if": "{{((gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 0) > 1) || (gdprPolicyVersionHistory?.data?.pagination?.has_more_pages === true) || ((gdprPolicyVersionHistory?.data?.pagination?.current_page ?? 1) > 1)}}", "props": { "className": "flex items-center justify-between gap-3 pt-3 mt-3 border-t border-gray-200 dark:border-gray-700" }, @@ -1192,7 +1192,7 @@ "props": { "className": "text-label-subtle text-sm" }, - "text": "$t:sirsoft-gdpr.settings.policy_version.history_page_summary|current={{gdprPolicyVersionHistory?.data?.pagination?.current_page ?? 1}}|last={{gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 1}}|total={{gdprPolicyVersionHistory?.data?.pagination?.total ?? 0}}" + "text": "$t:sirsoft-gdpr.settings.policy_version.history_page_summary|current={{gdprPolicyVersionHistory?.data?.pagination?.current_page ?? 1}}|last={{gdprPolicyVersionHistory?.data?.pagination?.last_page ?? '-'}}|total={{gdprPolicyVersionHistory?.data?.pagination?.total ?? 0}}" }, { "type": "basic", @@ -1220,7 +1220,7 @@ "handler": "setState", "params": { "target": "local", - "policyHistoryPage": "{{Math.min(Math.max(1, (_local.policyHistoryPage ?? 1) + (-1)), gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 1)}}" + "policyHistoryPage": "{{gdprPolicyVersionHistory?.data?.pagination?.last_page ? Math.min(Math.max(1, (_local.policyHistoryPage ?? 1) + (-1)), gdprPolicyVersionHistory?.data?.pagination?.last_page) : Math.max(1, (_local.policyHistoryPage ?? 1) + (-1))}}" } }, { @@ -1240,7 +1240,7 @@ "props": { "type": "button", "className": "btn btn-sm btn-neutral", - "disabled": "{{(gdprPolicyVersionHistory?.data?.pagination?.current_page ?? 1) >= (gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 1)}}" + "disabled": "{{gdprPolicyVersionHistory?.data?.pagination?.last_page ? ((gdprPolicyVersionHistory?.data?.pagination?.current_page ?? 1) >= gdprPolicyVersionHistory?.data?.pagination?.last_page) : (gdprPolicyVersionHistory?.data?.pagination?.has_more_pages !== true)}}" }, "text": "$t:sirsoft-gdpr.settings.policy_version.history_next", "actions": [ @@ -1252,7 +1252,7 @@ "handler": "setState", "params": { "target": "local", - "policyHistoryPage": "{{Math.min(Math.max(1, (_local.policyHistoryPage ?? 1) + (1)), gdprPolicyVersionHistory?.data?.pagination?.last_page ?? 1)}}" + "policyHistoryPage": "{{gdprPolicyVersionHistory?.data?.pagination?.last_page ? Math.min(Math.max(1, (_local.policyHistoryPage ?? 1) + (1)), gdprPolicyVersionHistory?.data?.pagination?.last_page) : Math.max(1, (_local.policyHistoryPage ?? 1) + (1))}}" } }, { From 0d1105abfc462ca28656b1b48d627c3af2158d31 Mon Sep 17 00:00:00 2001 From: HeuJung Date: Tue, 4 Aug 2026 09:55:00 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix(basic,admin=5Fbasic):=20=EC=83=81?= =?UTF-8?q?=ED=95=9C=20=EB=AA=A9=EB=A1=9D=EC=9D=98=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A0=80=20=EC=86=8C=EB=B9=84=EB=8B=A8=20=EC=A0=95=ED=95=A9=20?= =?UTF-8?q?=EB=B0=8F=20=ED=99=94=EB=A9=B4=20=ED=91=9C=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 저장소가 총 건수를 상한까지만 세게 되면서 마지막 페이지 번호가 없는 목록이 생겼는데, 화면은 그 값을 1 로 접거나 페이저 노출 조건으로만 쓰고 있었다. 앞은 "1페이지뿐" 으로 잘못 말하고 뒤는 페이저를 통째로 지워 뒤쪽 페이지로 갈 방법 자체를 없앤다. 둘 다 예외도 404 도 내지 않아 화면은 정상으로 보이고 기능만 없다. Pagination 컴포넌트가 세 형태를 모두 이해하도록 넓혔다. 마지막 페이지를 알면 페이지 산술이 우선이고, 모를 때만 다음 페이지 존재 여부로 판정한다. 처음·마지막 버튼을 따로 켜고 끌 수 있게 나눠, 마지막 페이지를 모르는 목록에서는 그 버튼만 감춰지고 나머지 이동은 그대로 열려 있다. DataGrid 는 페이저를 그리는 두 경로 모두에 그 값을 전달한다 — 한쪽만 전달하면 그 화면만 첫 페이지에 갇힌다. 마이페이지 주문·마일리지·위시리스트·알림함, 게시판 목록, 상품 리뷰·문의, 내가 쓴 글, 관리자 설정 탭과 스케줄 이력의 소비 지점을 전건 옮겼다. 검색 화면은 총 건수를 정확히 세지 못한 경우 "N건 이상" 으로 표기하고 검색어를 좁히면 정확한 건수를 볼 수 있다고 안내한다. 탭 배지도 세지 못한 값을 정확한 숫자처럼 보여주지 않는다. 최신순은 첫 페이지부터 커서로 이동하며, 주소로 특정 페이지를 지목한 딥링크는 종전 동작을 유지한다. 쇼핑 첫 화면과 분류 화면을 단일 요청으로 재배선하고, 섹션 partial 이 부모가 선언한 이름을 읽도록 통일했다. 데이터소스 이름에 직결돼 있으면 다른 화면이 재사용할 때 폴백에 걸려 예외도 로그도 없이 섹션이 사라진다. 상품 상세의 "최근 본 상품" 이 계속 비어 있던 것은 존재하지 않는 라우트 파라미터를 읽고 있어서였다. 게시판 목록 세 스킨의 블라인드 아이콘 대비와 이미지 위 공통 색의 다크 모드 표기를 정리했다. --- .../CHANGELOG.md | 1 + .../frontend/partial/admin.json | 11 +- .../g7-template-sirsoft-basic-ja/CHANGELOG.md | 2 + .../frontend/partial/editor.json | 3 +- .../frontend/partial/search.json | 4 +- .../_bundled/sirsoft-admin_basic/CHANGELOG.md | 13 + ...agination-null-last-page-contract.test.tsx | 114 +++++++ .../sirsoft-admin_basic/components.json | 36 ++- .../dist/css/components.css | 2 +- .../dist/js/components.iife.js | 127 ++++---- .../src/components/composite/DataGrid.d.ts | 15 +- .../src/components/composite/Pagination.d.ts | 32 +- .../lang/partial/en/admin.json | 9 +- .../lang/partial/ko/admin.json | 9 +- .../layouts/admin_activity_log_list.json | 18 +- .../layouts/admin_module_list.json | 3 +- .../layouts/admin_plugin_list.json | 3 +- .../layouts/admin_role_list.json | 3 +- .../layouts/admin_user_list.json | 3 +- .../_partial_datagrid.json | 5 +- .../_partial_datagrid.json | 3 +- .../admin_language_pack_list/_content.json | 3 +- .../_partial_datagrid.json | 3 +- .../admin_schedule_list/_modal_history.json | 7 +- .../admin_schedule_list/_tab_schedules.json | 3 +- .../admin_settings/_tab_advanced.json | 145 ++++++++- .../_tab_identity_messages.json | 3 +- .../_tab_identity_policies.json | 8 +- .../admin_settings/_tab_mail_templates.json | 5 +- .../_tab_notification_definitions.json | 5 +- .../admin_template_list/_tab_admin.json | 3 +- .../admin_template_list/_tab_user.json | 3 +- .../src/components/composite/DataGrid.tsx | 35 ++- .../src/components/composite/Pagination.tsx | 96 ++++-- .../DataGridPaginationPropParity.test.ts | 74 +++++ .../composite/__tests__/Pagination.test.tsx | 45 +++ .../sirsoft-admin_basic/src/styles/main.css | 2 +- templates/_bundled/sirsoft-basic/CHANGELOG.md | 10 + ...board-blinded-icon-contrast-parity.test.ts | 78 +++++ ...agination-null-last-page-contract.test.tsx | 114 +++++++ .../shop-index-category-filter.test.tsx | 14 +- .../shop-product-sections-responsive.test.tsx | 28 +- ...hared-partial-datasource-contract.test.tsx | 296 ++++++++++++++++++ .../_bundled/sirsoft-basic/components.json | 114 ++++++- .../sirsoft-basic/dist/css/components.css | 2 +- .../sirsoft-basic/dist/js/components.iife.js | 26 +- .../src/components/composite/Pagination.d.ts | 32 +- .../sirsoft-basic/lang/partial/en/editor.json | 1 + .../sirsoft-basic/lang/partial/en/search.json | 6 +- .../sirsoft-basic/lang/partial/ko/editor.json | 1 + .../sirsoft-basic/lang/partial/ko/search.json | 6 +- .../partials/board/types/basic/index.json | 17 +- .../partials/board/types/card/index.json | 11 +- .../partials/board/types/gallery/index.json | 29 +- .../partials/mypage/board/_my_comments.json | 5 +- .../partials/mypage/board/_my_posts.json | 5 +- .../partials/mypage/inquiries/_list.json | 3 +- .../partials/mypage/mileage/_list.json | 8 +- .../partials/mypage/notifications/_list.json | 8 +- .../layouts/partials/mypage/orders/_list.json | 8 +- .../partials/mypage/wishlist/_list.json | 5 +- .../partials/search/_search_filters.json | 6 +- .../partials/search/_search_input.json | 4 +- .../partials/search/_search_states.json | 22 +- .../layouts/partials/search/_search_tabs.json | 16 +- .../partials/shop/detail/_tab_qna.json | 3 +- .../partials/shop/detail/_tab_reviews.json | 7 +- .../shop/list/_category_breadcrumb.json | 8 +- .../partials/shop/list/_category_filter.json | 12 +- .../partials/shop/list/_new_products.json | 24 +- .../partials/shop/list/_popular_products.json | 24 +- .../partials/shop/list/_product_grid.json | 30 +- .../partials/shop/list/_recent_products.json | 24 +- .../shop/list/_search_filter_bar.json | 2 +- .../sirsoft-basic/layouts/search/index.json | 25 +- .../sirsoft-basic/layouts/shop/category.json | 101 +++--- .../sirsoft-basic/layouts/shop/index.json | 134 ++++---- .../sirsoft-basic/layouts/shop/show.json | 26 +- .../sirsoft-basic/layouts/users/posts.json | 3 +- .../src/components/composite/Pagination.tsx | 96 ++++-- .../__tests__/PaginationHasMorePages.test.tsx | 51 +++ .../_bundled/sirsoft-basic/template.json | 2 +- 82 files changed, 1843 insertions(+), 460 deletions(-) create mode 100644 templates/_bundled/sirsoft-admin_basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx create mode 100644 templates/_bundled/sirsoft-admin_basic/src/components/composite/__tests__/DataGridPaginationPropParity.test.ts create mode 100644 templates/_bundled/sirsoft-basic/__tests__/layouts/board-blinded-icon-contrast-parity.test.ts create mode 100644 templates/_bundled/sirsoft-basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx create mode 100644 templates/_bundled/sirsoft-basic/__tests__/layouts/shop-shared-partial-datasource-contract.test.tsx create mode 100644 templates/_bundled/sirsoft-basic/src/components/composite/__tests__/PaginationHasMorePages.test.tsx diff --git a/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/CHANGELOG.md b/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/CHANGELOG.md index f3da91f7..9b8700de 100644 --- a/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/CHANGELOG.md +++ b/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/CHANGELOG.md @@ -16,6 +16,7 @@ - 역할 관리 목록의 「권한 수」 열 제목 일본어 번역 추가. - 확장 업데이트 모달의 「검색 인덱스 재생성」 선택 항목 문구 일본어 번역 추가 — 항목 이름과 설명(재생성 시 인덱스 잠금·재색인 안내)이 일본어 로케일에서 자연스럽게 표시됩니다. +- 관리자 목록의 총 건수 정확도 표기와 고급 환경설정의 목록 한계값 항목 문구 일본어 번역 추가 — 총 건수를 상한까지만 센 경우의 "N건 이상" 표기와 상한 설정 항목이 일본어 로케일에서 자연스럽게 표시됩니다. ## [1.0.4] - 2026-07-17 diff --git a/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/frontend/partial/admin.json b/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/frontend/partial/admin.json index 5f919366..55b11d69 100644 --- a/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/frontend/partial/admin.json +++ b/lang-packs/_bundled/g7-template-sirsoft-admin_basic-ja/frontend/partial/admin.json @@ -1788,7 +1788,13 @@ "geoip_update_success": "GeoIP DBの更新が完了しました。", "geoip_update_failed": "GeoIP DBの更新に失敗しました。", "seo_sitemap_cache": "Sitemap キャッシュ", - "seo_sitemap_cache_desc": "sitemap.xml をキャッシュします。" + "seo_sitemap_cache_desc": "sitemap.xml をキャッシュします。", + "pagination": "リスト制限値", + "pagination_desc": "大規模リストにおいて、総件数を集計する範囲と直接リクエスト可能なページ番号の上限です。上限を超えると、総件数を「N件以上」と表示し、最後のページへのジャンプのみ非表示になります — 次ページへの移動はそのまま利用可能です。", + "pagination_result_cap": "総件数集計の上限", + "pagination_result_cap_desc": "この件数までのみ正確に集計します。0の場合は常にすべてを集計します(大規模リストの場合、リストが遅くなる可能性があります)。", + "pagination_max_page": "ページ番号の上限", + "pagination_max_page_desc": "アドレスから直接リクエスト可能な最大ページ番号です。0の場合は制限しません。通常の次ページへの移動はこの値に関わらず引き続き可能です。" }, "notification_definitions": { "title": "通知設定", @@ -2307,7 +2313,8 @@ "no_value": "-" }, "pagination": { - "total": "合計{{count}}件" + "total": "合計{{count}}件", + "total_at_least": "合計{{count}}件以上" }, "empty": { "title": "アクティビティログがありません", diff --git a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/CHANGELOG.md b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/CHANGELOG.md index ea9c9392..a4a499f9 100644 --- a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/CHANGELOG.md +++ b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/CHANGELOG.md @@ -10,6 +10,8 @@ - 회원가입 화면의 휴대폰번호·전화번호 입력란 라벨·안내 문구(`auth.mobile`, `auth.phone`) 일본어 번역 추가 — 회원가입 시 연락처 입력란이 일본어 로케일에서 자연스럽게 표시됩니다. - 모달 닫기 버튼의 안내 라벨(`common.close_modal`) 일본어 번역 추가 — 화면 낭독기 사용자에게 버튼 용도가 일본어로 안내됩니다. +- 검색 결과 건수 표기와 검색어 구체화 안내(`search.result_count_suffix_at_least`, `search.refine_query_hint`) 일본어 번역 추가 — 총 건수를 상한까지만 센 경우의 "N건 이상" 표기가 일본어 로케일에서 자연스럽게 표시됩니다. +- 편집기 화면 문구(`editor.*`) 일본어 번역 보강. ## [1.0.1] - 2026-07-08 diff --git a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/editor.json b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/editor.json index a0f5d373..773f033d 100644 --- a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/editor.json +++ b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/editor.json @@ -1103,7 +1103,8 @@ "wishlist": "ウィッシュリスト", "mileage_balance": "マイレージ残高", "mileage_history": "マイレージ履歴", - "reviewSettings": "レビューポリシー設定" + "reviewSettings": "レビューポリシー設定", + "storefront": "ショッピング最初の画面統合データ" }, "provider": { "naver": "Naver", diff --git a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/search.json b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/search.json index a8407b38..c4e9132a 100644 --- a/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/search.json +++ b/lang-packs/_bundled/g7-template-sirsoft-basic-ja/frontend/partial/search.json @@ -47,5 +47,7 @@ "hint_title": "検索のコツ", "hint_1": "2語以上を入力するとより正確な結果が得られます", "hint_2": "検索キーワードはタイトルと内容から一緒に検索されます" - } + }, + "result_count_suffix_at_least": "件以上", + "refine_query_hint": "検索キーワードをより具体的に入力すると、正確な件数と最後のページを確認できます。" } diff --git a/templates/_bundled/sirsoft-admin_basic/CHANGELOG.md b/templates/_bundled/sirsoft-admin_basic/CHANGELOG.md index 6765a254..939f0965 100644 --- a/templates/_bundled/sirsoft-admin_basic/CHANGELOG.md +++ b/templates/_bundled/sirsoft-admin_basic/CHANGELOG.md @@ -29,6 +29,8 @@ - 코어 최소 요구 버전을 7.0.6 로 상향했습니다. 역할 관리 목록의 「권한 수」 열과 메뉴 관리의 하위 메뉴 역할 표시가 7.0.6 에서 추가된 응답 항목을 사용하므로, 이전 버전에서는 값이 비어 보입니다. - SEO 설정의 "지금 생성" 버튼이 sitemap 생성을 예약하고 바로 응답합니다. 사이트 규모가 커도 화면이 멈추지 않으며, 생성 중에는 버튼이 비활성화되고 진행 상황이 화면에 표시됩니다. +- 총 건수를 정확히 셀 수 없을 만큼 기록이 많은 관리자 목록에서도 페이지 이동 버튼이 사라지지 않습니다. 마지막 페이지로 바로 뛰는 버튼만 이때 감춰집니다. +- 목록의 총 건수 정확도 표시를 사용하기 위해 이 템플릿은 이제 코어 7.0.6 이상이 필요합니다. ### Fixed @@ -40,9 +42,12 @@ - 표 하단 합계 영역의 값이 계산되지 않고 표현식 원문 그대로 표시되던 문제를 수정했습니다. 주문 상세의 합계 행에서 수량·적립금·배송비 자리에 값 대신 알 수 없는 문자열이 보였습니다. - 표의 셀 안쪽·확장 영역·합계 영역에서 일부 화면 요소가 아무 표시 없이 사라지던 문제를 수정했습니다. 표 바깥에서는 정상 표시되는 요소가 표 안에서만 빠졌으며, 본인인증 이력의 확장 영역에서 부가 정보 내용이 제목만 남고 보이지 않았습니다. - 카드 목록의 카드 내용 항목 이름이 실제 동작과 다르게 안내되던 문제를 수정했습니다(레이아웃 편집기의 속성 목록에 없는 이름이 표시되고 실제로 쓰는 이름은 빠져 있었습니다). +- 활동 로그 목록에서 이전·다음 버튼이 모두 눌리지 않아 첫 페이지에 갇히던 문제를 수정했습니다. +- 활동 로그의 총 건수가 끝까지 세지 못한 값인데도 정확한 숫자처럼 표시되던 문제를 수정했습니다. 이제 그 경우 「N건 이상」으로 표시되며, 글 번호도 「-」로 표시됩니다. - 모듈·플러그인·템플릿 관리 화면에서 '업데이트 확인'을 눌러 업데이트가 발견돼도 항상 "모두 최신 상태입니다"라고 안내되던 문제를 수정했습니다. - 게시판·상품 카테고리 목록의 새로고침 버튼과 언어팩 재활성화 안내에서 번역되지 않은 내부 문구가 그대로 보이던 문제를 수정했습니다. - 모듈·플러그인·템플릿 관리 화면의 목록 요약이 항상 "총 0개 중 1-0개 표시"로 나오고, 목록이 한 화면을 넘어도 다음 쪽으로 넘어갈 수 없던 문제를 수정했습니다. +- 기록이 아주 많아 총 건수를 정확히 세지 못한 목록에서 페이지 이동 버튼이 통째로 사라지던 문제를 수정했습니다. 마지막 페이지로 바로 뛰는 버튼만 감춰지고 이전·다음 이동은 그대로 동작합니다. - 업데이트 모달에서 수정된 화면 확인에 실패했는데도 "수정된 레이아웃이 없습니다"라고 표시되던 문제를 수정했습니다. 그대로 '모두 교체'를 진행하면 직접 고친 화면이 사라질 수 있어, 이제 확인하지 못했다는 안내와 함께 '수정 유지'를 권장합니다. - 본인확인 코드 입력 화면에서 "남은 시도" 안내를 제거했습니다. 외부 인증 화면을 거쳐 돌아온 경우 실제보다 많은 횟수가 표시될 수 있어, 잘못된 숫자를 보여주는 대신 표시하지 않도록 했습니다. 시도 횟수 제한 자체는 종전대로 적용됩니다. - 회원 목록에서 검색할 때 페이지를 넘길 때와 달리 표 위에 로딩 표시가 뜨지 않던 문제를 수정했습니다. @@ -66,6 +71,14 @@ - 메일 템플릿·알림 정의 목록에서 각 행이 같은 식별자를 공유해 화면 읽기 도구가 항목을 구분하지 못하던 문제를 수정했습니다. - 관리자 목록에서 검색·필터·정렬을 바꿀 때 표가 다시 그려지는 동안 로딩 표시가 나오지 않던 문제를 수정했습니다. 응답이 늦으면 아무 반응이 없는 것처럼 보여 같은 조작을 반복하게 되던 상황이 사라집니다. +### Added + +- 환경설정 > 고급에 목록 한계값 항목을 추가했습니다. 총 건수를 세는 범위와 주소로 직접 요청할 수 있는 최대 페이지 번호를 조정할 수 있으며, 두 값 모두 0 으로 두면 제한하지 않습니다. + +### Changed + +- 페이지네이션에서 처음/마지막 버튼을 따로 켜고 끌 수 있게 했습니다. 총 건수를 정확히 셀 수 없는 목록에서는 마지막 페이지 버튼이 자동으로 감춰지고 다음 페이지 이동은 그대로 됩니다. + ## [1.0.3] - 2026-07-14 ### Fixed diff --git a/templates/_bundled/sirsoft-admin_basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx b/templates/_bundled/sirsoft-admin_basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx new file mode 100644 index 00000000..210373cc --- /dev/null +++ b/templates/_bundled/sirsoft-admin_basic/__tests__/layouts/pagination-null-last-page-contract.test.tsx @@ -0,0 +1,114 @@ +/** + * @file pagination-null-last-page-contract.test.tsx + * @description 상한 목록의 last_page=null 을 화면이 1 로 붕괴시키지 않는지 전수 검증 (#519) + * + * 총 건수를 상한까지만 센 목록은 마지막 페이지를 계산할 수 없어 서버가 `last_page: null` + * 을 보낸다. 화면이 이를 `?? 1` 로 채우면 "1페이지뿐" 이라고 잘못 말하고, 페이저 표시 + * 조건을 `last_page > 1` 로만 잡으면 페이저가 통째로 사라져 **"다음" 이동 경로 자체가 + * 없어진다.** + * + * 특정 화면을 손으로 열거하면 새로 추가되는 목록을 놓치므로, 이 템플릿(관리자)의 레이아웃 전체를 + * 스캔해 조건으로 도출한다. + * + * @vitest-environment jsdom + */ + +import { describe, it, expect } from 'vitest'; +import * as fs from 'fs'; +import * as path from 'path'; + +const layoutsDir = path.resolve(__dirname, '../../layouts'); + +/** 레이아웃 JSON 파일 경로를 재귀 수집한다. */ +function collectLayoutFiles(dir: string, found: string[] = []): string[] { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) collectLayoutFiles(full, found); + else if (entry.isFile() && entry.name.endsWith('.json')) found.push(full); + } + return found; +} + +const layoutFiles = collectLayoutFiles(layoutsDir); + +/** 저장소 기준 상대 경로로 바꾼다 (실패 메시지 가독성). */ +function rel(file: string): string { + return path.relative(layoutsDir, file).replace(/\\/g, '/'); +} + +describe('상한 목록의 last_page=null 계약 (#519)', () => { + it('레이아웃 파일을 실제로 수집해야 함 (스캔 대상이 0이면 이 테스트는 아무것도 증명하지 못한다)', () => { + expect(layoutFiles.length).toBeGreaterThan(0); + }); + + it('last_page 를 1 로 채우는 표현식이 없어야 함', () => { + // `last_page ?? 1` / `last_page || 1` — 계산 불가한 값을 사실처럼 채우는 형태 + const pattern = /last_page\s*(\?\?|\|\|)\s*1(?![0-9])/; + const offenders: string[] = []; + + for (const file of layoutFiles) { + const raw = fs.readFileSync(file, 'utf-8'); + raw.split('\n').forEach((line, i) => { + if (pattern.test(line)) offenders.push(`${rel(file)}:${i + 1}`); + }); + } + + expect(offenders).toEqual([]); + }); + + it('페이저 표시 조건이 last_page 하나에만 의존하지 않아야 함', () => { + // `"if": "{{... last_page > 1}}"` 만으로 판정하면 null 일 때 페이저가 사라진다. + // has_more_pages 또는 current_page 를 함께 보아야 한다. + const offenders: string[] = []; + + for (const file of layoutFiles) { + const raw = fs.readFileSync(file, 'utf-8'); + raw.split('\n').forEach((line, i) => { + if (!/"if"\s*:/.test(line) || !/last_page/.test(line)) return; + if (/has_more_pages/.test(line) || /current_page/.test(line)) return; + offenders.push(`${rel(file)}:${i + 1}`); + }); + } + + expect(offenders).toEqual([]); + }); + + it('totalPages 를 null 로 넘기는 Pagination 은 hasMorePages 를 함께 넘겨야 함', () => { + // totalPages 가 null 이면 컴포넌트는 총 페이지 수를 모르므로, + // hasMorePages 없이는 "다음" 버튼을 열 근거가 없어 비활성화된다. + const offenders: string[] = []; + + /** Pagination 컴포넌트 노드를 재귀 수집한다. */ + function collectPaginations(node: unknown, acc: any[] = []): any[] { + if (!node || typeof node !== 'object') return acc; + const obj = node as Record; + if (obj.name === 'Pagination' && obj.props) acc.push(obj); + for (const value of Object.values(obj)) { + if (Array.isArray(value)) value.forEach((v) => collectPaginations(v, acc)); + else if (value && typeof value === 'object') collectPaginations(value, acc); + } + return acc; + } + + for (const file of layoutFiles) { + const raw = fs.readFileSync(file, 'utf-8'); + let parsed: unknown; + try { + parsed = JSON.parse(raw); + } catch { + continue; + } + + for (const node of collectPaginations(parsed)) { + const totalPages = node.props.totalPages; + if (typeof totalPages !== 'string') continue; + if (!/\?\?\s*null/.test(totalPages)) continue; + if (node.props.hasMorePages === undefined) { + offenders.push(`${rel(file)} — ${totalPages}`); + } + } + } + + expect(offenders).toEqual([]); + }); +}); diff --git a/templates/_bundled/sirsoft-admin_basic/components.json b/templates/_bundled/sirsoft-admin_basic/components.json index 0ec95389..c3ad137b 100644 --- a/templates/_bundled/sirsoft-admin_basic/components.json +++ b/templates/_bundled/sirsoft-admin_basic/components.json @@ -1299,10 +1299,15 @@ "required": false, "description": "서버 사이드 페이지네이션 현재 페이지" }, + "serverHasMorePages": { + "type": "boolean", + "required": false, + "description": "다음 페이지 존재 여부 — 총 건수를 상한까지만 센 목록에서 '다음' 이동을 열어 둔다" + }, "serverTotalPages": { "type": "number", "required": false, - "description": "서버 사이드 페이지네이션 전체 페이지 수" + "description": "서버 사이드 페이지네이션 전체 페이지 수 (총 건수가 부정확하면 null)" }, "alwaysShowPagination": { "type": "boolean", @@ -1443,8 +1448,8 @@ }, "totalPages": { "type": "number", - "required": true, - "description": "전체 페이지 수" + "required": false, + "description": "전체 페이지 수. 총 건수를 정확히 세지 못한 목록은 null (페이지 번호 목록과 마지막 페이지 점프가 감춰지고 이전/다음만 남음)" }, "onPageChange": { "type": "function", @@ -1463,6 +1468,21 @@ "default": true, "description": "처음/마지막 버튼 표시 여부" }, + "showFirst": { + "type": "boolean", + "required": false, + "description": "처음 버튼 표시 여부 (미지정 시 showFirstLast 를 따름)" + }, + "showLast": { + "type": "boolean", + "required": false, + "description": "마지막 버튼 표시 여부 (미지정 시 showFirstLast 를 따름). 총 건수가 부정확해 totalPages 가 null 이면 이 값과 무관하게 감춰짐" + }, + "hasMorePages": { + "type": "boolean", + "required": false, + "description": "다음 페이지 존재 여부. 총 건수를 모르는 목록에서 '다음' 버튼 활성 여부를 이 값으로 판정" + }, "className": { "type": "string", "required": false, @@ -4767,7 +4787,11 @@ "type": "composite", "description": "커스텀 항목 렌더링이 가능한 리치 셀렉트 컴포넌트", "path": "src/components/composite/RichSelect.tsx", - "skipBindingKeys": ["children", "selectedChildren", "__componentLayoutDefs"], + "skipBindingKeys": [ + "children", + "selectedChildren", + "__componentLayoutDefs" + ], "props": { "options": { "type": "array", @@ -4824,7 +4848,9 @@ "type": "composite", "description": "Dropdown button with portal-based menu similar to ActionMenu", "path": "src/components/composite/DropdownButton.tsx", - "skipBindingKeys": ["children"], + "skipBindingKeys": [ + "children" + ], "props": { "label": { "type": "string", diff --git a/templates/_bundled/sirsoft-admin_basic/dist/css/components.css b/templates/_bundled/sirsoft-admin_basic/dist/css/components.css index 26d39150..cf7c34f2 100644 --- a/templates/_bundled/sirsoft-admin_basic/dist/css/components.css +++ b/templates/_bundled/sirsoft-admin_basic/dist/css/components.css @@ -1 +1 @@ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-scroll-snap-strictness:proximity;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Pretendard", ui-sans-serif, system-ui, -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-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-lime-50:oklch(98.6% .031 120.757);--color-lime-100:oklch(96.7% .067 122.328);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-lime-400:oklch(84.1% .238 128.85);--color-lime-500:oklch(76.8% .233 130.85);--color-lime-600:oklch(64.8% .2 131.684);--color-lime-700:oklch(53.2% .157 131.589);--color-lime-800:oklch(45.3% .124 130.933);--color-lime-900:oklch(40.5% .101 131.063);--color-lime-950:oklch(27.4% .072 132.109);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-emerald-950:oklch(26.2% .051 172.552);--color-teal-50:oklch(98.4% .014 180.72);--color-teal-100:oklch(95.3% .051 180.801);--color-teal-200:oklch(91% .096 180.426);--color-teal-300:oklch(85.5% .138 181.071);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-600:oklch(60% .118 184.704);--color-teal-700:oklch(51.1% .096 186.391);--color-teal-800:oklch(43.7% .078 188.216);--color-teal-900:oklch(38.6% .063 188.416);--color-teal-950:oklch(27.7% .046 192.524);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-100:oklch(95.6% .045 203.388);--color-cyan-200:oklch(91.7% .08 205.041);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-cyan-700:oklch(52% .105 223.128);--color-cyan-800:oklch(45% .085 224.283);--color-cyan-900:oklch(39.8% .07 227.392);--color-cyan-950:oklch(30.2% .056 229.695);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-200:oklch(90.1% .058 230.902);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:oklch(50% .134 242.749);--color-sky-800:oklch(44.3% .11 240.79);--color-sky-900:oklch(39.1% .09 240.876);--color-sky-950:oklch(29.3% .066 243.157);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-blue-950:oklch(28.2% .091 267.935);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-300:oklch(78.5% .115 274.713);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-indigo-950:oklch(25.7% .09 281.288);--color-violet-50:oklch(96.9% .016 293.756);--color-violet-100:oklch(94.3% .029 294.588);--color-violet-200:oklch(89.4% .057 293.283);--color-violet-300:oklch(81.1% .111 293.571);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:oklch(49.1% .27 292.581);--color-violet-800:oklch(43.2% .232 292.759);--color-violet-900:oklch(38% .189 293.745);--color-violet-950:oklch(28.3% .141 291.089);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-fuchsia-50:oklch(97.7% .017 320.058);--color-fuchsia-100:oklch(95.2% .037 318.852);--color-fuchsia-200:oklch(90.3% .076 319.62);--color-fuchsia-300:oklch(83.3% .145 321.434);--color-fuchsia-400:oklch(74% .238 322.16);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-fuchsia-600:oklch(59.1% .293 322.896);--color-fuchsia-700:oklch(51.8% .253 323.949);--color-fuchsia-800:oklch(45.2% .211 324.591);--color-fuchsia-900:oklch(40.1% .17 325.612);--color-fuchsia-950:oklch(29.3% .136 325.661);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-100:oklch(94.8% .028 342.258);--color-pink-200:oklch(89.9% .061 343.231);--color-pink-300:oklch(82.3% .12 346.018);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-pink-600:oklch(59.2% .249 .584);--color-pink-700:oklch(52.5% .223 3.958);--color-pink-800:oklch(45.9% .187 3.815);--color-pink-900:oklch(40.8% .153 2.432);--color-pink-950:oklch(28.4% .109 3.907);--color-rose-50:oklch(96.9% .015 12.422);--color-rose-100:oklch(94.1% .03 12.58);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-rose-700:oklch(51.4% .222 16.935);--color-rose-800:oklch(45.5% .188 13.697);--color-rose-900:oklch(41% .159 10.272);--color-rose-950:oklch(27.1% .105 12.094);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-slate-950:oklch(12.9% .042 264.695);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-gray-950:oklch(13% .028 261.692);--color-zinc-50:oklch(98.5% 0 0);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-neutral-50:oklch(98.5% 0 0);--color-neutral-100:oklch(97% 0 0);--color-neutral-200:oklch(92.2% 0 0);--color-neutral-300:oklch(87% 0 0);--color-neutral-400:oklch(70.8% 0 0);--color-neutral-500:oklch(55.6% 0 0);--color-neutral-600:oklch(43.9% 0 0);--color-neutral-700:oklch(37.1% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-neutral-900:oklch(20.5% 0 0);--color-neutral-950:oklch(14.5% 0 0);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-sm:40rem;--breakpoint-md:48rem;--breakpoint-lg:64rem;--breakpoint-xl:80rem;--breakpoint-2xl:96rem;--container-3xs:16rem;--container-2xs:18rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--text-7xl:4.5rem;--text-7xl--line-height:1;--text-8xl:6rem;--text-8xl--line-height:1;--text-9xl:8rem;--text-9xl--line-height:1;--font-weight-thin:100;--font-weight-extralight:200;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--leading-loose:2;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--drop-shadow-sm:0 1px 2px #00000026;--drop-shadow-md:0 3px 3px #0000001f;--drop-shadow-lg:0 4px 4px #00000026;--drop-shadow-xl:0 9px 7px #0000001a;--drop-shadow-2xl:0 25px 25px #00000026;--ease-in:cubic-bezier(.4, 0, 1, 1);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--blur-xl:24px;--blur-2xl:40px;--blur-3xl:64px;--aspect-video:16 / 9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}body{color:var(--color-gray-900);font-size:.875rem;line-height:1.5}.dark :is(body){color:var(--color-gray-100)}}@layer components{.admin_layout_root{background-color:var(--color-gray-50);height:100vh;display:flex;overflow:hidden}.dark .admin_layout_root{background-color:var(--color-gray-900)}.mobile_overlay{inset:calc(var(--spacing) * 0);z-index:40;background-color:#00000080;position:fixed}.left_sidebar_area{inset-block:calc(var(--spacing) * 0);left:calc(var(--spacing) * 0);z-index:50;width:calc(var(--spacing) * 64);background-color:var(--color-white);flex-direction:column;display:flex;position:relative}.dark .left_sidebar_area{background-color:var(--color-gray-800)}.left_sidebar_area{transition:width .3s}.left_sidebar_area_collapsed{width:calc(var(--spacing) * 0);overflow:hidden}.left_sidebar_area_portable{left:calc(var(--spacing) * 0);z-index:50;width:calc(var(--spacing) * 64);background-color:var(--color-white);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.3s;flex-direction:column;transition-duration:.3s;display:flex;position:fixed}.dark .left_sidebar_area_portable{background-color:var(--color-gray-800)}.left_sidebar_area_portable{top:4rem;bottom:0;transform:translate(-100%)}.left_sidebar_area_portable_open{transform:translate(0)}.sidebar_header{height:calc(var(--spacing) * 16);padding-inline:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.sidebar_logo{height:calc(var(--spacing) * 10);min-width:calc(var(--spacing) * 0);align-items:center;gap:calc(var(--spacing) * 2);flex:1;display:flex}.logo_image{max-height:calc(var(--spacing) * 10);object-fit:contain;width:auto}.admin_sidebar_menu{padding-top:calc(var(--spacing) * 4);box-shadow:inset 3px 2px 2px var(--tw-shadow-color,#0000000a);flex:1;overflow-y:auto}.admin_sidebar_menu_portable{padding-top:calc(var(--spacing) * 4);flex:1;overflow-y:auto}.admin_sidebar_empty{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 8);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-400)}.dark .admin_sidebar_empty{color:var(--color-gray-500)}.admin_sidebar_empty_icon{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));color:var(--color-gray-300)}.dark .admin_sidebar_empty_icon{color:var(--color-gray-600)}.sidebar_footer{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100);padding:calc(var(--spacing) * 3);flex-shrink:0}.dark .sidebar_footer{border-color:var(--color-gray-700)}.sidebar_view_site_link{height:calc(var(--spacing) * 9);align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));text-decoration-line:none;display:flex}@media(hover:hover){.sidebar_view_site_link:hover{background-color:var(--color-slate-100);color:var(--color-slate-900)}}.dark .sidebar_view_site_link{color:var(--color-slate-300)}@media(hover:hover){.dark .sidebar_view_site_link:hover{background-color:var(--color-slate-700);color:var(--color-white)}}.sidebar_view_site_icon{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));flex-shrink:0}.sidebar_view_site_label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.right_content_wrapper{min-width:calc(var(--spacing) * 0);flex-direction:column;flex:1;display:flex}.right_content_area{min-height:calc(var(--spacing) * 0);min-width:calc(var(--spacing) * 0);flex:1;overflow:auto}.main_header{z-index:50;height:calc(var(--spacing) * 16);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 8);flex-shrink:0;justify-content:space-between;align-items:center;display:flex;position:relative}.dark .main_header{background-color:var(--color-gray-800)}.main_header{box-shadow:0 1px 3px #00000014}.header_left{flex:1;align-items:center;display:flex}.header_right{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.menu_toggle_btn{margin-left:calc(var(--spacing) * -2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);color:var(--color-slate-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.menu_toggle_btn:hover{background-color:var(--color-slate-100);color:var(--color-slate-900)}}.dark .menu_toggle_btn{color:var(--color-slate-400)}@media(hover:hover){.dark .menu_toggle_btn:hover{background-color:var(--color-slate-700);color:var(--color-white)}}.menu_toggle_btn_icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.user_avatar_section{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 3);display:flex;position:relative}.user_avatar{height:calc(var(--spacing) * 9);width:calc(var(--spacing) * 9);background-color:var(--color-gray-200);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex;overflow:hidden}.dark .user_avatar{background-color:var(--color-gray-600)}.user_avatar_img{object-fit:cover;width:100%;height:100%}.user_info{display:none}@media(min-width:48rem){.user_info{display:block}}.mobile_header_portable{z-index:50;height:calc(var(--spacing) * 16);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex;position:relative}.dark .mobile_header_portable{background-color:var(--color-gray-800)}.mobile_header_portable{box-shadow:0 1px 3px #00000014}.mobile_header_left{align-items:center;gap:calc(var(--spacing) * 3);display:flex}.mobile_header_right{align-items:center;gap:calc(var(--spacing) * 2);flex-shrink:0;display:flex}.mobile_logo{min-width:calc(var(--spacing) * 0);align-items:center;gap:calc(var(--spacing) * 2);flex:1;display:flex}.mobile_logo_image{height:calc(var(--spacing) * 8);object-fit:contain;width:auto;max-width:100%}.main_content{min-height:fit-content}.layout_warnings{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2)}.text-primary{color:var(--color-gray-900)}.dark .text-primary{color:var(--color-white)}.text-secondary{color:var(--color-gray-600)}.dark .text-secondary{color:var(--color-gray-400)}.text-muted,.dark .text-muted{color:var(--color-gray-500)}.text-tertiary{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .text-tertiary{color:var(--color-gray-400)}.text-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .text-label{color:var(--color-gray-400)}.text-label-subtle{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .text-label-subtle{color:var(--color-gray-400)}.text-heading{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .text-heading{color:var(--color-white)}.section-heading{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-heading{color:var(--color-white)}.section-title{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-title{color:var(--color-white)}.section-description{margin-bottom:calc(var(--spacing) * 6);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .section-description{color:var(--color-gray-400)}.card-title{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .card-title{color:var(--color-white)}.card-title-aside{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);display:flex}.card-description{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .card-description{color:var(--color-gray-400)}.admin-card>.card-title{margin-bottom:calc(var(--spacing) * 4)}.admin-card>.card-title:has(+.card-description){margin-bottom:calc(var(--spacing) * 0)}.admin-card>.card-description{margin-bottom:calc(var(--spacing) * 4)}.card-header{margin-bottom:calc(var(--spacing) * 0);align-items:center;gap:calc(var(--spacing) * 2);--tw-leading:1;line-height:1;display:flex}.card-header>*{--tw-leading:1;line-height:1}.text-body{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}.dark .text-body{color:var(--color-gray-300)}.icon-muted{color:var(--color-gray-400)}.dark .icon-muted{color:var(--color-gray-500)}.bg-success{background-color:var(--color-green-100)}.dark .bg-success{background-color:var(--color-green-900)}.bg-success-light{background-color:var(--color-green-50)}.dark .bg-success-light{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .bg-success-light{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.bg-success-solid{background-color:var(--color-green-600)}.dark .bg-success-solid{background-color:var(--color-green-500)}.bg-error{background-color:var(--color-red-100)}.dark .bg-error{background-color:var(--color-red-900)}.bg-error-light{background-color:var(--color-red-50)}.dark .bg-error-light{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .bg-error-light{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.bg-error-solid{background-color:var(--color-red-600)}.dark .bg-error-solid{background-color:var(--color-red-500)}.bg-warning{background-color:var(--color-yellow-100)}.dark .bg-warning{background-color:var(--color-yellow-900)}.bg-warning-light{background-color:var(--color-yellow-50)}.dark .bg-warning-light{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .bg-warning-light{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.bg-warning-solid{background-color:var(--color-yellow-600)}.dark .bg-warning-solid{background-color:var(--color-yellow-500)}.bg-info{background-color:var(--color-blue-100)}.dark .bg-info{background-color:var(--color-blue-900)}.bg-info-light{background-color:var(--color-blue-50)}.dark .bg-info-light{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .bg-info-light{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.bg-info-solid{background-color:var(--color-blue-600)}.dark .bg-info-solid{background-color:var(--color-blue-500)}.bg-ready{background-color:var(--color-cyan-100)}.dark .bg-ready{background-color:var(--color-cyan-900)}.bg-ready-light{background-color:var(--color-cyan-50)}.dark .bg-ready-light{background-color:#104e6433}@supports (color:color-mix(in lab,red,red)){.dark .bg-ready-light{background-color:color-mix(in oklab,var(--color-cyan-900) 20%,transparent)}}.bg-ready-solid{background-color:var(--color-cyan-600)}.dark .bg-ready-solid{background-color:var(--color-cyan-400)}.bg-progress{background-color:var(--color-indigo-100)}.dark .bg-progress{background-color:var(--color-indigo-900)}.bg-progress-light{background-color:var(--color-indigo-50)}.dark .bg-progress-light{background-color:#312c8533}@supports (color:color-mix(in lab,red,red)){.dark .bg-progress-light{background-color:color-mix(in oklab,var(--color-indigo-900) 20%,transparent)}}.bg-progress-solid{background-color:var(--color-indigo-600)}.dark .bg-progress-solid{background-color:var(--color-indigo-400)}.bg-complete{background-color:var(--color-pink-100)}.dark .bg-complete{background-color:var(--color-pink-900)}.bg-complete-light{background-color:var(--color-pink-50)}.dark .bg-complete-light{background-color:#86104333}@supports (color:color-mix(in lab,red,red)){.dark .bg-complete-light{background-color:color-mix(in oklab,var(--color-pink-900) 20%,transparent)}}.bg-complete-solid{background-color:var(--color-pink-600)}.dark .bg-complete-solid{background-color:var(--color-pink-400)}.bg-hold{background-color:var(--color-gray-100)}.dark .bg-hold{background-color:var(--color-gray-800)}.bg-hold-light{background-color:var(--color-gray-50)}.dark .bg-hold-light{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .bg-hold-light{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.bg-hold-solid{background-color:var(--color-gray-500)}.dark .bg-hold-solid{background-color:var(--color-gray-400)}.text-success{color:var(--color-green-600)}.dark .text-success{color:var(--color-green-400)}.text-error{color:var(--color-red-600)}.dark .text-error{color:var(--color-red-400)}.text-warning{color:var(--color-yellow-600)}.dark .text-warning{color:var(--color-yellow-400)}.text-info{color:var(--color-blue-600)}.dark .text-info{color:var(--color-blue-400)}.text-ready{color:var(--color-cyan-600)}.dark .text-ready{color:var(--color-cyan-400)}.text-progress{color:var(--color-indigo-600)}.dark .text-progress{color:var(--color-indigo-400)}.text-complete{color:var(--color-pink-600)}.dark .text-complete{color:var(--color-pink-400)}.text-hold{color:var(--color-gray-500)}.dark .text-hold{color:var(--color-gray-400)}.text-success-light{color:var(--color-white)}.dark .text-success-light{color:var(--color-green-900)}.text-error-light{color:var(--color-white)}.dark .text-error-light{color:var(--color-red-900)}.text-warning-light{color:var(--color-white)}.dark .text-warning-light{color:var(--color-yellow-900)}.text-info-light{color:var(--color-white)}.dark .text-info-light{color:var(--color-blue-900)}.text-ready-light{color:var(--color-white)}.dark .text-ready-light{color:var(--color-cyan-900)}.text-progress-light{color:var(--color-white)}.dark .text-progress-light{color:var(--color-indigo-900)}.text-complete-light{color:var(--color-white)}.dark .text-complete-light{color:var(--color-pink-900)}.text-hold-light{color:var(--color-white)}.dark .text-hold-light{color:var(--color-gray-900)}.border-success{border-color:var(--color-green-200)}.dark .border-success{border-color:var(--color-green-800)}.border-error{border-color:var(--color-red-200)}.dark .border-error{border-color:var(--color-red-800)}.border-warning{border-color:var(--color-yellow-200)}.dark .border-warning{border-color:var(--color-yellow-800)}.border-info{border-color:var(--color-blue-200)}.dark .border-info{border-color:var(--color-blue-800)}.border-ready{border-color:var(--color-cyan-200)}.dark .border-ready{border-color:var(--color-cyan-800)}.border-progress{border-color:var(--color-indigo-200)}.dark .border-progress{border-color:var(--color-indigo-800)}.border-complete{border-color:var(--color-pink-200)}.dark .border-complete{border-color:var(--color-pink-800)}.border-hold{border-color:var(--color-gray-300)}.dark .border-hold{border-color:var(--color-gray-600)}.btn-success{background-color:var(--color-green-600);color:var(--color-white)}@media(hover:hover){.btn-success:hover{background-color:var(--color-green-700)}}.dark .btn-success{background-color:var(--color-green-500)}@media(hover:hover){.dark .btn-success:hover{background-color:var(--color-green-600)}}.btn-danger{background-color:var(--color-red-600);color:var(--color-white)}@media(hover:hover){.btn-danger:hover{background-color:var(--color-red-700)}}.dark .btn-danger{background-color:var(--color-red-500)}@media(hover:hover){.dark .btn-danger:hover{background-color:var(--color-red-600)}}.btn-warning{background-color:var(--color-yellow-600);color:var(--color-white)}@media(hover:hover){.btn-warning:hover{background-color:var(--color-yellow-700)}}.dark .btn-warning{background-color:var(--color-yellow-500)}@media(hover:hover){.dark .btn-warning:hover{background-color:var(--color-yellow-600)}}.btn-info{background-color:var(--color-blue-600);color:var(--color-white)}@media(hover:hover){.btn-info:hover{background-color:var(--color-blue-700)}}.dark .btn-info{background-color:var(--color-blue-500)}@media(hover:hover){.dark .btn-info:hover{background-color:var(--color-blue-600)}}.btn-neutral{background-color:var(--color-gray-600);color:var(--color-white)}@media(hover:hover){.btn-neutral:hover{background-color:var(--color-gray-700)}}.dark .btn-neutral{background-color:var(--color-gray-500)}@media(hover:hover){.dark .btn-neutral:hover{background-color:var(--color-gray-600)}}.btn-sm{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5)}.button{cursor:pointer;justify-content:center;align-items:center;display:inline-flex}.button:disabled{cursor:not-allowed}.btn{cursor:pointer;justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;transition-duration:.2s;display:inline-flex}.btn:disabled{cursor:not-allowed;opacity:.5}.btn-primary{background-color:var(--color-gray-900);color:var(--color-white)}@media(hover:hover){.btn-primary:hover{background-color:var(--color-gray-800)}}.dark .btn-primary{background-color:var(--color-white);color:var(--color-gray-900)}@media(hover:hover){.dark .btn-primary:hover{background-color:var(--color-gray-100)}}.btn-secondary{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);color:var(--color-gray-700)}@media(hover:hover){.btn-secondary:hover{background-color:var(--color-gray-50)}}.dark .btn-secondary{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-300)}@media(hover:hover){.dark .btn-secondary:hover{background-color:var(--color-gray-700)}}.btn-outline{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);background-color:#0000}@media(hover:hover){.btn-outline:hover{background-color:var(--color-gray-50)}}.dark .btn-outline{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .btn-outline:hover{background-color:var(--color-gray-700)}}.btn-ghost{color:var(--color-gray-600);background-color:#0000}@media(hover:hover){.btn-ghost:hover{background-color:var(--color-gray-100)}}.dark .btn-ghost{color:var(--color-gray-400)}@media(hover:hover){.dark .btn-ghost:hover{background-color:var(--color-gray-700)}}.btn-icon{border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);color:var(--color-gray-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.btn-icon:hover{background-color:var(--color-gray-100)}}.dark .btn-icon{color:var(--color-gray-400)}@media(hover:hover){.dark .btn-icon:hover{background-color:var(--color-gray-700)}}.btn-preset{background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);border-radius:3.40282e38px}.dark .btn-preset{background-color:var(--color-gray-700);color:var(--color-gray-300)}.btn-date{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .btn-date{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-date.active{border-color:var(--color-gray-900);background-color:var(--color-gray-900);color:var(--color-white)}.dark .btn-date.active{border-color:var(--color-white);background-color:var(--color-white);color:var(--color-gray-900)}.btn-badge{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);border-radius:3.40282e38px}.dark .btn-badge{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-badge.active{border-color:var(--color-gray-900);color:var(--color-gray-900)}.dark .btn-badge.active{border-color:var(--color-white);color:var(--color-blue-400)}.btn-group{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);display:inline-flex;overflow:hidden}.dark .btn-group{border-color:var(--color-gray-700)}.btn-group-item{border-right-style:var(--tw-border-style);border-right-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.btn-group-item:hover{background-color:var(--color-gray-50)}}.dark .btn-group-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}@media(hover:hover){.dark .btn-group-item:hover{background-color:var(--color-gray-700)}}.btn-group-item:last-child{border-right-style:var(--tw-border-style);border-right-width:0}.btn-group-item.active{background-color:var(--color-gray-900);color:var(--color-white)}.dark .btn-group-item.active{background-color:var(--color-white);color:var(--color-gray-900)}.btn-badge-green{border-color:var(--color-green-500);background-color:var(--color-green-50);color:var(--color-green-600)}.dark .btn-badge-green{border-color:var(--color-green-400);background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-green{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .btn-badge-green{color:var(--color-green-400)}.btn-badge-blue{border-color:var(--color-blue-500);background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .btn-badge-blue{border-color:var(--color-blue-400);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-blue{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .btn-badge-blue{color:var(--color-blue-400)}.btn-badge-cyan{border-color:var(--color-cyan-500);background-color:var(--color-cyan-50);color:var(--color-cyan-600)}.dark .btn-badge-cyan{border-color:var(--color-cyan-400);background-color:#104e6433}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-cyan{background-color:color-mix(in oklab,var(--color-cyan-900) 20%,transparent)}}.dark .btn-badge-cyan{color:var(--color-cyan-400)}.btn-badge-indigo{border-color:var(--color-indigo-500);background-color:var(--color-indigo-50);color:var(--color-indigo-600)}.dark .btn-badge-indigo{border-color:var(--color-indigo-400);background-color:#312c8533}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-indigo{background-color:color-mix(in oklab,var(--color-indigo-900) 20%,transparent)}}.dark .btn-badge-indigo{color:var(--color-indigo-400)}.btn-badge-pink{border-color:var(--color-pink-500);background-color:var(--color-pink-50);color:var(--color-pink-600)}.dark .btn-badge-pink{border-color:var(--color-pink-400);background-color:#86104333}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-pink{background-color:color-mix(in oklab,var(--color-pink-900) 20%,transparent)}}.dark .btn-badge-pink{color:var(--color-pink-400)}.btn-badge-red{border-color:var(--color-red-500);background-color:var(--color-red-50);color:var(--color-red-600)}.dark .btn-badge-red{border-color:var(--color-red-400);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-red{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .btn-badge-red{color:var(--color-red-400)}.btn-badge-gray{border-color:var(--color-gray-300);background-color:var(--color-gray-50);color:var(--color-gray-600)}.dark .btn-badge-gray{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-badge-yellow{border-color:var(--color-yellow-500);background-color:var(--color-yellow-50);color:var(--color-yellow-600)}.dark .btn-badge-yellow{border-color:var(--color-yellow-400);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-yellow{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .btn-badge-yellow{color:var(--color-yellow-400)}.btn-badge-orange{border-color:var(--color-orange-500);background-color:var(--color-orange-50);color:var(--color-orange-600)}.dark .btn-badge-orange{border-color:var(--color-orange-400);background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-orange{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .btn-badge-orange{color:var(--color-orange-400)}.input{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input::placeholder{color:var(--color-gray-400)}.input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .input{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .input::placeholder{color:var(--color-gray-500)}.input:disabled{cursor:not-allowed;background-color:var(--color-gray-100);color:var(--color-gray-400)}.dark .input:disabled{background-color:var(--color-gray-800);color:var(--color-gray-500)}.input-tag{border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input-tag:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .input-tag{background-color:var(--color-gray-700);color:var(--color-gray-100)}.input-tag{min-height:2.25rem}.textarea{border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.textarea::placeholder{color:var(--color-gray-400)}.textarea:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .textarea{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .textarea::placeholder{color:var(--color-gray-500)}.textarea:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .textarea:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.input-readonly{cursor:default;color:var(--color-gray-500)}.dark .input-readonly{color:var(--color-gray-400)}.input-w-xs{width:calc(var(--spacing) * 20)}.input-w-sm{width:calc(var(--spacing) * 32)}.input-w-md{width:calc(var(--spacing) * 48)}.input-w-lg{width:calc(var(--spacing) * 64)}.input-w-xl{width:calc(var(--spacing) * 80)}.input-w-auto{width:auto}.input-search-container{border-radius:var(--radius-lg);background-color:var(--color-gray-100);align-items:center;display:flex;overflow:hidden}.dark .input-search-container{background-color:var(--color-gray-700)}.input-search-container .input-search-icon{padding-left:calc(var(--spacing) * 3);color:var(--color-gray-400)}.dark :is(.input-search-container .input-search-icon){color:var(--color-gray-500)}.input-search-container input{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);background-color:#0000;flex:1}.input-search-container input::placeholder{color:var(--color-gray-400)}.input-search-container input:focus{--tw-outline-style:none;outline-style:none}.dark :is(.input-search-container input){color:var(--color-gray-100)}.dark :is(.input-search-container input)::placeholder{color:var(--color-gray-500)}.input-search-container .input-search-dropdown{cursor:pointer;border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}@media(hover:hover){.input-search-container .input-search-dropdown:hover{background-color:var(--color-gray-50)}}.dark :is(.input-search-container .input-search-dropdown){border-color:var(--color-gray-700);color:var(--color-gray-400)}@media(hover:hover){.dark :is(.input-search-container .input-search-dropdown):hover{background-color:var(--color-gray-700)}}.select{height:calc(var(--spacing) * 9);cursor:pointer;appearance:none;border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.select:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select{background-color:var(--color-gray-700);color:var(--color-gray-100)}.select:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .select:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem}.dark .select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")}.select-composite{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.select-composite:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select-composite{background-color:var(--color-gray-700);color:var(--color-gray-100)}.select-composite:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .select-composite:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.select-composite-sm{height:calc(var(--spacing) * 8);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.select-w-sm{width:calc(var(--spacing) * 32)}.select-w-md{width:calc(var(--spacing) * 48)}.select-w-lg{width:calc(var(--spacing) * 64)}.input-split{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.input-split input{border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input-split input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark :is(.input-split input){background-color:var(--color-gray-700);color:var(--color-gray-100)}.input-split .input-split-separator{color:var(--color-gray-400)}.dark :is(.input-split .input-split-separator){color:var(--color-gray-500)}.tag-input-container{align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.tag{align-items:center;gap:calc(var(--spacing) * 1);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);border-radius:3.40282e38px;display:inline-flex}.dark .tag{background-color:var(--color-gray-700);color:var(--color-gray-300)}.tag .tag-remove{cursor:pointer;color:var(--color-gray-400)}@media(hover:hover){.tag .tag-remove:hover{color:var(--color-gray-600)}}.dark :is(.tag .tag-remove){color:var(--color-gray-500)}@media(hover:hover){.dark :is(.tag .tag-remove):hover{color:var(--color-gray-300)}}:where(.form-group>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}.textarea-sm{min-height:5rem}.textarea-md{min-height:8rem}.textarea-lg{min-height:12rem}.input-error{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-500)}.input-error:focus{--tw-ring-color:var(--color-red-500)}.dark .input-error{border-color:var(--color-red-500)}.input-success{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-500)}.input-success:focus{--tw-ring-color:var(--color-green-500)}.dark .input-success{border-color:var(--color-green-500)}.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-500)}:is(.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea):focus{--tw-ring-color:var(--color-red-500)}.dark :is(.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea){border-color:var(--color-red-500)}.form-error{margin-top:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-500);display:block}.dark .form-error{color:var(--color-red-400)}.form-success{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-600)}.dark .form-success{color:var(--color-green-400)}.checkbox{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-gray-900);border-radius:.25rem}.checkbox:focus{--tw-ring-color:var(--color-gray-500)}.dark .checkbox{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.checkbox:checked{border-color:var(--color-gray-900);background-color:var(--color-gray-900)}.dark .checkbox:checked{border-color:var(--color-white);background-color:var(--color-white)}.radio{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-blue-600);flex-shrink:0}.radio:focus{--tw-ring-color:var(--color-blue-500)}.dark .radio{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.dark .radio:focus{--tw-ring-color:var(--color-blue-400)}.radio:checked{border-color:var(--color-blue-600)}.dark .radio:checked{border-color:var(--color-blue-400)}.toggle{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 11);cursor:pointer;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;align-items:center;display:inline-flex;position:relative}.toggle-track{inset:calc(var(--spacing) * 0);background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.dark .toggle-track{background-color:var(--color-gray-700)}.toggle.active .toggle-track{background-color:var(--color-blue-600)}.toggle-handle{left:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.toggle.active .toggle-handle{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.badge{align-items:center;gap:calc(var(--spacing) * 1);border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);display:inline-flex}.badge-green{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-200);background-color:var(--color-green-100);color:var(--color-green-700)}.dark .badge-green{border-color:var(--color-green-800);background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-green{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .badge-green{color:var(--color-green-400)}.badge-blue{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-200);background-color:var(--color-blue-100);color:var(--color-blue-700)}.dark .badge-blue{border-color:var(--color-blue-800);background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-blue{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .badge-blue{color:var(--color-blue-400)}.badge-cyan{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-cyan-200);background-color:var(--color-cyan-100);color:var(--color-cyan-700)}.dark .badge-cyan{border-color:var(--color-cyan-800);background-color:#104e644d}@supports (color:color-mix(in lab,red,red)){.dark .badge-cyan{background-color:color-mix(in oklab,var(--color-cyan-900) 30%,transparent)}}.dark .badge-cyan{color:var(--color-cyan-400)}.badge-indigo{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-indigo-200);background-color:var(--color-indigo-100);color:var(--color-indigo-700)}.dark .badge-indigo{border-color:var(--color-indigo-800);background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .badge-indigo{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .badge-indigo{color:var(--color-indigo-400)}.badge-pink{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-pink-200);background-color:var(--color-pink-100);color:var(--color-pink-700)}.dark .badge-pink{border-color:var(--color-pink-800);background-color:#8610434d}@supports (color:color-mix(in lab,red,red)){.dark .badge-pink{background-color:color-mix(in oklab,var(--color-pink-900) 30%,transparent)}}.dark .badge-pink{color:var(--color-pink-400)}.badge-red{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-100);color:var(--color-red-700)}.dark .badge-red{border-color:var(--color-red-800);background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-red{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .badge-red{color:var(--color-red-400)}.badge-gray{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);color:var(--color-gray-600)}.dark .badge-gray{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.badge-yellow{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-100);color:var(--color-yellow-700)}.dark .badge-yellow{border-color:var(--color-yellow-800);background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-yellow{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .badge-yellow{color:var(--color-yellow-400)}.badge-status{align-items:center;gap:calc(var(--spacing) * 1.5);padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:3.40282e38px;display:inline-flex}.badge-status-success{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .badge-status-success{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-success{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .badge-status-success{color:var(--color-green-400)}.badge-status-processing{background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .badge-status-processing{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-processing{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .badge-status-processing{color:var(--color-blue-400)}.badge-status-error{background-color:var(--color-red-50);color:var(--color-red-600)}.dark .badge-status-error{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-error{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .badge-status-error{color:var(--color-red-400)}.badge-status-expired{background-color:var(--color-gray-50);color:var(--color-gray-500)}.dark .badge-status-expired{background-color:var(--color-gray-800);color:var(--color-gray-400)}.search-bar{gap:calc(var(--spacing) * 3);flex-direction:column;display:flex}@media(min-width:48rem){.search-bar{flex-flow:wrap;align-items:center}}.search-bar-select{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.search-bar-select::placeholder{color:var(--color-gray-400)}.search-bar-select:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .search-bar-select{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .search-bar-select::placeholder{color:var(--color-gray-500)}@media(min-width:48rem){.search-bar-select{width:calc(var(--spacing) * 32);flex-shrink:0}}.search-bar-select-custom{width:100%}@media(min-width:48rem){.search-bar-select-custom{width:calc(var(--spacing) * 32);flex-shrink:0}}.search-bar-input{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.search-bar-input::placeholder{color:var(--color-gray-400)}.search-bar-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .search-bar-input{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .search-bar-input::placeholder{color:var(--color-gray-500)}@media(min-width:48rem){.search-bar-input{min-width:calc(var(--spacing) * 64);flex:1}}.search-bar-reset{cursor:pointer;justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);background-color:#0000;transition-duration:.2s;display:inline-flex}@media(hover:hover){.search-bar-reset:hover{background-color:var(--color-gray-50)}}.dark .search-bar-reset{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .search-bar-reset:hover{background-color:var(--color-gray-700)}}.search-bar-reset{gap:calc(var(--spacing) * 1.5);flex-shrink:0;width:100%}@media(min-width:48rem){.search-bar-reset{width:auto}}.filter-grid{gap:calc(var(--spacing) * 4);flex-direction:column;display:flex}.filter-row{gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}@media(min-width:48rem){.filter-row{flex-direction:row;align-items:center}}.filter-label{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);display:block}.dark .filter-label{color:var(--color-gray-400)}@media(min-width:48rem){.filter-label{margin-bottom:calc(var(--spacing) * 0);width:calc(var(--spacing) * 14);flex-shrink:0;align-items:center;display:flex}}.filter-buttons{align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.dropdown{display:inline-block;position:relative}.dropdown-trigger{border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.dropdown-trigger:hover{background-color:var(--color-gray-50)}}.dropdown-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dropdown-trigger{color:var(--color-gray-900)}.dark .dropdown-trigger{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-white)}@media(hover:hover){.dark .dropdown-trigger:hover{background-color:var(--color-gray-700)}}.dark .dropdown-trigger:focus{--tw-ring-color:var(--color-blue-400)}.dropdown-trigger-text{cursor:pointer;--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900);align-items:center;display:inline-flex}@media(hover:hover){.dropdown-trigger-text:hover{color:var(--color-blue-600)}}.dark .dropdown-trigger-text{color:var(--color-white)}@media(hover:hover){.dark .dropdown-trigger-text:hover{color:var(--color-blue-400)}}.dropdown-menu{z-index:50;width:calc(var(--spacing) * 56);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .dropdown-menu{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.dropdown-menu-bottom-left{top:100%;left:calc(var(--spacing) * 0);margin-top:calc(var(--spacing) * 2)}.dropdown-menu-bottom-right{top:100%;right:calc(var(--spacing) * 0);margin-top:calc(var(--spacing) * 2)}.dropdown-menu-top-left{bottom:100%;left:calc(var(--spacing) * 0);margin-bottom:calc(var(--spacing) * 2)}.dropdown-menu-top-right{right:calc(var(--spacing) * 0);margin-bottom:calc(var(--spacing) * 2);bottom:100%}.dropdown-item{cursor:pointer;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}@media(hover:hover){.dropdown-item:hover{background-color:var(--color-gray-100)}}.dark .dropdown-item{color:var(--color-gray-300)}@media(hover:hover){.dark .dropdown-item:hover{background-color:var(--color-gray-700)}}.dropdown-item-icon{color:var(--color-gray-400)}.dark .dropdown-item-icon{color:var(--color-gray-500)}.dropdown-item-disabled{cursor:not-allowed;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-400)}.dark .dropdown-item-disabled{color:var(--color-gray-500)}.dropdown-item-focused{background-color:var(--color-gray-100)}.dark .dropdown-item-focused{background-color:var(--color-gray-700)}.dropdown-divider{margin-block:calc(var(--spacing) * 1);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100)}.dark .dropdown-divider{border-color:var(--color-gray-700)}.pagination{justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.pagination-btn{border-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);color:var(--color-gray-700);border-radius:.25rem}@media(hover:hover){.pagination-btn:hover{background-color:var(--color-gray-100)}}.pagination-btn:disabled{cursor:not-allowed;opacity:.5}.dark .pagination-btn{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-300)}@media(hover:hover){.dark .pagination-btn:hover{background-color:var(--color-gray-700)}}.pagination-btn-active{border-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));border-width:1px;border-color:var(--color-blue-500);background-color:var(--color-blue-500);color:var(--color-white);border-radius:.25rem}.dark .pagination-btn-active{border-color:var(--color-blue-600);background-color:var(--color-blue-600)}.pagination-ellipsis{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .pagination-ellipsis{color:var(--color-gray-400)}.dialog-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.dialog-title{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .dialog-title{color:var(--color-white)}.dialog-close{color:var(--color-gray-400)}@media(hover:hover){.dialog-close:hover{color:var(--color-gray-600)}}.dialog-close:focus{--tw-outline-style:none;outline-style:none}.dark .dialog-close{color:var(--color-gray-500)}@media(hover:hover){.dark .dialog-close:hover{color:var(--color-gray-300)}}.dialog-body{margin-bottom:calc(var(--spacing) * 6);color:var(--color-gray-700)}.dark .dialog-body{color:var(--color-gray-300)}.dialog-footer{justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.dialog-btn-primary{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-primary:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-primary:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-primary{background-color:var(--color-blue-600);color:var(--color-white)}@media(hover:hover){.dialog-btn-primary:hover{background-color:var(--color-blue-700)}}.dialog-btn-primary:focus{--tw-ring-color:var(--color-blue-500)}.dialog-btn-secondary{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-secondary:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-secondary:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-secondary{background-color:var(--color-gray-200);color:var(--color-gray-800)}@media(hover:hover){.dialog-btn-secondary:hover{background-color:var(--color-gray-300)}}.dialog-btn-secondary:focus{--tw-ring-color:var(--color-gray-500)}.dark .dialog-btn-secondary{background-color:var(--color-gray-700);color:var(--color-gray-200)}@media(hover:hover){.dark .dialog-btn-secondary:hover{background-color:var(--color-gray-600)}}.dialog-btn-danger{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-danger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-danger:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-danger{background-color:var(--color-red-600);color:var(--color-white)}@media(hover:hover){.dialog-btn-danger:hover{background-color:var(--color-red-700)}}.dialog-btn-danger:focus{--tw-ring-color:var(--color-red-500)}.dialog-btn-ghost{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-ghost:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-ghost:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-ghost{color:var(--color-gray-700);background-color:#0000}@media(hover:hover){.dialog-btn-ghost:hover{background-color:var(--color-gray-100)}}.dialog-btn-ghost:focus{--tw-ring-color:var(--color-gray-500)}.dark .dialog-btn-ghost{color:var(--color-gray-300)}@media(hover:hover){.dark .dialog-btn-ghost:hover{background-color:var(--color-gray-700)}}.alert-error{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-900);display:flex}.dark .alert-error{border-color:var(--color-red-800);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-error{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .alert-error{color:var(--color-red-100)}.alert-icon-info{margin-top:calc(var(--spacing) * .5);color:var(--color-blue-600);flex-shrink:0}.dark .alert-icon-info{color:var(--color-blue-400)}.alert-icon-success{margin-top:calc(var(--spacing) * .5);color:var(--color-green-600);flex-shrink:0}.dark .alert-icon-success{color:var(--color-green-400)}.alert-icon-warning{margin-top:calc(var(--spacing) * .5);color:var(--color-yellow-600);flex-shrink:0}.dark .alert-icon-warning{color:var(--color-yellow-400)}.alert-icon-error{margin-top:calc(var(--spacing) * .5);color:var(--color-red-600);flex-shrink:0}.dark .alert-icon-error{color:var(--color-red-400)}.alert-text-info{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-800);flex:1}.dark .alert-text-info{color:var(--color-blue-200)}.alert-text-success{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-800);flex:1}.dark .alert-text-success{color:var(--color-green-200)}.alert-text-warning{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-800);flex:1}.dark .alert-text-warning{color:var(--color-yellow-200)}.alert-text-error{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-800);flex:1}.dark .alert-text-error{color:var(--color-red-200)}.alert-dismiss{cursor:pointer;border-style:var(--tw-border-style);padding:calc(var(--spacing) * 0);background-color:#0000;border-width:0;flex-shrink:0}@media(hover:hover){.alert-dismiss:hover{opacity:.7}}.table-container{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);overflow:hidden}.dark .table-container{border-color:var(--color-gray-700)}.table{width:100%}.table thead{background-color:var(--color-gray-50)}.dark :is(.table thead){background-color:var(--color-gray-800)}.table th{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600)}.dark :is(.table th){border-color:var(--color-gray-700);color:var(--color-gray-400)}.table th.sortable{cursor:pointer}@media(hover:hover){.table th.sortable:hover{background-color:var(--color-gray-100)}.dark :is(.table th.sortable):hover{background-color:var(--color-gray-700)}}.table tbody tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);background-color:var(--color-white)}.dark :is(.table tbody tr){border-color:var(--color-gray-800);background-color:var(--color-gray-900)}.table tbody tr:hover{background-color:var(--color-gray-50)}.dark :is(.table tbody tr:hover){background-color:var(--color-gray-800)}.table tbody tr.selected{background-color:var(--color-blue-50)}.dark :is(.table tbody tr.selected){background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark :is(.table tbody tr.selected){background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.table td{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark :is(.table td){color:var(--color-gray-100)}.table-actions{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);justify-content:space-between;align-items:center;display:flex}.dark .table-actions{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.log-table{width:100%}.log-table th{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500)}.dark :is(.log-table th){border-color:var(--color-gray-700);color:var(--color-gray-400)}.log-table td{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark :is(.log-table td){border-color:var(--color-gray-800);color:var(--color-gray-100)}.log-table tbody tr:last-child td{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.log-table-actor{border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);align-items:center;display:inline-flex}.dark .log-table-actor{border-color:var(--color-gray-600);background-color:var(--color-gray-700);color:var(--color-gray-300)}.log-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .log-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.log-card-header{margin-bottom:calc(var(--spacing) * 2);justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);display:flex}.log-card-body{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .log-card-body{color:var(--color-gray-100)}.modal-overlay{inset:calc(var(--spacing) * 0);z-index:50;background-color:#00000080;justify-content:center;align-items:center;display:flex;position:fixed}@supports (color:color-mix(in lab,red,red)){.modal-overlay{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.modal-wrapper{inset:calc(var(--spacing) * 0);justify-content:center;align-items:center;display:flex;position:fixed}.modal-backdrop{inset:calc(var(--spacing) * 0);background-color:#00000080;position:absolute}@supports (color:color-mix(in lab,red,red)){.modal-backdrop{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.modal-container{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);max-width:calc(100vw - 2rem);max-height:90vh;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:relative;overflow:hidden}.dark .modal-container{background-color:var(--color-gray-800)}.modal-header-row{padding:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.modal-title{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .modal-title{color:var(--color-white)}.modal-close-btn{color:var(--color-gray-400);margin-left:auto}@media(hover:hover){.modal-close-btn:hover{color:var(--color-gray-600)}}.modal-close-btn:focus{--tw-outline-style:none;outline-style:none}.dark .modal-close-btn{color:var(--color-gray-500)}@media(hover:hover){.dark .modal-close-btn:hover{color:var(--color-gray-300)}}.modal-body-content{max-height:calc(90vh - 140px);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4);overflow-y:auto}.modal{margin-inline:calc(var(--spacing) * 4);width:100%;max-width:var(--container-md);border-radius:var(--radius-xl);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .modal{background-color:var(--color-gray-800)}.modal-lg{max-width:var(--container-lg)}.modal-xl{max-width:var(--container-xl)}.modal-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:flex-start;display:flex}.modal-close{cursor:pointer;border-radius:var(--radius-lg);padding:calc(var(--spacing) * 1);color:var(--color-gray-400)}@media(hover:hover){.modal-close:hover{background-color:var(--color-gray-100);color:var(--color-gray-600)}}.dark .modal-close{color:var(--color-gray-500)}@media(hover:hover){.dark .modal-close:hover{background-color:var(--color-gray-700);color:var(--color-gray-300)}}.modal-body{padding-block:calc(var(--spacing) * 4)}.modal-footer{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.danger-confirm-icon{margin-inline:auto;margin-bottom:calc(var(--spacing) * 4);height:calc(var(--spacing) * 12);width:calc(var(--spacing) * 12);background-color:var(--color-red-100);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex}.dark .danger-confirm-icon{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .danger-confirm-icon{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.danger-confirm-icon svg,.danger-confirm-icon .icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-red-600)}.dark :is(.danger-confirm-icon svg,.danger-confirm-icon .icon){color:var(--color-red-400)}.danger-confirm-title{margin-bottom:calc(var(--spacing) * 2);text-align:center;font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .danger-confirm-title{color:var(--color-white)}.danger-confirm-message{margin-bottom:calc(var(--spacing) * 4);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .danger-confirm-message{color:var(--color-gray-400)}.danger-confirm-warning{margin-bottom:calc(var(--spacing) * 4);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-50);padding:calc(var(--spacing) * 4)}.dark .danger-confirm-warning{border-color:var(--color-yellow-700);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .danger-confirm-warning{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.danger-confirm-warning-title{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-yellow-800)}.dark .danger-confirm-warning-title{color:var(--color-yellow-300)}.danger-confirm-warning-list{list-style-type:disc;list-style-position:inside}:where(.danger-confirm-warning-list>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.danger-confirm-warning-list{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-700)}.dark .danger-confirm-warning-list{color:var(--color-yellow-400)}.danger-confirm-alert{text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-600)}.dark .danger-confirm-alert{color:var(--color-red-400)}.danger-confirm-footer{margin-top:calc(var(--spacing) * 6);justify-content:center;gap:calc(var(--spacing) * 3);display:flex}.card{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);width:100%;padding:calc(var(--spacing) * 6)}.dark .card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card{margin-bottom:calc(var(--spacing) * 6);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .admin-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card{flex-direction:column;display:flex}.admin-card-clickable{margin-bottom:calc(var(--spacing) * 2);cursor:pointer;border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2.5)}@media(hover:hover){.admin-card-clickable:hover{border-color:var(--color-gray-300)}}.dark .admin-card-clickable{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}@media(hover:hover){.dark .admin-card-clickable:hover{border-color:var(--color-gray-600)}}.admin-card-selected{margin-bottom:calc(var(--spacing) * 2);cursor:pointer;border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-500);background-color:var(--color-blue-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2.5)}.dark .admin-card-selected{border-color:var(--color-blue-500);background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .admin-card-selected{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.card-clickable{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));overflow:hidden}@media(hover:hover){.card-clickable:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .card-clickable{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.card-image{height:calc(var(--spacing) * 48);object-fit:cover;width:100%}.card-content{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));white-space:pre-line;color:var(--color-gray-600)}.dark .card-content{color:var(--color-gray-300)}.card-section-title{margin-inline:calc(var(--spacing) * -6);margin-bottom:calc(var(--spacing) * 6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 6);padding-bottom:calc(var(--spacing) * 4);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .card-section-title{border-color:var(--color-gray-700);color:var(--color-white)}.card-account{border-radius:var(--radius-lg);background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4);position:relative}.dark .card-account{background-color:var(--color-gray-900)}.card-account-default{top:calc(var(--spacing) * 3);left:calc(var(--spacing) * 3);position:absolute}.card-account-delete{top:calc(var(--spacing) * 3);right:calc(var(--spacing) * 3);position:absolute}.admin-card-accordion{margin-bottom:calc(var(--spacing) * 6);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .admin-card-accordion{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card-accordion{flex-direction:column;display:flex}.admin-card-accordion>.card-header{cursor:pointer}.admin-card-accordion-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;margin-left:auto;transition-duration:.2s}.dark .admin-card-accordion-icon{color:var(--color-gray-500)}.admin-card-accordion>.row-stack{margin-top:calc(var(--spacing) * 6)}.admin-card-accordion-icon.open{rotate:180deg}.tabs{border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding:calc(var(--spacing) * 1);display:inline-flex}.dark .tabs{background-color:var(--color-gray-800)}.tab{cursor:pointer;border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.tab:hover{color:var(--color-gray-700)}}.dark .tab{color:var(--color-gray-400)}@media(hover:hover){.dark .tab:hover{color:var(--color-gray-300)}}.tab.active{background-color:var(--color-white);color:var(--color-gray-900);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .tab.active{background-color:var(--color-gray-700);color:var(--color-white)}.toast{right:calc(var(--spacing) * 4);bottom:calc(var(--spacing) * 4);z-index:50;position:fixed}.toast-item{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);background-color:var(--color-gray-900);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);display:flex}.dark .toast-item{background-color:var(--color-white);color:var(--color-gray-900)}.toast-icon{flex-shrink:0}.drag-item{align-items:center;gap:calc(var(--spacing) * 4);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4);display:flex}@media(hover:hover){.drag-item:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .drag-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.drag-handle{cursor:grab;color:var(--color-gray-400)}.dark .drag-handle{color:var(--color-gray-500)}.drag-handle:active{cursor:grabbing}.table-mobile-cards{display:none}.excel-card{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .excel-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.excel-card-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:flex-start;gap:calc(var(--spacing) * 3);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-bottom:calc(var(--spacing) * 3);display:flex}.dark .excel-card-header{border-color:var(--color-gray-700)}.excel-card-title{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .excel-card-title{color:var(--color-white)}:where(.excel-card-body>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.excel-card-row{padding-block:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));justify-content:space-between;align-items:center;display:flex}.excel-card-label{color:var(--color-gray-500);flex-shrink:0}.dark .excel-card-label{color:var(--color-gray-400)}.excel-card-value{text-align:right;color:var(--color-gray-900)}.dark .excel-card-value{color:var(--color-white)}.excel-card-footer{margin-top:calc(var(--spacing) * 4);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100);padding-top:calc(var(--spacing) * 3);justify-content:space-between;align-items:center;display:flex}.dark .excel-card-footer{border-color:var(--color-gray-700)}.excel-card-creator{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}.dark .excel-card-creator{color:var(--color-gray-300)}.excel-card-creator-label{margin-right:calc(var(--spacing) * 2);color:var(--color-gray-500)}.dark .excel-card-creator-label{color:var(--color-gray-400)}.select-custom{display:inline-block;position:relative}.select-trigger{height:calc(var(--spacing) * 9);border-radius:var(--radius-xl);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);cursor:pointer;justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);border-style:var(--tw-border-style);border-width:0;display:flex}.select-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select-trigger{background-color:var(--color-gray-700);color:var(--color-gray-200)}.select-trigger:disabled{cursor:not-allowed;opacity:.5}.select-trigger span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.select-trigger-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;flex-shrink:0;transition-duration:.2s}.dark .select-trigger-icon{color:var(--color-gray-500)}.select-trigger-icon.open{rotate:180deg}.select-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);width:100%;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute;overflow:hidden}.dark .select-dropdown{border-color:var(--color-gray-600);background-color:var(--color-gray-800)}.select-dropdown.hidden{display:none}.select-dropdown-inner{max-height:calc(var(--spacing) * 60);padding-block:calc(var(--spacing) * 2);overflow:auto}.select-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));align-items:center;display:flex}@media(hover:hover){.select-option:hover{background-color:var(--color-gray-100)}}.dark .select-option{color:var(--color-gray-200)}@media(hover:hover){.dark .select-option:hover{background-color:var(--color-gray-700)}}.select-option.selected{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-blue-600)}.dark .select-option.selected{color:var(--color-blue-400)}.select-option:disabled{cursor:not-allowed;opacity:.5}.select-option-check{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-blue-600);flex-shrink:0;margin-left:auto}.dark .select-option-check{color:var(--color-blue-400)}.multi-select{display:inline-block;position:relative}.multi-select-trigger{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);cursor:pointer;background-color:#0000;display:inline-flex}@media(hover:hover){.multi-select-trigger:hover{background-color:var(--color-gray-50)}}.dark .multi-select-trigger{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .multi-select-trigger:hover{background-color:var(--color-gray-700)}}.multi-select-count{color:var(--color-gray-500)}.dark .multi-select-count{color:var(--color-gray-400)}.multi-select-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);min-width:calc(var(--spacing) * 64);border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .multi-select-dropdown{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.multi-select-dropdown.hidden{display:none}.multi-select-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .multi-select-header{border-color:var(--color-gray-700);color:var(--color-white)}.multi-select-hint{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal);color:var(--color-gray-500)}.dark .multi-select-hint{color:var(--color-gray-400)}.multi-select-items{max-height:calc(var(--spacing) * 80);padding-block:calc(var(--spacing) * 2);overflow:auto}.multi-select-item{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);display:flex}@media(hover:hover){.multi-select-item:hover{background-color:var(--color-gray-50)}}.dark .multi-select-item{color:var(--color-gray-300)}@media(hover:hover){.dark .multi-select-item:hover{background-color:var(--color-gray-700)}}.multi-select-item.disabled{cursor:not-allowed;opacity:.5}.multi-select-item.disabled:hover,.dark .multi-select-item.disabled:hover{background-color:#0000}.select-ghost{display:inline-block;position:relative}.select-ghost-trigger{align-items:center;gap:calc(var(--spacing) * 1);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);cursor:pointer;border-radius:var(--radius-lg);color:var(--color-gray-700);background-color:#0000;display:inline-flex}@media(hover:hover){.select-ghost-trigger:hover{background-color:var(--color-gray-100)}}.dark .select-ghost-trigger{color:var(--color-gray-300)}@media(hover:hover){.dark .select-ghost-trigger:hover{background-color:var(--color-gray-700)}}.select-ghost-dropdown{z-index:50;margin-top:calc(var(--spacing) * 1);min-width:calc(var(--spacing) * 32);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-block:calc(var(--spacing) * 1);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .select-ghost-dropdown{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.select-ghost-dropdown.hidden{display:none}.select-ghost-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);justify-content:space-between;align-items:center;display:flex}@media(hover:hover){.select-ghost-option:hover{background-color:var(--color-gray-50)}}.dark .select-ghost-option{color:var(--color-gray-300)}@media(hover:hover){.dark .select-ghost-option:hover{background-color:var(--color-gray-700)}}.select-ghost-option.selected{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-blue-600)}.dark .select-ghost-option.selected{color:var(--color-blue-400)}.select-ghost-check{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-blue-600)}.dark .select-ghost-check{color:var(--color-blue-400)}.searchable-dropdown{display:inline-block;position:relative}.searchable-dropdown-trigger{border-radius:var(--radius-xl);background-color:var(--color-gray-100);min-width:200px;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);cursor:pointer;justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);border-style:var(--tw-border-style);border-width:0;display:flex}.searchable-dropdown-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .searchable-dropdown-trigger{background-color:var(--color-gray-700);color:var(--color-gray-200)}.searchable-dropdown-trigger:disabled{cursor:not-allowed;opacity:.5}.searchable-dropdown-trigger-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;flex-shrink:0;transition-duration:.2s}.dark .searchable-dropdown-trigger-icon{color:var(--color-gray-500)}.searchable-dropdown-trigger-icon.open{rotate:180deg}.searchable-dropdown-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);width:100%;min-width:280px;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute;overflow:hidden}.dark .searchable-dropdown-dropdown{border-color:var(--color-gray-600);background-color:var(--color-gray-800)}.searchable-dropdown-dropdown.hidden{display:none}.searchable-dropdown-search{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding:calc(var(--spacing) * 3)}.dark .searchable-dropdown-search{border-color:var(--color-gray-700)}.searchable-dropdown-search-input{border-radius:var(--radius-lg);background-color:var(--color-gray-50);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.searchable-dropdown-search-input::placeholder{color:var(--color-gray-400)}.searchable-dropdown-search-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;border-color:#0000;outline-style:none}.dark .searchable-dropdown-search-input{border-color:var(--color-gray-600);background-color:var(--color-gray-700);color:var(--color-white)}.dark .searchable-dropdown-search-input::placeholder{color:var(--color-gray-500)}.searchable-dropdown-items{max-height:calc(var(--spacing) * 80);padding-block:calc(var(--spacing) * 2);overflow:auto}.searchable-dropdown-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;align-items:flex-start;gap:calc(var(--spacing) * 3);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media(hover:hover){.searchable-dropdown-option:hover{background-color:var(--color-gray-100)}.dark .searchable-dropdown-option:hover{background-color:var(--color-gray-700)}}.searchable-dropdown-option:disabled{cursor:not-allowed;opacity:.5}.searchable-dropdown-check-area{margin-top:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);flex-shrink:0}.searchable-dropdown-check{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-blue-600)}.dark .searchable-dropdown-check{color:var(--color-blue-400)}.searchable-dropdown-option-content{min-width:calc(var(--spacing) * 0);flex:1}.searchable-dropdown-option-label{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .searchable-dropdown-option-label{color:var(--color-blue-400)}.searchable-dropdown-option.selected .searchable-dropdown-option-label{color:var(--color-blue-600)}.dark :is(.searchable-dropdown-option.selected .searchable-dropdown-option-label){color:var(--color-blue-400)}.searchable-dropdown-option-description{margin-top:calc(var(--spacing) * .5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .searchable-dropdown-option-description{color:var(--color-gray-400)}.searchable-dropdown-message{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 6);text-align:center;color:var(--color-gray-500)}.dark .searchable-dropdown-message{color:var(--color-gray-400)}.stat-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.stat-card:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .stat-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.stats-icon{border-radius:var(--radius-lg);padding:calc(var(--spacing) * 3)}.stats-icon-blue{background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .stats-icon-blue{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-blue{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .stats-icon-blue{color:var(--color-blue-400)}.stats-icon-green{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .stats-icon-green{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-green{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .stats-icon-green{color:var(--color-green-400)}.stats-icon-purple{background-color:var(--color-purple-50);color:var(--color-purple-600)}.dark .stats-icon-purple{background-color:#59168b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-purple{background-color:color-mix(in oklab,var(--color-purple-900) 20%,transparent)}}.dark .stats-icon-purple{color:var(--color-purple-400)}.stats-icon-orange{background-color:var(--color-orange-50);color:var(--color-orange-600)}.dark .stats-icon-orange{background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-orange{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .stats-icon-orange{color:var(--color-orange-400)}.stats-change{align-items:center;gap:calc(var(--spacing) * 1);border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * 1);display:flex}.stats-trend-up{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .stats-trend-up{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-trend-up{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .stats-trend-up{color:var(--color-green-400)}.stats-trend-down{background-color:var(--color-red-50);color:var(--color-red-600)}.dark .stats-trend-down{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .stats-trend-down{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .stats-trend-down{color:var(--color-red-400)}.stats-trend-neutral{background-color:var(--color-gray-50);color:var(--color-gray-600)}.dark .stats-trend-neutral{background-color:var(--color-gray-700);color:var(--color-gray-400)}.stats-value-lg{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);color:var(--color-gray-900)}.dark .stats-value-lg{color:var(--color-white)}.stats-label{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .stats-label{color:var(--color-gray-400)}.stats-sublabel{align-items:center;gap:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500);display:flex}.dark .stats-sublabel{color:var(--color-gray-400)}:where(.tree>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.tree-item{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);display:flex}@media(hover:hover){.tree-item:hover{background-color:var(--color-gray-50)}.dark .tree-item:hover{background-color:var(--color-gray-800)}}.tree-item.selected{background-color:var(--color-blue-50)}.dark .tree-item.selected{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tree-item.selected{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.tree-item-content{min-width:calc(var(--spacing) * 0);align-items:center;gap:calc(var(--spacing) * 2);flex:1;display:flex}.tree-item-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);flex-shrink:0}.dark .tree-item-icon{color:var(--color-gray-500)}.tree-item-label{text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);overflow:hidden}.dark .tree-item-label{color:var(--color-gray-300)}.tree-expand-btn{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-gray-400);border-radius:.25rem;justify-content:center;align-items:center;display:flex}@media(hover:hover){.tree-expand-btn:hover{background-color:var(--color-gray-100)}}.dark .tree-expand-btn{color:var(--color-gray-500)}@media(hover:hover){.dark .tree-expand-btn:hover{background-color:var(--color-gray-700)}}.tree-expand-btn.hidden{visibility:hidden}.tree-children{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-children{border-color:var(--color-gray-700)}.tree-level-1{margin-left:calc(var(--spacing) * 0);border-left-style:var(--tw-border-style);padding-left:calc(var(--spacing) * 0);border-left-width:0}.tree-level-2{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-2{border-color:var(--color-gray-700)}.tree-level-3{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-3{border-color:var(--color-gray-700)}.tree-level-4{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-4{border-color:var(--color-gray-700)}.category-item{cursor:pointer;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);justify-content:space-between;align-items:center;display:flex}.dark .category-item{color:var(--color-white)}@media(hover:hover){.category-item:hover{background-color:var(--color-gray-50)}.dark .category-item:hover{background-color:var(--color-gray-700)}}.category-item[data-selected=true]{background-color:var(--color-blue-600);color:var(--color-white)}.category-item[data-selected=true]:hover{background-color:var(--color-blue-700)}.category-item-mobile{cursor:pointer;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .category-item-mobile{color:var(--color-white)}.category-item-mobile[data-selected=true]{background-color:var(--color-blue-50)}.dark .category-item-mobile[data-selected=true]{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .category-item-mobile[data-selected=true]{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.stats-card{height:calc(var(--spacing) * 36);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-100);background-color:var(--color-white);padding:calc(var(--spacing) * 5);flex-direction:column;justify-content:space-between;display:flex}.dark .stats-card{border-color:var(--color-slate-700);background-color:var(--color-slate-800)}.dashboard-panel{border-radius:var(--radius-3xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-100);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dashboard-panel{border-color:var(--color-slate-700);background-color:var(--color-slate-800)}.stats-value{margin-bottom:calc(var(--spacing) * 2);--tw-leading:1;--tw-font-weight:var(--font-weight-bold);font-size:2rem;line-height:1;font-weight:var(--font-weight-bold);color:var(--color-slate-900)}.dark .stats-value{color:var(--color-white)}.stats-trend{align-items:center;gap:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-emerald-500);display:flex}.status-active{padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-green-100);color:var(--color-green-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-active{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .status-active{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .status-active{color:var(--color-green-400)}.status-inactive{padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-gray-100);color:var(--color-gray-600);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-inactive{background-color:var(--color-gray-700);color:var(--color-gray-400)}.status-pending{padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-yellow-100);color:var(--color-yellow-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-pending{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .status-pending{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .status-pending{color:var(--color-yellow-400)}.status-error{padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-red-100);color:var(--color-red-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-error{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .status-error{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .status-error{color:var(--color-red-400)}.list-item{align-items:center;gap:calc(var(--spacing) * 3);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-block:calc(var(--spacing) * 3);display:flex}.dark .list-item{border-color:var(--color-gray-700)}.list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.list-item-hover{margin-inline:calc(var(--spacing) * -3);align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);display:flex}@media(hover:hover){.list-item-hover:hover{background-color:var(--color-gray-50)}.dark .list-item-hover:hover{background-color:var(--color-gray-800)}}.empty-state{padding-block:calc(var(--spacing) * 12);text-align:center;flex-direction:column;justify-content:center;align-items:center;display:flex}.empty-state-icon{margin-bottom:calc(var(--spacing) * 4);height:calc(var(--spacing) * 16);width:calc(var(--spacing) * 16);color:var(--color-gray-300)}.dark .empty-state-icon{color:var(--color-gray-600)}.empty-state-title{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .empty-state-title{color:var(--color-white)}.empty-state-description{margin-bottom:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .empty-state-description{color:var(--color-gray-400)}.page-header{margin-bottom:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.page-header-padded{margin-bottom:calc(var(--spacing) * 6);padding-inline:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.page-header-fullbleed{margin-inline:calc(var(--spacing) * -6);margin-bottom:calc(var(--spacing) * 6);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.dark .page-header-fullbleed{background-color:var(--color-gray-900)}.page-actions{align-items:center;gap:calc(var(--spacing) * 3);display:flex}.section-card{margin-top:calc(var(--spacing) * 6);scroll-margin-top:calc(var(--spacing) * 32);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6)}.dark .section-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.section-header{margin-bottom:calc(var(--spacing) * 6);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-header{color:var(--color-white)}.info-row{margin-bottom:calc(var(--spacing) * 4);align-items:center;gap:calc(var(--spacing) * 2);display:flex}.action-buttons{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.form-label{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900);display:block}.dark .form-label{color:var(--color-white)}.form-label-muted{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);display:block}.dark .form-label-muted{color:var(--color-gray-400)}.form-hint{margin-top:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .form-hint{color:var(--color-gray-400)}.text-value{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .text-value{color:var(--color-white)}.text-link{cursor:pointer;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-600)}@media(hover:hover){.text-link:hover{color:var(--color-blue-700)}}.dark .text-link{color:var(--color-blue-400)}@media(hover:hover){.dark .text-link:hover{color:var(--color-blue-300)}}.icon-xs{height:calc(var(--spacing) * 3);width:calc(var(--spacing) * 3)}.icon-sm{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4)}.icon-md{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.icon-lg{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6)}.icon-xl{height:calc(var(--spacing) * 8);width:calc(var(--spacing) * 8)}.icon-2xl{height:calc(var(--spacing) * 10);width:calc(var(--spacing) * 10)}.icon-action{cursor:pointer;color:var(--color-gray-400)}@media(hover:hover){.icon-action:hover{color:var(--color-gray-600)}}.dark .icon-action{color:var(--color-gray-500)}@media(hover:hover){.dark .icon-action:hover{color:var(--color-gray-300)}}.page-container{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 6)}.dark .page-container{background-color:var(--color-gray-900)}.content-area{max-width:var(--container-7xl);margin-inline:auto}.content-panel{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6)}.dark .content-panel{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.flex-row-center{align-items:center;display:flex}.flex-row-between{justify-content:space-between;align-items:center;display:flex}.flex-row-end{justify-content:flex-end;align-items:center;display:flex}.flex-col-start{flex-direction:column;align-items:flex-start;display:flex}.flex-between{justify-content:space-between;align-items:center;display:flex}.flex-center{align-items:center;display:flex}.flex-end{justify-content:flex-end;align-items:center;display:flex}.flex-wrap-center{flex-wrap:wrap;align-items:center;display:flex}.inline-flex-center{align-items:center;display:inline-flex}.row-stack{flex-direction:column;display:flex}:where(.row-stack>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-color:var(--color-gray-200)}:where(.dark .row-stack>:not(:last-child)){border-color:var(--color-gray-700)}.row-stack>*{padding-block:calc(var(--spacing) * 2)}.row-stack>:first-child{padding-top:calc(var(--spacing) * 0)}.row-stack>:last-child{padding-bottom:calc(var(--spacing) * 0)}.stack-flush{gap:calc(var(--spacing) * 0);flex-direction:column;display:flex}.stack-tight{gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}.card-stack{margin-top:calc(var(--spacing) * 3);gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}.card-stack-item{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .card-stack-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.card-stack-item{flex-direction:column;display:flex}.option-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);overflow:hidden}.dark .option-card{border-color:var(--color-gray-700);background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .option-card{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.option-card>*{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3)}.option-card>*+*{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200)}.dark :is(.option-card>*+*){border-color:var(--color-gray-700)}.alert-info{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-200);background-color:var(--color-blue-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-900);display:flex}.dark .alert-info{border-color:var(--color-blue-800);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .alert-info{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .alert-info{color:var(--color-blue-100)}.alert-warning{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-900);display:flex}.dark .alert-warning{border-color:var(--color-yellow-800);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-warning{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .alert-warning{color:var(--color-yellow-100)}.alert-danger{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-900);display:flex}.dark .alert-danger{border-color:var(--color-red-800);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-danger{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .alert-danger{color:var(--color-red-100)}.alert-success{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-200);background-color:var(--color-green-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-900);display:flex}.dark .alert-success{border-color:var(--color-green-800);background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .alert-success{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .alert-success{color:var(--color-green-100)}.alert-info:has(+.row-stack),.alert-warning:has(+.row-stack),.alert-danger:has(+.row-stack),.alert-success:has(+.row-stack),.alert-error:has(+.row-stack),.alert-info:has(+.toggle-group),.alert-warning:has(+.toggle-group),.alert-danger:has(+.toggle-group),.alert-success:has(+.toggle-group),.alert-error:has(+.toggle-group),.alert-info:has(+.grid-2col-responsive),.alert-warning:has(+.grid-2col-responsive),.alert-danger:has(+.grid-2col-responsive),.alert-success:has(+.grid-2col-responsive),.alert-error:has(+.grid-2col-responsive){margin-bottom:calc(var(--spacing) * 4)}.row-stack>.alert-info,.row-stack>.alert-warning,.row-stack>.alert-danger,.row-stack>.alert-success,.row-stack>.alert-error{padding-top:calc(var(--spacing) * 3);padding-bottom:calc(var(--spacing) * 3)}.row-stack>*+.alert-info,.row-stack>*+.alert-warning,.row-stack>*+.alert-danger,.row-stack>*+.alert-success,.row-stack>*+.alert-error{margin-top:calc(var(--spacing) * 3)}.sub-tab-nav{gap:calc(var(--spacing) * 1);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 6);display:flex}@media(min-width:40rem){.sub-tab-nav{padding-inline:calc(var(--spacing) * 8)}}@media(min-width:64rem){.sub-tab-nav{padding-inline:calc(var(--spacing) * 10)}}.dark .sub-tab-nav{background-color:var(--color-gray-800)}.section-header{margin-bottom:calc(var(--spacing) * 6)}.section-header>h3{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark :is(.section-header>h3){color:var(--color-white)}.section-header>p{margin-top:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark :is(.section-header>p){color:var(--color-gray-400)}.toggle-group{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);overflow:hidden}.dark .toggle-group{border-color:var(--color-gray-700)}.section-header-row{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);background-color:var(--color-gray-50);display:flex}.dark .section-header-row{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .section-header-row{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.section-header-row{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200)}.dark .section-header-row{border-color:var(--color-gray-700)}.section-header-row{cursor:pointer;-webkit-user-select:none;user-select:none}.list-row-hover{background-color:var(--color-white)}.dark .list-row-hover{background-color:var(--color-gray-800)}@media(hover:hover){.list-row-hover:hover{background-color:var(--color-gray-50)}.dark .list-row-hover:hover{background-color:var(--color-gray-700)}}.list-row-hover{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.text-hint{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-400)}.dark .text-hint{color:var(--color-gray-500)}.text-info-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-blue-700)}.dark .text-info-soft{color:var(--color-blue-300)}.text-warning-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-yellow-700)}.dark .text-warning-soft{color:var(--color-yellow-300)}.text-danger-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-700)}.dark .text-danger-soft{color:var(--color-red-300)}.text-success-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-green-700)}.dark .text-success-soft{color:var(--color-green-300)}.checkbox-aligned{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-gray-900);border-radius:.25rem}.checkbox-aligned:focus{--tw-ring-color:var(--color-gray-500)}.dark .checkbox-aligned{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.checkbox-aligned{margin-top:calc(var(--spacing) * .5);flex-shrink:0}:where(.divider-list>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-color:var(--color-gray-200)}:where(.dark .divider-list>:not(:last-child)){border-color:var(--color-gray-700)}.modal-footer-buttons{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.spinner-sm{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);animation:var(--animate-spin)}.text-required{margin-left:calc(var(--spacing) * 1);color:var(--color-red-500)}.dark .text-required{color:var(--color-red-400)}.form-label-block{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);display:block}.dark .form-label-block{color:var(--color-gray-300)}.form-sub-label{margin-bottom:calc(var(--spacing) * 1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500);display:block}.dark .form-sub-label{color:var(--color-gray-400)}.text-field-hint{margin-top:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal);color:var(--color-gray-500)}.dark .text-field-hint{color:var(--color-gray-400)}.section-heading-md{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-heading-md{color:var(--color-white)}.grid-3col-responsive{align-items:flex-start;gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}@media(min-width:64rem){.grid-3col-responsive{grid-template-columns:repeat(3,minmax(0,1fr))}}.text-error-soft{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-700)}.dark .text-error-soft{color:var(--color-red-300)}.text-error-strong{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-red-800)}.dark .text-error-strong{color:var(--color-red-200)}.row-content-indent{padding-left:calc(var(--spacing) * 6)}.flex-start{align-items:flex-start;display:flex}.inline-clickable{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2);display:inline-flex}.grid-2col{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.empty-state-cell{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 4);text-align:center}.form-error-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-500)}.dark .form-error-xs{color:var(--color-red-400)}.text-page-heading{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);color:var(--color-gray-900)}.dark .text-page-heading{color:var(--color-white)}.text-table-header{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider);color:var(--color-gray-500);text-transform:uppercase}.dark .text-table-header{color:var(--color-gray-400)}.panel-header-row{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4)}.dark .panel-header-row{border-color:var(--color-gray-700)}.grid-2col-responsive{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}@media(min-width:48rem){.grid-2col-responsive{grid-template-columns:repeat(2,minmax(0,1fr))}}.panel-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-block:calc(var(--spacing) * 3)}.dark .panel-header{border-color:var(--color-gray-700)}.gap-sm{gap:calc(var(--spacing) * 2)}.gap-md{gap:calc(var(--spacing) * 4)}.gap-lg{gap:calc(var(--spacing) * 6)}.mb-sm{margin-bottom:calc(var(--spacing) * 2)}.mb-md{margin-bottom:calc(var(--spacing) * 4)}.mb-lg{margin-bottom:calc(var(--spacing) * 6)}.surface{background-color:var(--color-white)}.dark .surface{background-color:var(--color-gray-800)}.surface-alt{background-color:var(--color-gray-50)}.dark .surface-alt{background-color:var(--color-gray-900)}.card-border{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.dark .card-border{border-color:var(--color-gray-700)}.card-border-xl{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.dark .card-border-xl{border-color:var(--color-gray-700)}.border-light{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300)}.dark .border-light{border-color:var(--color-gray-600)}.disabled-state:disabled{cursor:not-allowed;opacity:.5}.focus-ring:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dark .focus-ring:focus{--tw-ring-offset-color:var(--color-gray-800)}.focus-ring-inset:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;--tw-ring-inset:inset;outline-style:none}@media(hover:hover){.hover-bg:hover{background-color:var(--color-gray-100)}.dark .hover-bg:hover{background-color:var(--color-gray-700)}.hover-bg-light:hover{background-color:var(--color-gray-50)}.dark .hover-bg-light:hover{background-color:var(--color-gray-800)}}.btn-base{border-radius:var(--radius-lg);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;justify-content:center;align-items:center;transition-duration:.2s;display:inline-flex}.btn-base:disabled{cursor:not-allowed;opacity:.5}.btn-size-sm{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.btn-size-md{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.btn-size-lg{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 3);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.badge-base{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:3.40282e38px;align-items:center;display:inline-flex}.badge-size-sm{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5)}.badge-size-md{padding-inline:calc(var(--spacing) * 2.5);padding-block:calc(var(--spacing) * 1)}.shadow-card{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-dropdown{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.shadow-hover:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.page-title{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}@media(min-width:40rem){.page-title{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}}.dark .page-title{color:var(--color-white)}.page-description{margin-top:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .page-description{color:var(--color-gray-400)}.page-action-primary{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-blue-600);color:var(--color-white);display:flex}@media(hover:hover){.page-action-primary:hover{background-color:var(--color-blue-700)}}.page-action-secondary{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-gray-200);color:var(--color-gray-900);display:flex}@media(hover:hover){.page-action-secondary:hover{background-color:var(--color-gray-300)}}.dark .page-action-secondary{background-color:var(--color-gray-700);color:var(--color-gray-200)}@media(hover:hover){.dark .page-action-secondary:hover{background-color:var(--color-gray-600)}}.page-action-danger{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-red-600);color:var(--color-white);display:flex}@media(hover:hover){.page-action-danger:hover{background-color:var(--color-red-700)}}.page-action-ghost{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));color:var(--color-gray-500);display:flex}@media(hover:hover){.page-action-ghost:hover{background-color:var(--color-gray-100);color:var(--color-gray-700)}}.dark .page-action-ghost{color:var(--color-gray-400)}@media(hover:hover){.dark .page-action-ghost:hover{background-color:var(--color-gray-800);color:var(--color-gray-200)}}.tab-container{margin-bottom:calc(var(--spacing) * 6);gap:calc(var(--spacing) * 6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);display:flex}.dark .tab-container{border-color:var(--color-gray-700)}.tab-btn{align-items:center;gap:calc(var(--spacing) * 2);border-bottom-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 1);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px;margin-bottom:-1px;display:flex}@media(hover:hover){.tab-btn:hover{color:var(--color-gray-900)}}.dark .tab-btn{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn:hover{color:var(--color-gray-200)}}.tab-btn-active{align-items:center;gap:calc(var(--spacing) * 2);border-bottom-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 1);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-bottom-width:2px;border-color:var(--color-blue-600);color:var(--color-blue-600);margin-bottom:-1px;display:flex}.dark .tab-btn-active{color:var(--color-blue-400)}.tab-badge{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);background-color:var(--color-gray-100);color:var(--color-gray-600);border-radius:3.40282e38px}.dark .tab-badge{background-color:var(--color-gray-700);color:var(--color-gray-400)}.tab-badge-active{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);background-color:var(--color-blue-100);color:var(--color-blue-600);border-radius:3.40282e38px}.dark .tab-badge-active{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tab-badge-active{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .tab-badge-active{color:var(--color-blue-400)}.sticky-tab-nav{top:calc(var(--spacing) * 0);z-index:40;margin-inline:calc(var(--spacing) * -6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 6);position:sticky}.dark .sticky-tab-nav{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-tab-nav-responsive{top:calc(var(--spacing) * 0);z-index:40;margin-inline:calc(var(--spacing) * -4);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 4);position:sticky}@media(min-width:40rem){.sticky-tab-nav-responsive{margin-inline:calc(var(--spacing) * -6);padding-inline:calc(var(--spacing) * 6)}}@media(min-width:64rem){.sticky-tab-nav-responsive{margin-inline:calc(var(--spacing) * -8);padding-inline:calc(var(--spacing) * 8)}}.dark .sticky-tab-nav-responsive{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-section-header{top:calc(var(--spacing) * 0);z-index:40;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);position:sticky}.dark .sticky-section-header{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-footer-buttons{bottom:calc(var(--spacing) * 0);z-index:10;margin-top:calc(var(--spacing) * 6);justify-content:flex-end;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);display:flex;position:sticky}.dark .sticky-footer-buttons{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.sticky-footer-buttons-between{bottom:calc(var(--spacing) * 0);z-index:10;margin-top:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);display:flex;position:sticky}.dark .sticky-footer-buttons-between{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.footer-buttons-between{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);display:flex}.dark .footer-buttons-between{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.tab-content{padding-top:calc(var(--spacing) * 6)}.admin-page-content{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 6)}.dark .admin-page-content{background-color:var(--color-gray-900)}.admin-page-content-compact{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 4)}.dark .admin-page-content-compact{background-color:var(--color-gray-900)}.admin-page-content-responsive{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 4)}@media(min-width:40rem){.admin-page-content-responsive{padding:calc(var(--spacing) * 6)}}@media(min-width:64rem){.admin-page-content-responsive{padding:calc(var(--spacing) * 8)}}.dark .admin-page-content-responsive{background-color:var(--color-gray-900)}.admin-page-content-responsive-fluid{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4)}@media(min-width:40rem){.admin-page-content-responsive-fluid{padding:calc(var(--spacing) * 6)}}@media(min-width:64rem){.admin-page-content-responsive-fluid{padding:calc(var(--spacing) * 8)}}.dark .admin-page-content-responsive-fluid{background-color:var(--color-gray-900)}.admin-page-content-fluid{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 6)}.dark .admin-page-content-fluid{background-color:var(--color-gray-900)}.admin-page-content-fluid-compact{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4)}.dark .admin-page-content-fluid-compact{background-color:var(--color-gray-900)}.admin-page-content-viewport{background-color:var(--color-gray-50);height:calc(100vh - 64px);padding:calc(var(--spacing) * 6);overflow:hidden}.dark .admin-page-content-viewport{background-color:var(--color-gray-900)}.admin-page-content-viewport-compact{background-color:var(--color-gray-50);height:calc(100vh - 64px);padding:calc(var(--spacing) * 4);overflow:hidden}.dark .admin-page-content-viewport-compact{background-color:var(--color-gray-900)}.bulk-actions-bar{margin-bottom:calc(var(--spacing) * 4);align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);padding:calc(var(--spacing) * 3);display:flex}.dark .bulk-actions-bar{border-color:var(--color-gray-600);background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .bulk-actions-bar{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}:where(.admin-sidebar-menu-list>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}.admin-sidebar-menu-list{padding-inline:calc(var(--spacing) * 4);padding-bottom:calc(var(--spacing) * 4)}.admin-sidebar-item{border-radius:var(--radius-lg);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));justify-content:space-between;align-items:center;display:flex}.admin-sidebar-subitem{border-radius:var(--radius-lg);width:100%;padding-block:6px;padding-right:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-size:13px;font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));justify-content:space-between;align-items:center;padding-left:30px;display:flex}.admin-sidebar-item-active{background-color:var(--color-slate-100);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-900)}.dark .admin-sidebar-item-active{background-color:var(--color-slate-700);color:var(--color-white)}.admin-sidebar-item-active-parent{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-900)}.dark .admin-sidebar-item-active-parent{color:var(--color-white)}.admin-sidebar-item-default{color:var(--color-slate-500)}@media(hover:hover){.admin-sidebar-item-default:hover{background-color:var(--color-slate-50);color:var(--color-slate-900)}}.dark .admin-sidebar-item-default{color:var(--color-slate-400)}@media(hover:hover){.dark .admin-sidebar-item-default:hover{background-color:var(--color-slate-800);color:var(--color-white)}}.admin-sidebar-item-disabled{cursor:default;color:var(--color-slate-400)}.dark .admin-sidebar-item-disabled{color:var(--color-slate-500)}.admin-sidebar-icon-box{flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;display:flex}.admin-sidebar-chevron-trail{align-items:center;gap:calc(var(--spacing) * 1);display:flex}.admin-sidebar-item-content{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.admin-sidebar-module-icon{margin-right:calc(var(--spacing) * 2);height:calc(var(--spacing) * 3);width:calc(var(--spacing) * 3);color:var(--color-slate-400);opacity:.6}.dark .admin-sidebar-module-icon{color:var(--color-slate-500)}.admin-sidebar-tree-line-vertical{top:calc(var(--spacing) * 0);bottom:calc(var(--spacing) * 0);z-index:0;border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-slate-300);position:absolute;left:12px}.dark .admin-sidebar-tree-line-vertical{border-color:var(--color-slate-600)}.admin-sidebar-tree-line-curve{top:calc(var(--spacing) * 0);z-index:10;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-left-style:var(--tw-border-style);width:14px;height:50%;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-left-width:1px;border-color:var(--color-slate-300);border-bottom-left-radius:10px;position:absolute;left:12px}@media(hover:hover){.admin-sidebar-tree-line-curve:is(:where(.group):hover *){border-color:var(--color-blue-400)}}.dark .admin-sidebar-tree-line-curve{border-color:var(--color-slate-600)}@media(hover:hover){.dark .admin-sidebar-tree-line-curve:is(:where(.group):hover *){border-color:var(--color-blue-500)}}.admin-sidebar-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-slate-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.dark .admin-sidebar-header{border-color:var(--color-slate-700)}.admin-sidebar-collapse-btn{border-radius:var(--radius-md);padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.admin-sidebar-collapse-btn:hover{background-color:var(--color-slate-100)}.dark .admin-sidebar-collapse-btn:hover{background-color:var(--color-slate-700)}}.tab-nav-default{gap:calc(var(--spacing) * 2);display:flex}.tab-nav-underline{gap:calc(var(--spacing) * 0);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);display:flex}.dark .tab-nav-underline{border-color:var(--color-gray-700)}.tab-btn-base{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.tab-btn-default{border-bottom-style:var(--tw-border-style);color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px}@media(hover:hover){.tab-btn-default:hover{background-color:var(--color-gray-50);color:var(--color-gray-900)}}.dark .tab-btn-default{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-default:hover{background-color:var(--color-gray-700);color:var(--color-gray-200)}}.tab-btn-default-active{border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-blue-600);background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .tab-btn-default-active{border-color:var(--color-blue-400);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tab-btn-default-active{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .tab-btn-default-active{color:var(--color-blue-400)}.tab-btn-underline{border-bottom-style:var(--tw-border-style);color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px}@media(hover:hover){.tab-btn-underline:hover{border-color:var(--color-gray-300);color:var(--color-gray-900)}}.dark .tab-btn-underline{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-underline:hover{border-color:var(--color-gray-600);color:var(--color-gray-200)}}.tab-btn-underline-active{border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-blue-600);color:var(--color-blue-600)}.dark .tab-btn-underline-active{border-color:var(--color-blue-400);color:var(--color-blue-400)}.tab-btn-pills{border-radius:var(--radius-lg);color:var(--color-gray-600)}@media(hover:hover){.tab-btn-pills:hover{background-color:var(--color-gray-100)}}.dark .tab-btn-pills{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-pills:hover{background-color:var(--color-gray-700)}}.tab-btn-pills-active{border-radius:var(--radius-lg);background-color:var(--color-blue-600);color:var(--color-white)}.dark .tab-btn-pills-active{background-color:var(--color-blue-500)}.tab-btn-disabled{cursor:not-allowed;color:var(--color-gray-400);opacity:.5}.dark .tab-btn-disabled{color:var(--color-gray-600)}.toggle-container{align-items:flex-start;column-gap:calc(var(--spacing) * 2);flex-wrap:wrap;width:auto;display:inline-flex}.toggle-switch-wrapper{flex-shrink:0;align-items:center;display:inline-flex;position:relative}.toggle-switch-track{background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:relative}.toggle-switch-track:is(:where(.peer):focus~*){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-300);--tw-outline-style:none;outline-style:none}.toggle-switch-track:is(:where(.peer):checked~*){background-color:var(--color-blue-600)}.dark .toggle-switch-track{background-color:var(--color-gray-700)}.dark .toggle-switch-track:is(:where(.peer):focus~*){--tw-ring-color:var(--color-blue-800)}.dark .toggle-switch-track:is(:where(.peer):checked~*){background-color:var(--color-blue-500)}.toggle-switch-track:after{top:calc(var(--spacing) * .5);left:calc(var(--spacing) * .5);--tw-content:"";border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);content:var(--tw-content);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.dark .toggle-switch-track:after{content:var(--tw-content);border-color:var(--color-gray-600)}.toggle-switch-track:is(:where(.peer):checked~*):after{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y);content:var(--tw-content);border-color:var(--color-white)}.toggle-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .toggle-label{color:var(--color-white)}.toggle-description{margin-top:calc(var(--spacing) * 1);width:100%;font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .toggle-description{color:var(--color-gray-400)}.admin-footer{margin-top:calc(var(--spacing) * 6);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4)}.admin-footer-row{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 4);flex-direction:column;display:flex}@media(min-width:48rem){.admin-footer-row{flex-direction:row}}.admin-footer-group{align-items:center;gap:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);display:flex}.dark .admin-footer-group{color:var(--color-gray-400)}.admin-footer-link-hover{cursor:pointer;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.admin-footer-link-hover:hover{color:var(--color-blue-600)}.dark .admin-footer-link-hover:hover{color:var(--color-blue-400)}}.admin-footer-link{align-items:center;gap:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media(hover:hover){.admin-footer-link:hover{color:var(--color-blue-600)}}.dark .admin-footer-link{color:var(--color-gray-400)}@media(hover:hover){.dark .admin-footer-link:hover{color:var(--color-blue-400)}}.responsive-grid{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.grid-cols-1-lg-3{gap:calc(var(--spacing) * 6);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.col-span-1-lg,.col-span-2-lg{grid-column:span 1/span 1}.modal-description{margin-top:calc(var(--spacing) * 1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .modal-description{color:var(--color-gray-400)}.toast-message{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-1{inset:calc(var(--spacing) * 1)}.inset-2{inset:calc(var(--spacing) * 2)}.inset-3{inset:calc(var(--spacing) * 3)}.inset-4{inset:calc(var(--spacing) * 4)}.inset-5{inset:calc(var(--spacing) * 5)}.inset-6{inset:calc(var(--spacing) * 6)}.inset-8{inset:calc(var(--spacing) * 8)}.inset-auto{inset:auto}.inset-x-0{inset-inline:calc(var(--spacing) * 0)}.inset-x-1{inset-inline:calc(var(--spacing) * 1)}.inset-x-2{inset-inline:calc(var(--spacing) * 2)}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-4{inset-inline:calc(var(--spacing) * 4)}.inset-x-auto{inset-inline:auto}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.inset-y-1{inset-block:calc(var(--spacing) * 1)}.inset-y-2{inset-block:calc(var(--spacing) * 2)}.inset-y-3{inset-block:calc(var(--spacing) * 3)}.inset-y-4{inset-block:calc(var(--spacing) * 4)}.inset-y-auto{inset-block:auto}.-top-1{top:calc(var(--spacing) * -1)}.-top-2{top:calc(var(--spacing) * -2)}.-top-3{top:calc(var(--spacing) * -3)}.-top-4{top:calc(var(--spacing) * -4)}.top-0{top:calc(var(--spacing) * 0)}.top-1{top:calc(var(--spacing) * 1)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-1\/3{top:33.3333%}.top-1\/4{top:25%}.top-2{top:calc(var(--spacing) * 2)}.top-2\/3{top:66.6667%}.top-3{top:calc(var(--spacing) * 3)}.top-3\/4{top:75%}.top-4{top:calc(var(--spacing) * 4)}.top-5{top:calc(var(--spacing) * 5)}.top-6{top:calc(var(--spacing) * 6)}.top-8{top:calc(var(--spacing) * 8)}.top-10{top:calc(var(--spacing) * 10)}.top-12{top:calc(var(--spacing) * 12)}.top-16{top:calc(var(--spacing) * 16)}.top-auto{top:auto}.top-full{top:100%}.-right-1{right:calc(var(--spacing) * -1)}.-right-2{right:calc(var(--spacing) * -2)}.-right-3{right:calc(var(--spacing) * -3)}.-right-4{right:calc(var(--spacing) * -4)}.right-0{right:calc(var(--spacing) * 0)}.right-1{right:calc(var(--spacing) * 1)}.right-1\/2{right:50%}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-8{right:calc(var(--spacing) * 8)}.right-10{right:calc(var(--spacing) * 10)}.right-12{right:calc(var(--spacing) * 12)}.right-16{right:calc(var(--spacing) * 16)}.right-auto{right:auto}.right-full{right:100%}.-bottom-1{bottom:calc(var(--spacing) * -1)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-3{bottom:calc(var(--spacing) * -3)}.-bottom-4{bottom:calc(var(--spacing) * -4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-1{bottom:calc(var(--spacing) * 1)}.bottom-1\/2{bottom:50%}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-5{bottom:calc(var(--spacing) * 5)}.bottom-6{bottom:calc(var(--spacing) * 6)}.bottom-8{bottom:calc(var(--spacing) * 8)}.bottom-10{bottom:calc(var(--spacing) * 10)}.bottom-12{bottom:calc(var(--spacing) * 12)}.bottom-16{bottom:calc(var(--spacing) * 16)}.bottom-auto{bottom:auto}.bottom-full{bottom:100%}.-left-1{left:calc(var(--spacing) * -1)}.-left-2{left:calc(var(--spacing) * -2)}.-left-3{left:calc(var(--spacing) * -3)}.-left-4{left:calc(var(--spacing) * -4)}.left-0{left:calc(var(--spacing) * 0)}.left-1{left:calc(var(--spacing) * 1)}.left-1\.5{left:calc(var(--spacing) * 1.5)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing) * 2)}.left-3{left:calc(var(--spacing) * 3)}.left-4{left:calc(var(--spacing) * 4)}.left-5{left:calc(var(--spacing) * 5)}.left-6{left:calc(var(--spacing) * 6)}.left-8{left:calc(var(--spacing) * 8)}.left-10{left:calc(var(--spacing) * 10)}.left-12{left:calc(var(--spacing) * 12)}.left-16{left:calc(var(--spacing) * 16)}.left-auto{left:auto}.left-full{left:100%}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[999\]{z-index:999}.z-\[9999\]{z-index:9999}.z-auto{z-index:auto}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-first{order:-9999}.order-last{order:9999}.order-none{order:0}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.col-span-9{grid-column:span 9/span 9}.col-span-10{grid-column:span 10/span 10}.col-span-11{grid-column:span 11/span 11}.col-span-12{grid-column:span 12/span 12}.col-span-full{grid-column:1/-1}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.col-start-3{grid-column-start:3}.col-start-4{grid-column-start:4}.col-start-5{grid-column-start:5}.col-start-6{grid-column-start:6}.col-start-7{grid-column-start:7}.col-start-8{grid-column-start:8}.col-start-9{grid-column-start:9}.col-start-10{grid-column-start:10}.col-start-11{grid-column-start:11}.col-start-12{grid-column-start:12}.col-start-13{grid-column-start:13}.col-end-1{grid-column-end:1}.col-end-2{grid-column-end:2}.col-end-3{grid-column-end:3}.col-end-4{grid-column-end:4}.col-end-5{grid-column-end:5}.col-end-6{grid-column-end:6}.col-end-7{grid-column-end:7}.col-end-8{grid-column-end:8}.col-end-9{grid-column-end:9}.col-end-10{grid-column-end:10}.col-end-11{grid-column-end:11}.col-end-12{grid-column-end:12}.col-end-13{grid-column-end:13}.row-span-1{grid-row:span 1/span 1}.row-span-2{grid-row:span 2/span 2}.row-span-3{grid-row:span 3/span 3}.row-span-4{grid-row:span 4/span 4}.row-span-5{grid-row:span 5/span 5}.row-span-6{grid-row:span 6/span 6}.row-span-full{grid-row:1/-1}.row-start-1{grid-row-start:1}.row-start-2{grid-row-start:2}.row-start-3{grid-row-start:3}.row-start-4{grid-row-start:4}.row-start-5{grid-row-start:5}.row-start-6{grid-row-start:6}.row-start-7{grid-row-start:7}.row-end-1{grid-row-end:1}.row-end-2{grid-row-end:2}.row-end-3{grid-row-end:3}.row-end-4{grid-row-end:4}.row-end-5{grid-row-end:5}.row-end-6{grid-row-end:6}.row-end-7{grid-row-end:7}.float-end{float:inline-end}.float-left{float:left}.float-none{float:none}.float-right{float:right}.float-start{float:inline-start}.clear-both{clear:both}.clear-end{clear:inline-end}.clear-left{clear:left}.clear-none{clear:none}.clear-right{clear:right}.clear-start{clear:inline-start}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.\!m-0{margin:calc(var(--spacing) * 0)!important}.\!m-1{margin:calc(var(--spacing) * 1)!important}.\!m-2{margin:calc(var(--spacing) * 2)!important}.\!m-3{margin:calc(var(--spacing) * 3)!important}.\!m-4{margin:calc(var(--spacing) * 4)!important}.\!m-5{margin:calc(var(--spacing) * 5)!important}.\!m-6{margin:calc(var(--spacing) * 6)!important}.\!m-8{margin:calc(var(--spacing) * 8)!important}.\!m-10{margin:calc(var(--spacing) * 10)!important}.\!m-12{margin:calc(var(--spacing) * 12)!important}.\!m-16{margin:calc(var(--spacing) * 16)!important}.\!m-auto{margin:auto!important}.-m-1{margin:calc(var(--spacing) * -1)}.-m-2{margin:calc(var(--spacing) * -2)}.-m-3{margin:calc(var(--spacing) * -3)}.-m-4{margin:calc(var(--spacing) * -4)}.-m-5{margin:calc(var(--spacing) * -5)}.-m-6{margin:calc(var(--spacing) * -6)}.-m-8{margin:calc(var(--spacing) * -8)}.m-0{margin:calc(var(--spacing) * 0)}.m-1{margin:calc(var(--spacing) * 1)}.m-2{margin:calc(var(--spacing) * 2)}.m-3{margin:calc(var(--spacing) * 3)}.m-4{margin:calc(var(--spacing) * 4)}.m-5{margin:calc(var(--spacing) * 5)}.m-6{margin:calc(var(--spacing) * 6)}.m-8{margin:calc(var(--spacing) * 8)}.m-10{margin:calc(var(--spacing) * 10)}.m-12{margin:calc(var(--spacing) * 12)}.m-16{margin:calc(var(--spacing) * 16)}.m-auto{margin:auto}.\!mx-0{margin-inline:calc(var(--spacing) * 0)!important}.\!mx-1{margin-inline:calc(var(--spacing) * 1)!important}.\!mx-2{margin-inline:calc(var(--spacing) * 2)!important}.\!mx-3{margin-inline:calc(var(--spacing) * 3)!important}.\!mx-4{margin-inline:calc(var(--spacing) * 4)!important}.\!mx-5{margin-inline:calc(var(--spacing) * 5)!important}.\!mx-6{margin-inline:calc(var(--spacing) * 6)!important}.\!mx-8{margin-inline:calc(var(--spacing) * 8)!important}.\!mx-10{margin-inline:calc(var(--spacing) * 10)!important}.\!mx-12{margin-inline:calc(var(--spacing) * 12)!important}.\!mx-16{margin-inline:calc(var(--spacing) * 16)!important}.\!mx-auto{margin-inline:auto!important}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.-mx-2{margin-inline:calc(var(--spacing) * -2)}.-mx-3{margin-inline:calc(var(--spacing) * -3)}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.-mx-5{margin-inline:calc(var(--spacing) * -5)}.-mx-6{margin-inline:calc(var(--spacing) * -6)}.-mx-8{margin-inline:calc(var(--spacing) * -8)}.mx-0{margin-inline:calc(var(--spacing) * 0)}.mx-1{margin-inline:calc(var(--spacing) * 1)}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-6{margin-inline:calc(var(--spacing) * 6)}.mx-8{margin-inline:calc(var(--spacing) * 8)}.mx-10{margin-inline:calc(var(--spacing) * 10)}.mx-12{margin-inline:calc(var(--spacing) * 12)}.mx-16{margin-inline:calc(var(--spacing) * 16)}.mx-auto{margin-inline:auto}.\!my-0{margin-block:calc(var(--spacing) * 0)!important}.\!my-1{margin-block:calc(var(--spacing) * 1)!important}.\!my-2{margin-block:calc(var(--spacing) * 2)!important}.\!my-3{margin-block:calc(var(--spacing) * 3)!important}.\!my-4{margin-block:calc(var(--spacing) * 4)!important}.\!my-5{margin-block:calc(var(--spacing) * 5)!important}.\!my-6{margin-block:calc(var(--spacing) * 6)!important}.\!my-8{margin-block:calc(var(--spacing) * 8)!important}.\!my-10{margin-block:calc(var(--spacing) * 10)!important}.\!my-12{margin-block:calc(var(--spacing) * 12)!important}.\!my-16{margin-block:calc(var(--spacing) * 16)!important}.\!my-auto{margin-block:auto!important}.-my-1{margin-block:calc(var(--spacing) * -1)}.-my-2{margin-block:calc(var(--spacing) * -2)}.-my-3{margin-block:calc(var(--spacing) * -3)}.-my-4{margin-block:calc(var(--spacing) * -4)}.-my-5{margin-block:calc(var(--spacing) * -5)}.-my-6{margin-block:calc(var(--spacing) * -6)}.-my-8{margin-block:calc(var(--spacing) * -8)}.my-0{margin-block:calc(var(--spacing) * 0)}.my-1{margin-block:calc(var(--spacing) * 1)}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.my-5{margin-block:calc(var(--spacing) * 5)}.my-6{margin-block:calc(var(--spacing) * 6)}.my-8{margin-block:calc(var(--spacing) * 8)}.my-10{margin-block:calc(var(--spacing) * 10)}.my-12{margin-block:calc(var(--spacing) * 12)}.my-16{margin-block:calc(var(--spacing) * 16)}.my-auto{margin-block:auto}.-mt-1{margin-top:calc(var(--spacing) * -1)}.-mt-2{margin-top:calc(var(--spacing) * -2)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.-mt-4{margin-top:calc(var(--spacing) * -4)}.-mt-5{margin-top:calc(var(--spacing) * -5)}.-mt-6{margin-top:calc(var(--spacing) * -6)}.-mt-8{margin-top:calc(var(--spacing) * -8)}.mt-0{margin-top:calc(var(--spacing) * 0)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mt-auto{margin-top:auto}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mr-2{margin-right:calc(var(--spacing) * -2)}.-mr-3{margin-right:calc(var(--spacing) * -3)}.-mr-4{margin-right:calc(var(--spacing) * -4)}.-mr-5{margin-right:calc(var(--spacing) * -5)}.-mr-6{margin-right:calc(var(--spacing) * -6)}.-mr-8{margin-right:calc(var(--spacing) * -8)}.mr-0{margin-right:calc(var(--spacing) * 0)}.mr-1{margin-right:calc(var(--spacing) * 1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mr-4{margin-right:calc(var(--spacing) * 4)}.mr-5{margin-right:calc(var(--spacing) * 5)}.mr-6{margin-right:calc(var(--spacing) * 6)}.mr-8{margin-right:calc(var(--spacing) * 8)}.mr-10{margin-right:calc(var(--spacing) * 10)}.mr-12{margin-right:calc(var(--spacing) * 12)}.mr-16{margin-right:calc(var(--spacing) * 16)}.mr-auto{margin-right:auto}.-mb-1{margin-bottom:calc(var(--spacing) * -1)}.-mb-2{margin-bottom:calc(var(--spacing) * -2)}.-mb-3{margin-bottom:calc(var(--spacing) * -3)}.-mb-4{margin-bottom:calc(var(--spacing) * -4)}.-mb-5{margin-bottom:calc(var(--spacing) * -5)}.-mb-6{margin-bottom:calc(var(--spacing) * -6)}.-mb-8{margin-bottom:calc(var(--spacing) * -8)}.-mb-px{margin-bottom:-1px}.mb-0{margin-bottom:calc(var(--spacing) * 0)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.mb-16{margin-bottom:calc(var(--spacing) * 16)}.mb-auto{margin-bottom:auto}.-ml-1{margin-left:calc(var(--spacing) * -1)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.-ml-3{margin-left:calc(var(--spacing) * -3)}.-ml-4{margin-left:calc(var(--spacing) * -4)}.-ml-5{margin-left:calc(var(--spacing) * -5)}.-ml-6{margin-left:calc(var(--spacing) * -6)}.-ml-8{margin-left:calc(var(--spacing) * -8)}.ml-0{margin-left:calc(var(--spacing) * 0)}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-5{margin-left:calc(var(--spacing) * 5)}.ml-6{margin-left:calc(var(--spacing) * 6)}.ml-8{margin-left:calc(var(--spacing) * 8)}.ml-10{margin-left:calc(var(--spacing) * 10)}.ml-12{margin-left:calc(var(--spacing) * 12)}.ml-16{margin-left:calc(var(--spacing) * 16)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.box-content{box-sizing:content-box}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\!block{display:block!important}.\!flex{display:flex!important}.\!grid{display:grid!important}.\!hidden{display:none!important}.\!inline{display:inline!important}.\!inline-block{display:inline-block!important}.\!inline-flex{display:inline-flex!important}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.aspect-auto{aspect-ratio:auto}.aspect-square{aspect-ratio:1}.aspect-video{aspect-ratio:var(--aspect-video)}.\!h-0{height:calc(var(--spacing) * 0)!important}.\!h-1{height:calc(var(--spacing) * 1)!important}.\!h-2{height:calc(var(--spacing) * 2)!important}.\!h-3{height:calc(var(--spacing) * 3)!important}.\!h-4{height:calc(var(--spacing) * 4)!important}.\!h-5{height:calc(var(--spacing) * 5)!important}.\!h-6{height:calc(var(--spacing) * 6)!important}.\!h-8{height:calc(var(--spacing) * 8)!important}.\!h-10{height:calc(var(--spacing) * 10)!important}.\!h-12{height:calc(var(--spacing) * 12)!important}.\!h-16{height:calc(var(--spacing) * 16)!important}.\!h-20{height:calc(var(--spacing) * 20)!important}.\!h-24{height:calc(var(--spacing) * 24)!important}.\!h-32{height:calc(var(--spacing) * 32)!important}.\!h-40{height:calc(var(--spacing) * 40)!important}.\!h-48{height:calc(var(--spacing) * 48)!important}.\!h-56{height:calc(var(--spacing) * 56)!important}.\!h-64{height:calc(var(--spacing) * 64)!important}.\!h-72{height:calc(var(--spacing) * 72)!important}.\!h-80{height:calc(var(--spacing) * 80)!important}.\!h-96{height:calc(var(--spacing) * 96)!important}.\!h-auto{height:auto!important}.\!h-fit{height:fit-content!important}.\!h-full{height:100%!important}.\!h-max{height:max-content!important}.\!h-min{height:min-content!important}.\!h-px{height:1px!important}.\!h-screen{height:100vh!important}.h-0{height:calc(var(--spacing) * 0)}.h-1{height:calc(var(--spacing) * 1)}.h-1\/2{height:50%}.h-1\/3{height:33.3333%}.h-1\/4{height:25%}.h-1\/5{height:20%}.h-1\/6{height:16.6667%}.h-2{height:calc(var(--spacing) * 2)}.h-2\/3{height:66.6667%}.h-2\/4{height:50%}.h-2\/5{height:40%}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-3\/4{height:75%}.h-3\/5{height:60%}.h-4{height:calc(var(--spacing) * 4)}.h-4\/5{height:80%}.h-5{height:calc(var(--spacing) * 5)}.h-5\/6{height:83.3333%}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-24{height:calc(var(--spacing) * 24)}.h-32{height:calc(var(--spacing) * 32)}.h-40{height:calc(var(--spacing) * 40)}.h-48{height:calc(var(--spacing) * 48)}.h-56{height:calc(var(--spacing) * 56)}.h-64{height:calc(var(--spacing) * 64)}.h-72{height:calc(var(--spacing) * 72)}.h-80{height:calc(var(--spacing) * 80)}.h-96{height:calc(var(--spacing) * 96)}.h-\[42px\]{height:42px}.h-auto{height:auto}.h-fit{height:fit-content}.h-full{height:100%}.h-max{height:max-content}.h-min{height:min-content}.h-px{height:1px}.h-screen{height:100vh}.max-h-0{max-height:calc(var(--spacing) * 0)}.max-h-20{max-height:calc(var(--spacing) * 20)}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[70vh\]{max-height:70vh}.max-h-\[400px\]{max-height:400px}.max-h-\[500px\]{max-height:500px}.max-h-\[calc\(90vh-140px\)\]{max-height:calc(90vh - 140px)}.max-h-\[calc\(100vh-320px\)\]{max-height:calc(100vh - 320px)}.max-h-fit{max-height:fit-content}.max-h-full{max-height:100%}.max-h-max{max-height:max-content}.max-h-min{max-height:min-content}.max-h-none{max-height:none}.max-h-screen{max-height:100vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-12{min-height:calc(var(--spacing) * 12)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-20{min-height:calc(var(--spacing) * 20)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-32{min-height:calc(var(--spacing) * 32)}.min-h-40{min-height:calc(var(--spacing) * 40)}.min-h-48{min-height:calc(var(--spacing) * 48)}.min-h-56{min-height:calc(var(--spacing) * 56)}.min-h-64{min-height:calc(var(--spacing) * 64)}.min-h-\[40px\]{min-height:40px}.min-h-\[42px\]{min-height:42px}.min-h-\[60px\]{min-height:60px}.min-h-\[60vh\]{min-height:60vh}.min-h-\[80px\]{min-height:80px}.min-h-\[200px\]{min-height:200px}.min-h-\[400px\]{min-height:400px}.min-h-fit{min-height:fit-content}.min-h-full{min-height:100%}.min-h-max{min-height:max-content}.min-h-min{min-height:min-content}.min-h-screen{min-height:100vh}.\!w-0{width:calc(var(--spacing) * 0)!important}.\!w-1{width:calc(var(--spacing) * 1)!important}.\!w-1\/2{width:50%!important}.\!w-1\/3{width:33.3333%!important}.\!w-1\/4{width:25%!important}.\!w-1\/5{width:20%!important}.\!w-1\/6{width:16.6667%!important}.\!w-2{width:calc(var(--spacing) * 2)!important}.\!w-2\/3{width:66.6667%!important}.\!w-2\/4{width:50%!important}.\!w-2\/5{width:40%!important}.\!w-3{width:calc(var(--spacing) * 3)!important}.\!w-3\/4{width:75%!important}.\!w-3\/5{width:60%!important}.\!w-4{width:calc(var(--spacing) * 4)!important}.\!w-4\/5{width:80%!important}.\!w-5{width:calc(var(--spacing) * 5)!important}.\!w-5\/6{width:83.3333%!important}.\!w-6{width:calc(var(--spacing) * 6)!important}.\!w-8{width:calc(var(--spacing) * 8)!important}.\!w-10{width:calc(var(--spacing) * 10)!important}.\!w-12{width:calc(var(--spacing) * 12)!important}.\!w-16{width:calc(var(--spacing) * 16)!important}.\!w-20{width:calc(var(--spacing) * 20)!important}.\!w-24{width:calc(var(--spacing) * 24)!important}.\!w-32{width:calc(var(--spacing) * 32)!important}.\!w-40{width:calc(var(--spacing) * 40)!important}.\!w-48{width:calc(var(--spacing) * 48)!important}.\!w-52{width:calc(var(--spacing) * 52)!important}.\!w-56{width:calc(var(--spacing) * 56)!important}.\!w-60{width:calc(var(--spacing) * 60)!important}.\!w-64{width:calc(var(--spacing) * 64)!important}.\!w-72{width:calc(var(--spacing) * 72)!important}.\!w-80{width:calc(var(--spacing) * 80)!important}.\!w-96{width:calc(var(--spacing) * 96)!important}.\!w-auto{width:auto!important}.\!w-fit{width:fit-content!important}.\!w-full{width:100%!important}.\!w-max{width:max-content!important}.\!w-min{width:min-content!important}.\!w-px{width:1px!important}.\!w-screen{width:100vw!important}.w-0{width:calc(var(--spacing) * 0)}.w-1{width:calc(var(--spacing) * 1)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-1\/4{width:25%}.w-1\/5{width:20%}.w-1\/6{width:16.6667%}.w-2{width:calc(var(--spacing) * 2)}.w-2\/3{width:66.6667%}.w-2\/4{width:50%}.w-2\/5{width:40%}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-3\/4{width:75%}.w-3\/5{width:60%}.w-4{width:calc(var(--spacing) * 4)}.w-4\/5{width:80%}.w-5{width:calc(var(--spacing) * 5)}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-40{width:calc(var(--spacing) * 40)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-80{width:calc(var(--spacing) * 80)}.w-96{width:calc(var(--spacing) * 96)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-min{width:min-content}.w-px{width:1px}.w-screen{width:100vw}.\!max-w-0{max-width:calc(var(--spacing) * 0)!important}.\!max-w-2xl{max-width:var(--container-2xl)!important}.\!max-w-3xl{max-width:var(--container-3xl)!important}.\!max-w-4xl{max-width:var(--container-4xl)!important}.\!max-w-full{max-width:100%!important}.\!max-w-lg{max-width:var(--container-lg)!important}.\!max-w-md{max-width:var(--container-md)!important}.\!max-w-none{max-width:none!important}.\!max-w-sm{max-width:var(--container-sm)!important}.\!max-w-xl{max-width:var(--container-xl)!important}.\!max-w-xs{max-width:var(--container-xs)!important}.max-w-0{max-width:calc(var(--spacing) * 0)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[200px\]{max-width:200px}.max-w-\[calc\(100vw-1rem\)\]{max-width:calc(100vw - 1rem)}.max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.max-w-fit{max-width:fit-content}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-max{max-width:max-content}.max-w-md{max-width:var(--container-md)}.max-w-min{max-width:min-content}.max-w-none{max-width:none}.max-w-prose{max-width:65ch}.max-w-screen-2xl{max-width:var(--breakpoint-2xl)}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.max-w-screen-md{max-width:var(--breakpoint-md)}.max-w-screen-sm{max-width:var(--breakpoint-sm)}.max-w-screen-xl{max-width:var(--breakpoint-xl)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.\!min-w-0{min-width:calc(var(--spacing) * 0)!important}.\!min-w-4{min-width:calc(var(--spacing) * 4)!important}.\!min-w-8{min-width:calc(var(--spacing) * 8)!important}.\!min-w-12{min-width:calc(var(--spacing) * 12)!important}.\!min-w-16{min-width:calc(var(--spacing) * 16)!important}.\!min-w-20{min-width:calc(var(--spacing) * 20)!important}.\!min-w-24{min-width:calc(var(--spacing) * 24)!important}.\!min-w-32{min-width:calc(var(--spacing) * 32)!important}.\!min-w-40{min-width:calc(var(--spacing) * 40)!important}.\!min-w-48{min-width:calc(var(--spacing) * 48)!important}.\!min-w-56{min-width:calc(var(--spacing) * 56)!important}.\!min-w-64{min-width:calc(var(--spacing) * 64)!important}.\!min-w-fit{min-width:fit-content!important}.\!min-w-full{min-width:100%!important}.\!min-w-max{min-width:max-content!important}.\!min-w-min{min-width:min-content!important}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[20px\]{min-width:20px}.min-w-\[80px\]{min-width:80px}.min-w-\[120px\]{min-width:120px}.min-w-\[200px\]{min-width:200px}.min-w-\[280px\]{min-width:280px}.min-w-\[300px\]{min-width:300px}.min-w-fit{min-width:fit-content}.min-w-full{min-width:100%}.min-w-max{min-width:max-content}.min-w-min{min-width:min-content}.\!flex-1{flex:1!important}.\!flex-auto{flex:auto!important}.\!flex-initial{flex:0 auto!important}.\!flex-none{flex:none!important}.flex-1{flex:1}.flex-auto{flex:auto}.flex-initial{flex:0 auto}.flex-none{flex:none}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-0{flex-basis:calc(var(--spacing) * 0)}.basis-1{flex-basis:calc(var(--spacing) * 1)}.basis-1\/2{flex-basis:50%}.basis-1\/3{flex-basis:33.3333%}.basis-1\/4{flex-basis:25%}.basis-2{flex-basis:calc(var(--spacing) * 2)}.basis-2\/3{flex-basis:66.6667%}.basis-3\/4{flex-basis:75%}.basis-auto{flex-basis:auto}.basis-full{flex-basis:100%}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.caption-bottom{caption-side:bottom}.caption-top{caption-side:top}.border-collapse{border-collapse:collapse}.border-separate{border-collapse:separate}.border-spacing-0{--tw-border-spacing-x:calc(var(--spacing) * 0);--tw-border-spacing-y:calc(var(--spacing) * 0);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-1{--tw-border-spacing-x:calc(var(--spacing) * 1);--tw-border-spacing-y:calc(var(--spacing) * 1);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-2{--tw-border-spacing-x:calc(var(--spacing) * 2);--tw-border-spacing-y:calc(var(--spacing) * 2);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-3{--tw-border-spacing-x:calc(var(--spacing) * 3);--tw-border-spacing-y:calc(var(--spacing) * 3);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-4{--tw-border-spacing-x:calc(var(--spacing) * 4);--tw-border-spacing-y:calc(var(--spacing) * 4);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-5{--tw-border-spacing-x:calc(var(--spacing) * 5);--tw-border-spacing-y:calc(var(--spacing) * 5);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-6{--tw-border-spacing-x:calc(var(--spacing) * 6);--tw-border-spacing-y:calc(var(--spacing) * 6);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-8{--tw-border-spacing-x:calc(var(--spacing) * 8);--tw-border-spacing-y:calc(var(--spacing) * 8);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.origin-bottom{transform-origin:bottom}.origin-bottom-left{transform-origin:0 100%}.origin-bottom-right{transform-origin:100% 100%}.origin-center{transform-origin:50%}.origin-left{transform-origin:0}.origin-right{transform-origin:100%}.origin-top{transform-origin:top}.origin-top-left{transform-origin:0 0}.origin-top-right{transform-origin:100% 0}.-translate-x-1{--tw-translate-x:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-2{--tw-translate-x:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-3{--tw-translate-x:calc(var(--spacing) * -3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-4{--tw-translate-x:calc(var(--spacing) * -4);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1{--tw-translate-x:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/2{--tw-translate-x: 50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/3{--tw-translate-x:calc(1 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/4{--tw-translate-x: 25% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-2{--tw-translate-x:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-2\/3{--tw-translate-x:calc(2 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-3{--tw-translate-x:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-3\/4{--tw-translate-x: 75% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-6{--tw-translate-x:calc(var(--spacing) * 6);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-8{--tw-translate-x:calc(var(--spacing) * 8);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-10{--tw-translate-x:calc(var(--spacing) * 10);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-12{--tw-translate-x:calc(var(--spacing) * 12);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1{--tw-translate-y:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-2{--tw-translate-y:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-3{--tw-translate-y:calc(var(--spacing) * -3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-4{--tw-translate-y:calc(var(--spacing) * -4);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-full{--tw-translate-y:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-0{--tw-translate-y:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1{--tw-translate-y:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/2{--tw-translate-y: 50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/3{--tw-translate-y:calc(1 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/4{--tw-translate-y: 25% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-2{--tw-translate-y:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-2\/3{--tw-translate-y:calc(2 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3{--tw-translate-y:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3\/4{--tw-translate-y: 75% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-4{--tw-translate-y:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-5{--tw-translate-y:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-6{--tw-translate-y:calc(var(--spacing) * 6);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-8{--tw-translate-y:calc(var(--spacing) * 8);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-10{--tw-translate-y:calc(var(--spacing) * 10);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-12{--tw-translate-y:calc(var(--spacing) * 12);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-full{--tw-translate-y:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-50{--tw-scale-x:50%;--tw-scale-y:50%;--tw-scale-z:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-90{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-95{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-105{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-125{--tw-scale-x:125%;--tw-scale-y:125%;--tw-scale-z:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-150{--tw-scale-x:150%;--tw-scale-y:150%;--tw-scale-z:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-0{--tw-scale-x:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-50{--tw-scale-x:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-75{--tw-scale-x:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-90{--tw-scale-x:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-95{--tw-scale-x:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-100{--tw-scale-x:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-105{--tw-scale-x:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-110{--tw-scale-x:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-125{--tw-scale-x:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-150{--tw-scale-x:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-0{--tw-scale-y:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-50{--tw-scale-y:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-75{--tw-scale-y:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-90{--tw-scale-y:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-95{--tw-scale-y:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-100{--tw-scale-y:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-105{--tw-scale-y:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-110{--tw-scale-y:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-125{--tw-scale-y:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-150{--tw-scale-y:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.-rotate-1{rotate:-1deg}.-rotate-2{rotate:-2deg}.-rotate-3{rotate:-3deg}.-rotate-6{rotate:-6deg}.-rotate-12{rotate:-12deg}.-rotate-45{rotate:-45deg}.-rotate-90{rotate:-90deg}.-rotate-180{rotate:-180deg}.rotate-0{rotate:0deg}.rotate-1{rotate:1deg}.rotate-2{rotate:2deg}.rotate-3{rotate:3deg}.rotate-6{rotate:6deg}.rotate-12{rotate:12deg}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.-skew-x-1{--tw-skew-x:skewX( -1deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-2{--tw-skew-x:skewX( -2deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-3{--tw-skew-x:skewX( -3deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-6{--tw-skew-x:skewX( -6deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-12{--tw-skew-x:skewX( -12deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-0{--tw-skew-x:skewX(0deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-1{--tw-skew-x:skewX(1deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-2{--tw-skew-x:skewX(2deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-3{--tw-skew-x:skewX(3deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-6{--tw-skew-x:skewX(6deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-12{--tw-skew-x:skewX(12deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-1{--tw-skew-y:skewY( -1deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-2{--tw-skew-y:skewY( -2deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-3{--tw-skew-y:skewY( -3deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-6{--tw-skew-y:skewY( -6deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-12{--tw-skew-y:skewY( -12deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-0{--tw-skew-y:skewY(0deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-1{--tw-skew-y:skewY(1deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-2{--tw-skew-y:skewY(2deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-3{--tw-skew-y:skewY(3deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-6{--tw-skew-y:skewY(6deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-12{--tw-skew-y:skewY(12deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform,.transform-cpu{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform-gpu{transform:translateZ(0) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform-none{transform:none}.animate-\[loading_1\.5s_ease-in-out_infinite\]{animation:1.5s ease-in-out infinite loading}.animate-bounce{animation:var(--animate-bounce)}.animate-none{animation:none}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-alias{cursor:alias}.cursor-all-scroll{cursor:all-scroll}.cursor-auto{cursor:auto}.cursor-cell{cursor:cell}.cursor-col-resize{cursor:col-resize}.cursor-context-menu{cursor:context-menu}.cursor-copy{cursor:copy}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-e-resize{cursor:e-resize}.cursor-ew-resize{cursor:ew-resize}.cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}.cursor-help{cursor:help}.cursor-move{cursor:move}.cursor-n-resize{cursor:n-resize}.cursor-ne-resize{cursor:ne-resize}.cursor-nesw-resize{cursor:nesw-resize}.cursor-no-drop{cursor:no-drop}.cursor-none{cursor:none}.cursor-not-allowed{cursor:not-allowed}.cursor-ns-resize{cursor:ns-resize}.cursor-nw-resize{cursor:nw-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.cursor-progress{cursor:progress}.cursor-row-resize{cursor:row-resize}.cursor-s-resize{cursor:s-resize}.cursor-se-resize{cursor:se-resize}.cursor-sw-resize{cursor:sw-resize}.cursor-text{cursor:text}.cursor-vertical-text{cursor:vertical-text}.cursor-w-resize{cursor:w-resize}.cursor-wait{cursor:wait}.cursor-zoom-in{cursor:zoom-in}.cursor-zoom-out{cursor:zoom-out}.touch-pan-left{--tw-pan-x:pan-left;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-right{--tw-pan-x:pan-right;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-x{--tw-pan-x:pan-x;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-down{--tw-pan-y:pan-down;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-up{--tw-pan-y:pan-up;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-y{--tw-pan-y:pan-y;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-auto{touch-action:auto}.touch-manipulation{touch-action:manipulation}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.snap-both{scroll-snap-type:both var(--tw-scroll-snap-strictness)}.snap-none{scroll-snap-type:none}.snap-x{scroll-snap-type:x var(--tw-scroll-snap-strictness)}.snap-y{scroll-snap-type:y var(--tw-scroll-snap-strictness)}.snap-mandatory{--tw-scroll-snap-strictness:mandatory}.snap-proximity{--tw-scroll-snap-strictness:proximity}.snap-align-none{scroll-snap-align:none}.snap-center{scroll-snap-align:center}.snap-end{scroll-snap-align:end}.snap-start{scroll-snap-align:start}.snap-always{scroll-snap-stop:always}.snap-normal{scroll-snap-stop:normal}.scroll-mt-44{scroll-margin-top:calc(var(--spacing) * 44)}.list-inside{list-style-position:inside}.list-outside{list-style-position:outside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-auto{appearance:auto}.appearance-none{appearance:none}.columns-1{columns:1}.columns-2{columns:2}.columns-2xl{columns:var(--container-2xl)}.columns-2xs{columns:var(--container-2xs)}.columns-3{columns:3}.columns-3xl{columns:var(--container-3xl)}.columns-3xs{columns:var(--container-3xs)}.columns-4{columns:4}.columns-4xl{columns:var(--container-4xl)}.columns-5{columns:5}.columns-5xl{columns:var(--container-5xl)}.columns-6{columns:6}.columns-6xl{columns:var(--container-6xl)}.columns-7{columns:7}.columns-7xl{columns:var(--container-7xl)}.columns-8{columns:8}.columns-9{columns:9}.columns-10{columns:10}.columns-11{columns:11}.columns-12{columns:12}.columns-auto{columns:auto}.columns-lg{columns:var(--container-lg)}.columns-md{columns:var(--container-md)}.columns-sm{columns:var(--container-sm)}.columns-xl{columns:var(--container-xl)}.columns-xs{columns:var(--container-xs)}.break-before-all{break-before:all}.break-before-auto{break-before:auto}.break-before-avoid{break-before:avoid}.break-before-avoid-page{break-before:avoid-page}.break-before-column{break-before:column}.break-before-left{break-before:left}.break-before-page{break-before:page}.break-before-right{break-before:right}.break-inside-auto{break-inside:auto}.break-inside-avoid{break-inside:avoid}.break-inside-avoid-column{break-inside:avoid-column}.break-inside-avoid-page{break-inside:avoid-page}.break-after-all{break-after:all}.break-after-auto{break-after:auto}.break-after-avoid{break-after:avoid}.break-after-avoid-page{break-after:avoid-page}.break-after-column{break-after:column}.break-after-left{break-after:left}.break-after-page{break-after:page}.break-after-right{break-after:right}.auto-cols-auto{grid-auto-columns:auto}.auto-cols-fr{grid-auto-columns:minmax(0,1fr)}.auto-cols-max{grid-auto-columns:max-content}.auto-cols-min{grid-auto-columns:min-content}.grid-flow-col{grid-auto-flow:column}.grid-flow-col-dense{grid-auto-flow:column dense}.grid-flow-dense{grid-auto-flow:dense}.grid-flow-row{grid-auto-flow:row}.grid-flow-row-dense{grid-auto-flow:dense}.auto-rows-auto{grid-auto-rows:auto}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.auto-rows-max{grid-auto-rows:max-content}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.\!flex-col{flex-direction:column!important}.\!flex-row{flex-direction:row!important}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.\!flex-nowrap{flex-wrap:nowrap!important}.\!flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap}.flex-wrap{flex-wrap:wrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.place-content-around{place-content:space-around}.place-content-baseline{place-content:baseline start}.place-content-between{place-content:space-between}.place-content-center{place-content:center}.place-content-end{place-content:end}.place-content-evenly{place-content:space-evenly}.place-content-start{place-content:start}.place-content-stretch{place-content:stretch}.place-items-baseline{place-items:baseline}.place-items-center{place-items:center}.place-items-end{place-items:end}.place-items-start{place-items:start}.place-items-stretch{place-items:stretch stretch}.content-around{align-content:space-around}.content-between{align-content:space-between}.content-center{align-content:center}.content-end{align-content:flex-end}.content-evenly{align-content:space-evenly}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-evenly{justify-content:space-evenly}.justify-start{justify-content:flex-start}.\!gap-0{gap:calc(var(--spacing) * 0)!important}.\!gap-1{gap:calc(var(--spacing) * 1)!important}.\!gap-2{gap:calc(var(--spacing) * 2)!important}.\!gap-3{gap:calc(var(--spacing) * 3)!important}.\!gap-4{gap:calc(var(--spacing) * 4)!important}.\!gap-5{gap:calc(var(--spacing) * 5)!important}.\!gap-6{gap:calc(var(--spacing) * 6)!important}.\!gap-8{gap:calc(var(--spacing) * 8)!important}.\!gap-10{gap:calc(var(--spacing) * 10)!important}.\!gap-12{gap:calc(var(--spacing) * 12)!important}.\!gap-16{gap:calc(var(--spacing) * 16)!important}.gap-0{gap:calc(var(--spacing) * 0)}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-10{gap:calc(var(--spacing) * 10)}.gap-12{gap:calc(var(--spacing) * 12)}.gap-16{gap:calc(var(--spacing) * 16)}:where(.-space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * -1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-y-reverse)))}:where(.-space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * -2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-0{column-gap:calc(var(--spacing) * 0)}.gap-x-1{column-gap:calc(var(--spacing) * 1)}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-x-6{column-gap:calc(var(--spacing) * 6)}.gap-x-8{column-gap:calc(var(--spacing) * 8)}.gap-x-10{column-gap:calc(var(--spacing) * 10)}.gap-x-12{column-gap:calc(var(--spacing) * 12)}:where(.-space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-x-reverse)))}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 0) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-5>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 5) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.gap-y-0{row-gap:calc(var(--spacing) * 0)}.gap-y-1{row-gap:calc(var(--spacing) * 1)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-3{row-gap:calc(var(--spacing) * 3)}.gap-y-4{row-gap:calc(var(--spacing) * 4)}.gap-y-5{row-gap:calc(var(--spacing) * 5)}.gap-y-6{row-gap:calc(var(--spacing) * 6)}.gap-y-8{row-gap:calc(var(--spacing) * 8)}.gap-y-10{row-gap:calc(var(--spacing) * 10)}.gap-y-12{row-gap:calc(var(--spacing) * 12)}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-0>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(0px * var(--tw-divide-x-reverse));border-inline-end-width:calc(0px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-2>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(2px * var(--tw-divide-x-reverse));border-inline-end-width:calc(2px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-4>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(4px * var(--tw-divide-x-reverse));border-inline-end-width:calc(4px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-8>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(8px * var(--tw-divide-x-reverse));border-inline-end-width:calc(8px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-0>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(0px * var(--tw-divide-y-reverse));border-bottom-width:calc(0px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-2>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(2px * var(--tw-divide-y-reverse));border-bottom-width:calc(2px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-4>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(4px * var(--tw-divide-y-reverse));border-bottom-width:calc(4px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-8>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(8px * var(--tw-divide-y-reverse));border-bottom-width:calc(8px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}:where(.divide-dashed>:not(:last-child)){--tw-border-style:dashed;border-style:dashed}:where(.divide-dotted>:not(:last-child)){--tw-border-style:dotted;border-style:dotted}:where(.divide-double>:not(:last-child)){--tw-border-style:double;border-style:double}:where(.divide-none>:not(:last-child)){--tw-border-style:none;border-style:none}:where(.divide-solid>:not(:last-child)){--tw-border-style:solid;border-style:solid}:where(.divide-black>:not(:last-child)){border-color:var(--color-black)}:where(.divide-current>:not(:last-child)){border-color:currentColor}:where(.divide-gray-50>:not(:last-child)){border-color:var(--color-gray-50)}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}:where(.divide-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}:where(.divide-gray-400>:not(:last-child)){border-color:var(--color-gray-400)}:where(.divide-gray-500>:not(:last-child)){border-color:var(--color-gray-500)}:where(.divide-gray-600>:not(:last-child)){border-color:var(--color-gray-600)}:where(.divide-gray-700>:not(:last-child)){border-color:var(--color-gray-700)}:where(.divide-gray-800>:not(:last-child)){border-color:var(--color-gray-800)}:where(.divide-gray-900>:not(:last-child)){border-color:var(--color-gray-900)}:where(.divide-inherit>:not(:last-child)){border-color:inherit}:where(.divide-purple-100>:not(:last-child)){border-color:var(--color-purple-100)}:where(.divide-slate-50>:not(:last-child)){border-color:var(--color-slate-50)}:where(.divide-slate-100>:not(:last-child)){border-color:var(--color-slate-100)}:where(.divide-slate-200>:not(:last-child)){border-color:var(--color-slate-200)}:where(.divide-slate-300>:not(:last-child)){border-color:var(--color-slate-300)}:where(.divide-slate-400>:not(:last-child)){border-color:var(--color-slate-400)}:where(.divide-slate-500>:not(:last-child)){border-color:var(--color-slate-500)}:where(.divide-slate-600>:not(:last-child)){border-color:var(--color-slate-600)}:where(.divide-slate-700>:not(:last-child)){border-color:var(--color-slate-700)}:where(.divide-slate-800>:not(:last-child)){border-color:var(--color-slate-800)}:where(.divide-slate-900>:not(:last-child)){border-color:var(--color-slate-900)}:where(.divide-transparent>:not(:last-child)){border-color:#0000}:where(.divide-white>:not(:last-child)){border-color:var(--color-white)}.place-self-auto{place-self:auto}.place-self-center{place-self:center}.place-self-end{place-self:end}.place-self-start{place-self:start}.place-self-stretch{place-self:stretch stretch}.self-auto{align-self:auto}.self-baseline{align-self:baseline}.self-center{align-self:center}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-clip{overflow:clip}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-x-clip{overflow-x:clip}.overflow-x-hidden{overflow-x:hidden}.overflow-x-scroll{overflow-x:scroll}.overflow-x-visible{overflow-x:visible}.overflow-y-auto{overflow-y:auto}.overflow-y-clip{overflow-y:clip}.overflow-y-hidden{overflow-y:hidden}.overflow-y-scroll{overflow-y:scroll}.overflow-y-visible{overflow-y:visible}.overscroll-auto{overscroll-behavior:auto}.overscroll-contain{overscroll-behavior:contain}.overscroll-none{overscroll-behavior:none}.overscroll-x-auto{overscroll-behavior-x:auto}.overscroll-x-contain{overscroll-behavior-x:contain}.overscroll-x-none{overscroll-behavior-x:none}.overscroll-y-auto{overscroll-behavior-y:auto}.overscroll-y-contain{overscroll-behavior-y:contain}.overscroll-y-none{overscroll-behavior-y:none}.scroll-auto{scroll-behavior:auto}.scroll-smooth{scroll-behavior:smooth}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-2xl{border-top-left-radius:var(--radius-2xl);border-top-right-radius:var(--radius-2xl)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.rounded-t-full{border-top-left-radius:3.40282e38px;border-top-right-radius:3.40282e38px}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-t-sm{border-top-left-radius:var(--radius-sm);border-top-right-radius:var(--radius-sm)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-l-2xl{border-top-left-radius:var(--radius-2xl);border-bottom-left-radius:var(--radius-2xl)}.rounded-l-3xl{border-top-left-radius:var(--radius-3xl);border-bottom-left-radius:var(--radius-3xl)}.rounded-l-full{border-top-left-radius:3.40282e38px;border-bottom-left-radius:3.40282e38px}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-l-md{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-l-sm{border-top-left-radius:var(--radius-sm);border-bottom-left-radius:var(--radius-sm)}.rounded-l-xl{border-top-left-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-tl{border-top-left-radius:.25rem}.rounded-tl-2xl{border-top-left-radius:var(--radius-2xl)}.rounded-tl-3xl{border-top-left-radius:var(--radius-3xl)}.rounded-tl-full{border-top-left-radius:3.40282e38px}.rounded-tl-lg{border-top-left-radius:var(--radius-lg)}.rounded-tl-md{border-top-left-radius:var(--radius-md)}.rounded-tl-none{border-top-left-radius:0}.rounded-tl-sm{border-top-left-radius:var(--radius-sm)}.rounded-tl-xl{border-top-left-radius:var(--radius-xl)}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-r-2xl{border-top-right-radius:var(--radius-2xl);border-bottom-right-radius:var(--radius-2xl)}.rounded-r-3xl{border-top-right-radius:var(--radius-3xl);border-bottom-right-radius:var(--radius-3xl)}.rounded-r-full{border-top-right-radius:3.40282e38px;border-bottom-right-radius:3.40282e38px}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-r-md{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.rounded-r-sm{border-top-right-radius:var(--radius-sm);border-bottom-right-radius:var(--radius-sm)}.rounded-r-xl{border-top-right-radius:var(--radius-xl);border-bottom-right-radius:var(--radius-xl)}.rounded-tr{border-top-right-radius:.25rem}.rounded-tr-2xl{border-top-right-radius:var(--radius-2xl)}.rounded-tr-3xl{border-top-right-radius:var(--radius-3xl)}.rounded-tr-full{border-top-right-radius:3.40282e38px}.rounded-tr-lg{border-top-right-radius:var(--radius-lg)}.rounded-tr-md{border-top-right-radius:var(--radius-md)}.rounded-tr-none{border-top-right-radius:0}.rounded-tr-sm{border-top-right-radius:var(--radius-sm)}.rounded-tr-xl{border-top-right-radius:var(--radius-xl)}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-2xl{border-bottom-right-radius:var(--radius-2xl);border-bottom-left-radius:var(--radius-2xl)}.rounded-b-3xl{border-bottom-right-radius:var(--radius-3xl);border-bottom-left-radius:var(--radius-3xl)}.rounded-b-full{border-bottom-right-radius:3.40282e38px;border-bottom-left-radius:3.40282e38px}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.rounded-b-sm{border-bottom-right-radius:var(--radius-sm);border-bottom-left-radius:var(--radius-sm)}.rounded-b-xl{border-bottom-right-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-br{border-bottom-right-radius:.25rem}.rounded-br-2xl{border-bottom-right-radius:var(--radius-2xl)}.rounded-br-3xl{border-bottom-right-radius:var(--radius-3xl)}.rounded-br-full{border-bottom-right-radius:3.40282e38px}.rounded-br-lg{border-bottom-right-radius:var(--radius-lg)}.rounded-br-md{border-bottom-right-radius:var(--radius-md)}.rounded-br-none{border-bottom-right-radius:0}.rounded-br-sm{border-bottom-right-radius:var(--radius-sm)}.rounded-br-xl{border-bottom-right-radius:var(--radius-xl)}.rounded-bl{border-bottom-left-radius:.25rem}.rounded-bl-2xl{border-bottom-left-radius:var(--radius-2xl)}.rounded-bl-3xl{border-bottom-left-radius:var(--radius-3xl)}.rounded-bl-full{border-bottom-left-radius:3.40282e38px}.rounded-bl-lg{border-bottom-left-radius:var(--radius-lg)}.rounded-bl-md{border-bottom-left-radius:var(--radius-md)}.rounded-bl-none{border-bottom-left-radius:0}.rounded-bl-sm{border-bottom-left-radius:var(--radius-sm)}.rounded-bl-xl{border-bottom-left-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-3{border-style:var(--tw-border-style);border-width:3px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-8{border-style:var(--tw-border-style);border-width:8px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.border-x-2{border-inline-style:var(--tw-border-style);border-inline-width:2px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-y-0{border-block-style:var(--tw-border-style);border-block-width:0}.border-y-2{border-block-style:var(--tw-border-style);border-block-width:2px}.\!border-t-0{border-top-style:var(--tw-border-style)!important;border-top-width:0!important}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.border-t-2{border-top-style:var(--tw-border-style);border-top-width:2px}.border-t-4{border-top-style:var(--tw-border-style);border-top-width:4px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-0{border-right-style:var(--tw-border-style);border-right-width:0}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-r-4{border-right-style:var(--tw-border-style);border-right-width:4px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-b-4{border-bottom-style:var(--tw-border-style);border-bottom-width:4px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-dotted{--tw-border-style:dotted;border-style:dotted}.border-double{--tw-border-style:double;border-style:double}.border-hidden{--tw-border-style:hidden;border-style:hidden}.border-none{--tw-border-style:none;border-style:none}.border-solid{--tw-border-style:solid;border-style:solid}.border-amber-50{border-color:var(--color-amber-50)}.border-amber-100{border-color:var(--color-amber-100)}.border-amber-200{border-color:var(--color-amber-200)}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-amber-500{border-color:var(--color-amber-500)}.border-amber-600{border-color:var(--color-amber-600)}.border-amber-700{border-color:var(--color-amber-700)}.border-amber-800{border-color:var(--color-amber-800)}.border-amber-900{border-color:var(--color-amber-900)}.border-amber-950{border-color:var(--color-amber-950)}.border-black{border-color:var(--color-black)}.border-blue-50{border-color:var(--color-blue-50)}.border-blue-100{border-color:var(--color-blue-100)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-300{border-color:var(--color-blue-300)}.border-blue-400{border-color:var(--color-blue-400)}.border-blue-500{border-color:var(--color-blue-500)}.border-blue-600{border-color:var(--color-blue-600)}.border-blue-700{border-color:var(--color-blue-700)}.border-blue-800{border-color:var(--color-blue-800)}.border-blue-900{border-color:var(--color-blue-900)}.border-blue-950{border-color:var(--color-blue-950)}.border-current{border-color:currentColor}.border-cyan-50{border-color:var(--color-cyan-50)}.border-cyan-100{border-color:var(--color-cyan-100)}.border-cyan-200{border-color:var(--color-cyan-200)}.border-cyan-300{border-color:var(--color-cyan-300)}.border-cyan-400{border-color:var(--color-cyan-400)}.border-cyan-500{border-color:var(--color-cyan-500)}.border-cyan-600{border-color:var(--color-cyan-600)}.border-cyan-700{border-color:var(--color-cyan-700)}.border-cyan-800{border-color:var(--color-cyan-800)}.border-cyan-900{border-color:var(--color-cyan-900)}.border-cyan-950{border-color:var(--color-cyan-950)}.border-emerald-50{border-color:var(--color-emerald-50)}.border-emerald-100{border-color:var(--color-emerald-100)}.border-emerald-200{border-color:var(--color-emerald-200)}.border-emerald-300{border-color:var(--color-emerald-300)}.border-emerald-400{border-color:var(--color-emerald-400)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-emerald-600{border-color:var(--color-emerald-600)}.border-emerald-700{border-color:var(--color-emerald-700)}.border-emerald-800{border-color:var(--color-emerald-800)}.border-emerald-900{border-color:var(--color-emerald-900)}.border-emerald-950{border-color:var(--color-emerald-950)}.border-fuchsia-50{border-color:var(--color-fuchsia-50)}.border-fuchsia-100{border-color:var(--color-fuchsia-100)}.border-fuchsia-200{border-color:var(--color-fuchsia-200)}.border-fuchsia-300{border-color:var(--color-fuchsia-300)}.border-fuchsia-400{border-color:var(--color-fuchsia-400)}.border-fuchsia-500{border-color:var(--color-fuchsia-500)}.border-fuchsia-600{border-color:var(--color-fuchsia-600)}.border-fuchsia-700{border-color:var(--color-fuchsia-700)}.border-fuchsia-800{border-color:var(--color-fuchsia-800)}.border-fuchsia-900{border-color:var(--color-fuchsia-900)}.border-fuchsia-950{border-color:var(--color-fuchsia-950)}.border-gray-50{border-color:var(--color-gray-50)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400{border-color:var(--color-gray-400)}.border-gray-500{border-color:var(--color-gray-500)}.border-gray-600{border-color:var(--color-gray-600)}.border-gray-700{border-color:var(--color-gray-700)}.border-gray-800{border-color:var(--color-gray-800)}.border-gray-900{border-color:var(--color-gray-900)}.border-gray-950{border-color:var(--color-gray-950)}.border-green-50{border-color:var(--color-green-50)}.border-green-100{border-color:var(--color-green-100)}.border-green-200{border-color:var(--color-green-200)}.border-green-300{border-color:var(--color-green-300)}.border-green-400{border-color:var(--color-green-400)}.border-green-500{border-color:var(--color-green-500)}.border-green-600{border-color:var(--color-green-600)}.border-green-700{border-color:var(--color-green-700)}.border-green-800{border-color:var(--color-green-800)}.border-green-900{border-color:var(--color-green-900)}.border-green-950{border-color:var(--color-green-950)}.border-indigo-50{border-color:var(--color-indigo-50)}.border-indigo-100{border-color:var(--color-indigo-100)}.border-indigo-200{border-color:var(--color-indigo-200)}.border-indigo-300{border-color:var(--color-indigo-300)}.border-indigo-400{border-color:var(--color-indigo-400)}.border-indigo-500{border-color:var(--color-indigo-500)}.border-indigo-600{border-color:var(--color-indigo-600)}.border-indigo-700{border-color:var(--color-indigo-700)}.border-indigo-800{border-color:var(--color-indigo-800)}.border-indigo-900{border-color:var(--color-indigo-900)}.border-indigo-950{border-color:var(--color-indigo-950)}.border-inherit{border-color:inherit}.border-lime-50{border-color:var(--color-lime-50)}.border-lime-100{border-color:var(--color-lime-100)}.border-lime-200{border-color:var(--color-lime-200)}.border-lime-300{border-color:var(--color-lime-300)}.border-lime-400{border-color:var(--color-lime-400)}.border-lime-500{border-color:var(--color-lime-500)}.border-lime-600{border-color:var(--color-lime-600)}.border-lime-700{border-color:var(--color-lime-700)}.border-lime-800{border-color:var(--color-lime-800)}.border-lime-900{border-color:var(--color-lime-900)}.border-lime-950{border-color:var(--color-lime-950)}.border-neutral-50{border-color:var(--color-neutral-50)}.border-neutral-100{border-color:var(--color-neutral-100)}.border-neutral-200{border-color:var(--color-neutral-200)}.border-neutral-300{border-color:var(--color-neutral-300)}.border-neutral-400{border-color:var(--color-neutral-400)}.border-neutral-500{border-color:var(--color-neutral-500)}.border-neutral-600{border-color:var(--color-neutral-600)}.border-neutral-700{border-color:var(--color-neutral-700)}.border-neutral-800{border-color:var(--color-neutral-800)}.border-neutral-900{border-color:var(--color-neutral-900)}.border-neutral-950{border-color:var(--color-neutral-950)}.border-orange-50{border-color:var(--color-orange-50)}.border-orange-100{border-color:var(--color-orange-100)}.border-orange-200{border-color:var(--color-orange-200)}.border-orange-300{border-color:var(--color-orange-300)}.border-orange-400{border-color:var(--color-orange-400)}.border-orange-500{border-color:var(--color-orange-500)}.border-orange-600{border-color:var(--color-orange-600)}.border-orange-700{border-color:var(--color-orange-700)}.border-orange-800{border-color:var(--color-orange-800)}.border-orange-900{border-color:var(--color-orange-900)}.border-orange-950{border-color:var(--color-orange-950)}.border-pink-50{border-color:var(--color-pink-50)}.border-pink-100{border-color:var(--color-pink-100)}.border-pink-200{border-color:var(--color-pink-200)}.border-pink-300{border-color:var(--color-pink-300)}.border-pink-400{border-color:var(--color-pink-400)}.border-pink-500{border-color:var(--color-pink-500)}.border-pink-600{border-color:var(--color-pink-600)}.border-pink-700{border-color:var(--color-pink-700)}.border-pink-800{border-color:var(--color-pink-800)}.border-pink-900{border-color:var(--color-pink-900)}.border-pink-950{border-color:var(--color-pink-950)}.border-purple-50{border-color:var(--color-purple-50)}.border-purple-100{border-color:var(--color-purple-100)}.border-purple-200{border-color:var(--color-purple-200)}.border-purple-300{border-color:var(--color-purple-300)}.border-purple-400{border-color:var(--color-purple-400)}.border-purple-500{border-color:var(--color-purple-500)}.border-purple-600{border-color:var(--color-purple-600)}.border-purple-700{border-color:var(--color-purple-700)}.border-purple-800{border-color:var(--color-purple-800)}.border-purple-900{border-color:var(--color-purple-900)}.border-purple-950{border-color:var(--color-purple-950)}.border-red-50{border-color:var(--color-red-50)}.border-red-100{border-color:var(--color-red-100)}.border-red-200{border-color:var(--color-red-200)}.border-red-300{border-color:var(--color-red-300)}.border-red-400{border-color:var(--color-red-400)}.border-red-500{border-color:var(--color-red-500)}.border-red-600{border-color:var(--color-red-600)}.border-red-700{border-color:var(--color-red-700)}.border-red-800{border-color:var(--color-red-800)}.border-red-900{border-color:var(--color-red-900)}.border-red-950{border-color:var(--color-red-950)}.border-rose-50{border-color:var(--color-rose-50)}.border-rose-100{border-color:var(--color-rose-100)}.border-rose-200{border-color:var(--color-rose-200)}.border-rose-300{border-color:var(--color-rose-300)}.border-rose-400{border-color:var(--color-rose-400)}.border-rose-500{border-color:var(--color-rose-500)}.border-rose-600{border-color:var(--color-rose-600)}.border-rose-700{border-color:var(--color-rose-700)}.border-rose-800{border-color:var(--color-rose-800)}.border-rose-900{border-color:var(--color-rose-900)}.border-rose-950{border-color:var(--color-rose-950)}.border-sky-50{border-color:var(--color-sky-50)}.border-sky-100{border-color:var(--color-sky-100)}.border-sky-200{border-color:var(--color-sky-200)}.border-sky-300{border-color:var(--color-sky-300)}.border-sky-400{border-color:var(--color-sky-400)}.border-sky-500{border-color:var(--color-sky-500)}.border-sky-600{border-color:var(--color-sky-600)}.border-sky-700{border-color:var(--color-sky-700)}.border-sky-800{border-color:var(--color-sky-800)}.border-sky-900{border-color:var(--color-sky-900)}.border-sky-950{border-color:var(--color-sky-950)}.border-slate-50{border-color:var(--color-slate-50)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-slate-400{border-color:var(--color-slate-400)}.border-slate-500{border-color:var(--color-slate-500)}.border-slate-600{border-color:var(--color-slate-600)}.border-slate-700{border-color:var(--color-slate-700)}.border-slate-800{border-color:var(--color-slate-800)}.border-slate-900{border-color:var(--color-slate-900)}.border-slate-950{border-color:var(--color-slate-950)}.border-stone-50{border-color:var(--color-stone-50)}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-300{border-color:var(--color-stone-300)}.border-stone-400{border-color:var(--color-stone-400)}.border-stone-500{border-color:var(--color-stone-500)}.border-stone-600{border-color:var(--color-stone-600)}.border-stone-700{border-color:var(--color-stone-700)}.border-stone-800{border-color:var(--color-stone-800)}.border-stone-900{border-color:var(--color-stone-900)}.border-stone-950{border-color:var(--color-stone-950)}.border-teal-50{border-color:var(--color-teal-50)}.border-teal-100{border-color:var(--color-teal-100)}.border-teal-200{border-color:var(--color-teal-200)}.border-teal-300{border-color:var(--color-teal-300)}.border-teal-400{border-color:var(--color-teal-400)}.border-teal-500{border-color:var(--color-teal-500)}.border-teal-600{border-color:var(--color-teal-600)}.border-teal-700{border-color:var(--color-teal-700)}.border-teal-800{border-color:var(--color-teal-800)}.border-teal-900{border-color:var(--color-teal-900)}.border-teal-950{border-color:var(--color-teal-950)}.border-transparent{border-color:#0000}.border-violet-50{border-color:var(--color-violet-50)}.border-violet-100{border-color:var(--color-violet-100)}.border-violet-200{border-color:var(--color-violet-200)}.border-violet-300{border-color:var(--color-violet-300)}.border-violet-400{border-color:var(--color-violet-400)}.border-violet-500{border-color:var(--color-violet-500)}.border-violet-600{border-color:var(--color-violet-600)}.border-violet-700{border-color:var(--color-violet-700)}.border-violet-800{border-color:var(--color-violet-800)}.border-violet-900{border-color:var(--color-violet-900)}.border-violet-950{border-color:var(--color-violet-950)}.border-white{border-color:var(--color-white)}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.border-white\/30{border-color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.border-yellow-50{border-color:var(--color-yellow-50)}.border-yellow-100{border-color:var(--color-yellow-100)}.border-yellow-200{border-color:var(--color-yellow-200)}.border-yellow-300{border-color:var(--color-yellow-300)}.border-yellow-400{border-color:var(--color-yellow-400)}.border-yellow-500{border-color:var(--color-yellow-500)}.border-yellow-600{border-color:var(--color-yellow-600)}.border-yellow-700{border-color:var(--color-yellow-700)}.border-yellow-800{border-color:var(--color-yellow-800)}.border-yellow-900{border-color:var(--color-yellow-900)}.border-yellow-950{border-color:var(--color-yellow-950)}.border-zinc-50{border-color:var(--color-zinc-50)}.border-zinc-100{border-color:var(--color-zinc-100)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-400{border-color:var(--color-zinc-400)}.border-zinc-500{border-color:var(--color-zinc-500)}.border-zinc-600{border-color:var(--color-zinc-600)}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-800{border-color:var(--color-zinc-800)}.border-zinc-900{border-color:var(--color-zinc-900)}.border-zinc-950{border-color:var(--color-zinc-950)}.border-t-current{border-top-color:currentColor}.border-t-white{border-top-color:var(--color-white)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-200{background-color:var(--color-amber-200)}.bg-amber-300{background-color:var(--color-amber-300)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-600{background-color:var(--color-amber-600)}.bg-amber-700{background-color:var(--color-amber-700)}.bg-amber-800{background-color:var(--color-amber-800)}.bg-amber-900{background-color:var(--color-amber-900)}.bg-amber-950{background-color:var(--color-amber-950)}.bg-black{background-color:var(--color-black)}.bg-black\/0{background-color:#0000}@supports (color:color-mix(in lab,red,red)){.bg-black\/0{background-color:color-mix(in oklab,var(--color-black) 0%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-50\/70{background-color:#eff6ffb3}@supports (color:color-mix(in lab,red,red)){.bg-blue-50\/70{background-color:color-mix(in oklab,var(--color-blue-50) 70%,transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-200{background-color:var(--color-blue-200)}.bg-blue-300{background-color:var(--color-blue-300)}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-blue-700{background-color:var(--color-blue-700)}.bg-blue-800{background-color:var(--color-blue-800)}.bg-blue-900{background-color:var(--color-blue-900)}.bg-blue-950{background-color:var(--color-blue-950)}.bg-current{background-color:currentColor}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-cyan-100{background-color:var(--color-cyan-100)}.bg-cyan-200{background-color:var(--color-cyan-200)}.bg-cyan-300{background-color:var(--color-cyan-300)}.bg-cyan-400{background-color:var(--color-cyan-400)}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-600{background-color:var(--color-cyan-600)}.bg-cyan-700{background-color:var(--color-cyan-700)}.bg-cyan-800{background-color:var(--color-cyan-800)}.bg-cyan-900{background-color:var(--color-cyan-900)}.bg-cyan-950{background-color:var(--color-cyan-950)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-200{background-color:var(--color-emerald-200)}.bg-emerald-300{background-color:var(--color-emerald-300)}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-600{background-color:var(--color-emerald-600)}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-emerald-800{background-color:var(--color-emerald-800)}.bg-emerald-900{background-color:var(--color-emerald-900)}.bg-emerald-950{background-color:var(--color-emerald-950)}.bg-fuchsia-50{background-color:var(--color-fuchsia-50)}.bg-fuchsia-100{background-color:var(--color-fuchsia-100)}.bg-fuchsia-200{background-color:var(--color-fuchsia-200)}.bg-fuchsia-300{background-color:var(--color-fuchsia-300)}.bg-fuchsia-400{background-color:var(--color-fuchsia-400)}.bg-fuchsia-500{background-color:var(--color-fuchsia-500)}.bg-fuchsia-600{background-color:var(--color-fuchsia-600)}.bg-fuchsia-700{background-color:var(--color-fuchsia-700)}.bg-fuchsia-800{background-color:var(--color-fuchsia-800)}.bg-fuchsia-900{background-color:var(--color-fuchsia-900)}.bg-fuchsia-950{background-color:var(--color-fuchsia-950)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-100\/60{background-color:#f3f4f699}@supports (color:color-mix(in lab,red,red)){.bg-gray-100\/60{background-color:color-mix(in oklab,var(--color-gray-100) 60%,transparent)}}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-300{background-color:var(--color-gray-300)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500{background-color:var(--color-gray-500)}.bg-gray-600{background-color:var(--color-gray-600)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-800\/50{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.bg-gray-800\/50{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.bg-gray-900{background-color:var(--color-gray-900)}.bg-gray-950{background-color:var(--color-gray-950)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-200{background-color:var(--color-green-200)}.bg-green-300{background-color:var(--color-green-300)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-green-900{background-color:var(--color-green-900)}.bg-green-950{background-color:var(--color-green-950)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-indigo-200{background-color:var(--color-indigo-200)}.bg-indigo-300{background-color:var(--color-indigo-300)}.bg-indigo-400{background-color:var(--color-indigo-400)}.bg-indigo-500{background-color:var(--color-indigo-500)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-indigo-700{background-color:var(--color-indigo-700)}.bg-indigo-800{background-color:var(--color-indigo-800)}.bg-indigo-900{background-color:var(--color-indigo-900)}.bg-indigo-950{background-color:var(--color-indigo-950)}.bg-inherit{background-color:inherit}.bg-lime-50{background-color:var(--color-lime-50)}.bg-lime-100{background-color:var(--color-lime-100)}.bg-lime-200{background-color:var(--color-lime-200)}.bg-lime-300{background-color:var(--color-lime-300)}.bg-lime-400{background-color:var(--color-lime-400)}.bg-lime-500{background-color:var(--color-lime-500)}.bg-lime-600{background-color:var(--color-lime-600)}.bg-lime-700{background-color:var(--color-lime-700)}.bg-lime-800{background-color:var(--color-lime-800)}.bg-lime-900{background-color:var(--color-lime-900)}.bg-lime-950{background-color:var(--color-lime-950)}.bg-neutral-50{background-color:var(--color-neutral-50)}.bg-neutral-100{background-color:var(--color-neutral-100)}.bg-neutral-200{background-color:var(--color-neutral-200)}.bg-neutral-300{background-color:var(--color-neutral-300)}.bg-neutral-400{background-color:var(--color-neutral-400)}.bg-neutral-500{background-color:var(--color-neutral-500)}.bg-neutral-600{background-color:var(--color-neutral-600)}.bg-neutral-700{background-color:var(--color-neutral-700)}.bg-neutral-800{background-color:var(--color-neutral-800)}.bg-neutral-900{background-color:var(--color-neutral-900)}.bg-neutral-950{background-color:var(--color-neutral-950)}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-200{background-color:var(--color-orange-200)}.bg-orange-300{background-color:var(--color-orange-300)}.bg-orange-400{background-color:var(--color-orange-400)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-600{background-color:var(--color-orange-600)}.bg-orange-700{background-color:var(--color-orange-700)}.bg-orange-800{background-color:var(--color-orange-800)}.bg-orange-900{background-color:var(--color-orange-900)}.bg-orange-950{background-color:var(--color-orange-950)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-pink-100{background-color:var(--color-pink-100)}.bg-pink-200{background-color:var(--color-pink-200)}.bg-pink-300{background-color:var(--color-pink-300)}.bg-pink-400{background-color:var(--color-pink-400)}.bg-pink-500{background-color:var(--color-pink-500)}.bg-pink-600{background-color:var(--color-pink-600)}.bg-pink-700{background-color:var(--color-pink-700)}.bg-pink-800{background-color:var(--color-pink-800)}.bg-pink-900{background-color:var(--color-pink-900)}.bg-pink-950{background-color:var(--color-pink-950)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-purple-200{background-color:var(--color-purple-200)}.bg-purple-300{background-color:var(--color-purple-300)}.bg-purple-400{background-color:var(--color-purple-400)}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-600{background-color:var(--color-purple-600)}.bg-purple-700{background-color:var(--color-purple-700)}.bg-purple-800{background-color:var(--color-purple-800)}.bg-purple-900{background-color:var(--color-purple-900)}.bg-purple-950{background-color:var(--color-purple-950)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-200{background-color:var(--color-red-200)}.bg-red-300{background-color:var(--color-red-300)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/50{background-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/50{background-color:color-mix(in oklab,var(--color-red-500) 50%,transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-red-700{background-color:var(--color-red-700)}.bg-red-800{background-color:var(--color-red-800)}.bg-red-900{background-color:var(--color-red-900)}.bg-red-950{background-color:var(--color-red-950)}.bg-rose-50{background-color:var(--color-rose-50)}.bg-rose-100{background-color:var(--color-rose-100)}.bg-rose-200{background-color:var(--color-rose-200)}.bg-rose-300{background-color:var(--color-rose-300)}.bg-rose-400{background-color:var(--color-rose-400)}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-600{background-color:var(--color-rose-600)}.bg-rose-700{background-color:var(--color-rose-700)}.bg-rose-800{background-color:var(--color-rose-800)}.bg-rose-900{background-color:var(--color-rose-900)}.bg-rose-950{background-color:var(--color-rose-950)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-sky-200{background-color:var(--color-sky-200)}.bg-sky-300{background-color:var(--color-sky-300)}.bg-sky-400{background-color:var(--color-sky-400)}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-600{background-color:var(--color-sky-600)}.bg-sky-700{background-color:var(--color-sky-700)}.bg-sky-800{background-color:var(--color-sky-800)}.bg-sky-900{background-color:var(--color-sky-900)}.bg-sky-950{background-color:var(--color-sky-950)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-400{background-color:var(--color-slate-400)}.bg-slate-500{background-color:var(--color-slate-500)}.bg-slate-600{background-color:var(--color-slate-600)}.bg-slate-700{background-color:var(--color-slate-700)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-stone-50{background-color:var(--color-stone-50)}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200{background-color:var(--color-stone-200)}.bg-stone-300{background-color:var(--color-stone-300)}.bg-stone-400{background-color:var(--color-stone-400)}.bg-stone-500{background-color:var(--color-stone-500)}.bg-stone-600{background-color:var(--color-stone-600)}.bg-stone-700{background-color:var(--color-stone-700)}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-stone-950{background-color:var(--color-stone-950)}.bg-teal-50{background-color:var(--color-teal-50)}.bg-teal-100{background-color:var(--color-teal-100)}.bg-teal-200{background-color:var(--color-teal-200)}.bg-teal-300{background-color:var(--color-teal-300)}.bg-teal-400{background-color:var(--color-teal-400)}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-600{background-color:var(--color-teal-600)}.bg-teal-700{background-color:var(--color-teal-700)}.bg-teal-800{background-color:var(--color-teal-800)}.bg-teal-900{background-color:var(--color-teal-900)}.bg-teal-950{background-color:var(--color-teal-950)}.bg-transparent{background-color:#0000}.bg-violet-50{background-color:var(--color-violet-50)}.bg-violet-100{background-color:var(--color-violet-100)}.bg-violet-200{background-color:var(--color-violet-200)}.bg-violet-300{background-color:var(--color-violet-300)}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-500{background-color:var(--color-violet-500)}.bg-violet-600{background-color:var(--color-violet-600)}.bg-violet-700{background-color:var(--color-violet-700)}.bg-violet-800{background-color:var(--color-violet-800)}.bg-violet-900{background-color:var(--color-violet-900)}.bg-violet-950{background-color:var(--color-violet-950)}.bg-white{background-color:var(--color-white)}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-200{background-color:var(--color-yellow-200)}.bg-yellow-300{background-color:var(--color-yellow-300)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-600{background-color:var(--color-yellow-600)}.bg-yellow-700{background-color:var(--color-yellow-700)}.bg-yellow-800{background-color:var(--color-yellow-800)}.bg-yellow-900{background-color:var(--color-yellow-900)}.bg-yellow-950{background-color:var(--color-yellow-950)}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.bg-zinc-300{background-color:var(--color-zinc-300)}.bg-zinc-400{background-color:var(--color-zinc-400)}.bg-zinc-500{background-color:var(--color-zinc-500)}.bg-zinc-600{background-color:var(--color-zinc-600)}.bg-zinc-700{background-color:var(--color-zinc-700)}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-bl{--tw-gradient-position:to bottom left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-l{--tw-gradient-position:to left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-tl{--tw-gradient-position:to top left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-tr{--tw-gradient-position:to top right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-none{background-image:none}.from-black{--tw-gradient-from:var(--color-black);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-50{--tw-gradient-from:var(--color-blue-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-100{--tw-gradient-from:var(--color-blue-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-200{--tw-gradient-from:var(--color-blue-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-300{--tw-gradient-from:var(--color-blue-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-400{--tw-gradient-from:var(--color-blue-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-500{--tw-gradient-from:var(--color-blue-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-600{--tw-gradient-from:var(--color-blue-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-700{--tw-gradient-from:var(--color-blue-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-800{--tw-gradient-from:var(--color-blue-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-900{--tw-gradient-from:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-current{--tw-gradient-from:currentcolor;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-50{--tw-gradient-from:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-100{--tw-gradient-from:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-200{--tw-gradient-from:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-300{--tw-gradient-from:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-400{--tw-gradient-from:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-500{--tw-gradient-from:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-600{--tw-gradient-from:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-700{--tw-gradient-from:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-800{--tw-gradient-from:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-900{--tw-gradient-from:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-50{--tw-gradient-from:var(--color-green-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-100{--tw-gradient-from:var(--color-green-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-200{--tw-gradient-from:var(--color-green-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-300{--tw-gradient-from:var(--color-green-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-400{--tw-gradient-from:var(--color-green-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-500{--tw-gradient-from:var(--color-green-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-600{--tw-gradient-from:var(--color-green-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-700{--tw-gradient-from:var(--color-green-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-800{--tw-gradient-from:var(--color-green-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-900{--tw-gradient-from:var(--color-green-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-50{--tw-gradient-from:var(--color-indigo-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-100{--tw-gradient-from:var(--color-indigo-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-200{--tw-gradient-from:var(--color-indigo-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-300{--tw-gradient-from:var(--color-indigo-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-400{--tw-gradient-from:var(--color-indigo-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-500{--tw-gradient-from:var(--color-indigo-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-600{--tw-gradient-from:var(--color-indigo-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-700{--tw-gradient-from:var(--color-indigo-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-800{--tw-gradient-from:var(--color-indigo-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-900{--tw-gradient-from:var(--color-indigo-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-inherit{--tw-gradient-from:inherit;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-50{--tw-gradient-from:var(--color-orange-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-100{--tw-gradient-from:var(--color-orange-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-200{--tw-gradient-from:var(--color-orange-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-300{--tw-gradient-from:var(--color-orange-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-400{--tw-gradient-from:var(--color-orange-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-500{--tw-gradient-from:var(--color-orange-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-600{--tw-gradient-from:var(--color-orange-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-700{--tw-gradient-from:var(--color-orange-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-800{--tw-gradient-from:var(--color-orange-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-900{--tw-gradient-from:var(--color-orange-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-50{--tw-gradient-from:var(--color-pink-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-100{--tw-gradient-from:var(--color-pink-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-200{--tw-gradient-from:var(--color-pink-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-300{--tw-gradient-from:var(--color-pink-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-400{--tw-gradient-from:var(--color-pink-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-500{--tw-gradient-from:var(--color-pink-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-600{--tw-gradient-from:var(--color-pink-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-700{--tw-gradient-from:var(--color-pink-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-800{--tw-gradient-from:var(--color-pink-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-900{--tw-gradient-from:var(--color-pink-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-50{--tw-gradient-from:var(--color-purple-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-100{--tw-gradient-from:var(--color-purple-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-200{--tw-gradient-from:var(--color-purple-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-300{--tw-gradient-from:var(--color-purple-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-400{--tw-gradient-from:var(--color-purple-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-500{--tw-gradient-from:var(--color-purple-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-600{--tw-gradient-from:var(--color-purple-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-700{--tw-gradient-from:var(--color-purple-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-800{--tw-gradient-from:var(--color-purple-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-900{--tw-gradient-from:var(--color-purple-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-50{--tw-gradient-from:var(--color-red-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-100{--tw-gradient-from:var(--color-red-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-200{--tw-gradient-from:var(--color-red-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-300{--tw-gradient-from:var(--color-red-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-400{--tw-gradient-from:var(--color-red-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-500{--tw-gradient-from:var(--color-red-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-600{--tw-gradient-from:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-700{--tw-gradient-from:var(--color-red-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-800{--tw-gradient-from:var(--color-red-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-900{--tw-gradient-from:var(--color-red-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-50{--tw-gradient-from:var(--color-slate-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-100{--tw-gradient-from:var(--color-slate-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-200{--tw-gradient-from:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-300{--tw-gradient-from:var(--color-slate-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-400{--tw-gradient-from:var(--color-slate-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-500{--tw-gradient-from:var(--color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-600{--tw-gradient-from:var(--color-slate-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-700{--tw-gradient-from:var(--color-slate-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-800{--tw-gradient-from:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-900{--tw-gradient-from:var(--color-slate-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-transparent{--tw-gradient-from:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-white{--tw-gradient-from:var(--color-white);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-50{--tw-gradient-from:var(--color-yellow-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-100{--tw-gradient-from:var(--color-yellow-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-200{--tw-gradient-from:var(--color-yellow-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-300{--tw-gradient-from:var(--color-yellow-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-400{--tw-gradient-from:var(--color-yellow-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-500{--tw-gradient-from:var(--color-yellow-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-600{--tw-gradient-from:var(--color-yellow-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-700{--tw-gradient-from:var(--color-yellow-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-800{--tw-gradient-from:var(--color-yellow-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-900{--tw-gradient-from:var(--color-yellow-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.via-black{--tw-gradient-via:var(--color-black);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-50{--tw-gradient-via:var(--color-blue-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-100{--tw-gradient-via:var(--color-blue-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-200{--tw-gradient-via:var(--color-blue-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-300{--tw-gradient-via:var(--color-blue-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-400{--tw-gradient-via:var(--color-blue-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-500{--tw-gradient-via:var(--color-blue-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-600{--tw-gradient-via:var(--color-blue-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-700{--tw-gradient-via:var(--color-blue-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-800{--tw-gradient-via:var(--color-blue-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-900{--tw-gradient-via:var(--color-blue-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-current{--tw-gradient-via:currentcolor;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-50{--tw-gradient-via:var(--color-gray-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-100{--tw-gradient-via:var(--color-gray-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-200{--tw-gradient-via:var(--color-gray-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-300{--tw-gradient-via:var(--color-gray-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-400{--tw-gradient-via:var(--color-gray-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-500{--tw-gradient-via:var(--color-gray-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-600{--tw-gradient-via:var(--color-gray-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-700{--tw-gradient-via:var(--color-gray-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-800{--tw-gradient-via:var(--color-gray-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-900{--tw-gradient-via:var(--color-gray-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-inherit{--tw-gradient-via:inherit;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-50{--tw-gradient-via:var(--color-slate-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-100{--tw-gradient-via:var(--color-slate-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-200{--tw-gradient-via:var(--color-slate-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-300{--tw-gradient-via:var(--color-slate-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-400{--tw-gradient-via:var(--color-slate-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-500{--tw-gradient-via:var(--color-slate-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-600{--tw-gradient-via:var(--color-slate-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-700{--tw-gradient-via:var(--color-slate-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-800{--tw-gradient-via:var(--color-slate-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-900{--tw-gradient-via:var(--color-slate-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-transparent{--tw-gradient-via:transparent;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-white{--tw-gradient-via:var(--color-white);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-black{--tw-gradient-to:var(--color-black);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-50{--tw-gradient-to:var(--color-blue-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-100{--tw-gradient-to:var(--color-blue-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-200{--tw-gradient-to:var(--color-blue-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-300{--tw-gradient-to:var(--color-blue-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-400{--tw-gradient-to:var(--color-blue-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-500{--tw-gradient-to:var(--color-blue-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-600{--tw-gradient-to:var(--color-blue-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-700{--tw-gradient-to:var(--color-blue-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-800{--tw-gradient-to:var(--color-blue-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-900{--tw-gradient-to:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-current{--tw-gradient-to:currentcolor;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-100{--tw-gradient-to:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-200{--tw-gradient-to:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-300{--tw-gradient-to:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-400{--tw-gradient-to:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-500{--tw-gradient-to:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-600{--tw-gradient-to:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-700{--tw-gradient-to:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-800{--tw-gradient-to:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-900{--tw-gradient-to:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-indigo-50{--tw-gradient-to:var(--color-indigo-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-inherit{--tw-gradient-to:inherit;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-50{--tw-gradient-to:var(--color-purple-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-100{--tw-gradient-to:var(--color-purple-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-200{--tw-gradient-to:var(--color-purple-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-300{--tw-gradient-to:var(--color-purple-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-400{--tw-gradient-to:var(--color-purple-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-500{--tw-gradient-to:var(--color-purple-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-600{--tw-gradient-to:var(--color-purple-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-700{--tw-gradient-to:var(--color-purple-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-800{--tw-gradient-to:var(--color-purple-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-900{--tw-gradient-to:var(--color-purple-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-50{--tw-gradient-to:var(--color-red-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-100{--tw-gradient-to:var(--color-red-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-200{--tw-gradient-to:var(--color-red-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-300{--tw-gradient-to:var(--color-red-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-400{--tw-gradient-to:var(--color-red-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-500{--tw-gradient-to:var(--color-red-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-600{--tw-gradient-to:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-700{--tw-gradient-to:var(--color-red-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-800{--tw-gradient-to:var(--color-red-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-900{--tw-gradient-to:var(--color-red-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-50{--tw-gradient-to:var(--color-slate-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-100{--tw-gradient-to:var(--color-slate-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-200{--tw-gradient-to:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-300{--tw-gradient-to:var(--color-slate-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-400{--tw-gradient-to:var(--color-slate-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-500{--tw-gradient-to:var(--color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-600{--tw-gradient-to:var(--color-slate-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-700{--tw-gradient-to:var(--color-slate-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-800{--tw-gradient-to:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-900{--tw-gradient-to:var(--color-slate-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-white{--tw-gradient-to:var(--color-white);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.object-fill{object-fit:fill}.object-none{object-fit:none}.object-scale-down{object-fit:scale-down}.object-bottom{object-position:bottom}.object-center{object-position:center}.object-left{object-position:left}.object-left-bottom{object-position:left bottom}.object-left-top{object-position:left top}.object-right{object-position:right}.object-right-bottom{object-position:right bottom}.object-right-top{object-position:right top}.object-top{object-position:top}.\!p-0{padding:calc(var(--spacing) * 0)!important}.\!p-1{padding:calc(var(--spacing) * 1)!important}.\!p-2{padding:calc(var(--spacing) * 2)!important}.\!p-3{padding:calc(var(--spacing) * 3)!important}.\!p-4{padding:calc(var(--spacing) * 4)!important}.\!p-5{padding:calc(var(--spacing) * 5)!important}.\!p-6{padding:calc(var(--spacing) * 6)!important}.\!p-8{padding:calc(var(--spacing) * 8)!important}.\!p-10{padding:calc(var(--spacing) * 10)!important}.\!p-12{padding:calc(var(--spacing) * 12)!important}.\!p-16{padding:calc(var(--spacing) * 16)!important}.p-0{padding:calc(var(--spacing) * 0)}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-10{padding:calc(var(--spacing) * 10)}.p-12{padding:calc(var(--spacing) * 12)}.p-16{padding:calc(var(--spacing) * 16)}.p-20{padding:calc(var(--spacing) * 20)}.p-24{padding:calc(var(--spacing) * 24)}.\!px-0{padding-inline:calc(var(--spacing) * 0)!important}.\!px-1{padding-inline:calc(var(--spacing) * 1)!important}.\!px-2{padding-inline:calc(var(--spacing) * 2)!important}.\!px-3{padding-inline:calc(var(--spacing) * 3)!important}.\!px-4{padding-inline:calc(var(--spacing) * 4)!important}.\!px-5{padding-inline:calc(var(--spacing) * 5)!important}.\!px-6{padding-inline:calc(var(--spacing) * 6)!important}.\!px-8{padding-inline:calc(var(--spacing) * 8)!important}.\!px-10{padding-inline:calc(var(--spacing) * 10)!important}.\!px-12{padding-inline:calc(var(--spacing) * 12)!important}.\!px-16{padding-inline:calc(var(--spacing) * 16)!important}.px-0{padding-inline:calc(var(--spacing) * 0)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.px-10{padding-inline:calc(var(--spacing) * 10)}.px-12{padding-inline:calc(var(--spacing) * 12)}.px-16{padding-inline:calc(var(--spacing) * 16)}.\!py-0{padding-block:calc(var(--spacing) * 0)!important}.\!py-1{padding-block:calc(var(--spacing) * 1)!important}.\!py-2{padding-block:calc(var(--spacing) * 2)!important}.\!py-3{padding-block:calc(var(--spacing) * 3)!important}.\!py-4{padding-block:calc(var(--spacing) * 4)!important}.\!py-5{padding-block:calc(var(--spacing) * 5)!important}.\!py-6{padding-block:calc(var(--spacing) * 6)!important}.\!py-8{padding-block:calc(var(--spacing) * 8)!important}.\!py-10{padding-block:calc(var(--spacing) * 10)!important}.\!py-12{padding-block:calc(var(--spacing) * 12)!important}.\!py-16{padding-block:calc(var(--spacing) * 16)!important}.py-0{padding-block:calc(var(--spacing) * 0)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-0{padding-top:calc(var(--spacing) * 0)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:calc(var(--spacing) * 1)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pt-8{padding-top:calc(var(--spacing) * 8)}.pt-10{padding-top:calc(var(--spacing) * 10)}.pt-12{padding-top:calc(var(--spacing) * 12)}.pt-16{padding-top:calc(var(--spacing) * 16)}.pr-0{padding-right:calc(var(--spacing) * 0)}.pr-1{padding-right:calc(var(--spacing) * 1)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-5{padding-right:calc(var(--spacing) * 5)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-10{padding-right:calc(var(--spacing) * 10)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pr-16{padding-right:calc(var(--spacing) * 16)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pb-6{padding-bottom:calc(var(--spacing) * 6)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pb-10{padding-bottom:calc(var(--spacing) * 10)}.pb-12{padding-bottom:calc(var(--spacing) * 12)}.pb-16{padding-bottom:calc(var(--spacing) * 16)}.pb-24{padding-bottom:calc(var(--spacing) * 24)}.pl-0{padding-left:calc(var(--spacing) * 0)}.pl-1{padding-left:calc(var(--spacing) * 1)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-10{padding-left:calc(var(--spacing) * 10)}.pl-12{padding-left:calc(var(--spacing) * 12)}.pl-16{padding-left:calc(var(--spacing) * 16)}.\!text-center{text-align:center!important}.\!text-justify{text-align:justify!important}.\!text-left{text-align:left!important}.\!text-right{text-align:right!important}.text-center{text-align:center}.text-end{text-align:end}.text-justify{text-align:justify}.text-left{text-align:left}.text-right{text-align:right}.text-start{text-align:start}.indent-0{text-indent:calc(var(--spacing) * 0)}.indent-1{text-indent:calc(var(--spacing) * 1)}.indent-2{text-indent:calc(var(--spacing) * 2)}.indent-4{text-indent:calc(var(--spacing) * 4)}.indent-8{text-indent:calc(var(--spacing) * 8)}.align-baseline{vertical-align:baseline}.align-bottom{vertical-align:bottom}.align-middle{vertical-align:middle}.align-sub{vertical-align:sub}.align-super{vertical-align:super}.align-text-bottom{vertical-align:text-bottom}.align-text-top{vertical-align:text-top}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.\!text-2xl{font-size:var(--text-2xl)!important;line-height:var(--tw-leading,var(--text-2xl--line-height))!important}.\!text-3xl{font-size:var(--text-3xl)!important;line-height:var(--tw-leading,var(--text-3xl--line-height))!important}.\!text-4xl{font-size:var(--text-4xl)!important;line-height:var(--tw-leading,var(--text-4xl--line-height))!important}.\!text-base{font-size:var(--text-base)!important;line-height:var(--tw-leading,var(--text-base--line-height))!important}.\!text-lg{font-size:var(--text-lg)!important;line-height:var(--tw-leading,var(--text-lg--line-height))!important}.\!text-sm{font-size:var(--text-sm)!important;line-height:var(--tw-leading,var(--text-sm--line-height))!important}.\!text-xl{font-size:var(--text-xl)!important;line-height:var(--tw-leading,var(--text-xl--line-height))!important}.\!text-xs{font-size:var(--text-xs)!important;line-height:var(--tw-leading,var(--text-xs--line-height))!important}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.text-8xl{font-size:var(--text-8xl);line-height:var(--tw-leading,var(--text-8xl--line-height))}.text-9xl{font-size:var(--text-9xl);line-height:var(--tw-leading,var(--text-9xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[2rem\]{font-size:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-7{--tw-leading:calc(var(--spacing) * 7);line-height:calc(var(--spacing) * 7)}.leading-8{--tw-leading:calc(var(--spacing) * 8);line-height:calc(var(--spacing) * 8)}.leading-9{--tw-leading:calc(var(--spacing) * 9);line-height:calc(var(--spacing) * 9)}.leading-10{--tw-leading:calc(var(--spacing) * 10);line-height:calc(var(--spacing) * 10)}.leading-loose{--tw-leading:var(--leading-loose);line-height:var(--leading-loose)}.leading-none{--tw-leading:1;line-height:1}.leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.\!font-bold{--tw-font-weight:var(--font-weight-bold)!important;font-weight:var(--font-weight-bold)!important}.\!font-medium{--tw-font-weight:var(--font-weight-medium)!important;font-weight:var(--font-weight-medium)!important}.\!font-normal{--tw-font-weight:var(--font-weight-normal)!important;font-weight:var(--font-weight-normal)!important}.\!font-semibold{--tw-font-weight:var(--font-weight-semibold)!important;font-weight:var(--font-weight-semibold)!important}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-extralight{--tw-font-weight:var(--font-weight-extralight);font-weight:var(--font-weight-extralight)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.font-thin{--tw-font-weight:var(--font-weight-thin);font-weight:var(--font-weight-thin)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-tighter{--tw-tracking:var(--tracking-tighter);letter-spacing:var(--tracking-tighter)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-normal{overflow-wrap:normal;word-break:normal}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-break-spaces{white-space:break-spaces}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-50{color:var(--color-amber-50)}.text-amber-100{color:var(--color-amber-100)}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-black{color:var(--color-black)}.text-blue-50{color:var(--color-blue-50)}.text-blue-100{color:var(--color-blue-100)}.text-blue-200{color:var(--color-blue-200)}.text-blue-300{color:var(--color-blue-300)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-blue-950{color:var(--color-blue-950)}.text-current{color:currentColor}.text-cyan-50{color:var(--color-cyan-50)}.text-cyan-100{color:var(--color-cyan-100)}.text-cyan-200{color:var(--color-cyan-200)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-cyan-500{color:var(--color-cyan-500)}.text-cyan-600{color:var(--color-cyan-600)}.text-cyan-700{color:var(--color-cyan-700)}.text-cyan-800{color:var(--color-cyan-800)}.text-cyan-900{color:var(--color-cyan-900)}.text-cyan-950{color:var(--color-cyan-950)}.text-emerald-50{color:var(--color-emerald-50)}.text-emerald-100{color:var(--color-emerald-100)}.text-emerald-200{color:var(--color-emerald-200)}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-400{color:var(--color-emerald-400)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-emerald-950{color:var(--color-emerald-950)}.text-fuchsia-50{color:var(--color-fuchsia-50)}.text-fuchsia-100{color:var(--color-fuchsia-100)}.text-fuchsia-200{color:var(--color-fuchsia-200)}.text-fuchsia-300{color:var(--color-fuchsia-300)}.text-fuchsia-400{color:var(--color-fuchsia-400)}.text-fuchsia-500{color:var(--color-fuchsia-500)}.text-fuchsia-600{color:var(--color-fuchsia-600)}.text-fuchsia-700{color:var(--color-fuchsia-700)}.text-fuchsia-800{color:var(--color-fuchsia-800)}.text-fuchsia-900{color:var(--color-fuchsia-900)}.text-fuchsia-950{color:var(--color-fuchsia-950)}.text-gray-50{color:var(--color-gray-50)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-gray-950{color:var(--color-gray-950)}.text-green-50{color:var(--color-green-50)}.text-green-100{color:var(--color-green-100)}.text-green-200{color:var(--color-green-200)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-green-900{color:var(--color-green-900)}.text-green-950{color:var(--color-green-950)}.text-indigo-50{color:var(--color-indigo-50)}.text-indigo-100{color:var(--color-indigo-100)}.text-indigo-200{color:var(--color-indigo-200)}.text-indigo-300{color:var(--color-indigo-300)}.text-indigo-400{color:var(--color-indigo-400)}.text-indigo-500{color:var(--color-indigo-500)}.text-indigo-600{color:var(--color-indigo-600)}.text-indigo-700{color:var(--color-indigo-700)}.text-indigo-800{color:var(--color-indigo-800)}.text-indigo-900{color:var(--color-indigo-900)}.text-indigo-950{color:var(--color-indigo-950)}.text-inherit{color:inherit}.text-lime-50{color:var(--color-lime-50)}.text-lime-100{color:var(--color-lime-100)}.text-lime-200{color:var(--color-lime-200)}.text-lime-300{color:var(--color-lime-300)}.text-lime-400{color:var(--color-lime-400)}.text-lime-500{color:var(--color-lime-500)}.text-lime-600{color:var(--color-lime-600)}.text-lime-700{color:var(--color-lime-700)}.text-lime-800{color:var(--color-lime-800)}.text-lime-900{color:var(--color-lime-900)}.text-lime-950{color:var(--color-lime-950)}.text-neutral-50{color:var(--color-neutral-50)}.text-neutral-100{color:var(--color-neutral-100)}.text-neutral-200{color:var(--color-neutral-200)}.text-neutral-300{color:var(--color-neutral-300)}.text-neutral-400{color:var(--color-neutral-400)}.text-neutral-500{color:var(--color-neutral-500)}.text-neutral-600{color:var(--color-neutral-600)}.text-neutral-700{color:var(--color-neutral-700)}.text-neutral-800{color:var(--color-neutral-800)}.text-neutral-900{color:var(--color-neutral-900)}.text-neutral-950{color:var(--color-neutral-950)}.text-orange-50{color:var(--color-orange-50)}.text-orange-100{color:var(--color-orange-100)}.text-orange-200{color:var(--color-orange-200)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-orange-800{color:var(--color-orange-800)}.text-orange-900{color:var(--color-orange-900)}.text-orange-950{color:var(--color-orange-950)}.text-pink-50{color:var(--color-pink-50)}.text-pink-100{color:var(--color-pink-100)}.text-pink-200{color:var(--color-pink-200)}.text-pink-300{color:var(--color-pink-300)}.text-pink-400{color:var(--color-pink-400)}.text-pink-500{color:var(--color-pink-500)}.text-pink-600{color:var(--color-pink-600)}.text-pink-700{color:var(--color-pink-700)}.text-pink-800{color:var(--color-pink-800)}.text-pink-900{color:var(--color-pink-900)}.text-pink-950{color:var(--color-pink-950)}.text-purple-50{color:var(--color-purple-50)}.text-purple-100{color:var(--color-purple-100)}.text-purple-200{color:var(--color-purple-200)}.text-purple-300{color:var(--color-purple-300)}.text-purple-400{color:var(--color-purple-400)}.text-purple-500{color:var(--color-purple-500)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-800{color:var(--color-purple-800)}.text-purple-900{color:var(--color-purple-900)}.text-purple-950{color:var(--color-purple-950)}.text-red-50{color:var(--color-red-50)}.text-red-100{color:var(--color-red-100)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-red-950{color:var(--color-red-950)}.text-rose-50{color:var(--color-rose-50)}.text-rose-100{color:var(--color-rose-100)}.text-rose-200{color:var(--color-rose-200)}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-rose-500{color:var(--color-rose-500)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-rose-800{color:var(--color-rose-800)}.text-rose-900{color:var(--color-rose-900)}.text-rose-950{color:var(--color-rose-950)}.text-sky-50{color:var(--color-sky-50)}.text-sky-100{color:var(--color-sky-100)}.text-sky-200{color:var(--color-sky-200)}.text-sky-300{color:var(--color-sky-300)}.text-sky-400{color:var(--color-sky-400)}.text-sky-500{color:var(--color-sky-500)}.text-sky-600{color:var(--color-sky-600)}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-900{color:var(--color-sky-900)}.text-sky-950{color:var(--color-sky-950)}.text-slate-50{color:var(--color-slate-50)}.text-slate-100{color:var(--color-slate-100)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-slate-950{color:var(--color-slate-950)}.text-stone-50{color:var(--color-stone-50)}.text-stone-100{color:var(--color-stone-100)}.text-stone-200{color:var(--color-stone-200)}.text-stone-300{color:var(--color-stone-300)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-stone-950{color:var(--color-stone-950)}.text-teal-50{color:var(--color-teal-50)}.text-teal-100{color:var(--color-teal-100)}.text-teal-200{color:var(--color-teal-200)}.text-teal-300{color:var(--color-teal-300)}.text-teal-400{color:var(--color-teal-400)}.text-teal-500{color:var(--color-teal-500)}.text-teal-600{color:var(--color-teal-600)}.text-teal-700{color:var(--color-teal-700)}.text-teal-800{color:var(--color-teal-800)}.text-teal-900{color:var(--color-teal-900)}.text-teal-950{color:var(--color-teal-950)}.text-transparent{color:#0000}.text-violet-50{color:var(--color-violet-50)}.text-violet-100{color:var(--color-violet-100)}.text-violet-200{color:var(--color-violet-200)}.text-violet-300{color:var(--color-violet-300)}.text-violet-400{color:var(--color-violet-400)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.text-violet-800{color:var(--color-violet-800)}.text-violet-900{color:var(--color-violet-900)}.text-violet-950{color:var(--color-violet-950)}.text-white{color:var(--color-white)}.text-yellow-50{color:var(--color-yellow-50)}.text-yellow-100{color:var(--color-yellow-100)}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.text-yellow-800{color:var(--color-yellow-800)}.text-yellow-900{color:var(--color-yellow-900)}.text-yellow-950{color:var(--color-yellow-950)}.text-zinc-50{color:var(--color-zinc-50)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.text-zinc-950{color:var(--color-zinc-950)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.placeholder-gray-400::placeholder{color:var(--color-gray-400)}.accent-blue-500{accent-color:var(--color-blue-500)}.opacity-0{opacity:0}.opacity-5{opacity:.05}.opacity-10{opacity:.1}.opacity-15{opacity:.15}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-35{opacity:.35}.opacity-40{opacity:.4}.opacity-45{opacity:.45}.opacity-50{opacity:.5}.opacity-55{opacity:.55}.opacity-60{opacity:.6}.opacity-65{opacity:.65}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-85{opacity:.85}.opacity-90{opacity:.9}.opacity-95{opacity:.95}.opacity-100{opacity:1}.bg-blend-color{background-blend-mode:color}.bg-blend-color-burn{background-blend-mode:color-burn}.bg-blend-color-dodge{background-blend-mode:color-dodge}.bg-blend-darken{background-blend-mode:darken}.bg-blend-difference{background-blend-mode:difference}.bg-blend-exclusion{background-blend-mode:exclusion}.bg-blend-hard-light{background-blend-mode:hard-light}.bg-blend-hue{background-blend-mode:hue}.bg-blend-lighten{background-blend-mode:lighten}.bg-blend-luminosity{background-blend-mode:luminosity}.bg-blend-multiply{background-blend-mode:multiply}.bg-blend-normal{background-blend-mode:normal}.bg-blend-overlay{background-blend-mode:overlay}.bg-blend-saturation{background-blend-mode:saturation}.bg-blend-screen{background-blend-mode:screen}.bg-blend-soft-light{background-blend-mode:soft-light}.mix-blend-color{mix-blend-mode:color}.mix-blend-color-burn{mix-blend-mode:color-burn}.mix-blend-color-dodge{mix-blend-mode:color-dodge}.mix-blend-darken{mix-blend-mode:darken}.mix-blend-difference{mix-blend-mode:difference}.mix-blend-exclusion{mix-blend-mode:exclusion}.mix-blend-hard-light{mix-blend-mode:hard-light}.mix-blend-hue{mix-blend-mode:hue}.mix-blend-lighten{mix-blend-mode:lighten}.mix-blend-luminosity{mix-blend-mode:luminosity}.mix-blend-multiply{mix-blend-mode:multiply}.mix-blend-normal{mix-blend-mode:normal}.mix-blend-overlay{mix-blend-mode:overlay}.mix-blend-plus-darker{mix-blend-mode:plus-darker}.mix-blend-plus-lighter{mix-blend-mode:plus-lighter}.mix-blend-saturation{mix-blend-mode:saturation}.mix-blend-screen{mix-blend-mode:screen}.mix-blend-soft-light{mix-blend-mode:soft-light}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-4{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-8{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-purple-200{--tw-shadow-color:oklch(90.2% .063 306.703)}@supports (color:color-mix(in lab,red,red)){.shadow-purple-200{--tw-shadow-color:color-mix(in oklab, var(--color-purple-200) var(--tw-shadow-alpha), transparent)}}.ring-black{--tw-ring-color:var(--color-black)}.ring-blue-50{--tw-ring-color:var(--color-blue-50)}.ring-blue-100{--tw-ring-color:var(--color-blue-100)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-blue-300{--tw-ring-color:var(--color-blue-300)}.ring-blue-400{--tw-ring-color:var(--color-blue-400)}.ring-blue-500{--tw-ring-color:var(--color-blue-500)}.ring-blue-600{--tw-ring-color:var(--color-blue-600)}.ring-blue-700{--tw-ring-color:var(--color-blue-700)}.ring-blue-800{--tw-ring-color:var(--color-blue-800)}.ring-blue-900{--tw-ring-color:var(--color-blue-900)}.ring-current{--tw-ring-color:currentcolor}.ring-gray-50{--tw-ring-color:var(--color-gray-50)}.ring-gray-100{--tw-ring-color:var(--color-gray-100)}.ring-gray-200{--tw-ring-color:var(--color-gray-200)}.ring-gray-300{--tw-ring-color:var(--color-gray-300)}.ring-gray-400{--tw-ring-color:var(--color-gray-400)}.ring-gray-500{--tw-ring-color:var(--color-gray-500)}.ring-gray-600{--tw-ring-color:var(--color-gray-600)}.ring-gray-700{--tw-ring-color:var(--color-gray-700)}.ring-gray-800{--tw-ring-color:var(--color-gray-800)}.ring-gray-900{--tw-ring-color:var(--color-gray-900)}.ring-inherit{--tw-ring-color:inherit}.ring-slate-50{--tw-ring-color:var(--color-slate-50)}.ring-slate-100{--tw-ring-color:var(--color-slate-100)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.ring-slate-300{--tw-ring-color:var(--color-slate-300)}.ring-slate-400{--tw-ring-color:var(--color-slate-400)}.ring-slate-500{--tw-ring-color:var(--color-slate-500)}.ring-slate-600{--tw-ring-color:var(--color-slate-600)}.ring-slate-700{--tw-ring-color:var(--color-slate-700)}.ring-slate-800{--tw-ring-color:var(--color-slate-800)}.ring-slate-900{--tw-ring-color:var(--color-slate-900)}.ring-transparent{--tw-ring-color:transparent}.ring-white{--tw-ring-color:var(--color-white)}.ring-offset-0{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-4{--tw-ring-offset-width:4px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-8{--tw-ring-offset-width:8px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-black{--tw-ring-offset-color:var(--color-black)}.ring-offset-current{--tw-ring-offset-color:currentcolor}.ring-offset-inherit{--tw-ring-offset-color:inherit}.ring-offset-transparent{--tw-ring-offset-color:transparent}.ring-offset-white{--tw-ring-offset-color:var(--color-white)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-0{outline-style:var(--tw-outline-style);outline-width:0}.outline-1{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-4{outline-style:var(--tw-outline-style);outline-width:4px}.outline-8{outline-style:var(--tw-outline-style);outline-width:8px}.outline-offset-0{outline-offset:0px}.outline-offset-1{outline-offset:1px}.outline-offset-2{outline-offset:2px}.outline-offset-4{outline-offset:4px}.outline-offset-8{outline-offset:8px}.outline-black{outline-color:var(--color-black)}.outline-current{outline-color:currentColor}.outline-inherit{outline-color:inherit}.outline-transparent{outline-color:#0000}.outline-white{outline-color:var(--color-white)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-2xl{--tw-blur:blur(var(--blur-2xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-3xl{--tw-blur:blur(var(--blur-3xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-lg{--tw-blur:blur(var(--blur-lg));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-md{--tw-blur:blur(var(--blur-md));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-none{--tw-blur: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-sm{--tw-blur:blur(var(--blur-sm));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-xl{--tw-blur:blur(var(--blur-xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-0{--tw-brightness:brightness(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-50{--tw-brightness:brightness(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-75{--tw-brightness:brightness(75%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-90{--tw-brightness:brightness(90%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-95{--tw-brightness:brightness(95%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-100{--tw-brightness:brightness(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-105{--tw-brightness:brightness(105%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-110{--tw-brightness:brightness(110%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-125{--tw-brightness:brightness(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-150{--tw-brightness:brightness(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-200{--tw-brightness:brightness(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-0{--tw-contrast:contrast(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-50{--tw-contrast:contrast(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-75{--tw-contrast:contrast(75%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-100{--tw-contrast:contrast(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-125{--tw-contrast:contrast(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-150{--tw-contrast:contrast(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-200{--tw-contrast:contrast(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-2xl{--tw-drop-shadow-size:drop-shadow(0 25px 25px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-2xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-md{--tw-drop-shadow-size:drop-shadow(0 3px 3px var(--tw-drop-shadow-color,#0000001f));--tw-drop-shadow:drop-shadow(var(--drop-shadow-md));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-sm{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-sm));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-xl{--tw-drop-shadow-size:drop-shadow(0 9px 7px var(--tw-drop-shadow-color,#0000001a));--tw-drop-shadow:drop-shadow(var(--drop-shadow-xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-none{--tw-drop-shadow: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.grayscale{--tw-grayscale:grayscale(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.grayscale-0{--tw-grayscale:grayscale(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-15{--tw-hue-rotate:hue-rotate( -15deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-30{--tw-hue-rotate:hue-rotate( -30deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-60{--tw-hue-rotate:hue-rotate( -60deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-90{--tw-hue-rotate:hue-rotate( -90deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-180{--tw-hue-rotate:hue-rotate( -180deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-0{--tw-hue-rotate:hue-rotate(0deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-15{--tw-hue-rotate:hue-rotate(15deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-30{--tw-hue-rotate:hue-rotate(30deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-60{--tw-hue-rotate:hue-rotate(60deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-90{--tw-hue-rotate:hue-rotate(90deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-180{--tw-hue-rotate:hue-rotate(180deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert-0{--tw-invert:invert(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-0{--tw-saturate:saturate(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-50{--tw-saturate:saturate(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-100{--tw-saturate:saturate(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-150{--tw-saturate:saturate(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-200{--tw-saturate:saturate(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.sepia{--tw-sepia:sepia(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.sepia-0{--tw-sepia:sepia(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-2xl{--tw-backdrop-blur:blur(var(--blur-2xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-3xl{--tw-backdrop-blur:blur(var(--blur-3xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-none{--tw-backdrop-blur: ;-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-0{--tw-backdrop-brightness:brightness(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-50{--tw-backdrop-brightness:brightness(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-75{--tw-backdrop-brightness:brightness(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-90{--tw-backdrop-brightness:brightness(90%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-95{--tw-backdrop-brightness:brightness(95%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-100{--tw-backdrop-brightness:brightness(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-105{--tw-backdrop-brightness:brightness(105%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-110{--tw-backdrop-brightness:brightness(110%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-125{--tw-backdrop-brightness:brightness(125%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-150{--tw-backdrop-brightness:brightness(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-200{--tw-backdrop-brightness:brightness(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-0{--tw-backdrop-contrast:contrast(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-50{--tw-backdrop-contrast:contrast(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-75{--tw-backdrop-contrast:contrast(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-100{--tw-backdrop-contrast:contrast(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-125{--tw-backdrop-contrast:contrast(125%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-150{--tw-backdrop-contrast:contrast(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-200{--tw-backdrop-contrast:contrast(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale-0{--tw-backdrop-grayscale:grayscale(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-0{--tw-backdrop-hue-rotate:hue-rotate(0deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-15{--tw-backdrop-hue-rotate:hue-rotate(15deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-30{--tw-backdrop-hue-rotate:hue-rotate(30deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-60{--tw-backdrop-hue-rotate:hue-rotate(60deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-90{--tw-backdrop-hue-rotate:hue-rotate(90deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-180{--tw-backdrop-hue-rotate:hue-rotate(180deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert-0{--tw-backdrop-invert:invert(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-0{--tw-backdrop-opacity:opacity(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-5{--tw-backdrop-opacity:opacity(5%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-10{--tw-backdrop-opacity:opacity(10%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-20{--tw-backdrop-opacity:opacity(20%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-25{--tw-backdrop-opacity:opacity(25%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-30{--tw-backdrop-opacity:opacity(30%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-40{--tw-backdrop-opacity:opacity(40%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-50{--tw-backdrop-opacity:opacity(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-60{--tw-backdrop-opacity:opacity(60%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-70{--tw-backdrop-opacity:opacity(70%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-75{--tw-backdrop-opacity:opacity(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-80{--tw-backdrop-opacity:opacity(80%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-90{--tw-backdrop-opacity:opacity(90%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-95{--tw-backdrop-opacity:opacity(95%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-100{--tw-backdrop-opacity:opacity(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-0{--tw-backdrop-saturate:saturate(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-50{--tw-backdrop-saturate:saturate(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-100{--tw-backdrop-saturate:saturate(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-150{--tw-backdrop-saturate:saturate(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-200{--tw-backdrop-saturate:saturate(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia-0{--tw-backdrop-sepia:sepia(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.delay-75{transition-delay:75ms}.delay-100{transition-delay:.1s}.delay-150{transition-delay:.15s}.delay-200{transition-delay:.2s}.delay-300{transition-delay:.3s}.delay-500{transition-delay:.5s}.delay-700{transition-delay:.7s}.delay-1000{transition-delay:1s}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.duration-1000{--tw-duration:1s;transition-duration:1s}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-dashed{--tw-outline-style:dashed;outline-style:dashed}.outline-dotted{--tw-outline-style:dotted;outline-style:dotted}.outline-double{--tw-outline-style:double;outline-style:double}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.select-auto{-webkit-user-select:auto;user-select:auto}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}@media(hover:hover){.group-hover\:visible:is(:where(.group):hover *){visibility:visible}.group-hover\:translate-x-1:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:translate-y-1:is(:where(.group):hover *){--tw-translate-y:calc(var(--spacing) * 1);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:scale-100:is(:where(.group):hover *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:bg-black\/40:is(:where(.group):hover *){background-color:#0006}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-black\/40:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--color-black) 40%,transparent)}}.group-hover\:bg-blue-100:is(:where(.group):hover *){background-color:var(--color-blue-100)}.group-hover\:bg-blue-500:is(:where(.group):hover *){background-color:var(--color-blue-500)}.group-hover\:bg-blue-600:is(:where(.group):hover *){background-color:var(--color-blue-600)}.group-hover\:bg-gray-100:is(:where(.group):hover *){background-color:var(--color-gray-100)}.group-hover\:bg-gray-200:is(:where(.group):hover *){background-color:var(--color-gray-200)}.group-hover\:bg-gray-700:is(:where(.group):hover *){background-color:var(--color-gray-700)}.group-hover\:bg-gray-800:is(:where(.group):hover *){background-color:var(--color-gray-800)}.group-hover\:text-black:is(:where(.group):hover *){color:var(--color-black)}.group-hover\:text-blue-500:is(:where(.group):hover *){color:var(--color-blue-500)}.group-hover\:text-blue-600:is(:where(.group):hover *){color:var(--color-blue-600)}.group-hover\:text-blue-700:is(:where(.group):hover *){color:var(--color-blue-700)}.group-hover\:text-gray-700:is(:where(.group):hover *){color:var(--color-gray-700)}.group-hover\:text-gray-800:is(:where(.group):hover *){color:var(--color-gray-800)}.group-hover\:text-gray-900:is(:where(.group):hover *){color:var(--color-gray-900)}.group-hover\:text-white:is(:where(.group):hover *){color:var(--color-white)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-focus\:border-blue-500:is(:where(.group):focus *){border-color:var(--color-blue-500)}.group-focus\:ring-2:is(:where(.group):focus *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.peer-checked\:border-blue-500:is(:where(.peer):checked~*){border-color:var(--color-blue-500)}.peer-checked\:bg-blue-500:is(:where(.peer):checked~*){background-color:var(--color-blue-500)}.peer-invalid\:border-red-500:is(:where(.peer):invalid~*){border-color:var(--color-red-500)}.peer-invalid\:text-red-500:is(:where(.peer):invalid~*){color:var(--color-red-500)}.peer-focus\:border-blue-500:is(:where(.peer):focus~*){border-color:var(--color-blue-500)}.peer-focus\:ring-2:is(:where(.peer):focus~*){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.first-letter\:text-2xl:first-letter{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.first-letter\:font-bold:first-letter{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.first-letter\:text-blue-600:first-letter{color:var(--color-blue-600)}.first-line\:font-semibold:first-line{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.first-line\:text-gray-700:first-line{color:var(--color-gray-700)}.marker\:text-blue-500 ::marker{color:var(--color-blue-500)}.marker\:text-blue-500::marker{color:var(--color-blue-500)}.marker\:text-blue-500 ::-webkit-details-marker{color:var(--color-blue-500)}.marker\:text-blue-500::-webkit-details-marker{color:var(--color-blue-500)}.marker\:text-gray-500 ::marker{color:var(--color-gray-500)}.marker\:text-gray-500::marker{color:var(--color-gray-500)}.marker\:text-gray-500 ::-webkit-details-marker{color:var(--color-gray-500)}.marker\:text-gray-500::-webkit-details-marker{color:var(--color-gray-500)}.selection\:bg-blue-200 ::selection{background-color:var(--color-blue-200)}.selection\:bg-blue-200::selection{background-color:var(--color-blue-200)}.selection\:text-blue-900 ::selection{color:var(--color-blue-900)}.selection\:text-blue-900::selection{color:var(--color-blue-900)}.file\:mr-4::file-selector-button{margin-right:calc(var(--spacing) * 4)}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:rounded-full::file-selector-button{border-radius:3.40282e38px}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-blue-50::file-selector-button{background-color:var(--color-blue-50)}.file\:px-4::file-selector-button{padding-inline:calc(var(--spacing) * 4)}.file\:py-2::file-selector-button{padding-block:calc(var(--spacing) * 2)}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-semibold::file-selector-button{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.file\:text-blue-700::file-selector-button{color:var(--color-blue-700)}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:inset-0:before{content:var(--tw-content);inset:calc(var(--spacing) * 0)}.before\:block:before{content:var(--tw-content);display:block}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-0:after{content:var(--tw-content);inset:calc(var(--spacing) * 0)}.after\:clear-both:after{content:var(--tw-content);clear:both}.after\:block:after{content:var(--tw-content);display:block}.after\:h-4:after{content:var(--tw-content);height:calc(var(--spacing) * 4)}.after\:h-5:after{content:var(--tw-content);height:calc(var(--spacing) * 5)}.after\:h-6:after{content:var(--tw-content);height:calc(var(--spacing) * 6)}.after\:w-4:after{content:var(--tw-content);width:calc(var(--spacing) * 4)}.after\:w-5:after{content:var(--tw-content);width:calc(var(--spacing) * 5)}.after\:w-6:after{content:var(--tw-content);width:calc(var(--spacing) * 6)}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.first\:mt-0:first-child{margin-top:calc(var(--spacing) * 0)}.first\:rounded-t-lg:first-child{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.first\:rounded-l:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.first\:rounded-l-lg:first-child{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.first\:pt-0:first-child{padding-top:calc(var(--spacing) * 0)}.last\:mb-0:last-child{margin-bottom:calc(var(--spacing) * 0)}.last\:rounded-r:last-child{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.last\:rounded-r-lg:last-child{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.last\:rounded-b-lg:last-child{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.last\:border-r:last-child{border-right-style:var(--tw-border-style);border-right-width:1px}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:calc(var(--spacing) * 0)}.odd\:bg-gray-50:nth-child(odd){background-color:var(--color-gray-50)}.odd\:bg-gray-100:nth-child(odd){background-color:var(--color-gray-100)}.odd\:bg-white:nth-child(odd){background-color:var(--color-white)}.even\:bg-gray-50:nth-child(2n){background-color:var(--color-gray-50)}.even\:bg-gray-100:nth-child(2n){background-color:var(--color-gray-100)}.even\:bg-white:nth-child(2n){background-color:var(--color-white)}.checked\:border-blue-500:checked{border-color:var(--color-blue-500)}.checked\:border-blue-600:checked{border-color:var(--color-blue-600)}.checked\:bg-blue-500:checked{background-color:var(--color-blue-500)}.checked\:bg-blue-600:checked{background-color:var(--color-blue-600)}.checked\:bg-green-500:checked{background-color:var(--color-green-500)}.checked\:text-white:checked{color:var(--color-white)}.indeterminate\:border-gray-400:indeterminate{border-color:var(--color-gray-400)}.indeterminate\:bg-gray-300:indeterminate{background-color:var(--color-gray-300)}.placeholder-shown\:border-gray-300:placeholder-shown{border-color:var(--color-gray-300)}.autofill\:bg-yellow-100:autofill{background-color:var(--color-yellow-100)}.autofill\:text-black:autofill{color:var(--color-black)}.valid\:border-green-500:valid{border-color:var(--color-green-500)}.valid\:text-green-600:valid{color:var(--color-green-600)}.valid\:ring-green-500:valid{--tw-ring-color:var(--color-green-500)}.invalid\:border-red-500:invalid{border-color:var(--color-red-500)}.invalid\:text-red-600:invalid{color:var(--color-red-600)}.invalid\:ring-red-500:invalid{--tw-ring-color:var(--color-red-500)}.read-only\:cursor-not-allowed:read-only{cursor:not-allowed}.read-only\:bg-gray-50:read-only{background-color:var(--color-gray-50)}.read-only\:bg-gray-100:read-only{background-color:var(--color-gray-100)}.empty\:hidden:empty{display:none}.focus-within\:border-blue-500:focus-within{border-color:var(--color-blue-500)}.focus-within\:border-gray-500:focus-within{border-color:var(--color-gray-500)}.focus-within\:ring-2:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\:ring-blue-500:focus-within{--tw-ring-color:var(--color-blue-500)}.focus-within\:ring-gray-500:focus-within{--tw-ring-color:var(--color-gray-500)}@media(hover:hover){.hover\:-translate-y-0\.5:hover{--tw-translate-y:calc(var(--spacing) * -.5);translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:scale-95:hover{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-100:hover{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:border-blue-50:hover{border-color:var(--color-blue-50)}.hover\:border-blue-100:hover{border-color:var(--color-blue-100)}.hover\:border-blue-200:hover{border-color:var(--color-blue-200)}.hover\:border-blue-300:hover{border-color:var(--color-blue-300)}.hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.hover\:border-blue-600:hover{border-color:var(--color-blue-600)}.hover\:border-blue-700:hover{border-color:var(--color-blue-700)}.hover\:border-blue-800:hover{border-color:var(--color-blue-800)}.hover\:border-blue-900:hover{border-color:var(--color-blue-900)}.hover\:border-gray-50:hover{border-color:var(--color-gray-50)}.hover\:border-gray-100:hover{border-color:var(--color-gray-100)}.hover\:border-gray-200:hover{border-color:var(--color-gray-200)}.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:border-gray-400:hover{border-color:var(--color-gray-400)}.hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.hover\:border-gray-600:hover{border-color:var(--color-gray-600)}.hover\:border-gray-700:hover{border-color:var(--color-gray-700)}.hover\:border-gray-800:hover{border-color:var(--color-gray-800)}.hover\:border-gray-900:hover{border-color:var(--color-gray-900)}.hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-black:hover{background-color:var(--color-black)}.hover\:bg-black\/10:hover{background-color:#0000001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/10:hover{background-color:color-mix(in oklab,var(--color-black) 10%,transparent)}}.hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.hover\:bg-blue-100:hover{background-color:var(--color-blue-100)}.hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.hover\:bg-blue-300:hover{background-color:var(--color-blue-300)}.hover\:bg-blue-400:hover{background-color:var(--color-blue-400)}.hover\:bg-blue-500:hover{background-color:var(--color-blue-500)}.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-blue-800:hover{background-color:var(--color-blue-800)}.hover\:bg-blue-900:hover{background-color:var(--color-blue-900)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.hover\:bg-gray-400:hover{background-color:var(--color-gray-400)}.hover\:bg-gray-500:hover{background-color:var(--color-gray-500)}.hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}.hover\:bg-gray-900:hover{background-color:var(--color-gray-900)}.hover\:bg-green-50:hover{background-color:var(--color-green-50)}.hover\:bg-green-100:hover{background-color:var(--color-green-100)}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}.hover\:bg-green-300:hover{background-color:var(--color-green-300)}.hover\:bg-green-400:hover{background-color:var(--color-green-400)}.hover\:bg-green-500:hover{background-color:var(--color-green-500)}.hover\:bg-green-600:hover{background-color:var(--color-green-600)}.hover\:bg-green-700:hover{background-color:var(--color-green-700)}.hover\:bg-green-800:hover{background-color:var(--color-green-800)}.hover\:bg-green-900:hover{background-color:var(--color-green-900)}.hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.hover\:bg-indigo-100:hover{background-color:var(--color-indigo-100)}.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}.hover\:bg-indigo-300:hover{background-color:var(--color-indigo-300)}.hover\:bg-indigo-400:hover{background-color:var(--color-indigo-400)}.hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.hover\:bg-indigo-600:hover{background-color:var(--color-indigo-600)}.hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.hover\:bg-indigo-800:hover{background-color:var(--color-indigo-800)}.hover\:bg-indigo-900:hover{background-color:var(--color-indigo-900)}.hover\:bg-orange-50:hover{background-color:var(--color-orange-50)}.hover\:bg-orange-100:hover{background-color:var(--color-orange-100)}.hover\:bg-orange-200:hover{background-color:var(--color-orange-200)}.hover\:bg-orange-300:hover{background-color:var(--color-orange-300)}.hover\:bg-orange-400:hover{background-color:var(--color-orange-400)}.hover\:bg-orange-500:hover{background-color:var(--color-orange-500)}.hover\:bg-orange-600:hover{background-color:var(--color-orange-600)}.hover\:bg-orange-700:hover{background-color:var(--color-orange-700)}.hover\:bg-orange-800:hover{background-color:var(--color-orange-800)}.hover\:bg-orange-900:hover{background-color:var(--color-orange-900)}.hover\:bg-pink-50:hover{background-color:var(--color-pink-50)}.hover\:bg-pink-100:hover{background-color:var(--color-pink-100)}.hover\:bg-pink-200:hover{background-color:var(--color-pink-200)}.hover\:bg-pink-300:hover{background-color:var(--color-pink-300)}.hover\:bg-pink-400:hover{background-color:var(--color-pink-400)}.hover\:bg-pink-500:hover{background-color:var(--color-pink-500)}.hover\:bg-pink-600:hover{background-color:var(--color-pink-600)}.hover\:bg-pink-700:hover{background-color:var(--color-pink-700)}.hover\:bg-pink-800:hover{background-color:var(--color-pink-800)}.hover\:bg-pink-900:hover{background-color:var(--color-pink-900)}.hover\:bg-purple-50:hover{background-color:var(--color-purple-50)}.hover\:bg-purple-100:hover{background-color:var(--color-purple-100)}.hover\:bg-purple-200:hover{background-color:var(--color-purple-200)}.hover\:bg-purple-300:hover{background-color:var(--color-purple-300)}.hover\:bg-purple-400:hover{background-color:var(--color-purple-400)}.hover\:bg-purple-500:hover{background-color:var(--color-purple-500)}.hover\:bg-purple-600:hover{background-color:var(--color-purple-600)}.hover\:bg-purple-700:hover{background-color:var(--color-purple-700)}.hover\:bg-purple-800:hover{background-color:var(--color-purple-800)}.hover\:bg-purple-900:hover{background-color:var(--color-purple-900)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-100:hover{background-color:var(--color-red-100)}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-red-300:hover{background-color:var(--color-red-300)}.hover\:bg-red-400:hover{background-color:var(--color-red-400)}.hover\:bg-red-500:hover{background-color:var(--color-red-500)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-red-800:hover{background-color:var(--color-red-800)}.hover\:bg-red-900:hover{background-color:var(--color-red-900)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:bg-slate-300:hover{background-color:var(--color-slate-300)}.hover\:bg-slate-400:hover{background-color:var(--color-slate-400)}.hover\:bg-slate-500:hover{background-color:var(--color-slate-500)}.hover\:bg-slate-600:hover{background-color:var(--color-slate-600)}.hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-slate-900:hover{background-color:var(--color-slate-900)}.hover\:bg-transparent:hover{background-color:#0000}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:bg-yellow-50:hover{background-color:var(--color-yellow-50)}.hover\:bg-yellow-100:hover{background-color:var(--color-yellow-100)}.hover\:bg-yellow-200:hover{background-color:var(--color-yellow-200)}.hover\:bg-yellow-300:hover{background-color:var(--color-yellow-300)}.hover\:bg-yellow-400:hover{background-color:var(--color-yellow-400)}.hover\:bg-yellow-500:hover{background-color:var(--color-yellow-500)}.hover\:bg-yellow-600:hover{background-color:var(--color-yellow-600)}.hover\:bg-yellow-700:hover{background-color:var(--color-yellow-700)}.hover\:bg-yellow-800:hover{background-color:var(--color-yellow-800)}.hover\:bg-yellow-900:hover{background-color:var(--color-yellow-900)}.hover\:text-black:hover{color:var(--color-black)}.hover\:text-blue-50:hover{color:var(--color-blue-50)}.hover\:text-blue-100:hover{color:var(--color-blue-100)}.hover\:text-blue-200:hover{color:var(--color-blue-200)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-blue-400:hover{color:var(--color-blue-400)}.hover\:text-blue-500:hover{color:var(--color-blue-500)}.hover\:text-blue-600:hover{color:var(--color-blue-600)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-blue-800:hover{color:var(--color-blue-800)}.hover\:text-blue-900:hover{color:var(--color-blue-900)}.hover\:text-gray-50:hover{color:var(--color-gray-50)}.hover\:text-gray-100:hover{color:var(--color-gray-100)}.hover\:text-gray-200:hover{color:var(--color-gray-200)}.hover\:text-gray-300:hover{color:var(--color-gray-300)}.hover\:text-gray-400:hover{color:var(--color-gray-400)}.hover\:text-gray-500:hover{color:var(--color-gray-500)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-800:hover{color:var(--color-gray-800)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-green-50:hover{color:var(--color-green-50)}.hover\:text-green-100:hover{color:var(--color-green-100)}.hover\:text-green-200:hover{color:var(--color-green-200)}.hover\:text-green-300:hover{color:var(--color-green-300)}.hover\:text-green-400:hover{color:var(--color-green-400)}.hover\:text-green-500:hover{color:var(--color-green-500)}.hover\:text-green-600:hover{color:var(--color-green-600)}.hover\:text-green-700:hover{color:var(--color-green-700)}.hover\:text-green-800:hover{color:var(--color-green-800)}.hover\:text-green-900:hover{color:var(--color-green-900)}.hover\:text-purple-50:hover{color:var(--color-purple-50)}.hover\:text-purple-100:hover{color:var(--color-purple-100)}.hover\:text-purple-200:hover{color:var(--color-purple-200)}.hover\:text-purple-300:hover{color:var(--color-purple-300)}.hover\:text-purple-400:hover{color:var(--color-purple-400)}.hover\:text-purple-500:hover{color:var(--color-purple-500)}.hover\:text-purple-600:hover{color:var(--color-purple-600)}.hover\:text-purple-700:hover{color:var(--color-purple-700)}.hover\:text-purple-800:hover{color:var(--color-purple-800)}.hover\:text-purple-900:hover{color:var(--color-purple-900)}.hover\:text-red-50:hover{color:var(--color-red-50)}.hover\:text-red-100:hover{color:var(--color-red-100)}.hover\:text-red-200:hover{color:var(--color-red-200)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-red-800:hover{color:var(--color-red-800)}.hover\:text-red-900:hover{color:var(--color-red-900)}.hover\:text-slate-50:hover{color:var(--color-slate-50)}.hover\:text-slate-100:hover{color:var(--color-slate-100)}.hover\:text-slate-200:hover{color:var(--color-slate-200)}.hover\:text-slate-300:hover{color:var(--color-slate-300)}.hover\:text-slate-400:hover{color:var(--color-slate-400)}.hover\:text-slate-500:hover{color:var(--color-slate-500)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}.hover\:text-white:hover{color:var(--color-white)}.hover\:line-through:hover{text-decoration-line:line-through}.hover\:no-underline:hover{text-decoration-line:none}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-0:hover{opacity:0}.hover\:opacity-25:hover{opacity:.25}.hover\:opacity-50:hover{opacity:.5}.hover\:opacity-75:hover{opacity:.75}.hover\:opacity-100:hover{opacity:1}.hover\:shadow:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-none:hover{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-0:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-1:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-4:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-8:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-blue-500:hover{--tw-ring-color:var(--color-blue-500)}.hover\:ring-gray-500:hover{--tw-ring-color:var(--color-gray-500)}.hover\:ring-green-500:hover{--tw-ring-color:var(--color-green-500)}.hover\:ring-red-500:hover{--tw-ring-color:var(--color-red-500)}.file\:hover\:bg-blue-100::file-selector-button:hover{background-color:var(--color-blue-100)}}.focus\:border-blue-50:focus{border-color:var(--color-blue-50)}.focus\:border-blue-100:focus{border-color:var(--color-blue-100)}.focus\:border-blue-200:focus{border-color:var(--color-blue-200)}.focus\:border-blue-300:focus{border-color:var(--color-blue-300)}.focus\:border-blue-400:focus{border-color:var(--color-blue-400)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:border-blue-600:focus{border-color:var(--color-blue-600)}.focus\:border-blue-700:focus{border-color:var(--color-blue-700)}.focus\:border-blue-800:focus{border-color:var(--color-blue-800)}.focus\:border-blue-900:focus{border-color:var(--color-blue-900)}.focus\:border-gray-50:focus{border-color:var(--color-gray-50)}.focus\:border-gray-100:focus{border-color:var(--color-gray-100)}.focus\:border-gray-200:focus{border-color:var(--color-gray-200)}.focus\:border-gray-300:focus{border-color:var(--color-gray-300)}.focus\:border-gray-400:focus{border-color:var(--color-gray-400)}.focus\:border-gray-500:focus{border-color:var(--color-gray-500)}.focus\:border-gray-600:focus{border-color:var(--color-gray-600)}.focus\:border-gray-700:focus{border-color:var(--color-gray-700)}.focus\:border-gray-800:focus{border-color:var(--color-gray-800)}.focus\:border-gray-900:focus{border-color:var(--color-gray-900)}.focus\:border-green-500:focus{border-color:var(--color-green-500)}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:border-purple-500:focus{border-color:var(--color-purple-500)}.focus\:border-red-500:focus{border-color:var(--color-red-500)}.focus\:border-transparent:focus{border-color:#0000}.focus\:border-yellow-500:focus{border-color:var(--color-yellow-500)}.focus\:bg-black:focus{background-color:var(--color-black)}.focus\:bg-blue-50:focus{background-color:var(--color-blue-50)}.focus\:bg-blue-100:focus{background-color:var(--color-blue-100)}.focus\:bg-blue-200:focus{background-color:var(--color-blue-200)}.focus\:bg-blue-300:focus{background-color:var(--color-blue-300)}.focus\:bg-blue-400:focus{background-color:var(--color-blue-400)}.focus\:bg-blue-500:focus{background-color:var(--color-blue-500)}.focus\:bg-blue-600:focus{background-color:var(--color-blue-600)}.focus\:bg-blue-700:focus{background-color:var(--color-blue-700)}.focus\:bg-blue-800:focus{background-color:var(--color-blue-800)}.focus\:bg-blue-900:focus{background-color:var(--color-blue-900)}.focus\:bg-gray-50:focus{background-color:var(--color-gray-50)}.focus\:bg-gray-100:focus{background-color:var(--color-gray-100)}.focus\:bg-gray-200:focus{background-color:var(--color-gray-200)}.focus\:bg-gray-300:focus{background-color:var(--color-gray-300)}.focus\:bg-gray-400:focus{background-color:var(--color-gray-400)}.focus\:bg-gray-500:focus{background-color:var(--color-gray-500)}.focus\:bg-gray-600:focus{background-color:var(--color-gray-600)}.focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.focus\:bg-gray-800:focus{background-color:var(--color-gray-800)}.focus\:bg-gray-900:focus{background-color:var(--color-gray-900)}.focus\:bg-transparent:focus{background-color:#0000}.focus\:bg-white:focus{background-color:var(--color-white)}.focus\:text-black:focus{color:var(--color-black)}.focus\:text-blue-50:focus{color:var(--color-blue-50)}.focus\:text-blue-100:focus{color:var(--color-blue-100)}.focus\:text-blue-200:focus{color:var(--color-blue-200)}.focus\:text-blue-300:focus{color:var(--color-blue-300)}.focus\:text-blue-400:focus{color:var(--color-blue-400)}.focus\:text-blue-500:focus{color:var(--color-blue-500)}.focus\:text-blue-600:focus{color:var(--color-blue-600)}.focus\:text-blue-700:focus{color:var(--color-blue-700)}.focus\:text-blue-800:focus{color:var(--color-blue-800)}.focus\:text-blue-900:focus{color:var(--color-blue-900)}.focus\:text-gray-50:focus{color:var(--color-gray-50)}.focus\:text-gray-100:focus{color:var(--color-gray-100)}.focus\:text-gray-200:focus{color:var(--color-gray-200)}.focus\:text-gray-300:focus{color:var(--color-gray-300)}.focus\:text-gray-400:focus{color:var(--color-gray-400)}.focus\:text-gray-500:focus{color:var(--color-gray-500)}.focus\:text-gray-600:focus{color:var(--color-gray-600)}.focus\:text-gray-700:focus{color:var(--color-gray-700)}.focus\:text-gray-800:focus{color:var(--color-gray-800)}.focus\:text-gray-900:focus{color:var(--color-gray-900)}.focus\:text-white:focus{color:var(--color-white)}.focus\:shadow:focus{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-lg:focus{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-md:focus{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-none:focus{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-sm:focus{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-xl:focus{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-0:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-4:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-8:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-amber-500:focus{--tw-ring-color:var(--color-amber-500)}.focus\:ring-blue-50:focus{--tw-ring-color:var(--color-blue-50)}.focus\:ring-blue-100:focus{--tw-ring-color:var(--color-blue-100)}.focus\:ring-blue-200:focus{--tw-ring-color:var(--color-blue-200)}.focus\:ring-blue-300:focus{--tw-ring-color:var(--color-blue-300)}.focus\:ring-blue-400:focus{--tw-ring-color:var(--color-blue-400)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-blue-600:focus{--tw-ring-color:var(--color-blue-600)}.focus\:ring-blue-700:focus{--tw-ring-color:var(--color-blue-700)}.focus\:ring-blue-800:focus{--tw-ring-color:var(--color-blue-800)}.focus\:ring-blue-900:focus{--tw-ring-color:var(--color-blue-900)}.focus\:ring-current:focus{--tw-ring-color:currentcolor}.focus\:ring-gray-50:focus{--tw-ring-color:var(--color-gray-50)}.focus\:ring-gray-100:focus{--tw-ring-color:var(--color-gray-100)}.focus\:ring-gray-200:focus{--tw-ring-color:var(--color-gray-200)}.focus\:ring-gray-300:focus{--tw-ring-color:var(--color-gray-300)}.focus\:ring-gray-400:focus{--tw-ring-color:var(--color-gray-400)}.focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.focus\:ring-gray-600:focus{--tw-ring-color:var(--color-gray-600)}.focus\:ring-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.focus\:ring-gray-800:focus{--tw-ring-color:var(--color-gray-800)}.focus\:ring-gray-900:focus{--tw-ring-color:var(--color-gray-900)}.focus\:ring-green-500:focus{--tw-ring-color:var(--color-green-500)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:ring-purple-500:focus{--tw-ring-color:var(--color-purple-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-slate-400:focus{--tw-ring-color:var(--color-slate-400)}.focus\:ring-transparent:focus{--tw-ring-color:transparent}.focus\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-4:focus{--tw-ring-offset-width:4px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-gray-100:focus{--tw-ring-offset-color:var(--color-gray-100)}.focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.focus\:ring-offset-white:focus{--tw-ring-offset-color:var(--color-white)}.focus\:outline:focus{outline-style:var(--tw-outline-style);outline-width:1px}.focus\:outline-0:focus{outline-style:var(--tw-outline-style);outline-width:0}.focus\:outline-1:focus{outline-style:var(--tw-outline-style);outline-width:1px}.focus\:outline-2:focus{outline-style:var(--tw-outline-style);outline-width:2px}.focus\:outline-4:focus{outline-style:var(--tw-outline-style);outline-width:4px}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-blue-500:focus-visible{--tw-ring-color:var(--color-blue-500)}.focus-visible\:ring-gray-500:focus-visible{--tw-ring-color:var(--color-gray-500)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-100:active{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:cursor-grabbing:active{cursor:grabbing}.active\:bg-blue-600:active{background-color:var(--color-blue-600)}.active\:bg-blue-700:active{background-color:var(--color-blue-700)}.active\:bg-blue-800:active{background-color:var(--color-blue-800)}.active\:bg-gray-100:active{background-color:var(--color-gray-100)}.active\:bg-gray-200:active{background-color:var(--color-gray-200)}.active\:bg-gray-300:active{background-color:var(--color-gray-300)}.active\:bg-gray-700:active{background-color:var(--color-gray-700)}.active\:bg-gray-800:active{background-color:var(--color-gray-800)}.active\:bg-green-600:active{background-color:var(--color-green-600)}.active\:bg-green-700:active{background-color:var(--color-green-700)}.active\:bg-red-600:active{background-color:var(--color-red-600)}.active\:bg-red-700:active{background-color:var(--color-red-700)}.active\:opacity-75:active{opacity:.75}.active\:opacity-100:active{opacity:1}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:border-gray-200:disabled{border-color:var(--color-gray-200)}.disabled\:border-gray-300:disabled{border-color:var(--color-gray-300)}.disabled\:bg-blue-400:disabled{background-color:var(--color-blue-400)}.disabled\:bg-gray-100:disabled{background-color:var(--color-gray-100)}.disabled\:bg-gray-200:disabled{background-color:var(--color-gray-200)}.disabled\:bg-gray-300:disabled{background-color:var(--color-gray-300)}.disabled\:bg-gray-400:disabled{background-color:var(--color-gray-400)}.disabled\:text-gray-300:disabled{color:var(--color-gray-300)}.disabled\:text-gray-400:disabled{color:var(--color-gray-400)}.disabled\:text-gray-500:disabled{color:var(--color-gray-500)}.disabled\:no-underline:disabled{text-decoration-line:none}.disabled\:opacity-25:disabled{opacity:.25}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-75:disabled{opacity:.75}@media(hover:hover){.disabled\:hover\:bg-indigo-600:disabled:hover{background-color:var(--color-indigo-600)}.disabled\:hover\:bg-transparent:disabled:hover{background-color:#0000}}@media(min-width:40rem){.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:col-span-12{grid-column:span 12/span 12}.sm\:col-span-full{grid-column:1/-1}.sm\:m-0{margin:calc(var(--spacing) * 0)}.sm\:m-1{margin:calc(var(--spacing) * 1)}.sm\:m-2{margin:calc(var(--spacing) * 2)}.sm\:m-3{margin:calc(var(--spacing) * 3)}.sm\:m-4{margin:calc(var(--spacing) * 4)}.sm\:m-auto{margin:auto}.sm\:mx-auto{margin-inline:auto}.sm\:my-auto{margin-block:auto}.sm\:mt-6{margin-top:calc(var(--spacing) * 6)}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:inline{display:inline}.sm\:inline-block{display:inline-block}.sm\:inline-flex{display:inline-flex}.sm\:w-1\/2{width:50%}.sm\:w-1\/3{width:33.3333%}.sm\:w-1\/4{width:25%}.sm\:w-2\/3{width:66.6667%}.sm\:w-3\/4{width:75%}.sm\:w-40{width:calc(var(--spacing) * 40)}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:flex-col{flex-direction:column}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:items-start{align-items:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:justify-center{justify-content:center}.sm\:justify-end{justify-content:flex-end}.sm\:justify-start{justify-content:flex-start}.sm\:gap-0{gap:calc(var(--spacing) * 0)}.sm\:gap-1{gap:calc(var(--spacing) * 1)}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:gap-5{gap:calc(var(--spacing) * 5)}.sm\:gap-6{gap:calc(var(--spacing) * 6)}.sm\:gap-8{gap:calc(var(--spacing) * 8)}:where(.sm\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 0) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}.sm\:p-0{padding:calc(var(--spacing) * 0)}.sm\:p-1{padding:calc(var(--spacing) * 1)}.sm\:p-2{padding:calc(var(--spacing) * 2)}.sm\:p-3{padding:calc(var(--spacing) * 3)}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:p-8{padding:calc(var(--spacing) * 8)}.sm\:px-0{padding-inline:calc(var(--spacing) * 0)}.sm\:px-1{padding-inline:calc(var(--spacing) * 1)}.sm\:px-2{padding-inline:calc(var(--spacing) * 2)}.sm\:px-3{padding-inline:calc(var(--spacing) * 3)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:px-8{padding-inline:calc(var(--spacing) * 8)}.sm\:py-0{padding-block:calc(var(--spacing) * 0)}.sm\:py-1{padding-block:calc(var(--spacing) * 1)}.sm\:py-2{padding-block:calc(var(--spacing) * 2)}.sm\:py-3{padding-block:calc(var(--spacing) * 3)}.sm\:py-4{padding-block:calc(var(--spacing) * 4)}.sm\:py-5{padding-block:calc(var(--spacing) * 5)}.sm\:py-6{padding-block:calc(var(--spacing) * 6)}.sm\:py-8{padding-block:calc(var(--spacing) * 8)}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.sm\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:48rem){.md\:absolute{position:absolute}.md\:fixed{position:fixed}.md\:relative{position:relative}.md\:static{position:static}.md\:sticky{position:sticky}.md\:inset-0{inset:calc(var(--spacing) * 0)}.md\:top-0{top:calc(var(--spacing) * 0)}.md\:right-0{right:calc(var(--spacing) * 0)}.md\:bottom-0{bottom:calc(var(--spacing) * 0)}.md\:left-0{left:calc(var(--spacing) * 0)}.md\:order-1{order:1}.md\:order-2{order:2}.md\:order-3{order:3}.md\:order-first{order:-9999}.md\:order-last{order:9999}.md\:order-none{order:0}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-span-12{grid-column:span 12/span 12}.md\:col-span-full{grid-column:1/-1}.md\:row-span-1{grid-row:span 1/span 1}.md\:row-span-2{grid-row:span 2/span 2}.md\:row-span-3{grid-row:span 3/span 3}.md\:row-span-4{grid-row:span 4/span 4}.md\:row-span-full{grid-row:1/-1}.md\:m-0{margin:calc(var(--spacing) * 0)}.md\:m-1{margin:calc(var(--spacing) * 1)}.md\:m-2{margin:calc(var(--spacing) * 2)}.md\:m-3{margin:calc(var(--spacing) * 3)}.md\:m-4{margin:calc(var(--spacing) * 4)}.md\:m-auto{margin:auto}.md\:mx-auto{margin-inline:auto}.md\:my-auto{margin-block:auto}.md\:mt-0{margin-top:calc(var(--spacing) * 0)}.md\:mt-1{margin-top:calc(var(--spacing) * 1)}.md\:mt-2{margin-top:calc(var(--spacing) * 2)}.md\:mt-3{margin-top:calc(var(--spacing) * 3)}.md\:mt-4{margin-top:calc(var(--spacing) * 4)}.md\:mt-6{margin-top:calc(var(--spacing) * 6)}.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mr-0{margin-right:calc(var(--spacing) * 0)}.md\:mr-1{margin-right:calc(var(--spacing) * 1)}.md\:mr-2{margin-right:calc(var(--spacing) * 2)}.md\:mr-3{margin-right:calc(var(--spacing) * 3)}.md\:mr-4{margin-right:calc(var(--spacing) * 4)}.md\:mr-6{margin-right:calc(var(--spacing) * 6)}.md\:mr-8{margin-right:calc(var(--spacing) * 8)}.md\:mr-auto{margin-right:auto}.md\:mb-0{margin-bottom:calc(var(--spacing) * 0)}.md\:mb-1{margin-bottom:calc(var(--spacing) * 1)}.md\:mb-2{margin-bottom:calc(var(--spacing) * 2)}.md\:mb-3{margin-bottom:calc(var(--spacing) * 3)}.md\:mb-4{margin-bottom:calc(var(--spacing) * 4)}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:mb-8{margin-bottom:calc(var(--spacing) * 8)}.md\:ml-0{margin-left:calc(var(--spacing) * 0)}.md\:ml-1{margin-left:calc(var(--spacing) * 1)}.md\:ml-2{margin-left:calc(var(--spacing) * 2)}.md\:ml-3{margin-left:calc(var(--spacing) * 3)}.md\:ml-4{margin-left:calc(var(--spacing) * 4)}.md\:ml-6{margin-left:calc(var(--spacing) * 6)}.md\:ml-8{margin-left:calc(var(--spacing) * 8)}.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:inline{display:inline}.md\:inline-block{display:inline-block}.md\:inline-flex{display:inline-flex}.md\:h-20{height:calc(var(--spacing) * 20)}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.3333%}.md\:w-1\/4{width:25%}.md\:w-1\/5{width:20%}.md\:w-2\/3{width:66.6667%}.md\:w-2\/5{width:40%}.md\:w-3\/4{width:75%}.md\:w-3\/5{width:60%}.md\:w-4\/5{width:80%}.md\:w-20{width:calc(var(--spacing) * 20)}.md\:w-40{width:calc(var(--spacing) * 40)}.md\:w-48{width:calc(var(--spacing) * 48)}.md\:w-64{width:calc(var(--spacing) * 64)}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:w-80{width:calc(var(--spacing) * 80)}.md\:w-auto{width:auto}.md\:w-full{width:100%}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:max-w-3xl{max-width:var(--container-3xl)}.md\:max-w-4xl{max-width:var(--container-4xl)}.md\:max-w-5xl{max-width:var(--container-5xl)}.md\:max-w-6xl{max-width:var(--container-6xl)}.md\:max-w-7xl{max-width:var(--container-7xl)}.md\:max-w-full{max-width:100%}.md\:max-w-lg{max-width:var(--container-lg)}.md\:max-w-md{max-width:var(--container-md)}.md\:max-w-none{max-width:none}.md\:max-w-sm{max-width:var(--container-sm)}.md\:max-w-xl{max-width:var(--container-xl)}.md\:max-w-xs{max-width:var(--container-xs)}.md\:flex-1{flex:1}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.md\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.md\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.md\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.md\:grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.md\:grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.md\:flex-col{flex-direction:column}.md\:flex-row{flex-direction:row}.md\:items-baseline{align-items:baseline}.md\:items-center{align-items:center}.md\:items-end{align-items:flex-end}.md\:items-start{align-items:flex-start}.md\:items-stretch{align-items:stretch}.md\:justify-around{justify-content:space-around}.md\:justify-between{justify-content:space-between}.md\:justify-center{justify-content:center}.md\:justify-end{justify-content:flex-end}.md\:justify-start{justify-content:flex-start}.md\:gap-0{gap:calc(var(--spacing) * 0)}.md\:gap-1{gap:calc(var(--spacing) * 1)}.md\:gap-2{gap:calc(var(--spacing) * 2)}.md\:gap-3{gap:calc(var(--spacing) * 3)}.md\:gap-4{gap:calc(var(--spacing) * 4)}.md\:gap-5{gap:calc(var(--spacing) * 5)}.md\:gap-6{gap:calc(var(--spacing) * 6)}.md\:gap-8{gap:calc(var(--spacing) * 8)}.md\:gap-10{gap:calc(var(--spacing) * 10)}.md\:gap-12{gap:calc(var(--spacing) * 12)}:where(.md\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 0) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.md\:rounded-2xl{border-radius:var(--radius-2xl)}.md\:p-0{padding:calc(var(--spacing) * 0)}.md\:p-1{padding:calc(var(--spacing) * 1)}.md\:p-2{padding:calc(var(--spacing) * 2)}.md\:p-3{padding:calc(var(--spacing) * 3)}.md\:p-4{padding:calc(var(--spacing) * 4)}.md\:p-5{padding:calc(var(--spacing) * 5)}.md\:p-6{padding:calc(var(--spacing) * 6)}.md\:p-8{padding:calc(var(--spacing) * 8)}.md\:p-10{padding:calc(var(--spacing) * 10)}.md\:p-12{padding:calc(var(--spacing) * 12)}.md\:px-0{padding-inline:calc(var(--spacing) * 0)}.md\:px-1{padding-inline:calc(var(--spacing) * 1)}.md\:px-2{padding-inline:calc(var(--spacing) * 2)}.md\:px-3{padding-inline:calc(var(--spacing) * 3)}.md\:px-4{padding-inline:calc(var(--spacing) * 4)}.md\:px-5{padding-inline:calc(var(--spacing) * 5)}.md\:px-6{padding-inline:calc(var(--spacing) * 6)}.md\:px-8{padding-inline:calc(var(--spacing) * 8)}.md\:px-10{padding-inline:calc(var(--spacing) * 10)}.md\:px-12{padding-inline:calc(var(--spacing) * 12)}.md\:py-0{padding-block:calc(var(--spacing) * 0)}.md\:py-1{padding-block:calc(var(--spacing) * 1)}.md\:py-2{padding-block:calc(var(--spacing) * 2)}.md\:py-3{padding-block:calc(var(--spacing) * 3)}.md\:py-4{padding-block:calc(var(--spacing) * 4)}.md\:py-5{padding-block:calc(var(--spacing) * 5)}.md\:py-6{padding-block:calc(var(--spacing) * 6)}.md\:py-8{padding-block:calc(var(--spacing) * 8)}.md\:py-10{padding-block:calc(var(--spacing) * 10)}.md\:py-12{padding-block:calc(var(--spacing) * 12)}.md\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.md\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:64rem){.lg\:absolute{position:absolute}.lg\:fixed{position:fixed}.lg\:relative{position:relative}.lg\:static{position:static}.lg\:sticky{position:sticky}.lg\:inset-0{inset:calc(var(--spacing) * 0)}.lg\:top-0{top:calc(var(--spacing) * 0)}.lg\:right-0{right:calc(var(--spacing) * 0)}.lg\:bottom-0{bottom:calc(var(--spacing) * 0)}.lg\:left-0{left:calc(var(--spacing) * 0)}.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:order-3{order:3}.lg\:order-4{order:4}.lg\:order-first{order:-9999}.lg\:order-last{order:9999}.lg\:order-none{order:0}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-5{grid-column:span 5/span 5}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:col-span-12{grid-column:span 12/span 12}.lg\:col-span-full{grid-column:1/-1}.lg\:row-span-1{grid-row:span 1/span 1}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-span-3{grid-row:span 3/span 3}.lg\:row-span-4{grid-row:span 4/span 4}.lg\:row-span-full{grid-row:1/-1}.lg\:m-0{margin:calc(var(--spacing) * 0)}.lg\:m-1{margin:calc(var(--spacing) * 1)}.lg\:m-2{margin:calc(var(--spacing) * 2)}.lg\:m-3{margin:calc(var(--spacing) * 3)}.lg\:m-4{margin:calc(var(--spacing) * 4)}.lg\:m-auto{margin:auto}.lg\:mx-auto{margin-inline:auto}.lg\:my-auto{margin-block:auto}.lg\:mt-0{margin-top:calc(var(--spacing) * 0)}.lg\:mt-1{margin-top:calc(var(--spacing) * 1)}.lg\:mt-2{margin-top:calc(var(--spacing) * 2)}.lg\:mt-3{margin-top:calc(var(--spacing) * 3)}.lg\:mt-4{margin-top:calc(var(--spacing) * 4)}.lg\:mt-6{margin-top:calc(var(--spacing) * 6)}.lg\:mt-8{margin-top:calc(var(--spacing) * 8)}.lg\:mt-10{margin-top:calc(var(--spacing) * 10)}.lg\:mt-12{margin-top:calc(var(--spacing) * 12)}.lg\:mr-0{margin-right:calc(var(--spacing) * 0)}.lg\:mr-1{margin-right:calc(var(--spacing) * 1)}.lg\:mr-2{margin-right:calc(var(--spacing) * 2)}.lg\:mr-3{margin-right:calc(var(--spacing) * 3)}.lg\:mr-4{margin-right:calc(var(--spacing) * 4)}.lg\:mr-6{margin-right:calc(var(--spacing) * 6)}.lg\:mr-8{margin-right:calc(var(--spacing) * 8)}.lg\:mr-auto{margin-right:auto}.lg\:mb-0{margin-bottom:calc(var(--spacing) * 0)}.lg\:mb-1{margin-bottom:calc(var(--spacing) * 1)}.lg\:mb-2{margin-bottom:calc(var(--spacing) * 2)}.lg\:mb-3{margin-bottom:calc(var(--spacing) * 3)}.lg\:mb-4{margin-bottom:calc(var(--spacing) * 4)}.lg\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.lg\:mb-8{margin-bottom:calc(var(--spacing) * 8)}.lg\:mb-10{margin-bottom:calc(var(--spacing) * 10)}.lg\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.lg\:ml-0{margin-left:calc(var(--spacing) * 0)}.lg\:ml-1{margin-left:calc(var(--spacing) * 1)}.lg\:ml-2{margin-left:calc(var(--spacing) * 2)}.lg\:ml-3{margin-left:calc(var(--spacing) * 3)}.lg\:ml-4{margin-left:calc(var(--spacing) * 4)}.lg\:ml-6{margin-left:calc(var(--spacing) * 6)}.lg\:ml-8{margin-left:calc(var(--spacing) * 8)}.lg\:ml-auto{margin-left:auto}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:inline{display:inline}.lg\:inline-block{display:inline-block}.lg\:inline-flex{display:inline-flex}.lg\:w-1\/2{width:50%}.lg\:w-1\/3{width:33.3333%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:w-1\/6{width:16.6667%}.lg\:w-2\/3{width:66.6667%}.lg\:w-2\/5{width:40%}.lg\:w-3\/4{width:75%}.lg\:w-3\/5{width:60%}.lg\:w-4\/5{width:80%}.lg\:w-5\/6{width:83.3333%}.lg\:w-auto{width:auto}.lg\:w-full{width:100%}.lg\:max-w-2xl{max-width:var(--container-2xl)}.lg\:max-w-3xl{max-width:var(--container-3xl)}.lg\:max-w-4xl{max-width:var(--container-4xl)}.lg\:max-w-5xl{max-width:var(--container-5xl)}.lg\:max-w-6xl{max-width:var(--container-6xl)}.lg\:max-w-7xl{max-width:var(--container-7xl)}.lg\:max-w-full{max-width:100%}.lg\:max-w-lg{max-width:var(--container-lg)}.lg\:max-w-md{max-width:var(--container-md)}.lg\:max-w-none{max-width:none}.lg\:max-w-sm{max-width:var(--container-sm)}.lg\:max-w-xl{max-width:var(--container-xl)}.lg\:max-w-xs{max-width:var(--container-xs)}.lg\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.lg\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.lg\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.lg\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.lg\:grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.lg\:grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.lg\:flex-col{flex-direction:column}.lg\:flex-row{flex-direction:row}.lg\:items-baseline{align-items:baseline}.lg\:items-center{align-items:center}.lg\:items-end{align-items:flex-end}.lg\:items-start{align-items:flex-start}.lg\:items-stretch{align-items:stretch}.lg\:justify-around{justify-content:space-around}.lg\:justify-between{justify-content:space-between}.lg\:justify-center{justify-content:center}.lg\:justify-end{justify-content:flex-end}.lg\:justify-evenly{justify-content:space-evenly}.lg\:justify-start{justify-content:flex-start}.lg\:gap-0{gap:calc(var(--spacing) * 0)}.lg\:gap-1{gap:calc(var(--spacing) * 1)}.lg\:gap-2{gap:calc(var(--spacing) * 2)}.lg\:gap-3{gap:calc(var(--spacing) * 3)}.lg\:gap-4{gap:calc(var(--spacing) * 4)}.lg\:gap-5{gap:calc(var(--spacing) * 5)}.lg\:gap-6{gap:calc(var(--spacing) * 6)}.lg\:gap-8{gap:calc(var(--spacing) * 8)}.lg\:gap-10{gap:calc(var(--spacing) * 10)}.lg\:gap-12{gap:calc(var(--spacing) * 12)}.lg\:gap-16{gap:calc(var(--spacing) * 16)}:where(.lg\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-10>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 10) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 10) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 0) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-10>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 10) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 10) * calc(1 - var(--tw-space-x-reverse)))}.lg\:p-0{padding:calc(var(--spacing) * 0)}.lg\:p-1{padding:calc(var(--spacing) * 1)}.lg\:p-2{padding:calc(var(--spacing) * 2)}.lg\:p-3{padding:calc(var(--spacing) * 3)}.lg\:p-4{padding:calc(var(--spacing) * 4)}.lg\:p-5{padding:calc(var(--spacing) * 5)}.lg\:p-6{padding:calc(var(--spacing) * 6)}.lg\:p-8{padding:calc(var(--spacing) * 8)}.lg\:p-10{padding:calc(var(--spacing) * 10)}.lg\:p-12{padding:calc(var(--spacing) * 12)}.lg\:p-16{padding:calc(var(--spacing) * 16)}.lg\:px-0{padding-inline:calc(var(--spacing) * 0)}.lg\:px-1{padding-inline:calc(var(--spacing) * 1)}.lg\:px-2{padding-inline:calc(var(--spacing) * 2)}.lg\:px-3{padding-inline:calc(var(--spacing) * 3)}.lg\:px-4{padding-inline:calc(var(--spacing) * 4)}.lg\:px-5{padding-inline:calc(var(--spacing) * 5)}.lg\:px-6{padding-inline:calc(var(--spacing) * 6)}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:px-10{padding-inline:calc(var(--spacing) * 10)}.lg\:px-12{padding-inline:calc(var(--spacing) * 12)}.lg\:px-16{padding-inline:calc(var(--spacing) * 16)}.lg\:py-0{padding-block:calc(var(--spacing) * 0)}.lg\:py-1{padding-block:calc(var(--spacing) * 1)}.lg\:py-2{padding-block:calc(var(--spacing) * 2)}.lg\:py-3{padding-block:calc(var(--spacing) * 3)}.lg\:py-4{padding-block:calc(var(--spacing) * 4)}.lg\:py-5{padding-block:calc(var(--spacing) * 5)}.lg\:py-6{padding-block:calc(var(--spacing) * 6)}.lg\:py-8{padding-block:calc(var(--spacing) * 8)}.lg\:py-10{padding-block:calc(var(--spacing) * 10)}.lg\:py-12{padding-block:calc(var(--spacing) * 12)}.lg\:py-16{padding-block:calc(var(--spacing) * 16)}.lg\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.lg\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.lg\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.lg\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.lg\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.lg\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.lg\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.lg\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.lg\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:80rem){.xl\:col-span-1{grid-column:span 1/span 1}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-span-3{grid-column:span 3/span 3}.xl\:col-span-4{grid-column:span 4/span 4}.xl\:col-span-5{grid-column:span 5/span 5}.xl\:col-span-6{grid-column:span 6/span 6}.xl\:col-span-12{grid-column:span 12/span 12}.xl\:col-span-full{grid-column:1/-1}.xl\:row-span-1{grid-row:span 1/span 1}.xl\:row-span-2{grid-row:span 2/span 2}.xl\:row-span-3{grid-row:span 3/span 3}.xl\:row-span-full{grid-row:1/-1}.xl\:m-0{margin:calc(var(--spacing) * 0)}.xl\:m-auto{margin:auto}.xl\:mx-auto{margin-inline:auto}.xl\:my-auto{margin-block:auto}.xl\:block{display:block}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:inline{display:inline}.xl\:inline-block{display:inline-block}.xl\:inline-flex{display:inline-flex}.xl\:w-1\/2{width:50%}.xl\:w-1\/3{width:33.3333%}.xl\:w-1\/4{width:25%}.xl\:w-1\/5{width:20%}.xl\:w-2\/3{width:66.6667%}.xl\:w-2\/5{width:40%}.xl\:w-3\/4{width:75%}.xl\:w-3\/5{width:60%}.xl\:w-4\/5{width:80%}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-2xl{max-width:var(--container-2xl)}.xl\:max-w-3xl{max-width:var(--container-3xl)}.xl\:max-w-4xl{max-width:var(--container-4xl)}.xl\:max-w-5xl{max-width:var(--container-5xl)}.xl\:max-w-6xl{max-width:var(--container-6xl)}.xl\:max-w-7xl{max-width:var(--container-7xl)}.xl\:max-w-full{max-width:100%}.xl\:max-w-lg{max-width:var(--container-lg)}.xl\:max-w-md{max-width:var(--container-md)}.xl\:max-w-none{max-width:none}.xl\:max-w-sm{max-width:var(--container-sm)}.xl\:max-w-xl{max-width:var(--container-xl)}.xl\:max-w-xs{max-width:var(--container-xs)}.xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.xl\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.xl\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.xl\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.xl\:flex-col{flex-direction:column}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:items-end{align-items:flex-end}.xl\:items-start{align-items:flex-start}.xl\:justify-between{justify-content:space-between}.xl\:justify-center{justify-content:center}.xl\:justify-end{justify-content:flex-end}.xl\:justify-start{justify-content:flex-start}.xl\:gap-0{gap:calc(var(--spacing) * 0)}.xl\:gap-1{gap:calc(var(--spacing) * 1)}.xl\:gap-2{gap:calc(var(--spacing) * 2)}.xl\:gap-3{gap:calc(var(--spacing) * 3)}.xl\:gap-4{gap:calc(var(--spacing) * 4)}.xl\:gap-5{gap:calc(var(--spacing) * 5)}.xl\:gap-6{gap:calc(var(--spacing) * 6)}.xl\:gap-8{gap:calc(var(--spacing) * 8)}.xl\:gap-10{gap:calc(var(--spacing) * 10)}.xl\:gap-12{gap:calc(var(--spacing) * 12)}.xl\:gap-16{gap:calc(var(--spacing) * 16)}:where(.xl\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 0) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.xl\:p-0{padding:calc(var(--spacing) * 0)}.xl\:p-1{padding:calc(var(--spacing) * 1)}.xl\:p-2{padding:calc(var(--spacing) * 2)}.xl\:p-3{padding:calc(var(--spacing) * 3)}.xl\:p-4{padding:calc(var(--spacing) * 4)}.xl\:p-5{padding:calc(var(--spacing) * 5)}.xl\:p-6{padding:calc(var(--spacing) * 6)}.xl\:p-8{padding:calc(var(--spacing) * 8)}.xl\:p-10{padding:calc(var(--spacing) * 10)}.xl\:p-12{padding:calc(var(--spacing) * 12)}.xl\:p-16{padding:calc(var(--spacing) * 16)}.xl\:px-0{padding-inline:calc(var(--spacing) * 0)}.xl\:px-1{padding-inline:calc(var(--spacing) * 1)}.xl\:px-2{padding-inline:calc(var(--spacing) * 2)}.xl\:px-3{padding-inline:calc(var(--spacing) * 3)}.xl\:px-4{padding-inline:calc(var(--spacing) * 4)}.xl\:px-5{padding-inline:calc(var(--spacing) * 5)}.xl\:px-6{padding-inline:calc(var(--spacing) * 6)}.xl\:px-8{padding-inline:calc(var(--spacing) * 8)}.xl\:px-10{padding-inline:calc(var(--spacing) * 10)}.xl\:px-12{padding-inline:calc(var(--spacing) * 12)}.xl\:px-16{padding-inline:calc(var(--spacing) * 16)}.xl\:py-0{padding-block:calc(var(--spacing) * 0)}.xl\:py-1{padding-block:calc(var(--spacing) * 1)}.xl\:py-2{padding-block:calc(var(--spacing) * 2)}.xl\:py-3{padding-block:calc(var(--spacing) * 3)}.xl\:py-4{padding-block:calc(var(--spacing) * 4)}.xl\:py-5{padding-block:calc(var(--spacing) * 5)}.xl\:py-6{padding-block:calc(var(--spacing) * 6)}.xl\:py-8{padding-block:calc(var(--spacing) * 8)}.xl\:py-10{padding-block:calc(var(--spacing) * 10)}.xl\:py-12{padding-block:calc(var(--spacing) * 12)}.xl\:py-16{padding-block:calc(var(--spacing) * 16)}.xl\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.xl\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.xl\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.xl\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.xl\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.xl\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.xl\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.xl\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.xl\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.xl\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:96rem){.\32xl\:col-span-1{grid-column:span 1/span 1}.\32xl\:col-span-2{grid-column:span 2/span 2}.\32xl\:col-span-3{grid-column:span 3/span 3}.\32xl\:col-span-4{grid-column:span 4/span 4}.\32xl\:col-span-6{grid-column:span 6/span 6}.\32xl\:col-span-12{grid-column:span 12/span 12}.\32xl\:block{display:block}.\32xl\:flex{display:flex}.\32xl\:hidden{display:none}.\32xl\:w-1\/2{width:50%}.\32xl\:w-1\/3{width:33.3333%}.\32xl\:w-1\/4{width:25%}.\32xl\:w-2\/3{width:66.6667%}.\32xl\:w-3\/4{width:75%}.\32xl\:w-auto{width:auto}.\32xl\:w-full{width:100%}.\32xl\:max-w-2xl{max-width:var(--container-2xl)}.\32xl\:max-w-3xl{max-width:var(--container-3xl)}.\32xl\:max-w-4xl{max-width:var(--container-4xl)}.\32xl\:max-w-5xl{max-width:var(--container-5xl)}.\32xl\:max-w-6xl{max-width:var(--container-6xl)}.\32xl\:max-w-7xl{max-width:var(--container-7xl)}.\32xl\:max-w-full{max-width:100%}.\32xl\:max-w-lg{max-width:var(--container-lg)}.\32xl\:max-w-md{max-width:var(--container-md)}.\32xl\:max-w-sm{max-width:var(--container-sm)}.\32xl\:max-w-xl{max-width:var(--container-xl)}.\32xl\:max-w-xs{max-width:var(--container-xs)}.\32xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.\32xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.\32xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.\32xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.\32xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.\32xl\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.\32xl\:gap-0{gap:calc(var(--spacing) * 0)}.\32xl\:gap-4{gap:calc(var(--spacing) * 4)}.\32xl\:gap-6{gap:calc(var(--spacing) * 6)}.\32xl\:gap-8{gap:calc(var(--spacing) * 8)}.\32xl\:gap-12{gap:calc(var(--spacing) * 12)}.\32xl\:gap-16{gap:calc(var(--spacing) * 16)}.\32xl\:p-0{padding:calc(var(--spacing) * 0)}.\32xl\:p-4{padding:calc(var(--spacing) * 4)}.\32xl\:p-6{padding:calc(var(--spacing) * 6)}.\32xl\:p-8{padding:calc(var(--spacing) * 8)}.\32xl\:p-12{padding:calc(var(--spacing) * 12)}.\32xl\:p-16{padding:calc(var(--spacing) * 16)}.\32xl\:px-0{padding-inline:calc(var(--spacing) * 0)}.\32xl\:px-4{padding-inline:calc(var(--spacing) * 4)}.\32xl\:px-6{padding-inline:calc(var(--spacing) * 6)}.\32xl\:px-8{padding-inline:calc(var(--spacing) * 8)}.\32xl\:px-12{padding-inline:calc(var(--spacing) * 12)}.\32xl\:px-16{padding-inline:calc(var(--spacing) * 16)}.\32xl\:py-0{padding-block:calc(var(--spacing) * 0)}.\32xl\:py-4{padding-block:calc(var(--spacing) * 4)}.\32xl\:py-6{padding-block:calc(var(--spacing) * 6)}.\32xl\:py-8{padding-block:calc(var(--spacing) * 8)}.\32xl\:py-12{padding-block:calc(var(--spacing) * 12)}.\32xl\:py-16{padding-block:calc(var(--spacing) * 16)}.\32xl\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.\32xl\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.\32xl\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.\32xl\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.\32xl\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.\32xl\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.\32xl\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.\32xl\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.\32xl\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}.dark .dark\:block{display:block}.dark .dark\:flex{display:flex}.dark .dark\:flex-1{flex:1}.dark .dark\:flex-auto{flex:auto}.dark .dark\:flex-none{flex:none}.dark .dark\:flex-col{flex-direction:column}.dark .dark\:flex-row{flex-direction:row}.dark .dark\:flex-wrap{flex-wrap:wrap}.dark .dark\:items-center{align-items:center}.dark .dark\:items-end{align-items:flex-end}.dark .dark\:items-start{align-items:flex-start}.dark .dark\:items-stretch{align-items:stretch}.dark .dark\:justify-around{justify-content:space-around}.dark .dark\:justify-between{justify-content:space-between}.dark .dark\:justify-center{justify-content:center}.dark .dark\:justify-end{justify-content:flex-end}.dark .dark\:justify-start{justify-content:flex-start}:where(.dark .dark\:divide-gray-50>:not(:last-child)){border-color:var(--color-gray-50)}:where(.dark .dark\:divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.dark .dark\:divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}:where(.dark .dark\:divide-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}:where(.dark .dark\:divide-gray-400>:not(:last-child)){border-color:var(--color-gray-400)}:where(.dark .dark\:divide-gray-500>:not(:last-child)){border-color:var(--color-gray-500)}:where(.dark .dark\:divide-gray-600>:not(:last-child)){border-color:var(--color-gray-600)}:where(.dark .dark\:divide-gray-700>:not(:last-child)){border-color:var(--color-gray-700)}:where(.dark .dark\:divide-gray-800>:not(:last-child)){border-color:var(--color-gray-800)}:where(.dark .dark\:divide-gray-900>:not(:last-child)){border-color:var(--color-gray-900)}:where(.dark .dark\:divide-purple-800>:not(:last-child)){border-color:var(--color-purple-800)}:where(.dark .dark\:divide-slate-700>:not(:last-child)){border-color:var(--color-slate-700)}.dark .dark\:self-center{align-self:center}.dark .dark\:self-end{align-self:flex-end}.dark .dark\:self-start{align-self:flex-start}.dark .dark\:self-stretch{align-self:stretch}.dark .dark\:overflow-auto{overflow:auto}.dark .dark\:overflow-hidden{overflow:hidden}.dark .dark\:overflow-visible{overflow:visible}.dark .dark\:overflow-x-auto{overflow-x:auto}.dark .dark\:overflow-y-auto{overflow-y:auto}.dark .dark\:rounded{border-radius:.25rem}.dark .dark\:rounded-full{border-radius:3.40282e38px}.dark .dark\:rounded-lg{border-radius:var(--radius-lg)}.dark .dark\:rounded-none{border-radius:0}.dark .dark\:rounded-sm{border-radius:var(--radius-sm)}.dark .dark\:rounded-xl{border-radius:var(--radius-xl)}.dark .dark\:border{border-style:var(--tw-border-style);border-width:1px}.dark .dark\:border-0{border-style:var(--tw-border-style);border-width:0}.dark .dark\:border-dashed{--tw-border-style:dashed;border-style:dashed}.dark .dark\:border-dotted{--tw-border-style:dotted;border-style:dotted}.dark .dark\:border-solid{--tw-border-style:solid;border-style:solid}.dark .dark\:border-amber-50{border-color:var(--color-amber-50)}.dark .dark\:border-amber-100{border-color:var(--color-amber-100)}.dark .dark\:border-amber-200{border-color:var(--color-amber-200)}.dark .dark\:border-amber-300{border-color:var(--color-amber-300)}.dark .dark\:border-amber-400{border-color:var(--color-amber-400)}.dark .dark\:border-amber-500{border-color:var(--color-amber-500)}.dark .dark\:border-amber-600{border-color:var(--color-amber-600)}.dark .dark\:border-amber-700{border-color:var(--color-amber-700)}.dark .dark\:border-amber-800{border-color:var(--color-amber-800)}.dark .dark\:border-amber-900{border-color:var(--color-amber-900)}.dark .dark\:border-black{border-color:var(--color-black)}.dark .dark\:border-blue-50{border-color:var(--color-blue-50)}.dark .dark\:border-blue-100{border-color:var(--color-blue-100)}.dark .dark\:border-blue-200{border-color:var(--color-blue-200)}.dark .dark\:border-blue-300{border-color:var(--color-blue-300)}.dark .dark\:border-blue-400{border-color:var(--color-blue-400)}.dark .dark\:border-blue-500{border-color:var(--color-blue-500)}.dark .dark\:border-blue-600{border-color:var(--color-blue-600)}.dark .dark\:border-blue-700{border-color:var(--color-blue-700)}.dark .dark\:border-blue-800{border-color:var(--color-blue-800)}.dark .dark\:border-blue-900{border-color:var(--color-blue-900)}.dark .dark\:border-gray-50{border-color:var(--color-gray-50)}.dark .dark\:border-gray-100{border-color:var(--color-gray-100)}.dark .dark\:border-gray-200{border-color:var(--color-gray-200)}.dark .dark\:border-gray-300{border-color:var(--color-gray-300)}.dark .dark\:border-gray-400{border-color:var(--color-gray-400)}.dark .dark\:border-gray-500{border-color:var(--color-gray-500)}.dark .dark\:border-gray-600{border-color:var(--color-gray-600)}.dark .dark\:border-gray-700{border-color:var(--color-gray-700)}.dark .dark\:border-gray-800{border-color:var(--color-gray-800)}.dark .dark\:border-gray-900{border-color:var(--color-gray-900)}.dark .dark\:border-gray-950{border-color:var(--color-gray-950)}.dark .dark\:border-green-50{border-color:var(--color-green-50)}.dark .dark\:border-green-100{border-color:var(--color-green-100)}.dark .dark\:border-green-200{border-color:var(--color-green-200)}.dark .dark\:border-green-300{border-color:var(--color-green-300)}.dark .dark\:border-green-400{border-color:var(--color-green-400)}.dark .dark\:border-green-500{border-color:var(--color-green-500)}.dark .dark\:border-green-600{border-color:var(--color-green-600)}.dark .dark\:border-green-700{border-color:var(--color-green-700)}.dark .dark\:border-green-800{border-color:var(--color-green-800)}.dark .dark\:border-green-900{border-color:var(--color-green-900)}.dark .dark\:border-indigo-400{border-color:var(--color-indigo-400)}.dark .dark\:border-orange-50{border-color:var(--color-orange-50)}.dark .dark\:border-orange-100{border-color:var(--color-orange-100)}.dark .dark\:border-orange-200{border-color:var(--color-orange-200)}.dark .dark\:border-orange-300{border-color:var(--color-orange-300)}.dark .dark\:border-orange-400{border-color:var(--color-orange-400)}.dark .dark\:border-orange-500{border-color:var(--color-orange-500)}.dark .dark\:border-orange-600{border-color:var(--color-orange-600)}.dark .dark\:border-orange-700{border-color:var(--color-orange-700)}.dark .dark\:border-orange-800{border-color:var(--color-orange-800)}.dark .dark\:border-orange-900{border-color:var(--color-orange-900)}.dark .dark\:border-pink-50{border-color:var(--color-pink-50)}.dark .dark\:border-pink-100{border-color:var(--color-pink-100)}.dark .dark\:border-pink-200{border-color:var(--color-pink-200)}.dark .dark\:border-pink-300{border-color:var(--color-pink-300)}.dark .dark\:border-pink-400{border-color:var(--color-pink-400)}.dark .dark\:border-pink-500{border-color:var(--color-pink-500)}.dark .dark\:border-pink-600{border-color:var(--color-pink-600)}.dark .dark\:border-pink-700{border-color:var(--color-pink-700)}.dark .dark\:border-pink-800{border-color:var(--color-pink-800)}.dark .dark\:border-pink-900{border-color:var(--color-pink-900)}.dark .dark\:border-purple-50{border-color:var(--color-purple-50)}.dark .dark\:border-purple-100{border-color:var(--color-purple-100)}.dark .dark\:border-purple-200{border-color:var(--color-purple-200)}.dark .dark\:border-purple-300{border-color:var(--color-purple-300)}.dark .dark\:border-purple-400{border-color:var(--color-purple-400)}.dark .dark\:border-purple-500{border-color:var(--color-purple-500)}.dark .dark\:border-purple-600{border-color:var(--color-purple-600)}.dark .dark\:border-purple-700{border-color:var(--color-purple-700)}.dark .dark\:border-purple-800{border-color:var(--color-purple-800)}.dark .dark\:border-purple-900{border-color:var(--color-purple-900)}.dark .dark\:border-red-50{border-color:var(--color-red-50)}.dark .dark\:border-red-100{border-color:var(--color-red-100)}.dark .dark\:border-red-200{border-color:var(--color-red-200)}.dark .dark\:border-red-300{border-color:var(--color-red-300)}.dark .dark\:border-red-400{border-color:var(--color-red-400)}.dark .dark\:border-red-500{border-color:var(--color-red-500)}.dark .dark\:border-red-600{border-color:var(--color-red-600)}.dark .dark\:border-red-700{border-color:var(--color-red-700)}.dark .dark\:border-red-800{border-color:var(--color-red-800)}.dark .dark\:border-red-900{border-color:var(--color-red-900)}.dark .dark\:border-slate-50{border-color:var(--color-slate-50)}.dark .dark\:border-slate-100{border-color:var(--color-slate-100)}.dark .dark\:border-slate-200{border-color:var(--color-slate-200)}.dark .dark\:border-slate-300{border-color:var(--color-slate-300)}.dark .dark\:border-slate-400{border-color:var(--color-slate-400)}.dark .dark\:border-slate-500{border-color:var(--color-slate-500)}.dark .dark\:border-slate-600{border-color:var(--color-slate-600)}.dark .dark\:border-slate-700{border-color:var(--color-slate-700)}.dark .dark\:border-slate-800{border-color:var(--color-slate-800)}.dark .dark\:border-slate-900{border-color:var(--color-slate-900)}.dark .dark\:border-slate-950{border-color:var(--color-slate-950)}.dark .dark\:border-transparent{border-color:#0000}.dark .dark\:border-white{border-color:var(--color-white)}.dark .dark\:border-yellow-50{border-color:var(--color-yellow-50)}.dark .dark\:border-yellow-100{border-color:var(--color-yellow-100)}.dark .dark\:border-yellow-200{border-color:var(--color-yellow-200)}.dark .dark\:border-yellow-300{border-color:var(--color-yellow-300)}.dark .dark\:border-yellow-400{border-color:var(--color-yellow-400)}.dark .dark\:border-yellow-500{border-color:var(--color-yellow-500)}.dark .dark\:border-yellow-600{border-color:var(--color-yellow-600)}.dark .dark\:border-yellow-700{border-color:var(--color-yellow-700)}.dark .dark\:border-yellow-800{border-color:var(--color-yellow-800)}.dark .dark\:border-yellow-900{border-color:var(--color-yellow-900)}.dark .dark\:bg-amber-50{background-color:var(--color-amber-50)}.dark .dark\:bg-amber-100{background-color:var(--color-amber-100)}.dark .dark\:bg-amber-200{background-color:var(--color-amber-200)}.dark .dark\:bg-amber-300{background-color:var(--color-amber-300)}.dark .dark\:bg-amber-400{background-color:var(--color-amber-400)}.dark .dark\:bg-amber-500{background-color:var(--color-amber-500)}.dark .dark\:bg-amber-600{background-color:var(--color-amber-600)}.dark .dark\:bg-amber-700{background-color:var(--color-amber-700)}.dark .dark\:bg-amber-800{background-color:var(--color-amber-800)}.dark .dark\:bg-amber-900{background-color:var(--color-amber-900)}.dark .dark\:bg-amber-900\/20{background-color:#7b330633}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/20{background-color:color-mix(in oklab,var(--color-amber-900) 20%,transparent)}}.dark .dark\:bg-amber-900\/30{background-color:#7b33064d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/30{background-color:color-mix(in oklab,var(--color-amber-900) 30%,transparent)}}.dark .dark\:bg-amber-900\/40{background-color:#7b330666}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/40{background-color:color-mix(in oklab,var(--color-amber-900) 40%,transparent)}}.dark .dark\:bg-amber-950{background-color:var(--color-amber-950)}.dark .dark\:bg-black{background-color:var(--color-black)}.dark .dark\:bg-blue-50{background-color:var(--color-blue-50)}.dark .dark\:bg-blue-100{background-color:var(--color-blue-100)}.dark .dark\:bg-blue-200{background-color:var(--color-blue-200)}.dark .dark\:bg-blue-300{background-color:var(--color-blue-300)}.dark .dark\:bg-blue-400{background-color:var(--color-blue-400)}.dark .dark\:bg-blue-500{background-color:var(--color-blue-500)}.dark .dark\:bg-blue-600{background-color:var(--color-blue-600)}.dark .dark\:bg-blue-700{background-color:var(--color-blue-700)}.dark .dark\:bg-blue-800{background-color:var(--color-blue-800)}.dark .dark\:bg-blue-900{background-color:var(--color-blue-900)}.dark .dark\:bg-blue-900\/20{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/20{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .dark\:bg-blue-900\/30{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/30{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .dark\:bg-blue-900\/40{background-color:#1c398e66}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/40{background-color:color-mix(in oklab,var(--color-blue-900) 40%,transparent)}}.dark .dark\:bg-blue-900\/50{background-color:#1c398e80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/50{background-color:color-mix(in oklab,var(--color-blue-900) 50%,transparent)}}.dark .dark\:bg-blue-950{background-color:var(--color-blue-950)}.dark .dark\:bg-cyan-50{background-color:var(--color-cyan-50)}.dark .dark\:bg-cyan-100{background-color:var(--color-cyan-100)}.dark .dark\:bg-cyan-200{background-color:var(--color-cyan-200)}.dark .dark\:bg-cyan-300{background-color:var(--color-cyan-300)}.dark .dark\:bg-cyan-400{background-color:var(--color-cyan-400)}.dark .dark\:bg-cyan-500{background-color:var(--color-cyan-500)}.dark .dark\:bg-cyan-600{background-color:var(--color-cyan-600)}.dark .dark\:bg-cyan-700{background-color:var(--color-cyan-700)}.dark .dark\:bg-cyan-800{background-color:var(--color-cyan-800)}.dark .dark\:bg-cyan-900{background-color:var(--color-cyan-900)}.dark .dark\:bg-cyan-900\/30{background-color:#104e644d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-cyan-900\/30{background-color:color-mix(in oklab,var(--color-cyan-900) 30%,transparent)}}.dark .dark\:bg-cyan-950{background-color:var(--color-cyan-950)}.dark .dark\:bg-emerald-50{background-color:var(--color-emerald-50)}.dark .dark\:bg-emerald-100{background-color:var(--color-emerald-100)}.dark .dark\:bg-emerald-200{background-color:var(--color-emerald-200)}.dark .dark\:bg-emerald-300{background-color:var(--color-emerald-300)}.dark .dark\:bg-emerald-400{background-color:var(--color-emerald-400)}.dark .dark\:bg-emerald-500{background-color:var(--color-emerald-500)}.dark .dark\:bg-emerald-600{background-color:var(--color-emerald-600)}.dark .dark\:bg-emerald-700{background-color:var(--color-emerald-700)}.dark .dark\:bg-emerald-800{background-color:var(--color-emerald-800)}.dark .dark\:bg-emerald-900{background-color:var(--color-emerald-900)}.dark .dark\:bg-emerald-950{background-color:var(--color-emerald-950)}.dark .dark\:bg-gray-50{background-color:var(--color-gray-50)}.dark .dark\:bg-gray-100{background-color:var(--color-gray-100)}.dark .dark\:bg-gray-200{background-color:var(--color-gray-200)}.dark .dark\:bg-gray-300{background-color:var(--color-gray-300)}.dark .dark\:bg-gray-400{background-color:var(--color-gray-400)}.dark .dark\:bg-gray-500{background-color:var(--color-gray-500)}.dark .dark\:bg-gray-600{background-color:var(--color-gray-600)}.dark .dark\:bg-gray-700{background-color:var(--color-gray-700)}.dark .dark\:bg-gray-700\/30{background-color:#3641534d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-700\/30{background-color:color-mix(in oklab,var(--color-gray-700) 30%,transparent)}}.dark .dark\:bg-gray-700\/50{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-700\/50{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.dark .dark\:bg-gray-800{background-color:var(--color-gray-800)}.dark .dark\:bg-gray-800\/50{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-800\/50{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.dark .dark\:bg-gray-800\/90{background-color:#1e2939e6}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-800\/90{background-color:color-mix(in oklab,var(--color-gray-800) 90%,transparent)}}.dark .dark\:bg-gray-900{background-color:var(--color-gray-900)}.dark .dark\:bg-gray-900\/50{background-color:#10182880}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-900\/50{background-color:color-mix(in oklab,var(--color-gray-900) 50%,transparent)}}.dark .dark\:bg-gray-950{background-color:var(--color-gray-950)}.dark .dark\:bg-green-50{background-color:var(--color-green-50)}.dark .dark\:bg-green-100{background-color:var(--color-green-100)}.dark .dark\:bg-green-200{background-color:var(--color-green-200)}.dark .dark\:bg-green-300{background-color:var(--color-green-300)}.dark .dark\:bg-green-400{background-color:var(--color-green-400)}.dark .dark\:bg-green-500{background-color:var(--color-green-500)}.dark .dark\:bg-green-600{background-color:var(--color-green-600)}.dark .dark\:bg-green-700{background-color:var(--color-green-700)}.dark .dark\:bg-green-800{background-color:var(--color-green-800)}.dark .dark\:bg-green-900{background-color:var(--color-green-900)}.dark .dark\:bg-green-900\/20{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-green-900\/20{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .dark\:bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-green-900\/30{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .dark\:bg-green-950{background-color:var(--color-green-950)}.dark .dark\:bg-indigo-50{background-color:var(--color-indigo-50)}.dark .dark\:bg-indigo-100{background-color:var(--color-indigo-100)}.dark .dark\:bg-indigo-200{background-color:var(--color-indigo-200)}.dark .dark\:bg-indigo-300{background-color:var(--color-indigo-300)}.dark .dark\:bg-indigo-400{background-color:var(--color-indigo-400)}.dark .dark\:bg-indigo-500{background-color:var(--color-indigo-500)}.dark .dark\:bg-indigo-600{background-color:var(--color-indigo-600)}.dark .dark\:bg-indigo-700{background-color:var(--color-indigo-700)}.dark .dark\:bg-indigo-800{background-color:var(--color-indigo-800)}.dark .dark\:bg-indigo-900{background-color:var(--color-indigo-900)}.dark .dark\:bg-indigo-900\/30{background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-indigo-900\/30{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .dark\:bg-indigo-950{background-color:var(--color-indigo-950)}.dark .dark\:bg-orange-50{background-color:var(--color-orange-50)}.dark .dark\:bg-orange-100{background-color:var(--color-orange-100)}.dark .dark\:bg-orange-200{background-color:var(--color-orange-200)}.dark .dark\:bg-orange-300{background-color:var(--color-orange-300)}.dark .dark\:bg-orange-400{background-color:var(--color-orange-400)}.dark .dark\:bg-orange-500{background-color:var(--color-orange-500)}.dark .dark\:bg-orange-600{background-color:var(--color-orange-600)}.dark .dark\:bg-orange-700{background-color:var(--color-orange-700)}.dark .dark\:bg-orange-800{background-color:var(--color-orange-800)}.dark .dark\:bg-orange-900{background-color:var(--color-orange-900)}.dark .dark\:bg-orange-900\/20{background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-orange-900\/20{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .dark\:bg-orange-900\/30{background-color:#7e2a0c4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-orange-900\/30{background-color:color-mix(in oklab,var(--color-orange-900) 30%,transparent)}}.dark .dark\:bg-orange-950{background-color:var(--color-orange-950)}.dark .dark\:bg-pink-50{background-color:var(--color-pink-50)}.dark .dark\:bg-pink-100{background-color:var(--color-pink-100)}.dark .dark\:bg-pink-200{background-color:var(--color-pink-200)}.dark .dark\:bg-pink-300{background-color:var(--color-pink-300)}.dark .dark\:bg-pink-400{background-color:var(--color-pink-400)}.dark .dark\:bg-pink-500{background-color:var(--color-pink-500)}.dark .dark\:bg-pink-600{background-color:var(--color-pink-600)}.dark .dark\:bg-pink-700{background-color:var(--color-pink-700)}.dark .dark\:bg-pink-800{background-color:var(--color-pink-800)}.dark .dark\:bg-pink-900{background-color:var(--color-pink-900)}.dark .dark\:bg-pink-950{background-color:var(--color-pink-950)}.dark .dark\:bg-purple-50{background-color:var(--color-purple-50)}.dark .dark\:bg-purple-100{background-color:var(--color-purple-100)}.dark .dark\:bg-purple-200{background-color:var(--color-purple-200)}.dark .dark\:bg-purple-300{background-color:var(--color-purple-300)}.dark .dark\:bg-purple-400{background-color:var(--color-purple-400)}.dark .dark\:bg-purple-500{background-color:var(--color-purple-500)}.dark .dark\:bg-purple-600{background-color:var(--color-purple-600)}.dark .dark\:bg-purple-700{background-color:var(--color-purple-700)}.dark .dark\:bg-purple-800{background-color:var(--color-purple-800)}.dark .dark\:bg-purple-800\/50{background-color:#6e11b080}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-800\/50{background-color:color-mix(in oklab,var(--color-purple-800) 50%,transparent)}}.dark .dark\:bg-purple-900{background-color:var(--color-purple-900)}.dark .dark\:bg-purple-900\/20{background-color:#59168b33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/20{background-color:color-mix(in oklab,var(--color-purple-900) 20%,transparent)}}.dark .dark\:bg-purple-900\/30{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/30{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.dark .dark\:bg-purple-900\/50{background-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/50{background-color:color-mix(in oklab,var(--color-purple-900) 50%,transparent)}}.dark .dark\:bg-purple-950{background-color:var(--color-purple-950)}.dark .dark\:bg-red-50{background-color:var(--color-red-50)}.dark .dark\:bg-red-100{background-color:var(--color-red-100)}.dark .dark\:bg-red-200{background-color:var(--color-red-200)}.dark .dark\:bg-red-300{background-color:var(--color-red-300)}.dark .dark\:bg-red-400{background-color:var(--color-red-400)}.dark .dark\:bg-red-500{background-color:var(--color-red-500)}.dark .dark\:bg-red-600{background-color:var(--color-red-600)}.dark .dark\:bg-red-700{background-color:var(--color-red-700)}.dark .dark\:bg-red-800{background-color:var(--color-red-800)}.dark .dark\:bg-red-900{background-color:var(--color-red-900)}.dark .dark\:bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .dark\:bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .dark\:bg-red-950{background-color:var(--color-red-950)}.dark .dark\:bg-rose-50{background-color:var(--color-rose-50)}.dark .dark\:bg-rose-100{background-color:var(--color-rose-100)}.dark .dark\:bg-rose-200{background-color:var(--color-rose-200)}.dark .dark\:bg-rose-300{background-color:var(--color-rose-300)}.dark .dark\:bg-rose-400{background-color:var(--color-rose-400)}.dark .dark\:bg-rose-500{background-color:var(--color-rose-500)}.dark .dark\:bg-rose-600{background-color:var(--color-rose-600)}.dark .dark\:bg-rose-700{background-color:var(--color-rose-700)}.dark .dark\:bg-rose-800{background-color:var(--color-rose-800)}.dark .dark\:bg-rose-900{background-color:var(--color-rose-900)}.dark .dark\:bg-rose-950{background-color:var(--color-rose-950)}.dark .dark\:bg-slate-50{background-color:var(--color-slate-50)}.dark .dark\:bg-slate-100{background-color:var(--color-slate-100)}.dark .dark\:bg-slate-200{background-color:var(--color-slate-200)}.dark .dark\:bg-slate-300{background-color:var(--color-slate-300)}.dark .dark\:bg-slate-400{background-color:var(--color-slate-400)}.dark .dark\:bg-slate-500{background-color:var(--color-slate-500)}.dark .dark\:bg-slate-600{background-color:var(--color-slate-600)}.dark .dark\:bg-slate-700{background-color:var(--color-slate-700)}.dark .dark\:bg-slate-700\/50{background-color:#31415880}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-slate-700\/50{background-color:color-mix(in oklab,var(--color-slate-700) 50%,transparent)}}.dark .dark\:bg-slate-800{background-color:var(--color-slate-800)}.dark .dark\:bg-slate-900{background-color:var(--color-slate-900)}.dark .dark\:bg-slate-950{background-color:var(--color-slate-950)}.dark .dark\:bg-teal-50{background-color:var(--color-teal-50)}.dark .dark\:bg-teal-100{background-color:var(--color-teal-100)}.dark .dark\:bg-teal-200{background-color:var(--color-teal-200)}.dark .dark\:bg-teal-300{background-color:var(--color-teal-300)}.dark .dark\:bg-teal-400{background-color:var(--color-teal-400)}.dark .dark\:bg-teal-500{background-color:var(--color-teal-500)}.dark .dark\:bg-teal-600{background-color:var(--color-teal-600)}.dark .dark\:bg-teal-700{background-color:var(--color-teal-700)}.dark .dark\:bg-teal-800{background-color:var(--color-teal-800)}.dark .dark\:bg-teal-900{background-color:var(--color-teal-900)}.dark .dark\:bg-teal-900\/30{background-color:#0b4f4a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-teal-900\/30{background-color:color-mix(in oklab,var(--color-teal-900) 30%,transparent)}}.dark .dark\:bg-teal-950{background-color:var(--color-teal-950)}.dark .dark\:bg-white{background-color:var(--color-white)}.dark .dark\:bg-yellow-50{background-color:var(--color-yellow-50)}.dark .dark\:bg-yellow-100{background-color:var(--color-yellow-100)}.dark .dark\:bg-yellow-200{background-color:var(--color-yellow-200)}.dark .dark\:bg-yellow-300{background-color:var(--color-yellow-300)}.dark .dark\:bg-yellow-400{background-color:var(--color-yellow-400)}.dark .dark\:bg-yellow-500{background-color:var(--color-yellow-500)}.dark .dark\:bg-yellow-600{background-color:var(--color-yellow-600)}.dark .dark\:bg-yellow-700{background-color:var(--color-yellow-700)}.dark .dark\:bg-yellow-800{background-color:var(--color-yellow-800)}.dark .dark\:bg-yellow-900{background-color:var(--color-yellow-900)}.dark .dark\:bg-yellow-900\/20{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/20{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .dark\:bg-yellow-900\/30{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/30{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .dark\:bg-yellow-900\/40{background-color:#733e0a66}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/40{background-color:color-mix(in oklab,var(--color-yellow-900) 40%,transparent)}}.dark .dark\:bg-yellow-950{background-color:var(--color-yellow-950)}.dark .dark\:from-blue-900{--tw-gradient-from:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-50{--tw-gradient-from:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-100{--tw-gradient-from:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-200{--tw-gradient-from:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-300{--tw-gradient-from:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-400{--tw-gradient-from:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-500{--tw-gradient-from:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-600{--tw-gradient-from:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-700{--tw-gradient-from:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-800{--tw-gradient-from:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-900{--tw-gradient-from:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-100{--tw-gradient-to:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-200{--tw-gradient-to:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-300{--tw-gradient-to:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-400{--tw-gradient-to:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-500{--tw-gradient-to:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-600{--tw-gradient-to:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-700{--tw-gradient-to:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-800{--tw-gradient-to:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-900{--tw-gradient-to:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-indigo-900{--tw-gradient-to:var(--color-indigo-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:text-center{text-align:center}.dark .dark\:text-justify{text-align:justify}.dark .dark\:text-left{text-align:left}.dark .dark\:text-right{text-align:right}.dark .dark\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.dark .dark\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.dark .dark\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.dark .dark\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.dark .dark\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.dark .dark\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.dark .dark\:font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.dark .dark\:font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.dark .dark\:font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.dark .dark\:whitespace-normal{white-space:normal}.dark .dark\:whitespace-nowrap{white-space:nowrap}.dark .dark\:whitespace-pre-wrap{white-space:pre-wrap}.dark .dark\:text-amber-50{color:var(--color-amber-50)}.dark .dark\:text-amber-100{color:var(--color-amber-100)}.dark .dark\:text-amber-200{color:var(--color-amber-200)}.dark .dark\:text-amber-300{color:var(--color-amber-300)}.dark .dark\:text-amber-400{color:var(--color-amber-400)}.dark .dark\:text-amber-500{color:var(--color-amber-500)}.dark .dark\:text-amber-600{color:var(--color-amber-600)}.dark .dark\:text-amber-700{color:var(--color-amber-700)}.dark .dark\:text-amber-800{color:var(--color-amber-800)}.dark .dark\:text-amber-900{color:var(--color-amber-900)}.dark .dark\:text-black{color:var(--color-black)}.dark .dark\:text-blue-50{color:var(--color-blue-50)}.dark .dark\:text-blue-100{color:var(--color-blue-100)}.dark .dark\:text-blue-200{color:var(--color-blue-200)}.dark .dark\:text-blue-300{color:var(--color-blue-300)}.dark .dark\:text-blue-400{color:var(--color-blue-400)}.dark .dark\:text-blue-500{color:var(--color-blue-500)}.dark .dark\:text-blue-600{color:var(--color-blue-600)}.dark .dark\:text-blue-700{color:var(--color-blue-700)}.dark .dark\:text-blue-800{color:var(--color-blue-800)}.dark .dark\:text-blue-900{color:var(--color-blue-900)}.dark .dark\:text-cyan-50{color:var(--color-cyan-50)}.dark .dark\:text-cyan-100{color:var(--color-cyan-100)}.dark .dark\:text-cyan-200{color:var(--color-cyan-200)}.dark .dark\:text-cyan-300{color:var(--color-cyan-300)}.dark .dark\:text-cyan-400{color:var(--color-cyan-400)}.dark .dark\:text-cyan-500{color:var(--color-cyan-500)}.dark .dark\:text-cyan-600{color:var(--color-cyan-600)}.dark .dark\:text-cyan-700{color:var(--color-cyan-700)}.dark .dark\:text-cyan-800{color:var(--color-cyan-800)}.dark .dark\:text-cyan-900{color:var(--color-cyan-900)}.dark .dark\:text-emerald-50{color:var(--color-emerald-50)}.dark .dark\:text-emerald-100{color:var(--color-emerald-100)}.dark .dark\:text-emerald-200{color:var(--color-emerald-200)}.dark .dark\:text-emerald-300{color:var(--color-emerald-300)}.dark .dark\:text-emerald-400{color:var(--color-emerald-400)}.dark .dark\:text-emerald-500{color:var(--color-emerald-500)}.dark .dark\:text-emerald-600{color:var(--color-emerald-600)}.dark .dark\:text-emerald-700{color:var(--color-emerald-700)}.dark .dark\:text-emerald-800{color:var(--color-emerald-800)}.dark .dark\:text-emerald-900{color:var(--color-emerald-900)}.dark .dark\:text-gray-50{color:var(--color-gray-50)}.dark .dark\:text-gray-100{color:var(--color-gray-100)}.dark .dark\:text-gray-200{color:var(--color-gray-200)}.dark .dark\:text-gray-300{color:var(--color-gray-300)}.dark .dark\:text-gray-400{color:var(--color-gray-400)}.dark .dark\:text-gray-500{color:var(--color-gray-500)}.dark .dark\:text-gray-600{color:var(--color-gray-600)}.dark .dark\:text-gray-700{color:var(--color-gray-700)}.dark .dark\:text-gray-800{color:var(--color-gray-800)}.dark .dark\:text-gray-900{color:var(--color-gray-900)}.dark .dark\:text-green-50{color:var(--color-green-50)}.dark .dark\:text-green-100{color:var(--color-green-100)}.dark .dark\:text-green-200{color:var(--color-green-200)}.dark .dark\:text-green-300{color:var(--color-green-300)}.dark .dark\:text-green-400{color:var(--color-green-400)}.dark .dark\:text-green-500{color:var(--color-green-500)}.dark .dark\:text-green-600{color:var(--color-green-600)}.dark .dark\:text-green-700{color:var(--color-green-700)}.dark .dark\:text-green-800{color:var(--color-green-800)}.dark .dark\:text-green-900{color:var(--color-green-900)}.dark .dark\:text-indigo-50{color:var(--color-indigo-50)}.dark .dark\:text-indigo-100{color:var(--color-indigo-100)}.dark .dark\:text-indigo-200{color:var(--color-indigo-200)}.dark .dark\:text-indigo-300{color:var(--color-indigo-300)}.dark .dark\:text-indigo-400{color:var(--color-indigo-400)}.dark .dark\:text-indigo-500{color:var(--color-indigo-500)}.dark .dark\:text-indigo-600{color:var(--color-indigo-600)}.dark .dark\:text-indigo-700{color:var(--color-indigo-700)}.dark .dark\:text-indigo-800{color:var(--color-indigo-800)}.dark .dark\:text-indigo-900{color:var(--color-indigo-900)}.dark .dark\:text-orange-50{color:var(--color-orange-50)}.dark .dark\:text-orange-100{color:var(--color-orange-100)}.dark .dark\:text-orange-200{color:var(--color-orange-200)}.dark .dark\:text-orange-300{color:var(--color-orange-300)}.dark .dark\:text-orange-400{color:var(--color-orange-400)}.dark .dark\:text-orange-500{color:var(--color-orange-500)}.dark .dark\:text-orange-600{color:var(--color-orange-600)}.dark .dark\:text-orange-700{color:var(--color-orange-700)}.dark .dark\:text-orange-800{color:var(--color-orange-800)}.dark .dark\:text-orange-900{color:var(--color-orange-900)}.dark .dark\:text-pink-50{color:var(--color-pink-50)}.dark .dark\:text-pink-100{color:var(--color-pink-100)}.dark .dark\:text-pink-200{color:var(--color-pink-200)}.dark .dark\:text-pink-300{color:var(--color-pink-300)}.dark .dark\:text-pink-400{color:var(--color-pink-400)}.dark .dark\:text-pink-500{color:var(--color-pink-500)}.dark .dark\:text-pink-600{color:var(--color-pink-600)}.dark .dark\:text-pink-700{color:var(--color-pink-700)}.dark .dark\:text-pink-800{color:var(--color-pink-800)}.dark .dark\:text-pink-900{color:var(--color-pink-900)}.dark .dark\:text-purple-50{color:var(--color-purple-50)}.dark .dark\:text-purple-100{color:var(--color-purple-100)}.dark .dark\:text-purple-200{color:var(--color-purple-200)}.dark .dark\:text-purple-300{color:var(--color-purple-300)}.dark .dark\:text-purple-400{color:var(--color-purple-400)}.dark .dark\:text-purple-500{color:var(--color-purple-500)}.dark .dark\:text-purple-600{color:var(--color-purple-600)}.dark .dark\:text-purple-700{color:var(--color-purple-700)}.dark .dark\:text-purple-800{color:var(--color-purple-800)}.dark .dark\:text-purple-900{color:var(--color-purple-900)}.dark .dark\:text-red-50{color:var(--color-red-50)}.dark .dark\:text-red-100{color:var(--color-red-100)}.dark .dark\:text-red-200{color:var(--color-red-200)}.dark .dark\:text-red-300{color:var(--color-red-300)}.dark .dark\:text-red-400{color:var(--color-red-400)}.dark .dark\:text-red-500{color:var(--color-red-500)}.dark .dark\:text-red-600{color:var(--color-red-600)}.dark .dark\:text-red-700{color:var(--color-red-700)}.dark .dark\:text-red-800{color:var(--color-red-800)}.dark .dark\:text-red-900{color:var(--color-red-900)}.dark .dark\:text-slate-50{color:var(--color-slate-50)}.dark .dark\:text-slate-100{color:var(--color-slate-100)}.dark .dark\:text-slate-200{color:var(--color-slate-200)}.dark .dark\:text-slate-300{color:var(--color-slate-300)}.dark .dark\:text-slate-400{color:var(--color-slate-400)}.dark .dark\:text-slate-500{color:var(--color-slate-500)}.dark .dark\:text-slate-600{color:var(--color-slate-600)}.dark .dark\:text-slate-700{color:var(--color-slate-700)}.dark .dark\:text-slate-800{color:var(--color-slate-800)}.dark .dark\:text-slate-900{color:var(--color-slate-900)}.dark .dark\:text-teal-50{color:var(--color-teal-50)}.dark .dark\:text-teal-100{color:var(--color-teal-100)}.dark .dark\:text-teal-200{color:var(--color-teal-200)}.dark .dark\:text-teal-300{color:var(--color-teal-300)}.dark .dark\:text-teal-400{color:var(--color-teal-400)}.dark .dark\:text-teal-500{color:var(--color-teal-500)}.dark .dark\:text-teal-600{color:var(--color-teal-600)}.dark .dark\:text-teal-700{color:var(--color-teal-700)}.dark .dark\:text-teal-800{color:var(--color-teal-800)}.dark .dark\:text-teal-900{color:var(--color-teal-900)}.dark .dark\:text-white{color:var(--color-white)}.dark .dark\:text-yellow-50{color:var(--color-yellow-50)}.dark .dark\:text-yellow-100{color:var(--color-yellow-100)}.dark .dark\:text-yellow-200{color:var(--color-yellow-200)}.dark .dark\:text-yellow-300{color:var(--color-yellow-300)}.dark .dark\:text-yellow-400{color:var(--color-yellow-400)}.dark .dark\:text-yellow-500{color:var(--color-yellow-500)}.dark .dark\:text-yellow-600{color:var(--color-yellow-600)}.dark .dark\:text-yellow-700{color:var(--color-yellow-700)}.dark .dark\:text-yellow-800{color:var(--color-yellow-800)}.dark .dark\:text-yellow-900{color:var(--color-yellow-900)}.dark .dark\:italic{font-style:italic}.dark .dark\:underline{text-decoration-line:underline}.dark .dark\:placeholder-gray-400::placeholder{color:var(--color-gray-400)}.dark .dark\:placeholder-gray-500::placeholder{color:var(--color-gray-500)}.dark .dark\:placeholder-gray-600::placeholder{color:var(--color-gray-600)}.dark .dark\:opacity-0{opacity:0}.dark .dark\:opacity-25{opacity:.25}.dark .dark\:opacity-50{opacity:.5}.dark .dark\:opacity-75{opacity:.75}.dark .dark\:opacity-100{opacity:1}.dark .dark\:shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-purple-900\/50{--tw-shadow-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:shadow-purple-900\/50{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-purple-900) 50%, transparent) var(--tw-shadow-alpha), transparent)}}.dark .dark\:ring-blue-50{--tw-ring-color:var(--color-blue-50)}.dark .dark\:ring-blue-100{--tw-ring-color:var(--color-blue-100)}.dark .dark\:ring-blue-200{--tw-ring-color:var(--color-blue-200)}.dark .dark\:ring-blue-300{--tw-ring-color:var(--color-blue-300)}.dark .dark\:ring-blue-400{--tw-ring-color:var(--color-blue-400)}.dark .dark\:ring-blue-500{--tw-ring-color:var(--color-blue-500)}.dark .dark\:ring-blue-600{--tw-ring-color:var(--color-blue-600)}.dark .dark\:ring-blue-700{--tw-ring-color:var(--color-blue-700)}.dark .dark\:ring-blue-800{--tw-ring-color:var(--color-blue-800)}.dark .dark\:ring-blue-900{--tw-ring-color:var(--color-blue-900)}.dark .dark\:ring-gray-50{--tw-ring-color:var(--color-gray-50)}.dark .dark\:ring-gray-100{--tw-ring-color:var(--color-gray-100)}.dark .dark\:ring-gray-200{--tw-ring-color:var(--color-gray-200)}.dark .dark\:ring-gray-300{--tw-ring-color:var(--color-gray-300)}.dark .dark\:ring-gray-400{--tw-ring-color:var(--color-gray-400)}.dark .dark\:ring-gray-500{--tw-ring-color:var(--color-gray-500)}.dark .dark\:ring-gray-600{--tw-ring-color:var(--color-gray-600)}.dark .dark\:ring-gray-700{--tw-ring-color:var(--color-gray-700)}.dark .dark\:ring-gray-800{--tw-ring-color:var(--color-gray-800)}.dark .dark\:ring-gray-900{--tw-ring-color:var(--color-gray-900)}.dark .dark\:ring-offset-gray-800{--tw-ring-offset-color:var(--color-gray-800)}.dark .dark\:ring-offset-gray-900{--tw-ring-offset-color:var(--color-gray-900)}@media(hover:hover){.dark .dark\:group-hover\:bg-gray-700:is(:where(.group):hover *){background-color:var(--color-gray-700)}.dark .dark\:group-hover\:bg-gray-800:is(:where(.group):hover *){background-color:var(--color-gray-800)}.dark .dark\:group-hover\:text-gray-100:is(:where(.group):hover *){color:var(--color-gray-100)}.dark .dark\:group-hover\:text-white:is(:where(.group):hover *){color:var(--color-white)}}.dark .dark\:selection\:bg-blue-800 ::selection{background-color:var(--color-blue-800)}.dark .dark\:selection\:bg-blue-800::selection{background-color:var(--color-blue-800)}.dark .dark\:selection\:text-blue-100 ::selection{color:var(--color-blue-100)}.dark .dark\:selection\:text-blue-100::selection{color:var(--color-blue-100)}.dark .dark\:odd\:bg-gray-800:nth-child(odd){background-color:var(--color-gray-800)}.dark .dark\:odd\:bg-gray-900:nth-child(odd){background-color:var(--color-gray-900)}.dark .dark\:even\:bg-gray-800:nth-child(2n){background-color:var(--color-gray-800)}.dark .dark\:even\:bg-gray-900:nth-child(2n){background-color:var(--color-gray-900)}@media(hover:hover){.dark .dark\:hover\:border-amber-50:hover{border-color:var(--color-amber-50)}.dark .dark\:hover\:border-amber-100:hover{border-color:var(--color-amber-100)}.dark .dark\:hover\:border-amber-200:hover{border-color:var(--color-amber-200)}.dark .dark\:hover\:border-amber-300:hover{border-color:var(--color-amber-300)}.dark .dark\:hover\:border-amber-400:hover{border-color:var(--color-amber-400)}.dark .dark\:hover\:border-amber-500:hover{border-color:var(--color-amber-500)}.dark .dark\:hover\:border-amber-600:hover{border-color:var(--color-amber-600)}.dark .dark\:hover\:border-amber-700:hover{border-color:var(--color-amber-700)}.dark .dark\:hover\:border-amber-800:hover{border-color:var(--color-amber-800)}.dark .dark\:hover\:border-amber-900:hover{border-color:var(--color-amber-900)}.dark .dark\:hover\:border-blue-50:hover{border-color:var(--color-blue-50)}.dark .dark\:hover\:border-blue-100:hover{border-color:var(--color-blue-100)}.dark .dark\:hover\:border-blue-200:hover{border-color:var(--color-blue-200)}.dark .dark\:hover\:border-blue-300:hover{border-color:var(--color-blue-300)}.dark .dark\:hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.dark .dark\:hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.dark .dark\:hover\:border-blue-600:hover{border-color:var(--color-blue-600)}.dark .dark\:hover\:border-blue-700:hover{border-color:var(--color-blue-700)}.dark .dark\:hover\:border-blue-800:hover{border-color:var(--color-blue-800)}.dark .dark\:hover\:border-blue-900:hover{border-color:var(--color-blue-900)}.dark .dark\:hover\:border-gray-50:hover{border-color:var(--color-gray-50)}.dark .dark\:hover\:border-gray-100:hover{border-color:var(--color-gray-100)}.dark .dark\:hover\:border-gray-200:hover{border-color:var(--color-gray-200)}.dark .dark\:hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.dark .dark\:hover\:border-gray-400:hover{border-color:var(--color-gray-400)}.dark .dark\:hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.dark .dark\:hover\:border-gray-600:hover{border-color:var(--color-gray-600)}.dark .dark\:hover\:border-gray-700:hover{border-color:var(--color-gray-700)}.dark .dark\:hover\:border-gray-800:hover{border-color:var(--color-gray-800)}.dark .dark\:hover\:border-gray-900:hover{border-color:var(--color-gray-900)}.dark .dark\:hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.dark .dark\:hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.dark .dark\:hover\:bg-amber-200:hover{background-color:var(--color-amber-200)}.dark .dark\:hover\:bg-amber-300:hover{background-color:var(--color-amber-300)}.dark .dark\:hover\:bg-amber-400:hover{background-color:var(--color-amber-400)}.dark .dark\:hover\:bg-amber-500:hover{background-color:var(--color-amber-500)}.dark .dark\:hover\:bg-amber-600:hover{background-color:var(--color-amber-600)}.dark .dark\:hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.dark .dark\:hover\:bg-amber-800:hover{background-color:var(--color-amber-800)}.dark .dark\:hover\:bg-amber-900:hover{background-color:var(--color-amber-900)}.dark .dark\:hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.dark .dark\:hover\:bg-blue-100:hover{background-color:var(--color-blue-100)}.dark .dark\:hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.dark .dark\:hover\:bg-blue-300:hover{background-color:var(--color-blue-300)}.dark .dark\:hover\:bg-blue-400:hover{background-color:var(--color-blue-400)}.dark .dark\:hover\:bg-blue-500:hover{background-color:var(--color-blue-500)}.dark .dark\:hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.dark .dark\:hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.dark .dark\:hover\:bg-blue-800:hover{background-color:var(--color-blue-800)}.dark .dark\:hover\:bg-blue-900:hover{background-color:var(--color-blue-900)}.dark .dark\:hover\:bg-blue-900\/30:hover{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-blue-900\/30:hover{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .dark\:hover\:bg-blue-900\/50:hover{background-color:#1c398e80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-blue-900\/50:hover{background-color:color-mix(in oklab,var(--color-blue-900) 50%,transparent)}}.dark .dark\:hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.dark .dark\:hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.dark .dark\:hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.dark .dark\:hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.dark .dark\:hover\:bg-gray-400:hover{background-color:var(--color-gray-400)}.dark .dark\:hover\:bg-gray-500:hover{background-color:var(--color-gray-500)}.dark .dark\:hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.dark .dark\:hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.dark .dark\:hover\:bg-gray-700\/30:hover{background-color:#3641534d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-700\/30:hover{background-color:color-mix(in oklab,var(--color-gray-700) 30%,transparent)}}.dark .dark\:hover\:bg-gray-700\/50:hover{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-700\/50:hover{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.dark .dark\:hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}.dark .dark\:hover\:bg-gray-800\/50:hover{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-800\/50:hover{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.dark .dark\:hover\:bg-gray-900:hover{background-color:var(--color-gray-900)}.dark .dark\:hover\:bg-green-50:hover{background-color:var(--color-green-50)}.dark .dark\:hover\:bg-green-100:hover{background-color:var(--color-green-100)}.dark .dark\:hover\:bg-green-200:hover{background-color:var(--color-green-200)}.dark .dark\:hover\:bg-green-300:hover{background-color:var(--color-green-300)}.dark .dark\:hover\:bg-green-400:hover{background-color:var(--color-green-400)}.dark .dark\:hover\:bg-green-500:hover{background-color:var(--color-green-500)}.dark .dark\:hover\:bg-green-600:hover{background-color:var(--color-green-600)}.dark .dark\:hover\:bg-green-700:hover{background-color:var(--color-green-700)}.dark .dark\:hover\:bg-green-800:hover{background-color:var(--color-green-800)}.dark .dark\:hover\:bg-green-900:hover{background-color:var(--color-green-900)}.dark .dark\:hover\:bg-green-900\/30:hover{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-green-900\/30:hover{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .dark\:hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.dark .dark\:hover\:bg-indigo-100:hover{background-color:var(--color-indigo-100)}.dark .dark\:hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}.dark .dark\:hover\:bg-indigo-300:hover{background-color:var(--color-indigo-300)}.dark .dark\:hover\:bg-indigo-400:hover{background-color:var(--color-indigo-400)}.dark .dark\:hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.dark .dark\:hover\:bg-indigo-600:hover{background-color:var(--color-indigo-600)}.dark .dark\:hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.dark .dark\:hover\:bg-indigo-800:hover{background-color:var(--color-indigo-800)}.dark .dark\:hover\:bg-indigo-900:hover{background-color:var(--color-indigo-900)}.dark .dark\:hover\:bg-indigo-900\/30:hover{background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-indigo-900\/30:hover{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .dark\:hover\:bg-orange-50:hover{background-color:var(--color-orange-50)}.dark .dark\:hover\:bg-orange-100:hover{background-color:var(--color-orange-100)}.dark .dark\:hover\:bg-orange-200:hover{background-color:var(--color-orange-200)}.dark .dark\:hover\:bg-orange-300:hover{background-color:var(--color-orange-300)}.dark .dark\:hover\:bg-orange-400:hover{background-color:var(--color-orange-400)}.dark .dark\:hover\:bg-orange-500:hover{background-color:var(--color-orange-500)}.dark .dark\:hover\:bg-orange-600:hover{background-color:var(--color-orange-600)}.dark .dark\:hover\:bg-orange-700:hover{background-color:var(--color-orange-700)}.dark .dark\:hover\:bg-orange-800:hover{background-color:var(--color-orange-800)}.dark .dark\:hover\:bg-orange-900:hover{background-color:var(--color-orange-900)}.dark .dark\:hover\:bg-orange-900\/30:hover{background-color:#7e2a0c4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-orange-900\/30:hover{background-color:color-mix(in oklab,var(--color-orange-900) 30%,transparent)}}.dark .dark\:hover\:bg-pink-50:hover{background-color:var(--color-pink-50)}.dark .dark\:hover\:bg-pink-100:hover{background-color:var(--color-pink-100)}.dark .dark\:hover\:bg-pink-200:hover{background-color:var(--color-pink-200)}.dark .dark\:hover\:bg-pink-300:hover{background-color:var(--color-pink-300)}.dark .dark\:hover\:bg-pink-400:hover{background-color:var(--color-pink-400)}.dark .dark\:hover\:bg-pink-500:hover{background-color:var(--color-pink-500)}.dark .dark\:hover\:bg-pink-600:hover{background-color:var(--color-pink-600)}.dark .dark\:hover\:bg-pink-700:hover{background-color:var(--color-pink-700)}.dark .dark\:hover\:bg-pink-800:hover{background-color:var(--color-pink-800)}.dark .dark\:hover\:bg-pink-900:hover{background-color:var(--color-pink-900)}.dark .dark\:hover\:bg-purple-50:hover{background-color:var(--color-purple-50)}.dark .dark\:hover\:bg-purple-100:hover{background-color:var(--color-purple-100)}.dark .dark\:hover\:bg-purple-200:hover{background-color:var(--color-purple-200)}.dark .dark\:hover\:bg-purple-300:hover{background-color:var(--color-purple-300)}.dark .dark\:hover\:bg-purple-400:hover{background-color:var(--color-purple-400)}.dark .dark\:hover\:bg-purple-500:hover{background-color:var(--color-purple-500)}.dark .dark\:hover\:bg-purple-600:hover{background-color:var(--color-purple-600)}.dark .dark\:hover\:bg-purple-700:hover{background-color:var(--color-purple-700)}.dark .dark\:hover\:bg-purple-800:hover{background-color:var(--color-purple-800)}.dark .dark\:hover\:bg-purple-900:hover{background-color:var(--color-purple-900)}.dark .dark\:hover\:bg-purple-900\/30:hover{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-purple-900\/30:hover{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.dark .dark\:hover\:bg-red-50:hover{background-color:var(--color-red-50)}.dark .dark\:hover\:bg-red-100:hover{background-color:var(--color-red-100)}.dark .dark\:hover\:bg-red-200:hover{background-color:var(--color-red-200)}.dark .dark\:hover\:bg-red-300:hover{background-color:var(--color-red-300)}.dark .dark\:hover\:bg-red-400:hover{background-color:var(--color-red-400)}.dark .dark\:hover\:bg-red-500:hover{background-color:var(--color-red-500)}.dark .dark\:hover\:bg-red-600:hover{background-color:var(--color-red-600)}.dark .dark\:hover\:bg-red-700:hover{background-color:var(--color-red-700)}.dark .dark\:hover\:bg-red-800:hover{background-color:var(--color-red-800)}.dark .dark\:hover\:bg-red-900:hover{background-color:var(--color-red-900)}.dark .dark\:hover\:bg-red-900\/20:hover{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-red-900\/20:hover{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .dark\:hover\:bg-red-900\/30:hover{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-red-900\/30:hover{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .dark\:hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.dark .dark\:hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.dark .dark\:hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.dark .dark\:hover\:bg-slate-300:hover{background-color:var(--color-slate-300)}.dark .dark\:hover\:bg-slate-400:hover{background-color:var(--color-slate-400)}.dark .dark\:hover\:bg-slate-500:hover{background-color:var(--color-slate-500)}.dark .dark\:hover\:bg-slate-600:hover{background-color:var(--color-slate-600)}.dark .dark\:hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.dark .dark\:hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.dark .dark\:hover\:bg-slate-900:hover{background-color:var(--color-slate-900)}.dark .dark\:hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.dark .dark\:hover\:bg-yellow-500:hover{background-color:var(--color-yellow-500)}.dark .dark\:hover\:bg-yellow-800:hover{background-color:var(--color-yellow-800)}.dark .dark\:hover\:bg-yellow-900\/30:hover{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-yellow-900\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .dark\:hover\:text-black:hover{color:var(--color-black)}.dark .dark\:hover\:text-blue-50:hover{color:var(--color-blue-50)}.dark .dark\:hover\:text-blue-100:hover{color:var(--color-blue-100)}.dark .dark\:hover\:text-blue-200:hover{color:var(--color-blue-200)}.dark .dark\:hover\:text-blue-300:hover{color:var(--color-blue-300)}.dark .dark\:hover\:text-blue-400:hover{color:var(--color-blue-400)}.dark .dark\:hover\:text-blue-500:hover{color:var(--color-blue-500)}.dark .dark\:hover\:text-blue-600:hover{color:var(--color-blue-600)}.dark .dark\:hover\:text-blue-700:hover{color:var(--color-blue-700)}.dark .dark\:hover\:text-blue-800:hover{color:var(--color-blue-800)}.dark .dark\:hover\:text-blue-900:hover{color:var(--color-blue-900)}.dark .dark\:hover\:text-gray-50:hover{color:var(--color-gray-50)}.dark .dark\:hover\:text-gray-100:hover{color:var(--color-gray-100)}.dark .dark\:hover\:text-gray-200:hover{color:var(--color-gray-200)}.dark .dark\:hover\:text-gray-300:hover{color:var(--color-gray-300)}.dark .dark\:hover\:text-gray-400:hover{color:var(--color-gray-400)}.dark .dark\:hover\:text-gray-500:hover{color:var(--color-gray-500)}.dark .dark\:hover\:text-gray-600:hover{color:var(--color-gray-600)}.dark .dark\:hover\:text-gray-700:hover{color:var(--color-gray-700)}.dark .dark\:hover\:text-gray-800:hover{color:var(--color-gray-800)}.dark .dark\:hover\:text-gray-900:hover{color:var(--color-gray-900)}.dark .dark\:hover\:text-green-50:hover{color:var(--color-green-50)}.dark .dark\:hover\:text-green-100:hover{color:var(--color-green-100)}.dark .dark\:hover\:text-green-200:hover{color:var(--color-green-200)}.dark .dark\:hover\:text-green-300:hover{color:var(--color-green-300)}.dark .dark\:hover\:text-green-400:hover{color:var(--color-green-400)}.dark .dark\:hover\:text-green-500:hover{color:var(--color-green-500)}.dark .dark\:hover\:text-green-600:hover{color:var(--color-green-600)}.dark .dark\:hover\:text-green-700:hover{color:var(--color-green-700)}.dark .dark\:hover\:text-green-800:hover{color:var(--color-green-800)}.dark .dark\:hover\:text-green-900:hover{color:var(--color-green-900)}.dark .dark\:hover\:text-purple-200:hover{color:var(--color-purple-200)}.dark .dark\:hover\:text-purple-300:hover{color:var(--color-purple-300)}.dark .dark\:hover\:text-red-50:hover{color:var(--color-red-50)}.dark .dark\:hover\:text-red-100:hover{color:var(--color-red-100)}.dark .dark\:hover\:text-red-200:hover{color:var(--color-red-200)}.dark .dark\:hover\:text-red-300:hover{color:var(--color-red-300)}.dark .dark\:hover\:text-red-400:hover{color:var(--color-red-400)}.dark .dark\:hover\:text-red-500:hover{color:var(--color-red-500)}.dark .dark\:hover\:text-red-600:hover{color:var(--color-red-600)}.dark .dark\:hover\:text-red-700:hover{color:var(--color-red-700)}.dark .dark\:hover\:text-red-800:hover{color:var(--color-red-800)}.dark .dark\:hover\:text-red-900:hover{color:var(--color-red-900)}.dark .dark\:hover\:text-slate-800:hover{color:var(--color-slate-800)}.dark .dark\:hover\:text-white:hover{color:var(--color-white)}}.dark .dark\:focus\:border-blue-400:focus{border-color:var(--color-blue-400)}.dark .dark\:focus\:border-gray-500:focus{border-color:var(--color-gray-500)}.dark .dark\:focus\:border-gray-600:focus{border-color:var(--color-gray-600)}.dark .dark\:focus\:border-gray-700:focus{border-color:var(--color-gray-700)}.dark .dark\:focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.dark .dark\:focus\:bg-gray-800:focus{background-color:var(--color-gray-800)}.dark .dark\:focus\:bg-gray-900:focus{background-color:var(--color-gray-900)}.dark .dark\:focus\:text-gray-100:focus{color:var(--color-gray-100)}.dark .dark\:focus\:text-gray-200:focus{color:var(--color-gray-200)}.dark .dark\:focus\:text-gray-300:focus{color:var(--color-gray-300)}.dark .dark\:focus\:text-white:focus{color:var(--color-white)}.dark .dark\:focus\:ring-amber-400:focus{--tw-ring-color:var(--color-amber-400)}.dark .dark\:focus\:ring-amber-500:focus{--tw-ring-color:var(--color-amber-500)}.dark .dark\:focus\:ring-amber-600:focus{--tw-ring-color:var(--color-amber-600)}.dark .dark\:focus\:ring-blue-400:focus{--tw-ring-color:var(--color-blue-400)}.dark .dark\:focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.dark .dark\:focus\:ring-blue-600:focus{--tw-ring-color:var(--color-blue-600)}.dark .dark\:focus\:ring-blue-700:focus{--tw-ring-color:var(--color-blue-700)}.dark .dark\:focus\:ring-gray-400:focus{--tw-ring-color:var(--color-gray-400)}.dark .dark\:focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.dark .dark\:focus\:ring-gray-600:focus{--tw-ring-color:var(--color-gray-600)}.dark .dark\:focus\:ring-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.dark .dark\:focus\:ring-indigo-400:focus{--tw-ring-color:var(--color-indigo-400)}.dark .dark\:focus\:ring-red-400:focus{--tw-ring-color:var(--color-red-400)}.dark .dark\:focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.dark .dark\:focus\:ring-slate-500:focus{--tw-ring-color:var(--color-slate-500)}.dark .dark\:focus\:ring-offset-gray-800:focus{--tw-ring-offset-color:var(--color-gray-800)}.dark .dark\:focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.dark .dark\:active\:bg-gray-600:active{background-color:var(--color-gray-600)}.dark .dark\:active\:bg-gray-700:active{background-color:var(--color-gray-700)}.dark .dark\:active\:bg-gray-800:active{background-color:var(--color-gray-800)}.dark .dark\:disabled\:bg-gray-700:disabled{background-color:var(--color-gray-700)}.dark .dark\:disabled\:bg-gray-800:disabled{background-color:var(--color-gray-800)}.dark .dark\:disabled\:bg-gray-900:disabled{background-color:var(--color-gray-900)}.dark .dark\:disabled\:text-gray-500:disabled{color:var(--color-gray-500)}.dark .dark\:disabled\:text-gray-600:disabled{color:var(--color-gray-600)}@media(hover:hover){.dark .dark\:disabled\:hover\:bg-indigo-500:disabled:hover{background-color:var(--color-indigo-500)}}.\[\&_table\]\:w-full table{width:100%}.\[\&_table\]\:border-collapse table{border-collapse:collapse}.\[\&_table\]\:border table{border-style:var(--tw-border-style);border-width:1px}.\[\&_table\]\:border-gray-300 table{border-color:var(--color-gray-300)}.dark .dark\:\[\&_table\]\:border-gray-600 table{border-color:var(--color-gray-600)}.\[\&_td\]\:border td{border-style:var(--tw-border-style);border-width:1px}.\[\&_td\]\:border-gray-300 td{border-color:var(--color-gray-300)}.\[\&_td\]\:px-4 td{padding-inline:calc(var(--spacing) * 4)}.\[\&_td\]\:py-2 td{padding-block:calc(var(--spacing) * 2)}.dark .dark\:\[\&_td\]\:border-gray-600 td{border-color:var(--color-gray-600)}.\[\&_th\]\:border th{border-style:var(--tw-border-style);border-width:1px}.\[\&_th\]\:border-gray-300 th{border-color:var(--color-gray-300)}.\[\&_th\]\:bg-gray-100 th{background-color:var(--color-gray-100)}.\[\&_th\]\:px-4 th{padding-inline:calc(var(--spacing) * 4)}.\[\&_th\]\:py-2 th{padding-block:calc(var(--spacing) * 2)}.\[\&_th\]\:text-left th{text-align:left}.dark .dark\:\[\&_th\]\:border-gray-600 th{border-color:var(--color-gray-600)}.dark .dark\:\[\&_th\]\:bg-gray-800 th{background-color:var(--color-gray-800)}.\[\&_tr\:hover\]\:bg-gray-50 tr:hover{background-color:var(--color-gray-50)}.dark .dark\:\[\&_tr\:hover\]\:bg-gray-700\/50 tr:hover{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:\[\&_tr\:hover\]\:bg-gray-700\/50 tr:hover{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}}.dashboard-content{max-width:80rem;margin:0 auto}.dashboard-stats-grid{gap:var(--spacing-5);margin-bottom:var(--spacing-8);grid-template-columns:1fr;display:grid}@media(min-width:768px){.dashboard-stats-grid{grid-template-columns:repeat(2,1fr)}}@media(min-width:1024px){.dashboard-stats-grid{grid-template-columns:repeat(4,1fr)}}.dashboard-main-grid{gap:var(--spacing-6);grid-template-columns:1fr;display:grid}@media(min-width:1024px){.dashboard-main-grid{grid-template-columns:repeat(5,1fr)}}.dashboard-column-left{gap:var(--spacing-6);flex-direction:column;display:flex}@media(min-width:1024px){.dashboard-column-left{grid-column:span 3}}.dashboard-column-right{gap:var(--spacing-6);flex-direction:column;display:flex}@media(min-width:1024px){.dashboard-column-right{grid-column:span 2}}.progress-bar-container{background-color:var(--color-slate-100);border-radius:var(--radius-full);width:100%;height:.5rem;overflow:hidden}.dark .progress-bar-container{background-color:var(--color-slate-700)}.progress-bar-fill{border-radius:var(--radius-full);height:.5rem;transition:width var(--transition-normal) var(--ease-in-out)}.progress-bar-emerald{background-color:var(--color-emerald-500)}.progress-bar-blue{background-color:var(--color-blue-500)}.progress-bar-slate{background-color:var(--color-slate-600)}.dark .progress-bar-slate{background-color:var(--color-slate-500)}.module-list{gap:var(--spacing-3);flex-direction:column;display:flex}.module-item{padding:var(--spacing-2) 0;justify-content:space-between;align-items:center;display:flex}.module-info{flex-direction:column;display:flex}.module-name{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .module-name{color:#fff}.module-version{font-size:var(--text-xs);color:var(--color-slate-500)}.dark .module-version{color:var(--color-slate-400)}.module-badge-active{padding:var(--spacing-1) var(--spacing-2);font-size:var(--text-xs);font-weight:var(--font-medium);background-color:var(--color-green-100);color:var(--color-green-700);border-radius:var(--radius-full)}.dark .module-badge-active{background-color:var(--color-green-900);color:var(--color-green-400)}.module-badge-inactive{padding:var(--spacing-1) var(--spacing-2);font-size:var(--text-xs);font-weight:var(--font-medium);background-color:var(--color-slate-100);color:var(--color-slate-600);border-radius:var(--radius-full)}.dark .module-badge-inactive{background-color:var(--color-slate-700);color:var(--color-slate-400)}.activity-list{gap:var(--spacing-4);flex-direction:column;display:flex}.activity-item{align-items:flex-start;gap:var(--spacing-3);display:flex}.activity-dot{width:.5rem;height:.5rem;margin-top:var(--spacing-2);border-radius:var(--radius-full);background-color:var(--color-blue-500);flex-shrink:0}.activity-content{flex:1;min-width:0}.activity-title{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .activity-title{color:#fff}.activity-desc{font-size:var(--text-sm);color:var(--color-slate-500)}.dark .activity-desc{color:var(--color-slate-400)}.activity-time{font-size:var(--text-xs);color:var(--color-slate-400);white-space:nowrap}.dark .activity-time{color:var(--color-slate-500)}.alert-list{gap:var(--spacing-3);flex-direction:column;display:flex}.alert-item{align-items:flex-start;gap:var(--spacing-3);padding:var(--spacing-3);border-radius:var(--radius-lg);display:flex}.alert-bg-info{background-color:var(--color-blue-50)}.dark .alert-bg-info{background-color:var(--color-slate-800)}.alert-bg-warning{background-color:var(--color-yellow-50)}.dark .alert-bg-warning{background-color:var(--color-slate-800)}.alert-bg-error{background-color:var(--color-red-50)}.dark .alert-bg-error{background-color:var(--color-slate-800)}.alert-bg-success{background-color:var(--color-green-50)}.dark .alert-bg-success{background-color:var(--color-slate-800)}.alert-dot{width:.5rem;height:.5rem;margin-top:var(--spacing-2);border-radius:var(--radius-full);flex-shrink:0}.alert-dot-info{background-color:var(--color-blue-500)}.alert-dot-warning{background-color:var(--color-yellow-500)}.alert-dot-error{background-color:var(--color-red-500)}.alert-dot-success{background-color:var(--color-green-500)}.alert-content{flex:1}.alert-title{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .alert-title{color:#fff}.alert-time{font-size:var(--text-xs);color:var(--color-slate-500);margin-top:var(--spacing-1)}.dark .alert-time{color:var(--color-slate-400)}.text-subtitle{font-size:var(--text-sm);color:var(--color-slate-500)}.dark .text-subtitle{color:var(--color-slate-400)}.section-margin{margin-bottom:var(--spacing-6)}.flex-between{justify-content:space-between;align-items:center;display:flex}.flex-between-mb{margin-bottom:var(--spacing-2);justify-content:space-between;align-items:center;display:flex}.resource-label{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-700)}.dark .resource-label{color:var(--color-slate-300)}.resource-value{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-700)}.dark .resource-value{color:var(--color-slate-300)}.icon-stat{width:1rem;height:1rem}.icon-trend{width:.875rem;height:.875rem}@media(max-width:640px){.input-split{flex-wrap:wrap}.input-split input{flex:1;min-width:0}.page-header{flex-direction:column;align-items:flex-start;gap:1rem}.page-actions{justify-content:flex-start;width:100%}}@media(min-width:640px){.responsive-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1024px){.responsive-grid,.grid-cols-1-lg-3{grid-template-columns:repeat(3,minmax(0,1fr))}.col-span-1-lg{grid-column:span 1/span 1}.col-span-2-lg{grid-column:span 2/span 2}}@media(max-width:480px){.responsive-grid{grid-template-columns:1fr!important}}@media(max-width:768px){.table-mobile-cards{display:block}.table-mobile-cards>*+*{margin-top:1rem}.table-desktop{display:none!important}}@media(max-width:640px){.modal{max-width:calc(100% - 2rem);max-height:calc(100vh - 2rem);margin-left:1rem;margin-right:1rem;overflow-y:auto}.modal-overlay{align-items:flex-start;padding:2rem 1rem 1rem}.modal-footer{flex-direction:column;gap:.5rem}.modal-footer .btn,.card-account{width:100%}}.sidebar-scrollbar{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .3s}.sidebar-scrollbar:hover{scrollbar-color:#d1d5db transparent}.dark .sidebar-scrollbar:hover{scrollbar-color:#4b5563 transparent}.sidebar-scrollbar::-webkit-scrollbar{width:6px}.sidebar-scrollbar::-webkit-scrollbar-track{background:0 0}.sidebar-scrollbar::-webkit-scrollbar-thumb{background:0 0;border-radius:3px;transition:background .3s}.sidebar-scrollbar:hover::-webkit-scrollbar-thumb{background:#d1d5db}.dark .sidebar-scrollbar:hover::-webkit-scrollbar-thumb{background:#4b5563}.sidebar-scrollbar::-webkit-scrollbar-thumb:hover{background:#9ca3af}.dark .sidebar-scrollbar::-webkit-scrollbar-thumb:hover{background:#6b7280}@property --tw-border-spacing-x{syntax:"";inherits:false;initial-value:0}@property --tw-border-spacing-y{syntax:"";inherits:false;initial-value:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-scroll-snap-strictness{syntax:"*";inherits:false;initial-value:proximity}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.yarl__fullsize{height:100%;width:100%}.yarl__relative{position:relative}.yarl__portal{inset:0;opacity:0;overflow:hidden;pointer-events:auto;position:fixed;transition:opacity var(--yarl__fade_animation_duration,.25s) var(--yarl__fade_animation_timing_function,ease);z-index:var(--yarl__portal_zindex,9999)}.yarl__portal_open{opacity:1}.yarl__container{background-color:var(--yarl__container_background_color,var(--yarl__color_backdrop,#000));inset:0;outline:none;overflow:hidden;overscroll-behavior:var(--yarl__controller_overscroll_behavior,contain);position:absolute;touch-action:var(--yarl__controller_touch_action,none);-webkit-user-select:none;-moz-user-select:none;user-select:none}.yarl__carousel{align-content:center;align-items:stretch;display:flex;flex:0 0 auto;height:100%;justify-content:center;opacity:var(--yarl__pull_opacity,1);transform:translate(var(--yarl__swipe_offset,0),var(--yarl__pull_offset,0));width:calc(100% + (var(--yarl__carousel_slides_count) - 1)*(100% + var(--yarl__carousel_spacing_px, 0)*1px + var(--yarl__carousel_spacing_percent, 0)*1%))}.yarl__carousel_with_slides{-moz-column-gap:calc(var(--yarl__carousel_spacing_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_spacing_percent, 0)*1%);column-gap:calc(var(--yarl__carousel_spacing_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_spacing_percent, 0)*1%)}.yarl__flex_center{align-content:center;align-items:center;display:flex;justify-content:center}.yarl__slide{flex:1;overflow:hidden;padding:calc(var(--yarl__carousel_padding_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_padding_percent, 0)*1%);position:relative}[dir=rtl] .yarl__slide{--yarl__direction:-1}.yarl__slide_image{max-height:100%;max-width:100%;-o-object-fit:contain;object-fit:contain;touch-action:var(--yarl__controller_touch_action,none);-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-touch-callout:none}.yarl__slide_image_cover{height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.yarl__slide_image_loading{opacity:0}@media screen and (min-width:800px){.yarl__slide_wrapper:not(.yarl__slide_wrapper_interactive) .yarl__slide_image{-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0);-webkit-transform-style:preserve-3d}}.yarl__slide_placeholder{left:50%;line-height:0;position:absolute;top:50%;transform:translate(-50%) translateY(-50%)}.yarl__slide_loading{animation:yarl__delayed_fadein 1s linear;color:var(--yarl__slide_icon_loading_color,var(--yarl__color_button,hsla(0,0%,100%,.8)))}.yarl__slide_loading line{animation:yarl__stroke_opacity 1s linear infinite}.yarl__slide_loading line:first-of-type{animation-delay:-1.875s}.yarl__slide_loading line:nth-of-type(2){animation-delay:-1.75s}.yarl__slide_loading line:nth-of-type(3){animation-delay:-1.625s}.yarl__slide_loading line:nth-of-type(4){animation-delay:-1.5s}.yarl__slide_loading line:nth-of-type(5){animation-delay:-1.375s}.yarl__slide_loading line:nth-of-type(6){animation-delay:-1.25s}.yarl__slide_loading line:nth-of-type(7){animation-delay:-1.125s}.yarl__slide_loading line:nth-of-type(8){animation-delay:-1s}.yarl__slide_error{color:var(--yarl__slide_icon_error_color,red);height:var(--yarl__slide_icon_error_size,48px);width:var(--yarl__slide_icon_error_size,48px)}@media(prefers-reduced-motion){.yarl__portal,.yarl__slide{transition:unset}.yarl__slide_loading,.yarl__slide_loading line{animation:unset}}.yarl__toolbar{display:flex;justify-content:flex-end;inset:0 0 auto auto;padding:var(--yarl__toolbar_padding,8px);position:absolute}[dir=rtl] .yarl__toolbar{inset:0 auto auto 0}.yarl__icon{height:var(--yarl__icon_size,32px);width:var(--yarl__icon_size,32px)}.yarl__button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--yarl__button_background_color,transparent);border:var(--yarl__button_border,0);color:var(--yarl__color_button,hsla(0,0%,100%,.8));cursor:pointer;filter:var(--yarl__button_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));line-height:0;margin:var(--yarl__button_margin,0);outline:none;padding:var(--yarl__button_padding,8px);-webkit-tap-highlight-color:transparent}.yarl__button:focus{color:var(--yarl__color_button_active,#fff)}.yarl__button:focus:not(:focus-visible){color:var(--yarl__color_button,hsla(0,0%,100%,.8))}.yarl__button:focus-visible{color:var(--yarl__color_button_active,#fff)}@media(hover:hover){.yarl__button:focus-visible:hover,.yarl__button:focus:hover,.yarl__button:hover{color:var(--yarl__color_button_active,#fff)}}.yarl__button:disabled{color:var(--yarl__color_button_disabled,hsla(0,0%,100%,.4));cursor:default}.yarl__navigation_next,.yarl__navigation_prev{padding:var(--yarl__navigation_button_padding,24px 16px);position:absolute;top:50%;transform:translateY(-50%)}.yarl__navigation_prev{left:0}[dir=rtl] .yarl__navigation_prev{left:unset;right:0;transform:translateY(-50%) rotate(180deg)}.yarl__navigation_next{right:0}[dir=rtl] .yarl__navigation_next{left:0;right:unset;transform:translateY(-50%) rotate(180deg)}.yarl__no_scroll{height:100%;overflow:hidden;overscroll-behavior:none}@keyframes yarl__delayed_fadein{0%{opacity:0}80%{opacity:0}to{opacity:1}}@keyframes yarl__stroke_opacity{0%{stroke-opacity:1}to{stroke-opacity:.125}}.yarl__counter{bottom:var(--yarl__counter_bottom,unset);color:var(--yarl__counter_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));filter:var(--yarl__counter_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));left:var(--yarl__counter_left,0);line-height:var(--yarl__counter_line_height,var(--yarl__icon_size,32px));margin:var(--yarl__counter_margin,var(--yarl__toolbar_padding,8px));padding:var(--yarl__counter_padding,var(--yarl__button_padding,8px));position:var(--yarl__counter_position,absolute);right:var(--yarl__counter_right,unset);top:var(--yarl__counter_top,0);-webkit-user-select:var(--yarl__counter_user_select,none);-moz-user-select:var(--yarl__counter_user_select,none);user-select:var(--yarl__counter_user_select,none)}[dir=rtl] .yarl__counter{left:var(--yarl__counter_rtl_left,unset);right:var(--yarl__counter_rtl_right,0)}.yarl__thumbnails{display:flex;height:100%}.yarl__thumbnails_bottom,.yarl__thumbnails_end .yarl__thumbnails_track,.yarl__thumbnails_start .yarl__thumbnails_track,.yarl__thumbnails_top{flex-direction:column}.yarl__thumbnails_wrapper{flex:1;position:relative}.yarl__thumbnails_container{background-color:var(--yarl__thumbnails_container_background_color,var(--yarl__color_backdrop,#000));flex:0 0 auto;overflow:hidden;padding:var(--yarl__thumbnails_container_padding,16px);position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-touch-callout:none}.yarl__thumbnails_vignette{pointer-events:none;position:absolute;--yarl__thumbnails_vignette_size:12%}@media(min-width:1200px){.yarl__thumbnails_vignette{--yarl__thumbnails_vignette_size:8%}}@media(min-width:2000px){.yarl__thumbnails_vignette{--yarl__thumbnails_vignette_size:5%}}.yarl__thumbnails_bottom .yarl__thumbnails_vignette,.yarl__thumbnails_top .yarl__thumbnails_vignette{background:linear-gradient(to right,var(--yarl__color_backdrop,#000) 0,transparent var(--yarl__thumbnails_vignette_size,12%) calc(100% - var(--yarl__thumbnails_vignette_size, 12%)),var(--yarl__color_backdrop,#000) 100%);height:100%;left:0;right:0}.yarl__thumbnails_end .yarl__thumbnails_vignette,.yarl__thumbnails_start .yarl__thumbnails_vignette{background:linear-gradient(to bottom,var(--yarl__color_backdrop,#000) 0,transparent var(--yarl__thumbnails_vignette_size,12%) calc(100% - var(--yarl__thumbnails_vignette_size, 12%)),var(--yarl__color_backdrop,#000) 100%);bottom:0;top:0;width:100%}.yarl__thumbnails_track{gap:var(--yarl__thumbnails_thumbnail_gap,16px);outline:none}.yarl__thumbnails_thumbnail{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--yarl__thumbnails_thumbnail_background,#000);border-color:var(--yarl__thumbnails_thumbnail_border_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));border-radius:var(--yarl__thumbnails_thumbnail_border_radius,4px);border-style:var(--yarl__thumbnails_thumbnail_border_style,solid);border-width:var(--yarl__thumbnails_thumbnail_border,1px);cursor:pointer;flex:0 0 auto;-webkit-tap-highlight-color:transparent;box-sizing:content-box;height:var(--yarl__thumbnails_thumbnail_height,80px);outline:none;overflow:hidden;padding:var(--yarl__thumbnails_thumbnail_padding,4px);position:relative;width:var(--yarl__thumbnails_thumbnail_width,120px)}.yarl__thumbnails_thumbnail_active{border-color:var(--yarl__thumbnails_thumbnail_active_border_color,var(--yarl__color_button_active,#fff))}.yarl__thumbnails_thumbnail_fadein{animation:yarl__thumbnails_thumbnail_fadein var(--yarl__thumbnails_thumbnail_fadein_duration,.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadein_delay,0s) forwards;opacity:0}.yarl__thumbnails_thumbnail_fadeout{animation:yarl__thumbnails_thumbnail_fadeout var(--yarl__thumbnails_thumbnail_fadeout_duration,.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadeout_delay,0s) forwards;cursor:unset}.yarl__thumbnails_thumbnail_placeholder{cursor:unset;visibility:hidden}.yarl__thumbnails_thumbnail:focus{box-shadow:var(--yarl__thumbnails_thumbnail_focus_box_shadow,#000 0 0 0 2px,var(--yarl__color_button,hsla(0,0%,100%,.8)) 0 0 0 4px)}.yarl__thumbnails_thumbnail:focus:not(:focus-visible){box-shadow:unset}.yarl__thumbnails_thumbnail:focus-visible{box-shadow:var(--yarl__thumbnails_thumbnail_focus_box_shadow,#000 0 0 0 2px,var(--yarl__color_button,hsla(0,0%,100%,.8)) 0 0 0 4px)}.yarl__thumbnails_thumbnail_icon{color:var(--yarl__thumbnails_thumbnail_icon_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));filter:var(--yarl__thumbnails_thumbnail_icon_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));height:var(--yarl__thumbnails_thumbnail_icon_size,32px);left:50%;position:absolute;top:50%;transform:translate(-50%) translateY(-50%);width:var(--yarl__thumbnails_thumbnail_icon_size,32px)}@keyframes yarl__thumbnails_thumbnail_fadein{0%{opacity:0}to{opacity:1}}@keyframes yarl__thumbnails_thumbnail_fadeout{0%{opacity:1}to{opacity:0}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-scroll-snap-strictness:proximity;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Pretendard", ui-sans-serif, system-ui, -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-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-lime-50:oklch(98.6% .031 120.757);--color-lime-100:oklch(96.7% .067 122.328);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-lime-400:oklch(84.1% .238 128.85);--color-lime-500:oklch(76.8% .233 130.85);--color-lime-600:oklch(64.8% .2 131.684);--color-lime-700:oklch(53.2% .157 131.589);--color-lime-800:oklch(45.3% .124 130.933);--color-lime-900:oklch(40.5% .101 131.063);--color-lime-950:oklch(27.4% .072 132.109);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-emerald-950:oklch(26.2% .051 172.552);--color-teal-50:oklch(98.4% .014 180.72);--color-teal-100:oklch(95.3% .051 180.801);--color-teal-200:oklch(91% .096 180.426);--color-teal-300:oklch(85.5% .138 181.071);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-teal-600:oklch(60% .118 184.704);--color-teal-700:oklch(51.1% .096 186.391);--color-teal-800:oklch(43.7% .078 188.216);--color-teal-900:oklch(38.6% .063 188.416);--color-teal-950:oklch(27.7% .046 192.524);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-100:oklch(95.6% .045 203.388);--color-cyan-200:oklch(91.7% .08 205.041);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-cyan-700:oklch(52% .105 223.128);--color-cyan-800:oklch(45% .085 224.283);--color-cyan-900:oklch(39.8% .07 227.392);--color-cyan-950:oklch(30.2% .056 229.695);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-200:oklch(90.1% .058 230.902);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-600:oklch(58.8% .158 241.966);--color-sky-700:oklch(50% .134 242.749);--color-sky-800:oklch(44.3% .11 240.79);--color-sky-900:oklch(39.1% .09 240.876);--color-sky-950:oklch(29.3% .066 243.157);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-blue-950:oklch(28.2% .091 267.935);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-300:oklch(78.5% .115 274.713);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-indigo-950:oklch(25.7% .09 281.288);--color-violet-50:oklch(96.9% .016 293.756);--color-violet-100:oklch(94.3% .029 294.588);--color-violet-200:oklch(89.4% .057 293.283);--color-violet-300:oklch(81.1% .111 293.571);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-500:oklch(60.6% .25 292.717);--color-violet-600:oklch(54.1% .281 293.009);--color-violet-700:oklch(49.1% .27 292.581);--color-violet-800:oklch(43.2% .232 292.759);--color-violet-900:oklch(38% .189 293.745);--color-violet-950:oklch(28.3% .141 291.089);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-fuchsia-50:oklch(97.7% .017 320.058);--color-fuchsia-100:oklch(95.2% .037 318.852);--color-fuchsia-200:oklch(90.3% .076 319.62);--color-fuchsia-300:oklch(83.3% .145 321.434);--color-fuchsia-400:oklch(74% .238 322.16);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-fuchsia-600:oklch(59.1% .293 322.896);--color-fuchsia-700:oklch(51.8% .253 323.949);--color-fuchsia-800:oklch(45.2% .211 324.591);--color-fuchsia-900:oklch(40.1% .17 325.612);--color-fuchsia-950:oklch(29.3% .136 325.661);--color-pink-50:oklch(97.1% .014 343.198);--color-pink-100:oklch(94.8% .028 342.258);--color-pink-200:oklch(89.9% .061 343.231);--color-pink-300:oklch(82.3% .12 346.018);--color-pink-400:oklch(71.8% .202 349.761);--color-pink-500:oklch(65.6% .241 354.308);--color-pink-600:oklch(59.2% .249 .584);--color-pink-700:oklch(52.5% .223 3.958);--color-pink-800:oklch(45.9% .187 3.815);--color-pink-900:oklch(40.8% .153 2.432);--color-pink-950:oklch(28.4% .109 3.907);--color-rose-50:oklch(96.9% .015 12.422);--color-rose-100:oklch(94.1% .03 12.58);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-rose-700:oklch(51.4% .222 16.935);--color-rose-800:oklch(45.5% .188 13.697);--color-rose-900:oklch(41% .159 10.272);--color-rose-950:oklch(27.1% .105 12.094);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-slate-950:oklch(12.9% .042 264.695);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-gray-950:oklch(13% .028 261.692);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-neutral-50:oklch(98.5% 0 none);--color-neutral-100:oklch(97% 0 none);--color-neutral-200:oklch(92.2% 0 none);--color-neutral-300:oklch(87% 0 none);--color-neutral-400:oklch(70.8% 0 none);--color-neutral-500:oklch(55.6% 0 none);--color-neutral-600:oklch(43.9% 0 none);--color-neutral-700:oklch(37.1% 0 none);--color-neutral-800:oklch(26.9% 0 none);--color-neutral-900:oklch(20.5% 0 none);--color-neutral-950:oklch(14.5% 0 none);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-sm:40rem;--breakpoint-md:48rem;--breakpoint-lg:64rem;--breakpoint-xl:80rem;--breakpoint-2xl:96rem;--container-3xs:16rem;--container-2xs:18rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-5xl:3rem;--text-5xl--line-height:1;--text-6xl:3.75rem;--text-6xl--line-height:1;--text-7xl:4.5rem;--text-7xl--line-height:1;--text-8xl:6rem;--text-8xl--line-height:1;--text-9xl:8rem;--text-9xl--line-height:1;--font-weight-thin:100;--font-weight-extralight:200;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tighter:-.05em;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--leading-loose:2;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--drop-shadow-sm:0 1px 2px #00000026;--drop-shadow-md:0 3px 3px #0000001f;--drop-shadow-lg:0 4px 4px #00000026;--drop-shadow-xl:0 9px 7px #0000001a;--drop-shadow-2xl:0 25px 25px #00000026;--ease-in:cubic-bezier(.4, 0, 1, 1);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--blur-xl:24px;--blur-2xl:40px;--blur-3xl:64px;--aspect-video:16 / 9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}body{color:var(--color-gray-900);font-size:.875rem;line-height:1.5}.dark :is(body){color:var(--color-gray-100)}}@layer components{.admin_layout_root{background-color:var(--color-gray-50);height:100vh;display:flex;overflow:hidden}.dark .admin_layout_root{background-color:var(--color-gray-900)}.mobile_overlay{z-index:40;background-color:#00000080;position:fixed;inset:0}.left_sidebar_area{z-index:50;width:calc(var(--spacing) * 64);background-color:var(--color-white);inset-block:0;flex-direction:column;display:flex;position:relative;left:0}.dark .left_sidebar_area{background-color:var(--color-gray-800)}.left_sidebar_area{transition:width .3s}.left_sidebar_area_collapsed{width:0;overflow:hidden}.left_sidebar_area_portable{z-index:50;width:calc(var(--spacing) * 64);background-color:var(--color-white);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.3s;flex-direction:column;transition-duration:.3s;display:flex;position:fixed;left:0}.dark .left_sidebar_area_portable{background-color:var(--color-gray-800)}.left_sidebar_area_portable{top:4rem;bottom:0;transform:translate(-100%)}.left_sidebar_area_portable_open{transform:translate(0)}.sidebar_header{height:calc(var(--spacing) * 16);padding-inline:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.sidebar_logo{height:calc(var(--spacing) * 10);align-items:center;gap:calc(var(--spacing) * 2);flex:1;min-width:0;display:flex}.logo_image{max-height:calc(var(--spacing) * 10);object-fit:contain;width:auto}.admin_sidebar_menu{padding-top:calc(var(--spacing) * 4);box-shadow:inset 3px 2px 2px var(--tw-shadow-color,#0000000a);flex:1;overflow-y:auto}.admin_sidebar_menu_portable{padding-top:calc(var(--spacing) * 4);flex:1;overflow-y:auto}.admin_sidebar_empty{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 8);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-400)}.dark .admin_sidebar_empty{color:var(--color-gray-500)}.admin_sidebar_empty_icon{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));color:var(--color-gray-300)}.dark .admin_sidebar_empty_icon{color:var(--color-gray-600)}.sidebar_footer{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100);padding:calc(var(--spacing) * 3);flex-shrink:0}.dark .sidebar_footer{border-color:var(--color-gray-700)}.sidebar_view_site_link{height:calc(var(--spacing) * 9);align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));text-decoration-line:none;display:flex}@media(hover:hover){.sidebar_view_site_link:hover{background-color:var(--color-slate-100);color:var(--color-slate-900)}}.dark .sidebar_view_site_link{color:var(--color-slate-300)}@media(hover:hover){.dark .sidebar_view_site_link:hover{background-color:var(--color-slate-700);color:var(--color-white)}}.sidebar_view_site_icon{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));flex-shrink:0}.sidebar_view_site_label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.right_content_wrapper{flex-direction:column;flex:1;min-width:0;display:flex}.right_content_area{flex:1;min-width:0;min-height:0;overflow:auto}.main_header{z-index:50;height:calc(var(--spacing) * 16);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 8);flex-shrink:0;justify-content:space-between;align-items:center;display:flex;position:relative}.dark .main_header{background-color:var(--color-gray-800)}.main_header{box-shadow:0 1px 3px #00000014}.header_left{flex:1;align-items:center;display:flex}.header_right{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.menu_toggle_btn{margin-left:calc(var(--spacing) * -2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);color:var(--color-slate-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.menu_toggle_btn:hover{background-color:var(--color-slate-100);color:var(--color-slate-900)}}.dark .menu_toggle_btn{color:var(--color-slate-400)}@media(hover:hover){.dark .menu_toggle_btn:hover{background-color:var(--color-slate-700);color:var(--color-white)}}.menu_toggle_btn_icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.user_avatar_section{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 3);display:flex;position:relative}.user_avatar{height:calc(var(--spacing) * 9);width:calc(var(--spacing) * 9);background-color:var(--color-gray-200);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex;overflow:hidden}.dark .user_avatar{background-color:var(--color-gray-600)}.user_avatar_img{object-fit:cover;width:100%;height:100%}.user_info{display:none}@media(min-width:48rem){.user_info{display:block}}.mobile_header_portable{z-index:50;height:calc(var(--spacing) * 16);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex;position:relative}.dark .mobile_header_portable{background-color:var(--color-gray-800)}.mobile_header_portable{box-shadow:0 1px 3px #00000014}.mobile_header_left{align-items:center;gap:calc(var(--spacing) * 3);display:flex}.mobile_header_right{align-items:center;gap:calc(var(--spacing) * 2);flex-shrink:0;display:flex}.mobile_logo{align-items:center;gap:calc(var(--spacing) * 2);flex:1;min-width:0;display:flex}.mobile_logo_image{height:calc(var(--spacing) * 8);object-fit:contain;width:auto;max-width:100%}.main_content{min-height:fit-content}.layout_warnings{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2)}.text-primary{color:var(--color-gray-900)}.dark .text-primary{color:var(--color-white)}.text-secondary{color:var(--color-gray-600)}.dark .text-secondary{color:var(--color-gray-400)}.text-muted,.dark .text-muted{color:var(--color-gray-500)}.text-tertiary{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .text-tertiary{color:var(--color-gray-400)}.text-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .text-label{color:var(--color-gray-400)}.text-label-subtle{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .text-label-subtle{color:var(--color-gray-400)}.text-heading{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .text-heading{color:var(--color-white)}.section-heading{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-heading{color:var(--color-white)}.section-title{margin-bottom:var(--spacing);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-title{color:var(--color-white)}.section-description{margin-bottom:calc(var(--spacing) * 6);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .section-description{color:var(--color-gray-400)}.card-title{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .card-title{color:var(--color-white)}.card-title-aside{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);display:flex}.card-description{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .card-description{color:var(--color-gray-400)}.admin-card>.card-title{margin-bottom:calc(var(--spacing) * 4)}.admin-card>.card-title:has(+.card-description){margin-bottom:0}.admin-card>.card-description{margin-bottom:calc(var(--spacing) * 4)}.card-header{align-items:center;gap:calc(var(--spacing) * 2);--tw-leading:1;margin-bottom:0;line-height:1;display:flex}.card-header>*{--tw-leading:1;line-height:1}.text-body{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}.dark .text-body{color:var(--color-gray-300)}.icon-muted{color:var(--color-gray-400)}.dark .icon-muted{color:var(--color-gray-500)}.bg-success{background-color:var(--color-green-100)}.dark .bg-success{background-color:var(--color-green-900)}.bg-success-light{background-color:var(--color-green-50)}.dark .bg-success-light{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .bg-success-light{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.bg-success-solid{background-color:var(--color-green-600)}.dark .bg-success-solid{background-color:var(--color-green-500)}.bg-error{background-color:var(--color-red-100)}.dark .bg-error{background-color:var(--color-red-900)}.bg-error-light{background-color:var(--color-red-50)}.dark .bg-error-light{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .bg-error-light{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.bg-error-solid{background-color:var(--color-red-600)}.dark .bg-error-solid{background-color:var(--color-red-500)}.bg-warning{background-color:var(--color-yellow-100)}.dark .bg-warning{background-color:var(--color-yellow-900)}.bg-warning-light{background-color:var(--color-yellow-50)}.dark .bg-warning-light{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .bg-warning-light{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.bg-warning-solid{background-color:var(--color-yellow-600)}.dark .bg-warning-solid{background-color:var(--color-yellow-500)}.bg-info{background-color:var(--color-blue-100)}.dark .bg-info{background-color:var(--color-blue-900)}.bg-info-light{background-color:var(--color-blue-50)}.dark .bg-info-light{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .bg-info-light{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.bg-info-solid{background-color:var(--color-blue-600)}.dark .bg-info-solid{background-color:var(--color-blue-500)}.bg-ready{background-color:var(--color-cyan-100)}.dark .bg-ready{background-color:var(--color-cyan-900)}.bg-ready-light{background-color:var(--color-cyan-50)}.dark .bg-ready-light{background-color:#104e6433}@supports (color:color-mix(in lab,red,red)){.dark .bg-ready-light{background-color:color-mix(in oklab,var(--color-cyan-900) 20%,transparent)}}.bg-ready-solid{background-color:var(--color-cyan-600)}.dark .bg-ready-solid{background-color:var(--color-cyan-400)}.bg-progress{background-color:var(--color-indigo-100)}.dark .bg-progress{background-color:var(--color-indigo-900)}.bg-progress-light{background-color:var(--color-indigo-50)}.dark .bg-progress-light{background-color:#312c8533}@supports (color:color-mix(in lab,red,red)){.dark .bg-progress-light{background-color:color-mix(in oklab,var(--color-indigo-900) 20%,transparent)}}.bg-progress-solid{background-color:var(--color-indigo-600)}.dark .bg-progress-solid{background-color:var(--color-indigo-400)}.bg-complete{background-color:var(--color-pink-100)}.dark .bg-complete{background-color:var(--color-pink-900)}.bg-complete-light{background-color:var(--color-pink-50)}.dark .bg-complete-light{background-color:#86104333}@supports (color:color-mix(in lab,red,red)){.dark .bg-complete-light{background-color:color-mix(in oklab,var(--color-pink-900) 20%,transparent)}}.bg-complete-solid{background-color:var(--color-pink-600)}.dark .bg-complete-solid{background-color:var(--color-pink-400)}.bg-hold{background-color:var(--color-gray-100)}.dark .bg-hold{background-color:var(--color-gray-800)}.bg-hold-light{background-color:var(--color-gray-50)}.dark .bg-hold-light{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .bg-hold-light{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.bg-hold-solid{background-color:var(--color-gray-500)}.dark .bg-hold-solid{background-color:var(--color-gray-400)}.text-success{color:var(--color-green-600)}.dark .text-success{color:var(--color-green-400)}.text-error{color:var(--color-red-600)}.dark .text-error{color:var(--color-red-400)}.text-warning{color:var(--color-yellow-600)}.dark .text-warning{color:var(--color-yellow-400)}.text-info{color:var(--color-blue-600)}.dark .text-info{color:var(--color-blue-400)}.text-ready{color:var(--color-cyan-600)}.dark .text-ready{color:var(--color-cyan-400)}.text-progress{color:var(--color-indigo-600)}.dark .text-progress{color:var(--color-indigo-400)}.text-complete{color:var(--color-pink-600)}.dark .text-complete{color:var(--color-pink-400)}.text-hold{color:var(--color-gray-500)}.dark .text-hold{color:var(--color-gray-400)}.text-success-light{color:var(--color-white)}.dark .text-success-light{color:var(--color-green-900)}.text-error-light{color:var(--color-white)}.dark .text-error-light{color:var(--color-red-900)}.text-warning-light{color:var(--color-white)}.dark .text-warning-light{color:var(--color-yellow-900)}.text-info-light{color:var(--color-white)}.dark .text-info-light{color:var(--color-blue-900)}.text-ready-light{color:var(--color-white)}.dark .text-ready-light{color:var(--color-cyan-900)}.text-progress-light{color:var(--color-white)}.dark .text-progress-light{color:var(--color-indigo-900)}.text-complete-light{color:var(--color-white)}.dark .text-complete-light{color:var(--color-pink-900)}.text-hold-light{color:var(--color-white)}.dark .text-hold-light{color:var(--color-gray-900)}.border-success{border-color:var(--color-green-200)}.dark .border-success{border-color:var(--color-green-800)}.border-error{border-color:var(--color-red-200)}.dark .border-error{border-color:var(--color-red-800)}.border-warning{border-color:var(--color-yellow-200)}.dark .border-warning{border-color:var(--color-yellow-800)}.border-info{border-color:var(--color-blue-200)}.dark .border-info{border-color:var(--color-blue-800)}.border-ready{border-color:var(--color-cyan-200)}.dark .border-ready{border-color:var(--color-cyan-800)}.border-progress{border-color:var(--color-indigo-200)}.dark .border-progress{border-color:var(--color-indigo-800)}.border-complete{border-color:var(--color-pink-200)}.dark .border-complete{border-color:var(--color-pink-800)}.border-hold{border-color:var(--color-gray-300)}.dark .border-hold{border-color:var(--color-gray-600)}.btn-success{background-color:var(--color-green-600);color:var(--color-white)}@media(hover:hover){.btn-success:hover{background-color:var(--color-green-700)}}.dark .btn-success{background-color:var(--color-green-500)}@media(hover:hover){.dark .btn-success:hover{background-color:var(--color-green-600)}}.btn-danger{background-color:var(--color-red-600);color:var(--color-white)}@media(hover:hover){.btn-danger:hover{background-color:var(--color-red-700)}}.dark .btn-danger{background-color:var(--color-red-500)}@media(hover:hover){.dark .btn-danger:hover{background-color:var(--color-red-600)}}.btn-warning{background-color:var(--color-yellow-600);color:var(--color-white)}@media(hover:hover){.btn-warning:hover{background-color:var(--color-yellow-700)}}.dark .btn-warning{background-color:var(--color-yellow-500)}@media(hover:hover){.dark .btn-warning:hover{background-color:var(--color-yellow-600)}}.btn-info{background-color:var(--color-blue-600);color:var(--color-white)}@media(hover:hover){.btn-info:hover{background-color:var(--color-blue-700)}}.dark .btn-info{background-color:var(--color-blue-500)}@media(hover:hover){.dark .btn-info:hover{background-color:var(--color-blue-600)}}.btn-neutral{background-color:var(--color-gray-600);color:var(--color-white)}@media(hover:hover){.btn-neutral:hover{background-color:var(--color-gray-700)}}.dark .btn-neutral{background-color:var(--color-gray-500)}@media(hover:hover){.dark .btn-neutral:hover{background-color:var(--color-gray-600)}}.btn-sm{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5)}.button{cursor:pointer;justify-content:center;align-items:center;display:inline-flex}.button:disabled{cursor:not-allowed}.btn{cursor:pointer;justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;transition-duration:.2s;display:inline-flex}.btn:disabled{cursor:not-allowed;opacity:.5}.btn-primary{background-color:var(--color-gray-900);color:var(--color-white)}@media(hover:hover){.btn-primary:hover{background-color:var(--color-gray-800)}}.dark .btn-primary{background-color:var(--color-white);color:var(--color-gray-900)}@media(hover:hover){.dark .btn-primary:hover{background-color:var(--color-gray-100)}}.btn-secondary{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);color:var(--color-gray-700)}@media(hover:hover){.btn-secondary:hover{background-color:var(--color-gray-50)}}.dark .btn-secondary{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-300)}@media(hover:hover){.dark .btn-secondary:hover{background-color:var(--color-gray-700)}}.btn-outline{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);background-color:#0000}@media(hover:hover){.btn-outline:hover{background-color:var(--color-gray-50)}}.dark .btn-outline{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .btn-outline:hover{background-color:var(--color-gray-700)}}.btn-ghost{color:var(--color-gray-600);background-color:#0000}@media(hover:hover){.btn-ghost:hover{background-color:var(--color-gray-100)}}.dark .btn-ghost{color:var(--color-gray-400)}@media(hover:hover){.dark .btn-ghost:hover{background-color:var(--color-gray-700)}}.btn-icon{border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);color:var(--color-gray-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.btn-icon:hover{background-color:var(--color-gray-100)}}.dark .btn-icon{color:var(--color-gray-400)}@media(hover:hover){.dark .btn-icon:hover{background-color:var(--color-gray-700)}}.btn-preset{background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);border-radius:3.40282e38px}.dark .btn-preset{background-color:var(--color-gray-700);color:var(--color-gray-300)}.btn-date{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .btn-date{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-date.active{border-color:var(--color-gray-900);background-color:var(--color-gray-900);color:var(--color-white)}.dark .btn-date.active{border-color:var(--color-white);background-color:var(--color-white);color:var(--color-gray-900)}.btn-badge{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);border-radius:3.40282e38px}.dark .btn-badge{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-badge.active{border-color:var(--color-gray-900);color:var(--color-gray-900)}.dark .btn-badge.active{border-color:var(--color-white);color:var(--color-blue-400)}.btn-group{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);display:inline-flex;overflow:hidden}.dark .btn-group{border-color:var(--color-gray-700)}.btn-group-item{border-right-style:var(--tw-border-style);border-right-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.btn-group-item:hover{background-color:var(--color-gray-50)}}.dark .btn-group-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}@media(hover:hover){.dark .btn-group-item:hover{background-color:var(--color-gray-700)}}.btn-group-item:last-child{border-right-style:var(--tw-border-style);border-right-width:0}.btn-group-item.active{background-color:var(--color-gray-900);color:var(--color-white)}.dark .btn-group-item.active{background-color:var(--color-white);color:var(--color-gray-900)}.btn-badge-green{border-color:var(--color-green-500);background-color:var(--color-green-50);color:var(--color-green-600)}.dark .btn-badge-green{border-color:var(--color-green-400);background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-green{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .btn-badge-green{color:var(--color-green-400)}.btn-badge-blue{border-color:var(--color-blue-500);background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .btn-badge-blue{border-color:var(--color-blue-400);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-blue{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .btn-badge-blue{color:var(--color-blue-400)}.btn-badge-cyan{border-color:var(--color-cyan-500);background-color:var(--color-cyan-50);color:var(--color-cyan-600)}.dark .btn-badge-cyan{border-color:var(--color-cyan-400);background-color:#104e6433}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-cyan{background-color:color-mix(in oklab,var(--color-cyan-900) 20%,transparent)}}.dark .btn-badge-cyan{color:var(--color-cyan-400)}.btn-badge-indigo{border-color:var(--color-indigo-500);background-color:var(--color-indigo-50);color:var(--color-indigo-600)}.dark .btn-badge-indigo{border-color:var(--color-indigo-400);background-color:#312c8533}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-indigo{background-color:color-mix(in oklab,var(--color-indigo-900) 20%,transparent)}}.dark .btn-badge-indigo{color:var(--color-indigo-400)}.btn-badge-pink{border-color:var(--color-pink-500);background-color:var(--color-pink-50);color:var(--color-pink-600)}.dark .btn-badge-pink{border-color:var(--color-pink-400);background-color:#86104333}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-pink{background-color:color-mix(in oklab,var(--color-pink-900) 20%,transparent)}}.dark .btn-badge-pink{color:var(--color-pink-400)}.btn-badge-red{border-color:var(--color-red-500);background-color:var(--color-red-50);color:var(--color-red-600)}.dark .btn-badge-red{border-color:var(--color-red-400);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-red{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .btn-badge-red{color:var(--color-red-400)}.btn-badge-gray{border-color:var(--color-gray-300);background-color:var(--color-gray-50);color:var(--color-gray-600)}.dark .btn-badge-gray{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-400)}.btn-badge-yellow{border-color:var(--color-yellow-500);background-color:var(--color-yellow-50);color:var(--color-yellow-600)}.dark .btn-badge-yellow{border-color:var(--color-yellow-400);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-yellow{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .btn-badge-yellow{color:var(--color-yellow-400)}.btn-badge-orange{border-color:var(--color-orange-500);background-color:var(--color-orange-50);color:var(--color-orange-600)}.dark .btn-badge-orange{border-color:var(--color-orange-400);background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .btn-badge-orange{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .btn-badge-orange{color:var(--color-orange-400)}.input{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input::placeholder{color:var(--color-gray-400)}.input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .input{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .input::placeholder{color:var(--color-gray-500)}.input:disabled{cursor:not-allowed;background-color:var(--color-gray-100);color:var(--color-gray-400)}.dark .input:disabled{background-color:var(--color-gray-800);color:var(--color-gray-500)}.input-tag{border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input-tag:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .input-tag{background-color:var(--color-gray-700);color:var(--color-gray-100)}.input-tag{min-height:2.25rem}.textarea{border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.textarea::placeholder{color:var(--color-gray-400)}.textarea:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .textarea{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .textarea::placeholder{color:var(--color-gray-500)}.textarea:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .textarea:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.input-readonly{cursor:default;color:var(--color-gray-500)}.dark .input-readonly{color:var(--color-gray-400)}.input-w-xs{width:calc(var(--spacing) * 20)}.input-w-sm{width:calc(var(--spacing) * 32)}.input-w-md{width:calc(var(--spacing) * 48)}.input-w-lg{width:calc(var(--spacing) * 64)}.input-w-xl{width:calc(var(--spacing) * 80)}.input-w-auto{width:auto}.input-search-container{border-radius:var(--radius-lg);background-color:var(--color-gray-100);align-items:center;display:flex;overflow:hidden}.dark .input-search-container{background-color:var(--color-gray-700)}.input-search-container .input-search-icon{padding-left:calc(var(--spacing) * 3);color:var(--color-gray-400)}.dark :is(.input-search-container .input-search-icon){color:var(--color-gray-500)}.input-search-container input{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);background-color:#0000;flex:1}.input-search-container input::placeholder{color:var(--color-gray-400)}.input-search-container input:focus{--tw-outline-style:none;outline-style:none}.dark :is(.input-search-container input){color:var(--color-gray-100)}.dark :is(.input-search-container input)::placeholder{color:var(--color-gray-500)}.input-search-container .input-search-dropdown{cursor:pointer;border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}@media(hover:hover){.input-search-container .input-search-dropdown:hover{background-color:var(--color-gray-50)}}.dark :is(.input-search-container .input-search-dropdown){border-color:var(--color-gray-700);color:var(--color-gray-400)}@media(hover:hover){.dark :is(.input-search-container .input-search-dropdown):hover{background-color:var(--color-gray-700)}}.select{height:calc(var(--spacing) * 9);cursor:pointer;appearance:none;border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.select:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select{background-color:var(--color-gray-700);color:var(--color-gray-100)}.select:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .select:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem}.dark .select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")}.select-composite{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.select-composite:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select-composite{background-color:var(--color-gray-700);color:var(--color-gray-100)}.select-composite:disabled{cursor:not-allowed;background-color:var(--color-gray-200);color:var(--color-gray-400)}.dark .select-composite:disabled{background-color:var(--color-gray-700);color:var(--color-gray-500)}.select-composite-sm{height:calc(var(--spacing) * 8);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.select-w-sm{width:calc(var(--spacing) * 32)}.select-w-md{width:calc(var(--spacing) * 48)}.select-w-lg{width:calc(var(--spacing) * 64)}.input-split{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.input-split input{border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.input-split input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark :is(.input-split input){background-color:var(--color-gray-700);color:var(--color-gray-100)}.input-split .input-split-separator{color:var(--color-gray-400)}.dark :is(.input-split .input-split-separator){color:var(--color-gray-500)}.tag-input-container{align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.tag{align-items:center;gap:var(--spacing);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 3);padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);border-radius:3.40282e38px;display:inline-flex}.dark .tag{background-color:var(--color-gray-700);color:var(--color-gray-300)}.tag .tag-remove{cursor:pointer;color:var(--color-gray-400)}@media(hover:hover){.tag .tag-remove:hover{color:var(--color-gray-600)}}.dark :is(.tag .tag-remove){color:var(--color-gray-500)}@media(hover:hover){.dark :is(.tag .tag-remove):hover{color:var(--color-gray-300)}}:where(.form-group>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}.textarea-sm{min-height:5rem}.textarea-md{min-height:8rem}.textarea-lg{min-height:12rem}.input-error{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-500)}.input-error:focus{--tw-ring-color:var(--color-red-500)}.dark .input-error{border-color:var(--color-red-500)}.input-success{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-500)}.input-success:focus{--tw-ring-color:var(--color-green-500)}.dark .input-success{border-color:var(--color-green-500)}.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-500)}:is(.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea):focus{--tw-ring-color:var(--color-red-500)}.dark :is(.form-field-error .input,.form-field-error input[type],.form-field-error select,.form-field-error textarea){border-color:var(--color-red-500)}.form-error{margin-top:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-500);display:block}.dark .form-error{color:var(--color-red-400)}.form-success{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-600)}.dark .form-success{color:var(--color-green-400)}.checkbox{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-gray-900);border-radius:.25rem}.checkbox:focus{--tw-ring-color:var(--color-gray-500)}.dark .checkbox{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.checkbox:checked{border-color:var(--color-gray-900);background-color:var(--color-gray-900)}.dark .checkbox:checked{border-color:var(--color-white);background-color:var(--color-white)}.radio{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-blue-600);flex-shrink:0}.radio:focus{--tw-ring-color:var(--color-blue-500)}.dark .radio{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.dark .radio:focus{--tw-ring-color:var(--color-blue-400)}.radio:checked{border-color:var(--color-blue-600)}.dark .radio:checked{border-color:var(--color-blue-400)}.toggle{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 11);cursor:pointer;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;align-items:center;display:inline-flex;position:relative}.toggle-track{background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute;inset:0}.dark .toggle-track{background-color:var(--color-gray-700)}.toggle.active .toggle-track{background-color:var(--color-blue-600)}.toggle-handle{left:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.toggle.active .toggle-handle{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.badge{align-items:center;gap:var(--spacing);border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);display:inline-flex}.badge-green{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-200);background-color:var(--color-green-100);color:var(--color-green-700)}.dark .badge-green{border-color:var(--color-green-800);background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-green{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .badge-green{color:var(--color-green-400)}.badge-blue{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-200);background-color:var(--color-blue-100);color:var(--color-blue-700)}.dark .badge-blue{border-color:var(--color-blue-800);background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-blue{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .badge-blue{color:var(--color-blue-400)}.badge-cyan{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-cyan-200);background-color:var(--color-cyan-100);color:var(--color-cyan-700)}.dark .badge-cyan{border-color:var(--color-cyan-800);background-color:#104e644d}@supports (color:color-mix(in lab,red,red)){.dark .badge-cyan{background-color:color-mix(in oklab,var(--color-cyan-900) 30%,transparent)}}.dark .badge-cyan{color:var(--color-cyan-400)}.badge-indigo{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-indigo-200);background-color:var(--color-indigo-100);color:var(--color-indigo-700)}.dark .badge-indigo{border-color:var(--color-indigo-800);background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .badge-indigo{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .badge-indigo{color:var(--color-indigo-400)}.badge-pink{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-pink-200);background-color:var(--color-pink-100);color:var(--color-pink-700)}.dark .badge-pink{border-color:var(--color-pink-800);background-color:#8610434d}@supports (color:color-mix(in lab,red,red)){.dark .badge-pink{background-color:color-mix(in oklab,var(--color-pink-900) 30%,transparent)}}.dark .badge-pink{color:var(--color-pink-400)}.badge-red{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-100);color:var(--color-red-700)}.dark .badge-red{border-color:var(--color-red-800);background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-red{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .badge-red{color:var(--color-red-400)}.badge-gray{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);color:var(--color-gray-600)}.dark .badge-gray{border-color:var(--color-gray-700);background-color:var(--color-gray-800);color:var(--color-gray-400)}.badge-yellow{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-100);color:var(--color-yellow-700)}.dark .badge-yellow{border-color:var(--color-yellow-800);background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .badge-yellow{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .badge-yellow{color:var(--color-yellow-400)}.badge-status{align-items:center;gap:calc(var(--spacing) * 1.5);padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:3.40282e38px;display:inline-flex}.badge-status-success{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .badge-status-success{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-success{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .badge-status-success{color:var(--color-green-400)}.badge-status-processing{background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .badge-status-processing{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-processing{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .badge-status-processing{color:var(--color-blue-400)}.badge-status-error{background-color:var(--color-red-50);color:var(--color-red-600)}.dark .badge-status-error{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .badge-status-error{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .badge-status-error{color:var(--color-red-400)}.badge-status-expired{background-color:var(--color-gray-50);color:var(--color-gray-500)}.dark .badge-status-expired{background-color:var(--color-gray-800);color:var(--color-gray-400)}.search-bar{gap:calc(var(--spacing) * 3);flex-direction:column;display:flex}@media(min-width:48rem){.search-bar{flex-flow:wrap;align-items:center}}.search-bar-select{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.search-bar-select::placeholder{color:var(--color-gray-400)}.search-bar-select:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .search-bar-select{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .search-bar-select::placeholder{color:var(--color-gray-500)}@media(min-width:48rem){.search-bar-select{width:calc(var(--spacing) * 32);flex-shrink:0}}.search-bar-select-custom{width:100%}@media(min-width:48rem){.search-bar-select-custom{width:calc(var(--spacing) * 32);flex-shrink:0}}.search-bar-input{height:calc(var(--spacing) * 9);border-radius:var(--radius-lg);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.search-bar-input::placeholder{color:var(--color-gray-400)}.search-bar-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .search-bar-input{background-color:var(--color-gray-700);color:var(--color-gray-100)}.dark .search-bar-input::placeholder{color:var(--color-gray-500)}@media(min-width:48rem){.search-bar-input{min-width:calc(var(--spacing) * 64);flex:1}}.search-bar-reset{cursor:pointer;justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);background-color:#0000;transition-duration:.2s;display:inline-flex}@media(hover:hover){.search-bar-reset:hover{background-color:var(--color-gray-50)}}.dark .search-bar-reset{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .search-bar-reset:hover{background-color:var(--color-gray-700)}}.search-bar-reset{gap:calc(var(--spacing) * 1.5);flex-shrink:0;width:100%}@media(min-width:48rem){.search-bar-reset{width:auto}}.filter-grid{gap:calc(var(--spacing) * 4);flex-direction:column;display:flex}.filter-row{gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}@media(min-width:48rem){.filter-row{flex-direction:row;align-items:center}}.filter-label{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);display:block}.dark .filter-label{color:var(--color-gray-400)}@media(min-width:48rem){.filter-label{width:calc(var(--spacing) * 14);flex-shrink:0;align-items:center;margin-bottom:0;display:flex}}.filter-buttons{align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.dropdown{display:inline-block;position:relative}.dropdown-trigger{border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.dropdown-trigger:hover{background-color:var(--color-gray-50)}}.dropdown-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dropdown-trigger{color:var(--color-gray-900)}.dark .dropdown-trigger{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-white)}@media(hover:hover){.dark .dropdown-trigger:hover{background-color:var(--color-gray-700)}}.dark .dropdown-trigger:focus{--tw-ring-color:var(--color-blue-400)}.dropdown-trigger-text{cursor:pointer;--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900);align-items:center;display:inline-flex}@media(hover:hover){.dropdown-trigger-text:hover{color:var(--color-blue-600)}}.dark .dropdown-trigger-text{color:var(--color-white)}@media(hover:hover){.dark .dropdown-trigger-text:hover{color:var(--color-blue-400)}}.dropdown-menu{z-index:50;width:calc(var(--spacing) * 56);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .dropdown-menu{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.dropdown-menu-bottom-left{margin-top:calc(var(--spacing) * 2);top:100%;left:0}.dropdown-menu-bottom-right{margin-top:calc(var(--spacing) * 2);top:100%;right:0}.dropdown-menu-top-left{margin-bottom:calc(var(--spacing) * 2);bottom:100%;left:0}.dropdown-menu-top-right{margin-bottom:calc(var(--spacing) * 2);bottom:100%;right:0}.dropdown-item{cursor:pointer;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}@media(hover:hover){.dropdown-item:hover{background-color:var(--color-gray-100)}}.dark .dropdown-item{color:var(--color-gray-300)}@media(hover:hover){.dark .dropdown-item:hover{background-color:var(--color-gray-700)}}.dropdown-item-icon{color:var(--color-gray-400)}.dark .dropdown-item-icon{color:var(--color-gray-500)}.dropdown-item-disabled{cursor:not-allowed;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-400)}.dark .dropdown-item-disabled{color:var(--color-gray-500)}.dropdown-item-focused{background-color:var(--color-gray-100)}.dark .dropdown-item-focused{background-color:var(--color-gray-700)}.dropdown-divider{margin-block:var(--spacing);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100)}.dark .dropdown-divider{border-color:var(--color-gray-700)}.pagination{justify-content:center;align-items:center;gap:calc(var(--spacing) * 2);flex-wrap:wrap;display:flex}.pagination-btn{border-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 3);padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);color:var(--color-gray-700);border-radius:.25rem}@media(hover:hover){.pagination-btn:hover{background-color:var(--color-gray-100)}}.pagination-btn:disabled{cursor:not-allowed;opacity:.5}.dark .pagination-btn{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-gray-300)}@media(hover:hover){.dark .pagination-btn:hover{background-color:var(--color-gray-700)}}.pagination-btn-active{border-style:var(--tw-border-style);padding-inline:calc(var(--spacing) * 3);padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));border-width:1px;border-color:var(--color-blue-500);background-color:var(--color-blue-500);color:var(--color-white);border-radius:.25rem}.dark .pagination-btn-active{border-color:var(--color-blue-600);background-color:var(--color-blue-600)}.pagination-ellipsis{padding-inline:calc(var(--spacing) * 3);padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .pagination-ellipsis{color:var(--color-gray-400)}.dialog-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.dialog-title{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .dialog-title{color:var(--color-white)}.dialog-close{color:var(--color-gray-400)}@media(hover:hover){.dialog-close:hover{color:var(--color-gray-600)}}.dialog-close:focus{--tw-outline-style:none;outline-style:none}.dark .dialog-close{color:var(--color-gray-500)}@media(hover:hover){.dark .dialog-close:hover{color:var(--color-gray-300)}}.dialog-body{margin-bottom:calc(var(--spacing) * 6);color:var(--color-gray-700)}.dark .dialog-body{color:var(--color-gray-300)}.dialog-footer{justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.dialog-btn-primary{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-primary:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-primary:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-primary{background-color:var(--color-blue-600);color:var(--color-white)}@media(hover:hover){.dialog-btn-primary:hover{background-color:var(--color-blue-700)}}.dialog-btn-primary:focus{--tw-ring-color:var(--color-blue-500)}.dialog-btn-secondary{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-secondary:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-secondary:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-secondary{background-color:var(--color-gray-200);color:var(--color-gray-800)}@media(hover:hover){.dialog-btn-secondary:hover{background-color:var(--color-gray-300)}}.dialog-btn-secondary:focus{--tw-ring-color:var(--color-gray-500)}.dark .dialog-btn-secondary{background-color:var(--color-gray-700);color:var(--color-gray-200)}@media(hover:hover){.dark .dialog-btn-secondary:hover{background-color:var(--color-gray-600)}}.dialog-btn-danger{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-danger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-danger:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-danger{background-color:var(--color-red-600);color:var(--color-white)}@media(hover:hover){.dialog-btn-danger:hover{background-color:var(--color-red-700)}}.dialog-btn-danger:focus{--tw-ring-color:var(--color-red-500)}.dialog-btn-ghost{border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.dialog-btn-ghost:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dialog-btn-ghost:disabled{cursor:not-allowed;opacity:.5}.dialog-btn-ghost{color:var(--color-gray-700);background-color:#0000}@media(hover:hover){.dialog-btn-ghost:hover{background-color:var(--color-gray-100)}}.dialog-btn-ghost:focus{--tw-ring-color:var(--color-gray-500)}.dark .dialog-btn-ghost{color:var(--color-gray-300)}@media(hover:hover){.dark .dialog-btn-ghost:hover{background-color:var(--color-gray-700)}}.alert-error{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-900);display:flex}.dark .alert-error{border-color:var(--color-red-800);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-error{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .alert-error{color:var(--color-red-100)}.alert-icon-info{margin-top:calc(var(--spacing) * .5);color:var(--color-blue-600);flex-shrink:0}.dark .alert-icon-info{color:var(--color-blue-400)}.alert-icon-success{margin-top:calc(var(--spacing) * .5);color:var(--color-green-600);flex-shrink:0}.dark .alert-icon-success{color:var(--color-green-400)}.alert-icon-warning{margin-top:calc(var(--spacing) * .5);color:var(--color-yellow-600);flex-shrink:0}.dark .alert-icon-warning{color:var(--color-yellow-400)}.alert-icon-error{margin-top:calc(var(--spacing) * .5);color:var(--color-red-600);flex-shrink:0}.dark .alert-icon-error{color:var(--color-red-400)}.alert-text-info{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-800);flex:1}.dark .alert-text-info{color:var(--color-blue-200)}.alert-text-success{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-800);flex:1}.dark .alert-text-success{color:var(--color-green-200)}.alert-text-warning{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-800);flex:1}.dark .alert-text-warning{color:var(--color-yellow-200)}.alert-text-error{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-800);flex:1}.dark .alert-text-error{color:var(--color-red-200)}.alert-dismiss{cursor:pointer;border-style:var(--tw-border-style);background-color:#0000;border-width:0;flex-shrink:0;padding:0}@media(hover:hover){.alert-dismiss:hover{opacity:.7}}.table-container{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);overflow:hidden}.dark .table-container{border-color:var(--color-gray-700)}.table{width:100%}.table thead{background-color:var(--color-gray-50)}.dark :is(.table thead){background-color:var(--color-gray-800)}.table th{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-600)}.dark :is(.table th){border-color:var(--color-gray-700);color:var(--color-gray-400)}.table th.sortable{cursor:pointer}@media(hover:hover){.table th.sortable:hover{background-color:var(--color-gray-100)}.dark :is(.table th.sortable):hover{background-color:var(--color-gray-700)}}.table tbody tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);background-color:var(--color-white)}.dark :is(.table tbody tr){border-color:var(--color-gray-800);background-color:var(--color-gray-900)}.table tbody tr:hover{background-color:var(--color-gray-50)}.dark :is(.table tbody tr:hover){background-color:var(--color-gray-800)}.table tbody tr.selected{background-color:var(--color-blue-50)}.dark :is(.table tbody tr.selected){background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark :is(.table tbody tr.selected){background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.table td{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark :is(.table td){color:var(--color-gray-100)}.table-actions{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);justify-content:space-between;align-items:center;display:flex}.dark .table-actions{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.log-table{width:100%}.log-table th{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500)}.dark :is(.log-table th){border-color:var(--color-gray-700);color:var(--color-gray-400)}.log-table td{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark :is(.log-table td){border-color:var(--color-gray-800);color:var(--color-gray-100)}.log-table tbody tr:last-child td{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.log-table-actor{border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);align-items:center;display:inline-flex}.dark .log-table-actor{border-color:var(--color-gray-600);background-color:var(--color-gray-700);color:var(--color-gray-300)}.log-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .log-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.log-card-header{margin-bottom:calc(var(--spacing) * 2);justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);display:flex}.log-card-body{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .log-card-body{color:var(--color-gray-100)}.modal-overlay{z-index:50;background-color:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}@supports (color:color-mix(in lab,red,red)){.modal-overlay{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.modal-wrapper{justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.modal-backdrop{background-color:#00000080;position:absolute;inset:0}@supports (color:color-mix(in lab,red,red)){.modal-backdrop{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.modal-container{border-radius:var(--radius-lg);background-color:var(--color-white);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);max-width:calc(100vw - 2rem);max-height:90vh;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:relative;overflow:hidden}.dark .modal-container{background-color:var(--color-gray-800)}.modal-header-row{padding:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.modal-title{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .modal-title{color:var(--color-white)}.modal-close-btn{color:var(--color-gray-400);margin-left:auto}@media(hover:hover){.modal-close-btn:hover{color:var(--color-gray-600)}}.modal-close-btn:focus{--tw-outline-style:none;outline-style:none}.dark .modal-close-btn{color:var(--color-gray-500)}@media(hover:hover){.dark .modal-close-btn:hover{color:var(--color-gray-300)}}.modal-body-content{max-height:calc(90vh - 140px);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4);overflow-y:auto}.modal{margin-inline:calc(var(--spacing) * 4);width:100%;max-width:var(--container-md);border-radius:var(--radius-xl);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .modal{background-color:var(--color-gray-800)}.modal-lg{max-width:var(--container-lg)}.modal-xl{max-width:var(--container-xl)}.modal-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:flex-start;display:flex}.modal-close{cursor:pointer;border-radius:var(--radius-lg);padding:var(--spacing);color:var(--color-gray-400)}@media(hover:hover){.modal-close:hover{background-color:var(--color-gray-100);color:var(--color-gray-600)}}.dark .modal-close{color:var(--color-gray-500)}@media(hover:hover){.dark .modal-close:hover{background-color:var(--color-gray-700);color:var(--color-gray-300)}}.modal-body{padding-block:calc(var(--spacing) * 4)}.modal-footer{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.danger-confirm-icon{margin-inline:auto;margin-bottom:calc(var(--spacing) * 4);height:calc(var(--spacing) * 12);width:calc(var(--spacing) * 12);background-color:var(--color-red-100);border-radius:3.40282e38px;justify-content:center;align-items:center;display:flex}.dark .danger-confirm-icon{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .danger-confirm-icon{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.danger-confirm-icon svg,.danger-confirm-icon .icon{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-red-600)}.dark :is(.danger-confirm-icon svg,.danger-confirm-icon .icon){color:var(--color-red-400)}.danger-confirm-title{margin-bottom:calc(var(--spacing) * 2);text-align:center;font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .danger-confirm-title{color:var(--color-white)}.danger-confirm-message{margin-bottom:calc(var(--spacing) * 4);text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .danger-confirm-message{color:var(--color-gray-400)}.danger-confirm-warning{margin-bottom:calc(var(--spacing) * 4);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-50);padding:calc(var(--spacing) * 4)}.dark .danger-confirm-warning{border-color:var(--color-yellow-700);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .danger-confirm-warning{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.danger-confirm-warning-title{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-yellow-800)}.dark .danger-confirm-warning-title{color:var(--color-yellow-300)}.danger-confirm-warning-list{list-style-type:disc;list-style-position:inside}:where(.danger-confirm-warning-list>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}.danger-confirm-warning-list{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-700)}.dark .danger-confirm-warning-list{color:var(--color-yellow-400)}.danger-confirm-alert{text-align:center;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-600)}.dark .danger-confirm-alert{color:var(--color-red-400)}.danger-confirm-footer{margin-top:calc(var(--spacing) * 6);justify-content:center;gap:calc(var(--spacing) * 3);display:flex}.card{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);width:100%;padding:calc(var(--spacing) * 6)}.dark .card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card{margin-bottom:calc(var(--spacing) * 6);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .admin-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card{flex-direction:column;display:flex}.admin-card-clickable{margin-bottom:calc(var(--spacing) * 2);cursor:pointer;border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2.5)}@media(hover:hover){.admin-card-clickable:hover{border-color:var(--color-gray-300)}}.dark .admin-card-clickable{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}@media(hover:hover){.dark .admin-card-clickable:hover{border-color:var(--color-gray-600)}}.admin-card-selected{margin-bottom:calc(var(--spacing) * 2);cursor:pointer;border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-500);background-color:var(--color-blue-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2.5)}.dark .admin-card-selected{border-color:var(--color-blue-500);background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .admin-card-selected{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.card-clickable{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));overflow:hidden}@media(hover:hover){.card-clickable:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .card-clickable{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.card-image{height:calc(var(--spacing) * 48);object-fit:cover;width:100%}.card-content{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));white-space:pre-line;color:var(--color-gray-600)}.dark .card-content{color:var(--color-gray-300)}.card-section-title{margin-inline:calc(var(--spacing) * -6);margin-bottom:calc(var(--spacing) * 6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 6);padding-bottom:calc(var(--spacing) * 4);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .card-section-title{border-color:var(--color-gray-700);color:var(--color-white)}.card-account{border-radius:var(--radius-lg);background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4);position:relative}.dark .card-account{background-color:var(--color-gray-900)}.card-account-default{top:calc(var(--spacing) * 3);left:calc(var(--spacing) * 3);position:absolute}.card-account-delete{top:calc(var(--spacing) * 3);right:calc(var(--spacing) * 3);position:absolute}.admin-card-accordion{margin-bottom:calc(var(--spacing) * 6);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .admin-card-accordion{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.admin-card-accordion{flex-direction:column;display:flex}.admin-card-accordion>.card-header{cursor:pointer}.admin-card-accordion-icon{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;margin-left:auto;transition-duration:.2s}.dark .admin-card-accordion-icon{color:var(--color-gray-500)}.admin-card-accordion>.row-stack{margin-top:calc(var(--spacing) * 6)}.admin-card-accordion-icon.open{rotate:180deg}.tabs{border-radius:var(--radius-lg);background-color:var(--color-gray-100);padding:var(--spacing);display:inline-flex}.dark .tabs{background-color:var(--color-gray-800)}.tab{cursor:pointer;border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.tab:hover{color:var(--color-gray-700)}}.dark .tab{color:var(--color-gray-400)}@media(hover:hover){.dark .tab:hover{color:var(--color-gray-300)}}.tab.active{background-color:var(--color-white);color:var(--color-gray-900);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .tab.active{background-color:var(--color-gray-700);color:var(--color-white)}.toast{right:calc(var(--spacing) * 4);bottom:calc(var(--spacing) * 4);z-index:50;position:fixed}.toast-item{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);background-color:var(--color-gray-900);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);display:flex}.dark .toast-item{background-color:var(--color-white);color:var(--color-gray-900)}.toast-icon{flex-shrink:0}.drag-item{align-items:center;gap:calc(var(--spacing) * 4);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4);display:flex}@media(hover:hover){.drag-item:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .drag-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.drag-handle{cursor:grab;color:var(--color-gray-400)}.dark .drag-handle{color:var(--color-gray-500)}.drag-handle:active{cursor:grabbing}.table-mobile-cards{display:none}.excel-card{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .excel-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.excel-card-header{margin-bottom:calc(var(--spacing) * 4);justify-content:space-between;align-items:flex-start;gap:calc(var(--spacing) * 3);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-bottom:calc(var(--spacing) * 3);display:flex}.dark .excel-card-header{border-color:var(--color-gray-700)}.excel-card-title{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .excel-card-title{color:var(--color-white)}:where(.excel-card-body>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.excel-card-row{padding-block:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));justify-content:space-between;align-items:center;display:flex}.excel-card-label{color:var(--color-gray-500);flex-shrink:0}.dark .excel-card-label{color:var(--color-gray-400)}.excel-card-value{text-align:right;color:var(--color-gray-900)}.dark .excel-card-value{color:var(--color-white)}.excel-card-footer{margin-top:calc(var(--spacing) * 4);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-100);padding-top:calc(var(--spacing) * 3);justify-content:space-between;align-items:center;display:flex}.dark .excel-card-footer{border-color:var(--color-gray-700)}.excel-card-creator{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700)}.dark .excel-card-creator{color:var(--color-gray-300)}.excel-card-creator-label{margin-right:calc(var(--spacing) * 2);color:var(--color-gray-500)}.dark .excel-card-creator-label{color:var(--color-gray-400)}.select-custom{display:inline-block;position:relative}.select-trigger{height:calc(var(--spacing) * 9);border-radius:var(--radius-xl);background-color:var(--color-gray-100);width:100%;padding-inline:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);cursor:pointer;justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);border-style:var(--tw-border-style);border-width:0;display:flex}.select-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .select-trigger{background-color:var(--color-gray-700);color:var(--color-gray-200)}.select-trigger:disabled{cursor:not-allowed;opacity:.5}.select-trigger span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.select-trigger-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;flex-shrink:0;transition-duration:.2s}.dark .select-trigger-icon{color:var(--color-gray-500)}.select-trigger-icon.open{rotate:180deg}.select-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);width:100%;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute;overflow:hidden}.dark .select-dropdown{border-color:var(--color-gray-600);background-color:var(--color-gray-800)}.select-dropdown.hidden{display:none}.select-dropdown-inner{max-height:calc(var(--spacing) * 60);padding-block:calc(var(--spacing) * 2);overflow:auto}.select-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));align-items:center;display:flex}@media(hover:hover){.select-option:hover{background-color:var(--color-gray-100)}}.dark .select-option{color:var(--color-gray-200)}@media(hover:hover){.dark .select-option:hover{background-color:var(--color-gray-700)}}.select-option.selected{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-blue-600)}.dark .select-option.selected{color:var(--color-blue-400)}.select-option:disabled{cursor:not-allowed;opacity:.5}.select-option-check{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-blue-600);flex-shrink:0;margin-left:auto}.dark .select-option-check{color:var(--color-blue-400)}.multi-select{display:inline-block;position:relative}.multi-select-trigger{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);color:var(--color-gray-700);cursor:pointer;background-color:#0000;display:inline-flex}@media(hover:hover){.multi-select-trigger:hover{background-color:var(--color-gray-50)}}.dark .multi-select-trigger{border-color:var(--color-gray-600);color:var(--color-gray-300)}@media(hover:hover){.dark .multi-select-trigger:hover{background-color:var(--color-gray-700)}}.multi-select-count{color:var(--color-gray-500)}.dark .multi-select-count{color:var(--color-gray-400)}.multi-select-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);min-width:calc(var(--spacing) * 64);border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .multi-select-dropdown{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.multi-select-dropdown.hidden{display:none}.multi-select-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .multi-select-header{border-color:var(--color-gray-700);color:var(--color-white)}.multi-select-hint{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal);color:var(--color-gray-500)}.dark .multi-select-hint{color:var(--color-gray-400)}.multi-select-items{max-height:calc(var(--spacing) * 80);padding-block:calc(var(--spacing) * 2);overflow:auto}.multi-select-item{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);display:flex}@media(hover:hover){.multi-select-item:hover{background-color:var(--color-gray-50)}}.dark .multi-select-item{color:var(--color-gray-300)}@media(hover:hover){.dark .multi-select-item:hover{background-color:var(--color-gray-700)}}.multi-select-item.disabled{cursor:not-allowed;opacity:.5}.multi-select-item.disabled:hover,.dark .multi-select-item.disabled:hover{background-color:#0000}.select-ghost{display:inline-block;position:relative}.select-ghost-trigger{align-items:center;gap:var(--spacing);padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);cursor:pointer;border-radius:var(--radius-lg);color:var(--color-gray-700);background-color:#0000;display:inline-flex}@media(hover:hover){.select-ghost-trigger:hover{background-color:var(--color-gray-100)}}.dark .select-ghost-trigger{color:var(--color-gray-300)}@media(hover:hover){.dark .select-ghost-trigger:hover{background-color:var(--color-gray-700)}}.select-ghost-dropdown{z-index:50;margin-top:var(--spacing);min-width:calc(var(--spacing) * 32);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding-block:var(--spacing);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute}.dark .select-ghost-dropdown{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.select-ghost-dropdown.hidden{display:none}.select-ghost-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);justify-content:space-between;align-items:center;display:flex}@media(hover:hover){.select-ghost-option:hover{background-color:var(--color-gray-50)}}.dark .select-ghost-option{color:var(--color-gray-300)}@media(hover:hover){.dark .select-ghost-option:hover{background-color:var(--color-gray-700)}}.select-ghost-option.selected{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-blue-600)}.dark .select-ghost-option.selected{color:var(--color-blue-400)}.select-ghost-check{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-blue-600)}.dark .select-ghost-check{color:var(--color-blue-400)}.searchable-dropdown{display:inline-block;position:relative}.searchable-dropdown-trigger{border-radius:var(--radius-xl);background-color:var(--color-gray-100);min-width:200px;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);cursor:pointer;justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 2);border-style:var(--tw-border-style);border-width:0;display:flex}.searchable-dropdown-trigger:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;outline-style:none}.dark .searchable-dropdown-trigger{background-color:var(--color-gray-700);color:var(--color-gray-200)}.searchable-dropdown-trigger:disabled{cursor:not-allowed;opacity:.5}.searchable-dropdown-trigger-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;flex-shrink:0;transition-duration:.2s}.dark .searchable-dropdown-trigger-icon{color:var(--color-gray-500)}.searchable-dropdown-trigger-icon.open{rotate:180deg}.searchable-dropdown-dropdown{z-index:50;margin-top:calc(var(--spacing) * 2);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);width:100%;min-width:280px;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);position:absolute;overflow:hidden}.dark .searchable-dropdown-dropdown{border-color:var(--color-gray-600);background-color:var(--color-gray-800)}.searchable-dropdown-dropdown.hidden{display:none}.searchable-dropdown-search{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding:calc(var(--spacing) * 3)}.dark .searchable-dropdown-search{border-color:var(--color-gray-700)}.searchable-dropdown-search-input{border-radius:var(--radius-lg);background-color:var(--color-gray-50);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.searchable-dropdown-search-input::placeholder{color:var(--color-gray-400)}.searchable-dropdown-search-input:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;border-color:#0000;outline-style:none}.dark .searchable-dropdown-search-input{border-color:var(--color-gray-600);background-color:var(--color-gray-700);color:var(--color-white)}.dark .searchable-dropdown-search-input::placeholder{color:var(--color-gray-500)}.searchable-dropdown-items{max-height:calc(var(--spacing) * 80);padding-block:calc(var(--spacing) * 2);overflow:auto}.searchable-dropdown-option{cursor:pointer;width:100%;padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);text-align:left;align-items:flex-start;gap:calc(var(--spacing) * 3);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media(hover:hover){.searchable-dropdown-option:hover{background-color:var(--color-gray-100)}.dark .searchable-dropdown-option:hover{background-color:var(--color-gray-700)}}.searchable-dropdown-option:disabled{cursor:not-allowed;opacity:.5}.searchable-dropdown-check-area{margin-top:calc(var(--spacing) * .5);height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);flex-shrink:0}.searchable-dropdown-check{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5);color:var(--color-blue-600)}.dark .searchable-dropdown-check{color:var(--color-blue-400)}.searchable-dropdown-option-content{flex:1;min-width:0}.searchable-dropdown-option-label{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .searchable-dropdown-option-label{color:var(--color-blue-400)}.searchable-dropdown-option.selected .searchable-dropdown-option-label{color:var(--color-blue-600)}.dark :is(.searchable-dropdown-option.selected .searchable-dropdown-option-label){color:var(--color-blue-400)}.searchable-dropdown-option-description{margin-top:calc(var(--spacing) * .5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .searchable-dropdown-option-description{color:var(--color-gray-400)}.searchable-dropdown-message{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 6);text-align:center;color:var(--color-gray-500)}.dark .searchable-dropdown-message{color:var(--color-gray-400)}.stat-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.stat-card:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.dark .stat-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.stats-icon{border-radius:var(--radius-lg);padding:calc(var(--spacing) * 3)}.stats-icon-blue{background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .stats-icon-blue{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-blue{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .stats-icon-blue{color:var(--color-blue-400)}.stats-icon-green{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .stats-icon-green{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-green{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .stats-icon-green{color:var(--color-green-400)}.stats-icon-purple{background-color:var(--color-purple-50);color:var(--color-purple-600)}.dark .stats-icon-purple{background-color:#59168b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-purple{background-color:color-mix(in oklab,var(--color-purple-900) 20%,transparent)}}.dark .stats-icon-purple{color:var(--color-purple-400)}.stats-icon-orange{background-color:var(--color-orange-50);color:var(--color-orange-600)}.dark .stats-icon-orange{background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .stats-icon-orange{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .stats-icon-orange{color:var(--color-orange-400)}.stats-change{align-items:center;gap:var(--spacing);border-radius:var(--radius-md);padding-inline:calc(var(--spacing) * 2);padding-block:var(--spacing);display:flex}.stats-trend-up{background-color:var(--color-green-50);color:var(--color-green-600)}.dark .stats-trend-up{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .stats-trend-up{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .stats-trend-up{color:var(--color-green-400)}.stats-trend-down{background-color:var(--color-red-50);color:var(--color-red-600)}.dark .stats-trend-down{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .stats-trend-down{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .stats-trend-down{color:var(--color-red-400)}.stats-trend-neutral{background-color:var(--color-gray-50);color:var(--color-gray-600)}.dark .stats-trend-neutral{background-color:var(--color-gray-700);color:var(--color-gray-400)}.stats-value-lg{margin-bottom:var(--spacing);font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);color:var(--color-gray-900)}.dark .stats-value-lg{color:var(--color-white)}.stats-label{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .stats-label{color:var(--color-gray-400)}.stats-sublabel{align-items:center;gap:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500);display:flex}.dark .stats-sublabel{color:var(--color-gray-400)}:where(.tree>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}.tree-item{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);display:flex}@media(hover:hover){.tree-item:hover{background-color:var(--color-gray-50)}.dark .tree-item:hover{background-color:var(--color-gray-800)}}.tree-item.selected{background-color:var(--color-blue-50)}.dark .tree-item.selected{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tree-item.selected{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.tree-item-content{align-items:center;gap:calc(var(--spacing) * 2);flex:1;min-width:0;display:flex}.tree-item-icon{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);color:var(--color-gray-400);flex-shrink:0}.dark .tree-item-icon{color:var(--color-gray-500)}.tree-item-label{text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-700);overflow:hidden}.dark .tree-item-label{color:var(--color-gray-300)}.tree-expand-btn{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6);color:var(--color-gray-400);border-radius:.25rem;justify-content:center;align-items:center;display:flex}@media(hover:hover){.tree-expand-btn:hover{background-color:var(--color-gray-100)}}.dark .tree-expand-btn{color:var(--color-gray-500)}@media(hover:hover){.dark .tree-expand-btn:hover{background-color:var(--color-gray-700)}}.tree-expand-btn.hidden{visibility:hidden}.tree-children{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-children{border-color:var(--color-gray-700)}.tree-level-1{border-left-style:var(--tw-border-style);border-left-width:0;margin-left:0;padding-left:0}.tree-level-2{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-2{border-color:var(--color-gray-700)}.tree-level-3{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-3{border-color:var(--color-gray-700)}.tree-level-4{margin-left:calc(var(--spacing) * 6);border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-gray-200);padding-left:calc(var(--spacing) * 4)}.dark .tree-level-4{border-color:var(--color-gray-700)}.category-item{cursor:pointer;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900);justify-content:space-between;align-items:center;display:flex}.dark .category-item{color:var(--color-white)}@media(hover:hover){.category-item:hover{background-color:var(--color-gray-50)}.dark .category-item:hover{background-color:var(--color-gray-700)}}.category-item[data-selected=true]{background-color:var(--color-blue-600);color:var(--color-white)}.category-item[data-selected=true]:hover{background-color:var(--color-blue-700)}.category-item-mobile{cursor:pointer;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .category-item-mobile{color:var(--color-white)}.category-item-mobile[data-selected=true]{background-color:var(--color-blue-50)}.dark .category-item-mobile[data-selected=true]{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .category-item-mobile[data-selected=true]{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.stats-card{height:calc(var(--spacing) * 36);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-100);background-color:var(--color-white);padding:calc(var(--spacing) * 5);flex-direction:column;justify-content:space-between;display:flex}.dark .stats-card{border-color:var(--color-slate-700);background-color:var(--color-slate-800)}.dashboard-panel{border-radius:var(--radius-3xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-100);background-color:var(--color-white);padding:calc(var(--spacing) * 6);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dashboard-panel{border-color:var(--color-slate-700);background-color:var(--color-slate-800)}.stats-value{margin-bottom:calc(var(--spacing) * 2);--tw-leading:1;--tw-font-weight:var(--font-weight-bold);font-size:2rem;line-height:1;font-weight:var(--font-weight-bold);color:var(--color-slate-900)}.dark .stats-value{color:var(--color-white)}.stats-trend{align-items:center;gap:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-emerald-500);display:flex}.status-active{padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-green-100);color:var(--color-green-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-active{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .status-active{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .status-active{color:var(--color-green-400)}.status-inactive{padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-gray-100);color:var(--color-gray-600);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-inactive{background-color:var(--color-gray-700);color:var(--color-gray-400)}.status-pending{padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-yellow-100);color:var(--color-yellow-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-pending{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .status-pending{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .status-pending{color:var(--color-yellow-400)}.status-error{padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);background-color:var(--color-red-100);color:var(--color-red-700);border-radius:3.40282e38px;align-items:center;display:inline-flex}.dark .status-error{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .status-error{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .status-error{color:var(--color-red-400)}.list-item{align-items:center;gap:calc(var(--spacing) * 3);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-100);padding-block:calc(var(--spacing) * 3);display:flex}.dark .list-item{border-color:var(--color-gray-700)}.list-item:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.list-item-hover{margin-inline:calc(var(--spacing) * -3);align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);display:flex}@media(hover:hover){.list-item-hover:hover{background-color:var(--color-gray-50)}.dark .list-item-hover:hover{background-color:var(--color-gray-800)}}.empty-state{padding-block:calc(var(--spacing) * 12);text-align:center;flex-direction:column;justify-content:center;align-items:center;display:flex}.empty-state-icon{margin-bottom:calc(var(--spacing) * 4);height:calc(var(--spacing) * 16);width:calc(var(--spacing) * 16);color:var(--color-gray-300)}.dark .empty-state-icon{color:var(--color-gray-600)}.empty-state-title{margin-bottom:calc(var(--spacing) * 2);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .empty-state-title{color:var(--color-white)}.empty-state-description{margin-bottom:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .empty-state-description{color:var(--color-gray-400)}.page-header{margin-bottom:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.page-header-padded{margin-bottom:calc(var(--spacing) * 6);padding-inline:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.page-header-fullbleed{margin-inline:calc(var(--spacing) * -6);margin-bottom:calc(var(--spacing) * 6);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;display:flex}.dark .page-header-fullbleed{background-color:var(--color-gray-900)}.page-actions{align-items:center;gap:calc(var(--spacing) * 3);display:flex}.section-card{margin-top:calc(var(--spacing) * 6);scroll-margin-top:calc(var(--spacing) * 32);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6)}.dark .section-card{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.section-header{margin-bottom:calc(var(--spacing) * 6);font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-header{color:var(--color-white)}.info-row{margin-bottom:calc(var(--spacing) * 4);align-items:center;gap:calc(var(--spacing) * 2);display:flex}.action-buttons{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.form-label{margin-bottom:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900);display:block}.dark .form-label{color:var(--color-white)}.form-label-muted{margin-bottom:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-500);display:block}.dark .form-label-muted{color:var(--color-gray-400)}.form-hint{margin-top:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .form-hint{color:var(--color-gray-400)}.text-value{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-900)}.dark .text-value{color:var(--color-white)}.text-link{cursor:pointer;font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-600)}@media(hover:hover){.text-link:hover{color:var(--color-blue-700)}}.dark .text-link{color:var(--color-blue-400)}@media(hover:hover){.dark .text-link:hover{color:var(--color-blue-300)}}.icon-xs{height:calc(var(--spacing) * 3);width:calc(var(--spacing) * 3)}.icon-sm{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4)}.icon-md{height:calc(var(--spacing) * 5);width:calc(var(--spacing) * 5)}.icon-lg{height:calc(var(--spacing) * 6);width:calc(var(--spacing) * 6)}.icon-xl{height:calc(var(--spacing) * 8);width:calc(var(--spacing) * 8)}.icon-2xl{height:calc(var(--spacing) * 10);width:calc(var(--spacing) * 10)}.icon-action{cursor:pointer;color:var(--color-gray-400)}@media(hover:hover){.icon-action:hover{color:var(--color-gray-600)}}.dark .icon-action{color:var(--color-gray-500)}@media(hover:hover){.dark .icon-action:hover{color:var(--color-gray-300)}}.page-container{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 6)}.dark .page-container{background-color:var(--color-gray-900)}.content-area{max-width:var(--container-7xl);margin-inline:auto}.content-panel{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 6)}.dark .content-panel{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.flex-row-center{align-items:center;display:flex}.flex-row-between{justify-content:space-between;align-items:center;display:flex}.flex-row-end{justify-content:flex-end;align-items:center;display:flex}.flex-col-start{flex-direction:column;align-items:flex-start;display:flex}.flex-between{justify-content:space-between;align-items:center;display:flex}.flex-center{align-items:center;display:flex}.flex-end{justify-content:flex-end;align-items:center;display:flex}.flex-wrap-center{flex-wrap:wrap;align-items:center;display:flex}.inline-flex-center{align-items:center;display:inline-flex}.row-stack{flex-direction:column;display:flex}:where(.row-stack>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-color:var(--color-gray-200)}:where(.dark .row-stack>:not(:last-child)){border-color:var(--color-gray-700)}.row-stack>*{padding-block:calc(var(--spacing) * 2)}.row-stack>:first-child{padding-top:0}.row-stack>:last-child{padding-bottom:0}.stack-flush{flex-direction:column;gap:0;display:flex}.stack-tight{gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}.card-stack{margin-top:calc(var(--spacing) * 3);gap:calc(var(--spacing) * 2);flex-direction:column;display:flex}.card-stack-item{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-white);padding:calc(var(--spacing) * 4)}.dark .card-stack-item{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.card-stack-item{flex-direction:column;display:flex}.option-card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);overflow:hidden}.dark .option-card{border-color:var(--color-gray-700);background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .option-card{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.option-card>*{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3)}.option-card>*+*{border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200)}.dark :is(.option-card>*+*){border-color:var(--color-gray-700)}.alert-info{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-blue-200);background-color:var(--color-blue-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-blue-900);display:flex}.dark .alert-info{border-color:var(--color-blue-800);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .alert-info{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .alert-info{color:var(--color-blue-100)}.alert-warning{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-yellow-200);background-color:var(--color-yellow-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-yellow-900);display:flex}.dark .alert-warning{border-color:var(--color-yellow-800);background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-warning{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .alert-warning{color:var(--color-yellow-100)}.alert-danger{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-red-200);background-color:var(--color-red-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-900);display:flex}.dark .alert-danger{border-color:var(--color-red-800);background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .alert-danger{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .alert-danger{color:var(--color-red-100)}.alert-success{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-green-200);background-color:var(--color-green-50);padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 3);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-green-900);display:flex}.dark .alert-success{border-color:var(--color-green-800);background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .alert-success{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .alert-success{color:var(--color-green-100)}.alert-info:has(+.row-stack),.alert-warning:has(+.row-stack),.alert-danger:has(+.row-stack),.alert-success:has(+.row-stack),.alert-error:has(+.row-stack),.alert-info:has(+.toggle-group),.alert-warning:has(+.toggle-group),.alert-danger:has(+.toggle-group),.alert-success:has(+.toggle-group),.alert-error:has(+.toggle-group),.alert-info:has(+.grid-2col-responsive),.alert-warning:has(+.grid-2col-responsive),.alert-danger:has(+.grid-2col-responsive),.alert-success:has(+.grid-2col-responsive),.alert-error:has(+.grid-2col-responsive){margin-bottom:calc(var(--spacing) * 4)}.row-stack>.alert-info,.row-stack>.alert-warning,.row-stack>.alert-danger,.row-stack>.alert-success,.row-stack>.alert-error{padding-top:calc(var(--spacing) * 3);padding-bottom:calc(var(--spacing) * 3)}.row-stack>*+.alert-info,.row-stack>*+.alert-warning,.row-stack>*+.alert-danger,.row-stack>*+.alert-success,.row-stack>*+.alert-error{margin-top:calc(var(--spacing) * 3)}.sub-tab-nav{gap:var(--spacing);background-color:var(--color-white);padding-inline:calc(var(--spacing) * 6);display:flex}@media(min-width:40rem){.sub-tab-nav{padding-inline:calc(var(--spacing) * 8)}}@media(min-width:64rem){.sub-tab-nav{padding-inline:calc(var(--spacing) * 10)}}.dark .sub-tab-nav{background-color:var(--color-gray-800)}.section-header{margin-bottom:calc(var(--spacing) * 6)}.section-header>h3{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark :is(.section-header>h3){color:var(--color-white)}.section-header>p{margin-top:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark :is(.section-header>p){color:var(--color-gray-400)}.toggle-group{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);overflow:hidden}.dark .toggle-group{border-color:var(--color-gray-700)}.section-header-row{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);background-color:var(--color-gray-50);display:flex}.dark .section-header-row{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .section-header-row{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.section-header-row{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200)}.dark .section-header-row{border-color:var(--color-gray-700)}.section-header-row{cursor:pointer;-webkit-user-select:none;user-select:none}.list-row-hover{background-color:var(--color-white)}.dark .list-row-hover{background-color:var(--color-gray-800)}@media(hover:hover){.list-row-hover:hover{background-color:var(--color-gray-50)}.dark .list-row-hover:hover{background-color:var(--color-gray-700)}}.list-row-hover{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2.5);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.text-hint{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-400)}.dark .text-hint{color:var(--color-gray-500)}.text-info-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-blue-700)}.dark .text-info-soft{color:var(--color-blue-300)}.text-warning-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-yellow-700)}.dark .text-warning-soft{color:var(--color-yellow-300)}.text-danger-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-700)}.dark .text-danger-soft{color:var(--color-red-300)}.text-success-soft{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-green-700)}.dark .text-success-soft{color:var(--color-green-300)}.checkbox-aligned{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);cursor:pointer;border-color:var(--color-gray-300);color:var(--color-gray-900);border-radius:.25rem}.checkbox-aligned:focus{--tw-ring-color:var(--color-gray-500)}.dark .checkbox-aligned{border-color:var(--color-gray-600);background-color:var(--color-gray-800);color:var(--color-blue-400)}.checkbox-aligned{margin-top:calc(var(--spacing) * .5);flex-shrink:0}:where(.divider-list>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-color:var(--color-gray-200)}:where(.dark .divider-list>:not(:last-child)){border-color:var(--color-gray-700)}.modal-footer-buttons{margin-top:calc(var(--spacing) * 6);justify-content:flex-end;gap:calc(var(--spacing) * 3);display:flex}.spinner-sm{height:calc(var(--spacing) * 4);width:calc(var(--spacing) * 4);animation:var(--animate-spin)}.text-required{margin-left:var(--spacing);color:var(--color-red-500)}.dark .text-required{color:var(--color-red-400)}.form-label-block{margin-bottom:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-700);display:block}.dark .form-label-block{color:var(--color-gray-300)}.form-sub-label{margin-bottom:var(--spacing);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500);display:block}.dark .form-sub-label{color:var(--color-gray-400)}.text-field-hint{margin-top:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal);color:var(--color-gray-500)}.dark .text-field-hint{color:var(--color-gray-400)}.section-heading-md{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}.dark .section-heading-md{color:var(--color-white)}.grid-3col-responsive{align-items:flex-start;gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}@media(min-width:64rem){.grid-3col-responsive{grid-template-columns:repeat(3,minmax(0,1fr))}}.text-error-soft{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-red-700)}.dark .text-error-soft{color:var(--color-red-300)}.text-error-strong{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-red-800)}.dark .text-error-strong{color:var(--color-red-200)}.row-content-indent{padding-left:calc(var(--spacing) * 6)}.flex-start{align-items:flex-start;display:flex}.inline-clickable{cursor:pointer;align-items:center;gap:calc(var(--spacing) * 2);display:inline-flex}.grid-2col{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.empty-state-cell{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 4);text-align:center}.form-error-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-red-500)}.dark .form-error-xs{color:var(--color-red-400)}.text-page-heading{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height));--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);color:var(--color-gray-900)}.dark .text-page-heading{color:var(--color-white)}.text-table-header{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider);color:var(--color-gray-500);text-transform:uppercase}.dark .text-table-header{color:var(--color-gray-400)}.panel-header-row{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4)}.dark .panel-header-row{border-color:var(--color-gray-700)}.grid-2col-responsive{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}@media(min-width:48rem){.grid-2col-responsive{grid-template-columns:repeat(2,minmax(0,1fr))}}.panel-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);padding-block:calc(var(--spacing) * 3)}.dark .panel-header{border-color:var(--color-gray-700)}.gap-sm{gap:calc(var(--spacing) * 2)}.gap-md{gap:calc(var(--spacing) * 4)}.gap-lg{gap:calc(var(--spacing) * 6)}.mb-sm{margin-bottom:calc(var(--spacing) * 2)}.mb-md{margin-bottom:calc(var(--spacing) * 4)}.mb-lg{margin-bottom:calc(var(--spacing) * 6)}.surface{background-color:var(--color-white)}.dark .surface{background-color:var(--color-gray-800)}.surface-alt{background-color:var(--color-gray-50)}.dark .surface-alt{background-color:var(--color-gray-900)}.card-border{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.dark .card-border{border-color:var(--color-gray-700)}.card-border-xl{border-radius:var(--radius-xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200)}.dark .card-border-xl{border-color:var(--color-gray-700)}.border-light{border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300)}.dark .border-light{border-color:var(--color-gray-600)}.disabled-state:disabled{cursor:not-allowed;opacity:.5}.focus-ring:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.dark .focus-ring:focus{--tw-ring-offset-color:var(--color-gray-800)}.focus-ring-inset:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;--tw-ring-inset:inset;outline-style:none}@media(hover:hover){.hover-bg:hover{background-color:var(--color-gray-100)}.dark .hover-bg:hover{background-color:var(--color-gray-700)}.hover-bg-light:hover{background-color:var(--color-gray-50)}.dark .hover-bg-light:hover{background-color:var(--color-gray-800)}}.btn-base{border-radius:var(--radius-lg);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));--tw-duration:.2s;justify-content:center;align-items:center;transition-duration:.2s;display:inline-flex}.btn-base:disabled{cursor:not-allowed;opacity:.5}.btn-size-sm{padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.btn-size-md{padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.btn-size-lg{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 3);font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.badge-base{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:3.40282e38px;align-items:center;display:inline-flex}.badge-size-sm{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5)}.badge-size-md{padding-inline:calc(var(--spacing) * 2.5);padding-block:var(--spacing)}.shadow-card{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-dropdown{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.shadow-hover:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.page-title{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);color:var(--color-gray-900)}@media(min-width:40rem){.page-title{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}}.dark .page-title{color:var(--color-white)}.page-description{margin-top:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600)}.dark .page-description{color:var(--color-gray-400)}.page-action-primary{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-blue-600);color:var(--color-white);display:flex}@media(hover:hover){.page-action-primary:hover{background-color:var(--color-blue-700)}}.page-action-secondary{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-gray-200);color:var(--color-gray-900);display:flex}@media(hover:hover){.page-action-secondary:hover{background-color:var(--color-gray-300)}}.dark .page-action-secondary{background-color:var(--color-gray-700);color:var(--color-gray-200)}@media(hover:hover){.dark .page-action-secondary:hover{background-color:var(--color-gray-600)}}.page-action-danger{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));background-color:var(--color-red-600);color:var(--color-white);display:flex}@media(hover:hover){.page-action-danger:hover{background-color:var(--color-red-700)}}.page-action-ghost{align-items:center;gap:calc(var(--spacing) * 2);border-radius:var(--radius-lg);padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));color:var(--color-gray-500);display:flex}@media(hover:hover){.page-action-ghost:hover{background-color:var(--color-gray-100);color:var(--color-gray-700)}}.dark .page-action-ghost{color:var(--color-gray-400)}@media(hover:hover){.dark .page-action-ghost:hover{background-color:var(--color-gray-800);color:var(--color-gray-200)}}.tab-container{margin-bottom:calc(var(--spacing) * 6);gap:calc(var(--spacing) * 6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);display:flex}.dark .tab-container{border-color:var(--color-gray-700)}.tab-btn{align-items:center;gap:calc(var(--spacing) * 2);border-bottom-style:var(--tw-border-style);padding-inline:var(--spacing);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px;margin-bottom:-1px;display:flex}@media(hover:hover){.tab-btn:hover{color:var(--color-gray-900)}}.dark .tab-btn{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn:hover{color:var(--color-gray-200)}}.tab-btn-active{align-items:center;gap:calc(var(--spacing) * 2);border-bottom-style:var(--tw-border-style);padding-inline:var(--spacing);padding-block:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-bottom-width:2px;border-color:var(--color-blue-600);color:var(--color-blue-600);margin-bottom:-1px;display:flex}.dark .tab-btn-active{color:var(--color-blue-400)}.tab-badge{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);background-color:var(--color-gray-100);color:var(--color-gray-600);border-radius:3.40282e38px}.dark .tab-badge{background-color:var(--color-gray-700);color:var(--color-gray-400)}.tab-badge-active{padding-inline:calc(var(--spacing) * 2);padding-block:calc(var(--spacing) * .5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);background-color:var(--color-blue-100);color:var(--color-blue-600);border-radius:3.40282e38px}.dark .tab-badge-active{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tab-badge-active{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .tab-badge-active{color:var(--color-blue-400)}.sticky-tab-nav{z-index:40;margin-inline:calc(var(--spacing) * -6);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 6);position:sticky;top:0}.dark .sticky-tab-nav{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-tab-nav-responsive{z-index:40;margin-inline:calc(var(--spacing) * -4);border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 4);position:sticky;top:0}@media(min-width:40rem){.sticky-tab-nav-responsive{margin-inline:calc(var(--spacing) * -6);padding-inline:calc(var(--spacing) * 6)}}@media(min-width:64rem){.sticky-tab-nav-responsive{margin-inline:calc(var(--spacing) * -8);padding-inline:calc(var(--spacing) * 8)}}.dark .sticky-tab-nav-responsive{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-section-header{z-index:40;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);position:sticky;top:0}.dark .sticky-section-header{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.sticky-footer-buttons{z-index:10;margin-top:calc(var(--spacing) * 6);justify-content:flex-end;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);display:flex;position:sticky;bottom:0}.dark .sticky-footer-buttons{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.sticky-footer-buttons-between{z-index:10;margin-top:calc(var(--spacing) * 6);justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);display:flex;position:sticky;bottom:0}.dark .sticky-footer-buttons-between{border-color:var(--color-gray-700);background-color:var(--color-gray-800)}.footer-buttons-between{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 3);border-top-style:var(--tw-border-style);border-top-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-50);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3);display:flex}.dark .footer-buttons-between{border-color:var(--color-gray-700);background-color:var(--color-gray-900)}.tab-content{padding-top:calc(var(--spacing) * 6)}.admin-page-content{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 6)}.dark .admin-page-content{background-color:var(--color-gray-900)}.admin-page-content-compact{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 4)}.dark .admin-page-content-compact{background-color:var(--color-gray-900)}.admin-page-content-responsive{background-color:var(--color-gray-50);min-height:100vh;padding:calc(var(--spacing) * 4)}@media(min-width:40rem){.admin-page-content-responsive{padding:calc(var(--spacing) * 6)}}@media(min-width:64rem){.admin-page-content-responsive{padding:calc(var(--spacing) * 8)}}.dark .admin-page-content-responsive{background-color:var(--color-gray-900)}.admin-page-content-responsive-fluid{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4)}@media(min-width:40rem){.admin-page-content-responsive-fluid{padding:calc(var(--spacing) * 6)}}@media(min-width:64rem){.admin-page-content-responsive-fluid{padding:calc(var(--spacing) * 8)}}.dark .admin-page-content-responsive-fluid{background-color:var(--color-gray-900)}.admin-page-content-fluid{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 6)}.dark .admin-page-content-fluid{background-color:var(--color-gray-900)}.admin-page-content-fluid-compact{background-color:var(--color-gray-50);padding:calc(var(--spacing) * 4)}.dark .admin-page-content-fluid-compact{background-color:var(--color-gray-900)}.admin-page-content-viewport{background-color:var(--color-gray-50);height:calc(100vh - 64px);padding:calc(var(--spacing) * 6);overflow:hidden}.dark .admin-page-content-viewport{background-color:var(--color-gray-900)}.admin-page-content-viewport-compact{background-color:var(--color-gray-50);height:calc(100vh - 64px);padding:calc(var(--spacing) * 4);overflow:hidden}.dark .admin-page-content-viewport-compact{background-color:var(--color-gray-900)}.bulk-actions-bar{margin-bottom:calc(var(--spacing) * 4);align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-200);background-color:var(--color-gray-100);padding:calc(var(--spacing) * 3);display:flex}.dark .bulk-actions-bar{border-color:var(--color-gray-600);background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .bulk-actions-bar{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}:where(.admin-sidebar-menu-list>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}.admin-sidebar-menu-list{padding-inline:calc(var(--spacing) * 4);padding-bottom:calc(var(--spacing) * 4)}.admin-sidebar-item{border-radius:var(--radius-lg);width:100%;padding-inline:calc(var(--spacing) * 3);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));justify-content:space-between;align-items:center;display:flex}.admin-sidebar-subitem{border-radius:var(--radius-lg);width:100%;padding-block:6px;padding-right:calc(var(--spacing) * 3);--tw-font-weight:var(--font-weight-medium);font-size:13px;font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));justify-content:space-between;align-items:center;padding-left:30px;display:flex}.admin-sidebar-item-active{background-color:var(--color-slate-100);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-900)}.dark .admin-sidebar-item-active{background-color:var(--color-slate-700);color:var(--color-white)}.admin-sidebar-item-active-parent{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-slate-900)}.dark .admin-sidebar-item-active-parent{color:var(--color-white)}.admin-sidebar-item-default{color:var(--color-slate-500)}@media(hover:hover){.admin-sidebar-item-default:hover{background-color:var(--color-slate-50);color:var(--color-slate-900)}}.dark .admin-sidebar-item-default{color:var(--color-slate-400)}@media(hover:hover){.dark .admin-sidebar-item-default:hover{background-color:var(--color-slate-800);color:var(--color-white)}}.admin-sidebar-item-disabled{cursor:default;color:var(--color-slate-400)}.dark .admin-sidebar-item-disabled{color:var(--color-slate-500)}.admin-sidebar-icon-box{flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;display:flex}.admin-sidebar-chevron-trail{align-items:center;gap:var(--spacing);display:flex}.admin-sidebar-item-content{align-items:center;gap:calc(var(--spacing) * 2);display:flex}.admin-sidebar-module-icon{margin-right:calc(var(--spacing) * 2);height:calc(var(--spacing) * 3);width:calc(var(--spacing) * 3);color:var(--color-slate-400);opacity:.6}.dark .admin-sidebar-module-icon{color:var(--color-slate-500)}.admin-sidebar-tree-line-vertical{z-index:0;border-left-style:var(--tw-border-style);border-left-width:1px;border-color:var(--color-slate-300);position:absolute;top:0;bottom:0;left:12px}.dark .admin-sidebar-tree-line-vertical{border-color:var(--color-slate-600)}.admin-sidebar-tree-line-curve{z-index:10;border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-left-style:var(--tw-border-style);width:14px;height:50%;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-left-width:1px;border-color:var(--color-slate-300);border-bottom-left-radius:10px;position:absolute;top:0;left:12px}@media(hover:hover){.admin-sidebar-tree-line-curve:is(:where(.group):hover *){border-color:var(--color-blue-400)}}.dark .admin-sidebar-tree-line-curve{border-color:var(--color-slate-600)}@media(hover:hover){.dark .admin-sidebar-tree-line-curve:is(:where(.group):hover *){border-color:var(--color-blue-500)}}.admin-sidebar-header{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-slate-200);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 4);justify-content:space-between;align-items:center;display:flex}.dark .admin-sidebar-header{border-color:var(--color-slate-700)}.admin-sidebar-collapse-btn{border-radius:var(--radius-md);padding:calc(var(--spacing) * 2);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.admin-sidebar-collapse-btn:hover{background-color:var(--color-slate-100)}.dark .admin-sidebar-collapse-btn:hover{background-color:var(--color-slate-700)}}.tab-nav-default{gap:calc(var(--spacing) * 2);display:flex}.tab-nav-underline{border-bottom-style:var(--tw-border-style);border-bottom-width:1px;border-color:var(--color-gray-200);gap:0;display:flex}.dark .tab-nav-underline{border-color:var(--color-gray-700)}.tab-btn-base{align-items:center;gap:calc(var(--spacing) * 2);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.tab-btn-default{border-bottom-style:var(--tw-border-style);color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px}@media(hover:hover){.tab-btn-default:hover{background-color:var(--color-gray-50);color:var(--color-gray-900)}}.dark .tab-btn-default{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-default:hover{background-color:var(--color-gray-700);color:var(--color-gray-200)}}.tab-btn-default-active{border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-blue-600);background-color:var(--color-blue-50);color:var(--color-blue-600)}.dark .tab-btn-default-active{border-color:var(--color-blue-400);background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .tab-btn-default-active{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .tab-btn-default-active{color:var(--color-blue-400)}.tab-btn-underline{border-bottom-style:var(--tw-border-style);color:var(--color-gray-600);border-color:#0000;border-bottom-width:2px}@media(hover:hover){.tab-btn-underline:hover{border-color:var(--color-gray-300);color:var(--color-gray-900)}}.dark .tab-btn-underline{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-underline:hover{border-color:var(--color-gray-600);color:var(--color-gray-200)}}.tab-btn-underline-active{border-bottom-style:var(--tw-border-style);border-bottom-width:2px;border-color:var(--color-blue-600);color:var(--color-blue-600)}.dark .tab-btn-underline-active{border-color:var(--color-blue-400);color:var(--color-blue-400)}.tab-btn-pills{border-radius:var(--radius-lg);color:var(--color-gray-600)}@media(hover:hover){.tab-btn-pills:hover{background-color:var(--color-gray-100)}}.dark .tab-btn-pills{color:var(--color-gray-400)}@media(hover:hover){.dark .tab-btn-pills:hover{background-color:var(--color-gray-700)}}.tab-btn-pills-active{border-radius:var(--radius-lg);background-color:var(--color-blue-600);color:var(--color-white)}.dark .tab-btn-pills-active{background-color:var(--color-blue-500)}.tab-btn-disabled{cursor:not-allowed;color:var(--color-gray-400);opacity:.5}.dark .tab-btn-disabled{color:var(--color-gray-600)}.toggle-container{align-items:flex-start;column-gap:calc(var(--spacing) * 2);flex-wrap:wrap;width:auto;display:inline-flex}.toggle-switch-wrapper{flex-shrink:0;align-items:center;display:inline-flex;position:relative}.toggle-switch-track{background-color:var(--color-gray-200);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:relative}.toggle-switch-track:is(:where(.peer):focus~*){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-300);--tw-outline-style:none;outline-style:none}.toggle-switch-track:is(:where(.peer):checked~*){background-color:var(--color-blue-600)}.dark .toggle-switch-track{background-color:var(--color-gray-700)}.dark .toggle-switch-track:is(:where(.peer):focus~*){--tw-ring-color:var(--color-blue-800)}.dark .toggle-switch-track:is(:where(.peer):checked~*){background-color:var(--color-blue-500)}.toggle-switch-track:after{top:calc(var(--spacing) * .5);left:calc(var(--spacing) * .5);--tw-content:"";border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-gray-300);background-color:var(--color-white);content:var(--tw-content);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px;position:absolute}.dark .toggle-switch-track:after{content:var(--tw-content);border-color:var(--color-gray-600)}.toggle-switch-track:is(:where(.peer):checked~*):after{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y);content:var(--tw-content);border-color:var(--color-white)}.toggle-label{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-gray-900)}.dark .toggle-label{color:var(--color-white)}.toggle-description{margin-top:var(--spacing);width:100%;font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--color-gray-500)}.dark .toggle-description{color:var(--color-gray-400)}.admin-footer{margin-top:calc(var(--spacing) * 6);padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4)}.admin-footer-row{justify-content:space-between;align-items:center;gap:calc(var(--spacing) * 4);flex-direction:column;display:flex}@media(min-width:48rem){.admin-footer-row{flex-direction:row}}.admin-footer-group{align-items:center;gap:calc(var(--spacing) * 4);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);display:flex}.dark .admin-footer-group{color:var(--color-gray-400)}.admin-footer-link-hover{cursor:pointer;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.admin-footer-link-hover:hover{color:var(--color-blue-600)}.dark .admin-footer-link-hover:hover{color:var(--color-blue-400)}}.admin-footer-link{align-items:center;gap:calc(var(--spacing) * 2);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-600);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}@media(hover:hover){.admin-footer-link:hover{color:var(--color-blue-600)}}.dark .admin-footer-link{color:var(--color-gray-400)}@media(hover:hover){.dark .admin-footer-link:hover{color:var(--color-blue-400)}}.responsive-grid{gap:calc(var(--spacing) * 4);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.grid-cols-1-lg-3{gap:calc(var(--spacing) * 6);grid-template-columns:repeat(1,minmax(0,1fr));display:grid}.col-span-1-lg,.col-span-2-lg{grid-column:span 1/span 1}.modal-description{margin-top:var(--spacing);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--color-gray-500)}.dark .modal-description{color:var(--color-gray-400)}.toast-message{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-1{inset:var(--spacing)}.inset-2{inset:calc(var(--spacing) * 2)}.inset-3{inset:calc(var(--spacing) * 3)}.inset-4{inset:calc(var(--spacing) * 4)}.inset-5{inset:calc(var(--spacing) * 5)}.inset-6{inset:calc(var(--spacing) * 6)}.inset-8{inset:calc(var(--spacing) * 8)}.inset-auto{inset:auto}.inset-x-0{inset-inline:0}.inset-x-1{inset-inline:var(--spacing)}.inset-x-2{inset-inline:calc(var(--spacing) * 2)}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-4{inset-inline:calc(var(--spacing) * 4)}.inset-x-auto{inset-inline:auto}.inset-y-0{inset-block:0}.inset-y-1{inset-block:var(--spacing)}.inset-y-2{inset-block:calc(var(--spacing) * 2)}.inset-y-3{inset-block:calc(var(--spacing) * 3)}.inset-y-4{inset-block:calc(var(--spacing) * 4)}.inset-y-auto{inset-block:auto}.-top-1{top:calc(var(--spacing) * -1)}.-top-2{top:calc(var(--spacing) * -2)}.-top-3{top:calc(var(--spacing) * -3)}.-top-4{top:calc(var(--spacing) * -4)}.top-0{top:0}.top-1{top:var(--spacing)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-1\/3{top:33.3333%}.top-1\/4{top:25%}.top-2{top:calc(var(--spacing) * 2)}.top-2\/3{top:66.6667%}.top-3{top:calc(var(--spacing) * 3)}.top-3\/4{top:75%}.top-4{top:calc(var(--spacing) * 4)}.top-5{top:calc(var(--spacing) * 5)}.top-6{top:calc(var(--spacing) * 6)}.top-8{top:calc(var(--spacing) * 8)}.top-10{top:calc(var(--spacing) * 10)}.top-12{top:calc(var(--spacing) * 12)}.top-16{top:calc(var(--spacing) * 16)}.top-auto{top:auto}.top-full{top:100%}.-right-1{right:calc(var(--spacing) * -1)}.-right-2{right:calc(var(--spacing) * -2)}.-right-3{right:calc(var(--spacing) * -3)}.-right-4{right:calc(var(--spacing) * -4)}.right-0{right:0}.right-1{right:var(--spacing)}.right-1\/2{right:50%}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-8{right:calc(var(--spacing) * 8)}.right-10{right:calc(var(--spacing) * 10)}.right-12{right:calc(var(--spacing) * 12)}.right-16{right:calc(var(--spacing) * 16)}.right-auto{right:auto}.right-full{right:100%}.-bottom-1{bottom:calc(var(--spacing) * -1)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-3{bottom:calc(var(--spacing) * -3)}.-bottom-4{bottom:calc(var(--spacing) * -4)}.bottom-0{bottom:0}.bottom-1{bottom:var(--spacing)}.bottom-1\/2{bottom:50%}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-5{bottom:calc(var(--spacing) * 5)}.bottom-6{bottom:calc(var(--spacing) * 6)}.bottom-8{bottom:calc(var(--spacing) * 8)}.bottom-10{bottom:calc(var(--spacing) * 10)}.bottom-12{bottom:calc(var(--spacing) * 12)}.bottom-16{bottom:calc(var(--spacing) * 16)}.bottom-auto{bottom:auto}.bottom-full{bottom:100%}.-left-1{left:calc(var(--spacing) * -1)}.-left-2{left:calc(var(--spacing) * -2)}.-left-3{left:calc(var(--spacing) * -3)}.-left-4{left:calc(var(--spacing) * -4)}.left-0{left:0}.left-1{left:var(--spacing)}.left-1\.5{left:calc(var(--spacing) * 1.5)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing) * 2)}.left-3{left:calc(var(--spacing) * 3)}.left-4{left:calc(var(--spacing) * 4)}.left-5{left:calc(var(--spacing) * 5)}.left-6{left:calc(var(--spacing) * 6)}.left-8{left:calc(var(--spacing) * 8)}.left-10{left:calc(var(--spacing) * 10)}.left-12{left:calc(var(--spacing) * 12)}.left-16{left:calc(var(--spacing) * 16)}.left-auto{left:auto}.left-full{left:100%}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[999\]{z-index:999}.z-\[9999\]{z-index:9999}.z-auto{z-index:auto}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-first{order:-9999}.order-last{order:9999}.order-none{order:0}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.col-span-7{grid-column:span 7/span 7}.col-span-8{grid-column:span 8/span 8}.col-span-9{grid-column:span 9/span 9}.col-span-10{grid-column:span 10/span 10}.col-span-11{grid-column:span 11/span 11}.col-span-12{grid-column:span 12/span 12}.col-span-full{grid-column:1/-1}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.col-start-3{grid-column-start:3}.col-start-4{grid-column-start:4}.col-start-5{grid-column-start:5}.col-start-6{grid-column-start:6}.col-start-7{grid-column-start:7}.col-start-8{grid-column-start:8}.col-start-9{grid-column-start:9}.col-start-10{grid-column-start:10}.col-start-11{grid-column-start:11}.col-start-12{grid-column-start:12}.col-start-13{grid-column-start:13}.col-end-1{grid-column-end:1}.col-end-2{grid-column-end:2}.col-end-3{grid-column-end:3}.col-end-4{grid-column-end:4}.col-end-5{grid-column-end:5}.col-end-6{grid-column-end:6}.col-end-7{grid-column-end:7}.col-end-8{grid-column-end:8}.col-end-9{grid-column-end:9}.col-end-10{grid-column-end:10}.col-end-11{grid-column-end:11}.col-end-12{grid-column-end:12}.col-end-13{grid-column-end:13}.row-span-1{grid-row:span 1/span 1}.row-span-2{grid-row:span 2/span 2}.row-span-3{grid-row:span 3/span 3}.row-span-4{grid-row:span 4/span 4}.row-span-5{grid-row:span 5/span 5}.row-span-6{grid-row:span 6/span 6}.row-span-full{grid-row:1/-1}.row-start-1{grid-row-start:1}.row-start-2{grid-row-start:2}.row-start-3{grid-row-start:3}.row-start-4{grid-row-start:4}.row-start-5{grid-row-start:5}.row-start-6{grid-row-start:6}.row-start-7{grid-row-start:7}.row-end-1{grid-row-end:1}.row-end-2{grid-row-end:2}.row-end-3{grid-row-end:3}.row-end-4{grid-row-end:4}.row-end-5{grid-row-end:5}.row-end-6{grid-row-end:6}.row-end-7{grid-row-end:7}.float-end{float:inline-end}.float-left{float:left}.float-none{float:none}.float-right{float:right}.float-start{float:inline-start}.clear-both{clear:both}.clear-end{clear:inline-end}.clear-left{clear:left}.clear-none{clear:none}.clear-right{clear:right}.clear-start{clear:inline-start}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.\!m-0{margin:0!important}.\!m-1{margin:var(--spacing)!important}.\!m-2{margin:calc(var(--spacing) * 2)!important}.\!m-3{margin:calc(var(--spacing) * 3)!important}.\!m-4{margin:calc(var(--spacing) * 4)!important}.\!m-5{margin:calc(var(--spacing) * 5)!important}.\!m-6{margin:calc(var(--spacing) * 6)!important}.\!m-8{margin:calc(var(--spacing) * 8)!important}.\!m-10{margin:calc(var(--spacing) * 10)!important}.\!m-12{margin:calc(var(--spacing) * 12)!important}.\!m-16{margin:calc(var(--spacing) * 16)!important}.\!m-auto{margin:auto!important}.-m-1{margin:calc(var(--spacing) * -1)}.-m-2{margin:calc(var(--spacing) * -2)}.-m-3{margin:calc(var(--spacing) * -3)}.-m-4{margin:calc(var(--spacing) * -4)}.-m-5{margin:calc(var(--spacing) * -5)}.-m-6{margin:calc(var(--spacing) * -6)}.-m-8{margin:calc(var(--spacing) * -8)}.m-0{margin:0}.m-1{margin:var(--spacing)}.m-2{margin:calc(var(--spacing) * 2)}.m-3{margin:calc(var(--spacing) * 3)}.m-4{margin:calc(var(--spacing) * 4)}.m-5{margin:calc(var(--spacing) * 5)}.m-6{margin:calc(var(--spacing) * 6)}.m-8{margin:calc(var(--spacing) * 8)}.m-10{margin:calc(var(--spacing) * 10)}.m-12{margin:calc(var(--spacing) * 12)}.m-16{margin:calc(var(--spacing) * 16)}.m-auto{margin:auto}.\!mx-0{margin-inline:0!important}.\!mx-1{margin-inline:var(--spacing)!important}.\!mx-2{margin-inline:calc(var(--spacing) * 2)!important}.\!mx-3{margin-inline:calc(var(--spacing) * 3)!important}.\!mx-4{margin-inline:calc(var(--spacing) * 4)!important}.\!mx-5{margin-inline:calc(var(--spacing) * 5)!important}.\!mx-6{margin-inline:calc(var(--spacing) * 6)!important}.\!mx-8{margin-inline:calc(var(--spacing) * 8)!important}.\!mx-10{margin-inline:calc(var(--spacing) * 10)!important}.\!mx-12{margin-inline:calc(var(--spacing) * 12)!important}.\!mx-16{margin-inline:calc(var(--spacing) * 16)!important}.\!mx-auto{margin-inline:auto!important}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.-mx-2{margin-inline:calc(var(--spacing) * -2)}.-mx-3{margin-inline:calc(var(--spacing) * -3)}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.-mx-5{margin-inline:calc(var(--spacing) * -5)}.-mx-6{margin-inline:calc(var(--spacing) * -6)}.-mx-8{margin-inline:calc(var(--spacing) * -8)}.mx-0{margin-inline:0}.mx-1{margin-inline:var(--spacing)}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-6{margin-inline:calc(var(--spacing) * 6)}.mx-8{margin-inline:calc(var(--spacing) * 8)}.mx-10{margin-inline:calc(var(--spacing) * 10)}.mx-12{margin-inline:calc(var(--spacing) * 12)}.mx-16{margin-inline:calc(var(--spacing) * 16)}.mx-auto{margin-inline:auto}.\!my-0{margin-block:0!important}.\!my-1{margin-block:var(--spacing)!important}.\!my-2{margin-block:calc(var(--spacing) * 2)!important}.\!my-3{margin-block:calc(var(--spacing) * 3)!important}.\!my-4{margin-block:calc(var(--spacing) * 4)!important}.\!my-5{margin-block:calc(var(--spacing) * 5)!important}.\!my-6{margin-block:calc(var(--spacing) * 6)!important}.\!my-8{margin-block:calc(var(--spacing) * 8)!important}.\!my-10{margin-block:calc(var(--spacing) * 10)!important}.\!my-12{margin-block:calc(var(--spacing) * 12)!important}.\!my-16{margin-block:calc(var(--spacing) * 16)!important}.\!my-auto{margin-block:auto!important}.-my-1{margin-block:calc(var(--spacing) * -1)}.-my-2{margin-block:calc(var(--spacing) * -2)}.-my-3{margin-block:calc(var(--spacing) * -3)}.-my-4{margin-block:calc(var(--spacing) * -4)}.-my-5{margin-block:calc(var(--spacing) * -5)}.-my-6{margin-block:calc(var(--spacing) * -6)}.-my-8{margin-block:calc(var(--spacing) * -8)}.my-0{margin-block:0}.my-1{margin-block:var(--spacing)}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.my-5{margin-block:calc(var(--spacing) * 5)}.my-6{margin-block:calc(var(--spacing) * 6)}.my-8{margin-block:calc(var(--spacing) * 8)}.my-10{margin-block:calc(var(--spacing) * 10)}.my-12{margin-block:calc(var(--spacing) * 12)}.my-16{margin-block:calc(var(--spacing) * 16)}.my-auto{margin-block:auto}.-mt-1{margin-top:calc(var(--spacing) * -1)}.-mt-2{margin-top:calc(var(--spacing) * -2)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.-mt-4{margin-top:calc(var(--spacing) * -4)}.-mt-5{margin-top:calc(var(--spacing) * -5)}.-mt-6{margin-top:calc(var(--spacing) * -6)}.-mt-8{margin-top:calc(var(--spacing) * -8)}.mt-0{margin-top:0}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mt-auto{margin-top:auto}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mr-2{margin-right:calc(var(--spacing) * -2)}.-mr-3{margin-right:calc(var(--spacing) * -3)}.-mr-4{margin-right:calc(var(--spacing) * -4)}.-mr-5{margin-right:calc(var(--spacing) * -5)}.-mr-6{margin-right:calc(var(--spacing) * -6)}.-mr-8{margin-right:calc(var(--spacing) * -8)}.mr-0{margin-right:0}.mr-1{margin-right:var(--spacing)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mr-4{margin-right:calc(var(--spacing) * 4)}.mr-5{margin-right:calc(var(--spacing) * 5)}.mr-6{margin-right:calc(var(--spacing) * 6)}.mr-8{margin-right:calc(var(--spacing) * 8)}.mr-10{margin-right:calc(var(--spacing) * 10)}.mr-12{margin-right:calc(var(--spacing) * 12)}.mr-16{margin-right:calc(var(--spacing) * 16)}.mr-auto{margin-right:auto}.-mb-1{margin-bottom:calc(var(--spacing) * -1)}.-mb-2{margin-bottom:calc(var(--spacing) * -2)}.-mb-3{margin-bottom:calc(var(--spacing) * -3)}.-mb-4{margin-bottom:calc(var(--spacing) * -4)}.-mb-5{margin-bottom:calc(var(--spacing) * -5)}.-mb-6{margin-bottom:calc(var(--spacing) * -6)}.-mb-8{margin-bottom:calc(var(--spacing) * -8)}.-mb-px{margin-bottom:-1px}.mb-0{margin-bottom:0}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.mb-16{margin-bottom:calc(var(--spacing) * 16)}.mb-auto{margin-bottom:auto}.-ml-1{margin-left:calc(var(--spacing) * -1)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.-ml-3{margin-left:calc(var(--spacing) * -3)}.-ml-4{margin-left:calc(var(--spacing) * -4)}.-ml-5{margin-left:calc(var(--spacing) * -5)}.-ml-6{margin-left:calc(var(--spacing) * -6)}.-ml-8{margin-left:calc(var(--spacing) * -8)}.ml-0{margin-left:0}.ml-0\.5{margin-left:calc(var(--spacing) * .5)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-5{margin-left:calc(var(--spacing) * 5)}.ml-6{margin-left:calc(var(--spacing) * 6)}.ml-8{margin-left:calc(var(--spacing) * 8)}.ml-10{margin-left:calc(var(--spacing) * 10)}.ml-12{margin-left:calc(var(--spacing) * 12)}.ml-16{margin-left:calc(var(--spacing) * 16)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.box-content{box-sizing:content-box}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\!block{display:block!important}.\!flex{display:flex!important}.\!grid{display:grid!important}.\!hidden{display:none!important}.\!inline{display:inline!important}.\!inline-block{display:inline-block!important}.\!inline-flex{display:inline-flex!important}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.aspect-auto{aspect-ratio:auto}.aspect-square{aspect-ratio:1}.aspect-video{aspect-ratio:var(--aspect-video)}.\!h-0{height:0!important}.\!h-1{height:var(--spacing)!important}.\!h-2{height:calc(var(--spacing) * 2)!important}.\!h-3{height:calc(var(--spacing) * 3)!important}.\!h-4{height:calc(var(--spacing) * 4)!important}.\!h-5{height:calc(var(--spacing) * 5)!important}.\!h-6{height:calc(var(--spacing) * 6)!important}.\!h-8{height:calc(var(--spacing) * 8)!important}.\!h-10{height:calc(var(--spacing) * 10)!important}.\!h-12{height:calc(var(--spacing) * 12)!important}.\!h-16{height:calc(var(--spacing) * 16)!important}.\!h-20{height:calc(var(--spacing) * 20)!important}.\!h-24{height:calc(var(--spacing) * 24)!important}.\!h-32{height:calc(var(--spacing) * 32)!important}.\!h-40{height:calc(var(--spacing) * 40)!important}.\!h-48{height:calc(var(--spacing) * 48)!important}.\!h-56{height:calc(var(--spacing) * 56)!important}.\!h-64{height:calc(var(--spacing) * 64)!important}.\!h-72{height:calc(var(--spacing) * 72)!important}.\!h-80{height:calc(var(--spacing) * 80)!important}.\!h-96{height:calc(var(--spacing) * 96)!important}.\!h-auto{height:auto!important}.\!h-fit{height:fit-content!important}.\!h-full{height:100%!important}.\!h-max{height:max-content!important}.\!h-min{height:min-content!important}.\!h-px{height:1px!important}.\!h-screen{height:100vh!important}.h-0{height:0}.h-1{height:var(--spacing)}.h-1\/2{height:50%}.h-1\/3{height:33.3333%}.h-1\/4{height:25%}.h-1\/5{height:20%}.h-1\/6{height:16.6667%}.h-2{height:calc(var(--spacing) * 2)}.h-2\/3{height:66.6667%}.h-2\/4{height:50%}.h-2\/5{height:40%}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-3\/4{height:75%}.h-3\/5{height:60%}.h-4{height:calc(var(--spacing) * 4)}.h-4\/5{height:80%}.h-5{height:calc(var(--spacing) * 5)}.h-5\/6{height:83.3333%}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-24{height:calc(var(--spacing) * 24)}.h-32{height:calc(var(--spacing) * 32)}.h-40{height:calc(var(--spacing) * 40)}.h-48{height:calc(var(--spacing) * 48)}.h-56{height:calc(var(--spacing) * 56)}.h-64{height:calc(var(--spacing) * 64)}.h-72{height:calc(var(--spacing) * 72)}.h-80{height:calc(var(--spacing) * 80)}.h-96{height:calc(var(--spacing) * 96)}.h-\[42px\]{height:42px}.h-auto{height:auto}.h-fit{height:fit-content}.h-full{height:100%}.h-max{height:max-content}.h-min{height:min-content}.h-px{height:1px}.h-screen{height:100vh}.max-h-0{max-height:0}.max-h-20{max-height:calc(var(--spacing) * 20)}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-60{max-height:calc(var(--spacing) * 60)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-80{max-height:calc(var(--spacing) * 80)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[70vh\]{max-height:70vh}.max-h-\[400px\]{max-height:400px}.max-h-\[500px\]{max-height:500px}.max-h-\[calc\(90vh-140px\)\]{max-height:calc(90vh - 140px)}.max-h-\[calc\(100vh-320px\)\]{max-height:calc(100vh - 320px)}.max-h-fit{max-height:fit-content}.max-h-full{max-height:100%}.max-h-max{max-height:max-content}.max-h-min{max-height:min-content}.max-h-none{max-height:none}.max-h-screen{max-height:100vh}.min-h-0{min-height:0}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-12{min-height:calc(var(--spacing) * 12)}.min-h-16{min-height:calc(var(--spacing) * 16)}.min-h-20{min-height:calc(var(--spacing) * 20)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-32{min-height:calc(var(--spacing) * 32)}.min-h-40{min-height:calc(var(--spacing) * 40)}.min-h-48{min-height:calc(var(--spacing) * 48)}.min-h-56{min-height:calc(var(--spacing) * 56)}.min-h-64{min-height:calc(var(--spacing) * 64)}.min-h-\[40px\]{min-height:40px}.min-h-\[42px\]{min-height:42px}.min-h-\[60px\]{min-height:60px}.min-h-\[60vh\]{min-height:60vh}.min-h-\[80px\]{min-height:80px}.min-h-\[200px\]{min-height:200px}.min-h-\[400px\]{min-height:400px}.min-h-fit{min-height:fit-content}.min-h-full{min-height:100%}.min-h-max{min-height:max-content}.min-h-min{min-height:min-content}.min-h-screen{min-height:100vh}.\!w-0{width:0!important}.\!w-1{width:var(--spacing)!important}.\!w-1\/2{width:50%!important}.\!w-1\/3{width:33.3333%!important}.\!w-1\/4{width:25%!important}.\!w-1\/5{width:20%!important}.\!w-1\/6{width:16.6667%!important}.\!w-2{width:calc(var(--spacing) * 2)!important}.\!w-2\/3{width:66.6667%!important}.\!w-2\/4{width:50%!important}.\!w-2\/5{width:40%!important}.\!w-3{width:calc(var(--spacing) * 3)!important}.\!w-3\/4{width:75%!important}.\!w-3\/5{width:60%!important}.\!w-4{width:calc(var(--spacing) * 4)!important}.\!w-4\/5{width:80%!important}.\!w-5{width:calc(var(--spacing) * 5)!important}.\!w-5\/6{width:83.3333%!important}.\!w-6{width:calc(var(--spacing) * 6)!important}.\!w-8{width:calc(var(--spacing) * 8)!important}.\!w-10{width:calc(var(--spacing) * 10)!important}.\!w-12{width:calc(var(--spacing) * 12)!important}.\!w-16{width:calc(var(--spacing) * 16)!important}.\!w-20{width:calc(var(--spacing) * 20)!important}.\!w-24{width:calc(var(--spacing) * 24)!important}.\!w-32{width:calc(var(--spacing) * 32)!important}.\!w-40{width:calc(var(--spacing) * 40)!important}.\!w-48{width:calc(var(--spacing) * 48)!important}.\!w-52{width:calc(var(--spacing) * 52)!important}.\!w-56{width:calc(var(--spacing) * 56)!important}.\!w-60{width:calc(var(--spacing) * 60)!important}.\!w-64{width:calc(var(--spacing) * 64)!important}.\!w-72{width:calc(var(--spacing) * 72)!important}.\!w-80{width:calc(var(--spacing) * 80)!important}.\!w-96{width:calc(var(--spacing) * 96)!important}.\!w-auto{width:auto!important}.\!w-fit{width:fit-content!important}.\!w-full{width:100%!important}.\!w-max{width:max-content!important}.\!w-min{width:min-content!important}.\!w-px{width:1px!important}.\!w-screen{width:100vw!important}.w-0{width:0}.w-1{width:var(--spacing)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-1\/4{width:25%}.w-1\/5{width:20%}.w-1\/6{width:16.6667%}.w-2{width:calc(var(--spacing) * 2)}.w-2\/3{width:66.6667%}.w-2\/4{width:50%}.w-2\/5{width:40%}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-3\/4{width:75%}.w-3\/5{width:60%}.w-4{width:calc(var(--spacing) * 4)}.w-4\/5{width:80%}.w-5{width:calc(var(--spacing) * 5)}.w-5\/6{width:83.3333%}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-36{width:calc(var(--spacing) * 36)}.w-40{width:calc(var(--spacing) * 40)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-80{width:calc(var(--spacing) * 80)}.w-96{width:calc(var(--spacing) * 96)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-min{width:min-content}.w-px{width:1px}.w-screen{width:100vw}.\!max-w-0{max-width:0!important}.\!max-w-2xl{max-width:var(--container-2xl)!important}.\!max-w-3xl{max-width:var(--container-3xl)!important}.\!max-w-4xl{max-width:var(--container-4xl)!important}.\!max-w-full{max-width:100%!important}.\!max-w-lg{max-width:var(--container-lg)!important}.\!max-w-md{max-width:var(--container-md)!important}.\!max-w-none{max-width:none!important}.\!max-w-sm{max-width:var(--container-sm)!important}.\!max-w-xl{max-width:var(--container-xl)!important}.\!max-w-xs{max-width:var(--container-xs)!important}.max-w-0{max-width:0}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[200px\]{max-width:200px}.max-w-\[calc\(100vw-1rem\)\]{max-width:calc(100vw - 1rem)}.max-w-\[calc\(100vw-2rem\)\]{max-width:calc(100vw - 2rem)}.max-w-fit{max-width:fit-content}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-max{max-width:max-content}.max-w-md{max-width:var(--container-md)}.max-w-min{max-width:min-content}.max-w-none{max-width:none}.max-w-prose{max-width:65ch}.max-w-screen-2xl{max-width:var(--breakpoint-2xl)}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.max-w-screen-md{max-width:var(--breakpoint-md)}.max-w-screen-sm{max-width:var(--breakpoint-sm)}.max-w-screen-xl{max-width:var(--breakpoint-xl)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.\!min-w-0{min-width:0!important}.\!min-w-4{min-width:calc(var(--spacing) * 4)!important}.\!min-w-8{min-width:calc(var(--spacing) * 8)!important}.\!min-w-12{min-width:calc(var(--spacing) * 12)!important}.\!min-w-16{min-width:calc(var(--spacing) * 16)!important}.\!min-w-20{min-width:calc(var(--spacing) * 20)!important}.\!min-w-24{min-width:calc(var(--spacing) * 24)!important}.\!min-w-32{min-width:calc(var(--spacing) * 32)!important}.\!min-w-40{min-width:calc(var(--spacing) * 40)!important}.\!min-w-48{min-width:calc(var(--spacing) * 48)!important}.\!min-w-56{min-width:calc(var(--spacing) * 56)!important}.\!min-w-64{min-width:calc(var(--spacing) * 64)!important}.\!min-w-fit{min-width:fit-content!important}.\!min-w-full{min-width:100%!important}.\!min-w-max{min-width:max-content!important}.\!min-w-min{min-width:min-content!important}.min-w-0{min-width:0}.min-w-\[20px\]{min-width:20px}.min-w-\[80px\]{min-width:80px}.min-w-\[120px\]{min-width:120px}.min-w-\[200px\]{min-width:200px}.min-w-\[280px\]{min-width:280px}.min-w-\[300px\]{min-width:300px}.min-w-fit{min-width:fit-content}.min-w-full{min-width:100%}.min-w-max{min-width:max-content}.min-w-min{min-width:min-content}.\!flex-1{flex:1!important}.\!flex-auto{flex:auto!important}.\!flex-initial{flex:0 auto!important}.\!flex-none{flex:none!important}.flex-1{flex:1}.flex-auto{flex:auto}.flex-initial{flex:0 auto}.flex-none{flex:none}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-0{flex-basis:0}.basis-1{flex-basis:var(--spacing)}.basis-1\/2{flex-basis:50%}.basis-1\/3{flex-basis:33.3333%}.basis-1\/4{flex-basis:25%}.basis-2{flex-basis:calc(var(--spacing) * 2)}.basis-2\/3{flex-basis:66.6667%}.basis-3\/4{flex-basis:75%}.basis-auto{flex-basis:auto}.basis-full{flex-basis:100%}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.caption-bottom{caption-side:bottom}.caption-top{caption-side:top}.border-collapse{border-collapse:collapse}.border-separate{border-collapse:separate}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-1{--tw-border-spacing-x:var(--spacing);--tw-border-spacing-y:var(--spacing);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-2{--tw-border-spacing-x:calc(var(--spacing) * 2);--tw-border-spacing-y:calc(var(--spacing) * 2);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-3{--tw-border-spacing-x:calc(var(--spacing) * 3);--tw-border-spacing-y:calc(var(--spacing) * 3);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-4{--tw-border-spacing-x:calc(var(--spacing) * 4);--tw-border-spacing-y:calc(var(--spacing) * 4);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-5{--tw-border-spacing-x:calc(var(--spacing) * 5);--tw-border-spacing-y:calc(var(--spacing) * 5);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-6{--tw-border-spacing-x:calc(var(--spacing) * 6);--tw-border-spacing-y:calc(var(--spacing) * 6);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.border-spacing-8{--tw-border-spacing-x:calc(var(--spacing) * 8);--tw-border-spacing-y:calc(var(--spacing) * 8);border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.origin-bottom{transform-origin:bottom}.origin-bottom-left{transform-origin:0 100%}.origin-bottom-right{transform-origin:100% 100%}.origin-center{transform-origin:50%}.origin-left{transform-origin:0}.origin-right{transform-origin:100%}.origin-top{transform-origin:top}.origin-top-left{transform-origin:0 0}.origin-top-right{transform-origin:100% 0}.-translate-x-1{--tw-translate-x:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-2{--tw-translate-x:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-3{--tw-translate-x:calc(var(--spacing) * -3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-4{--tw-translate-x:calc(var(--spacing) * -4);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/2{--tw-translate-x: 50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/3{--tw-translate-x:calc(1 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1\/4{--tw-translate-x: 25% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-2{--tw-translate-x:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-2\/3{--tw-translate-x:calc(2 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-3{--tw-translate-x:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-3\/4{--tw-translate-x: 75% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-6{--tw-translate-x:calc(var(--spacing) * 6);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-8{--tw-translate-x:calc(var(--spacing) * 8);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-10{--tw-translate-x:calc(var(--spacing) * 10);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-12{--tw-translate-x:calc(var(--spacing) * 12);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1{--tw-translate-y:calc(var(--spacing) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-2{--tw-translate-y:calc(var(--spacing) * -2);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-3{--tw-translate-y:calc(var(--spacing) * -3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-4{--tw-translate-y:calc(var(--spacing) * -4);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-full{--tw-translate-y:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-0{--tw-translate-y:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1{--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/2{--tw-translate-y: 50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/3{--tw-translate-y:calc(1 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-1\/4{--tw-translate-y: 25% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-2{--tw-translate-y:calc(var(--spacing) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-2\/3{--tw-translate-y:calc(2 / 3 * 100%);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3{--tw-translate-y:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3\/4{--tw-translate-y: 75% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-4{--tw-translate-y:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-5{--tw-translate-y:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-6{--tw-translate-y:calc(var(--spacing) * 6);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-8{--tw-translate-y:calc(var(--spacing) * 8);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-10{--tw-translate-y:calc(var(--spacing) * 10);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-12{--tw-translate-y:calc(var(--spacing) * 12);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-full{--tw-translate-y:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-50{--tw-scale-x:50%;--tw-scale-y:50%;--tw-scale-z:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-90{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-95{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-105{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-125{--tw-scale-x:125%;--tw-scale-y:125%;--tw-scale-z:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-150{--tw-scale-x:150%;--tw-scale-y:150%;--tw-scale-z:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-0{--tw-scale-x:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-50{--tw-scale-x:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-75{--tw-scale-x:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-90{--tw-scale-x:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-95{--tw-scale-x:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-100{--tw-scale-x:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-105{--tw-scale-x:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-110{--tw-scale-x:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-125{--tw-scale-x:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-x-150{--tw-scale-x:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-0{--tw-scale-y:0%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-50{--tw-scale-y:50%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-75{--tw-scale-y:75%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-90{--tw-scale-y:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-95{--tw-scale-y:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-100{--tw-scale-y:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-105{--tw-scale-y:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-110{--tw-scale-y:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-125{--tw-scale-y:125%;scale:var(--tw-scale-x) var(--tw-scale-y)}.scale-y-150{--tw-scale-y:150%;scale:var(--tw-scale-x) var(--tw-scale-y)}.-rotate-1{rotate:-1deg}.-rotate-2{rotate:-2deg}.-rotate-3{rotate:-3deg}.-rotate-6{rotate:-6deg}.-rotate-12{rotate:-12deg}.-rotate-45{rotate:-45deg}.-rotate-90{rotate:-90deg}.-rotate-180{rotate:-180deg}.rotate-0{rotate:0deg}.rotate-1{rotate:1deg}.rotate-2{rotate:2deg}.rotate-3{rotate:3deg}.rotate-6{rotate:6deg}.rotate-12{rotate:12deg}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.-skew-x-1{--tw-skew-x:skewX( -1deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-2{--tw-skew-x:skewX( -2deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-3{--tw-skew-x:skewX( -3deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-6{--tw-skew-x:skewX( -6deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-x-12{--tw-skew-x:skewX( -12deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-0{--tw-skew-x:skewX(0deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-1{--tw-skew-x:skewX(1deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-2{--tw-skew-x:skewX(2deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-3{--tw-skew-x:skewX(3deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-6{--tw-skew-x:skewX(6deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-x-12{--tw-skew-x:skewX(12deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-1{--tw-skew-y:skewY( -1deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-2{--tw-skew-y:skewY( -2deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-3{--tw-skew-y:skewY( -3deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-6{--tw-skew-y:skewY( -6deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.-skew-y-12{--tw-skew-y:skewY( -12deg );transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-0{--tw-skew-y:skewY(0deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-1{--tw-skew-y:skewY(1deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-2{--tw-skew-y:skewY(2deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-3{--tw-skew-y:skewY(3deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-6{--tw-skew-y:skewY(6deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.skew-y-12{--tw-skew-y:skewY(12deg);transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform,.transform-cpu{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform-gpu{transform:translateZ(0) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.transform-none{transform:none}.animate-\[loading_1\.5s_ease-in-out_infinite\]{animation:1.5s ease-in-out infinite loading}.animate-bounce{animation:var(--animate-bounce)}.animate-none{animation:none}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-alias{cursor:alias}.cursor-all-scroll{cursor:all-scroll}.cursor-auto{cursor:auto}.cursor-cell{cursor:cell}.cursor-col-resize{cursor:col-resize}.cursor-context-menu{cursor:context-menu}.cursor-copy{cursor:copy}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-e-resize{cursor:e-resize}.cursor-ew-resize{cursor:ew-resize}.cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}.cursor-help{cursor:help}.cursor-move{cursor:move}.cursor-n-resize{cursor:n-resize}.cursor-ne-resize{cursor:ne-resize}.cursor-nesw-resize{cursor:nesw-resize}.cursor-no-drop{cursor:no-drop}.cursor-none{cursor:none}.cursor-not-allowed{cursor:not-allowed}.cursor-ns-resize{cursor:ns-resize}.cursor-nw-resize{cursor:nw-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.cursor-progress{cursor:progress}.cursor-row-resize{cursor:row-resize}.cursor-s-resize{cursor:s-resize}.cursor-se-resize{cursor:se-resize}.cursor-sw-resize{cursor:sw-resize}.cursor-text{cursor:text}.cursor-vertical-text{cursor:vertical-text}.cursor-w-resize{cursor:w-resize}.cursor-wait{cursor:wait}.cursor-zoom-in{cursor:zoom-in}.cursor-zoom-out{cursor:zoom-out}.touch-pan-left{--tw-pan-x:pan-left;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-right{--tw-pan-x:pan-right;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-x{--tw-pan-x:pan-x;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-down{--tw-pan-y:pan-down;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-up{--tw-pan-y:pan-up;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pan-y{--tw-pan-y:pan-y;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.touch-auto{touch-action:auto}.touch-manipulation{touch-action:manipulation}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.snap-both{scroll-snap-type:both var(--tw-scroll-snap-strictness)}.snap-none{scroll-snap-type:none}.snap-x{scroll-snap-type:x var(--tw-scroll-snap-strictness)}.snap-y{scroll-snap-type:y var(--tw-scroll-snap-strictness)}.snap-mandatory{--tw-scroll-snap-strictness:mandatory}.snap-proximity{--tw-scroll-snap-strictness:proximity}.snap-align-none{scroll-snap-align:none}.snap-center{scroll-snap-align:center}.snap-end{scroll-snap-align:end}.snap-start{scroll-snap-align:start}.snap-always{scroll-snap-stop:always}.snap-normal{scroll-snap-stop:normal}.scroll-mt-44{scroll-margin-top:calc(var(--spacing) * 44)}.list-inside{list-style-position:inside}.list-outside{list-style-position:outside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-auto{appearance:auto}.appearance-none{appearance:none}.columns-1{columns:1}.columns-2{columns:2}.columns-2xl{columns:var(--container-2xl)}.columns-2xs{columns:var(--container-2xs)}.columns-3{columns:3}.columns-3xl{columns:var(--container-3xl)}.columns-3xs{columns:var(--container-3xs)}.columns-4{columns:4}.columns-4xl{columns:var(--container-4xl)}.columns-5{columns:5}.columns-5xl{columns:var(--container-5xl)}.columns-6{columns:6}.columns-6xl{columns:var(--container-6xl)}.columns-7{columns:7}.columns-7xl{columns:var(--container-7xl)}.columns-8{columns:8}.columns-9{columns:9}.columns-10{columns:10}.columns-11{columns:11}.columns-12{columns:12}.columns-auto{columns:auto}.columns-lg{columns:var(--container-lg)}.columns-md{columns:var(--container-md)}.columns-sm{columns:var(--container-sm)}.columns-xl{columns:var(--container-xl)}.columns-xs{columns:var(--container-xs)}.break-before-all{break-before:all}.break-before-auto{break-before:auto}.break-before-avoid{break-before:avoid}.break-before-avoid-page{break-before:avoid-page}.break-before-column{break-before:column}.break-before-left{break-before:left}.break-before-page{break-before:page}.break-before-right{break-before:right}.break-inside-auto{break-inside:auto}.break-inside-avoid{break-inside:avoid}.break-inside-avoid-column{break-inside:avoid-column}.break-inside-avoid-page{break-inside:avoid-page}.break-after-all{break-after:all}.break-after-auto{break-after:auto}.break-after-avoid{break-after:avoid}.break-after-avoid-page{break-after:avoid-page}.break-after-column{break-after:column}.break-after-left{break-after:left}.break-after-page{break-after:page}.break-after-right{break-after:right}.auto-cols-auto{grid-auto-columns:auto}.auto-cols-fr{grid-auto-columns:minmax(0,1fr)}.auto-cols-max{grid-auto-columns:max-content}.auto-cols-min{grid-auto-columns:min-content}.grid-flow-col{grid-auto-flow:column}.grid-flow-col-dense{grid-auto-flow:column dense}.grid-flow-dense{grid-auto-flow:dense}.grid-flow-row{grid-auto-flow:row}.grid-flow-row-dense{grid-auto-flow:dense}.auto-rows-auto{grid-auto-rows:auto}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.auto-rows-max{grid-auto-rows:max-content}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.grid-cols-11{grid-template-columns:repeat(11,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.\!flex-col{flex-direction:column!important}.\!flex-row{flex-direction:row!important}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.\!flex-nowrap{flex-wrap:nowrap!important}.\!flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap}.flex-wrap{flex-wrap:wrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.place-content-around{place-content:space-around}.place-content-baseline{place-content:baseline start}.place-content-between{place-content:space-between}.place-content-center{place-content:center}.place-content-end{place-content:end}.place-content-evenly{place-content:space-evenly}.place-content-start{place-content:start}.place-content-stretch{place-content:stretch}.place-items-baseline{place-items:baseline}.place-items-center{place-items:center}.place-items-end{place-items:end}.place-items-start{place-items:start}.place-items-stretch{place-items:stretch stretch}.content-around{align-content:space-around}.content-between{align-content:space-between}.content-center{align-content:center}.content-end{align-content:flex-end}.content-evenly{align-content:space-evenly}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-evenly{justify-content:space-evenly}.justify-start{justify-content:flex-start}.\!gap-0{gap:0!important}.\!gap-1{gap:var(--spacing)!important}.\!gap-2{gap:calc(var(--spacing) * 2)!important}.\!gap-3{gap:calc(var(--spacing) * 3)!important}.\!gap-4{gap:calc(var(--spacing) * 4)!important}.\!gap-5{gap:calc(var(--spacing) * 5)!important}.\!gap-6{gap:calc(var(--spacing) * 6)!important}.\!gap-8{gap:calc(var(--spacing) * 8)!important}.\!gap-10{gap:calc(var(--spacing) * 10)!important}.\!gap-12{gap:calc(var(--spacing) * 12)!important}.\!gap-16{gap:calc(var(--spacing) * 16)!important}.gap-0{gap:0}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-10{gap:calc(var(--spacing) * 10)}.gap-12{gap:calc(var(--spacing) * 12)}.gap-16{gap:calc(var(--spacing) * 16)}:where(.-space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * -1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-y-reverse)))}:where(.-space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * -2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-0{column-gap:0}.gap-x-1{column-gap:var(--spacing)}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-x-6{column-gap:calc(var(--spacing) * 6)}.gap-x-8{column-gap:calc(var(--spacing) * 8)}.gap-x-10{column-gap:calc(var(--spacing) * 10)}.gap-x-12{column-gap:calc(var(--spacing) * 12)}:where(.-space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -1) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -1) * calc(1 - var(--tw-space-x-reverse)))}:where(.-space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * -2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * -2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline:0}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-5>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 5) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.gap-y-0{row-gap:0}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-3{row-gap:calc(var(--spacing) * 3)}.gap-y-4{row-gap:calc(var(--spacing) * 4)}.gap-y-5{row-gap:calc(var(--spacing) * 5)}.gap-y-6{row-gap:calc(var(--spacing) * 6)}.gap-y-8{row-gap:calc(var(--spacing) * 8)}.gap-y-10{row-gap:calc(var(--spacing) * 10)}.gap-y-12{row-gap:calc(var(--spacing) * 12)}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-0>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(0px * var(--tw-divide-x-reverse));border-inline-end-width:calc(0px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-2>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(2px * var(--tw-divide-x-reverse));border-inline-end-width:calc(2px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-4>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(4px * var(--tw-divide-x-reverse));border-inline-end-width:calc(4px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-x-8>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(8px * var(--tw-divide-x-reverse));border-inline-end-width:calc(8px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-0>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(0px * var(--tw-divide-y-reverse));border-bottom-width:calc(0px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-2>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(2px * var(--tw-divide-y-reverse));border-bottom-width:calc(2px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-4>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(4px * var(--tw-divide-y-reverse));border-bottom-width:calc(4px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-8>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(8px * var(--tw-divide-y-reverse));border-bottom-width:calc(8px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}:where(.divide-dashed>:not(:last-child)){--tw-border-style:dashed;border-style:dashed}:where(.divide-dotted>:not(:last-child)){--tw-border-style:dotted;border-style:dotted}:where(.divide-double>:not(:last-child)){--tw-border-style:double;border-style:double}:where(.divide-none>:not(:last-child)){--tw-border-style:none;border-style:none}:where(.divide-solid>:not(:last-child)){--tw-border-style:solid;border-style:solid}:where(.divide-black>:not(:last-child)){border-color:var(--color-black)}:where(.divide-current>:not(:last-child)){border-color:currentColor}:where(.divide-gray-50>:not(:last-child)){border-color:var(--color-gray-50)}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}:where(.divide-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}:where(.divide-gray-400>:not(:last-child)){border-color:var(--color-gray-400)}:where(.divide-gray-500>:not(:last-child)){border-color:var(--color-gray-500)}:where(.divide-gray-600>:not(:last-child)){border-color:var(--color-gray-600)}:where(.divide-gray-700>:not(:last-child)){border-color:var(--color-gray-700)}:where(.divide-gray-800>:not(:last-child)){border-color:var(--color-gray-800)}:where(.divide-gray-900>:not(:last-child)){border-color:var(--color-gray-900)}:where(.divide-inherit>:not(:last-child)){border-color:inherit}:where(.divide-purple-100>:not(:last-child)){border-color:var(--color-purple-100)}:where(.divide-slate-50>:not(:last-child)){border-color:var(--color-slate-50)}:where(.divide-slate-100>:not(:last-child)){border-color:var(--color-slate-100)}:where(.divide-slate-200>:not(:last-child)){border-color:var(--color-slate-200)}:where(.divide-slate-300>:not(:last-child)){border-color:var(--color-slate-300)}:where(.divide-slate-400>:not(:last-child)){border-color:var(--color-slate-400)}:where(.divide-slate-500>:not(:last-child)){border-color:var(--color-slate-500)}:where(.divide-slate-600>:not(:last-child)){border-color:var(--color-slate-600)}:where(.divide-slate-700>:not(:last-child)){border-color:var(--color-slate-700)}:where(.divide-slate-800>:not(:last-child)){border-color:var(--color-slate-800)}:where(.divide-slate-900>:not(:last-child)){border-color:var(--color-slate-900)}:where(.divide-transparent>:not(:last-child)){border-color:#0000}:where(.divide-white>:not(:last-child)){border-color:var(--color-white)}.place-self-auto{place-self:auto}.place-self-center{place-self:center}.place-self-end{place-self:end}.place-self-start{place-self:start}.place-self-stretch{place-self:stretch stretch}.self-auto{align-self:auto}.self-baseline{align-self:baseline}.self-center{align-self:center}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-clip{overflow:clip}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-x-clip{overflow-x:clip}.overflow-x-hidden{overflow-x:hidden}.overflow-x-scroll{overflow-x:scroll}.overflow-x-visible{overflow-x:visible}.overflow-y-auto{overflow-y:auto}.overflow-y-clip{overflow-y:clip}.overflow-y-hidden{overflow-y:hidden}.overflow-y-scroll{overflow-y:scroll}.overflow-y-visible{overflow-y:visible}.overscroll-auto{overscroll-behavior:auto}.overscroll-contain{overscroll-behavior:contain}.overscroll-none{overscroll-behavior:none}.overscroll-x-auto{overscroll-behavior-x:auto}.overscroll-x-contain{overscroll-behavior-x:contain}.overscroll-x-none{overscroll-behavior-x:none}.overscroll-y-auto{overscroll-behavior-y:auto}.overscroll-y-contain{overscroll-behavior-y:contain}.overscroll-y-none{overscroll-behavior-y:none}.scroll-auto{scroll-behavior:auto}.scroll-smooth{scroll-behavior:smooth}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-t-2xl{border-top-left-radius:var(--radius-2xl);border-top-right-radius:var(--radius-2xl)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.rounded-t-full{border-top-left-radius:3.40282e38px;border-top-right-radius:3.40282e38px}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.rounded-t-none{border-top-left-radius:0;border-top-right-radius:0}.rounded-t-sm{border-top-left-radius:var(--radius-sm);border-top-right-radius:var(--radius-sm)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-l-2xl{border-top-left-radius:var(--radius-2xl);border-bottom-left-radius:var(--radius-2xl)}.rounded-l-3xl{border-top-left-radius:var(--radius-3xl);border-bottom-left-radius:var(--radius-3xl)}.rounded-l-full{border-top-left-radius:3.40282e38px;border-bottom-left-radius:3.40282e38px}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-l-md{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-l-sm{border-top-left-radius:var(--radius-sm);border-bottom-left-radius:var(--radius-sm)}.rounded-l-xl{border-top-left-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-tl{border-top-left-radius:.25rem}.rounded-tl-2xl{border-top-left-radius:var(--radius-2xl)}.rounded-tl-3xl{border-top-left-radius:var(--radius-3xl)}.rounded-tl-full{border-top-left-radius:3.40282e38px}.rounded-tl-lg{border-top-left-radius:var(--radius-lg)}.rounded-tl-md{border-top-left-radius:var(--radius-md)}.rounded-tl-none{border-top-left-radius:0}.rounded-tl-sm{border-top-left-radius:var(--radius-sm)}.rounded-tl-xl{border-top-left-radius:var(--radius-xl)}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-r-2xl{border-top-right-radius:var(--radius-2xl);border-bottom-right-radius:var(--radius-2xl)}.rounded-r-3xl{border-top-right-radius:var(--radius-3xl);border-bottom-right-radius:var(--radius-3xl)}.rounded-r-full{border-top-right-radius:3.40282e38px;border-bottom-right-radius:3.40282e38px}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-r-md{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.rounded-r-sm{border-top-right-radius:var(--radius-sm);border-bottom-right-radius:var(--radius-sm)}.rounded-r-xl{border-top-right-radius:var(--radius-xl);border-bottom-right-radius:var(--radius-xl)}.rounded-tr{border-top-right-radius:.25rem}.rounded-tr-2xl{border-top-right-radius:var(--radius-2xl)}.rounded-tr-3xl{border-top-right-radius:var(--radius-3xl)}.rounded-tr-full{border-top-right-radius:3.40282e38px}.rounded-tr-lg{border-top-right-radius:var(--radius-lg)}.rounded-tr-md{border-top-right-radius:var(--radius-md)}.rounded-tr-none{border-top-right-radius:0}.rounded-tr-sm{border-top-right-radius:var(--radius-sm)}.rounded-tr-xl{border-top-right-radius:var(--radius-xl)}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-b-2xl{border-bottom-right-radius:var(--radius-2xl);border-bottom-left-radius:var(--radius-2xl)}.rounded-b-3xl{border-bottom-right-radius:var(--radius-3xl);border-bottom-left-radius:var(--radius-3xl)}.rounded-b-full{border-bottom-right-radius:3.40282e38px;border-bottom-left-radius:3.40282e38px}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.rounded-b-sm{border-bottom-right-radius:var(--radius-sm);border-bottom-left-radius:var(--radius-sm)}.rounded-b-xl{border-bottom-right-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-br{border-bottom-right-radius:.25rem}.rounded-br-2xl{border-bottom-right-radius:var(--radius-2xl)}.rounded-br-3xl{border-bottom-right-radius:var(--radius-3xl)}.rounded-br-full{border-bottom-right-radius:3.40282e38px}.rounded-br-lg{border-bottom-right-radius:var(--radius-lg)}.rounded-br-md{border-bottom-right-radius:var(--radius-md)}.rounded-br-none{border-bottom-right-radius:0}.rounded-br-sm{border-bottom-right-radius:var(--radius-sm)}.rounded-br-xl{border-bottom-right-radius:var(--radius-xl)}.rounded-bl{border-bottom-left-radius:.25rem}.rounded-bl-2xl{border-bottom-left-radius:var(--radius-2xl)}.rounded-bl-3xl{border-bottom-left-radius:var(--radius-3xl)}.rounded-bl-full{border-bottom-left-radius:3.40282e38px}.rounded-bl-lg{border-bottom-left-radius:var(--radius-lg)}.rounded-bl-md{border-bottom-left-radius:var(--radius-md)}.rounded-bl-none{border-bottom-left-radius:0}.rounded-bl-sm{border-bottom-left-radius:var(--radius-sm)}.rounded-bl-xl{border-bottom-left-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-3{border-style:var(--tw-border-style);border-width:3px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-8{border-style:var(--tw-border-style);border-width:8px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.border-x-2{border-inline-style:var(--tw-border-style);border-inline-width:2px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-y-0{border-block-style:var(--tw-border-style);border-block-width:0}.border-y-2{border-block-style:var(--tw-border-style);border-block-width:2px}.\!border-t-0{border-top-style:var(--tw-border-style)!important;border-top-width:0!important}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.border-t-2{border-top-style:var(--tw-border-style);border-top-width:2px}.border-t-4{border-top-style:var(--tw-border-style);border-top-width:4px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-0{border-right-style:var(--tw-border-style);border-right-width:0}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-r-4{border-right-style:var(--tw-border-style);border-right-width:4px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-b-4{border-bottom-style:var(--tw-border-style);border-bottom-width:4px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-dotted{--tw-border-style:dotted;border-style:dotted}.border-double{--tw-border-style:double;border-style:double}.border-hidden{--tw-border-style:hidden;border-style:hidden}.border-none{--tw-border-style:none;border-style:none}.border-solid{--tw-border-style:solid;border-style:solid}.border-amber-50{border-color:var(--color-amber-50)}.border-amber-100{border-color:var(--color-amber-100)}.border-amber-200{border-color:var(--color-amber-200)}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-amber-500{border-color:var(--color-amber-500)}.border-amber-600{border-color:var(--color-amber-600)}.border-amber-700{border-color:var(--color-amber-700)}.border-amber-800{border-color:var(--color-amber-800)}.border-amber-900{border-color:var(--color-amber-900)}.border-amber-950{border-color:var(--color-amber-950)}.border-black{border-color:var(--color-black)}.border-blue-50{border-color:var(--color-blue-50)}.border-blue-100{border-color:var(--color-blue-100)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-300{border-color:var(--color-blue-300)}.border-blue-400{border-color:var(--color-blue-400)}.border-blue-500{border-color:var(--color-blue-500)}.border-blue-600{border-color:var(--color-blue-600)}.border-blue-700{border-color:var(--color-blue-700)}.border-blue-800{border-color:var(--color-blue-800)}.border-blue-900{border-color:var(--color-blue-900)}.border-blue-950{border-color:var(--color-blue-950)}.border-current{border-color:currentColor}.border-cyan-50{border-color:var(--color-cyan-50)}.border-cyan-100{border-color:var(--color-cyan-100)}.border-cyan-200{border-color:var(--color-cyan-200)}.border-cyan-300{border-color:var(--color-cyan-300)}.border-cyan-400{border-color:var(--color-cyan-400)}.border-cyan-500{border-color:var(--color-cyan-500)}.border-cyan-600{border-color:var(--color-cyan-600)}.border-cyan-700{border-color:var(--color-cyan-700)}.border-cyan-800{border-color:var(--color-cyan-800)}.border-cyan-900{border-color:var(--color-cyan-900)}.border-cyan-950{border-color:var(--color-cyan-950)}.border-emerald-50{border-color:var(--color-emerald-50)}.border-emerald-100{border-color:var(--color-emerald-100)}.border-emerald-200{border-color:var(--color-emerald-200)}.border-emerald-300{border-color:var(--color-emerald-300)}.border-emerald-400{border-color:var(--color-emerald-400)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-emerald-600{border-color:var(--color-emerald-600)}.border-emerald-700{border-color:var(--color-emerald-700)}.border-emerald-800{border-color:var(--color-emerald-800)}.border-emerald-900{border-color:var(--color-emerald-900)}.border-emerald-950{border-color:var(--color-emerald-950)}.border-fuchsia-50{border-color:var(--color-fuchsia-50)}.border-fuchsia-100{border-color:var(--color-fuchsia-100)}.border-fuchsia-200{border-color:var(--color-fuchsia-200)}.border-fuchsia-300{border-color:var(--color-fuchsia-300)}.border-fuchsia-400{border-color:var(--color-fuchsia-400)}.border-fuchsia-500{border-color:var(--color-fuchsia-500)}.border-fuchsia-600{border-color:var(--color-fuchsia-600)}.border-fuchsia-700{border-color:var(--color-fuchsia-700)}.border-fuchsia-800{border-color:var(--color-fuchsia-800)}.border-fuchsia-900{border-color:var(--color-fuchsia-900)}.border-fuchsia-950{border-color:var(--color-fuchsia-950)}.border-gray-50{border-color:var(--color-gray-50)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400{border-color:var(--color-gray-400)}.border-gray-500{border-color:var(--color-gray-500)}.border-gray-600{border-color:var(--color-gray-600)}.border-gray-700{border-color:var(--color-gray-700)}.border-gray-800{border-color:var(--color-gray-800)}.border-gray-900{border-color:var(--color-gray-900)}.border-gray-950{border-color:var(--color-gray-950)}.border-green-50{border-color:var(--color-green-50)}.border-green-100{border-color:var(--color-green-100)}.border-green-200{border-color:var(--color-green-200)}.border-green-300{border-color:var(--color-green-300)}.border-green-400{border-color:var(--color-green-400)}.border-green-500{border-color:var(--color-green-500)}.border-green-600{border-color:var(--color-green-600)}.border-green-700{border-color:var(--color-green-700)}.border-green-800{border-color:var(--color-green-800)}.border-green-900{border-color:var(--color-green-900)}.border-green-950{border-color:var(--color-green-950)}.border-indigo-50{border-color:var(--color-indigo-50)}.border-indigo-100{border-color:var(--color-indigo-100)}.border-indigo-200{border-color:var(--color-indigo-200)}.border-indigo-300{border-color:var(--color-indigo-300)}.border-indigo-400{border-color:var(--color-indigo-400)}.border-indigo-500{border-color:var(--color-indigo-500)}.border-indigo-600{border-color:var(--color-indigo-600)}.border-indigo-700{border-color:var(--color-indigo-700)}.border-indigo-800{border-color:var(--color-indigo-800)}.border-indigo-900{border-color:var(--color-indigo-900)}.border-indigo-950{border-color:var(--color-indigo-950)}.border-inherit{border-color:inherit}.border-lime-50{border-color:var(--color-lime-50)}.border-lime-100{border-color:var(--color-lime-100)}.border-lime-200{border-color:var(--color-lime-200)}.border-lime-300{border-color:var(--color-lime-300)}.border-lime-400{border-color:var(--color-lime-400)}.border-lime-500{border-color:var(--color-lime-500)}.border-lime-600{border-color:var(--color-lime-600)}.border-lime-700{border-color:var(--color-lime-700)}.border-lime-800{border-color:var(--color-lime-800)}.border-lime-900{border-color:var(--color-lime-900)}.border-lime-950{border-color:var(--color-lime-950)}.border-neutral-50{border-color:var(--color-neutral-50)}.border-neutral-100{border-color:var(--color-neutral-100)}.border-neutral-200{border-color:var(--color-neutral-200)}.border-neutral-300{border-color:var(--color-neutral-300)}.border-neutral-400{border-color:var(--color-neutral-400)}.border-neutral-500{border-color:var(--color-neutral-500)}.border-neutral-600{border-color:var(--color-neutral-600)}.border-neutral-700{border-color:var(--color-neutral-700)}.border-neutral-800{border-color:var(--color-neutral-800)}.border-neutral-900{border-color:var(--color-neutral-900)}.border-neutral-950{border-color:var(--color-neutral-950)}.border-orange-50{border-color:var(--color-orange-50)}.border-orange-100{border-color:var(--color-orange-100)}.border-orange-200{border-color:var(--color-orange-200)}.border-orange-300{border-color:var(--color-orange-300)}.border-orange-400{border-color:var(--color-orange-400)}.border-orange-500{border-color:var(--color-orange-500)}.border-orange-600{border-color:var(--color-orange-600)}.border-orange-700{border-color:var(--color-orange-700)}.border-orange-800{border-color:var(--color-orange-800)}.border-orange-900{border-color:var(--color-orange-900)}.border-orange-950{border-color:var(--color-orange-950)}.border-pink-50{border-color:var(--color-pink-50)}.border-pink-100{border-color:var(--color-pink-100)}.border-pink-200{border-color:var(--color-pink-200)}.border-pink-300{border-color:var(--color-pink-300)}.border-pink-400{border-color:var(--color-pink-400)}.border-pink-500{border-color:var(--color-pink-500)}.border-pink-600{border-color:var(--color-pink-600)}.border-pink-700{border-color:var(--color-pink-700)}.border-pink-800{border-color:var(--color-pink-800)}.border-pink-900{border-color:var(--color-pink-900)}.border-pink-950{border-color:var(--color-pink-950)}.border-purple-50{border-color:var(--color-purple-50)}.border-purple-100{border-color:var(--color-purple-100)}.border-purple-200{border-color:var(--color-purple-200)}.border-purple-300{border-color:var(--color-purple-300)}.border-purple-400{border-color:var(--color-purple-400)}.border-purple-500{border-color:var(--color-purple-500)}.border-purple-600{border-color:var(--color-purple-600)}.border-purple-700{border-color:var(--color-purple-700)}.border-purple-800{border-color:var(--color-purple-800)}.border-purple-900{border-color:var(--color-purple-900)}.border-purple-950{border-color:var(--color-purple-950)}.border-red-50{border-color:var(--color-red-50)}.border-red-100{border-color:var(--color-red-100)}.border-red-200{border-color:var(--color-red-200)}.border-red-300{border-color:var(--color-red-300)}.border-red-400{border-color:var(--color-red-400)}.border-red-500{border-color:var(--color-red-500)}.border-red-600{border-color:var(--color-red-600)}.border-red-700{border-color:var(--color-red-700)}.border-red-800{border-color:var(--color-red-800)}.border-red-900{border-color:var(--color-red-900)}.border-red-950{border-color:var(--color-red-950)}.border-rose-50{border-color:var(--color-rose-50)}.border-rose-100{border-color:var(--color-rose-100)}.border-rose-200{border-color:var(--color-rose-200)}.border-rose-300{border-color:var(--color-rose-300)}.border-rose-400{border-color:var(--color-rose-400)}.border-rose-500{border-color:var(--color-rose-500)}.border-rose-600{border-color:var(--color-rose-600)}.border-rose-700{border-color:var(--color-rose-700)}.border-rose-800{border-color:var(--color-rose-800)}.border-rose-900{border-color:var(--color-rose-900)}.border-rose-950{border-color:var(--color-rose-950)}.border-sky-50{border-color:var(--color-sky-50)}.border-sky-100{border-color:var(--color-sky-100)}.border-sky-200{border-color:var(--color-sky-200)}.border-sky-300{border-color:var(--color-sky-300)}.border-sky-400{border-color:var(--color-sky-400)}.border-sky-500{border-color:var(--color-sky-500)}.border-sky-600{border-color:var(--color-sky-600)}.border-sky-700{border-color:var(--color-sky-700)}.border-sky-800{border-color:var(--color-sky-800)}.border-sky-900{border-color:var(--color-sky-900)}.border-sky-950{border-color:var(--color-sky-950)}.border-slate-50{border-color:var(--color-slate-50)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-slate-400{border-color:var(--color-slate-400)}.border-slate-500{border-color:var(--color-slate-500)}.border-slate-600{border-color:var(--color-slate-600)}.border-slate-700{border-color:var(--color-slate-700)}.border-slate-800{border-color:var(--color-slate-800)}.border-slate-900{border-color:var(--color-slate-900)}.border-slate-950{border-color:var(--color-slate-950)}.border-stone-50{border-color:var(--color-stone-50)}.border-stone-100{border-color:var(--color-stone-100)}.border-stone-200{border-color:var(--color-stone-200)}.border-stone-300{border-color:var(--color-stone-300)}.border-stone-400{border-color:var(--color-stone-400)}.border-stone-500{border-color:var(--color-stone-500)}.border-stone-600{border-color:var(--color-stone-600)}.border-stone-700{border-color:var(--color-stone-700)}.border-stone-800{border-color:var(--color-stone-800)}.border-stone-900{border-color:var(--color-stone-900)}.border-stone-950{border-color:var(--color-stone-950)}.border-teal-50{border-color:var(--color-teal-50)}.border-teal-100{border-color:var(--color-teal-100)}.border-teal-200{border-color:var(--color-teal-200)}.border-teal-300{border-color:var(--color-teal-300)}.border-teal-400{border-color:var(--color-teal-400)}.border-teal-500{border-color:var(--color-teal-500)}.border-teal-600{border-color:var(--color-teal-600)}.border-teal-700{border-color:var(--color-teal-700)}.border-teal-800{border-color:var(--color-teal-800)}.border-teal-900{border-color:var(--color-teal-900)}.border-teal-950{border-color:var(--color-teal-950)}.border-transparent{border-color:#0000}.border-violet-50{border-color:var(--color-violet-50)}.border-violet-100{border-color:var(--color-violet-100)}.border-violet-200{border-color:var(--color-violet-200)}.border-violet-300{border-color:var(--color-violet-300)}.border-violet-400{border-color:var(--color-violet-400)}.border-violet-500{border-color:var(--color-violet-500)}.border-violet-600{border-color:var(--color-violet-600)}.border-violet-700{border-color:var(--color-violet-700)}.border-violet-800{border-color:var(--color-violet-800)}.border-violet-900{border-color:var(--color-violet-900)}.border-violet-950{border-color:var(--color-violet-950)}.border-white{border-color:var(--color-white)}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.border-white\/30{border-color:color-mix(in oklab,var(--color-white) 30%,transparent)}}.border-yellow-50{border-color:var(--color-yellow-50)}.border-yellow-100{border-color:var(--color-yellow-100)}.border-yellow-200{border-color:var(--color-yellow-200)}.border-yellow-300{border-color:var(--color-yellow-300)}.border-yellow-400{border-color:var(--color-yellow-400)}.border-yellow-500{border-color:var(--color-yellow-500)}.border-yellow-600{border-color:var(--color-yellow-600)}.border-yellow-700{border-color:var(--color-yellow-700)}.border-yellow-800{border-color:var(--color-yellow-800)}.border-yellow-900{border-color:var(--color-yellow-900)}.border-yellow-950{border-color:var(--color-yellow-950)}.border-zinc-50{border-color:var(--color-zinc-50)}.border-zinc-100{border-color:var(--color-zinc-100)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-400{border-color:var(--color-zinc-400)}.border-zinc-500{border-color:var(--color-zinc-500)}.border-zinc-600{border-color:var(--color-zinc-600)}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-800{border-color:var(--color-zinc-800)}.border-zinc-900{border-color:var(--color-zinc-900)}.border-zinc-950{border-color:var(--color-zinc-950)}.border-t-current{border-top-color:currentColor}.border-t-white{border-top-color:var(--color-white)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-200{background-color:var(--color-amber-200)}.bg-amber-300{background-color:var(--color-amber-300)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-600{background-color:var(--color-amber-600)}.bg-amber-700{background-color:var(--color-amber-700)}.bg-amber-800{background-color:var(--color-amber-800)}.bg-amber-900{background-color:var(--color-amber-900)}.bg-amber-950{background-color:var(--color-amber-950)}.bg-black{background-color:var(--color-black)}.bg-black\/0{background-color:#0000}@supports (color:color-mix(in lab,red,red)){.bg-black\/0{background-color:color-mix(in oklab,var(--color-black) 0%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-50\/70{background-color:#eff6ffb3}@supports (color:color-mix(in lab,red,red)){.bg-blue-50\/70{background-color:color-mix(in oklab,var(--color-blue-50) 70%,transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-200{background-color:var(--color-blue-200)}.bg-blue-300{background-color:var(--color-blue-300)}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-blue-700{background-color:var(--color-blue-700)}.bg-blue-800{background-color:var(--color-blue-800)}.bg-blue-900{background-color:var(--color-blue-900)}.bg-blue-950{background-color:var(--color-blue-950)}.bg-current{background-color:currentColor}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-cyan-100{background-color:var(--color-cyan-100)}.bg-cyan-200{background-color:var(--color-cyan-200)}.bg-cyan-300{background-color:var(--color-cyan-300)}.bg-cyan-400{background-color:var(--color-cyan-400)}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-600{background-color:var(--color-cyan-600)}.bg-cyan-700{background-color:var(--color-cyan-700)}.bg-cyan-800{background-color:var(--color-cyan-800)}.bg-cyan-900{background-color:var(--color-cyan-900)}.bg-cyan-950{background-color:var(--color-cyan-950)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-200{background-color:var(--color-emerald-200)}.bg-emerald-300{background-color:var(--color-emerald-300)}.bg-emerald-400{background-color:var(--color-emerald-400)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-600{background-color:var(--color-emerald-600)}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-emerald-800{background-color:var(--color-emerald-800)}.bg-emerald-900{background-color:var(--color-emerald-900)}.bg-emerald-950{background-color:var(--color-emerald-950)}.bg-fuchsia-50{background-color:var(--color-fuchsia-50)}.bg-fuchsia-100{background-color:var(--color-fuchsia-100)}.bg-fuchsia-200{background-color:var(--color-fuchsia-200)}.bg-fuchsia-300{background-color:var(--color-fuchsia-300)}.bg-fuchsia-400{background-color:var(--color-fuchsia-400)}.bg-fuchsia-500{background-color:var(--color-fuchsia-500)}.bg-fuchsia-600{background-color:var(--color-fuchsia-600)}.bg-fuchsia-700{background-color:var(--color-fuchsia-700)}.bg-fuchsia-800{background-color:var(--color-fuchsia-800)}.bg-fuchsia-900{background-color:var(--color-fuchsia-900)}.bg-fuchsia-950{background-color:var(--color-fuchsia-950)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-100\/60{background-color:#f3f4f699}@supports (color:color-mix(in lab,red,red)){.bg-gray-100\/60{background-color:color-mix(in oklab,var(--color-gray-100) 60%,transparent)}}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-300{background-color:var(--color-gray-300)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500{background-color:var(--color-gray-500)}.bg-gray-600{background-color:var(--color-gray-600)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-800\/50{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.bg-gray-800\/50{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.bg-gray-900{background-color:var(--color-gray-900)}.bg-gray-950{background-color:var(--color-gray-950)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-200{background-color:var(--color-green-200)}.bg-green-300{background-color:var(--color-green-300)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-green-900{background-color:var(--color-green-900)}.bg-green-950{background-color:var(--color-green-950)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-indigo-200{background-color:var(--color-indigo-200)}.bg-indigo-300{background-color:var(--color-indigo-300)}.bg-indigo-400{background-color:var(--color-indigo-400)}.bg-indigo-500{background-color:var(--color-indigo-500)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-indigo-700{background-color:var(--color-indigo-700)}.bg-indigo-800{background-color:var(--color-indigo-800)}.bg-indigo-900{background-color:var(--color-indigo-900)}.bg-indigo-950{background-color:var(--color-indigo-950)}.bg-inherit{background-color:inherit}.bg-lime-50{background-color:var(--color-lime-50)}.bg-lime-100{background-color:var(--color-lime-100)}.bg-lime-200{background-color:var(--color-lime-200)}.bg-lime-300{background-color:var(--color-lime-300)}.bg-lime-400{background-color:var(--color-lime-400)}.bg-lime-500{background-color:var(--color-lime-500)}.bg-lime-600{background-color:var(--color-lime-600)}.bg-lime-700{background-color:var(--color-lime-700)}.bg-lime-800{background-color:var(--color-lime-800)}.bg-lime-900{background-color:var(--color-lime-900)}.bg-lime-950{background-color:var(--color-lime-950)}.bg-neutral-50{background-color:var(--color-neutral-50)}.bg-neutral-100{background-color:var(--color-neutral-100)}.bg-neutral-200{background-color:var(--color-neutral-200)}.bg-neutral-300{background-color:var(--color-neutral-300)}.bg-neutral-400{background-color:var(--color-neutral-400)}.bg-neutral-500{background-color:var(--color-neutral-500)}.bg-neutral-600{background-color:var(--color-neutral-600)}.bg-neutral-700{background-color:var(--color-neutral-700)}.bg-neutral-800{background-color:var(--color-neutral-800)}.bg-neutral-900{background-color:var(--color-neutral-900)}.bg-neutral-950{background-color:var(--color-neutral-950)}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-200{background-color:var(--color-orange-200)}.bg-orange-300{background-color:var(--color-orange-300)}.bg-orange-400{background-color:var(--color-orange-400)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-600{background-color:var(--color-orange-600)}.bg-orange-700{background-color:var(--color-orange-700)}.bg-orange-800{background-color:var(--color-orange-800)}.bg-orange-900{background-color:var(--color-orange-900)}.bg-orange-950{background-color:var(--color-orange-950)}.bg-pink-50{background-color:var(--color-pink-50)}.bg-pink-100{background-color:var(--color-pink-100)}.bg-pink-200{background-color:var(--color-pink-200)}.bg-pink-300{background-color:var(--color-pink-300)}.bg-pink-400{background-color:var(--color-pink-400)}.bg-pink-500{background-color:var(--color-pink-500)}.bg-pink-600{background-color:var(--color-pink-600)}.bg-pink-700{background-color:var(--color-pink-700)}.bg-pink-800{background-color:var(--color-pink-800)}.bg-pink-900{background-color:var(--color-pink-900)}.bg-pink-950{background-color:var(--color-pink-950)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-purple-200{background-color:var(--color-purple-200)}.bg-purple-300{background-color:var(--color-purple-300)}.bg-purple-400{background-color:var(--color-purple-400)}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-600{background-color:var(--color-purple-600)}.bg-purple-700{background-color:var(--color-purple-700)}.bg-purple-800{background-color:var(--color-purple-800)}.bg-purple-900{background-color:var(--color-purple-900)}.bg-purple-950{background-color:var(--color-purple-950)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-200{background-color:var(--color-red-200)}.bg-red-300{background-color:var(--color-red-300)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/50{background-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/50{background-color:color-mix(in oklab,var(--color-red-500) 50%,transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-red-700{background-color:var(--color-red-700)}.bg-red-800{background-color:var(--color-red-800)}.bg-red-900{background-color:var(--color-red-900)}.bg-red-950{background-color:var(--color-red-950)}.bg-rose-50{background-color:var(--color-rose-50)}.bg-rose-100{background-color:var(--color-rose-100)}.bg-rose-200{background-color:var(--color-rose-200)}.bg-rose-300{background-color:var(--color-rose-300)}.bg-rose-400{background-color:var(--color-rose-400)}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-600{background-color:var(--color-rose-600)}.bg-rose-700{background-color:var(--color-rose-700)}.bg-rose-800{background-color:var(--color-rose-800)}.bg-rose-900{background-color:var(--color-rose-900)}.bg-rose-950{background-color:var(--color-rose-950)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-sky-200{background-color:var(--color-sky-200)}.bg-sky-300{background-color:var(--color-sky-300)}.bg-sky-400{background-color:var(--color-sky-400)}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-600{background-color:var(--color-sky-600)}.bg-sky-700{background-color:var(--color-sky-700)}.bg-sky-800{background-color:var(--color-sky-800)}.bg-sky-900{background-color:var(--color-sky-900)}.bg-sky-950{background-color:var(--color-sky-950)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-400{background-color:var(--color-slate-400)}.bg-slate-500{background-color:var(--color-slate-500)}.bg-slate-600{background-color:var(--color-slate-600)}.bg-slate-700{background-color:var(--color-slate-700)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-stone-50{background-color:var(--color-stone-50)}.bg-stone-100{background-color:var(--color-stone-100)}.bg-stone-200{background-color:var(--color-stone-200)}.bg-stone-300{background-color:var(--color-stone-300)}.bg-stone-400{background-color:var(--color-stone-400)}.bg-stone-500{background-color:var(--color-stone-500)}.bg-stone-600{background-color:var(--color-stone-600)}.bg-stone-700{background-color:var(--color-stone-700)}.bg-stone-800{background-color:var(--color-stone-800)}.bg-stone-900{background-color:var(--color-stone-900)}.bg-stone-950{background-color:var(--color-stone-950)}.bg-teal-50{background-color:var(--color-teal-50)}.bg-teal-100{background-color:var(--color-teal-100)}.bg-teal-200{background-color:var(--color-teal-200)}.bg-teal-300{background-color:var(--color-teal-300)}.bg-teal-400{background-color:var(--color-teal-400)}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-600{background-color:var(--color-teal-600)}.bg-teal-700{background-color:var(--color-teal-700)}.bg-teal-800{background-color:var(--color-teal-800)}.bg-teal-900{background-color:var(--color-teal-900)}.bg-teal-950{background-color:var(--color-teal-950)}.bg-transparent{background-color:#0000}.bg-violet-50{background-color:var(--color-violet-50)}.bg-violet-100{background-color:var(--color-violet-100)}.bg-violet-200{background-color:var(--color-violet-200)}.bg-violet-300{background-color:var(--color-violet-300)}.bg-violet-400{background-color:var(--color-violet-400)}.bg-violet-500{background-color:var(--color-violet-500)}.bg-violet-600{background-color:var(--color-violet-600)}.bg-violet-700{background-color:var(--color-violet-700)}.bg-violet-800{background-color:var(--color-violet-800)}.bg-violet-900{background-color:var(--color-violet-900)}.bg-violet-950{background-color:var(--color-violet-950)}.bg-white{background-color:var(--color-white)}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-200{background-color:var(--color-yellow-200)}.bg-yellow-300{background-color:var(--color-yellow-300)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-600{background-color:var(--color-yellow-600)}.bg-yellow-700{background-color:var(--color-yellow-700)}.bg-yellow-800{background-color:var(--color-yellow-800)}.bg-yellow-900{background-color:var(--color-yellow-900)}.bg-yellow-950{background-color:var(--color-yellow-950)}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.bg-zinc-300{background-color:var(--color-zinc-300)}.bg-zinc-400{background-color:var(--color-zinc-400)}.bg-zinc-500{background-color:var(--color-zinc-500)}.bg-zinc-600{background-color:var(--color-zinc-600)}.bg-zinc-700{background-color:var(--color-zinc-700)}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-bl{--tw-gradient-position:to bottom left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-l{--tw-gradient-position:to left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-tl{--tw-gradient-position:to top left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-tr{--tw-gradient-position:to top right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-none{background-image:none}.from-black{--tw-gradient-from:var(--color-black);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-50{--tw-gradient-from:var(--color-blue-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-100{--tw-gradient-from:var(--color-blue-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-200{--tw-gradient-from:var(--color-blue-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-300{--tw-gradient-from:var(--color-blue-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-400{--tw-gradient-from:var(--color-blue-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-500{--tw-gradient-from:var(--color-blue-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-600{--tw-gradient-from:var(--color-blue-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-700{--tw-gradient-from:var(--color-blue-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-800{--tw-gradient-from:var(--color-blue-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-blue-900{--tw-gradient-from:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-current{--tw-gradient-from:currentcolor;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-50{--tw-gradient-from:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-100{--tw-gradient-from:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-200{--tw-gradient-from:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-300{--tw-gradient-from:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-400{--tw-gradient-from:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-500{--tw-gradient-from:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-600{--tw-gradient-from:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-700{--tw-gradient-from:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-800{--tw-gradient-from:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-gray-900{--tw-gradient-from:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-50{--tw-gradient-from:var(--color-green-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-100{--tw-gradient-from:var(--color-green-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-200{--tw-gradient-from:var(--color-green-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-300{--tw-gradient-from:var(--color-green-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-400{--tw-gradient-from:var(--color-green-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-500{--tw-gradient-from:var(--color-green-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-600{--tw-gradient-from:var(--color-green-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-700{--tw-gradient-from:var(--color-green-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-800{--tw-gradient-from:var(--color-green-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-green-900{--tw-gradient-from:var(--color-green-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-50{--tw-gradient-from:var(--color-indigo-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-100{--tw-gradient-from:var(--color-indigo-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-200{--tw-gradient-from:var(--color-indigo-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-300{--tw-gradient-from:var(--color-indigo-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-400{--tw-gradient-from:var(--color-indigo-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-500{--tw-gradient-from:var(--color-indigo-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-600{--tw-gradient-from:var(--color-indigo-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-700{--tw-gradient-from:var(--color-indigo-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-800{--tw-gradient-from:var(--color-indigo-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-indigo-900{--tw-gradient-from:var(--color-indigo-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-inherit{--tw-gradient-from:inherit;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-50{--tw-gradient-from:var(--color-orange-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-100{--tw-gradient-from:var(--color-orange-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-200{--tw-gradient-from:var(--color-orange-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-300{--tw-gradient-from:var(--color-orange-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-400{--tw-gradient-from:var(--color-orange-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-500{--tw-gradient-from:var(--color-orange-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-600{--tw-gradient-from:var(--color-orange-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-700{--tw-gradient-from:var(--color-orange-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-800{--tw-gradient-from:var(--color-orange-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-orange-900{--tw-gradient-from:var(--color-orange-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-50{--tw-gradient-from:var(--color-pink-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-100{--tw-gradient-from:var(--color-pink-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-200{--tw-gradient-from:var(--color-pink-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-300{--tw-gradient-from:var(--color-pink-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-400{--tw-gradient-from:var(--color-pink-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-500{--tw-gradient-from:var(--color-pink-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-600{--tw-gradient-from:var(--color-pink-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-700{--tw-gradient-from:var(--color-pink-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-800{--tw-gradient-from:var(--color-pink-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-pink-900{--tw-gradient-from:var(--color-pink-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-50{--tw-gradient-from:var(--color-purple-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-100{--tw-gradient-from:var(--color-purple-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-200{--tw-gradient-from:var(--color-purple-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-300{--tw-gradient-from:var(--color-purple-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-400{--tw-gradient-from:var(--color-purple-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-500{--tw-gradient-from:var(--color-purple-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-600{--tw-gradient-from:var(--color-purple-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-700{--tw-gradient-from:var(--color-purple-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-800{--tw-gradient-from:var(--color-purple-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-purple-900{--tw-gradient-from:var(--color-purple-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-50{--tw-gradient-from:var(--color-red-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-100{--tw-gradient-from:var(--color-red-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-200{--tw-gradient-from:var(--color-red-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-300{--tw-gradient-from:var(--color-red-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-400{--tw-gradient-from:var(--color-red-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-500{--tw-gradient-from:var(--color-red-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-600{--tw-gradient-from:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-700{--tw-gradient-from:var(--color-red-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-800{--tw-gradient-from:var(--color-red-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-900{--tw-gradient-from:var(--color-red-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-50{--tw-gradient-from:var(--color-slate-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-100{--tw-gradient-from:var(--color-slate-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-200{--tw-gradient-from:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-300{--tw-gradient-from:var(--color-slate-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-400{--tw-gradient-from:var(--color-slate-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-500{--tw-gradient-from:var(--color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-600{--tw-gradient-from:var(--color-slate-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-700{--tw-gradient-from:var(--color-slate-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-800{--tw-gradient-from:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-slate-900{--tw-gradient-from:var(--color-slate-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-transparent{--tw-gradient-from:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-white{--tw-gradient-from:var(--color-white);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-50{--tw-gradient-from:var(--color-yellow-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-100{--tw-gradient-from:var(--color-yellow-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-200{--tw-gradient-from:var(--color-yellow-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-300{--tw-gradient-from:var(--color-yellow-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-400{--tw-gradient-from:var(--color-yellow-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-500{--tw-gradient-from:var(--color-yellow-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-600{--tw-gradient-from:var(--color-yellow-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-700{--tw-gradient-from:var(--color-yellow-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-800{--tw-gradient-from:var(--color-yellow-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-yellow-900{--tw-gradient-from:var(--color-yellow-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.via-black{--tw-gradient-via:var(--color-black);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-50{--tw-gradient-via:var(--color-blue-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-100{--tw-gradient-via:var(--color-blue-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-200{--tw-gradient-via:var(--color-blue-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-300{--tw-gradient-via:var(--color-blue-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-400{--tw-gradient-via:var(--color-blue-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-500{--tw-gradient-via:var(--color-blue-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-600{--tw-gradient-via:var(--color-blue-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-700{--tw-gradient-via:var(--color-blue-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-800{--tw-gradient-via:var(--color-blue-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-blue-900{--tw-gradient-via:var(--color-blue-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-current{--tw-gradient-via:currentcolor;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-50{--tw-gradient-via:var(--color-gray-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-100{--tw-gradient-via:var(--color-gray-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-200{--tw-gradient-via:var(--color-gray-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-300{--tw-gradient-via:var(--color-gray-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-400{--tw-gradient-via:var(--color-gray-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-500{--tw-gradient-via:var(--color-gray-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-600{--tw-gradient-via:var(--color-gray-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-700{--tw-gradient-via:var(--color-gray-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-800{--tw-gradient-via:var(--color-gray-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-gray-900{--tw-gradient-via:var(--color-gray-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-inherit{--tw-gradient-via:inherit;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-50{--tw-gradient-via:var(--color-slate-50);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-100{--tw-gradient-via:var(--color-slate-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-200{--tw-gradient-via:var(--color-slate-200);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-300{--tw-gradient-via:var(--color-slate-300);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-400{--tw-gradient-via:var(--color-slate-400);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-500{--tw-gradient-via:var(--color-slate-500);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-600{--tw-gradient-via:var(--color-slate-600);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-700{--tw-gradient-via:var(--color-slate-700);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-800{--tw-gradient-via:var(--color-slate-800);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-slate-900{--tw-gradient-via:var(--color-slate-900);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-transparent{--tw-gradient-via:transparent;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.via-white{--tw-gradient-via:var(--color-white);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-black{--tw-gradient-to:var(--color-black);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-50{--tw-gradient-to:var(--color-blue-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-100{--tw-gradient-to:var(--color-blue-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-200{--tw-gradient-to:var(--color-blue-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-300{--tw-gradient-to:var(--color-blue-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-400{--tw-gradient-to:var(--color-blue-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-500{--tw-gradient-to:var(--color-blue-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-600{--tw-gradient-to:var(--color-blue-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-700{--tw-gradient-to:var(--color-blue-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-800{--tw-gradient-to:var(--color-blue-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-blue-900{--tw-gradient-to:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-current{--tw-gradient-to:currentcolor;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-100{--tw-gradient-to:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-200{--tw-gradient-to:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-300{--tw-gradient-to:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-400{--tw-gradient-to:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-500{--tw-gradient-to:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-600{--tw-gradient-to:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-700{--tw-gradient-to:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-800{--tw-gradient-to:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-900{--tw-gradient-to:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-indigo-50{--tw-gradient-to:var(--color-indigo-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-inherit{--tw-gradient-to:inherit;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-50{--tw-gradient-to:var(--color-purple-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-100{--tw-gradient-to:var(--color-purple-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-200{--tw-gradient-to:var(--color-purple-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-300{--tw-gradient-to:var(--color-purple-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-400{--tw-gradient-to:var(--color-purple-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-500{--tw-gradient-to:var(--color-purple-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-600{--tw-gradient-to:var(--color-purple-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-700{--tw-gradient-to:var(--color-purple-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-800{--tw-gradient-to:var(--color-purple-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-900{--tw-gradient-to:var(--color-purple-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-50{--tw-gradient-to:var(--color-red-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-100{--tw-gradient-to:var(--color-red-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-200{--tw-gradient-to:var(--color-red-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-300{--tw-gradient-to:var(--color-red-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-400{--tw-gradient-to:var(--color-red-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-500{--tw-gradient-to:var(--color-red-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-600{--tw-gradient-to:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-700{--tw-gradient-to:var(--color-red-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-800{--tw-gradient-to:var(--color-red-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-red-900{--tw-gradient-to:var(--color-red-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-50{--tw-gradient-to:var(--color-slate-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-100{--tw-gradient-to:var(--color-slate-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-200{--tw-gradient-to:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-300{--tw-gradient-to:var(--color-slate-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-400{--tw-gradient-to:var(--color-slate-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-500{--tw-gradient-to:var(--color-slate-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-600{--tw-gradient-to:var(--color-slate-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-700{--tw-gradient-to:var(--color-slate-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-800{--tw-gradient-to:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-slate-900{--tw-gradient-to:var(--color-slate-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-white{--tw-gradient-to:var(--color-white);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.object-fill{object-fit:fill}.object-none{object-fit:none}.object-scale-down{object-fit:scale-down}.object-bottom{object-position:bottom}.object-center{object-position:center}.object-left{object-position:left}.object-left-bottom{object-position:left bottom}.object-left-top{object-position:left top}.object-right{object-position:right}.object-right-bottom{object-position:right bottom}.object-right-top{object-position:right top}.object-top{object-position:top}.\!p-0{padding:0!important}.\!p-1{padding:var(--spacing)!important}.\!p-2{padding:calc(var(--spacing) * 2)!important}.\!p-3{padding:calc(var(--spacing) * 3)!important}.\!p-4{padding:calc(var(--spacing) * 4)!important}.\!p-5{padding:calc(var(--spacing) * 5)!important}.\!p-6{padding:calc(var(--spacing) * 6)!important}.\!p-8{padding:calc(var(--spacing) * 8)!important}.\!p-10{padding:calc(var(--spacing) * 10)!important}.\!p-12{padding:calc(var(--spacing) * 12)!important}.\!p-16{padding:calc(var(--spacing) * 16)!important}.p-0{padding:0}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-10{padding:calc(var(--spacing) * 10)}.p-12{padding:calc(var(--spacing) * 12)}.p-16{padding:calc(var(--spacing) * 16)}.p-20{padding:calc(var(--spacing) * 20)}.p-24{padding:calc(var(--spacing) * 24)}.\!px-0{padding-inline:0!important}.\!px-1{padding-inline:var(--spacing)!important}.\!px-2{padding-inline:calc(var(--spacing) * 2)!important}.\!px-3{padding-inline:calc(var(--spacing) * 3)!important}.\!px-4{padding-inline:calc(var(--spacing) * 4)!important}.\!px-5{padding-inline:calc(var(--spacing) * 5)!important}.\!px-6{padding-inline:calc(var(--spacing) * 6)!important}.\!px-8{padding-inline:calc(var(--spacing) * 8)!important}.\!px-10{padding-inline:calc(var(--spacing) * 10)!important}.\!px-12{padding-inline:calc(var(--spacing) * 12)!important}.\!px-16{padding-inline:calc(var(--spacing) * 16)!important}.px-0{padding-inline:0}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.px-10{padding-inline:calc(var(--spacing) * 10)}.px-12{padding-inline:calc(var(--spacing) * 12)}.px-16{padding-inline:calc(var(--spacing) * 16)}.\!py-0{padding-block:0!important}.\!py-1{padding-block:var(--spacing)!important}.\!py-2{padding-block:calc(var(--spacing) * 2)!important}.\!py-3{padding-block:calc(var(--spacing) * 3)!important}.\!py-4{padding-block:calc(var(--spacing) * 4)!important}.\!py-5{padding-block:calc(var(--spacing) * 5)!important}.\!py-6{padding-block:calc(var(--spacing) * 6)!important}.\!py-8{padding-block:calc(var(--spacing) * 8)!important}.\!py-10{padding-block:calc(var(--spacing) * 10)!important}.\!py-12{padding-block:calc(var(--spacing) * 12)!important}.\!py-16{padding-block:calc(var(--spacing) * 16)!important}.py-0{padding-block:0}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-10{padding-block:calc(var(--spacing) * 10)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-0{padding-top:0}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pt-8{padding-top:calc(var(--spacing) * 8)}.pt-10{padding-top:calc(var(--spacing) * 10)}.pt-12{padding-top:calc(var(--spacing) * 12)}.pt-16{padding-top:calc(var(--spacing) * 16)}.pr-0{padding-right:0}.pr-1{padding-right:var(--spacing)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-5{padding-right:calc(var(--spacing) * 5)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-10{padding-right:calc(var(--spacing) * 10)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pr-16{padding-right:calc(var(--spacing) * 16)}.pb-0{padding-bottom:0}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pb-6{padding-bottom:calc(var(--spacing) * 6)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pb-10{padding-bottom:calc(var(--spacing) * 10)}.pb-12{padding-bottom:calc(var(--spacing) * 12)}.pb-16{padding-bottom:calc(var(--spacing) * 16)}.pb-24{padding-bottom:calc(var(--spacing) * 24)}.pl-0{padding-left:0}.pl-1{padding-left:var(--spacing)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-10{padding-left:calc(var(--spacing) * 10)}.pl-12{padding-left:calc(var(--spacing) * 12)}.pl-16{padding-left:calc(var(--spacing) * 16)}.\!text-center{text-align:center!important}.\!text-justify{text-align:justify!important}.\!text-left{text-align:left!important}.\!text-right{text-align:right!important}.text-center{text-align:center}.text-end{text-align:end}.text-justify{text-align:justify}.text-left{text-align:left}.text-right{text-align:right}.text-start{text-align:start}.indent-0{text-indent:0}.indent-1{text-indent:var(--spacing)}.indent-2{text-indent:calc(var(--spacing) * 2)}.indent-4{text-indent:calc(var(--spacing) * 4)}.indent-8{text-indent:calc(var(--spacing) * 8)}.align-baseline{vertical-align:baseline}.align-bottom{vertical-align:bottom}.align-middle{vertical-align:middle}.align-sub{vertical-align:sub}.align-super{vertical-align:super}.align-text-bottom{vertical-align:text-bottom}.align-text-top{vertical-align:text-top}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.\!text-2xl{font-size:var(--text-2xl)!important;line-height:var(--tw-leading,var(--text-2xl--line-height))!important}.\!text-3xl{font-size:var(--text-3xl)!important;line-height:var(--tw-leading,var(--text-3xl--line-height))!important}.\!text-4xl{font-size:var(--text-4xl)!important;line-height:var(--tw-leading,var(--text-4xl--line-height))!important}.\!text-base{font-size:var(--text-base)!important;line-height:var(--tw-leading,var(--text-base--line-height))!important}.\!text-lg{font-size:var(--text-lg)!important;line-height:var(--tw-leading,var(--text-lg--line-height))!important}.\!text-sm{font-size:var(--text-sm)!important;line-height:var(--tw-leading,var(--text-sm--line-height))!important}.\!text-xl{font-size:var(--text-xl)!important;line-height:var(--tw-leading,var(--text-xl--line-height))!important}.\!text-xs{font-size:var(--text-xs)!important;line-height:var(--tw-leading,var(--text-xs--line-height))!important}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.text-8xl{font-size:var(--text-8xl);line-height:var(--tw-leading,var(--text-8xl--line-height))}.text-9xl{font-size:var(--text-9xl);line-height:var(--tw-leading,var(--text-9xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[2rem\]{font-size:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-3{--tw-leading:calc(var(--spacing) * 3);line-height:calc(var(--spacing) * 3)}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-7{--tw-leading:calc(var(--spacing) * 7);line-height:calc(var(--spacing) * 7)}.leading-8{--tw-leading:calc(var(--spacing) * 8);line-height:calc(var(--spacing) * 8)}.leading-9{--tw-leading:calc(var(--spacing) * 9);line-height:calc(var(--spacing) * 9)}.leading-10{--tw-leading:calc(var(--spacing) * 10);line-height:calc(var(--spacing) * 10)}.leading-loose{--tw-leading:var(--leading-loose);line-height:var(--leading-loose)}.leading-none{--tw-leading:1;line-height:1}.leading-normal{--tw-leading:var(--leading-normal);line-height:var(--leading-normal)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.\!font-bold{--tw-font-weight:var(--font-weight-bold)!important;font-weight:var(--font-weight-bold)!important}.\!font-medium{--tw-font-weight:var(--font-weight-medium)!important;font-weight:var(--font-weight-medium)!important}.\!font-normal{--tw-font-weight:var(--font-weight-normal)!important;font-weight:var(--font-weight-normal)!important}.\!font-semibold{--tw-font-weight:var(--font-weight-semibold)!important;font-weight:var(--font-weight-semibold)!important}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-extralight{--tw-font-weight:var(--font-weight-extralight);font-weight:var(--font-weight-extralight)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.font-thin{--tw-font-weight:var(--font-weight-thin);font-weight:var(--font-weight-thin)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-tighter{--tw-tracking:var(--tracking-tighter);letter-spacing:var(--tracking-tighter)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-normal{overflow-wrap:normal;word-break:normal}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-break-spaces{white-space:break-spaces}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-50{color:var(--color-amber-50)}.text-amber-100{color:var(--color-amber-100)}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-black{color:var(--color-black)}.text-blue-50{color:var(--color-blue-50)}.text-blue-100{color:var(--color-blue-100)}.text-blue-200{color:var(--color-blue-200)}.text-blue-300{color:var(--color-blue-300)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-blue-950{color:var(--color-blue-950)}.text-current{color:currentColor}.text-cyan-50{color:var(--color-cyan-50)}.text-cyan-100{color:var(--color-cyan-100)}.text-cyan-200{color:var(--color-cyan-200)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-cyan-500{color:var(--color-cyan-500)}.text-cyan-600{color:var(--color-cyan-600)}.text-cyan-700{color:var(--color-cyan-700)}.text-cyan-800{color:var(--color-cyan-800)}.text-cyan-900{color:var(--color-cyan-900)}.text-cyan-950{color:var(--color-cyan-950)}.text-emerald-50{color:var(--color-emerald-50)}.text-emerald-100{color:var(--color-emerald-100)}.text-emerald-200{color:var(--color-emerald-200)}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-400{color:var(--color-emerald-400)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-emerald-950{color:var(--color-emerald-950)}.text-fuchsia-50{color:var(--color-fuchsia-50)}.text-fuchsia-100{color:var(--color-fuchsia-100)}.text-fuchsia-200{color:var(--color-fuchsia-200)}.text-fuchsia-300{color:var(--color-fuchsia-300)}.text-fuchsia-400{color:var(--color-fuchsia-400)}.text-fuchsia-500{color:var(--color-fuchsia-500)}.text-fuchsia-600{color:var(--color-fuchsia-600)}.text-fuchsia-700{color:var(--color-fuchsia-700)}.text-fuchsia-800{color:var(--color-fuchsia-800)}.text-fuchsia-900{color:var(--color-fuchsia-900)}.text-fuchsia-950{color:var(--color-fuchsia-950)}.text-gray-50{color:var(--color-gray-50)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-gray-950{color:var(--color-gray-950)}.text-green-50{color:var(--color-green-50)}.text-green-100{color:var(--color-green-100)}.text-green-200{color:var(--color-green-200)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-green-900{color:var(--color-green-900)}.text-green-950{color:var(--color-green-950)}.text-indigo-50{color:var(--color-indigo-50)}.text-indigo-100{color:var(--color-indigo-100)}.text-indigo-200{color:var(--color-indigo-200)}.text-indigo-300{color:var(--color-indigo-300)}.text-indigo-400{color:var(--color-indigo-400)}.text-indigo-500{color:var(--color-indigo-500)}.text-indigo-600{color:var(--color-indigo-600)}.text-indigo-700{color:var(--color-indigo-700)}.text-indigo-800{color:var(--color-indigo-800)}.text-indigo-900{color:var(--color-indigo-900)}.text-indigo-950{color:var(--color-indigo-950)}.text-inherit{color:inherit}.text-lime-50{color:var(--color-lime-50)}.text-lime-100{color:var(--color-lime-100)}.text-lime-200{color:var(--color-lime-200)}.text-lime-300{color:var(--color-lime-300)}.text-lime-400{color:var(--color-lime-400)}.text-lime-500{color:var(--color-lime-500)}.text-lime-600{color:var(--color-lime-600)}.text-lime-700{color:var(--color-lime-700)}.text-lime-800{color:var(--color-lime-800)}.text-lime-900{color:var(--color-lime-900)}.text-lime-950{color:var(--color-lime-950)}.text-neutral-50{color:var(--color-neutral-50)}.text-neutral-100{color:var(--color-neutral-100)}.text-neutral-200{color:var(--color-neutral-200)}.text-neutral-300{color:var(--color-neutral-300)}.text-neutral-400{color:var(--color-neutral-400)}.text-neutral-500{color:var(--color-neutral-500)}.text-neutral-600{color:var(--color-neutral-600)}.text-neutral-700{color:var(--color-neutral-700)}.text-neutral-800{color:var(--color-neutral-800)}.text-neutral-900{color:var(--color-neutral-900)}.text-neutral-950{color:var(--color-neutral-950)}.text-orange-50{color:var(--color-orange-50)}.text-orange-100{color:var(--color-orange-100)}.text-orange-200{color:var(--color-orange-200)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-orange-800{color:var(--color-orange-800)}.text-orange-900{color:var(--color-orange-900)}.text-orange-950{color:var(--color-orange-950)}.text-pink-50{color:var(--color-pink-50)}.text-pink-100{color:var(--color-pink-100)}.text-pink-200{color:var(--color-pink-200)}.text-pink-300{color:var(--color-pink-300)}.text-pink-400{color:var(--color-pink-400)}.text-pink-500{color:var(--color-pink-500)}.text-pink-600{color:var(--color-pink-600)}.text-pink-700{color:var(--color-pink-700)}.text-pink-800{color:var(--color-pink-800)}.text-pink-900{color:var(--color-pink-900)}.text-pink-950{color:var(--color-pink-950)}.text-purple-50{color:var(--color-purple-50)}.text-purple-100{color:var(--color-purple-100)}.text-purple-200{color:var(--color-purple-200)}.text-purple-300{color:var(--color-purple-300)}.text-purple-400{color:var(--color-purple-400)}.text-purple-500{color:var(--color-purple-500)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-800{color:var(--color-purple-800)}.text-purple-900{color:var(--color-purple-900)}.text-purple-950{color:var(--color-purple-950)}.text-red-50{color:var(--color-red-50)}.text-red-100{color:var(--color-red-100)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-red-900{color:var(--color-red-900)}.text-red-950{color:var(--color-red-950)}.text-rose-50{color:var(--color-rose-50)}.text-rose-100{color:var(--color-rose-100)}.text-rose-200{color:var(--color-rose-200)}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-rose-500{color:var(--color-rose-500)}.text-rose-600{color:var(--color-rose-600)}.text-rose-700{color:var(--color-rose-700)}.text-rose-800{color:var(--color-rose-800)}.text-rose-900{color:var(--color-rose-900)}.text-rose-950{color:var(--color-rose-950)}.text-sky-50{color:var(--color-sky-50)}.text-sky-100{color:var(--color-sky-100)}.text-sky-200{color:var(--color-sky-200)}.text-sky-300{color:var(--color-sky-300)}.text-sky-400{color:var(--color-sky-400)}.text-sky-500{color:var(--color-sky-500)}.text-sky-600{color:var(--color-sky-600)}.text-sky-700{color:var(--color-sky-700)}.text-sky-800{color:var(--color-sky-800)}.text-sky-900{color:var(--color-sky-900)}.text-sky-950{color:var(--color-sky-950)}.text-slate-50{color:var(--color-slate-50)}.text-slate-100{color:var(--color-slate-100)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-slate-950{color:var(--color-slate-950)}.text-stone-50{color:var(--color-stone-50)}.text-stone-100{color:var(--color-stone-100)}.text-stone-200{color:var(--color-stone-200)}.text-stone-300{color:var(--color-stone-300)}.text-stone-400{color:var(--color-stone-400)}.text-stone-500{color:var(--color-stone-500)}.text-stone-600{color:var(--color-stone-600)}.text-stone-700{color:var(--color-stone-700)}.text-stone-800{color:var(--color-stone-800)}.text-stone-900{color:var(--color-stone-900)}.text-stone-950{color:var(--color-stone-950)}.text-teal-50{color:var(--color-teal-50)}.text-teal-100{color:var(--color-teal-100)}.text-teal-200{color:var(--color-teal-200)}.text-teal-300{color:var(--color-teal-300)}.text-teal-400{color:var(--color-teal-400)}.text-teal-500{color:var(--color-teal-500)}.text-teal-600{color:var(--color-teal-600)}.text-teal-700{color:var(--color-teal-700)}.text-teal-800{color:var(--color-teal-800)}.text-teal-900{color:var(--color-teal-900)}.text-teal-950{color:var(--color-teal-950)}.text-transparent{color:#0000}.text-violet-50{color:var(--color-violet-50)}.text-violet-100{color:var(--color-violet-100)}.text-violet-200{color:var(--color-violet-200)}.text-violet-300{color:var(--color-violet-300)}.text-violet-400{color:var(--color-violet-400)}.text-violet-500{color:var(--color-violet-500)}.text-violet-600{color:var(--color-violet-600)}.text-violet-700{color:var(--color-violet-700)}.text-violet-800{color:var(--color-violet-800)}.text-violet-900{color:var(--color-violet-900)}.text-violet-950{color:var(--color-violet-950)}.text-white{color:var(--color-white)}.text-yellow-50{color:var(--color-yellow-50)}.text-yellow-100{color:var(--color-yellow-100)}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.text-yellow-800{color:var(--color-yellow-800)}.text-yellow-900{color:var(--color-yellow-900)}.text-yellow-950{color:var(--color-yellow-950)}.text-zinc-50{color:var(--color-zinc-50)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.text-zinc-950{color:var(--color-zinc-950)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.placeholder-gray-400::placeholder{color:var(--color-gray-400)}.accent-blue-500{accent-color:var(--color-blue-500)}.opacity-0{opacity:0}.opacity-5{opacity:.05}.opacity-10{opacity:.1}.opacity-15{opacity:.15}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-35{opacity:.35}.opacity-40{opacity:.4}.opacity-45{opacity:.45}.opacity-50{opacity:.5}.opacity-55{opacity:.55}.opacity-60{opacity:.6}.opacity-65{opacity:.65}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-85{opacity:.85}.opacity-90{opacity:.9}.opacity-95{opacity:.95}.opacity-100{opacity:1}.bg-blend-color{background-blend-mode:color}.bg-blend-color-burn{background-blend-mode:color-burn}.bg-blend-color-dodge{background-blend-mode:color-dodge}.bg-blend-darken{background-blend-mode:darken}.bg-blend-difference{background-blend-mode:difference}.bg-blend-exclusion{background-blend-mode:exclusion}.bg-blend-hard-light{background-blend-mode:hard-light}.bg-blend-hue{background-blend-mode:hue}.bg-blend-lighten{background-blend-mode:lighten}.bg-blend-luminosity{background-blend-mode:luminosity}.bg-blend-multiply{background-blend-mode:multiply}.bg-blend-normal{background-blend-mode:normal}.bg-blend-overlay{background-blend-mode:overlay}.bg-blend-saturation{background-blend-mode:saturation}.bg-blend-screen{background-blend-mode:screen}.bg-blend-soft-light{background-blend-mode:soft-light}.mix-blend-color{mix-blend-mode:color}.mix-blend-color-burn{mix-blend-mode:color-burn}.mix-blend-color-dodge{mix-blend-mode:color-dodge}.mix-blend-darken{mix-blend-mode:darken}.mix-blend-difference{mix-blend-mode:difference}.mix-blend-exclusion{mix-blend-mode:exclusion}.mix-blend-hard-light{mix-blend-mode:hard-light}.mix-blend-hue{mix-blend-mode:hue}.mix-blend-lighten{mix-blend-mode:lighten}.mix-blend-luminosity{mix-blend-mode:luminosity}.mix-blend-multiply{mix-blend-mode:multiply}.mix-blend-normal{mix-blend-mode:normal}.mix-blend-overlay{mix-blend-mode:overlay}.mix-blend-plus-darker{mix-blend-mode:plus-darker}.mix-blend-plus-lighter{mix-blend-mode:plus-lighter}.mix-blend-saturation{mix-blend-mode:saturation}.mix-blend-screen{mix-blend-mode:screen}.mix-blend-soft-light{mix-blend-mode:soft-light}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-4{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-8{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-purple-200{--tw-shadow-color:oklch(90.2% .063 306.703)}@supports (color:color-mix(in lab,red,red)){.shadow-purple-200{--tw-shadow-color:color-mix(in oklab, var(--color-purple-200) var(--tw-shadow-alpha), transparent)}}.ring-black{--tw-ring-color:var(--color-black)}.ring-blue-50{--tw-ring-color:var(--color-blue-50)}.ring-blue-100{--tw-ring-color:var(--color-blue-100)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.ring-blue-300{--tw-ring-color:var(--color-blue-300)}.ring-blue-400{--tw-ring-color:var(--color-blue-400)}.ring-blue-500{--tw-ring-color:var(--color-blue-500)}.ring-blue-600{--tw-ring-color:var(--color-blue-600)}.ring-blue-700{--tw-ring-color:var(--color-blue-700)}.ring-blue-800{--tw-ring-color:var(--color-blue-800)}.ring-blue-900{--tw-ring-color:var(--color-blue-900)}.ring-current{--tw-ring-color:currentcolor}.ring-gray-50{--tw-ring-color:var(--color-gray-50)}.ring-gray-100{--tw-ring-color:var(--color-gray-100)}.ring-gray-200{--tw-ring-color:var(--color-gray-200)}.ring-gray-300{--tw-ring-color:var(--color-gray-300)}.ring-gray-400{--tw-ring-color:var(--color-gray-400)}.ring-gray-500{--tw-ring-color:var(--color-gray-500)}.ring-gray-600{--tw-ring-color:var(--color-gray-600)}.ring-gray-700{--tw-ring-color:var(--color-gray-700)}.ring-gray-800{--tw-ring-color:var(--color-gray-800)}.ring-gray-900{--tw-ring-color:var(--color-gray-900)}.ring-inherit{--tw-ring-color:inherit}.ring-slate-50{--tw-ring-color:var(--color-slate-50)}.ring-slate-100{--tw-ring-color:var(--color-slate-100)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.ring-slate-300{--tw-ring-color:var(--color-slate-300)}.ring-slate-400{--tw-ring-color:var(--color-slate-400)}.ring-slate-500{--tw-ring-color:var(--color-slate-500)}.ring-slate-600{--tw-ring-color:var(--color-slate-600)}.ring-slate-700{--tw-ring-color:var(--color-slate-700)}.ring-slate-800{--tw-ring-color:var(--color-slate-800)}.ring-slate-900{--tw-ring-color:var(--color-slate-900)}.ring-transparent{--tw-ring-color:transparent}.ring-white{--tw-ring-color:var(--color-white)}.ring-offset-0{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-4{--tw-ring-offset-width:4px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-8{--tw-ring-offset-width:8px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.ring-offset-black{--tw-ring-offset-color:var(--color-black)}.ring-offset-current{--tw-ring-offset-color:currentcolor}.ring-offset-inherit{--tw-ring-offset-color:inherit}.ring-offset-transparent{--tw-ring-offset-color:transparent}.ring-offset-white{--tw-ring-offset-color:var(--color-white)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-0{outline-style:var(--tw-outline-style);outline-width:0}.outline-1{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-4{outline-style:var(--tw-outline-style);outline-width:4px}.outline-8{outline-style:var(--tw-outline-style);outline-width:8px}.outline-offset-0{outline-offset:0px}.outline-offset-1{outline-offset:1px}.outline-offset-2{outline-offset:2px}.outline-offset-4{outline-offset:4px}.outline-offset-8{outline-offset:8px}.outline-black{outline-color:var(--color-black)}.outline-current{outline-color:currentColor}.outline-inherit{outline-color:inherit}.outline-transparent{outline-color:#0000}.outline-white{outline-color:var(--color-white)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-2xl{--tw-blur:blur(var(--blur-2xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-3xl{--tw-blur:blur(var(--blur-3xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-lg{--tw-blur:blur(var(--blur-lg));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-md{--tw-blur:blur(var(--blur-md));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-none{--tw-blur: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-sm{--tw-blur:blur(var(--blur-sm));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.blur-xl{--tw-blur:blur(var(--blur-xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-0{--tw-brightness:brightness(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-50{--tw-brightness:brightness(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-75{--tw-brightness:brightness(75%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-90{--tw-brightness:brightness(90%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-95{--tw-brightness:brightness(95%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-100{--tw-brightness:brightness(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-105{--tw-brightness:brightness(105%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-110{--tw-brightness:brightness(110%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-125{--tw-brightness:brightness(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-150{--tw-brightness:brightness(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.brightness-200{--tw-brightness:brightness(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-0{--tw-contrast:contrast(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-50{--tw-contrast:contrast(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-75{--tw-contrast:contrast(75%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-100{--tw-contrast:contrast(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-125{--tw-contrast:contrast(125%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-150{--tw-contrast:contrast(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.contrast-200{--tw-contrast:contrast(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-2xl{--tw-drop-shadow-size:drop-shadow(0 25px 25px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-2xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-md{--tw-drop-shadow-size:drop-shadow(0 3px 3px var(--tw-drop-shadow-color,#0000001f));--tw-drop-shadow:drop-shadow(var(--drop-shadow-md));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-sm{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-sm));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-xl{--tw-drop-shadow-size:drop-shadow(0 9px 7px var(--tw-drop-shadow-color,#0000001a));--tw-drop-shadow:drop-shadow(var(--drop-shadow-xl));filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow-none{--tw-drop-shadow: ;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.grayscale{--tw-grayscale:grayscale(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.grayscale-0{--tw-grayscale:grayscale(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-15{--tw-hue-rotate:hue-rotate( -15deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-30{--tw-hue-rotate:hue-rotate( -30deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-60{--tw-hue-rotate:hue-rotate( -60deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-90{--tw-hue-rotate:hue-rotate( -90deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.-hue-rotate-180{--tw-hue-rotate:hue-rotate( -180deg );filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-0{--tw-hue-rotate:hue-rotate(0deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-15{--tw-hue-rotate:hue-rotate(15deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-30{--tw-hue-rotate:hue-rotate(30deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-60{--tw-hue-rotate:hue-rotate(60deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-90{--tw-hue-rotate:hue-rotate(90deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.hue-rotate-180{--tw-hue-rotate:hue-rotate(180deg);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert-0{--tw-invert:invert(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-0{--tw-saturate:saturate(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-50{--tw-saturate:saturate(50%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-100{--tw-saturate:saturate(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-150{--tw-saturate:saturate(150%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.saturate-200{--tw-saturate:saturate(200%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.sepia{--tw-sepia:sepia(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.sepia-0{--tw-sepia:sepia(0%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-2xl{--tw-backdrop-blur:blur(var(--blur-2xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-3xl{--tw-backdrop-blur:blur(var(--blur-3xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-none{--tw-backdrop-blur: ;-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-0{--tw-backdrop-brightness:brightness(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-50{--tw-backdrop-brightness:brightness(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-75{--tw-backdrop-brightness:brightness(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-90{--tw-backdrop-brightness:brightness(90%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-95{--tw-backdrop-brightness:brightness(95%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-100{--tw-backdrop-brightness:brightness(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-105{--tw-backdrop-brightness:brightness(105%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-110{--tw-backdrop-brightness:brightness(110%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-125{--tw-backdrop-brightness:brightness(125%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-150{--tw-backdrop-brightness:brightness(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-brightness-200{--tw-backdrop-brightness:brightness(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-0{--tw-backdrop-contrast:contrast(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-50{--tw-backdrop-contrast:contrast(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-75{--tw-backdrop-contrast:contrast(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-100{--tw-backdrop-contrast:contrast(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-125{--tw-backdrop-contrast:contrast(125%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-150{--tw-backdrop-contrast:contrast(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-contrast-200{--tw-backdrop-contrast:contrast(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale-0{--tw-backdrop-grayscale:grayscale(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-0{--tw-backdrop-hue-rotate:hue-rotate(0deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-15{--tw-backdrop-hue-rotate:hue-rotate(15deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-30{--tw-backdrop-hue-rotate:hue-rotate(30deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-60{--tw-backdrop-hue-rotate:hue-rotate(60deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-90{--tw-backdrop-hue-rotate:hue-rotate(90deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-hue-rotate-180{--tw-backdrop-hue-rotate:hue-rotate(180deg);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert-0{--tw-backdrop-invert:invert(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-0{--tw-backdrop-opacity:opacity(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-5{--tw-backdrop-opacity:opacity(5%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-10{--tw-backdrop-opacity:opacity(10%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-20{--tw-backdrop-opacity:opacity(20%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-25{--tw-backdrop-opacity:opacity(25%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-30{--tw-backdrop-opacity:opacity(30%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-40{--tw-backdrop-opacity:opacity(40%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-50{--tw-backdrop-opacity:opacity(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-60{--tw-backdrop-opacity:opacity(60%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-70{--tw-backdrop-opacity:opacity(70%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-75{--tw-backdrop-opacity:opacity(75%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-80{--tw-backdrop-opacity:opacity(80%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-90{--tw-backdrop-opacity:opacity(90%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-95{--tw-backdrop-opacity:opacity(95%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-opacity-100{--tw-backdrop-opacity:opacity(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-0{--tw-backdrop-saturate:saturate(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-50{--tw-backdrop-saturate:saturate(50%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-100{--tw-backdrop-saturate:saturate(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-150{--tw-backdrop-saturate:saturate(150%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-saturate-200{--tw-backdrop-saturate:saturate(200%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia-0{--tw-backdrop-sepia:sepia(0%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.delay-75{transition-delay:75ms}.delay-100{transition-delay:.1s}.delay-150{transition-delay:.15s}.delay-200{transition-delay:.2s}.delay-300{transition-delay:.3s}.delay-500{transition-delay:.5s}.delay-700{transition-delay:.7s}.delay-1000{transition-delay:1s}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.duration-1000{--tw-duration:1s;transition-duration:1s}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-linear{--tw-ease:linear;transition-timing-function:linear}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-dashed{--tw-outline-style:dashed;outline-style:dashed}.outline-dotted{--tw-outline-style:dotted;outline-style:dotted}.outline-double{--tw-outline-style:double;outline-style:double}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.select-auto{-webkit-user-select:auto;user-select:auto}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}@media(hover:hover){.group-hover\:visible:is(:where(.group):hover *){visibility:visible}.group-hover\:translate-x-1:is(:where(.group):hover *){--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:translate-y-1:is(:where(.group):hover *){--tw-translate-y:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:scale-100:is(:where(.group):hover *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:bg-black\/40:is(:where(.group):hover *){background-color:#0006}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-black\/40:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--color-black) 40%,transparent)}}.group-hover\:bg-blue-100:is(:where(.group):hover *){background-color:var(--color-blue-100)}.group-hover\:bg-blue-500:is(:where(.group):hover *){background-color:var(--color-blue-500)}.group-hover\:bg-blue-600:is(:where(.group):hover *){background-color:var(--color-blue-600)}.group-hover\:bg-gray-100:is(:where(.group):hover *){background-color:var(--color-gray-100)}.group-hover\:bg-gray-200:is(:where(.group):hover *){background-color:var(--color-gray-200)}.group-hover\:bg-gray-700:is(:where(.group):hover *){background-color:var(--color-gray-700)}.group-hover\:bg-gray-800:is(:where(.group):hover *){background-color:var(--color-gray-800)}.group-hover\:text-black:is(:where(.group):hover *){color:var(--color-black)}.group-hover\:text-blue-500:is(:where(.group):hover *){color:var(--color-blue-500)}.group-hover\:text-blue-600:is(:where(.group):hover *){color:var(--color-blue-600)}.group-hover\:text-blue-700:is(:where(.group):hover *){color:var(--color-blue-700)}.group-hover\:text-gray-700:is(:where(.group):hover *){color:var(--color-gray-700)}.group-hover\:text-gray-800:is(:where(.group):hover *){color:var(--color-gray-800)}.group-hover\:text-gray-900:is(:where(.group):hover *){color:var(--color-gray-900)}.group-hover\:text-white:is(:where(.group):hover *){color:var(--color-white)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-focus\:border-blue-500:is(:where(.group):focus *){border-color:var(--color-blue-500)}.group-focus\:ring-2:is(:where(.group):focus *){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.peer-checked\:border-blue-500:is(:where(.peer):checked~*){border-color:var(--color-blue-500)}.peer-checked\:bg-blue-500:is(:where(.peer):checked~*){background-color:var(--color-blue-500)}.peer-invalid\:border-red-500:is(:where(.peer):invalid~*){border-color:var(--color-red-500)}.peer-invalid\:text-red-500:is(:where(.peer):invalid~*){color:var(--color-red-500)}.peer-focus\:border-blue-500:is(:where(.peer):focus~*){border-color:var(--color-blue-500)}.peer-focus\:ring-2:is(:where(.peer):focus~*){--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.first-letter\:text-2xl:first-letter{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.first-letter\:font-bold:first-letter{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.first-letter\:text-blue-600:first-letter{color:var(--color-blue-600)}.first-line\:font-semibold:first-line{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.first-line\:text-gray-700:first-line{color:var(--color-gray-700)}.marker\:text-blue-500 ::marker{color:var(--color-blue-500)}.marker\:text-blue-500::marker{color:var(--color-blue-500)}.marker\:text-blue-500 ::-webkit-details-marker{color:var(--color-blue-500)}.marker\:text-blue-500::-webkit-details-marker{color:var(--color-blue-500)}.marker\:text-gray-500 ::marker{color:var(--color-gray-500)}.marker\:text-gray-500::marker{color:var(--color-gray-500)}.marker\:text-gray-500 ::-webkit-details-marker{color:var(--color-gray-500)}.marker\:text-gray-500::-webkit-details-marker{color:var(--color-gray-500)}.selection\:bg-blue-200 ::selection{background-color:var(--color-blue-200)}.selection\:bg-blue-200::selection{background-color:var(--color-blue-200)}.selection\:text-blue-900 ::selection{color:var(--color-blue-900)}.selection\:text-blue-900::selection{color:var(--color-blue-900)}.file\:mr-4::file-selector-button{margin-right:calc(var(--spacing) * 4)}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:rounded-full::file-selector-button{border-radius:3.40282e38px}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-blue-50::file-selector-button{background-color:var(--color-blue-50)}.file\:px-4::file-selector-button{padding-inline:calc(var(--spacing) * 4)}.file\:py-2::file-selector-button{padding-block:calc(var(--spacing) * 2)}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-semibold::file-selector-button{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.file\:text-blue-700::file-selector-button{color:var(--color-blue-700)}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:inset-0:before{content:var(--tw-content);inset:0}.before\:block:before{content:var(--tw-content);display:block}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-0:after{content:var(--tw-content);inset:0}.after\:clear-both:after{content:var(--tw-content);clear:both}.after\:block:after{content:var(--tw-content);display:block}.after\:h-4:after{content:var(--tw-content);height:calc(var(--spacing) * 4)}.after\:h-5:after{content:var(--tw-content);height:calc(var(--spacing) * 5)}.after\:h-6:after{content:var(--tw-content);height:calc(var(--spacing) * 6)}.after\:w-4:after{content:var(--tw-content);width:calc(var(--spacing) * 4)}.after\:w-5:after{content:var(--tw-content);width:calc(var(--spacing) * 5)}.after\:w-6:after{content:var(--tw-content);width:calc(var(--spacing) * 6)}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.first\:mt-0:first-child{margin-top:0}.first\:rounded-t-lg:first-child{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.first\:rounded-l:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.first\:rounded-l-lg:first-child{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.first\:pt-0:first-child{padding-top:0}.last\:mb-0:last-child{margin-bottom:0}.last\:rounded-r:last-child{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.last\:rounded-r-lg:last-child{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.last\:rounded-b-lg:last-child{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.last\:border-r:last-child{border-right-style:var(--tw-border-style);border-right-width:1px}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.last\:pb-0:last-child{padding-bottom:0}.odd\:bg-gray-50:nth-child(odd){background-color:var(--color-gray-50)}.odd\:bg-gray-100:nth-child(odd){background-color:var(--color-gray-100)}.odd\:bg-white:nth-child(odd){background-color:var(--color-white)}.even\:bg-gray-50:nth-child(2n){background-color:var(--color-gray-50)}.even\:bg-gray-100:nth-child(2n){background-color:var(--color-gray-100)}.even\:bg-white:nth-child(2n){background-color:var(--color-white)}.checked\:border-blue-500:checked{border-color:var(--color-blue-500)}.checked\:border-blue-600:checked{border-color:var(--color-blue-600)}.checked\:bg-blue-500:checked{background-color:var(--color-blue-500)}.checked\:bg-blue-600:checked{background-color:var(--color-blue-600)}.checked\:bg-green-500:checked{background-color:var(--color-green-500)}.checked\:text-white:checked{color:var(--color-white)}.indeterminate\:border-gray-400:indeterminate{border-color:var(--color-gray-400)}.indeterminate\:bg-gray-300:indeterminate{background-color:var(--color-gray-300)}.placeholder-shown\:border-gray-300:placeholder-shown{border-color:var(--color-gray-300)}.autofill\:bg-yellow-100:autofill{background-color:var(--color-yellow-100)}.autofill\:text-black:autofill{color:var(--color-black)}.valid\:border-green-500:valid{border-color:var(--color-green-500)}.valid\:text-green-600:valid{color:var(--color-green-600)}.valid\:ring-green-500:valid{--tw-ring-color:var(--color-green-500)}.invalid\:border-red-500:invalid{border-color:var(--color-red-500)}.invalid\:text-red-600:invalid{color:var(--color-red-600)}.invalid\:ring-red-500:invalid{--tw-ring-color:var(--color-red-500)}.read-only\:cursor-not-allowed:read-only{cursor:not-allowed}.read-only\:bg-gray-50:read-only{background-color:var(--color-gray-50)}.read-only\:bg-gray-100:read-only{background-color:var(--color-gray-100)}.empty\:hidden:empty{display:none}.focus-within\:border-blue-500:focus-within{border-color:var(--color-blue-500)}.focus-within\:border-gray-500:focus-within{border-color:var(--color-gray-500)}.focus-within\:ring-2:focus-within{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\:ring-blue-500:focus-within{--tw-ring-color:var(--color-blue-500)}.focus-within\:ring-gray-500:focus-within{--tw-ring-color:var(--color-gray-500)}@media(hover:hover){.hover\:-translate-y-0\.5:hover{--tw-translate-y:calc(var(--spacing) * -.5);translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:scale-95:hover{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-100:hover{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:border-blue-50:hover{border-color:var(--color-blue-50)}.hover\:border-blue-100:hover{border-color:var(--color-blue-100)}.hover\:border-blue-200:hover{border-color:var(--color-blue-200)}.hover\:border-blue-300:hover{border-color:var(--color-blue-300)}.hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.hover\:border-blue-600:hover{border-color:var(--color-blue-600)}.hover\:border-blue-700:hover{border-color:var(--color-blue-700)}.hover\:border-blue-800:hover{border-color:var(--color-blue-800)}.hover\:border-blue-900:hover{border-color:var(--color-blue-900)}.hover\:border-gray-50:hover{border-color:var(--color-gray-50)}.hover\:border-gray-100:hover{border-color:var(--color-gray-100)}.hover\:border-gray-200:hover{border-color:var(--color-gray-200)}.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:border-gray-400:hover{border-color:var(--color-gray-400)}.hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.hover\:border-gray-600:hover{border-color:var(--color-gray-600)}.hover\:border-gray-700:hover{border-color:var(--color-gray-700)}.hover\:border-gray-800:hover{border-color:var(--color-gray-800)}.hover\:border-gray-900:hover{border-color:var(--color-gray-900)}.hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-black:hover{background-color:var(--color-black)}.hover\:bg-black\/10:hover{background-color:#0000001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/10:hover{background-color:color-mix(in oklab,var(--color-black) 10%,transparent)}}.hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.hover\:bg-blue-100:hover{background-color:var(--color-blue-100)}.hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.hover\:bg-blue-300:hover{background-color:var(--color-blue-300)}.hover\:bg-blue-400:hover{background-color:var(--color-blue-400)}.hover\:bg-blue-500:hover{background-color:var(--color-blue-500)}.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-blue-800:hover{background-color:var(--color-blue-800)}.hover\:bg-blue-900:hover{background-color:var(--color-blue-900)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.hover\:bg-gray-400:hover{background-color:var(--color-gray-400)}.hover\:bg-gray-500:hover{background-color:var(--color-gray-500)}.hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}.hover\:bg-gray-900:hover{background-color:var(--color-gray-900)}.hover\:bg-green-50:hover{background-color:var(--color-green-50)}.hover\:bg-green-100:hover{background-color:var(--color-green-100)}.hover\:bg-green-200:hover{background-color:var(--color-green-200)}.hover\:bg-green-300:hover{background-color:var(--color-green-300)}.hover\:bg-green-400:hover{background-color:var(--color-green-400)}.hover\:bg-green-500:hover{background-color:var(--color-green-500)}.hover\:bg-green-600:hover{background-color:var(--color-green-600)}.hover\:bg-green-700:hover{background-color:var(--color-green-700)}.hover\:bg-green-800:hover{background-color:var(--color-green-800)}.hover\:bg-green-900:hover{background-color:var(--color-green-900)}.hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.hover\:bg-indigo-100:hover{background-color:var(--color-indigo-100)}.hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}.hover\:bg-indigo-300:hover{background-color:var(--color-indigo-300)}.hover\:bg-indigo-400:hover{background-color:var(--color-indigo-400)}.hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.hover\:bg-indigo-600:hover{background-color:var(--color-indigo-600)}.hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.hover\:bg-indigo-800:hover{background-color:var(--color-indigo-800)}.hover\:bg-indigo-900:hover{background-color:var(--color-indigo-900)}.hover\:bg-orange-50:hover{background-color:var(--color-orange-50)}.hover\:bg-orange-100:hover{background-color:var(--color-orange-100)}.hover\:bg-orange-200:hover{background-color:var(--color-orange-200)}.hover\:bg-orange-300:hover{background-color:var(--color-orange-300)}.hover\:bg-orange-400:hover{background-color:var(--color-orange-400)}.hover\:bg-orange-500:hover{background-color:var(--color-orange-500)}.hover\:bg-orange-600:hover{background-color:var(--color-orange-600)}.hover\:bg-orange-700:hover{background-color:var(--color-orange-700)}.hover\:bg-orange-800:hover{background-color:var(--color-orange-800)}.hover\:bg-orange-900:hover{background-color:var(--color-orange-900)}.hover\:bg-pink-50:hover{background-color:var(--color-pink-50)}.hover\:bg-pink-100:hover{background-color:var(--color-pink-100)}.hover\:bg-pink-200:hover{background-color:var(--color-pink-200)}.hover\:bg-pink-300:hover{background-color:var(--color-pink-300)}.hover\:bg-pink-400:hover{background-color:var(--color-pink-400)}.hover\:bg-pink-500:hover{background-color:var(--color-pink-500)}.hover\:bg-pink-600:hover{background-color:var(--color-pink-600)}.hover\:bg-pink-700:hover{background-color:var(--color-pink-700)}.hover\:bg-pink-800:hover{background-color:var(--color-pink-800)}.hover\:bg-pink-900:hover{background-color:var(--color-pink-900)}.hover\:bg-purple-50:hover{background-color:var(--color-purple-50)}.hover\:bg-purple-100:hover{background-color:var(--color-purple-100)}.hover\:bg-purple-200:hover{background-color:var(--color-purple-200)}.hover\:bg-purple-300:hover{background-color:var(--color-purple-300)}.hover\:bg-purple-400:hover{background-color:var(--color-purple-400)}.hover\:bg-purple-500:hover{background-color:var(--color-purple-500)}.hover\:bg-purple-600:hover{background-color:var(--color-purple-600)}.hover\:bg-purple-700:hover{background-color:var(--color-purple-700)}.hover\:bg-purple-800:hover{background-color:var(--color-purple-800)}.hover\:bg-purple-900:hover{background-color:var(--color-purple-900)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-100:hover{background-color:var(--color-red-100)}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-red-300:hover{background-color:var(--color-red-300)}.hover\:bg-red-400:hover{background-color:var(--color-red-400)}.hover\:bg-red-500:hover{background-color:var(--color-red-500)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-red-800:hover{background-color:var(--color-red-800)}.hover\:bg-red-900:hover{background-color:var(--color-red-900)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:bg-slate-300:hover{background-color:var(--color-slate-300)}.hover\:bg-slate-400:hover{background-color:var(--color-slate-400)}.hover\:bg-slate-500:hover{background-color:var(--color-slate-500)}.hover\:bg-slate-600:hover{background-color:var(--color-slate-600)}.hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-slate-900:hover{background-color:var(--color-slate-900)}.hover\:bg-transparent:hover{background-color:#0000}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:bg-yellow-50:hover{background-color:var(--color-yellow-50)}.hover\:bg-yellow-100:hover{background-color:var(--color-yellow-100)}.hover\:bg-yellow-200:hover{background-color:var(--color-yellow-200)}.hover\:bg-yellow-300:hover{background-color:var(--color-yellow-300)}.hover\:bg-yellow-400:hover{background-color:var(--color-yellow-400)}.hover\:bg-yellow-500:hover{background-color:var(--color-yellow-500)}.hover\:bg-yellow-600:hover{background-color:var(--color-yellow-600)}.hover\:bg-yellow-700:hover{background-color:var(--color-yellow-700)}.hover\:bg-yellow-800:hover{background-color:var(--color-yellow-800)}.hover\:bg-yellow-900:hover{background-color:var(--color-yellow-900)}.hover\:text-black:hover{color:var(--color-black)}.hover\:text-blue-50:hover{color:var(--color-blue-50)}.hover\:text-blue-100:hover{color:var(--color-blue-100)}.hover\:text-blue-200:hover{color:var(--color-blue-200)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-blue-400:hover{color:var(--color-blue-400)}.hover\:text-blue-500:hover{color:var(--color-blue-500)}.hover\:text-blue-600:hover{color:var(--color-blue-600)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-blue-800:hover{color:var(--color-blue-800)}.hover\:text-blue-900:hover{color:var(--color-blue-900)}.hover\:text-gray-50:hover{color:var(--color-gray-50)}.hover\:text-gray-100:hover{color:var(--color-gray-100)}.hover\:text-gray-200:hover{color:var(--color-gray-200)}.hover\:text-gray-300:hover{color:var(--color-gray-300)}.hover\:text-gray-400:hover{color:var(--color-gray-400)}.hover\:text-gray-500:hover{color:var(--color-gray-500)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-800:hover{color:var(--color-gray-800)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-green-50:hover{color:var(--color-green-50)}.hover\:text-green-100:hover{color:var(--color-green-100)}.hover\:text-green-200:hover{color:var(--color-green-200)}.hover\:text-green-300:hover{color:var(--color-green-300)}.hover\:text-green-400:hover{color:var(--color-green-400)}.hover\:text-green-500:hover{color:var(--color-green-500)}.hover\:text-green-600:hover{color:var(--color-green-600)}.hover\:text-green-700:hover{color:var(--color-green-700)}.hover\:text-green-800:hover{color:var(--color-green-800)}.hover\:text-green-900:hover{color:var(--color-green-900)}.hover\:text-purple-50:hover{color:var(--color-purple-50)}.hover\:text-purple-100:hover{color:var(--color-purple-100)}.hover\:text-purple-200:hover{color:var(--color-purple-200)}.hover\:text-purple-300:hover{color:var(--color-purple-300)}.hover\:text-purple-400:hover{color:var(--color-purple-400)}.hover\:text-purple-500:hover{color:var(--color-purple-500)}.hover\:text-purple-600:hover{color:var(--color-purple-600)}.hover\:text-purple-700:hover{color:var(--color-purple-700)}.hover\:text-purple-800:hover{color:var(--color-purple-800)}.hover\:text-purple-900:hover{color:var(--color-purple-900)}.hover\:text-red-50:hover{color:var(--color-red-50)}.hover\:text-red-100:hover{color:var(--color-red-100)}.hover\:text-red-200:hover{color:var(--color-red-200)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-red-800:hover{color:var(--color-red-800)}.hover\:text-red-900:hover{color:var(--color-red-900)}.hover\:text-slate-50:hover{color:var(--color-slate-50)}.hover\:text-slate-100:hover{color:var(--color-slate-100)}.hover\:text-slate-200:hover{color:var(--color-slate-200)}.hover\:text-slate-300:hover{color:var(--color-slate-300)}.hover\:text-slate-400:hover{color:var(--color-slate-400)}.hover\:text-slate-500:hover{color:var(--color-slate-500)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}.hover\:text-white:hover{color:var(--color-white)}.hover\:line-through:hover{text-decoration-line:line-through}.hover\:no-underline:hover{text-decoration-line:none}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-0:hover{opacity:0}.hover\:opacity-25:hover{opacity:.25}.hover\:opacity-50:hover{opacity:.5}.hover\:opacity-75:hover{opacity:.75}.hover\:opacity-100:hover{opacity:1}.hover\:shadow:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-none:hover{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-0:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-1:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-4:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-8:hover{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:ring-blue-500:hover{--tw-ring-color:var(--color-blue-500)}.hover\:ring-gray-500:hover{--tw-ring-color:var(--color-gray-500)}.hover\:ring-green-500:hover{--tw-ring-color:var(--color-green-500)}.hover\:ring-red-500:hover{--tw-ring-color:var(--color-red-500)}.file\:hover\:bg-blue-100::file-selector-button:hover{background-color:var(--color-blue-100)}}.focus\:border-blue-50:focus{border-color:var(--color-blue-50)}.focus\:border-blue-100:focus{border-color:var(--color-blue-100)}.focus\:border-blue-200:focus{border-color:var(--color-blue-200)}.focus\:border-blue-300:focus{border-color:var(--color-blue-300)}.focus\:border-blue-400:focus{border-color:var(--color-blue-400)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:border-blue-600:focus{border-color:var(--color-blue-600)}.focus\:border-blue-700:focus{border-color:var(--color-blue-700)}.focus\:border-blue-800:focus{border-color:var(--color-blue-800)}.focus\:border-blue-900:focus{border-color:var(--color-blue-900)}.focus\:border-gray-50:focus{border-color:var(--color-gray-50)}.focus\:border-gray-100:focus{border-color:var(--color-gray-100)}.focus\:border-gray-200:focus{border-color:var(--color-gray-200)}.focus\:border-gray-300:focus{border-color:var(--color-gray-300)}.focus\:border-gray-400:focus{border-color:var(--color-gray-400)}.focus\:border-gray-500:focus{border-color:var(--color-gray-500)}.focus\:border-gray-600:focus{border-color:var(--color-gray-600)}.focus\:border-gray-700:focus{border-color:var(--color-gray-700)}.focus\:border-gray-800:focus{border-color:var(--color-gray-800)}.focus\:border-gray-900:focus{border-color:var(--color-gray-900)}.focus\:border-green-500:focus{border-color:var(--color-green-500)}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:border-purple-500:focus{border-color:var(--color-purple-500)}.focus\:border-red-500:focus{border-color:var(--color-red-500)}.focus\:border-transparent:focus{border-color:#0000}.focus\:border-yellow-500:focus{border-color:var(--color-yellow-500)}.focus\:bg-black:focus{background-color:var(--color-black)}.focus\:bg-blue-50:focus{background-color:var(--color-blue-50)}.focus\:bg-blue-100:focus{background-color:var(--color-blue-100)}.focus\:bg-blue-200:focus{background-color:var(--color-blue-200)}.focus\:bg-blue-300:focus{background-color:var(--color-blue-300)}.focus\:bg-blue-400:focus{background-color:var(--color-blue-400)}.focus\:bg-blue-500:focus{background-color:var(--color-blue-500)}.focus\:bg-blue-600:focus{background-color:var(--color-blue-600)}.focus\:bg-blue-700:focus{background-color:var(--color-blue-700)}.focus\:bg-blue-800:focus{background-color:var(--color-blue-800)}.focus\:bg-blue-900:focus{background-color:var(--color-blue-900)}.focus\:bg-gray-50:focus{background-color:var(--color-gray-50)}.focus\:bg-gray-100:focus{background-color:var(--color-gray-100)}.focus\:bg-gray-200:focus{background-color:var(--color-gray-200)}.focus\:bg-gray-300:focus{background-color:var(--color-gray-300)}.focus\:bg-gray-400:focus{background-color:var(--color-gray-400)}.focus\:bg-gray-500:focus{background-color:var(--color-gray-500)}.focus\:bg-gray-600:focus{background-color:var(--color-gray-600)}.focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.focus\:bg-gray-800:focus{background-color:var(--color-gray-800)}.focus\:bg-gray-900:focus{background-color:var(--color-gray-900)}.focus\:bg-transparent:focus{background-color:#0000}.focus\:bg-white:focus{background-color:var(--color-white)}.focus\:text-black:focus{color:var(--color-black)}.focus\:text-blue-50:focus{color:var(--color-blue-50)}.focus\:text-blue-100:focus{color:var(--color-blue-100)}.focus\:text-blue-200:focus{color:var(--color-blue-200)}.focus\:text-blue-300:focus{color:var(--color-blue-300)}.focus\:text-blue-400:focus{color:var(--color-blue-400)}.focus\:text-blue-500:focus{color:var(--color-blue-500)}.focus\:text-blue-600:focus{color:var(--color-blue-600)}.focus\:text-blue-700:focus{color:var(--color-blue-700)}.focus\:text-blue-800:focus{color:var(--color-blue-800)}.focus\:text-blue-900:focus{color:var(--color-blue-900)}.focus\:text-gray-50:focus{color:var(--color-gray-50)}.focus\:text-gray-100:focus{color:var(--color-gray-100)}.focus\:text-gray-200:focus{color:var(--color-gray-200)}.focus\:text-gray-300:focus{color:var(--color-gray-300)}.focus\:text-gray-400:focus{color:var(--color-gray-400)}.focus\:text-gray-500:focus{color:var(--color-gray-500)}.focus\:text-gray-600:focus{color:var(--color-gray-600)}.focus\:text-gray-700:focus{color:var(--color-gray-700)}.focus\:text-gray-800:focus{color:var(--color-gray-800)}.focus\:text-gray-900:focus{color:var(--color-gray-900)}.focus\:text-white:focus{color:var(--color-white)}.focus\:shadow:focus{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-lg:focus{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-md:focus{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-none:focus{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-sm:focus{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:shadow-xl:focus{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-0:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-4:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-8:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-amber-500:focus{--tw-ring-color:var(--color-amber-500)}.focus\:ring-blue-50:focus{--tw-ring-color:var(--color-blue-50)}.focus\:ring-blue-100:focus{--tw-ring-color:var(--color-blue-100)}.focus\:ring-blue-200:focus{--tw-ring-color:var(--color-blue-200)}.focus\:ring-blue-300:focus{--tw-ring-color:var(--color-blue-300)}.focus\:ring-blue-400:focus{--tw-ring-color:var(--color-blue-400)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-blue-600:focus{--tw-ring-color:var(--color-blue-600)}.focus\:ring-blue-700:focus{--tw-ring-color:var(--color-blue-700)}.focus\:ring-blue-800:focus{--tw-ring-color:var(--color-blue-800)}.focus\:ring-blue-900:focus{--tw-ring-color:var(--color-blue-900)}.focus\:ring-current:focus{--tw-ring-color:currentcolor}.focus\:ring-gray-50:focus{--tw-ring-color:var(--color-gray-50)}.focus\:ring-gray-100:focus{--tw-ring-color:var(--color-gray-100)}.focus\:ring-gray-200:focus{--tw-ring-color:var(--color-gray-200)}.focus\:ring-gray-300:focus{--tw-ring-color:var(--color-gray-300)}.focus\:ring-gray-400:focus{--tw-ring-color:var(--color-gray-400)}.focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.focus\:ring-gray-600:focus{--tw-ring-color:var(--color-gray-600)}.focus\:ring-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.focus\:ring-gray-800:focus{--tw-ring-color:var(--color-gray-800)}.focus\:ring-gray-900:focus{--tw-ring-color:var(--color-gray-900)}.focus\:ring-green-500:focus{--tw-ring-color:var(--color-green-500)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:ring-purple-500:focus{--tw-ring-color:var(--color-purple-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-slate-400:focus{--tw-ring-color:var(--color-slate-400)}.focus\:ring-transparent:focus{--tw-ring-color:transparent}.focus\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-4:focus{--tw-ring-offset-width:4px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus\:ring-offset-gray-100:focus{--tw-ring-offset-color:var(--color-gray-100)}.focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.focus\:ring-offset-white:focus{--tw-ring-offset-color:var(--color-white)}.focus\:outline:focus{outline-style:var(--tw-outline-style);outline-width:1px}.focus\:outline-0:focus{outline-style:var(--tw-outline-style);outline-width:0}.focus\:outline-1:focus{outline-style:var(--tw-outline-style);outline-width:1px}.focus\:outline-2:focus{outline-style:var(--tw-outline-style);outline-width:2px}.focus\:outline-4:focus{outline-style:var(--tw-outline-style);outline-width:4px}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus\:ring-inset:focus{--tw-ring-inset:inset}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-blue-500:focus-visible{--tw-ring-color:var(--color-blue-500)}.focus-visible\:ring-gray-500:focus-visible{--tw-ring-color:var(--color-gray-500)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-100:active{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:cursor-grabbing:active{cursor:grabbing}.active\:bg-blue-600:active{background-color:var(--color-blue-600)}.active\:bg-blue-700:active{background-color:var(--color-blue-700)}.active\:bg-blue-800:active{background-color:var(--color-blue-800)}.active\:bg-gray-100:active{background-color:var(--color-gray-100)}.active\:bg-gray-200:active{background-color:var(--color-gray-200)}.active\:bg-gray-300:active{background-color:var(--color-gray-300)}.active\:bg-gray-700:active{background-color:var(--color-gray-700)}.active\:bg-gray-800:active{background-color:var(--color-gray-800)}.active\:bg-green-600:active{background-color:var(--color-green-600)}.active\:bg-green-700:active{background-color:var(--color-green-700)}.active\:bg-red-600:active{background-color:var(--color-red-600)}.active\:bg-red-700:active{background-color:var(--color-red-700)}.active\:opacity-75:active{opacity:.75}.active\:opacity-100:active{opacity:1}.disabled\:cursor-default:disabled{cursor:default}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:border-gray-200:disabled{border-color:var(--color-gray-200)}.disabled\:border-gray-300:disabled{border-color:var(--color-gray-300)}.disabled\:bg-blue-400:disabled{background-color:var(--color-blue-400)}.disabled\:bg-gray-100:disabled{background-color:var(--color-gray-100)}.disabled\:bg-gray-200:disabled{background-color:var(--color-gray-200)}.disabled\:bg-gray-300:disabled{background-color:var(--color-gray-300)}.disabled\:bg-gray-400:disabled{background-color:var(--color-gray-400)}.disabled\:text-gray-300:disabled{color:var(--color-gray-300)}.disabled\:text-gray-400:disabled{color:var(--color-gray-400)}.disabled\:text-gray-500:disabled{color:var(--color-gray-500)}.disabled\:no-underline:disabled{text-decoration-line:none}.disabled\:opacity-25:disabled{opacity:.25}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-75:disabled{opacity:.75}@media(hover:hover){.disabled\:hover\:bg-indigo-600:disabled:hover{background-color:var(--color-indigo-600)}.disabled\:hover\:bg-transparent:disabled:hover{background-color:#0000}}@media(min-width:40rem){.sm\:col-span-1{grid-column:span 1/span 1}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:col-span-3{grid-column:span 3/span 3}.sm\:col-span-4{grid-column:span 4/span 4}.sm\:col-span-6{grid-column:span 6/span 6}.sm\:col-span-12{grid-column:span 12/span 12}.sm\:col-span-full{grid-column:1/-1}.sm\:m-0{margin:0}.sm\:m-1{margin:var(--spacing)}.sm\:m-2{margin:calc(var(--spacing) * 2)}.sm\:m-3{margin:calc(var(--spacing) * 3)}.sm\:m-4{margin:calc(var(--spacing) * 4)}.sm\:m-auto{margin:auto}.sm\:mx-auto{margin-inline:auto}.sm\:my-auto{margin-block:auto}.sm\:mt-6{margin-top:calc(var(--spacing) * 6)}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:inline{display:inline}.sm\:inline-block{display:inline-block}.sm\:inline-flex{display:inline-flex}.sm\:w-1\/2{width:50%}.sm\:w-1\/3{width:33.3333%}.sm\:w-1\/4{width:25%}.sm\:w-2\/3{width:66.6667%}.sm\:w-3\/4{width:75%}.sm\:w-40{width:calc(var(--spacing) * 40)}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:flex-col{flex-direction:column}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:items-end{align-items:flex-end}.sm\:items-start{align-items:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:justify-center{justify-content:center}.sm\:justify-end{justify-content:flex-end}.sm\:justify-start{justify-content:flex-start}.sm\:gap-0{gap:0}.sm\:gap-1{gap:var(--spacing)}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:gap-5{gap:calc(var(--spacing) * 5)}.sm\:gap-6{gap:calc(var(--spacing) * 6)}.sm\:gap-8{gap:calc(var(--spacing) * 8)}:where(.sm\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}:where(.sm\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.sm\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline:0}:where(.sm\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.sm\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}.sm\:p-0{padding:0}.sm\:p-1{padding:var(--spacing)}.sm\:p-2{padding:calc(var(--spacing) * 2)}.sm\:p-3{padding:calc(var(--spacing) * 3)}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:p-8{padding:calc(var(--spacing) * 8)}.sm\:px-0{padding-inline:0}.sm\:px-1{padding-inline:var(--spacing)}.sm\:px-2{padding-inline:calc(var(--spacing) * 2)}.sm\:px-3{padding-inline:calc(var(--spacing) * 3)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:px-8{padding-inline:calc(var(--spacing) * 8)}.sm\:py-0{padding-block:0}.sm\:py-1{padding-block:var(--spacing)}.sm\:py-2{padding-block:calc(var(--spacing) * 2)}.sm\:py-3{padding-block:calc(var(--spacing) * 3)}.sm\:py-4{padding-block:calc(var(--spacing) * 4)}.sm\:py-5{padding-block:calc(var(--spacing) * 5)}.sm\:py-6{padding-block:calc(var(--spacing) * 6)}.sm\:py-8{padding-block:calc(var(--spacing) * 8)}.sm\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.sm\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.sm\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:48rem){.md\:absolute{position:absolute}.md\:fixed{position:fixed}.md\:relative{position:relative}.md\:static{position:static}.md\:sticky{position:sticky}.md\:inset-0{inset:0}.md\:top-0{top:0}.md\:right-0{right:0}.md\:bottom-0{bottom:0}.md\:left-0{left:0}.md\:order-1{order:1}.md\:order-2{order:2}.md\:order-3{order:3}.md\:order-first{order:-9999}.md\:order-last{order:9999}.md\:order-none{order:0}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:col-span-4{grid-column:span 4/span 4}.md\:col-span-5{grid-column:span 5/span 5}.md\:col-span-6{grid-column:span 6/span 6}.md\:col-span-12{grid-column:span 12/span 12}.md\:col-span-full{grid-column:1/-1}.md\:row-span-1{grid-row:span 1/span 1}.md\:row-span-2{grid-row:span 2/span 2}.md\:row-span-3{grid-row:span 3/span 3}.md\:row-span-4{grid-row:span 4/span 4}.md\:row-span-full{grid-row:1/-1}.md\:m-0{margin:0}.md\:m-1{margin:var(--spacing)}.md\:m-2{margin:calc(var(--spacing) * 2)}.md\:m-3{margin:calc(var(--spacing) * 3)}.md\:m-4{margin:calc(var(--spacing) * 4)}.md\:m-auto{margin:auto}.md\:mx-auto{margin-inline:auto}.md\:my-auto{margin-block:auto}.md\:mt-0{margin-top:0}.md\:mt-1{margin-top:var(--spacing)}.md\:mt-2{margin-top:calc(var(--spacing) * 2)}.md\:mt-3{margin-top:calc(var(--spacing) * 3)}.md\:mt-4{margin-top:calc(var(--spacing) * 4)}.md\:mt-6{margin-top:calc(var(--spacing) * 6)}.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mr-0{margin-right:0}.md\:mr-1{margin-right:var(--spacing)}.md\:mr-2{margin-right:calc(var(--spacing) * 2)}.md\:mr-3{margin-right:calc(var(--spacing) * 3)}.md\:mr-4{margin-right:calc(var(--spacing) * 4)}.md\:mr-6{margin-right:calc(var(--spacing) * 6)}.md\:mr-8{margin-right:calc(var(--spacing) * 8)}.md\:mr-auto{margin-right:auto}.md\:mb-0{margin-bottom:0}.md\:mb-1{margin-bottom:var(--spacing)}.md\:mb-2{margin-bottom:calc(var(--spacing) * 2)}.md\:mb-3{margin-bottom:calc(var(--spacing) * 3)}.md\:mb-4{margin-bottom:calc(var(--spacing) * 4)}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:mb-8{margin-bottom:calc(var(--spacing) * 8)}.md\:ml-0{margin-left:0}.md\:ml-1{margin-left:var(--spacing)}.md\:ml-2{margin-left:calc(var(--spacing) * 2)}.md\:ml-3{margin-left:calc(var(--spacing) * 3)}.md\:ml-4{margin-left:calc(var(--spacing) * 4)}.md\:ml-6{margin-left:calc(var(--spacing) * 6)}.md\:ml-8{margin-left:calc(var(--spacing) * 8)}.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:inline{display:inline}.md\:inline-block{display:inline-block}.md\:inline-flex{display:inline-flex}.md\:h-20{height:calc(var(--spacing) * 20)}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.3333%}.md\:w-1\/4{width:25%}.md\:w-1\/5{width:20%}.md\:w-2\/3{width:66.6667%}.md\:w-2\/5{width:40%}.md\:w-3\/4{width:75%}.md\:w-3\/5{width:60%}.md\:w-4\/5{width:80%}.md\:w-20{width:calc(var(--spacing) * 20)}.md\:w-40{width:calc(var(--spacing) * 40)}.md\:w-48{width:calc(var(--spacing) * 48)}.md\:w-64{width:calc(var(--spacing) * 64)}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:w-80{width:calc(var(--spacing) * 80)}.md\:w-auto{width:auto}.md\:w-full{width:100%}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:max-w-3xl{max-width:var(--container-3xl)}.md\:max-w-4xl{max-width:var(--container-4xl)}.md\:max-w-5xl{max-width:var(--container-5xl)}.md\:max-w-6xl{max-width:var(--container-6xl)}.md\:max-w-7xl{max-width:var(--container-7xl)}.md\:max-w-full{max-width:100%}.md\:max-w-lg{max-width:var(--container-lg)}.md\:max-w-md{max-width:var(--container-md)}.md\:max-w-none{max-width:none}.md\:max-w-sm{max-width:var(--container-sm)}.md\:max-w-xl{max-width:var(--container-xl)}.md\:max-w-xs{max-width:var(--container-xs)}.md\:flex-1{flex:1}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.md\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.md\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.md\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.md\:grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.md\:grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.md\:flex-col{flex-direction:column}.md\:flex-row{flex-direction:row}.md\:items-baseline{align-items:baseline}.md\:items-center{align-items:center}.md\:items-end{align-items:flex-end}.md\:items-start{align-items:flex-start}.md\:items-stretch{align-items:stretch}.md\:justify-around{justify-content:space-around}.md\:justify-between{justify-content:space-between}.md\:justify-center{justify-content:center}.md\:justify-end{justify-content:flex-end}.md\:justify-start{justify-content:flex-start}.md\:gap-0{gap:0}.md\:gap-1{gap:var(--spacing)}.md\:gap-2{gap:calc(var(--spacing) * 2)}.md\:gap-3{gap:calc(var(--spacing) * 3)}.md\:gap-4{gap:calc(var(--spacing) * 4)}.md\:gap-5{gap:calc(var(--spacing) * 5)}.md\:gap-6{gap:calc(var(--spacing) * 6)}.md\:gap-8{gap:calc(var(--spacing) * 8)}.md\:gap-10{gap:calc(var(--spacing) * 10)}.md\:gap-12{gap:calc(var(--spacing) * 12)}:where(.md\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}:where(.md\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.md\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline:0}:where(.md\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.md\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.md\:rounded-2xl{border-radius:var(--radius-2xl)}.md\:p-0{padding:0}.md\:p-1{padding:var(--spacing)}.md\:p-2{padding:calc(var(--spacing) * 2)}.md\:p-3{padding:calc(var(--spacing) * 3)}.md\:p-4{padding:calc(var(--spacing) * 4)}.md\:p-5{padding:calc(var(--spacing) * 5)}.md\:p-6{padding:calc(var(--spacing) * 6)}.md\:p-8{padding:calc(var(--spacing) * 8)}.md\:p-10{padding:calc(var(--spacing) * 10)}.md\:p-12{padding:calc(var(--spacing) * 12)}.md\:px-0{padding-inline:0}.md\:px-1{padding-inline:var(--spacing)}.md\:px-2{padding-inline:calc(var(--spacing) * 2)}.md\:px-3{padding-inline:calc(var(--spacing) * 3)}.md\:px-4{padding-inline:calc(var(--spacing) * 4)}.md\:px-5{padding-inline:calc(var(--spacing) * 5)}.md\:px-6{padding-inline:calc(var(--spacing) * 6)}.md\:px-8{padding-inline:calc(var(--spacing) * 8)}.md\:px-10{padding-inline:calc(var(--spacing) * 10)}.md\:px-12{padding-inline:calc(var(--spacing) * 12)}.md\:py-0{padding-block:0}.md\:py-1{padding-block:var(--spacing)}.md\:py-2{padding-block:calc(var(--spacing) * 2)}.md\:py-3{padding-block:calc(var(--spacing) * 3)}.md\:py-4{padding-block:calc(var(--spacing) * 4)}.md\:py-5{padding-block:calc(var(--spacing) * 5)}.md\:py-6{padding-block:calc(var(--spacing) * 6)}.md\:py-8{padding-block:calc(var(--spacing) * 8)}.md\:py-10{padding-block:calc(var(--spacing) * 10)}.md\:py-12{padding-block:calc(var(--spacing) * 12)}.md\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.md\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:64rem){.lg\:absolute{position:absolute}.lg\:fixed{position:fixed}.lg\:relative{position:relative}.lg\:static{position:static}.lg\:sticky{position:sticky}.lg\:inset-0{inset:0}.lg\:top-0{top:0}.lg\:right-0{right:0}.lg\:bottom-0{bottom:0}.lg\:left-0{left:0}.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:order-3{order:3}.lg\:order-4{order:4}.lg\:order-first{order:-9999}.lg\:order-last{order:9999}.lg\:order-none{order:0}.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-5{grid-column:span 5/span 5}.lg\:col-span-6{grid-column:span 6/span 6}.lg\:col-span-7{grid-column:span 7/span 7}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:col-span-12{grid-column:span 12/span 12}.lg\:col-span-full{grid-column:1/-1}.lg\:row-span-1{grid-row:span 1/span 1}.lg\:row-span-2{grid-row:span 2/span 2}.lg\:row-span-3{grid-row:span 3/span 3}.lg\:row-span-4{grid-row:span 4/span 4}.lg\:row-span-full{grid-row:1/-1}.lg\:m-0{margin:0}.lg\:m-1{margin:var(--spacing)}.lg\:m-2{margin:calc(var(--spacing) * 2)}.lg\:m-3{margin:calc(var(--spacing) * 3)}.lg\:m-4{margin:calc(var(--spacing) * 4)}.lg\:m-auto{margin:auto}.lg\:mx-auto{margin-inline:auto}.lg\:my-auto{margin-block:auto}.lg\:mt-0{margin-top:0}.lg\:mt-1{margin-top:var(--spacing)}.lg\:mt-2{margin-top:calc(var(--spacing) * 2)}.lg\:mt-3{margin-top:calc(var(--spacing) * 3)}.lg\:mt-4{margin-top:calc(var(--spacing) * 4)}.lg\:mt-6{margin-top:calc(var(--spacing) * 6)}.lg\:mt-8{margin-top:calc(var(--spacing) * 8)}.lg\:mt-10{margin-top:calc(var(--spacing) * 10)}.lg\:mt-12{margin-top:calc(var(--spacing) * 12)}.lg\:mr-0{margin-right:0}.lg\:mr-1{margin-right:var(--spacing)}.lg\:mr-2{margin-right:calc(var(--spacing) * 2)}.lg\:mr-3{margin-right:calc(var(--spacing) * 3)}.lg\:mr-4{margin-right:calc(var(--spacing) * 4)}.lg\:mr-6{margin-right:calc(var(--spacing) * 6)}.lg\:mr-8{margin-right:calc(var(--spacing) * 8)}.lg\:mr-auto{margin-right:auto}.lg\:mb-0{margin-bottom:0}.lg\:mb-1{margin-bottom:var(--spacing)}.lg\:mb-2{margin-bottom:calc(var(--spacing) * 2)}.lg\:mb-3{margin-bottom:calc(var(--spacing) * 3)}.lg\:mb-4{margin-bottom:calc(var(--spacing) * 4)}.lg\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.lg\:mb-8{margin-bottom:calc(var(--spacing) * 8)}.lg\:mb-10{margin-bottom:calc(var(--spacing) * 10)}.lg\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.lg\:ml-0{margin-left:0}.lg\:ml-1{margin-left:var(--spacing)}.lg\:ml-2{margin-left:calc(var(--spacing) * 2)}.lg\:ml-3{margin-left:calc(var(--spacing) * 3)}.lg\:ml-4{margin-left:calc(var(--spacing) * 4)}.lg\:ml-6{margin-left:calc(var(--spacing) * 6)}.lg\:ml-8{margin-left:calc(var(--spacing) * 8)}.lg\:ml-auto{margin-left:auto}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:inline{display:inline}.lg\:inline-block{display:inline-block}.lg\:inline-flex{display:inline-flex}.lg\:w-1\/2{width:50%}.lg\:w-1\/3{width:33.3333%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:w-1\/6{width:16.6667%}.lg\:w-2\/3{width:66.6667%}.lg\:w-2\/5{width:40%}.lg\:w-3\/4{width:75%}.lg\:w-3\/5{width:60%}.lg\:w-4\/5{width:80%}.lg\:w-5\/6{width:83.3333%}.lg\:w-auto{width:auto}.lg\:w-full{width:100%}.lg\:max-w-2xl{max-width:var(--container-2xl)}.lg\:max-w-3xl{max-width:var(--container-3xl)}.lg\:max-w-4xl{max-width:var(--container-4xl)}.lg\:max-w-5xl{max-width:var(--container-5xl)}.lg\:max-w-6xl{max-width:var(--container-6xl)}.lg\:max-w-7xl{max-width:var(--container-7xl)}.lg\:max-w-full{max-width:100%}.lg\:max-w-lg{max-width:var(--container-lg)}.lg\:max-w-md{max-width:var(--container-md)}.lg\:max-w-none{max-width:none}.lg\:max-w-sm{max-width:var(--container-sm)}.lg\:max-w-xl{max-width:var(--container-xl)}.lg\:max-w-xs{max-width:var(--container-xs)}.lg\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.lg\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.lg\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.lg\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.lg\:grid-rows-5{grid-template-rows:repeat(5,minmax(0,1fr))}.lg\:grid-rows-6{grid-template-rows:repeat(6,minmax(0,1fr))}.lg\:flex-col{flex-direction:column}.lg\:flex-row{flex-direction:row}.lg\:items-baseline{align-items:baseline}.lg\:items-center{align-items:center}.lg\:items-end{align-items:flex-end}.lg\:items-start{align-items:flex-start}.lg\:items-stretch{align-items:stretch}.lg\:justify-around{justify-content:space-around}.lg\:justify-between{justify-content:space-between}.lg\:justify-center{justify-content:center}.lg\:justify-end{justify-content:flex-end}.lg\:justify-evenly{justify-content:space-evenly}.lg\:justify-start{justify-content:flex-start}.lg\:gap-0{gap:0}.lg\:gap-1{gap:var(--spacing)}.lg\:gap-2{gap:calc(var(--spacing) * 2)}.lg\:gap-3{gap:calc(var(--spacing) * 3)}.lg\:gap-4{gap:calc(var(--spacing) * 4)}.lg\:gap-5{gap:calc(var(--spacing) * 5)}.lg\:gap-6{gap:calc(var(--spacing) * 6)}.lg\:gap-8{gap:calc(var(--spacing) * 8)}.lg\:gap-10{gap:calc(var(--spacing) * 10)}.lg\:gap-12{gap:calc(var(--spacing) * 12)}.lg\:gap-16{gap:calc(var(--spacing) * 16)}:where(.lg\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}:where(.lg\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-y-10>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 10) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 10) * calc(1 - var(--tw-space-y-reverse)))}:where(.lg\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline:0}:where(.lg\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}:where(.lg\:space-x-10>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 10) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 10) * calc(1 - var(--tw-space-x-reverse)))}.lg\:p-0{padding:0}.lg\:p-1{padding:var(--spacing)}.lg\:p-2{padding:calc(var(--spacing) * 2)}.lg\:p-3{padding:calc(var(--spacing) * 3)}.lg\:p-4{padding:calc(var(--spacing) * 4)}.lg\:p-5{padding:calc(var(--spacing) * 5)}.lg\:p-6{padding:calc(var(--spacing) * 6)}.lg\:p-8{padding:calc(var(--spacing) * 8)}.lg\:p-10{padding:calc(var(--spacing) * 10)}.lg\:p-12{padding:calc(var(--spacing) * 12)}.lg\:p-16{padding:calc(var(--spacing) * 16)}.lg\:px-0{padding-inline:0}.lg\:px-1{padding-inline:var(--spacing)}.lg\:px-2{padding-inline:calc(var(--spacing) * 2)}.lg\:px-3{padding-inline:calc(var(--spacing) * 3)}.lg\:px-4{padding-inline:calc(var(--spacing) * 4)}.lg\:px-5{padding-inline:calc(var(--spacing) * 5)}.lg\:px-6{padding-inline:calc(var(--spacing) * 6)}.lg\:px-8{padding-inline:calc(var(--spacing) * 8)}.lg\:px-10{padding-inline:calc(var(--spacing) * 10)}.lg\:px-12{padding-inline:calc(var(--spacing) * 12)}.lg\:px-16{padding-inline:calc(var(--spacing) * 16)}.lg\:py-0{padding-block:0}.lg\:py-1{padding-block:var(--spacing)}.lg\:py-2{padding-block:calc(var(--spacing) * 2)}.lg\:py-3{padding-block:calc(var(--spacing) * 3)}.lg\:py-4{padding-block:calc(var(--spacing) * 4)}.lg\:py-5{padding-block:calc(var(--spacing) * 5)}.lg\:py-6{padding-block:calc(var(--spacing) * 6)}.lg\:py-8{padding-block:calc(var(--spacing) * 8)}.lg\:py-10{padding-block:calc(var(--spacing) * 10)}.lg\:py-12{padding-block:calc(var(--spacing) * 12)}.lg\:py-16{padding-block:calc(var(--spacing) * 16)}.lg\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.lg\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.lg\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.lg\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.lg\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.lg\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.lg\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.lg\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.lg\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:80rem){.xl\:col-span-1{grid-column:span 1/span 1}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-span-3{grid-column:span 3/span 3}.xl\:col-span-4{grid-column:span 4/span 4}.xl\:col-span-5{grid-column:span 5/span 5}.xl\:col-span-6{grid-column:span 6/span 6}.xl\:col-span-12{grid-column:span 12/span 12}.xl\:col-span-full{grid-column:1/-1}.xl\:row-span-1{grid-row:span 1/span 1}.xl\:row-span-2{grid-row:span 2/span 2}.xl\:row-span-3{grid-row:span 3/span 3}.xl\:row-span-full{grid-row:1/-1}.xl\:m-0{margin:0}.xl\:m-auto{margin:auto}.xl\:mx-auto{margin-inline:auto}.xl\:my-auto{margin-block:auto}.xl\:block{display:block}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:inline{display:inline}.xl\:inline-block{display:inline-block}.xl\:inline-flex{display:inline-flex}.xl\:w-1\/2{width:50%}.xl\:w-1\/3{width:33.3333%}.xl\:w-1\/4{width:25%}.xl\:w-1\/5{width:20%}.xl\:w-2\/3{width:66.6667%}.xl\:w-2\/5{width:40%}.xl\:w-3\/4{width:75%}.xl\:w-3\/5{width:60%}.xl\:w-4\/5{width:80%}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-2xl{max-width:var(--container-2xl)}.xl\:max-w-3xl{max-width:var(--container-3xl)}.xl\:max-w-4xl{max-width:var(--container-4xl)}.xl\:max-w-5xl{max-width:var(--container-5xl)}.xl\:max-w-6xl{max-width:var(--container-6xl)}.xl\:max-w-7xl{max-width:var(--container-7xl)}.xl\:max-w-full{max-width:100%}.xl\:max-w-lg{max-width:var(--container-lg)}.xl\:max-w-md{max-width:var(--container-md)}.xl\:max-w-none{max-width:none}.xl\:max-w-sm{max-width:var(--container-sm)}.xl\:max-w-xl{max-width:var(--container-xl)}.xl\:max-w-xs{max-width:var(--container-xs)}.xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:grid-rows-1{grid-template-rows:repeat(1,minmax(0,1fr))}.xl\:grid-rows-2{grid-template-rows:repeat(2,minmax(0,1fr))}.xl\:grid-rows-3{grid-template-rows:repeat(3,minmax(0,1fr))}.xl\:grid-rows-4{grid-template-rows:repeat(4,minmax(0,1fr))}.xl\:flex-col{flex-direction:column}.xl\:flex-row{flex-direction:row}.xl\:items-center{align-items:center}.xl\:items-end{align-items:flex-end}.xl\:items-start{align-items:flex-start}.xl\:justify-between{justify-content:space-between}.xl\:justify-center{justify-content:center}.xl\:justify-end{justify-content:flex-end}.xl\:justify-start{justify-content:flex-start}.xl\:gap-0{gap:0}.xl\:gap-1{gap:var(--spacing)}.xl\:gap-2{gap:calc(var(--spacing) * 2)}.xl\:gap-3{gap:calc(var(--spacing) * 3)}.xl\:gap-4{gap:calc(var(--spacing) * 4)}.xl\:gap-5{gap:calc(var(--spacing) * 5)}.xl\:gap-6{gap:calc(var(--spacing) * 6)}.xl\:gap-8{gap:calc(var(--spacing) * 8)}.xl\:gap-10{gap:calc(var(--spacing) * 10)}.xl\:gap-12{gap:calc(var(--spacing) * 12)}.xl\:gap-16{gap:calc(var(--spacing) * 16)}:where(.xl\:space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block:0}:where(.xl\:space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}:where(.xl\:space-x-0>:not(:last-child)){--tw-space-x-reverse:0;margin-inline:0}:where(.xl\:space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.xl\:space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.xl\:p-0{padding:0}.xl\:p-1{padding:var(--spacing)}.xl\:p-2{padding:calc(var(--spacing) * 2)}.xl\:p-3{padding:calc(var(--spacing) * 3)}.xl\:p-4{padding:calc(var(--spacing) * 4)}.xl\:p-5{padding:calc(var(--spacing) * 5)}.xl\:p-6{padding:calc(var(--spacing) * 6)}.xl\:p-8{padding:calc(var(--spacing) * 8)}.xl\:p-10{padding:calc(var(--spacing) * 10)}.xl\:p-12{padding:calc(var(--spacing) * 12)}.xl\:p-16{padding:calc(var(--spacing) * 16)}.xl\:px-0{padding-inline:0}.xl\:px-1{padding-inline:var(--spacing)}.xl\:px-2{padding-inline:calc(var(--spacing) * 2)}.xl\:px-3{padding-inline:calc(var(--spacing) * 3)}.xl\:px-4{padding-inline:calc(var(--spacing) * 4)}.xl\:px-5{padding-inline:calc(var(--spacing) * 5)}.xl\:px-6{padding-inline:calc(var(--spacing) * 6)}.xl\:px-8{padding-inline:calc(var(--spacing) * 8)}.xl\:px-10{padding-inline:calc(var(--spacing) * 10)}.xl\:px-12{padding-inline:calc(var(--spacing) * 12)}.xl\:px-16{padding-inline:calc(var(--spacing) * 16)}.xl\:py-0{padding-block:0}.xl\:py-1{padding-block:var(--spacing)}.xl\:py-2{padding-block:calc(var(--spacing) * 2)}.xl\:py-3{padding-block:calc(var(--spacing) * 3)}.xl\:py-4{padding-block:calc(var(--spacing) * 4)}.xl\:py-5{padding-block:calc(var(--spacing) * 5)}.xl\:py-6{padding-block:calc(var(--spacing) * 6)}.xl\:py-8{padding-block:calc(var(--spacing) * 8)}.xl\:py-10{padding-block:calc(var(--spacing) * 10)}.xl\:py-12{padding-block:calc(var(--spacing) * 12)}.xl\:py-16{padding-block:calc(var(--spacing) * 16)}.xl\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.xl\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.xl\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.xl\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.xl\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.xl\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.xl\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.xl\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.xl\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.xl\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:96rem){.\32xl\:col-span-1{grid-column:span 1/span 1}.\32xl\:col-span-2{grid-column:span 2/span 2}.\32xl\:col-span-3{grid-column:span 3/span 3}.\32xl\:col-span-4{grid-column:span 4/span 4}.\32xl\:col-span-6{grid-column:span 6/span 6}.\32xl\:col-span-12{grid-column:span 12/span 12}.\32xl\:block{display:block}.\32xl\:flex{display:flex}.\32xl\:hidden{display:none}.\32xl\:w-1\/2{width:50%}.\32xl\:w-1\/3{width:33.3333%}.\32xl\:w-1\/4{width:25%}.\32xl\:w-2\/3{width:66.6667%}.\32xl\:w-3\/4{width:75%}.\32xl\:w-auto{width:auto}.\32xl\:w-full{width:100%}.\32xl\:max-w-2xl{max-width:var(--container-2xl)}.\32xl\:max-w-3xl{max-width:var(--container-3xl)}.\32xl\:max-w-4xl{max-width:var(--container-4xl)}.\32xl\:max-w-5xl{max-width:var(--container-5xl)}.\32xl\:max-w-6xl{max-width:var(--container-6xl)}.\32xl\:max-w-7xl{max-width:var(--container-7xl)}.\32xl\:max-w-full{max-width:100%}.\32xl\:max-w-lg{max-width:var(--container-lg)}.\32xl\:max-w-md{max-width:var(--container-md)}.\32xl\:max-w-sm{max-width:var(--container-sm)}.\32xl\:max-w-xl{max-width:var(--container-xl)}.\32xl\:max-w-xs{max-width:var(--container-xs)}.\32xl\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.\32xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.\32xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.\32xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.\32xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.\32xl\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.\32xl\:gap-0{gap:0}.\32xl\:gap-4{gap:calc(var(--spacing) * 4)}.\32xl\:gap-6{gap:calc(var(--spacing) * 6)}.\32xl\:gap-8{gap:calc(var(--spacing) * 8)}.\32xl\:gap-12{gap:calc(var(--spacing) * 12)}.\32xl\:gap-16{gap:calc(var(--spacing) * 16)}.\32xl\:p-0{padding:0}.\32xl\:p-4{padding:calc(var(--spacing) * 4)}.\32xl\:p-6{padding:calc(var(--spacing) * 6)}.\32xl\:p-8{padding:calc(var(--spacing) * 8)}.\32xl\:p-12{padding:calc(var(--spacing) * 12)}.\32xl\:p-16{padding:calc(var(--spacing) * 16)}.\32xl\:px-0{padding-inline:0}.\32xl\:px-4{padding-inline:calc(var(--spacing) * 4)}.\32xl\:px-6{padding-inline:calc(var(--spacing) * 6)}.\32xl\:px-8{padding-inline:calc(var(--spacing) * 8)}.\32xl\:px-12{padding-inline:calc(var(--spacing) * 12)}.\32xl\:px-16{padding-inline:calc(var(--spacing) * 16)}.\32xl\:py-0{padding-block:0}.\32xl\:py-4{padding-block:calc(var(--spacing) * 4)}.\32xl\:py-6{padding-block:calc(var(--spacing) * 6)}.\32xl\:py-8{padding-block:calc(var(--spacing) * 8)}.\32xl\:py-12{padding-block:calc(var(--spacing) * 12)}.\32xl\:py-16{padding-block:calc(var(--spacing) * 16)}.\32xl\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.\32xl\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.\32xl\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.\32xl\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.\32xl\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.\32xl\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.\32xl\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.\32xl\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.\32xl\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}.dark .dark\:block{display:block}.dark .dark\:flex{display:flex}.dark .dark\:flex-1{flex:1}.dark .dark\:flex-auto{flex:auto}.dark .dark\:flex-none{flex:none}.dark .dark\:flex-col{flex-direction:column}.dark .dark\:flex-row{flex-direction:row}.dark .dark\:flex-wrap{flex-wrap:wrap}.dark .dark\:items-center{align-items:center}.dark .dark\:items-end{align-items:flex-end}.dark .dark\:items-start{align-items:flex-start}.dark .dark\:items-stretch{align-items:stretch}.dark .dark\:justify-around{justify-content:space-around}.dark .dark\:justify-between{justify-content:space-between}.dark .dark\:justify-center{justify-content:center}.dark .dark\:justify-end{justify-content:flex-end}.dark .dark\:justify-start{justify-content:flex-start}:where(.dark .dark\:divide-gray-50>:not(:last-child)){border-color:var(--color-gray-50)}:where(.dark .dark\:divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.dark .dark\:divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}:where(.dark .dark\:divide-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}:where(.dark .dark\:divide-gray-400>:not(:last-child)){border-color:var(--color-gray-400)}:where(.dark .dark\:divide-gray-500>:not(:last-child)){border-color:var(--color-gray-500)}:where(.dark .dark\:divide-gray-600>:not(:last-child)){border-color:var(--color-gray-600)}:where(.dark .dark\:divide-gray-700>:not(:last-child)){border-color:var(--color-gray-700)}:where(.dark .dark\:divide-gray-800>:not(:last-child)){border-color:var(--color-gray-800)}:where(.dark .dark\:divide-gray-900>:not(:last-child)){border-color:var(--color-gray-900)}:where(.dark .dark\:divide-purple-800>:not(:last-child)){border-color:var(--color-purple-800)}:where(.dark .dark\:divide-slate-700>:not(:last-child)){border-color:var(--color-slate-700)}.dark .dark\:self-center{align-self:center}.dark .dark\:self-end{align-self:flex-end}.dark .dark\:self-start{align-self:flex-start}.dark .dark\:self-stretch{align-self:stretch}.dark .dark\:overflow-auto{overflow:auto}.dark .dark\:overflow-hidden{overflow:hidden}.dark .dark\:overflow-visible{overflow:visible}.dark .dark\:overflow-x-auto{overflow-x:auto}.dark .dark\:overflow-y-auto{overflow-y:auto}.dark .dark\:rounded{border-radius:.25rem}.dark .dark\:rounded-full{border-radius:3.40282e38px}.dark .dark\:rounded-lg{border-radius:var(--radius-lg)}.dark .dark\:rounded-none{border-radius:0}.dark .dark\:rounded-sm{border-radius:var(--radius-sm)}.dark .dark\:rounded-xl{border-radius:var(--radius-xl)}.dark .dark\:border{border-style:var(--tw-border-style);border-width:1px}.dark .dark\:border-0{border-style:var(--tw-border-style);border-width:0}.dark .dark\:border-dashed{--tw-border-style:dashed;border-style:dashed}.dark .dark\:border-dotted{--tw-border-style:dotted;border-style:dotted}.dark .dark\:border-solid{--tw-border-style:solid;border-style:solid}.dark .dark\:border-amber-50{border-color:var(--color-amber-50)}.dark .dark\:border-amber-100{border-color:var(--color-amber-100)}.dark .dark\:border-amber-200{border-color:var(--color-amber-200)}.dark .dark\:border-amber-300{border-color:var(--color-amber-300)}.dark .dark\:border-amber-400{border-color:var(--color-amber-400)}.dark .dark\:border-amber-500{border-color:var(--color-amber-500)}.dark .dark\:border-amber-600{border-color:var(--color-amber-600)}.dark .dark\:border-amber-700{border-color:var(--color-amber-700)}.dark .dark\:border-amber-800{border-color:var(--color-amber-800)}.dark .dark\:border-amber-900{border-color:var(--color-amber-900)}.dark .dark\:border-black{border-color:var(--color-black)}.dark .dark\:border-blue-50{border-color:var(--color-blue-50)}.dark .dark\:border-blue-100{border-color:var(--color-blue-100)}.dark .dark\:border-blue-200{border-color:var(--color-blue-200)}.dark .dark\:border-blue-300{border-color:var(--color-blue-300)}.dark .dark\:border-blue-400{border-color:var(--color-blue-400)}.dark .dark\:border-blue-500{border-color:var(--color-blue-500)}.dark .dark\:border-blue-600{border-color:var(--color-blue-600)}.dark .dark\:border-blue-700{border-color:var(--color-blue-700)}.dark .dark\:border-blue-800{border-color:var(--color-blue-800)}.dark .dark\:border-blue-900{border-color:var(--color-blue-900)}.dark .dark\:border-gray-50{border-color:var(--color-gray-50)}.dark .dark\:border-gray-100{border-color:var(--color-gray-100)}.dark .dark\:border-gray-200{border-color:var(--color-gray-200)}.dark .dark\:border-gray-300{border-color:var(--color-gray-300)}.dark .dark\:border-gray-400{border-color:var(--color-gray-400)}.dark .dark\:border-gray-500{border-color:var(--color-gray-500)}.dark .dark\:border-gray-600{border-color:var(--color-gray-600)}.dark .dark\:border-gray-700{border-color:var(--color-gray-700)}.dark .dark\:border-gray-800{border-color:var(--color-gray-800)}.dark .dark\:border-gray-900{border-color:var(--color-gray-900)}.dark .dark\:border-gray-950{border-color:var(--color-gray-950)}.dark .dark\:border-green-50{border-color:var(--color-green-50)}.dark .dark\:border-green-100{border-color:var(--color-green-100)}.dark .dark\:border-green-200{border-color:var(--color-green-200)}.dark .dark\:border-green-300{border-color:var(--color-green-300)}.dark .dark\:border-green-400{border-color:var(--color-green-400)}.dark .dark\:border-green-500{border-color:var(--color-green-500)}.dark .dark\:border-green-600{border-color:var(--color-green-600)}.dark .dark\:border-green-700{border-color:var(--color-green-700)}.dark .dark\:border-green-800{border-color:var(--color-green-800)}.dark .dark\:border-green-900{border-color:var(--color-green-900)}.dark .dark\:border-indigo-400{border-color:var(--color-indigo-400)}.dark .dark\:border-orange-50{border-color:var(--color-orange-50)}.dark .dark\:border-orange-100{border-color:var(--color-orange-100)}.dark .dark\:border-orange-200{border-color:var(--color-orange-200)}.dark .dark\:border-orange-300{border-color:var(--color-orange-300)}.dark .dark\:border-orange-400{border-color:var(--color-orange-400)}.dark .dark\:border-orange-500{border-color:var(--color-orange-500)}.dark .dark\:border-orange-600{border-color:var(--color-orange-600)}.dark .dark\:border-orange-700{border-color:var(--color-orange-700)}.dark .dark\:border-orange-800{border-color:var(--color-orange-800)}.dark .dark\:border-orange-900{border-color:var(--color-orange-900)}.dark .dark\:border-pink-50{border-color:var(--color-pink-50)}.dark .dark\:border-pink-100{border-color:var(--color-pink-100)}.dark .dark\:border-pink-200{border-color:var(--color-pink-200)}.dark .dark\:border-pink-300{border-color:var(--color-pink-300)}.dark .dark\:border-pink-400{border-color:var(--color-pink-400)}.dark .dark\:border-pink-500{border-color:var(--color-pink-500)}.dark .dark\:border-pink-600{border-color:var(--color-pink-600)}.dark .dark\:border-pink-700{border-color:var(--color-pink-700)}.dark .dark\:border-pink-800{border-color:var(--color-pink-800)}.dark .dark\:border-pink-900{border-color:var(--color-pink-900)}.dark .dark\:border-purple-50{border-color:var(--color-purple-50)}.dark .dark\:border-purple-100{border-color:var(--color-purple-100)}.dark .dark\:border-purple-200{border-color:var(--color-purple-200)}.dark .dark\:border-purple-300{border-color:var(--color-purple-300)}.dark .dark\:border-purple-400{border-color:var(--color-purple-400)}.dark .dark\:border-purple-500{border-color:var(--color-purple-500)}.dark .dark\:border-purple-600{border-color:var(--color-purple-600)}.dark .dark\:border-purple-700{border-color:var(--color-purple-700)}.dark .dark\:border-purple-800{border-color:var(--color-purple-800)}.dark .dark\:border-purple-900{border-color:var(--color-purple-900)}.dark .dark\:border-red-50{border-color:var(--color-red-50)}.dark .dark\:border-red-100{border-color:var(--color-red-100)}.dark .dark\:border-red-200{border-color:var(--color-red-200)}.dark .dark\:border-red-300{border-color:var(--color-red-300)}.dark .dark\:border-red-400{border-color:var(--color-red-400)}.dark .dark\:border-red-500{border-color:var(--color-red-500)}.dark .dark\:border-red-600{border-color:var(--color-red-600)}.dark .dark\:border-red-700{border-color:var(--color-red-700)}.dark .dark\:border-red-800{border-color:var(--color-red-800)}.dark .dark\:border-red-900{border-color:var(--color-red-900)}.dark .dark\:border-slate-50{border-color:var(--color-slate-50)}.dark .dark\:border-slate-100{border-color:var(--color-slate-100)}.dark .dark\:border-slate-200{border-color:var(--color-slate-200)}.dark .dark\:border-slate-300{border-color:var(--color-slate-300)}.dark .dark\:border-slate-400{border-color:var(--color-slate-400)}.dark .dark\:border-slate-500{border-color:var(--color-slate-500)}.dark .dark\:border-slate-600{border-color:var(--color-slate-600)}.dark .dark\:border-slate-700{border-color:var(--color-slate-700)}.dark .dark\:border-slate-800{border-color:var(--color-slate-800)}.dark .dark\:border-slate-900{border-color:var(--color-slate-900)}.dark .dark\:border-slate-950{border-color:var(--color-slate-950)}.dark .dark\:border-transparent{border-color:#0000}.dark .dark\:border-white{border-color:var(--color-white)}.dark .dark\:border-yellow-50{border-color:var(--color-yellow-50)}.dark .dark\:border-yellow-100{border-color:var(--color-yellow-100)}.dark .dark\:border-yellow-200{border-color:var(--color-yellow-200)}.dark .dark\:border-yellow-300{border-color:var(--color-yellow-300)}.dark .dark\:border-yellow-400{border-color:var(--color-yellow-400)}.dark .dark\:border-yellow-500{border-color:var(--color-yellow-500)}.dark .dark\:border-yellow-600{border-color:var(--color-yellow-600)}.dark .dark\:border-yellow-700{border-color:var(--color-yellow-700)}.dark .dark\:border-yellow-800{border-color:var(--color-yellow-800)}.dark .dark\:border-yellow-900{border-color:var(--color-yellow-900)}.dark .dark\:bg-amber-50{background-color:var(--color-amber-50)}.dark .dark\:bg-amber-100{background-color:var(--color-amber-100)}.dark .dark\:bg-amber-200{background-color:var(--color-amber-200)}.dark .dark\:bg-amber-300{background-color:var(--color-amber-300)}.dark .dark\:bg-amber-400{background-color:var(--color-amber-400)}.dark .dark\:bg-amber-500{background-color:var(--color-amber-500)}.dark .dark\:bg-amber-600{background-color:var(--color-amber-600)}.dark .dark\:bg-amber-700{background-color:var(--color-amber-700)}.dark .dark\:bg-amber-800{background-color:var(--color-amber-800)}.dark .dark\:bg-amber-900{background-color:var(--color-amber-900)}.dark .dark\:bg-amber-900\/20{background-color:#7b330633}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/20{background-color:color-mix(in oklab,var(--color-amber-900) 20%,transparent)}}.dark .dark\:bg-amber-900\/30{background-color:#7b33064d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/30{background-color:color-mix(in oklab,var(--color-amber-900) 30%,transparent)}}.dark .dark\:bg-amber-900\/40{background-color:#7b330666}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-amber-900\/40{background-color:color-mix(in oklab,var(--color-amber-900) 40%,transparent)}}.dark .dark\:bg-amber-950{background-color:var(--color-amber-950)}.dark .dark\:bg-black{background-color:var(--color-black)}.dark .dark\:bg-blue-50{background-color:var(--color-blue-50)}.dark .dark\:bg-blue-100{background-color:var(--color-blue-100)}.dark .dark\:bg-blue-200{background-color:var(--color-blue-200)}.dark .dark\:bg-blue-300{background-color:var(--color-blue-300)}.dark .dark\:bg-blue-400{background-color:var(--color-blue-400)}.dark .dark\:bg-blue-500{background-color:var(--color-blue-500)}.dark .dark\:bg-blue-600{background-color:var(--color-blue-600)}.dark .dark\:bg-blue-700{background-color:var(--color-blue-700)}.dark .dark\:bg-blue-800{background-color:var(--color-blue-800)}.dark .dark\:bg-blue-900{background-color:var(--color-blue-900)}.dark .dark\:bg-blue-900\/20{background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/20{background-color:color-mix(in oklab,var(--color-blue-900) 20%,transparent)}}.dark .dark\:bg-blue-900\/30{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/30{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .dark\:bg-blue-900\/40{background-color:#1c398e66}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/40{background-color:color-mix(in oklab,var(--color-blue-900) 40%,transparent)}}.dark .dark\:bg-blue-900\/50{background-color:#1c398e80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-blue-900\/50{background-color:color-mix(in oklab,var(--color-blue-900) 50%,transparent)}}.dark .dark\:bg-blue-950{background-color:var(--color-blue-950)}.dark .dark\:bg-cyan-50{background-color:var(--color-cyan-50)}.dark .dark\:bg-cyan-100{background-color:var(--color-cyan-100)}.dark .dark\:bg-cyan-200{background-color:var(--color-cyan-200)}.dark .dark\:bg-cyan-300{background-color:var(--color-cyan-300)}.dark .dark\:bg-cyan-400{background-color:var(--color-cyan-400)}.dark .dark\:bg-cyan-500{background-color:var(--color-cyan-500)}.dark .dark\:bg-cyan-600{background-color:var(--color-cyan-600)}.dark .dark\:bg-cyan-700{background-color:var(--color-cyan-700)}.dark .dark\:bg-cyan-800{background-color:var(--color-cyan-800)}.dark .dark\:bg-cyan-900{background-color:var(--color-cyan-900)}.dark .dark\:bg-cyan-900\/30{background-color:#104e644d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-cyan-900\/30{background-color:color-mix(in oklab,var(--color-cyan-900) 30%,transparent)}}.dark .dark\:bg-cyan-950{background-color:var(--color-cyan-950)}.dark .dark\:bg-emerald-50{background-color:var(--color-emerald-50)}.dark .dark\:bg-emerald-100{background-color:var(--color-emerald-100)}.dark .dark\:bg-emerald-200{background-color:var(--color-emerald-200)}.dark .dark\:bg-emerald-300{background-color:var(--color-emerald-300)}.dark .dark\:bg-emerald-400{background-color:var(--color-emerald-400)}.dark .dark\:bg-emerald-500{background-color:var(--color-emerald-500)}.dark .dark\:bg-emerald-600{background-color:var(--color-emerald-600)}.dark .dark\:bg-emerald-700{background-color:var(--color-emerald-700)}.dark .dark\:bg-emerald-800{background-color:var(--color-emerald-800)}.dark .dark\:bg-emerald-900{background-color:var(--color-emerald-900)}.dark .dark\:bg-emerald-950{background-color:var(--color-emerald-950)}.dark .dark\:bg-gray-50{background-color:var(--color-gray-50)}.dark .dark\:bg-gray-100{background-color:var(--color-gray-100)}.dark .dark\:bg-gray-200{background-color:var(--color-gray-200)}.dark .dark\:bg-gray-300{background-color:var(--color-gray-300)}.dark .dark\:bg-gray-400{background-color:var(--color-gray-400)}.dark .dark\:bg-gray-500{background-color:var(--color-gray-500)}.dark .dark\:bg-gray-600{background-color:var(--color-gray-600)}.dark .dark\:bg-gray-700{background-color:var(--color-gray-700)}.dark .dark\:bg-gray-700\/30{background-color:#3641534d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-700\/30{background-color:color-mix(in oklab,var(--color-gray-700) 30%,transparent)}}.dark .dark\:bg-gray-700\/50{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-700\/50{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.dark .dark\:bg-gray-800{background-color:var(--color-gray-800)}.dark .dark\:bg-gray-800\/50{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-800\/50{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.dark .dark\:bg-gray-800\/90{background-color:#1e2939e6}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-800\/90{background-color:color-mix(in oklab,var(--color-gray-800) 90%,transparent)}}.dark .dark\:bg-gray-900{background-color:var(--color-gray-900)}.dark .dark\:bg-gray-900\/50{background-color:#10182880}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-gray-900\/50{background-color:color-mix(in oklab,var(--color-gray-900) 50%,transparent)}}.dark .dark\:bg-gray-950{background-color:var(--color-gray-950)}.dark .dark\:bg-green-50{background-color:var(--color-green-50)}.dark .dark\:bg-green-100{background-color:var(--color-green-100)}.dark .dark\:bg-green-200{background-color:var(--color-green-200)}.dark .dark\:bg-green-300{background-color:var(--color-green-300)}.dark .dark\:bg-green-400{background-color:var(--color-green-400)}.dark .dark\:bg-green-500{background-color:var(--color-green-500)}.dark .dark\:bg-green-600{background-color:var(--color-green-600)}.dark .dark\:bg-green-700{background-color:var(--color-green-700)}.dark .dark\:bg-green-800{background-color:var(--color-green-800)}.dark .dark\:bg-green-900{background-color:var(--color-green-900)}.dark .dark\:bg-green-900\/20{background-color:#0d542b33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-green-900\/20{background-color:color-mix(in oklab,var(--color-green-900) 20%,transparent)}}.dark .dark\:bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-green-900\/30{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .dark\:bg-green-950{background-color:var(--color-green-950)}.dark .dark\:bg-indigo-50{background-color:var(--color-indigo-50)}.dark .dark\:bg-indigo-100{background-color:var(--color-indigo-100)}.dark .dark\:bg-indigo-200{background-color:var(--color-indigo-200)}.dark .dark\:bg-indigo-300{background-color:var(--color-indigo-300)}.dark .dark\:bg-indigo-400{background-color:var(--color-indigo-400)}.dark .dark\:bg-indigo-500{background-color:var(--color-indigo-500)}.dark .dark\:bg-indigo-600{background-color:var(--color-indigo-600)}.dark .dark\:bg-indigo-700{background-color:var(--color-indigo-700)}.dark .dark\:bg-indigo-800{background-color:var(--color-indigo-800)}.dark .dark\:bg-indigo-900{background-color:var(--color-indigo-900)}.dark .dark\:bg-indigo-900\/30{background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-indigo-900\/30{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .dark\:bg-indigo-950{background-color:var(--color-indigo-950)}.dark .dark\:bg-orange-50{background-color:var(--color-orange-50)}.dark .dark\:bg-orange-100{background-color:var(--color-orange-100)}.dark .dark\:bg-orange-200{background-color:var(--color-orange-200)}.dark .dark\:bg-orange-300{background-color:var(--color-orange-300)}.dark .dark\:bg-orange-400{background-color:var(--color-orange-400)}.dark .dark\:bg-orange-500{background-color:var(--color-orange-500)}.dark .dark\:bg-orange-600{background-color:var(--color-orange-600)}.dark .dark\:bg-orange-700{background-color:var(--color-orange-700)}.dark .dark\:bg-orange-800{background-color:var(--color-orange-800)}.dark .dark\:bg-orange-900{background-color:var(--color-orange-900)}.dark .dark\:bg-orange-900\/20{background-color:#7e2a0c33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-orange-900\/20{background-color:color-mix(in oklab,var(--color-orange-900) 20%,transparent)}}.dark .dark\:bg-orange-900\/30{background-color:#7e2a0c4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-orange-900\/30{background-color:color-mix(in oklab,var(--color-orange-900) 30%,transparent)}}.dark .dark\:bg-orange-950{background-color:var(--color-orange-950)}.dark .dark\:bg-pink-50{background-color:var(--color-pink-50)}.dark .dark\:bg-pink-100{background-color:var(--color-pink-100)}.dark .dark\:bg-pink-200{background-color:var(--color-pink-200)}.dark .dark\:bg-pink-300{background-color:var(--color-pink-300)}.dark .dark\:bg-pink-400{background-color:var(--color-pink-400)}.dark .dark\:bg-pink-500{background-color:var(--color-pink-500)}.dark .dark\:bg-pink-600{background-color:var(--color-pink-600)}.dark .dark\:bg-pink-700{background-color:var(--color-pink-700)}.dark .dark\:bg-pink-800{background-color:var(--color-pink-800)}.dark .dark\:bg-pink-900{background-color:var(--color-pink-900)}.dark .dark\:bg-pink-950{background-color:var(--color-pink-950)}.dark .dark\:bg-purple-50{background-color:var(--color-purple-50)}.dark .dark\:bg-purple-100{background-color:var(--color-purple-100)}.dark .dark\:bg-purple-200{background-color:var(--color-purple-200)}.dark .dark\:bg-purple-300{background-color:var(--color-purple-300)}.dark .dark\:bg-purple-400{background-color:var(--color-purple-400)}.dark .dark\:bg-purple-500{background-color:var(--color-purple-500)}.dark .dark\:bg-purple-600{background-color:var(--color-purple-600)}.dark .dark\:bg-purple-700{background-color:var(--color-purple-700)}.dark .dark\:bg-purple-800{background-color:var(--color-purple-800)}.dark .dark\:bg-purple-800\/50{background-color:#6e11b080}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-800\/50{background-color:color-mix(in oklab,var(--color-purple-800) 50%,transparent)}}.dark .dark\:bg-purple-900{background-color:var(--color-purple-900)}.dark .dark\:bg-purple-900\/20{background-color:#59168b33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/20{background-color:color-mix(in oklab,var(--color-purple-900) 20%,transparent)}}.dark .dark\:bg-purple-900\/30{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/30{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.dark .dark\:bg-purple-900\/50{background-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-purple-900\/50{background-color:color-mix(in oklab,var(--color-purple-900) 50%,transparent)}}.dark .dark\:bg-purple-950{background-color:var(--color-purple-950)}.dark .dark\:bg-red-50{background-color:var(--color-red-50)}.dark .dark\:bg-red-100{background-color:var(--color-red-100)}.dark .dark\:bg-red-200{background-color:var(--color-red-200)}.dark .dark\:bg-red-300{background-color:var(--color-red-300)}.dark .dark\:bg-red-400{background-color:var(--color-red-400)}.dark .dark\:bg-red-500{background-color:var(--color-red-500)}.dark .dark\:bg-red-600{background-color:var(--color-red-600)}.dark .dark\:bg-red-700{background-color:var(--color-red-700)}.dark .dark\:bg-red-800{background-color:var(--color-red-800)}.dark .dark\:bg-red-900{background-color:var(--color-red-900)}.dark .dark\:bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-red-900\/20{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .dark\:bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .dark\:bg-red-950{background-color:var(--color-red-950)}.dark .dark\:bg-rose-50{background-color:var(--color-rose-50)}.dark .dark\:bg-rose-100{background-color:var(--color-rose-100)}.dark .dark\:bg-rose-200{background-color:var(--color-rose-200)}.dark .dark\:bg-rose-300{background-color:var(--color-rose-300)}.dark .dark\:bg-rose-400{background-color:var(--color-rose-400)}.dark .dark\:bg-rose-500{background-color:var(--color-rose-500)}.dark .dark\:bg-rose-600{background-color:var(--color-rose-600)}.dark .dark\:bg-rose-700{background-color:var(--color-rose-700)}.dark .dark\:bg-rose-800{background-color:var(--color-rose-800)}.dark .dark\:bg-rose-900{background-color:var(--color-rose-900)}.dark .dark\:bg-rose-950{background-color:var(--color-rose-950)}.dark .dark\:bg-slate-50{background-color:var(--color-slate-50)}.dark .dark\:bg-slate-100{background-color:var(--color-slate-100)}.dark .dark\:bg-slate-200{background-color:var(--color-slate-200)}.dark .dark\:bg-slate-300{background-color:var(--color-slate-300)}.dark .dark\:bg-slate-400{background-color:var(--color-slate-400)}.dark .dark\:bg-slate-500{background-color:var(--color-slate-500)}.dark .dark\:bg-slate-600{background-color:var(--color-slate-600)}.dark .dark\:bg-slate-700{background-color:var(--color-slate-700)}.dark .dark\:bg-slate-700\/50{background-color:#31415880}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-slate-700\/50{background-color:color-mix(in oklab,var(--color-slate-700) 50%,transparent)}}.dark .dark\:bg-slate-800{background-color:var(--color-slate-800)}.dark .dark\:bg-slate-900{background-color:var(--color-slate-900)}.dark .dark\:bg-slate-950{background-color:var(--color-slate-950)}.dark .dark\:bg-teal-50{background-color:var(--color-teal-50)}.dark .dark\:bg-teal-100{background-color:var(--color-teal-100)}.dark .dark\:bg-teal-200{background-color:var(--color-teal-200)}.dark .dark\:bg-teal-300{background-color:var(--color-teal-300)}.dark .dark\:bg-teal-400{background-color:var(--color-teal-400)}.dark .dark\:bg-teal-500{background-color:var(--color-teal-500)}.dark .dark\:bg-teal-600{background-color:var(--color-teal-600)}.dark .dark\:bg-teal-700{background-color:var(--color-teal-700)}.dark .dark\:bg-teal-800{background-color:var(--color-teal-800)}.dark .dark\:bg-teal-900{background-color:var(--color-teal-900)}.dark .dark\:bg-teal-900\/30{background-color:#0b4f4a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-teal-900\/30{background-color:color-mix(in oklab,var(--color-teal-900) 30%,transparent)}}.dark .dark\:bg-teal-950{background-color:var(--color-teal-950)}.dark .dark\:bg-white{background-color:var(--color-white)}.dark .dark\:bg-yellow-50{background-color:var(--color-yellow-50)}.dark .dark\:bg-yellow-100{background-color:var(--color-yellow-100)}.dark .dark\:bg-yellow-200{background-color:var(--color-yellow-200)}.dark .dark\:bg-yellow-300{background-color:var(--color-yellow-300)}.dark .dark\:bg-yellow-400{background-color:var(--color-yellow-400)}.dark .dark\:bg-yellow-500{background-color:var(--color-yellow-500)}.dark .dark\:bg-yellow-600{background-color:var(--color-yellow-600)}.dark .dark\:bg-yellow-700{background-color:var(--color-yellow-700)}.dark .dark\:bg-yellow-800{background-color:var(--color-yellow-800)}.dark .dark\:bg-yellow-900{background-color:var(--color-yellow-900)}.dark .dark\:bg-yellow-900\/20{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/20{background-color:color-mix(in oklab,var(--color-yellow-900) 20%,transparent)}}.dark .dark\:bg-yellow-900\/30{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/30{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .dark\:bg-yellow-900\/40{background-color:#733e0a66}@supports (color:color-mix(in lab,red,red)){.dark .dark\:bg-yellow-900\/40{background-color:color-mix(in oklab,var(--color-yellow-900) 40%,transparent)}}.dark .dark\:bg-yellow-950{background-color:var(--color-yellow-950)}.dark .dark\:from-blue-900{--tw-gradient-from:var(--color-blue-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-50{--tw-gradient-from:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-100{--tw-gradient-from:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-200{--tw-gradient-from:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-300{--tw-gradient-from:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-400{--tw-gradient-from:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-500{--tw-gradient-from:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-600{--tw-gradient-from:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-700{--tw-gradient-from:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-800{--tw-gradient-from:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:from-gray-900{--tw-gradient-from:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-50{--tw-gradient-to:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-100{--tw-gradient-to:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-200{--tw-gradient-to:var(--color-gray-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-300{--tw-gradient-to:var(--color-gray-300);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-400{--tw-gradient-to:var(--color-gray-400);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-500{--tw-gradient-to:var(--color-gray-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-600{--tw-gradient-to:var(--color-gray-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-700{--tw-gradient-to:var(--color-gray-700);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-800{--tw-gradient-to:var(--color-gray-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-gray-900{--tw-gradient-to:var(--color-gray-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:to-indigo-900{--tw-gradient-to:var(--color-indigo-900);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.dark .dark\:text-center{text-align:center}.dark .dark\:text-justify{text-align:justify}.dark .dark\:text-left{text-align:left}.dark .dark\:text-right{text-align:right}.dark .dark\:text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.dark .dark\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.dark .dark\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.dark .dark\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.dark .dark\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.dark .dark\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.dark .dark\:font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.dark .dark\:font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.dark .dark\:font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.dark .dark\:whitespace-normal{white-space:normal}.dark .dark\:whitespace-nowrap{white-space:nowrap}.dark .dark\:whitespace-pre-wrap{white-space:pre-wrap}.dark .dark\:text-amber-50{color:var(--color-amber-50)}.dark .dark\:text-amber-100{color:var(--color-amber-100)}.dark .dark\:text-amber-200{color:var(--color-amber-200)}.dark .dark\:text-amber-300{color:var(--color-amber-300)}.dark .dark\:text-amber-400{color:var(--color-amber-400)}.dark .dark\:text-amber-500{color:var(--color-amber-500)}.dark .dark\:text-amber-600{color:var(--color-amber-600)}.dark .dark\:text-amber-700{color:var(--color-amber-700)}.dark .dark\:text-amber-800{color:var(--color-amber-800)}.dark .dark\:text-amber-900{color:var(--color-amber-900)}.dark .dark\:text-black{color:var(--color-black)}.dark .dark\:text-blue-50{color:var(--color-blue-50)}.dark .dark\:text-blue-100{color:var(--color-blue-100)}.dark .dark\:text-blue-200{color:var(--color-blue-200)}.dark .dark\:text-blue-300{color:var(--color-blue-300)}.dark .dark\:text-blue-400{color:var(--color-blue-400)}.dark .dark\:text-blue-500{color:var(--color-blue-500)}.dark .dark\:text-blue-600{color:var(--color-blue-600)}.dark .dark\:text-blue-700{color:var(--color-blue-700)}.dark .dark\:text-blue-800{color:var(--color-blue-800)}.dark .dark\:text-blue-900{color:var(--color-blue-900)}.dark .dark\:text-cyan-50{color:var(--color-cyan-50)}.dark .dark\:text-cyan-100{color:var(--color-cyan-100)}.dark .dark\:text-cyan-200{color:var(--color-cyan-200)}.dark .dark\:text-cyan-300{color:var(--color-cyan-300)}.dark .dark\:text-cyan-400{color:var(--color-cyan-400)}.dark .dark\:text-cyan-500{color:var(--color-cyan-500)}.dark .dark\:text-cyan-600{color:var(--color-cyan-600)}.dark .dark\:text-cyan-700{color:var(--color-cyan-700)}.dark .dark\:text-cyan-800{color:var(--color-cyan-800)}.dark .dark\:text-cyan-900{color:var(--color-cyan-900)}.dark .dark\:text-emerald-50{color:var(--color-emerald-50)}.dark .dark\:text-emerald-100{color:var(--color-emerald-100)}.dark .dark\:text-emerald-200{color:var(--color-emerald-200)}.dark .dark\:text-emerald-300{color:var(--color-emerald-300)}.dark .dark\:text-emerald-400{color:var(--color-emerald-400)}.dark .dark\:text-emerald-500{color:var(--color-emerald-500)}.dark .dark\:text-emerald-600{color:var(--color-emerald-600)}.dark .dark\:text-emerald-700{color:var(--color-emerald-700)}.dark .dark\:text-emerald-800{color:var(--color-emerald-800)}.dark .dark\:text-emerald-900{color:var(--color-emerald-900)}.dark .dark\:text-gray-50{color:var(--color-gray-50)}.dark .dark\:text-gray-100{color:var(--color-gray-100)}.dark .dark\:text-gray-200{color:var(--color-gray-200)}.dark .dark\:text-gray-300{color:var(--color-gray-300)}.dark .dark\:text-gray-400{color:var(--color-gray-400)}.dark .dark\:text-gray-500{color:var(--color-gray-500)}.dark .dark\:text-gray-600{color:var(--color-gray-600)}.dark .dark\:text-gray-700{color:var(--color-gray-700)}.dark .dark\:text-gray-800{color:var(--color-gray-800)}.dark .dark\:text-gray-900{color:var(--color-gray-900)}.dark .dark\:text-green-50{color:var(--color-green-50)}.dark .dark\:text-green-100{color:var(--color-green-100)}.dark .dark\:text-green-200{color:var(--color-green-200)}.dark .dark\:text-green-300{color:var(--color-green-300)}.dark .dark\:text-green-400{color:var(--color-green-400)}.dark .dark\:text-green-500{color:var(--color-green-500)}.dark .dark\:text-green-600{color:var(--color-green-600)}.dark .dark\:text-green-700{color:var(--color-green-700)}.dark .dark\:text-green-800{color:var(--color-green-800)}.dark .dark\:text-green-900{color:var(--color-green-900)}.dark .dark\:text-indigo-50{color:var(--color-indigo-50)}.dark .dark\:text-indigo-100{color:var(--color-indigo-100)}.dark .dark\:text-indigo-200{color:var(--color-indigo-200)}.dark .dark\:text-indigo-300{color:var(--color-indigo-300)}.dark .dark\:text-indigo-400{color:var(--color-indigo-400)}.dark .dark\:text-indigo-500{color:var(--color-indigo-500)}.dark .dark\:text-indigo-600{color:var(--color-indigo-600)}.dark .dark\:text-indigo-700{color:var(--color-indigo-700)}.dark .dark\:text-indigo-800{color:var(--color-indigo-800)}.dark .dark\:text-indigo-900{color:var(--color-indigo-900)}.dark .dark\:text-orange-50{color:var(--color-orange-50)}.dark .dark\:text-orange-100{color:var(--color-orange-100)}.dark .dark\:text-orange-200{color:var(--color-orange-200)}.dark .dark\:text-orange-300{color:var(--color-orange-300)}.dark .dark\:text-orange-400{color:var(--color-orange-400)}.dark .dark\:text-orange-500{color:var(--color-orange-500)}.dark .dark\:text-orange-600{color:var(--color-orange-600)}.dark .dark\:text-orange-700{color:var(--color-orange-700)}.dark .dark\:text-orange-800{color:var(--color-orange-800)}.dark .dark\:text-orange-900{color:var(--color-orange-900)}.dark .dark\:text-pink-50{color:var(--color-pink-50)}.dark .dark\:text-pink-100{color:var(--color-pink-100)}.dark .dark\:text-pink-200{color:var(--color-pink-200)}.dark .dark\:text-pink-300{color:var(--color-pink-300)}.dark .dark\:text-pink-400{color:var(--color-pink-400)}.dark .dark\:text-pink-500{color:var(--color-pink-500)}.dark .dark\:text-pink-600{color:var(--color-pink-600)}.dark .dark\:text-pink-700{color:var(--color-pink-700)}.dark .dark\:text-pink-800{color:var(--color-pink-800)}.dark .dark\:text-pink-900{color:var(--color-pink-900)}.dark .dark\:text-purple-50{color:var(--color-purple-50)}.dark .dark\:text-purple-100{color:var(--color-purple-100)}.dark .dark\:text-purple-200{color:var(--color-purple-200)}.dark .dark\:text-purple-300{color:var(--color-purple-300)}.dark .dark\:text-purple-400{color:var(--color-purple-400)}.dark .dark\:text-purple-500{color:var(--color-purple-500)}.dark .dark\:text-purple-600{color:var(--color-purple-600)}.dark .dark\:text-purple-700{color:var(--color-purple-700)}.dark .dark\:text-purple-800{color:var(--color-purple-800)}.dark .dark\:text-purple-900{color:var(--color-purple-900)}.dark .dark\:text-red-50{color:var(--color-red-50)}.dark .dark\:text-red-100{color:var(--color-red-100)}.dark .dark\:text-red-200{color:var(--color-red-200)}.dark .dark\:text-red-300{color:var(--color-red-300)}.dark .dark\:text-red-400{color:var(--color-red-400)}.dark .dark\:text-red-500{color:var(--color-red-500)}.dark .dark\:text-red-600{color:var(--color-red-600)}.dark .dark\:text-red-700{color:var(--color-red-700)}.dark .dark\:text-red-800{color:var(--color-red-800)}.dark .dark\:text-red-900{color:var(--color-red-900)}.dark .dark\:text-slate-50{color:var(--color-slate-50)}.dark .dark\:text-slate-100{color:var(--color-slate-100)}.dark .dark\:text-slate-200{color:var(--color-slate-200)}.dark .dark\:text-slate-300{color:var(--color-slate-300)}.dark .dark\:text-slate-400{color:var(--color-slate-400)}.dark .dark\:text-slate-500{color:var(--color-slate-500)}.dark .dark\:text-slate-600{color:var(--color-slate-600)}.dark .dark\:text-slate-700{color:var(--color-slate-700)}.dark .dark\:text-slate-800{color:var(--color-slate-800)}.dark .dark\:text-slate-900{color:var(--color-slate-900)}.dark .dark\:text-teal-50{color:var(--color-teal-50)}.dark .dark\:text-teal-100{color:var(--color-teal-100)}.dark .dark\:text-teal-200{color:var(--color-teal-200)}.dark .dark\:text-teal-300{color:var(--color-teal-300)}.dark .dark\:text-teal-400{color:var(--color-teal-400)}.dark .dark\:text-teal-500{color:var(--color-teal-500)}.dark .dark\:text-teal-600{color:var(--color-teal-600)}.dark .dark\:text-teal-700{color:var(--color-teal-700)}.dark .dark\:text-teal-800{color:var(--color-teal-800)}.dark .dark\:text-teal-900{color:var(--color-teal-900)}.dark .dark\:text-white{color:var(--color-white)}.dark .dark\:text-yellow-50{color:var(--color-yellow-50)}.dark .dark\:text-yellow-100{color:var(--color-yellow-100)}.dark .dark\:text-yellow-200{color:var(--color-yellow-200)}.dark .dark\:text-yellow-300{color:var(--color-yellow-300)}.dark .dark\:text-yellow-400{color:var(--color-yellow-400)}.dark .dark\:text-yellow-500{color:var(--color-yellow-500)}.dark .dark\:text-yellow-600{color:var(--color-yellow-600)}.dark .dark\:text-yellow-700{color:var(--color-yellow-700)}.dark .dark\:text-yellow-800{color:var(--color-yellow-800)}.dark .dark\:text-yellow-900{color:var(--color-yellow-900)}.dark .dark\:italic{font-style:italic}.dark .dark\:underline{text-decoration-line:underline}.dark .dark\:placeholder-gray-400::placeholder{color:var(--color-gray-400)}.dark .dark\:placeholder-gray-500::placeholder{color:var(--color-gray-500)}.dark .dark\:placeholder-gray-600::placeholder{color:var(--color-gray-600)}.dark .dark\:opacity-0{opacity:0}.dark .dark\:opacity-25{opacity:.25}.dark .dark\:opacity-50{opacity:.5}.dark .dark\:opacity-75{opacity:.75}.dark .dark\:opacity-100{opacity:1}.dark .dark\:shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark .dark\:shadow-purple-900\/50{--tw-shadow-color:#59168b80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:shadow-purple-900\/50{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-purple-900) 50%, transparent) var(--tw-shadow-alpha), transparent)}}.dark .dark\:ring-blue-50{--tw-ring-color:var(--color-blue-50)}.dark .dark\:ring-blue-100{--tw-ring-color:var(--color-blue-100)}.dark .dark\:ring-blue-200{--tw-ring-color:var(--color-blue-200)}.dark .dark\:ring-blue-300{--tw-ring-color:var(--color-blue-300)}.dark .dark\:ring-blue-400{--tw-ring-color:var(--color-blue-400)}.dark .dark\:ring-blue-500{--tw-ring-color:var(--color-blue-500)}.dark .dark\:ring-blue-600{--tw-ring-color:var(--color-blue-600)}.dark .dark\:ring-blue-700{--tw-ring-color:var(--color-blue-700)}.dark .dark\:ring-blue-800{--tw-ring-color:var(--color-blue-800)}.dark .dark\:ring-blue-900{--tw-ring-color:var(--color-blue-900)}.dark .dark\:ring-gray-50{--tw-ring-color:var(--color-gray-50)}.dark .dark\:ring-gray-100{--tw-ring-color:var(--color-gray-100)}.dark .dark\:ring-gray-200{--tw-ring-color:var(--color-gray-200)}.dark .dark\:ring-gray-300{--tw-ring-color:var(--color-gray-300)}.dark .dark\:ring-gray-400{--tw-ring-color:var(--color-gray-400)}.dark .dark\:ring-gray-500{--tw-ring-color:var(--color-gray-500)}.dark .dark\:ring-gray-600{--tw-ring-color:var(--color-gray-600)}.dark .dark\:ring-gray-700{--tw-ring-color:var(--color-gray-700)}.dark .dark\:ring-gray-800{--tw-ring-color:var(--color-gray-800)}.dark .dark\:ring-gray-900{--tw-ring-color:var(--color-gray-900)}.dark .dark\:ring-offset-gray-800{--tw-ring-offset-color:var(--color-gray-800)}.dark .dark\:ring-offset-gray-900{--tw-ring-offset-color:var(--color-gray-900)}@media(hover:hover){.dark .dark\:group-hover\:bg-gray-700:is(:where(.group):hover *){background-color:var(--color-gray-700)}.dark .dark\:group-hover\:bg-gray-800:is(:where(.group):hover *){background-color:var(--color-gray-800)}.dark .dark\:group-hover\:text-gray-100:is(:where(.group):hover *){color:var(--color-gray-100)}.dark .dark\:group-hover\:text-white:is(:where(.group):hover *){color:var(--color-white)}}.dark .dark\:selection\:bg-blue-800 ::selection{background-color:var(--color-blue-800)}.dark .dark\:selection\:bg-blue-800::selection{background-color:var(--color-blue-800)}.dark .dark\:selection\:text-blue-100 ::selection{color:var(--color-blue-100)}.dark .dark\:selection\:text-blue-100::selection{color:var(--color-blue-100)}.dark .dark\:odd\:bg-gray-800:nth-child(odd){background-color:var(--color-gray-800)}.dark .dark\:odd\:bg-gray-900:nth-child(odd){background-color:var(--color-gray-900)}.dark .dark\:even\:bg-gray-800:nth-child(2n){background-color:var(--color-gray-800)}.dark .dark\:even\:bg-gray-900:nth-child(2n){background-color:var(--color-gray-900)}@media(hover:hover){.dark .dark\:hover\:border-amber-50:hover{border-color:var(--color-amber-50)}.dark .dark\:hover\:border-amber-100:hover{border-color:var(--color-amber-100)}.dark .dark\:hover\:border-amber-200:hover{border-color:var(--color-amber-200)}.dark .dark\:hover\:border-amber-300:hover{border-color:var(--color-amber-300)}.dark .dark\:hover\:border-amber-400:hover{border-color:var(--color-amber-400)}.dark .dark\:hover\:border-amber-500:hover{border-color:var(--color-amber-500)}.dark .dark\:hover\:border-amber-600:hover{border-color:var(--color-amber-600)}.dark .dark\:hover\:border-amber-700:hover{border-color:var(--color-amber-700)}.dark .dark\:hover\:border-amber-800:hover{border-color:var(--color-amber-800)}.dark .dark\:hover\:border-amber-900:hover{border-color:var(--color-amber-900)}.dark .dark\:hover\:border-blue-50:hover{border-color:var(--color-blue-50)}.dark .dark\:hover\:border-blue-100:hover{border-color:var(--color-blue-100)}.dark .dark\:hover\:border-blue-200:hover{border-color:var(--color-blue-200)}.dark .dark\:hover\:border-blue-300:hover{border-color:var(--color-blue-300)}.dark .dark\:hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.dark .dark\:hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.dark .dark\:hover\:border-blue-600:hover{border-color:var(--color-blue-600)}.dark .dark\:hover\:border-blue-700:hover{border-color:var(--color-blue-700)}.dark .dark\:hover\:border-blue-800:hover{border-color:var(--color-blue-800)}.dark .dark\:hover\:border-blue-900:hover{border-color:var(--color-blue-900)}.dark .dark\:hover\:border-gray-50:hover{border-color:var(--color-gray-50)}.dark .dark\:hover\:border-gray-100:hover{border-color:var(--color-gray-100)}.dark .dark\:hover\:border-gray-200:hover{border-color:var(--color-gray-200)}.dark .dark\:hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.dark .dark\:hover\:border-gray-400:hover{border-color:var(--color-gray-400)}.dark .dark\:hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.dark .dark\:hover\:border-gray-600:hover{border-color:var(--color-gray-600)}.dark .dark\:hover\:border-gray-700:hover{border-color:var(--color-gray-700)}.dark .dark\:hover\:border-gray-800:hover{border-color:var(--color-gray-800)}.dark .dark\:hover\:border-gray-900:hover{border-color:var(--color-gray-900)}.dark .dark\:hover\:bg-amber-50:hover{background-color:var(--color-amber-50)}.dark .dark\:hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.dark .dark\:hover\:bg-amber-200:hover{background-color:var(--color-amber-200)}.dark .dark\:hover\:bg-amber-300:hover{background-color:var(--color-amber-300)}.dark .dark\:hover\:bg-amber-400:hover{background-color:var(--color-amber-400)}.dark .dark\:hover\:bg-amber-500:hover{background-color:var(--color-amber-500)}.dark .dark\:hover\:bg-amber-600:hover{background-color:var(--color-amber-600)}.dark .dark\:hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.dark .dark\:hover\:bg-amber-800:hover{background-color:var(--color-amber-800)}.dark .dark\:hover\:bg-amber-900:hover{background-color:var(--color-amber-900)}.dark .dark\:hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.dark .dark\:hover\:bg-blue-100:hover{background-color:var(--color-blue-100)}.dark .dark\:hover\:bg-blue-200:hover{background-color:var(--color-blue-200)}.dark .dark\:hover\:bg-blue-300:hover{background-color:var(--color-blue-300)}.dark .dark\:hover\:bg-blue-400:hover{background-color:var(--color-blue-400)}.dark .dark\:hover\:bg-blue-500:hover{background-color:var(--color-blue-500)}.dark .dark\:hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.dark .dark\:hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.dark .dark\:hover\:bg-blue-800:hover{background-color:var(--color-blue-800)}.dark .dark\:hover\:bg-blue-900:hover{background-color:var(--color-blue-900)}.dark .dark\:hover\:bg-blue-900\/30:hover{background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-blue-900\/30:hover{background-color:color-mix(in oklab,var(--color-blue-900) 30%,transparent)}}.dark .dark\:hover\:bg-blue-900\/50:hover{background-color:#1c398e80}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-blue-900\/50:hover{background-color:color-mix(in oklab,var(--color-blue-900) 50%,transparent)}}.dark .dark\:hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.dark .dark\:hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.dark .dark\:hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.dark .dark\:hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.dark .dark\:hover\:bg-gray-400:hover{background-color:var(--color-gray-400)}.dark .dark\:hover\:bg-gray-500:hover{background-color:var(--color-gray-500)}.dark .dark\:hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.dark .dark\:hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.dark .dark\:hover\:bg-gray-700\/30:hover{background-color:#3641534d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-700\/30:hover{background-color:color-mix(in oklab,var(--color-gray-700) 30%,transparent)}}.dark .dark\:hover\:bg-gray-700\/50:hover{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-700\/50:hover{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}.dark .dark\:hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}.dark .dark\:hover\:bg-gray-800\/50:hover{background-color:#1e293980}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-gray-800\/50:hover{background-color:color-mix(in oklab,var(--color-gray-800) 50%,transparent)}}.dark .dark\:hover\:bg-gray-900:hover{background-color:var(--color-gray-900)}.dark .dark\:hover\:bg-green-50:hover{background-color:var(--color-green-50)}.dark .dark\:hover\:bg-green-100:hover{background-color:var(--color-green-100)}.dark .dark\:hover\:bg-green-200:hover{background-color:var(--color-green-200)}.dark .dark\:hover\:bg-green-300:hover{background-color:var(--color-green-300)}.dark .dark\:hover\:bg-green-400:hover{background-color:var(--color-green-400)}.dark .dark\:hover\:bg-green-500:hover{background-color:var(--color-green-500)}.dark .dark\:hover\:bg-green-600:hover{background-color:var(--color-green-600)}.dark .dark\:hover\:bg-green-700:hover{background-color:var(--color-green-700)}.dark .dark\:hover\:bg-green-800:hover{background-color:var(--color-green-800)}.dark .dark\:hover\:bg-green-900:hover{background-color:var(--color-green-900)}.dark .dark\:hover\:bg-green-900\/30:hover{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-green-900\/30:hover{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.dark .dark\:hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.dark .dark\:hover\:bg-indigo-100:hover{background-color:var(--color-indigo-100)}.dark .dark\:hover\:bg-indigo-200:hover{background-color:var(--color-indigo-200)}.dark .dark\:hover\:bg-indigo-300:hover{background-color:var(--color-indigo-300)}.dark .dark\:hover\:bg-indigo-400:hover{background-color:var(--color-indigo-400)}.dark .dark\:hover\:bg-indigo-500:hover{background-color:var(--color-indigo-500)}.dark .dark\:hover\:bg-indigo-600:hover{background-color:var(--color-indigo-600)}.dark .dark\:hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.dark .dark\:hover\:bg-indigo-800:hover{background-color:var(--color-indigo-800)}.dark .dark\:hover\:bg-indigo-900:hover{background-color:var(--color-indigo-900)}.dark .dark\:hover\:bg-indigo-900\/30:hover{background-color:#312c854d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-indigo-900\/30:hover{background-color:color-mix(in oklab,var(--color-indigo-900) 30%,transparent)}}.dark .dark\:hover\:bg-orange-50:hover{background-color:var(--color-orange-50)}.dark .dark\:hover\:bg-orange-100:hover{background-color:var(--color-orange-100)}.dark .dark\:hover\:bg-orange-200:hover{background-color:var(--color-orange-200)}.dark .dark\:hover\:bg-orange-300:hover{background-color:var(--color-orange-300)}.dark .dark\:hover\:bg-orange-400:hover{background-color:var(--color-orange-400)}.dark .dark\:hover\:bg-orange-500:hover{background-color:var(--color-orange-500)}.dark .dark\:hover\:bg-orange-600:hover{background-color:var(--color-orange-600)}.dark .dark\:hover\:bg-orange-700:hover{background-color:var(--color-orange-700)}.dark .dark\:hover\:bg-orange-800:hover{background-color:var(--color-orange-800)}.dark .dark\:hover\:bg-orange-900:hover{background-color:var(--color-orange-900)}.dark .dark\:hover\:bg-orange-900\/30:hover{background-color:#7e2a0c4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-orange-900\/30:hover{background-color:color-mix(in oklab,var(--color-orange-900) 30%,transparent)}}.dark .dark\:hover\:bg-pink-50:hover{background-color:var(--color-pink-50)}.dark .dark\:hover\:bg-pink-100:hover{background-color:var(--color-pink-100)}.dark .dark\:hover\:bg-pink-200:hover{background-color:var(--color-pink-200)}.dark .dark\:hover\:bg-pink-300:hover{background-color:var(--color-pink-300)}.dark .dark\:hover\:bg-pink-400:hover{background-color:var(--color-pink-400)}.dark .dark\:hover\:bg-pink-500:hover{background-color:var(--color-pink-500)}.dark .dark\:hover\:bg-pink-600:hover{background-color:var(--color-pink-600)}.dark .dark\:hover\:bg-pink-700:hover{background-color:var(--color-pink-700)}.dark .dark\:hover\:bg-pink-800:hover{background-color:var(--color-pink-800)}.dark .dark\:hover\:bg-pink-900:hover{background-color:var(--color-pink-900)}.dark .dark\:hover\:bg-purple-50:hover{background-color:var(--color-purple-50)}.dark .dark\:hover\:bg-purple-100:hover{background-color:var(--color-purple-100)}.dark .dark\:hover\:bg-purple-200:hover{background-color:var(--color-purple-200)}.dark .dark\:hover\:bg-purple-300:hover{background-color:var(--color-purple-300)}.dark .dark\:hover\:bg-purple-400:hover{background-color:var(--color-purple-400)}.dark .dark\:hover\:bg-purple-500:hover{background-color:var(--color-purple-500)}.dark .dark\:hover\:bg-purple-600:hover{background-color:var(--color-purple-600)}.dark .dark\:hover\:bg-purple-700:hover{background-color:var(--color-purple-700)}.dark .dark\:hover\:bg-purple-800:hover{background-color:var(--color-purple-800)}.dark .dark\:hover\:bg-purple-900:hover{background-color:var(--color-purple-900)}.dark .dark\:hover\:bg-purple-900\/30:hover{background-color:#59168b4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-purple-900\/30:hover{background-color:color-mix(in oklab,var(--color-purple-900) 30%,transparent)}}.dark .dark\:hover\:bg-red-50:hover{background-color:var(--color-red-50)}.dark .dark\:hover\:bg-red-100:hover{background-color:var(--color-red-100)}.dark .dark\:hover\:bg-red-200:hover{background-color:var(--color-red-200)}.dark .dark\:hover\:bg-red-300:hover{background-color:var(--color-red-300)}.dark .dark\:hover\:bg-red-400:hover{background-color:var(--color-red-400)}.dark .dark\:hover\:bg-red-500:hover{background-color:var(--color-red-500)}.dark .dark\:hover\:bg-red-600:hover{background-color:var(--color-red-600)}.dark .dark\:hover\:bg-red-700:hover{background-color:var(--color-red-700)}.dark .dark\:hover\:bg-red-800:hover{background-color:var(--color-red-800)}.dark .dark\:hover\:bg-red-900:hover{background-color:var(--color-red-900)}.dark .dark\:hover\:bg-red-900\/20:hover{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-red-900\/20:hover{background-color:color-mix(in oklab,var(--color-red-900) 20%,transparent)}}.dark .dark\:hover\:bg-red-900\/30:hover{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-red-900\/30:hover{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.dark .dark\:hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.dark .dark\:hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.dark .dark\:hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.dark .dark\:hover\:bg-slate-300:hover{background-color:var(--color-slate-300)}.dark .dark\:hover\:bg-slate-400:hover{background-color:var(--color-slate-400)}.dark .dark\:hover\:bg-slate-500:hover{background-color:var(--color-slate-500)}.dark .dark\:hover\:bg-slate-600:hover{background-color:var(--color-slate-600)}.dark .dark\:hover\:bg-slate-700:hover{background-color:var(--color-slate-700)}.dark .dark\:hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.dark .dark\:hover\:bg-slate-900:hover{background-color:var(--color-slate-900)}.dark .dark\:hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white) 10%,transparent)}}.dark .dark\:hover\:bg-yellow-500:hover{background-color:var(--color-yellow-500)}.dark .dark\:hover\:bg-yellow-800:hover{background-color:var(--color-yellow-800)}.dark .dark\:hover\:bg-yellow-900\/30:hover{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.dark .dark\:hover\:bg-yellow-900\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.dark .dark\:hover\:text-black:hover{color:var(--color-black)}.dark .dark\:hover\:text-blue-50:hover{color:var(--color-blue-50)}.dark .dark\:hover\:text-blue-100:hover{color:var(--color-blue-100)}.dark .dark\:hover\:text-blue-200:hover{color:var(--color-blue-200)}.dark .dark\:hover\:text-blue-300:hover{color:var(--color-blue-300)}.dark .dark\:hover\:text-blue-400:hover{color:var(--color-blue-400)}.dark .dark\:hover\:text-blue-500:hover{color:var(--color-blue-500)}.dark .dark\:hover\:text-blue-600:hover{color:var(--color-blue-600)}.dark .dark\:hover\:text-blue-700:hover{color:var(--color-blue-700)}.dark .dark\:hover\:text-blue-800:hover{color:var(--color-blue-800)}.dark .dark\:hover\:text-blue-900:hover{color:var(--color-blue-900)}.dark .dark\:hover\:text-gray-50:hover{color:var(--color-gray-50)}.dark .dark\:hover\:text-gray-100:hover{color:var(--color-gray-100)}.dark .dark\:hover\:text-gray-200:hover{color:var(--color-gray-200)}.dark .dark\:hover\:text-gray-300:hover{color:var(--color-gray-300)}.dark .dark\:hover\:text-gray-400:hover{color:var(--color-gray-400)}.dark .dark\:hover\:text-gray-500:hover{color:var(--color-gray-500)}.dark .dark\:hover\:text-gray-600:hover{color:var(--color-gray-600)}.dark .dark\:hover\:text-gray-700:hover{color:var(--color-gray-700)}.dark .dark\:hover\:text-gray-800:hover{color:var(--color-gray-800)}.dark .dark\:hover\:text-gray-900:hover{color:var(--color-gray-900)}.dark .dark\:hover\:text-green-50:hover{color:var(--color-green-50)}.dark .dark\:hover\:text-green-100:hover{color:var(--color-green-100)}.dark .dark\:hover\:text-green-200:hover{color:var(--color-green-200)}.dark .dark\:hover\:text-green-300:hover{color:var(--color-green-300)}.dark .dark\:hover\:text-green-400:hover{color:var(--color-green-400)}.dark .dark\:hover\:text-green-500:hover{color:var(--color-green-500)}.dark .dark\:hover\:text-green-600:hover{color:var(--color-green-600)}.dark .dark\:hover\:text-green-700:hover{color:var(--color-green-700)}.dark .dark\:hover\:text-green-800:hover{color:var(--color-green-800)}.dark .dark\:hover\:text-green-900:hover{color:var(--color-green-900)}.dark .dark\:hover\:text-purple-200:hover{color:var(--color-purple-200)}.dark .dark\:hover\:text-purple-300:hover{color:var(--color-purple-300)}.dark .dark\:hover\:text-red-50:hover{color:var(--color-red-50)}.dark .dark\:hover\:text-red-100:hover{color:var(--color-red-100)}.dark .dark\:hover\:text-red-200:hover{color:var(--color-red-200)}.dark .dark\:hover\:text-red-300:hover{color:var(--color-red-300)}.dark .dark\:hover\:text-red-400:hover{color:var(--color-red-400)}.dark .dark\:hover\:text-red-500:hover{color:var(--color-red-500)}.dark .dark\:hover\:text-red-600:hover{color:var(--color-red-600)}.dark .dark\:hover\:text-red-700:hover{color:var(--color-red-700)}.dark .dark\:hover\:text-red-800:hover{color:var(--color-red-800)}.dark .dark\:hover\:text-red-900:hover{color:var(--color-red-900)}.dark .dark\:hover\:text-slate-800:hover{color:var(--color-slate-800)}.dark .dark\:hover\:text-white:hover{color:var(--color-white)}}.dark .dark\:focus\:border-blue-400:focus{border-color:var(--color-blue-400)}.dark .dark\:focus\:border-gray-500:focus{border-color:var(--color-gray-500)}.dark .dark\:focus\:border-gray-600:focus{border-color:var(--color-gray-600)}.dark .dark\:focus\:border-gray-700:focus{border-color:var(--color-gray-700)}.dark .dark\:focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.dark .dark\:focus\:bg-gray-800:focus{background-color:var(--color-gray-800)}.dark .dark\:focus\:bg-gray-900:focus{background-color:var(--color-gray-900)}.dark .dark\:focus\:text-gray-100:focus{color:var(--color-gray-100)}.dark .dark\:focus\:text-gray-200:focus{color:var(--color-gray-200)}.dark .dark\:focus\:text-gray-300:focus{color:var(--color-gray-300)}.dark .dark\:focus\:text-white:focus{color:var(--color-white)}.dark .dark\:focus\:ring-amber-400:focus{--tw-ring-color:var(--color-amber-400)}.dark .dark\:focus\:ring-amber-500:focus{--tw-ring-color:var(--color-amber-500)}.dark .dark\:focus\:ring-amber-600:focus{--tw-ring-color:var(--color-amber-600)}.dark .dark\:focus\:ring-blue-400:focus{--tw-ring-color:var(--color-blue-400)}.dark .dark\:focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.dark .dark\:focus\:ring-blue-600:focus{--tw-ring-color:var(--color-blue-600)}.dark .dark\:focus\:ring-blue-700:focus{--tw-ring-color:var(--color-blue-700)}.dark .dark\:focus\:ring-gray-400:focus{--tw-ring-color:var(--color-gray-400)}.dark .dark\:focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.dark .dark\:focus\:ring-gray-600:focus{--tw-ring-color:var(--color-gray-600)}.dark .dark\:focus\:ring-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.dark .dark\:focus\:ring-indigo-400:focus{--tw-ring-color:var(--color-indigo-400)}.dark .dark\:focus\:ring-red-400:focus{--tw-ring-color:var(--color-red-400)}.dark .dark\:focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.dark .dark\:focus\:ring-slate-500:focus{--tw-ring-color:var(--color-slate-500)}.dark .dark\:focus\:ring-offset-gray-800:focus{--tw-ring-offset-color:var(--color-gray-800)}.dark .dark\:focus\:ring-offset-gray-900:focus{--tw-ring-offset-color:var(--color-gray-900)}.dark .dark\:active\:bg-gray-600:active{background-color:var(--color-gray-600)}.dark .dark\:active\:bg-gray-700:active{background-color:var(--color-gray-700)}.dark .dark\:active\:bg-gray-800:active{background-color:var(--color-gray-800)}.dark .dark\:disabled\:bg-gray-600:disabled{background-color:var(--color-gray-600)}.dark .dark\:disabled\:bg-gray-700:disabled{background-color:var(--color-gray-700)}.dark .dark\:disabled\:bg-gray-800:disabled{background-color:var(--color-gray-800)}.dark .dark\:disabled\:bg-gray-900:disabled{background-color:var(--color-gray-900)}.dark .dark\:disabled\:text-gray-500:disabled{color:var(--color-gray-500)}.dark .dark\:disabled\:text-gray-600:disabled{color:var(--color-gray-600)}@media(hover:hover){.dark .dark\:disabled\:hover\:bg-indigo-500:disabled:hover{background-color:var(--color-indigo-500)}}.\[\&_table\]\:w-full table{width:100%}.\[\&_table\]\:border-collapse table{border-collapse:collapse}.\[\&_table\]\:border table{border-style:var(--tw-border-style);border-width:1px}.\[\&_table\]\:border-gray-300 table{border-color:var(--color-gray-300)}.dark .dark\:\[\&_table\]\:border-gray-600 table{border-color:var(--color-gray-600)}.\[\&_td\]\:border td{border-style:var(--tw-border-style);border-width:1px}.\[\&_td\]\:border-gray-300 td{border-color:var(--color-gray-300)}.\[\&_td\]\:px-4 td{padding-inline:calc(var(--spacing) * 4)}.\[\&_td\]\:py-2 td{padding-block:calc(var(--spacing) * 2)}.dark .dark\:\[\&_td\]\:border-gray-600 td{border-color:var(--color-gray-600)}.\[\&_th\]\:border th{border-style:var(--tw-border-style);border-width:1px}.\[\&_th\]\:border-gray-300 th{border-color:var(--color-gray-300)}.\[\&_th\]\:bg-gray-100 th{background-color:var(--color-gray-100)}.\[\&_th\]\:px-4 th{padding-inline:calc(var(--spacing) * 4)}.\[\&_th\]\:py-2 th{padding-block:calc(var(--spacing) * 2)}.\[\&_th\]\:text-left th{text-align:left}.dark .dark\:\[\&_th\]\:border-gray-600 th{border-color:var(--color-gray-600)}.dark .dark\:\[\&_th\]\:bg-gray-800 th{background-color:var(--color-gray-800)}.\[\&_tr\:hover\]\:bg-gray-50 tr:hover{background-color:var(--color-gray-50)}.dark .dark\:\[\&_tr\:hover\]\:bg-gray-700\/50 tr:hover{background-color:#36415380}@supports (color:color-mix(in lab,red,red)){.dark .dark\:\[\&_tr\:hover\]\:bg-gray-700\/50 tr:hover{background-color:color-mix(in oklab,var(--color-gray-700) 50%,transparent)}}}.dashboard-content{max-width:80rem;margin:0 auto}.dashboard-stats-grid{gap:var(--spacing-5);margin-bottom:var(--spacing-8);grid-template-columns:1fr;display:grid}@media(min-width:768px){.dashboard-stats-grid{grid-template-columns:repeat(2,1fr)}}@media(min-width:1024px){.dashboard-stats-grid{grid-template-columns:repeat(4,1fr)}}.dashboard-main-grid{gap:var(--spacing-6);grid-template-columns:1fr;display:grid}@media(min-width:1024px){.dashboard-main-grid{grid-template-columns:repeat(5,1fr)}}.dashboard-column-left{gap:var(--spacing-6);flex-direction:column;display:flex}@media(min-width:1024px){.dashboard-column-left{grid-column:span 3}}.dashboard-column-right{gap:var(--spacing-6);flex-direction:column;display:flex}@media(min-width:1024px){.dashboard-column-right{grid-column:span 2}}.progress-bar-container{background-color:var(--color-slate-100);border-radius:var(--radius-full);width:100%;height:.5rem;overflow:hidden}.dark .progress-bar-container{background-color:var(--color-slate-700)}.progress-bar-fill{border-radius:var(--radius-full);height:.5rem;transition:width var(--transition-normal) var(--ease-in-out)}.progress-bar-emerald{background-color:var(--color-emerald-500)}.progress-bar-blue{background-color:var(--color-blue-500)}.progress-bar-slate{background-color:var(--color-slate-600)}.dark .progress-bar-slate{background-color:var(--color-slate-500)}.module-list{gap:var(--spacing-3);flex-direction:column;display:flex}.module-item{padding:var(--spacing-2) 0;justify-content:space-between;align-items:center;display:flex}.module-info{flex-direction:column;display:flex}.module-name{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .module-name{color:#fff}.module-version{font-size:var(--text-xs);color:var(--color-slate-500)}.dark .module-version{color:var(--color-slate-400)}.module-badge-active{padding:var(--spacing-1) var(--spacing-2);font-size:var(--text-xs);font-weight:var(--font-medium);background-color:var(--color-green-100);color:var(--color-green-700);border-radius:var(--radius-full)}.dark .module-badge-active{background-color:var(--color-green-900);color:var(--color-green-400)}.module-badge-inactive{padding:var(--spacing-1) var(--spacing-2);font-size:var(--text-xs);font-weight:var(--font-medium);background-color:var(--color-slate-100);color:var(--color-slate-600);border-radius:var(--radius-full)}.dark .module-badge-inactive{background-color:var(--color-slate-700);color:var(--color-slate-400)}.activity-list{gap:var(--spacing-4);flex-direction:column;display:flex}.activity-item{align-items:flex-start;gap:var(--spacing-3);display:flex}.activity-dot{width:.5rem;height:.5rem;margin-top:var(--spacing-2);border-radius:var(--radius-full);background-color:var(--color-blue-500);flex-shrink:0}.activity-content{flex:1;min-width:0}.activity-title{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .activity-title{color:#fff}.activity-desc{font-size:var(--text-sm);color:var(--color-slate-500)}.dark .activity-desc{color:var(--color-slate-400)}.activity-time{font-size:var(--text-xs);color:var(--color-slate-400);white-space:nowrap}.dark .activity-time{color:var(--color-slate-500)}.alert-list{gap:var(--spacing-3);flex-direction:column;display:flex}.alert-item{align-items:flex-start;gap:var(--spacing-3);padding:var(--spacing-3);border-radius:var(--radius-lg);display:flex}.alert-bg-info{background-color:var(--color-blue-50)}.dark .alert-bg-info{background-color:var(--color-slate-800)}.alert-bg-warning{background-color:var(--color-yellow-50)}.dark .alert-bg-warning{background-color:var(--color-slate-800)}.alert-bg-error{background-color:var(--color-red-50)}.dark .alert-bg-error{background-color:var(--color-slate-800)}.alert-bg-success{background-color:var(--color-green-50)}.dark .alert-bg-success{background-color:var(--color-slate-800)}.alert-dot{width:.5rem;height:.5rem;margin-top:var(--spacing-2);border-radius:var(--radius-full);flex-shrink:0}.alert-dot-info{background-color:var(--color-blue-500)}.alert-dot-warning{background-color:var(--color-yellow-500)}.alert-dot-error{background-color:var(--color-red-500)}.alert-dot-success{background-color:var(--color-green-500)}.alert-content{flex:1}.alert-title{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-900)}.dark .alert-title{color:#fff}.alert-time{font-size:var(--text-xs);color:var(--color-slate-500);margin-top:var(--spacing-1)}.dark .alert-time{color:var(--color-slate-400)}.text-subtitle{font-size:var(--text-sm);color:var(--color-slate-500)}.dark .text-subtitle{color:var(--color-slate-400)}.section-margin{margin-bottom:var(--spacing-6)}.flex-between{justify-content:space-between;align-items:center;display:flex}.flex-between-mb{margin-bottom:var(--spacing-2);justify-content:space-between;align-items:center;display:flex}.resource-label{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-700)}.dark .resource-label{color:var(--color-slate-300)}.resource-value{font-size:var(--text-sm);font-weight:var(--font-medium);color:var(--color-slate-700)}.dark .resource-value{color:var(--color-slate-300)}.icon-stat{width:1rem;height:1rem}.icon-trend{width:.875rem;height:.875rem}@media(max-width:640px){.input-split{flex-wrap:wrap}.input-split input{flex:1;min-width:0}.page-header{flex-direction:column;align-items:flex-start;gap:1rem}.page-actions{justify-content:flex-start;width:100%}}@media(min-width:640px){.responsive-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1024px){.responsive-grid,.grid-cols-1-lg-3{grid-template-columns:repeat(3,minmax(0,1fr))}.col-span-1-lg{grid-column:span 1/span 1}.col-span-2-lg{grid-column:span 2/span 2}}@media(max-width:480px){.responsive-grid{grid-template-columns:1fr!important}}@media(max-width:768px){.table-mobile-cards{display:block}.table-mobile-cards>*+*{margin-top:1rem}.table-desktop{display:none!important}}@media(max-width:640px){.modal{max-width:calc(100% - 2rem);max-height:calc(100vh - 2rem);margin-left:1rem;margin-right:1rem;overflow-y:auto}.modal-overlay{align-items:flex-start;padding:2rem 1rem 1rem}.modal-footer{flex-direction:column;gap:.5rem}.modal-footer .btn,.card-account{width:100%}}.sidebar-scrollbar{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .3s}.sidebar-scrollbar:hover{scrollbar-color:#d1d5db transparent}.dark .sidebar-scrollbar:hover{scrollbar-color:#4b5563 transparent}.sidebar-scrollbar::-webkit-scrollbar{width:6px}.sidebar-scrollbar::-webkit-scrollbar-track{background:0 0}.sidebar-scrollbar::-webkit-scrollbar-thumb{background:0 0;border-radius:3px;transition:background .3s}.sidebar-scrollbar:hover::-webkit-scrollbar-thumb{background:#d1d5db}.dark .sidebar-scrollbar:hover::-webkit-scrollbar-thumb{background:#4b5563}.sidebar-scrollbar::-webkit-scrollbar-thumb:hover{background:#9ca3af}.dark .sidebar-scrollbar::-webkit-scrollbar-thumb:hover{background:#6b7280}@property --tw-border-spacing-x{syntax:"";inherits:false;initial-value:0}@property --tw-border-spacing-y{syntax:"";inherits:false;initial-value:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-scroll-snap-strictness{syntax:"*";inherits:false;initial-value:proximity}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.yarl__fullsize{width:100%;height:100%}.yarl__relative{position:relative}.yarl__portal{position:fixed;inset:0;opacity:0;overflow:hidden;z-index:var(--yarl__portal_zindex,9999);transition:opacity var(--yarl__fade_animation_duration,.25s) var(--yarl__fade_animation_timing_function,ease);pointer-events:auto}.yarl__portal_open{opacity:1}.yarl__container{position:absolute;inset:0;overflow:hidden;background-color:var(--yarl__container_background_color,var(--yarl__color_backdrop,#000));outline:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;touch-action:var(--yarl__controller_touch_action,none);overscroll-behavior:var(--yarl__controller_overscroll_behavior,contain)}.yarl__carousel{display:flex;flex:0 0 auto;height:100%;align-content:center;justify-content:center;align-items:stretch;width:calc(100% + (var(--yarl__carousel_slides_count) - 1)*(100% + var(--yarl__carousel_spacing_px, 0)*1px + var(--yarl__carousel_spacing_percent, 0)*1%));transform:translate(var(--yarl__swipe_offset,0),var(--yarl__pull_offset,0));opacity:var(--yarl__pull_opacity,1)}.yarl__carousel_with_slides{-moz-column-gap:calc(var(--yarl__carousel_spacing_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_spacing_percent, 0)*1%);column-gap:calc(var(--yarl__carousel_spacing_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_spacing_percent, 0)*1%)}.yarl__flex_center{display:flex;justify-content:center;align-content:center;align-items:center}.yarl__slide{flex:1;position:relative;overflow:hidden;padding:calc(var(--yarl__carousel_padding_px, 0)*1px + 100/(var(--yarl__carousel_slides_count)*100 + (var(--yarl__carousel_slides_count) - 1)*var(--yarl__carousel_spacing_percent, 0))*var(--yarl__carousel_padding_percent, 0)*1%)}[dir=rtl] .yarl__slide{--yarl__direction:-1}.yarl__slide_image{max-width:100%;max-height:100%;-o-object-fit:contain;object-fit:contain;touch-action:var(--yarl__controller_touch_action,none);-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-touch-callout:none}.yarl__slide_image_cover{width:100%;height:100%;-o-object-fit:cover;object-fit:cover}.yarl__slide_image_loading{opacity:0}@media screen and (min-width:800px){.yarl__slide_wrapper:not(.yarl__slide_wrapper_interactive) .yarl__slide_image{-webkit-transform:translateZ(0);-webkit-backface-visibility:hidden;-webkit-transform-style:preserve-3d}}.yarl__slide_placeholder{position:absolute;top:50%;left:50%;transform:translate(-50%) translateY(-50%);line-height:0}.yarl__slide_loading{color:var(--yarl__slide_icon_loading_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));animation:yarl__delayed_fadein 1s linear}.yarl__slide_loading line{animation:yarl__stroke_opacity 1s linear infinite}.yarl__slide_loading line:first-of-type{animation-delay:-1.875s}.yarl__slide_loading line:nth-of-type(2){animation-delay:-1.75s}.yarl__slide_loading line:nth-of-type(3){animation-delay:-1.625s}.yarl__slide_loading line:nth-of-type(4){animation-delay:-1.5s}.yarl__slide_loading line:nth-of-type(5){animation-delay:-1.375s}.yarl__slide_loading line:nth-of-type(6){animation-delay:-1.25s}.yarl__slide_loading line:nth-of-type(7){animation-delay:-1.125s}.yarl__slide_loading line:nth-of-type(8){animation-delay:-1s}.yarl__slide_error{width:var(--yarl__slide_icon_error_size,48px);height:var(--yarl__slide_icon_error_size,48px);color:var(--yarl__slide_icon_error_color,red)}@media(prefers-reduced-motion){.yarl__portal,.yarl__slide{transition:unset}.yarl__slide_loading,.yarl__slide_loading line{animation:unset}}.yarl__toolbar{position:absolute;inset:0 0 auto auto;display:flex;justify-content:flex-end;padding:var(--yarl__toolbar_padding,8px)}[dir=rtl] .yarl__toolbar{inset:0 auto auto 0}.yarl__icon{width:var(--yarl__icon_size,32px);height:var(--yarl__icon_size,32px)}.yarl__button{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--yarl__button_background_color,transparent);border:var(--yarl__button_border,0);margin:var(--yarl__button_margin,0);outline:none;line-height:0;padding:var(--yarl__button_padding,8px);color:var(--yarl__color_button,hsla(0,0%,100%,.8));filter:var(--yarl__button_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));-webkit-tap-highlight-color:transparent}.yarl__button:focus{color:var(--yarl__color_button_active,#fff)}.yarl__button:focus:not(:focus-visible){color:var(--yarl__color_button,hsla(0,0%,100%,.8))}.yarl__button:focus-visible{color:var(--yarl__color_button_active,#fff)}@media(hover:hover){.yarl__button:focus-visible:hover,.yarl__button:focus:hover,.yarl__button:hover{color:var(--yarl__color_button_active,#fff)}}.yarl__button:disabled{color:var(--yarl__color_button_disabled,hsla(0,0%,100%,.4));cursor:default}.yarl__navigation_next,.yarl__navigation_prev{position:absolute;top:50%;transform:translateY(-50%);padding:var(--yarl__navigation_button_padding,24px 16px)}.yarl__navigation_prev{left:0}[dir=rtl] .yarl__navigation_prev{left:unset;right:0;transform:translateY(-50%) rotate(180deg)}.yarl__navigation_next{right:0}[dir=rtl] .yarl__navigation_next{left:0;right:unset;transform:translateY(-50%) rotate(180deg)}.yarl__no_scroll{height:100%;overflow:hidden;overscroll-behavior:none}@keyframes yarl__delayed_fadein{0%{opacity:0}80%{opacity:0}to{opacity:1}}@keyframes yarl__stroke_opacity{0%{stroke-opacity:1}to{stroke-opacity:.125}}.yarl__counter{position:var(--yarl__counter_position,absolute);top:var(--yarl__counter_top,0);left:var(--yarl__counter_left,0);bottom:var(--yarl__counter_bottom,unset);right:var(--yarl__counter_right,unset);color:var(--yarl__counter_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));filter:var(--yarl__counter_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));margin:var(--yarl__counter_margin,var(--yarl__toolbar_padding,8px));padding:var(--yarl__counter_padding,var(--yarl__button_padding,8px));line-height:var(--yarl__counter_line_height,var(--yarl__icon_size,32px));-webkit-user-select:var(--yarl__counter_user_select,none);-moz-user-select:var(--yarl__counter_user_select,none);user-select:var(--yarl__counter_user_select,none)}[dir=rtl] .yarl__counter{left:var(--yarl__counter_rtl_left,unset);right:var(--yarl__counter_rtl_right,0)}.yarl__thumbnails{display:flex;height:100%}.yarl__thumbnails_bottom,.yarl__thumbnails_end .yarl__thumbnails_track,.yarl__thumbnails_start .yarl__thumbnails_track,.yarl__thumbnails_top{flex-direction:column}.yarl__thumbnails_wrapper{flex:1;position:relative}.yarl__thumbnails_container{flex:0 0 auto;background-color:var(--yarl__thumbnails_container_background_color,var(--yarl__color_backdrop,#000));padding:var(--yarl__thumbnails_container_padding,16px);position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-touch-callout:none}.yarl__thumbnails_vignette{position:absolute;pointer-events:none;--yarl__thumbnails_vignette_size:12%}@media(min-width:1200px){.yarl__thumbnails_vignette{--yarl__thumbnails_vignette_size:8%}}@media(min-width:2000px){.yarl__thumbnails_vignette{--yarl__thumbnails_vignette_size:5%}}.yarl__thumbnails_bottom .yarl__thumbnails_vignette,.yarl__thumbnails_top .yarl__thumbnails_vignette{height:100%;left:0;right:0;background:linear-gradient(to right,var(--yarl__color_backdrop,#000) 0,transparent var(--yarl__thumbnails_vignette_size,12%) calc(100% - var(--yarl__thumbnails_vignette_size, 12%)),var(--yarl__color_backdrop,#000) 100%)}.yarl__thumbnails_end .yarl__thumbnails_vignette,.yarl__thumbnails_start .yarl__thumbnails_vignette{width:100%;top:0;bottom:0;background:linear-gradient(to bottom,var(--yarl__color_backdrop,#000) 0,transparent var(--yarl__thumbnails_vignette_size,12%) calc(100% - var(--yarl__thumbnails_vignette_size, 12%)),var(--yarl__color_backdrop,#000) 100%)}.yarl__thumbnails_track{outline:none;gap:var(--yarl__thumbnails_thumbnail_gap,16px)}.yarl__thumbnails_thumbnail{flex:0 0 auto;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--yarl__thumbnails_thumbnail_background,#000);border-width:var(--yarl__thumbnails_thumbnail_border,1px);border-style:var(--yarl__thumbnails_thumbnail_border_style,solid);border-color:var(--yarl__thumbnails_thumbnail_border_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));border-radius:var(--yarl__thumbnails_thumbnail_border_radius,4px);-webkit-tap-highlight-color:transparent;position:relative;overflow:hidden;padding:var(--yarl__thumbnails_thumbnail_padding,4px);outline:none;width:var(--yarl__thumbnails_thumbnail_width,120px);height:var(--yarl__thumbnails_thumbnail_height,80px);box-sizing:content-box}.yarl__thumbnails_thumbnail_active{border-color:var(--yarl__thumbnails_thumbnail_active_border_color,var(--yarl__color_button_active,#fff))}.yarl__thumbnails_thumbnail_fadein{opacity:0;animation:yarl__thumbnails_thumbnail_fadein var(--yarl__thumbnails_thumbnail_fadein_duration,.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadein_delay,0s) forwards}.yarl__thumbnails_thumbnail_fadeout{animation:yarl__thumbnails_thumbnail_fadeout var(--yarl__thumbnails_thumbnail_fadeout_duration,.5s) ease-in-out var(--yarl__thumbnails_thumbnail_fadeout_delay,0s) forwards;cursor:unset}.yarl__thumbnails_thumbnail_placeholder{visibility:hidden;cursor:unset}.yarl__thumbnails_thumbnail:focus{box-shadow:var(--yarl__thumbnails_thumbnail_focus_box_shadow,#000 0 0 0 2px,var(--yarl__color_button,hsla(0,0%,100%,.8)) 0 0 0 4px)}.yarl__thumbnails_thumbnail:focus:not(:focus-visible){box-shadow:unset}.yarl__thumbnails_thumbnail:focus-visible{box-shadow:var(--yarl__thumbnails_thumbnail_focus_box_shadow,#000 0 0 0 2px,var(--yarl__color_button,hsla(0,0%,100%,.8)) 0 0 0 4px)}.yarl__thumbnails_thumbnail_icon{color:var(--yarl__thumbnails_thumbnail_icon_color,var(--yarl__color_button,hsla(0,0%,100%,.8)));filter:var(--yarl__thumbnails_thumbnail_icon_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));position:absolute;top:50%;left:50%;transform:translate(-50%) translateY(-50%);width:var(--yarl__thumbnails_thumbnail_icon_size,32px);height:var(--yarl__thumbnails_thumbnail_icon_size,32px)}@keyframes yarl__thumbnails_thumbnail_fadein{0%{opacity:0}to{opacity:1}}@keyframes yarl__thumbnails_thumbnail_fadeout{0%{opacity:1}to{opacity:0}} diff --git a/templates/_bundled/sirsoft-admin_basic/dist/js/components.iife.js b/templates/_bundled/sirsoft-admin_basic/dist/js/components.iife.js index 5c7ab7f0..f5ffab62 100644 --- a/templates/_bundled/sirsoft-admin_basic/dist/js/components.iife.js +++ b/templates/_bundled/sirsoft-admin_basic/dist/js/components.iife.js @@ -1,19 +1,20 @@ -var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var U5=Object.defineProperty;var B5=(R,h,v)=>h in R?U5(R,h,{enumerable:!0,configurable:!0,writable:!0,value:v}):R[h]=v;var $e=(R,h,v)=>B5(R,typeof h!="symbol"?h+"":h,v);var Aa,fr;function eb(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const n in t)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,r.get?r:{enumerable:!0,get:()=>t[n]})}}return e.default=t,Object.freeze(e)}const F=eb(v),tb=()=>window.G7Core,oe=v.forwardRef(({children:t,variant:e="primary",size:n="md",className:r="",...s},o)=>{const i="button",a=tb()?.style?.mergeClasses?.(i,r)??`${i} ${r}`;return h.jsx("button",{ref:o,className:a,...s,children:t})});oe.displayName="Button";const Ed=v.forwardRef(({accept:t,maxSize:e,onChange:n,onError:r,buttonText:s="Browse",placeholder:o="No file selected",className:i="",disabled:a,...l},c)=>{const d=v.useRef(null),u=c||d,[f,g]=v.useState(""),m={},p={};Object.entries(l).forEach(([x,E])=>{x.startsWith("data-editor-")||x==="id"||x==="onMouseMove"||x==="onMouseLeave"?m[x]=E:p[x]=E});const y=v.useCallback(()=>{!a&&u.current&&u.current.click()},[a,u]),b=v.useCallback(x=>{const E=x.target.files?.[0]||null;if(!E){g(""),n?.({target:{value:null,name:""}});return}if(e&&E.size>e*1024*1024){const C=`File size exceeds ${e}MB limit`;r?.(C),u.current&&(u.current.value=""),g("");return}g(E.name),n?.({target:{value:E,name:E.name}})},[e,n,r,u]),_=v.useCallback(x=>{x.stopPropagation(),u.current&&(u.current.value=""),g(""),n?.({target:{value:null,name:""}})},[n,u]);return h.jsxs("div",{className:`flex items-center gap-2 ${i}`,...m,children:[h.jsx("input",{ref:u,type:"file",accept:t,onChange:b,disabled:a,className:"hidden",...p}),h.jsx("button",{type:"button",onClick:y,disabled:a,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed",children:s}),h.jsx("span",{className:"flex-1 text-sm text-gray-600 dark:text-gray-400 truncate",children:f||o}),f&&!a&&h.jsx("button",{type:"button",onClick:_,className:"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200","aria-label":"Clear file",children:h.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})});Ed.displayName="FileInput";const nb=t=>t==="checkbox"?"checkbox":t==="radio"?"radio":"input",rb=(t,e)=>{const n=nb(t);return e?e.split(/\s+/).filter(Boolean).includes(n)?e:`${n} ${e}`:n},kt=v.forwardRef(({label:t,error:e,className:n="",onChange:r,onKeyPress:s,value:o,defaultValue:i,type:a="text",checked:l,defaultChecked:c,...d},u)=>{const f=a==="radio"||a==="checkbox",g=rb(a,n),m=v.useRef(!1),p=v.useRef(null);v.useImperativeHandle(u,()=>p.current);const[y,b]=v.useState(o??i??"");v.useEffect(()=>{if(!f&&!m.current&&o!==void 0){if(p.current&&document.activeElement===p.current)return;b(o)}},[o,f]);const _=v.useCallback(()=>{m.current=!0},[]),x=v.useCallback(M=>{if(m.current=!1,r){const T=M.target.value;b(T);const P=new Event("change",{bubbles:!0});Object.defineProperty(P,"target",{writable:!1,value:{value:T}}),Object.defineProperty(P,"currentTarget",{writable:!1,value:{value:T}}),r(P)}},[r]),E=v.useCallback(M=>{const T=M.target.value;b(T),!m.current&&r&&r(M)},[r]),C=v.useCallback(M=>{m.current||s&&s(M)},[s]),{loadingactions:k,formdata:N,...S}=d;if(f)return h.jsx("input",{ref:p,type:a,className:g,onChange:r,checked:l,defaultChecked:c,value:o,...S});const w=o!==void 0?{value:y}:{defaultValue:i};return h.jsx("input",{ref:p,type:a,className:g,onChange:E,onKeyPress:C,onCompositionStart:_,onCompositionEnd:x,...w,...S})});kt.displayName="Input";const Qn=({label:t,error:e,className:n="",onChange:r,value:s,defaultValue:o,...i})=>{const a=v.useRef(!1),l=v.useRef(null),[c,d]=v.useState(s??o??"");v.useEffect(()=>{!a.current&&s!==void 0&&d(s)},[s]);const u=v.useCallback(()=>{a.current=!0},[]),f=v.useCallback(E=>{if(a.current=!1,r){const C=E.target.value;d(C);const k=new Event("change",{bubbles:!0});Object.defineProperty(k,"target",{writable:!1,value:{value:C}}),Object.defineProperty(k,"currentTarget",{writable:!1,value:{value:C}}),r(k)}},[r]),g=v.useCallback(E=>{const C=E.target.value;d(C),!a.current&&r&&r(E)},[r]),{loadingactions:m,formdata:p,...y}=i,b=s!==void 0?{value:c}:{defaultValue:o},x=(n??"").split(/\s+/).filter(Boolean).includes("textarea")?n??"":["textarea",n].filter(Boolean).join(" ");return h.jsx("textarea",{ref:l,className:x,onChange:g,onCompositionStart:u,onCompositionEnd:f,...b,...y})},sn=({children:t,className:e="",...n})=>h.jsx("label",{className:e,...n,children:t}),I=v.forwardRef(({children:t,className:e="",...n},r)=>h.jsx("div",{ref:r,className:e,...n,children:t})),Q=({children:t,className:e="",...n})=>h.jsx("span",{className:e,...n,children:t}),yt=({children:t,className:e="",...n})=>h.jsx("p",{className:e,...n,children:t}),yn=({className:t="",alt:e="",...n})=>h.jsx("img",{className:t,alt:e,...n}),Da=({children:t,className:e="",...n})=>h.jsx("h1",{className:e,...n,children:t}),Jn=({children:t,className:e="",...n})=>h.jsx("h2",{className:e,...n,children:t}),Ir=({children:t,className:e="",...n})=>h.jsx("h3",{className:e,...n,children:t}),Md=({children:t,className:e="",...n})=>h.jsx("h4",{className:e,...n,children:t}),Ia=({children:t,className:e="",...n})=>h.jsx("ul",{className:e,...n,children:t}),sb=({children:t,className:e="",...n})=>h.jsx("ol",{className:e,...n,children:t}),La=({children:t,className:e="",...n})=>h.jsx("li",{className:e,...n,children:t}),Lr=({children:t,className:e="",...n})=>h.jsx("a",{className:e,...n,children:t}),Pa=({children:t,className:e="",...n})=>h.jsx("form",{className:e,...n,children:t}),Un=({children:t,className:e="",...n})=>h.jsx("svg",{className:e,...n,children:t}),ob=window.G7Core?.createLogger?.("Comp:Select")??{log:(...t)=>console.log("[Comp:Select]",...t),warn:(...t)=>console.warn("[Comp:Select]",...t),error:(...t)=>console.error("[Comp:Select]",...t)};function ib(t){const e=window.G7Core?.state?.get?.("_global.appConfig.localeNames")?.[t];return e||{ko:"한국어",en:"English",ja:"日本語",zh:"中文",es:"Español",fr:"Français",de:"Deutsch"}[t]||t}const Pr=({children:t,label:e,error:n,options:r,className:s="",value:o,onChange:i,disabled:a,searchable:l=!1,searchPlaceholder:c,editorAttrs:d,...u})=>{const[f,g]=v.useState(!1),[m,p]=v.useState(""),[y,b]=v.useState(null),_=v.useRef(null),x=v.useRef(null),E=v.useRef(null),C=v.useRef(null),k=v.useMemo(()=>r?Array.isArray(r)?r.length===0?[]:typeof r[0]=="string"?r.map(O=>({value:O,label:ib(O)})):r:(ob.warn("options prop is not an array:",r),null):null,[r]),N=v.useMemo(()=>k&&k.find(z=>String(z.value)===String(o))?.label||"",[k,o]),S=v.useMemo(()=>{if(!k)return null;if(!l||m.trim()==="")return k;const O=m.trim().toLowerCase();return k.filter(z=>z.label.toLowerCase().includes(O)||String(z.value).toLowerCase().includes(O))},[k,l,m]);v.useEffect(()=>{if(f&&l){p("");const O=setTimeout(()=>E.current?.focus(),0);return()=>clearTimeout(O)}f||p("")},[f,l]);const w=v.useCallback(()=>{if(!x.current)return;const O=x.current.getBoundingClientRect(),z=window.innerHeight-O.bottom,j=z<280&&O.top>z;b(j?{bottom:window.innerHeight-O.top+4,left:O.left,width:O.width}:{top:O.bottom+4,left:O.left,width:O.width})},[]);v.useEffect(()=>{if(!f){b(null);return}return w(),window.addEventListener("scroll",w,!0),window.addEventListener("resize",w),()=>{window.removeEventListener("scroll",w,!0),window.removeEventListener("resize",w)}},[f,w]),v.useEffect(()=>{const O=z=>{const H=z.target;_.current&&!_.current.contains(H)&&(!C.current||!C.current.contains(H))&&g(!1)};return f&&document.addEventListener("mousedown",O),()=>{document.removeEventListener("mousedown",O)}},[f]),v.useEffect(()=>{const O=z=>{z.key==="Escape"&&f&&(g(!1),x.current?.focus())};return document.addEventListener("keydown",O),()=>document.removeEventListener("keydown",O)},[f]);const M=()=>{a||g(!f)},T=O=>{i&&i({target:{value:O},preventDefault:()=>{},stopPropagation:()=>{},type:"change"}),g(!1),x.current?.focus()};if(!k){const z=(s??"").split(/\s+/).filter(Boolean).includes("select")?s??"":["select",s].filter(Boolean).join(" ");return h.jsx("select",{className:z,value:o,onChange:i,disabled:a,...d,...u,children:t})}const P=(s??"").split(/\s+/).filter(O=>O&&!O.includes(":")).join(" "),L=P.includes("bg-"),$=/text-(gray|red|blue|green|yellow|white|black|indigo|purple|pink|orange|amber|emerald|teal|cyan|slate)-\d+|text-white|text-black/.test(P),A=L?`${s}${$?"":" text-gray-700 dark:text-gray-200"}`:`w-full px-4 py-2.5 bg-gray-100 dark:bg-gray-700 border-0 rounded-xl text-sm text-gray-700 dark:text-gray-200 font-medium focus:ring-2 focus:ring-blue-500 focus:outline-none ${s}`,D={...d,...u};return h.jsxs(I,{ref:_,className:"relative",...D,children:[h.jsxs(oe,{ref:x,type:"button",onClick:M,disabled:a,className:`${A} flex items-center justify-between gap-2 text-left cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed`,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(Q,{className:"truncate",children:N||" "}),h.jsx(Un,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&y&&Tn.createPortal(h.jsxs(I,{ref:C,className:"fixed z-[9999] bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",style:{top:y.top,bottom:y.bottom,left:y.left,width:y.width},role:"listbox",children:[l&&h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-600",children:h.jsx(kt,{ref:E,type:"text",value:m,onChange:O=>p(O.target.value),placeholder:c??"Search...",className:"w-full px-3 py-2 bg-gray-100 dark:bg-gray-700 border-0 rounded-lg text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:outline-none",role:"searchbox","aria-label":c??"Search"})}),h.jsxs(I,{className:"py-2 max-h-60 overflow-auto",children:[S&&S.length===0&&h.jsx(I,{className:"px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center",children:m?"No results":""}),(S??[]).map(O=>{const z=String(O.value)===String(o);return h.jsxs(oe,{type:"button",onClick:()=>!O.disabled&&T(O.value),disabled:O.disabled,className:`w-full px-4 py-3 text-left text-sm flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${z?"text-blue-600 dark:text-blue-400 font-medium":"text-gray-700 dark:text-gray-200"} ${O.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,role:"option","aria-selected":z,children:[h.jsx(Q,{children:O.label}),z&&h.jsx(Un,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})]},O.value)})]})]}),document.body)]})},ab=({children:t,className:e="",...n})=>h.jsx("option",{className:e,...n,children:t}),lb=({children:t,className:e="",...n})=>h.jsx("optgroup",{className:e,...n,children:t}),vn=v.forwardRef(({label:t,className:e="",indeterminate:n=!1,...r},s)=>{const o=v.useRef(null),i=a=>{o.current=a,typeof s=="function"?s(a):s&&(s.current=a)};return v.useEffect(()=>{o.current&&(o.current.indeterminate=n)},[n]),h.jsx("input",{ref:i,type:"checkbox",className:e,...r})});vn.displayName="Checkbox";const $a=({children:t,className:e="",...n})=>h.jsx("table",{className:e,...n,children:t}),Fa=({children:t,className:e="",...n})=>h.jsx("thead",{className:e,...n,children:t}),za=({children:t,className:e="",...n})=>h.jsx("tbody",{className:e,...n,children:t}),Va=({children:t,className:e="",...n})=>h.jsx("tfoot",{className:e,...n,children:t}),Rn=({children:t,className:e="",...n})=>h.jsx("tr",{className:e,...n,children:t}),$r=({children:t,className:e="",...n})=>h.jsx("th",{className:e,...n,children:t}),Zt=({children:t,className:e="",...n})=>h.jsx("td",{className:e,...n,children:t}),Po=({children:t,className:e="",...n})=>h.jsx("nav",{className:e,...n,children:t}),Nd=({children:t,className:e="",...n})=>h.jsx("section",{className:e,...n,children:t}),re=({name:t,iconStyle:e="solid",size:n,color:r,spin:s=!1,pulse:o=!1,fixedWidth:i=!1,ariaLabel:a,className:l="",style:c,...d})=>{const u={solid:"fas",regular:"far",light:"fal",duotone:"fad",brands:"fab"},f={"fa-solid":"solid","fa-regular":"regular","fa-light":"light","fa-duotone":"duotone","fa-brands":"brands"},g=typeof t=="string"?t:String(t);let m=e,p=g;for(const[_,x]of Object.entries(f))if(g.startsWith(_+" ")){m=x,p=g.substring(_.length+1);break}p=p.replace(/^fa-/,"");const y=[u[m],`fa-${p}`,n&&`fa-${n}`,s&&"fa-spin",o&&"fa-pulse",i&&"fa-fw",r,l].filter(Boolean).join(" "),b=a||p.replace(/-/g," ");return h.jsx("i",{className:y,style:c,"aria-label":b,role:"img",...d})},cb={xs:"w-3 h-3",sm:"w-4 h-4",md:"w-5 h-5",lg:"w-6 h-6",xl:"w-8 h-8"},Od={xs:"xs",sm:"sm",md:void 0,lg:"lg",xl:"2x"},db=({icon:t,size:e="md",className:n="",color:r,ariaLabel:s})=>{const o=t.indexOf(":"),i=t.startsWith("fa-")||t.startsWith("fas ")||t.startsWith("far ")||t.startsWith("fab ")||t.startsWith("fal ")||t.startsWith("fad ");let a,l;i?(a="fa",l=t):o>0?(a=t.substring(0,o),l=t.substring(o+1)):(a="fa",l=t);const c=cb[e],d=r||"";switch(a){case"fa":return h.jsx(re,{name:l,size:Od[e],className:n,color:r,ariaLabel:s});case"svg":return h.jsx("img",{src:l,className:`${c} ${d} ${n}`.trim(),alt:s||"","aria-label":s,role:"img"});case"img":return h.jsx("img",{src:l,className:`${c} ${n}`.trim(),alt:s||"",role:"img"});default:return h.jsx(re,{name:t,size:Od[e],className:n,color:r,ariaLabel:s})}},ub=({children:t,className:e="",...n})=>h.jsx("code",{className:e,...n,children:t}),fb=({children:t,className:e="",...n})=>h.jsx("pre",{className:e,...n,children:t}),hb=({children:t})=>h.jsx(h.Fragment,{children:t}),gb=({className:t="",...e})=>h.jsx("hr",{className:t,...e}),mt=({className:t="",children:e,style:n,...r})=>h.jsx("i",{className:t,style:n,...r,children:e});var de=(t=>(t.User="fa-user",t.Users="fa-users",t.UserCircle="fa-user-circle",t.UserPlus="fa-user-plus",t.UserMinus="fa-user-minus",t.UserShield="fa-user-shield",t.Search="fa-search",t.Edit="fa-edit",t.Pencil="fa-pencil-alt",t.Trash="fa-trash",t.Save="fa-save",t.Plus="fa-plus",t.Minus="fa-minus",t.Check="fa-check",t.Times="fa-times",t.Download="fa-download",t.Upload="fa-upload",t.Copy="fa-copy",t.ExternalLink="fa-external-link-alt",t.Home="fa-home",t.CaretRight="fa-caret-right",t.CaretDown="fa-caret-down",t.ChevronLeft="fa-chevron-left",t.ChevronRight="fa-chevron-right",t.ChevronUp="fa-chevron-up",t.ChevronDown="fa-chevron-down",t.AngleLeft="fa-angle-left",t.AngleRight="fa-angle-right",t.AngleUp="fa-angle-up",t.AngleDown="fa-angle-down",t.ArrowUp="fa-arrow-up",t.ArrowDown="fa-arrow-down",t.ArrowLeft="fa-arrow-left",t.ArrowRight="fa-arrow-right",t.Bars="fa-bars",t.EllipsisVertical="fa-ellipsis-v",t.EllipsisHorizontal="fa-ellipsis-h",t.CheckCircle="fa-check-circle",t.TimesCircle="fa-times-circle",t.ExclamationCircle="fa-exclamation-circle",t.ExclamationTriangle="fa-exclamation-triangle",t.InfoCircle="fa-info-circle",t.QuestionCircle="fa-question-circle",t.XCircle="fa-times-circle",t.ShoppingCart="fa-shopping-cart",t.ShoppingBag="fa-shopping-bag",t.CreditCard="fa-credit-card",t.Tag="fa-tag",t.Tags="fa-tags",t.File="fa-file",t.FileAlt="fa-file-alt",t.Folder="fa-folder",t.FolderOpen="fa-folder-open",t.Envelope="fa-envelope",t.Phone="fa-phone",t.Comment="fa-comment",t.Comments="fa-comments",t.Cog="fa-cog",t.Cogs="fa-cogs",t.Settings="fa-cog",t.Wrench="fa-wrench",t.Sliders="fa-sliders-h",t.Image="fa-image",t.Images="fa-images",t.Video="fa-video",t.Camera="fa-camera",t.Heart="fa-heart",t.Star="fa-star",t.ThumbsUp="fa-thumbs-up",t.ThumbsDown="fa-thumbs-down",t.Share="fa-share",t.Clock="fa-clock",t.Calendar="fa-calendar",t.CalendarAlt="fa-calendar-alt",t.Lock="fa-lock",t.Unlock="fa-unlock",t.Eye="fa-eye",t.EyeSlash="fa-eye-slash",t.Bell="fa-bell",t.Flag="fa-flag",t.Map="fa-map",t.MapMarker="fa-map-marker-alt",t.Globe="fa-globe",t.Link="fa-link",t.Unlink="fa-unlink",t.Sun="fa-sun",t.Moon="fa-moon",t.ChartBar="fa-chart-bar",t.ChartLine="fa-chart-line",t.ChartPie="fa-chart-pie",t.ChartArea="fa-chart-area",t.Spinner="fa-spinner",t.CircleNotch="fa-circle-notch",t.CheckBadge="fa-badge-check",t.Cube="fa-cube",t.Cubes="fa-cubes",t.Plug="fa-plug",t.PuzzlePiece="fa-puzzle-piece",t.Palette="fa-palette",t))(de||{});const pb=({title:t,content:e,imageUrl:n,imageAlt:r="",className:s="",onClick:o,style:i,id:a,editorAttrs:l})=>h.jsxs(I,{className:`card-clickable ${s}`,onClick:o,style:i,id:a,...l,children:[n&&h.jsx(I,{className:"w-full",children:h.jsx(yn,{src:n,alt:r,className:"card-image"})}),h.jsxs(I,{className:"p-4",children:[t&&h.jsx(Jn,{className:"card-title",children:t}),e&&h.jsx(yt,{className:"card-content",children:e})]})]}),xs=({items:t,triggerLabel:e="작업",triggerIconName:n=de.EllipsisVertical,position:r="right",className:s="",style:o,children:i,onItemClick:a,id:l,editorAttrs:c})=>{const[d,u]=v.useState(!1),[f,g]=v.useState({top:0,left:0}),m=v.useRef(null),p=v.useRef(null),y=v.useCallback(()=>{if(!m.current)return;const x=m.current.getBoundingClientRect(),E=224;let C=r==="right"?x.right-E:x.left;const k=x.bottom+8;C<8&&(C=8),C+E>window.innerWidth-8&&(C=window.innerWidth-E-8),g({top:k,left:C})},[r]);v.useEffect(()=>{d&&y()},[d,y]),v.useEffect(()=>{if(!d)return;const x=()=>{u(!1)};return window.addEventListener("scroll",x,!0),window.addEventListener("resize",x),()=>{window.removeEventListener("scroll",x,!0),window.removeEventListener("resize",x)}},[d]),v.useEffect(()=>{const x=E=>{const C=E.target,k=m.current?.contains(C),N=p.current?.contains(C);!k&&!N&&u(!1)};return d&&document.addEventListener("mousedown",x),()=>{document.removeEventListener("mousedown",x)}},[d]);const b=x=>{x.disabled||(x.onClick&&x.onClick(),a&&a(x.id,x),u(!1))},_=d?Tn.createPortal(h.jsx("div",{ref:p,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:f.top,left:f.left},children:t.filter(x=>x.if!==!1).map(x=>{if(x.divider)return h.jsx("div",{className:"border-t border-gray-200 dark:border-gray-600 my-1"},x.id);const E=x.variant==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",C=x.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer";return h.jsxs("div",{onClick:()=>b(x),className:`flex items-center gap-3 px-4 py-3 transition-colors ${E} ${C}`,children:[x.iconName&&h.jsx(re,{name:x.iconName,className:"w-4 h-4"}),h.jsx("span",{className:"text-sm font-medium",children:x.label})]},x.id)})}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,id:l,...c,children:[i?h.jsx(I,{ref:m,onClick:()=>u(!d),className:"cursor-pointer",children:i}):h.jsxs(oe,{ref:m,onClick:()=>u(!d),className:"flex items-center gap-2 px-2 py-1 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[e&&h.jsx(Q,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:e}),n&&h.jsx(re,{name:n,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})]}),_]})},Fr=(t,e)=>window.G7Core?.t?.(t,e)??t,$o=({currentPage:t,totalPages:e,onPageChange:n,maxVisiblePages:r=5,showFirstLast:s=!0,className:o="",style:i,prevText:a,nextText:l,id:c,editorAttrs:d})=>{const u=v.useMemo(()=>{const p=[];if(e<=r+2)for(let y=1;y<=e;y++)p.push(y);else{const y=Math.floor(r/2);let b=Math.max(2,t-y),_=Math.min(e-1,t+y);t<=y+1?_=r:t>=e-y&&(b=e-r+1),p.push(1),b>2&&p.push("...");for(let x=b;x<=_;x++)p.push(x);_1&&p.push(e)}return p},[t,e,r]),f=p=>{p<1||p>e||p===t||n(p)},g=()=>{t>1&&n(t-1)},m=()=>{tf(1),disabled:t===1,className:"pagination-btn","aria-label":Fr("common.first_page"),children:"«"}),h.jsx(oe,{onClick:g,disabled:t===1,className:"pagination-btn","aria-label":Fr("common.prev_page"),children:a||h.jsx(h.Fragment,{children:"‹"})}),h.jsx(I,{className:"pagination",children:u.map((p,y)=>{if(p==="...")return h.jsx(Q,{className:"pagination-ellipsis",children:"..."},`ellipsis-${y}`);const b=p,_=b===t;return h.jsx(oe,{onClick:()=>f(b),className:_?"pagination-btn-active":"pagination-btn","aria-label":Fr("common.page_n",{n:b}),"aria-current":_?"page":void 0,children:b},b)})}),h.jsx(oe,{onClick:m,disabled:t===e,className:"pagination-btn","aria-label":Fr("common.next_page"),children:l||h.jsx(h.Fragment,{children:"›"})}),s&&h.jsx(oe,{onClick:()=>f(e),disabled:t===e,className:"pagination-btn","aria-label":Fr("common.last_page"),children:"»"})]})},Td=(t,e)=>window.G7Core?.t?.(t,e)??t,mb=()=>window.G7Core?.locale?.supported?.()??["ko","en"],bb=()=>window.G7Core?.locale?.current?.()??"ko",Ad=t=>{const e=Td(`common.language_${t}`);return e===`common.language_${t}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[t]||t.toUpperCase():e},yb=()=>mb().map(e=>({code:e,name:Ad(e),nativeName:Ad(e)})),Ha=({value:t={},onChange:e,inputType:n="text",layout:r="inline",availableLocales:s,defaultLocale:o,placeholder:i="",required:a=!1,disabled:l=!1,maxLength:c,rows:d=4,className:u="",name:f="multilingual_input",showCodeOnMobile:g=!1,error:m,id:p,editorAttrs:y})=>{const b=!!m,_=v.useMemo(()=>s??yb(),[s]),x=v.useMemo(()=>o??bb(),[o]),E=v.useRef(t),[C,k]=v.useState(t),N=v.useRef(JSON.stringify(t));v.useEffect(()=>{const K=JSON.stringify(t);K!==N.current&&(N.current=K,E.current=t,k(t))},[t]);const S=v.useCallback((K,Z)=>{const ee={target:{name:f,value:K}};Z&&(ee._changedKeys=Z),e?.(ee)},[e,f]),[w,M]=v.useState(()=>{if(r==="tabs"||r==="compact"){const ee=_.map(he=>he.code);return[x,...ee.filter(he=>he!==x)]}const K=Object.keys(t).filter(ee=>t[ee]!==void 0);if(K.length===0)return[x];const Z=[];return K.includes(x)&&Z.push(x),K.forEach(ee=>{ee!==x&&Z.push(ee)}),Z}),[T,P]=v.useState(x),L=v.useMemo(()=>_.filter(K=>!w.includes(K.code)),[_,w]),[$,A]=v.useState(!1);v.useEffect(()=>{if(r==="tabs"||r==="compact"){const he=_.map(ae=>ae.code),J=[x,...he.filter(ae=>ae!==x)],le=w.join(","),ue=J.join(",");le!==ue&&M(J);return}const K=Object.keys(t).filter(he=>t[he]!==void 0);if(K.length===0){M([x]);return}const Z=[];K.includes(x)&&Z.push(x),K.forEach(he=>{he!==x&&Z.push(he)});const ee=w.join(","),ne=Z.join(",");ee!==ne&&M(Z)},[t,x,r,_]);const D=v.useCallback((K,Z)=>{const ee={...E.current,[K]:Z};E.current=ee,k(ee),S(ee,[K])},[S]),O=v.useCallback(K=>{M(ee=>[...ee,K]),P(K),A(!1);const Z={...E.current,[K]:""};E.current=Z,k(Z),S(Z)},[S]),z=v.useCallback(K=>{if(K===x)return;M(ee=>ee.filter(ne=>ne!==K)),T===K&&P(x);const Z={...E.current};delete Z[K],E.current=Z,k(Z),S(Z)},[T,x,S]),H=v.useCallback(K=>{const Z=_.find(ee=>ee.code===K);return Z?.nativeName||Z?.name||K.toUpperCase()},[_]),j=v.useCallback(K=>!!C[K]?.trim(),[C]),W=v.useCallback(()=>L.length===0?null:h.jsxs(I,{className:"relative",children:[h.jsxs(oe,{type:"button",className:"inline-flex items-center gap-1 px-3 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 hover:bg-blue-100 dark:hover:bg-blue-900/50 rounded-full border border-dashed border-blue-300 dark:border-blue-600 transition-colors",onClick:()=>A(!$),disabled:l,children:[h.jsx(re,{name:"plus",className:"w-3 h-3"}),h.jsx(Q,{children:Td("common.add_language")})]}),$&&h.jsx(I,{className:"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-10 min-w-[120px]",children:L.map(K=>h.jsxs(oe,{type:"button",className:"w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 first:rounded-t-lg last:rounded-b-lg transition-colors",onClick:()=>O(K.code),children:[h.jsx(re,{name:"globe",className:"w-3.5 h-3.5 text-gray-400 dark:text-gray-500"}),h.jsx(Q,{children:K.nativeName||K.name})]},K.code))})]}),[L,$,l,O]),U=v.useCallback((K,Z=!1)=>{const ee=b?"input input-error":"input",ne=b?"textarea input-error":"textarea",he=K===x,J=`${i}${i?" (":""}${H(K)}${i?")":""}`;return h.jsxs(I,{className:"space-y-2",children:[h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsxs(Q,{className:` +var SirsoftAdminBasic=(function(R,h,v,Ln){"use strict";var iA=Object.defineProperty;var aA=(R,h,v)=>h in R?iA(R,h,{enumerable:!0,configurable:!0,writable:!0,value:v}):R[h]=v;var Pe=(R,h,v)=>aA(R,typeof h!="symbol"?h+"":h,v);var za,vr;function fb(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const F=fb(v),hb=()=>window.G7Core,ae=v.forwardRef(({children:e,variant:t="primary",size:n="md",className:r="",...s},o)=>{const i="button",a=hb()?.style?.mergeClasses?.(i,r)??`${i} ${r}`;return h.jsx("button",{ref:o,className:a,...s,children:e})});ae.displayName="Button";const Pd=v.forwardRef(({accept:e,maxSize:t,onChange:n,onError:r,buttonText:s="Browse",placeholder:o="No file selected",className:i="",disabled:a,...l},c)=>{const d=v.useRef(null),u=c||d,[f,g]=v.useState(""),m={},p={};Object.entries(l).forEach(([x,M])=>{x.startsWith("data-editor-")||x==="id"||x==="onMouseMove"||x==="onMouseLeave"?m[x]=M:p[x]=M});const y=v.useCallback(()=>{!a&&u.current&&u.current.click()},[a,u]),b=v.useCallback(x=>{const M=x.target.files?.[0]||null;if(!M){g(""),n?.({target:{value:null,name:""}});return}if(t&&M.size>t*1024*1024){const S=`File size exceeds ${t}MB limit`;r?.(S),u.current&&(u.current.value=""),g("");return}g(M.name),n?.({target:{value:M,name:M.name}})},[t,n,r,u]),_=v.useCallback(x=>{x.stopPropagation(),u.current&&(u.current.value=""),g(""),n?.({target:{value:null,name:""}})},[n,u]);return h.jsxs("div",{className:`flex items-center gap-2 ${i}`,...m,children:[h.jsx("input",{ref:u,type:"file",accept:e,onChange:b,disabled:a,className:"hidden",...p}),h.jsx("button",{type:"button",onClick:y,disabled:a,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed",children:s}),h.jsx("span",{className:"flex-1 text-sm text-gray-600 dark:text-gray-400 truncate",children:f||o}),f&&!a&&h.jsx("button",{type:"button",onClick:_,className:"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200","aria-label":"Clear file",children:h.jsx("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})});Pd.displayName="FileInput";const gb=e=>e==="checkbox"?"checkbox":e==="radio"?"radio":"input",pb=(e,t)=>{const n=gb(e);return t?t.split(/\s+/).filter(Boolean).includes(n)?t:`${n} ${t}`:n},kt=v.forwardRef(({label:e,error:t,className:n="",onChange:r,onKeyPress:s,value:o,defaultValue:i,type:a="text",checked:l,defaultChecked:c,...d},u)=>{const f=a==="radio"||a==="checkbox",g=pb(a,n),m=v.useRef(!1),p=v.useRef(null);v.useImperativeHandle(u,()=>p.current);const[y,b]=v.useState(o??i??"");v.useEffect(()=>{if(!f&&!m.current&&o!==void 0){if(p.current&&document.activeElement===p.current)return;b(o)}},[o,f]);const _=v.useCallback(()=>{m.current=!0},[]),x=v.useCallback(E=>{if(m.current=!1,r){const A=E.target.value;b(A);const P=new Event("change",{bubbles:!0});Object.defineProperty(P,"target",{writable:!1,value:{value:A}}),Object.defineProperty(P,"currentTarget",{writable:!1,value:{value:A}}),r(P)}},[r]),M=v.useCallback(E=>{const A=E.target.value;b(A),!m.current&&r&&r(E)},[r]),S=v.useCallback(E=>{m.current||s&&s(E)},[s]),{loadingactions:k,formdata:N,...C}=d;if(f)return h.jsx("input",{ref:p,type:a,className:g,onChange:r,checked:l,defaultChecked:c,value:o,...C});const w=o!==void 0?{value:y}:{defaultValue:i};return h.jsx("input",{ref:p,type:a,className:g,onChange:M,onKeyPress:S,onCompositionStart:_,onCompositionEnd:x,...w,...C})});kt.displayName="Input";const sr=({label:e,error:t,className:n="",onChange:r,value:s,defaultValue:o,...i})=>{const a=v.useRef(!1),l=v.useRef(null),[c,d]=v.useState(s??o??"");v.useEffect(()=>{!a.current&&s!==void 0&&d(s)},[s]);const u=v.useCallback(()=>{a.current=!0},[]),f=v.useCallback(M=>{if(a.current=!1,r){const S=M.target.value;d(S);const k=new Event("change",{bubbles:!0});Object.defineProperty(k,"target",{writable:!1,value:{value:S}}),Object.defineProperty(k,"currentTarget",{writable:!1,value:{value:S}}),r(k)}},[r]),g=v.useCallback(M=>{const S=M.target.value;d(S),!a.current&&r&&r(M)},[r]),{loadingactions:m,formdata:p,...y}=i,b=s!==void 0?{value:c}:{defaultValue:o},x=(n??"").split(/\s+/).filter(Boolean).includes("textarea")?n??"":["textarea",n].filter(Boolean).join(" ");return h.jsx("textarea",{ref:l,className:x,onChange:g,onCompositionStart:u,onCompositionEnd:f,...b,...y})},ln=({children:e,className:t="",...n})=>h.jsx("label",{className:t,...n,children:e}),I=v.forwardRef(({children:e,className:t="",...n},r)=>h.jsx("div",{ref:r,className:t,...n,children:e})),J=({children:e,className:t="",...n})=>h.jsx("span",{className:t,...n,children:e}),yt=({children:e,className:t="",...n})=>h.jsx("p",{className:t,...n,children:e}),Sn=({className:e="",alt:t="",...n})=>h.jsx("img",{className:e,alt:t,...n}),Ba=({children:e,className:t="",...n})=>h.jsx("h1",{className:t,...n,children:e}),or=({children:e,className:t="",...n})=>h.jsx("h2",{className:t,...n,children:e}),jr=({children:e,className:t="",...n})=>h.jsx("h3",{className:t,...n,children:e}),$d=({children:e,className:t="",...n})=>h.jsx("h4",{className:t,...n,children:e}),ja=({children:e,className:t="",...n})=>h.jsx("ul",{className:t,...n,children:e}),mb=({children:e,className:t="",...n})=>h.jsx("ol",{className:t,...n,children:e}),Wa=({children:e,className:t="",...n})=>h.jsx("li",{className:t,...n,children:e}),Wr=({children:e,className:t="",...n})=>h.jsx("a",{className:t,...n,children:e}),Ga=({children:e,className:t="",...n})=>h.jsx("form",{className:t,...n,children:e}),Xn=({children:e,className:t="",...n})=>h.jsx("svg",{className:t,...n,children:e}),bb=window.G7Core?.createLogger?.("Comp:Select")??{log:(...e)=>console.log("[Comp:Select]",...e),warn:(...e)=>console.warn("[Comp:Select]",...e),error:(...e)=>console.error("[Comp:Select]",...e)};function yb(e){const t=window.G7Core?.state?.get?.("_global.appConfig.localeNames")?.[e];return t||{ko:"한국어",en:"English",ja:"日本語",zh:"中文",es:"Español",fr:"Français",de:"Deutsch"}[e]||e}const Gr=({children:e,label:t,error:n,options:r,className:s="",value:o,onChange:i,disabled:a,searchable:l=!1,searchPlaceholder:c,editorAttrs:d,...u})=>{const[f,g]=v.useState(!1),[m,p]=v.useState(""),[y,b]=v.useState(null),_=v.useRef(null),x=v.useRef(null),M=v.useRef(null),S=v.useRef(null),k=v.useMemo(()=>r?Array.isArray(r)?r.length===0?[]:typeof r[0]=="string"?r.map(T=>({value:T,label:yb(T)})):r:(bb.warn("options prop is not an array:",r),null):null,[r]),N=v.useMemo(()=>k&&k.find(z=>String(z.value)===String(o))?.label||"",[k,o]),C=v.useMemo(()=>{if(!k)return null;if(!l||m.trim()==="")return k;const T=m.trim().toLowerCase();return k.filter(z=>z.label.toLowerCase().includes(T)||String(z.value).toLowerCase().includes(T))},[k,l,m]);v.useEffect(()=>{if(f&&l){p("");const T=setTimeout(()=>M.current?.focus(),0);return()=>clearTimeout(T)}f||p("")},[f,l]);const w=v.useCallback(()=>{if(!x.current)return;const T=x.current.getBoundingClientRect(),z=window.innerHeight-T.bottom,W=z<280&&T.top>z;b(W?{bottom:window.innerHeight-T.top+4,left:T.left,width:T.width}:{top:T.bottom+4,left:T.left,width:T.width})},[]);v.useEffect(()=>{if(!f){b(null);return}return w(),window.addEventListener("scroll",w,!0),window.addEventListener("resize",w),()=>{window.removeEventListener("scroll",w,!0),window.removeEventListener("resize",w)}},[f,w]),v.useEffect(()=>{const T=z=>{const U=z.target;_.current&&!_.current.contains(U)&&(!S.current||!S.current.contains(U))&&g(!1)};return f&&document.addEventListener("mousedown",T),()=>{document.removeEventListener("mousedown",T)}},[f]),v.useEffect(()=>{const T=z=>{z.key==="Escape"&&f&&(g(!1),x.current?.focus())};return document.addEventListener("keydown",T),()=>document.removeEventListener("keydown",T)},[f]);const E=()=>{a||g(!f)},A=T=>{i&&i({target:{value:T},preventDefault:()=>{},stopPropagation:()=>{},type:"change"}),g(!1),x.current?.focus()};if(!k){const z=(s??"").split(/\s+/).filter(Boolean).includes("select")?s??"":["select",s].filter(Boolean).join(" ");return h.jsx("select",{className:z,value:o,onChange:i,disabled:a,...d,...u,children:e})}const P=(s??"").split(/\s+/).filter(T=>T&&!T.includes(":")).join(" "),L=P.includes("bg-"),$=/text-(gray|red|blue|green|yellow|white|black|indigo|purple|pink|orange|amber|emerald|teal|cyan|slate)-\d+|text-white|text-black/.test(P),D=L?`${s}${$?"":" text-gray-700 dark:text-gray-200"}`:`w-full px-4 py-2.5 bg-gray-100 dark:bg-gray-700 border-0 rounded-xl text-sm text-gray-700 dark:text-gray-200 font-medium focus:ring-2 focus:ring-blue-500 focus:outline-none ${s}`,O={...d,...u};return h.jsxs(I,{ref:_,className:"relative",...O,children:[h.jsxs(ae,{ref:x,type:"button",onClick:E,disabled:a,className:`${D} flex items-center justify-between gap-2 text-left cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed`,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(J,{className:"truncate",children:N||" "}),h.jsx(Xn,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&y&&Ln.createPortal(h.jsxs(I,{ref:S,className:"fixed z-[9999] bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",style:{top:y.top,bottom:y.bottom,left:y.left,width:y.width},role:"listbox",children:[l&&h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-600",children:h.jsx(kt,{ref:M,type:"text",value:m,onChange:T=>p(T.target.value),placeholder:c??"Search...",className:"w-full px-3 py-2 bg-gray-100 dark:bg-gray-700 border-0 rounded-lg text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:outline-none",role:"searchbox","aria-label":c??"Search"})}),h.jsxs(I,{className:"py-2 max-h-60 overflow-auto",children:[C&&C.length===0&&h.jsx(I,{className:"px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center",children:m?"No results":""}),(C??[]).map(T=>{const z=String(T.value)===String(o);return h.jsxs(ae,{type:"button",onClick:()=>!T.disabled&&A(T.value),disabled:T.disabled,className:`w-full px-4 py-3 text-left text-sm flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${z?"text-blue-600 dark:text-blue-400 font-medium":"text-gray-700 dark:text-gray-200"} ${T.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,role:"option","aria-selected":z,children:[h.jsx(J,{children:T.label}),z&&h.jsx(Xn,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})]},T.value)})]})]}),document.body)]})},vb=({children:e,className:t="",...n})=>h.jsx("option",{className:t,...n,children:e}),xb=({children:e,className:t="",...n})=>h.jsx("optgroup",{className:t,...n,children:e}),_n=v.forwardRef(({label:e,className:t="",indeterminate:n=!1,...r},s)=>{const o=v.useRef(null),i=a=>{o.current=a,typeof s=="function"?s(a):s&&(s.current=a)};return v.useEffect(()=>{o.current&&(o.current.indeterminate=n)},[n]),h.jsx("input",{ref:i,type:"checkbox",className:t,...r})});_n.displayName="Checkbox";const Ya=({children:e,className:t="",...n})=>h.jsx("table",{className:t,...n,children:e}),Xa=({children:e,className:t="",...n})=>h.jsx("thead",{className:t,...n,children:e}),Ka=({children:e,className:t="",...n})=>h.jsx("tbody",{className:t,...n,children:e}),qa=({children:e,className:t="",...n})=>h.jsx("tfoot",{className:t,...n,children:e}),ir=({children:e,className:t="",...n})=>h.jsx("tr",{className:t,...n,children:e}),Yr=({children:e,className:t="",...n})=>h.jsx("th",{className:t,...n,children:e}),Jt=({children:e,className:t="",...n})=>h.jsx("td",{className:t,...n,children:e}),Go=({children:e,className:t="",...n})=>h.jsx("nav",{className:t,...n,children:e}),Fd=({children:e,className:t="",...n})=>h.jsx("section",{className:t,...n,children:e}),oe=({name:e,iconStyle:t="solid",size:n,color:r,spin:s=!1,pulse:o=!1,fixedWidth:i=!1,ariaLabel:a,className:l="",style:c,...d})=>{const u={solid:"fas",regular:"far",light:"fal",duotone:"fad",brands:"fab"},f={"fa-solid":"solid","fa-regular":"regular","fa-light":"light","fa-duotone":"duotone","fa-brands":"brands"},g=typeof e=="string"?e:String(e);let m=t,p=g;for(const[_,x]of Object.entries(f))if(g.startsWith(_+" ")){m=x,p=g.substring(_.length+1);break}p=p.replace(/^fa-/,"");const y=[u[m],`fa-${p}`,n&&`fa-${n}`,s&&"fa-spin",o&&"fa-pulse",i&&"fa-fw",r,l].filter(Boolean).join(" "),b=a||p.replace(/-/g," ");return h.jsx("i",{className:y,style:c,"aria-label":b,role:"img",...d})},wb={xs:"w-3 h-3",sm:"w-4 h-4",md:"w-5 h-5",lg:"w-6 h-6",xl:"w-8 h-8"},zd={xs:"xs",sm:"sm",md:void 0,lg:"lg",xl:"2x"},kb=({icon:e,size:t="md",className:n="",color:r,ariaLabel:s})=>{const o=e.indexOf(":"),i=e.startsWith("fa-")||e.startsWith("fas ")||e.startsWith("far ")||e.startsWith("fab ")||e.startsWith("fal ")||e.startsWith("fad ");let a,l;i?(a="fa",l=e):o>0?(a=e.substring(0,o),l=e.substring(o+1)):(a="fa",l=e);const c=wb[t],d=r||"";switch(a){case"fa":return h.jsx(oe,{name:l,size:zd[t],className:n,color:r,ariaLabel:s});case"svg":return h.jsx("img",{src:l,className:`${c} ${d} ${n}`.trim(),alt:s||"","aria-label":s,role:"img"});case"img":return h.jsx("img",{src:l,className:`${c} ${n}`.trim(),alt:s||"",role:"img"});default:return h.jsx(oe,{name:e,size:zd[t],className:n,color:r,ariaLabel:s})}},Cb=({children:e,className:t="",...n})=>h.jsx("code",{className:t,...n,children:e}),Sb=({children:e,className:t="",...n})=>h.jsx("pre",{className:t,...n,children:e}),_b=({children:e})=>h.jsx(h.Fragment,{children:e}),Eb=({className:e="",...t})=>h.jsx("hr",{className:e,...t}),pt=({className:e="",children:t,style:n,...r})=>h.jsx("i",{className:e,style:n,...r,children:t});var ue=(e=>(e.User="fa-user",e.Users="fa-users",e.UserCircle="fa-user-circle",e.UserPlus="fa-user-plus",e.UserMinus="fa-user-minus",e.UserShield="fa-user-shield",e.Search="fa-search",e.Edit="fa-edit",e.Pencil="fa-pencil-alt",e.Trash="fa-trash",e.Save="fa-save",e.Plus="fa-plus",e.Minus="fa-minus",e.Check="fa-check",e.Times="fa-times",e.Download="fa-download",e.Upload="fa-upload",e.Copy="fa-copy",e.ExternalLink="fa-external-link-alt",e.Home="fa-home",e.CaretRight="fa-caret-right",e.CaretDown="fa-caret-down",e.ChevronLeft="fa-chevron-left",e.ChevronRight="fa-chevron-right",e.ChevronUp="fa-chevron-up",e.ChevronDown="fa-chevron-down",e.AngleLeft="fa-angle-left",e.AngleRight="fa-angle-right",e.AngleUp="fa-angle-up",e.AngleDown="fa-angle-down",e.ArrowUp="fa-arrow-up",e.ArrowDown="fa-arrow-down",e.ArrowLeft="fa-arrow-left",e.ArrowRight="fa-arrow-right",e.Bars="fa-bars",e.EllipsisVertical="fa-ellipsis-v",e.EllipsisHorizontal="fa-ellipsis-h",e.CheckCircle="fa-check-circle",e.TimesCircle="fa-times-circle",e.ExclamationCircle="fa-exclamation-circle",e.ExclamationTriangle="fa-exclamation-triangle",e.InfoCircle="fa-info-circle",e.QuestionCircle="fa-question-circle",e.XCircle="fa-times-circle",e.ShoppingCart="fa-shopping-cart",e.ShoppingBag="fa-shopping-bag",e.CreditCard="fa-credit-card",e.Tag="fa-tag",e.Tags="fa-tags",e.File="fa-file",e.FileAlt="fa-file-alt",e.Folder="fa-folder",e.FolderOpen="fa-folder-open",e.Envelope="fa-envelope",e.Phone="fa-phone",e.Comment="fa-comment",e.Comments="fa-comments",e.Cog="fa-cog",e.Cogs="fa-cogs",e.Settings="fa-cog",e.Wrench="fa-wrench",e.Sliders="fa-sliders-h",e.Image="fa-image",e.Images="fa-images",e.Video="fa-video",e.Camera="fa-camera",e.Heart="fa-heart",e.Star="fa-star",e.ThumbsUp="fa-thumbs-up",e.ThumbsDown="fa-thumbs-down",e.Share="fa-share",e.Clock="fa-clock",e.Calendar="fa-calendar",e.CalendarAlt="fa-calendar-alt",e.Lock="fa-lock",e.Unlock="fa-unlock",e.Eye="fa-eye",e.EyeSlash="fa-eye-slash",e.Bell="fa-bell",e.Flag="fa-flag",e.Map="fa-map",e.MapMarker="fa-map-marker-alt",e.Globe="fa-globe",e.Link="fa-link",e.Unlink="fa-unlink",e.Sun="fa-sun",e.Moon="fa-moon",e.ChartBar="fa-chart-bar",e.ChartLine="fa-chart-line",e.ChartPie="fa-chart-pie",e.ChartArea="fa-chart-area",e.Spinner="fa-spinner",e.CircleNotch="fa-circle-notch",e.CheckBadge="fa-badge-check",e.Cube="fa-cube",e.Cubes="fa-cubes",e.Plug="fa-plug",e.PuzzlePiece="fa-puzzle-piece",e.Palette="fa-palette",e))(ue||{});const Mb=({title:e,content:t,imageUrl:n,imageAlt:r="",className:s="",onClick:o,style:i,id:a,editorAttrs:l})=>h.jsxs(I,{className:`card-clickable ${s}`,onClick:o,style:i,id:a,...l,children:[n&&h.jsx(I,{className:"w-full",children:h.jsx(Sn,{src:n,alt:r,className:"card-image"})}),h.jsxs(I,{className:"p-4",children:[e&&h.jsx(or,{className:"card-title",children:e}),t&&h.jsx(yt,{className:"card-content",children:t})]})]}),Ls=({items:e,triggerLabel:t="작업",triggerIconName:n=ue.EllipsisVertical,position:r="right",className:s="",style:o,children:i,onItemClick:a,id:l,editorAttrs:c})=>{const[d,u]=v.useState(!1),[f,g]=v.useState({top:0,left:0}),m=v.useRef(null),p=v.useRef(null),y=v.useCallback(()=>{if(!m.current)return;const x=m.current.getBoundingClientRect(),M=224;let S=r==="right"?x.right-M:x.left;const k=x.bottom+8;S<8&&(S=8),S+M>window.innerWidth-8&&(S=window.innerWidth-M-8),g({top:k,left:S})},[r]);v.useEffect(()=>{d&&y()},[d,y]),v.useEffect(()=>{if(!d)return;const x=()=>{u(!1)};return window.addEventListener("scroll",x,!0),window.addEventListener("resize",x),()=>{window.removeEventListener("scroll",x,!0),window.removeEventListener("resize",x)}},[d]),v.useEffect(()=>{const x=M=>{const S=M.target,k=m.current?.contains(S),N=p.current?.contains(S);!k&&!N&&u(!1)};return d&&document.addEventListener("mousedown",x),()=>{document.removeEventListener("mousedown",x)}},[d]);const b=x=>{x.disabled||(x.onClick&&x.onClick(),a&&a(x.id,x),u(!1))},_=d?Ln.createPortal(h.jsx("div",{ref:p,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:f.top,left:f.left},children:e.filter(x=>x.if!==!1).map(x=>{if(x.divider)return h.jsx("div",{className:"border-t border-gray-200 dark:border-gray-600 my-1"},x.id);const M=x.variant==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",S=x.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer";return h.jsxs("div",{onClick:()=>b(x),className:`flex items-center gap-3 px-4 py-3 transition-colors ${M} ${S}`,children:[x.iconName&&h.jsx(oe,{name:x.iconName,className:"w-4 h-4"}),h.jsx("span",{className:"text-sm font-medium",children:x.label})]},x.id)})}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,id:l,...c,children:[i?h.jsx(I,{ref:m,onClick:()=>u(!d),className:"cursor-pointer",children:i}):h.jsxs(ae,{ref:m,onClick:()=>u(!d),className:"flex items-center gap-2 px-2 py-1 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[t&&h.jsx(J,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:t}),n&&h.jsx(oe,{name:n,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})]}),_]})},Xr=(e,t)=>window.G7Core?.t?.(e,t)??e,Yo=({currentPage:e,totalPages:t,onPageChange:n,maxVisiblePages:r=5,showFirstLast:s=!0,showFirst:o,showLast:i,hasMorePages:a,className:l="",style:c,prevText:d,nextText:u,id:f,editorAttrs:g})=>{const m=t!=null,p=m?t:0,y=m?e{const N=[];if(!m)return N;if(p<=r+2)for(let C=1;C<=p;C++)N.push(C);else{const C=Math.floor(r/2);let w=Math.max(2,e-C),E=Math.min(p-1,e+C);e<=C+1?E=r:e>=p-C&&(w=p-r+1),N.push(1),w>2&&N.push("...");for(let A=w;A<=E;A++)N.push(A);E1&&N.push(p)}return N},[e,p,m,r]),M=N=>{N<1||N===e||m&&N>p||n(N)},S=()=>{e>1&&n(e-1)},k=()=>{y&&n(e+1)};return h.jsxs(I,{className:`pagination ${l}`,style:c,role:"navigation","aria-label":Xr("common.pagination"),id:f,...g,children:[b&&h.jsx(ae,{onClick:()=>M(1),disabled:e===1,className:"pagination-btn","aria-label":Xr("common.first_page"),children:"«"}),h.jsx(ae,{onClick:S,disabled:e===1,className:"pagination-btn","aria-label":Xr("common.prev_page"),children:d||h.jsx(h.Fragment,{children:"‹"})}),h.jsxs(I,{className:"pagination",children:[!m&&h.jsx(J,{className:"px-3 py-1 text-sm text-gray-700 dark:text-gray-300",children:e}),x.map((N,C)=>{if(N==="...")return h.jsx(J,{className:"pagination-ellipsis",children:"..."},`ellipsis-${C}`);const w=N,E=w===e;return h.jsx(ae,{onClick:()=>M(w),className:E?"pagination-btn-active":"pagination-btn","aria-label":Xr("common.page_n",{n:w}),"aria-current":E?"page":void 0,children:w},w)})]}),h.jsx(ae,{onClick:k,disabled:!y,className:"pagination-btn","aria-label":Xr("common.next_page"),children:u||h.jsx(h.Fragment,{children:"›"})}),_&&h.jsx(ae,{onClick:()=>M(p),disabled:e===p,className:"pagination-btn","aria-label":Xr("common.last_page"),children:"»"})]})},Vd=(e,t)=>window.G7Core?.t?.(e,t)??e,Nb=()=>window.G7Core?.locale?.supported?.()??["ko","en"],Tb=()=>window.G7Core?.locale?.current?.()??"ko",Hd=e=>{const t=Vd(`common.language_${e}`);return t===`common.language_${e}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[e]||e.toUpperCase():t},Ob=()=>Nb().map(t=>({code:t,name:Hd(t),nativeName:Hd(t)})),Za=({value:e={},onChange:t,inputType:n="text",layout:r="inline",availableLocales:s,defaultLocale:o,placeholder:i="",required:a=!1,disabled:l=!1,maxLength:c,rows:d=4,className:u="",name:f="multilingual_input",showCodeOnMobile:g=!1,error:m,id:p,editorAttrs:y})=>{const b=!!m,_=v.useMemo(()=>s??Ob(),[s]),x=v.useMemo(()=>o??Tb(),[o]),M=v.useRef(e),[S,k]=v.useState(e),N=v.useRef(JSON.stringify(e));v.useEffect(()=>{const K=JSON.stringify(e);K!==N.current&&(N.current=K,M.current=e,k(e))},[e]);const C=v.useCallback((K,q)=>{const ne={target:{name:f,value:K}};q&&(ne._changedKeys=q),t?.(ne)},[t,f]),[w,E]=v.useState(()=>{if(r==="tabs"||r==="compact"){const ne=_.map(fe=>fe.code);return[x,...ne.filter(fe=>fe!==x)]}const K=Object.keys(e).filter(ne=>e[ne]!==void 0);if(K.length===0)return[x];const q=[];return K.includes(x)&&q.push(x),K.forEach(ne=>{ne!==x&&q.push(ne)}),q}),[A,P]=v.useState(x),L=v.useMemo(()=>_.filter(K=>!w.includes(K.code)),[_,w]),[$,D]=v.useState(!1);v.useEffect(()=>{if(r==="tabs"||r==="compact"){const fe=_.map(ce=>ce.code),te=[x,...fe.filter(ce=>ce!==x)],le=w.join(","),ge=te.join(",");le!==ge&&E(te);return}const K=Object.keys(e).filter(fe=>e[fe]!==void 0);if(K.length===0){E([x]);return}const q=[];K.includes(x)&&q.push(x),K.forEach(fe=>{fe!==x&&q.push(fe)});const ne=w.join(","),ee=q.join(",");ne!==ee&&E(q)},[e,x,r,_]);const O=v.useCallback((K,q)=>{const ne={...M.current,[K]:q};M.current=ne,k(ne),C(ne,[K])},[C]),T=v.useCallback(K=>{E(ne=>[...ne,K]),P(K),D(!1);const q={...M.current,[K]:""};M.current=q,k(q),C(q)},[C]),z=v.useCallback(K=>{if(K===x)return;E(ne=>ne.filter(ee=>ee!==K)),A===K&&P(x);const q={...M.current};delete q[K],M.current=q,k(q),C(q)},[A,x,C]),U=v.useCallback(K=>{const q=_.find(ne=>ne.code===K);return q?.nativeName||q?.name||K.toUpperCase()},[_]),W=v.useCallback(K=>!!S[K]?.trim(),[S]),G=v.useCallback(()=>L.length===0?null:h.jsxs(I,{className:"relative",children:[h.jsxs(ae,{type:"button",className:"inline-flex items-center gap-1 px-3 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/30 hover:bg-blue-100 dark:hover:bg-blue-900/50 rounded-full border border-dashed border-blue-300 dark:border-blue-600 transition-colors",onClick:()=>D(!$),disabled:l,children:[h.jsx(oe,{name:"plus",className:"w-3 h-3"}),h.jsx(J,{children:Vd("common.add_language")})]}),$&&h.jsx(I,{className:"absolute top-full left-0 mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-10 min-w-[120px]",children:L.map(K=>h.jsxs(ae,{type:"button",className:"w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 first:rounded-t-lg last:rounded-b-lg transition-colors",onClick:()=>T(K.code),children:[h.jsx(oe,{name:"globe",className:"w-3.5 h-3.5 text-gray-400 dark:text-gray-500"}),h.jsx(J,{children:K.nativeName||K.name})]},K.code))})]}),[L,$,l,T]),B=v.useCallback((K,q=!1)=>{const ne=b?"input input-error":"input",ee=b?"textarea input-error":"textarea",fe=K===x,te=`${i}${i?" (":""}${U(K)}${i?")":""}`;return h.jsxs(I,{className:"space-y-2",children:[h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsxs(J,{className:` inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium - ${he?"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"} - `,children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),H(K),he&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),!he&&h.jsx(oe,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",onClick:()=>z(K),disabled:l,children:h.jsx(re,{name:"xmark",className:"w-3 h-3"})})]}),he&&Z&&W()]}),n==="textarea"?h.jsx(Qn,{name:`${f}[${K}]`,value:C[K]||"",onChange:le=>D(K,le.target.value),placeholder:J,required:a&&he,disabled:l,maxLength:c,rows:d,className:`${ne} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${K}]`,value:C[K]||"",onChange:le=>D(K,le.target.value),placeholder:J,required:a&&he,disabled:l,maxLength:c,className:ee})]},K)},[f,C,D,i,a,l,c,d,n,H,x,z,W,b]),B=v.useCallback(K=>{const Z=b?"input input-error":"input",ee=b?"textarea input-error":"textarea",ne=`${i}${i?" (":""}${H(K)}${i?")":""}`;return h.jsx(I,{children:n==="textarea"?h.jsx(Qn,{name:`${f}[${K}]`,value:C[K]||"",onChange:he=>D(K,he.target.value),placeholder:ne,required:a&&K===x,disabled:l,maxLength:c,rows:d,className:`${ee} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${K}]`,value:C[K]||"",onChange:he=>D(K,he.target.value),placeholder:ne,required:a&&K===x,disabled:l,maxLength:c,className:Z})},K)},[f,C,D,i,a,l,c,d,n,x,H,b]),G=()=>h.jsx(I,{className:u,children:h.jsx(I,{className:"space-y-4",children:w.map((K,Z)=>U(K,Z===0))})}),V=()=>h.jsxs(I,{className:u,children:[h.jsxs(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:[w.map(K=>h.jsxs(oe,{type:"button",className:` + ${fe?"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300"} + `,children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),U(K),fe&&h.jsx(J,{className:"text-red-500 dark:text-red-400",children:"*"}),!fe&&h.jsx(ae,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",onClick:()=>z(K),disabled:l,children:h.jsx(oe,{name:"xmark",className:"w-3 h-3"})})]}),fe&&q&&G()]}),n==="textarea"?h.jsx(sr,{name:`${f}[${K}]`,value:S[K]||"",onChange:le=>O(K,le.target.value),placeholder:te,required:a&&fe,disabled:l,maxLength:c,rows:d,className:`${ee} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${K}]`,value:S[K]||"",onChange:le=>O(K,le.target.value),placeholder:te,required:a&&fe,disabled:l,maxLength:c,className:ne})]},K)},[f,S,O,i,a,l,c,d,n,U,x,z,G,b]),j=v.useCallback(K=>{const q=b?"input input-error":"input",ne=b?"textarea input-error":"textarea",ee=`${i}${i?" (":""}${U(K)}${i?")":""}`;return h.jsx(I,{children:n==="textarea"?h.jsx(sr,{name:`${f}[${K}]`,value:S[K]||"",onChange:fe=>O(K,fe.target.value),placeholder:ee,required:a&&K===x,disabled:l,maxLength:c,rows:d,className:`${ne} resize-vertical`}):h.jsx(kt,{type:"text",name:`${f}[${K}]`,value:S[K]||"",onChange:fe=>O(K,fe.target.value),placeholder:ee,required:a&&K===x,disabled:l,maxLength:c,className:q})},K)},[f,S,O,i,a,l,c,d,n,x,U,b]),Y=()=>h.jsx(I,{className:u,children:h.jsx(I,{className:"space-y-4",children:w.map((K,q)=>B(K,q===0))})}),V=()=>h.jsxs(I,{className:u,children:[h.jsxs(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:[w.map(K=>h.jsxs(ae,{type:"button",className:` inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold transition-all - ${T===K?K===x?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":K===x?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"} - `,onClick:()=>P(K),children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),g?h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:H(K)}),h.jsx(Q,{className:"inline md:hidden",children:K.toUpperCase()})]}):H(K),K===x&&h.jsx(Q,{className:"text-red-500 dark:text-red-400",children:"*"}),j(K)&&T!==K&&h.jsx(re,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"}),K!==x&&h.jsx(oe,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors",onClick:Z=>{Z.stopPropagation(),z(K)},disabled:l,children:h.jsx(re,{name:"xmark",className:"w-3 h-3"})})]},K)),W()]}),w.map(K=>h.jsx(I,{className:T===K?"block":"hidden",children:B(K)},K))]}),X=()=>{const K=b?"input input-error":"input",Z=b?"textarea input-error":"textarea";return h.jsx(I,{className:u,children:h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(I,{className:"flex items-center shrink-0",children:w.map(ee=>h.jsxs(oe,{type:"button",className:` + ${A===K?K===x?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":K===x?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"} + `,onClick:()=>P(K),children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),g?h.jsxs(h.Fragment,{children:[h.jsx(J,{className:"hidden md:inline",children:U(K)}),h.jsx(J,{className:"inline md:hidden",children:K.toUpperCase()})]}):U(K),K===x&&h.jsx(J,{className:"text-red-500 dark:text-red-400",children:"*"}),W(K)&&A!==K&&h.jsx(oe,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"}),K!==x&&h.jsx(ae,{type:"button",className:"ml-0.5 p-0.5 rounded-full hover:bg-gray-300 dark:hover:bg-gray-500 transition-colors",onClick:q=>{q.stopPropagation(),z(K)},disabled:l,children:h.jsx(oe,{name:"xmark",className:"w-3 h-3"})})]},K)),G()]}),w.map(K=>h.jsx(I,{className:A===K?"block":"hidden",children:j(K)},K))]}),X=()=>{const K=b?"input input-error":"input",q=b?"textarea input-error":"textarea";return h.jsx(I,{className:u,children:h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(I,{className:"flex items-center shrink-0",children:w.map(ne=>h.jsxs(ae,{type:"button",className:` px-2 py-1.5 text-xs font-medium border-y border-l first:rounded-l last:rounded-r last:border-r transition-colors - ${T===ee?"bg-blue-500 text-white border-blue-500 dark:bg-blue-600 dark:border-blue-600":"bg-white text-gray-600 border-gray-200 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700"} - `,onClick:()=>P(ee),title:H(ee),children:[ee.toUpperCase(),ee===x&&h.jsx(Q,{className:"text-red-400 ml-0.5",children:"*"})]},ee))}),h.jsx(I,{className:"flex-1 min-w-0",children:n==="textarea"?h.jsx(Qn,{name:`${f}[${T}]`,value:C[T]||"",onChange:ee=>D(T,ee.target.value),placeholder:i,required:a&&T===x,disabled:l,maxLength:c,rows:d,className:Z}):h.jsx(kt,{type:"text",name:`${f}[${T}]`,value:C[T]||"",onChange:ee=>D(T,ee.target.value),placeholder:i,required:a&&T===x,disabled:l,maxLength:c,className:K})})]})})};return h.jsxs(I,{className:l?"opacity-50 cursor-not-allowed":void 0,id:p,...y,children:[r==="compact"?X():r==="tabs"?V():G(),b&&m&&h.jsx(Q,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:m})]})};Ha.displayName="MultilingualInput";const vb=()=>window.G7Core,Ua=({checked:t,value:e,onChange:n,disabled:r=!1,label:s,description:o,size:i="md",className:a="",name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(()=>t??e??!1);v.useEffect(()=>{f(t??e??!1)},[t,e]);const g=_=>{const x=_.target.checked;f(x),n&&!r&&n(_)},p={sm:{track:"w-9 h-5",thumb:"after:h-4 after:w-4"},md:{track:"w-11 h-6",thumb:"after:h-5 after:w-5"},lg:{track:"w-14 h-7",thumb:"after:h-6 after:w-6"}}[i],y=()=>{if(!r){const _=!u;if(f(_),n){const x=vb();x?.createChangeEvent&&n(x.createChangeEvent({checked:_,name:l}))}}},b=_=>{r||_.key!==" "&&_.key!=="Enter"&&_.key!=="Spacebar"||(_.preventDefault(),y())};return h.jsxs("div",{className:`toggle-container ${a}`,id:c,...d,children:[h.jsxs("div",{className:`toggle-switch-wrapper ${r?"cursor-not-allowed opacity-50":"cursor-pointer"}`,role:"switch","aria-checked":u,"aria-disabled":r||void 0,"aria-label":s??l,tabIndex:r?-1:0,onKeyDown:b,onClick:_=>{_.preventDefault(),_.stopPropagation(),y()},children:[h.jsx("input",{type:"checkbox",id:l,name:l,checked:u,onChange:g,disabled:r,className:"sr-only peer",tabIndex:-1}),h.jsx("div",{className:`toggle-switch-track ${p.track} ${p.thumb}`})]}),s&&h.jsx("span",{className:`toggle-label ${r?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:y,children:s}),o&&h.jsx("p",{className:"toggle-description",children:o})]})},Fo=({status:t,label:e,showIcon:n=!0,iconName:r,className:s="",style:o,id:i,editorAttrs:a})=>{const l={success:"bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-400 border-green-200 dark:border-green-800",warning:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800",error:"bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 border-red-200 dark:border-red-800",info:"bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border-blue-200 dark:border-blue-800",pending:"bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-600",default:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-600"},c={success:de.CheckCircle,warning:de.ExclamationTriangle,error:de.XCircle,info:de.InfoCircle,pending:de.Clock,default:de.QuestionCircle},d={success:"성공",warning:"경고",error:"오류",info:"정보",pending:"대기 중",default:"기본"},u=r||c[t],f=e||d[t],g=l[t];return h.jsxs(Q,{className:`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border ${g} ${s}`,style:o,id:i,...a,children:[n&&h.jsx(re,{name:u,className:"w-4 h-4"}),h.jsx(Q,{children:f})]})},Dd={blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",gray:"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300",teal:"bg-teal-100 text-teal-800 dark:bg-teal-900/30 dark:text-teal-300",cyan:"bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300"},xb={success:"green",warning:"yellow",danger:"red",error:"red",info:"blue",primary:"blue",secondary:"gray",text:"gray",default:"gray"},Id={sm:"px-1.5 py-0.5 text-[10px]",md:"px-2 py-0.5 text-xs",lg:"px-2.5 py-1 text-sm"},Ld=({color:t,variant:e,text:n,size:r="md",className:s="",style:o,children:i,id:a,editorAttrs:l})=>{const c=t||(e?xb[e]:void 0)||"gray",d=Dd[c]||Dd.gray,u=Id[r]||Id.md;return h.jsxs(Q,{className:`inline-flex items-center rounded-full font-medium ${u} ${d} ${s}`,style:o,id:a,...l,children:[n,i]})};const{entries:Pd,setPrototypeOf:$d,isFrozen:wb,getPrototypeOf:kb,getOwnPropertyDescriptor:Cb}=Object;let{freeze:Gt,seal:cn,create:Fd}=Object,{apply:Ba,construct:ja}=typeof Reflect<"u"&&Reflect;Gt||(Gt=function(e){return e}),cn||(cn=function(e){return e}),Ba||(Ba=function(e,n){for(var r=arguments.length,s=new Array(r>2?r-2:0),o=2;o1?n-1:0),s=1;s1?n-1:0),s=1;s2&&arguments[2]!==void 0?arguments[2]:Vo;$d&&$d(t,null);let r=e.length;for(;r--;){let s=e[r];if(typeof s=="string"){const o=n(s);o!==s&&(wb(e)||(e[r]=o),s=o)}t[s]=!0}return t}function Ob(t){for(let e=0;e/gm),Lb=cn(/\$\{[\w\W]*/gm),Pb=cn(/^data-[\-\w.\u00B7-\uFFFF]+$/),$b=cn(/^aria-[\-\w]+$/),jd=cn(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Fb=cn(/^(?:\w+script|data):/i),zb=cn(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Wd=cn(/^html$/i),Vb=cn(/^[a-z][.\w]*(-[.\w]+)+$/i);var Gd=Object.freeze({__proto__:null,ARIA_ATTR:$b,ATTR_WHITESPACE:zb,CUSTOM_ELEMENT:Vb,DATA_ATTR:Pb,DOCTYPE_NAME:Wd,ERB_EXPR:Ib,IS_ALLOWED_URI:jd,IS_SCRIPT_OR_DATA:Fb,MUSTACHE_EXPR:Db,TMPLIT_EXPR:Lb});const _s={element:1,text:3,progressingInstruction:7,comment:8,document:9},Hb=function(){return typeof window>"u"?null:window},Ub=function(e,n){if(typeof e!="object"||typeof e.createPolicy!="function")return null;let r=null;const s="data-tt-policy-suffix";n&&n.hasAttribute(s)&&(r=n.getAttribute(s));const o="dompurify"+(r?"#"+r:"");try{return e.createPolicy(o,{createHTML(i){return i},createScriptURL(i){return i}})}catch{return console.warn("TrustedTypes policy "+o+" could not be created."),null}},Yd=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function Kd(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:Hb();const e=ke=>Kd(ke);if(e.version="3.2.7",e.removed=[],!t||!t.document||t.document.nodeType!==_s.document||!t.Element)return e.isSupported=!1,e;let{document:n}=t;const r=n,s=r.currentScript,{DocumentFragment:o,HTMLTemplateElement:i,Node:a,Element:l,NodeFilter:c,NamedNodeMap:d=t.NamedNodeMap||t.MozNamedAttrMap,HTMLFormElement:u,DOMParser:f,trustedTypes:g}=t,m=l.prototype,p=Ss(m,"cloneNode"),y=Ss(m,"remove"),b=Ss(m,"nextSibling"),_=Ss(m,"childNodes"),x=Ss(m,"parentNode");if(typeof i=="function"){const ke=n.createElement("template");ke.content&&ke.content.ownerDocument&&(n=ke.content.ownerDocument)}let E,C="";const{implementation:k,createNodeIterator:N,createDocumentFragment:S,getElementsByTagName:w}=n,{importNode:M}=r;let T=Yd();e.isSupported=typeof Pd=="function"&&typeof x=="function"&&k&&k.createHTMLDocument!==void 0;const{MUSTACHE_EXPR:P,ERB_EXPR:L,TMPLIT_EXPR:$,DATA_ATTR:A,ARIA_ATTR:D,IS_SCRIPT_OR_DATA:O,ATTR_WHITESPACE:z,CUSTOM_ELEMENT:H}=Gd;let{IS_ALLOWED_URI:j}=Gd,W=null;const U=Ge({},[...Vd,...Ya,...Ka,...Xa,...Hd]);let B=null;const G=Ge({},[...Ud,...qa,...Bd,...Ho]);let V=Object.seal(Fd(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),X=null,K=null,Z=!0,ee=!0,ne=!1,he=!0,J=!1,le=!0,ue=!1,ae=!1,ve=!1,fe=!1,ye=!1,we=!1,He=!0,se=!1;const Oe="user-content-";let Le=!0,Ie=!1,Be={},st=null;const ut=Ge({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let je=null;const nt=Ge({},["audio","video","img","source","image","track"]);let Ye=null;const te=Ge({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ge="http://www.w3.org/1998/Math/MathML",me="http://www.w3.org/2000/svg",pe="http://www.w3.org/1999/xhtml";let be=pe,Ue=!1,Ce=null;const ze=Ge({},[ge,me,pe],Wa);let Se=Ge({},["mi","mo","mn","ms","mtext"]),Re=Ge({},["annotation-xml"]);const ot=Ge({},["title","style","font","a","script"]);let Pe=null;const Te=["application/xhtml+xml","text/html"],Ve="text/html";let De=null,qe=null;const it=n.createElement("form"),pt=function(Y){return Y instanceof RegExp||Y instanceof Function},rn=function(){let Y=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!(qe&&qe===Y)){if((!Y||typeof Y!="object")&&(Y={}),Y=Bn(Y),Pe=Te.indexOf(Y.PARSER_MEDIA_TYPE)===-1?Ve:Y.PARSER_MEDIA_TYPE,De=Pe==="application/xhtml+xml"?Wa:Vo,W=xn(Y,"ALLOWED_TAGS")?Ge({},Y.ALLOWED_TAGS,De):U,B=xn(Y,"ALLOWED_ATTR")?Ge({},Y.ALLOWED_ATTR,De):G,Ce=xn(Y,"ALLOWED_NAMESPACES")?Ge({},Y.ALLOWED_NAMESPACES,Wa):ze,Ye=xn(Y,"ADD_URI_SAFE_ATTR")?Ge(Bn(te),Y.ADD_URI_SAFE_ATTR,De):te,je=xn(Y,"ADD_DATA_URI_TAGS")?Ge(Bn(nt),Y.ADD_DATA_URI_TAGS,De):nt,st=xn(Y,"FORBID_CONTENTS")?Ge({},Y.FORBID_CONTENTS,De):ut,X=xn(Y,"FORBID_TAGS")?Ge({},Y.FORBID_TAGS,De):Bn({}),K=xn(Y,"FORBID_ATTR")?Ge({},Y.FORBID_ATTR,De):Bn({}),Be=xn(Y,"USE_PROFILES")?Y.USE_PROFILES:!1,Z=Y.ALLOW_ARIA_ATTR!==!1,ee=Y.ALLOW_DATA_ATTR!==!1,ne=Y.ALLOW_UNKNOWN_PROTOCOLS||!1,he=Y.ALLOW_SELF_CLOSE_IN_ATTR!==!1,J=Y.SAFE_FOR_TEMPLATES||!1,le=Y.SAFE_FOR_XML!==!1,ue=Y.WHOLE_DOCUMENT||!1,fe=Y.RETURN_DOM||!1,ye=Y.RETURN_DOM_FRAGMENT||!1,we=Y.RETURN_TRUSTED_TYPE||!1,ve=Y.FORCE_BODY||!1,He=Y.SANITIZE_DOM!==!1,se=Y.SANITIZE_NAMED_PROPS||!1,Le=Y.KEEP_CONTENT!==!1,Ie=Y.IN_PLACE||!1,j=Y.ALLOWED_URI_REGEXP||jd,be=Y.NAMESPACE||pe,Se=Y.MATHML_TEXT_INTEGRATION_POINTS||Se,Re=Y.HTML_INTEGRATION_POINTS||Re,V=Y.CUSTOM_ELEMENT_HANDLING||{},Y.CUSTOM_ELEMENT_HANDLING&&pt(Y.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(V.tagNameCheck=Y.CUSTOM_ELEMENT_HANDLING.tagNameCheck),Y.CUSTOM_ELEMENT_HANDLING&&pt(Y.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(V.attributeNameCheck=Y.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),Y.CUSTOM_ELEMENT_HANDLING&&typeof Y.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(V.allowCustomizedBuiltInElements=Y.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),J&&(ee=!1),ye&&(fe=!0),Be&&(W=Ge({},Hd),B=[],Be.html===!0&&(Ge(W,Vd),Ge(B,Ud)),Be.svg===!0&&(Ge(W,Ya),Ge(B,qa),Ge(B,Ho)),Be.svgFilters===!0&&(Ge(W,Ka),Ge(B,qa),Ge(B,Ho)),Be.mathMl===!0&&(Ge(W,Xa),Ge(B,Bd),Ge(B,Ho))),Y.ADD_TAGS&&(W===U&&(W=Bn(W)),Ge(W,Y.ADD_TAGS,De)),Y.ADD_ATTR&&(B===G&&(B=Bn(B)),Ge(B,Y.ADD_ATTR,De)),Y.ADD_URI_SAFE_ATTR&&Ge(Ye,Y.ADD_URI_SAFE_ATTR,De),Y.FORBID_CONTENTS&&(st===ut&&(st=Bn(st)),Ge(st,Y.FORBID_CONTENTS,De)),Le&&(W["#text"]=!0),ue&&Ge(W,["html","head","body"]),W.table&&(Ge(W,["tbody"]),delete X.tbody),Y.TRUSTED_TYPES_POLICY){if(typeof Y.TRUSTED_TYPES_POLICY.createHTML!="function")throw Cs('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof Y.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw Cs('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');E=Y.TRUSTED_TYPES_POLICY,C=E.createHTML("")}else E===void 0&&(E=Ub(g,s)),E!==null&&typeof C=="string"&&(C=E.createHTML(""));Gt&&Gt(Y),qe=Y}},bt=Ge({},[...Ya,...Ka,...Tb]),wt=Ge({},[...Xa,...Ab]),jt=function(Y){let ie=x(Y);(!ie||!ie.tagName)&&(ie={namespaceURI:be,tagName:"template"});const xe=Vo(Y.tagName),ht=Vo(ie.tagName);return Ce[Y.namespaceURI]?Y.namespaceURI===me?ie.namespaceURI===pe?xe==="svg":ie.namespaceURI===ge?xe==="svg"&&(ht==="annotation-xml"||Se[ht]):!!bt[xe]:Y.namespaceURI===ge?ie.namespaceURI===pe?xe==="math":ie.namespaceURI===me?xe==="math"&&Re[ht]:!!wt[xe]:Y.namespaceURI===pe?ie.namespaceURI===me&&!Re[ht]||ie.namespaceURI===ge&&!Se[ht]?!1:!wt[xe]&&(ot[xe]||!bt[xe]):!!(Pe==="application/xhtml+xml"&&Ce[Y.namespaceURI]):!1},ft=function(Y){ws(e.removed,{element:Y});try{x(Y).removeChild(Y)}catch{y(Y)}},Ke=function(Y,ie){try{ws(e.removed,{attribute:ie.getAttributeNode(Y),from:ie})}catch{ws(e.removed,{attribute:null,from:ie})}if(ie.removeAttribute(Y),Y==="is")if(fe||ye)try{ft(ie)}catch{}else try{ie.setAttribute(Y,"")}catch{}},Nn=function(Y){let ie=null,xe=null;if(ve)Y=""+Y;else{const q=Ga(Y,/^[\r\n\t ]+/);xe=q&&q[0]}Pe==="application/xhtml+xml"&&be===pe&&(Y=''+Y+"");const ht=E?E.createHTML(Y):Y;if(be===pe)try{ie=new f().parseFromString(ht,Pe)}catch{}if(!ie||!ie.documentElement){ie=k.createDocument(be,"template",null);try{ie.documentElement.innerHTML=Ue?C:ht}catch{}}const Lt=ie.body||ie.documentElement;return Y&&xe&&Lt.insertBefore(n.createTextNode(xe),Lt.childNodes[0]||null),be===pe?w.call(ie,ue?"html":"body")[0]:ue?ie.documentElement:Lt},Wt=function(Y){return N.call(Y.ownerDocument||Y,Y,c.SHOW_ELEMENT|c.SHOW_COMMENT|c.SHOW_TEXT|c.SHOW_PROCESSING_INSTRUCTION|c.SHOW_CDATA_SECTION,null)},bn=function(Y){return Y instanceof u&&(typeof Y.nodeName!="string"||typeof Y.textContent!="string"||typeof Y.removeChild!="function"||!(Y.attributes instanceof d)||typeof Y.removeAttribute!="function"||typeof Y.setAttribute!="function"||typeof Y.namespaceURI!="string"||typeof Y.insertBefore!="function"||typeof Y.hasChildNodes!="function")},hr=function(Y){return typeof a=="function"&&Y instanceof a};function On(ke,Y,ie){zo(ke,xe=>{xe.call(e,Y,ie,qe)})}const Ao=function(Y){let ie=null;if(On(T.beforeSanitizeElements,Y,null),bn(Y))return ft(Y),!0;const xe=De(Y.nodeName);if(On(T.uponSanitizeElement,Y,{tagName:xe,allowedTags:W}),le&&Y.hasChildNodes()&&!hr(Y.firstElementChild)&&Yt(/<[/\w!]/g,Y.innerHTML)&&Yt(/<[/\w!]/g,Y.textContent)||Y.nodeType===_s.progressingInstruction||le&&Y.nodeType===_s.comment&&Yt(/<[/\w]/g,Y.data))return ft(Y),!0;if(!W[xe]||X[xe]){if(!X[xe]&&Io(xe)&&(V.tagNameCheck instanceof RegExp&&Yt(V.tagNameCheck,xe)||V.tagNameCheck instanceof Function&&V.tagNameCheck(xe)))return!1;if(Le&&!st[xe]){const ht=x(Y)||Y.parentNode,Lt=_(Y)||Y.childNodes;if(Lt&&ht){const q=Lt.length;for(let ce=q-1;ce>=0;--ce){const Ee=p(Lt[ce],!0);Ee.__removalCount=(Y.__removalCount||0)+1,ht.insertBefore(Ee,b(Y))}}}return ft(Y),!0}return Y instanceof l&&!jt(Y)||(xe==="noscript"||xe==="noembed"||xe==="noframes")&&Yt(/<\/no(script|embed|frames)/i,Y.innerHTML)?(ft(Y),!0):(J&&Y.nodeType===_s.text&&(ie=Y.textContent,zo([P,L,$],ht=>{ie=ks(ie,ht," ")}),Y.textContent!==ie&&(ws(e.removed,{element:Y.cloneNode()}),Y.textContent=ie)),On(T.afterSanitizeElements,Y,null),!1)},Do=function(Y,ie,xe){if(He&&(ie==="id"||ie==="name")&&(xe in n||xe in it))return!1;if(!(ee&&!K[ie]&&Yt(A,ie))){if(!(Z&&Yt(D,ie))){if(!B[ie]||K[ie]){if(!(Io(Y)&&(V.tagNameCheck instanceof RegExp&&Yt(V.tagNameCheck,Y)||V.tagNameCheck instanceof Function&&V.tagNameCheck(Y))&&(V.attributeNameCheck instanceof RegExp&&Yt(V.attributeNameCheck,ie)||V.attributeNameCheck instanceof Function&&V.attributeNameCheck(ie,Y))||ie==="is"&&V.allowCustomizedBuiltInElements&&(V.tagNameCheck instanceof RegExp&&Yt(V.tagNameCheck,xe)||V.tagNameCheck instanceof Function&&V.tagNameCheck(xe))))return!1}else if(!Ye[ie]){if(!Yt(j,ks(xe,z,""))){if(!((ie==="src"||ie==="xlink:href"||ie==="href")&&Y!=="script"&&Eb(xe,"data:")===0&&je[Y])){if(!(ne&&!Yt(O,ks(xe,z,"")))){if(xe)return!1}}}}}}return!0},Io=function(Y){return Y!=="annotation-xml"&&Ga(Y,H)},Lo=function(Y){On(T.beforeSanitizeAttributes,Y,null);const{attributes:ie}=Y;if(!ie||bn(Y))return;const xe={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:B,forceKeepAttr:void 0};let ht=ie.length;for(;ht--;){const Lt=ie[ht],{name:q,namespaceURI:ce,value:Ee}=Lt,Ze=De(q),Fe=Ee;let Nt=q==="value"?Fe:Mb(Fe);if(xe.attrName=Ze,xe.attrValue=Nt,xe.keepAttr=!0,xe.forceKeepAttr=void 0,On(T.uponSanitizeAttribute,Y,xe),Nt=xe.attrValue,se&&(Ze==="id"||Ze==="name")&&(Ke(q,Y),Nt=Oe+Nt),le&&Yt(/((--!?|])>)|<\/(style|title|textarea)/i,Nt)){Ke(q,Y);continue}if(Ze==="attributename"&&Ga(Nt,"href")){Ke(q,Y);continue}if(xe.forceKeepAttr)continue;if(!xe.keepAttr){Ke(q,Y);continue}if(!he&&Yt(/\/>/i,Nt)){Ke(q,Y);continue}J&&zo([P,L,$],Rm=>{Nt=ks(Nt,Rm," ")});const Jm=De(Y.nodeName);if(!Do(Jm,Ze,Nt)){Ke(q,Y);continue}if(E&&typeof g=="object"&&typeof g.getAttributeType=="function"&&!ce)switch(g.getAttributeType(Jm,Ze)){case"TrustedHTML":{Nt=E.createHTML(Nt);break}case"TrustedScriptURL":{Nt=E.createScriptURL(Nt);break}}if(Nt!==Fe)try{ce?Y.setAttributeNS(ce,q,Nt):Y.setAttribute(q,Nt),bn(Y)?ft(Y):zd(e.removed)}catch{Ke(q,Y)}}On(T.afterSanitizeAttributes,Y,null)},vs=function ke(Y){let ie=null;const xe=Wt(Y);for(On(T.beforeSanitizeShadowDOM,Y,null);ie=xe.nextNode();)On(T.uponSanitizeShadowNode,ie,null),Ao(ie),Lo(ie),ie.content instanceof o&&ke(ie.content);On(T.afterSanitizeShadowDOM,Y,null)};return e.sanitize=function(ke){let Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},ie=null,xe=null,ht=null,Lt=null;if(Ue=!ke,Ue&&(ke=""),typeof ke!="string"&&!hr(ke))if(typeof ke.toString=="function"){if(ke=ke.toString(),typeof ke!="string")throw Cs("dirty is not a string, aborting")}else throw Cs("toString is not a function");if(!e.isSupported)return ke;if(ae||rn(Y),e.removed=[],typeof ke=="string"&&(Ie=!1),Ie){if(ke.nodeName){const Ee=De(ke.nodeName);if(!W[Ee]||X[Ee])throw Cs("root node is forbidden and cannot be sanitized in-place")}}else if(ke instanceof a)ie=Nn(""),xe=ie.ownerDocument.importNode(ke,!0),xe.nodeType===_s.element&&xe.nodeName==="BODY"||xe.nodeName==="HTML"?ie=xe:ie.appendChild(xe);else{if(!fe&&!J&&!ue&&ke.indexOf("<")===-1)return E&&we?E.createHTML(ke):ke;if(ie=Nn(ke),!ie)return fe?null:we?C:""}ie&&ve&&ft(ie.firstChild);const q=Wt(Ie?ke:ie);for(;ht=q.nextNode();)Ao(ht),Lo(ht),ht.content instanceof o&&vs(ht.content);if(Ie)return ke;if(fe){if(ye)for(Lt=S.call(ie.ownerDocument);ie.firstChild;)Lt.appendChild(ie.firstChild);else Lt=ie;return(B.shadowroot||B.shadowrootmode)&&(Lt=M.call(r,Lt,!0)),Lt}let ce=ue?ie.outerHTML:ie.innerHTML;return ue&&W["!doctype"]&&ie.ownerDocument&&ie.ownerDocument.doctype&&ie.ownerDocument.doctype.name&&Yt(Wd,ie.ownerDocument.doctype.name)&&(ce=" -`+ce),J&&zo([P,L,$],Ee=>{ce=ks(ce,Ee," ")}),E&&we?E.createHTML(ce):ce},e.setConfig=function(){let ke=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};rn(ke),ae=!0},e.clearConfig=function(){qe=null,ae=!1},e.isValidAttribute=function(ke,Y,ie){qe||rn({});const xe=De(ke),ht=De(Y);return Do(xe,ht,ie)},e.addHook=function(ke,Y){typeof Y=="function"&&ws(T[ke],Y)},e.removeHook=function(ke,Y){if(Y!==void 0){const ie=Sb(T[ke],Y);return ie===-1?void 0:_b(T[ke],ie,1)[0]}return zd(T[ke])},e.removeHooks=function(ke){T[ke]=[]},e.removeAllHooks=function(){T=Yd()},e}var Bb=Kd();const Za=({content:t,text:e,isHtml:n=!0,className:r="",purifyConfig:s,id:o,editorAttrs:i})=>{const a=e??t??"";if(!a||a.trim()==="")return null;if(!n){const u=` + ${A===ne?"bg-blue-500 text-white border-blue-500 dark:bg-blue-600 dark:border-blue-600":"bg-white text-gray-600 border-gray-200 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700"} + `,onClick:()=>P(ne),title:U(ne),children:[ne.toUpperCase(),ne===x&&h.jsx(J,{className:"text-red-400 ml-0.5",children:"*"})]},ne))}),h.jsx(I,{className:"flex-1 min-w-0",children:n==="textarea"?h.jsx(sr,{name:`${f}[${A}]`,value:S[A]||"",onChange:ne=>O(A,ne.target.value),placeholder:i,required:a&&A===x,disabled:l,maxLength:c,rows:d,className:q}):h.jsx(kt,{type:"text",name:`${f}[${A}]`,value:S[A]||"",onChange:ne=>O(A,ne.target.value),placeholder:i,required:a&&A===x,disabled:l,maxLength:c,className:K})})]})})};return h.jsxs(I,{className:l?"opacity-50 cursor-not-allowed":void 0,id:p,...y,children:[r==="compact"?X():r==="tabs"?V():Y(),b&&m&&h.jsx(J,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:m})]})};Za.displayName="MultilingualInput";const Ab=()=>window.G7Core,Qa=({checked:e,value:t,onChange:n,disabled:r=!1,label:s,description:o,size:i="md",className:a="",name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(()=>e??t??!1);v.useEffect(()=>{f(e??t??!1)},[e,t]);const g=_=>{const x=_.target.checked;f(x),n&&!r&&n(_)},p={sm:{track:"w-9 h-5",thumb:"after:h-4 after:w-4"},md:{track:"w-11 h-6",thumb:"after:h-5 after:w-5"},lg:{track:"w-14 h-7",thumb:"after:h-6 after:w-6"}}[i],y=()=>{if(!r){const _=!u;if(f(_),n){const x=Ab();x?.createChangeEvent&&n(x.createChangeEvent({checked:_,name:l}))}}},b=_=>{r||_.key!==" "&&_.key!=="Enter"&&_.key!=="Spacebar"||(_.preventDefault(),y())};return h.jsxs("div",{className:`toggle-container ${a}`,id:c,...d,children:[h.jsxs("div",{className:`toggle-switch-wrapper ${r?"cursor-not-allowed opacity-50":"cursor-pointer"}`,role:"switch","aria-checked":u,"aria-disabled":r||void 0,"aria-label":s??l,tabIndex:r?-1:0,onKeyDown:b,onClick:_=>{_.preventDefault(),_.stopPropagation(),y()},children:[h.jsx("input",{type:"checkbox",id:l,name:l,checked:u,onChange:g,disabled:r,className:"sr-only peer",tabIndex:-1}),h.jsx("div",{className:`toggle-switch-track ${p.track} ${p.thumb}`})]}),s&&h.jsx("span",{className:`toggle-label ${r?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:y,children:s}),o&&h.jsx("p",{className:"toggle-description",children:o})]})},Xo=({status:e,label:t,showIcon:n=!0,iconName:r,className:s="",style:o,id:i,editorAttrs:a})=>{const l={success:"bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-400 border-green-200 dark:border-green-800",warning:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800",error:"bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-400 border-red-200 dark:border-red-800",info:"bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 border-blue-200 dark:border-blue-800",pending:"bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-600",default:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-600"},c={success:ue.CheckCircle,warning:ue.ExclamationTriangle,error:ue.XCircle,info:ue.InfoCircle,pending:ue.Clock,default:ue.QuestionCircle},d={success:"성공",warning:"경고",error:"오류",info:"정보",pending:"대기 중",default:"기본"},u=r||c[e],f=t||d[e],g=l[e];return h.jsxs(J,{className:`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border ${g} ${s}`,style:o,id:i,...a,children:[n&&h.jsx(oe,{name:u,className:"w-4 h-4"}),h.jsx(J,{children:f})]})},Ud={blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",gray:"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300",teal:"bg-teal-100 text-teal-800 dark:bg-teal-900/30 dark:text-teal-300",cyan:"bg-cyan-100 text-cyan-800 dark:bg-cyan-900/30 dark:text-cyan-300"},Db={success:"green",warning:"yellow",danger:"red",error:"red",info:"blue",primary:"blue",secondary:"gray",text:"gray",default:"gray"},Bd={sm:"px-1.5 py-0.5 text-[10px]",md:"px-2 py-0.5 text-xs",lg:"px-2.5 py-1 text-sm"},jd=({color:e,variant:t,text:n,size:r="md",className:s="",style:o,children:i,id:a,editorAttrs:l})=>{const c=e||(t?Db[t]:void 0)||"gray",d=Ud[c]||Ud.gray,u=Bd[r]||Bd.md;return h.jsxs(J,{className:`inline-flex items-center rounded-full font-medium ${u} ${d} ${s}`,style:o,id:a,...l,children:[n,i]})};function Wd(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n2?r-2:0),o=2;o1?n-1:0),s=1;s"u"?null:Ct(BigInt.prototype.toString),Jd=typeof Symbol>"u"?null:Ct(Symbol.prototype.toString),vt=Ct(Object.prototype.hasOwnProperty),$s=Ct(Object.prototype.toString),$t=Ct(RegExp.prototype.test),Qr=Yb(TypeError);function Ct(e){return function(t){t instanceof RegExp&&(t.lastIndex=0);for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s2&&arguments[2]!==void 0?arguments[2]:Ps;if(Yd&&Yd(e,null),!Xt(t))return e;let r=t.length;for(;r--;){let s=t[r];if(typeof s=="string"){const o=n(s);o!==s&&(zb(t)||(t[r]=o),s=o)}e[s]=!0}return e}function Xb(e){for(let t=0;t/g),ey=fn(/\${[\w\W]*/g),ty=fn(/^data-[\-\w.\u00B7-\uFFFF]+$/),ny=fn(/^aria-[\-\w]+$/),ru=fn(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),ry=fn(/^(?:\w+script|data):/i),sy=fn(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),oy=fn(/^html$/i),iy=fn(/^[a-z][.\w]*(-[.\w]+)+$/i),$n={element:1,attribute:2,text:3,cdataSection:4,entityReference:5,entityNode:6,progressingInstruction:7,comment:8,document:9,documentType:10,documentFragment:11,notation:12},ay=function(){return typeof window>"u"?null:window},ly=function(t,n){if(typeof t!="object"||typeof t.createPolicy!="function")return null;let r=null;const s="data-tt-policy-suffix";n&&n.hasAttribute(s)&&(r=n.getAttribute(s));const o="dompurify"+(r?"#"+r:"");try{return t.createPolicy(o,{createHTML(i){return i},createScriptURL(i){return i}})}catch{return console.warn("TrustedTypes policy "+o+" could not be created."),null}},su=function(){return{afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}};function ou(){let e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:ay();const t=se=>ou(se);if(t.version="3.4.8",t.removed=[],!e||!e.document||e.document.nodeType!==$n.document||!e.Element)return t.isSupported=!1,t;let n=e.document;const r=n,s=r.currentScript;e.DocumentFragment;const o=e.HTMLTemplateElement,i=e.Node,a=e.Element,l=e.NodeFilter,c=e.NamedNodeMap;c===void 0&&(e.NamedNodeMap||e.MozNamedAttrMap),e.HTMLFormElement;const d=e.DOMParser,u=e.trustedTypes,f=a.prototype,g=Pn(f,"cloneNode"),m=Pn(f,"remove"),p=Pn(f,"nextSibling"),y=Pn(f,"childNodes"),b=Pn(f,"parentNode"),_=Pn(f,"shadowRoot"),x=Pn(f,"attributes"),M=i&&i.prototype?Pn(i.prototype,"nodeType"):null,S=i&&i.prototype?Pn(i.prototype,"nodeName"):null;if(typeof o=="function"){const se=n.createElement("template");se.content&&se.content.ownerDocument&&(n=se.content.ownerDocument)}let k,N="",C=0;const w=function(H){if(C>0)throw Qr('The configured TRUSTED_TYPES_POLICY.createHTML must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose createHTML wraps DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');C++;try{return k.createHTML(H)}finally{C--}},E=n,A=E.implementation,P=E.createNodeIterator,L=E.createDocumentFragment,$=E.getElementsByTagName,D=r.importNode;let O=su();t.isSupported=typeof Gd=="function"&&typeof b=="function"&&A&&A.createHTMLDocument!==void 0;const T=Jb,z=Rb,U=ey,W=ty,G=ny,B=ry,j=sy,Y=iy;let V=ru,X=null;const K=Ue({},[...Rd,...tl,...nl,...rl,...eu]);let q=null;const ne=Ue({},[...tu,...sl,...nu,...Ko]);let ee=Object.seal(Kr(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),fe=null,te=null;const le=Object.seal(Kr(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let ge=!0,ce=!0,ye=!1,me=!0,pe=!1,ke=!0,$e=!1,ie=!1,_e=!1,Ae=!1,Ie=!1,Be=!1,gt=!0,mt=!1;const Ge="user-content-";let ot=!0,Fe=!1,re={},de=null;const xe=Ue({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let ve=null;const we=Ue({},["audio","video","img","source","image","track"]);let Ve=null;const De=Ue({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ne="http://www.w3.org/1998/Math/MathML",Te="http://www.w3.org/2000/svg",He="http://www.w3.org/1999/xhtml";let Ke=He,Le=!1,Ee=null;const ze=Ue({},[Ne,Te,He],el);let rt=Ue({},["mi","mo","mn","ms","mtext"]),it=Ue({},["annotation-xml"]);const Ye=Ue({},["title","style","font","a","script"]);let at=null;const xn=["application/xhtml+xml","text/html"],bt="text/html";let qe=null,dt=null;const Zt=n.createElement("form"),on=function(H){return H instanceof RegExp||H instanceof Function},Je=function(){let H=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(dt&&dt===H)return;(!H||typeof H!="object")&&(H={}),H=Vt(H),at=xn.indexOf(H.PARSER_MEDIA_TYPE)===-1?bt:H.PARSER_MEDIA_TYPE,qe=at==="application/xhtml+xml"?el:Ps,X=vt(H,"ALLOWED_TAGS")&&Xt(H.ALLOWED_TAGS)?Ue({},H.ALLOWED_TAGS,qe):K,q=vt(H,"ALLOWED_ATTR")&&Xt(H.ALLOWED_ATTR)?Ue({},H.ALLOWED_ATTR,qe):ne,Ee=vt(H,"ALLOWED_NAMESPACES")&&Xt(H.ALLOWED_NAMESPACES)?Ue({},H.ALLOWED_NAMESPACES,el):ze,Ve=vt(H,"ADD_URI_SAFE_ATTR")&&Xt(H.ADD_URI_SAFE_ATTR)?Ue(Vt(De),H.ADD_URI_SAFE_ATTR,qe):De,ve=vt(H,"ADD_DATA_URI_TAGS")&&Xt(H.ADD_DATA_URI_TAGS)?Ue(Vt(we),H.ADD_DATA_URI_TAGS,qe):we,de=vt(H,"FORBID_CONTENTS")&&Xt(H.FORBID_CONTENTS)?Ue({},H.FORBID_CONTENTS,qe):xe,fe=vt(H,"FORBID_TAGS")&&Xt(H.FORBID_TAGS)?Ue({},H.FORBID_TAGS,qe):Vt({}),te=vt(H,"FORBID_ATTR")&&Xt(H.FORBID_ATTR)?Ue({},H.FORBID_ATTR,qe):Vt({}),re=vt(H,"USE_PROFILES")?H.USE_PROFILES&&typeof H.USE_PROFILES=="object"?Vt(H.USE_PROFILES):H.USE_PROFILES:!1,ge=H.ALLOW_ARIA_ATTR!==!1,ce=H.ALLOW_DATA_ATTR!==!1,ye=H.ALLOW_UNKNOWN_PROTOCOLS||!1,me=H.ALLOW_SELF_CLOSE_IN_ATTR!==!1,pe=H.SAFE_FOR_TEMPLATES||!1,ke=H.SAFE_FOR_XML!==!1,$e=H.WHOLE_DOCUMENT||!1,Ae=H.RETURN_DOM||!1,Ie=H.RETURN_DOM_FRAGMENT||!1,Be=H.RETURN_TRUSTED_TYPE||!1,_e=H.FORCE_BODY||!1,gt=H.SANITIZE_DOM!==!1,mt=H.SANITIZE_NAMED_PROPS||!1,ot=H.KEEP_CONTENT!==!1,Fe=H.IN_PLACE||!1,V=qb(H.ALLOWED_URI_REGEXP)?H.ALLOWED_URI_REGEXP:ru,Ke=typeof H.NAMESPACE=="string"?H.NAMESPACE:He,rt=vt(H,"MATHML_TEXT_INTEGRATION_POINTS")&&H.MATHML_TEXT_INTEGRATION_POINTS&&typeof H.MATHML_TEXT_INTEGRATION_POINTS=="object"?Vt(H.MATHML_TEXT_INTEGRATION_POINTS):Ue({},["mi","mo","mn","ms","mtext"]),it=vt(H,"HTML_INTEGRATION_POINTS")&&H.HTML_INTEGRATION_POINTS&&typeof H.HTML_INTEGRATION_POINTS=="object"?Vt(H.HTML_INTEGRATION_POINTS):Ue({},["annotation-xml"]);const Z=vt(H,"CUSTOM_ELEMENT_HANDLING")&&H.CUSTOM_ELEMENT_HANDLING&&typeof H.CUSTOM_ELEMENT_HANDLING=="object"?Vt(H.CUSTOM_ELEMENT_HANDLING):Kr(null);if(ee=Kr(null),vt(Z,"tagNameCheck")&&on(Z.tagNameCheck)&&(ee.tagNameCheck=Z.tagNameCheck),vt(Z,"attributeNameCheck")&&on(Z.attributeNameCheck)&&(ee.attributeNameCheck=Z.attributeNameCheck),vt(Z,"allowCustomizedBuiltInElements")&&typeof Z.allowCustomizedBuiltInElements=="boolean"&&(ee.allowCustomizedBuiltInElements=Z.allowCustomizedBuiltInElements),pe&&(ce=!1),Ie&&(Ae=!0),re&&(X=Ue({},eu),q=Kr(null),re.html===!0&&(Ue(X,Rd),Ue(q,tu)),re.svg===!0&&(Ue(X,tl),Ue(q,sl),Ue(q,Ko)),re.svgFilters===!0&&(Ue(X,nl),Ue(q,sl),Ue(q,Ko)),re.mathMl===!0&&(Ue(X,rl),Ue(q,nu),Ue(q,Ko))),le.tagCheck=null,le.attributeCheck=null,vt(H,"ADD_TAGS")&&(typeof H.ADD_TAGS=="function"?le.tagCheck=H.ADD_TAGS:Xt(H.ADD_TAGS)&&(X===K&&(X=Vt(X)),Ue(X,H.ADD_TAGS,qe))),vt(H,"ADD_ATTR")&&(typeof H.ADD_ATTR=="function"?le.attributeCheck=H.ADD_ATTR:Xt(H.ADD_ATTR)&&(q===ne&&(q=Vt(q)),Ue(q,H.ADD_ATTR,qe))),vt(H,"ADD_URI_SAFE_ATTR")&&Xt(H.ADD_URI_SAFE_ATTR)&&Ue(Ve,H.ADD_URI_SAFE_ATTR,qe),vt(H,"FORBID_CONTENTS")&&Xt(H.FORBID_CONTENTS)&&(de===xe&&(de=Vt(de)),Ue(de,H.FORBID_CONTENTS,qe)),vt(H,"ADD_FORBID_CONTENTS")&&Xt(H.ADD_FORBID_CONTENTS)&&(de===xe&&(de=Vt(de)),Ue(de,H.ADD_FORBID_CONTENTS,qe)),ot&&(X["#text"]=!0),$e&&Ue(X,["html","head","body"]),X.table&&(Ue(X,["tbody"]),delete fe.tbody),H.TRUSTED_TYPES_POLICY){if(typeof H.TRUSTED_TYPES_POLICY.createHTML!="function")throw Qr('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if(typeof H.TRUSTED_TYPES_POLICY.createScriptURL!="function")throw Qr('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const he=k;k=H.TRUSTED_TYPES_POLICY;try{N=w("")}catch(tt){throw k=he,tt}}else k===void 0&&H.TRUSTED_TYPES_POLICY!==null&&(k=ly(u,s)),k&&typeof N=="string"&&(N=w(""));(O.uponSanitizeElement.length>0||O.uponSanitizeAttribute.length>0)&&X===K&&(X=Vt(X)),O.uponSanitizeAttribute.length>0&&q===ne&&(q=Vt(q)),Yt&&Yt(H),dt=H},Qt=Ue({},[...tl,...nl,...Zb]),wn=Ue({},[...rl,...Qb]),xr=function(H){let Z=b(H);(!Z||!Z.tagName)&&(Z={namespaceURI:Ke,tagName:"template"});const he=Ps(H.tagName),tt=Ps(Z.tagName);return Ee[H.namespaceURI]?H.namespaceURI===Te?Z.namespaceURI===He?he==="svg":Z.namespaceURI===Ne?he==="svg"&&(tt==="annotation-xml"||rt[tt]):!!Qt[he]:H.namespaceURI===Ne?Z.namespaceURI===He?he==="math":Z.namespaceURI===Te?he==="math"&&it[tt]:!!wn[he]:H.namespaceURI===He?Z.namespaceURI===Te&&!it[tt]||Z.namespaceURI===Ne&&!rt[tt]?!1:!wn[he]&&(Ye[he]||!Qt[he]):!!(at==="application/xhtml+xml"&&Ee[H.namespaceURI]):!1},an=function(H){qr(t.removed,{element:H});try{b(H).removeChild(H)}catch{m(H)}},tr=function(H,Z){try{qr(t.removed,{attribute:Z.getAttributeNode(H),from:Z})}catch{qr(t.removed,{attribute:null,from:Z})}if(Z.removeAttribute(H),H==="is")if(Ae||Ie)try{an(Z)}catch{}else try{Z.setAttribute(H,"")}catch{}},Va=function(H){let Z=null,he=null;if(_e)H=""+H;else{const ut=qd(H,/^[\r\n\t ]+/);he=ut&&ut[0]}at==="application/xhtml+xml"&&Ke===He&&(H=''+H+"");const tt=k?w(H):H;if(Ke===He)try{Z=new d().parseFromString(tt,at)}catch{}if(!Z||!Z.documentElement){Z=A.createDocument(Ke,"template",null);try{Z.documentElement.innerHTML=Le?N:tt}catch{}}const je=Z.body||Z.documentElement;return H&&he&&je.insertBefore(n.createTextNode(he),je.childNodes[0]||null),Ke===He?$.call(Z,$e?"html":"body")[0]:$e?Z.documentElement:je},Bo=function(H){return P.call(H.ownerDocument||H,H,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT|l.SHOW_PROCESSING_INSTRUCTION|l.SHOW_CDATA_SECTION,null)},Is=function(H){var Z,he;H.normalize();const tt=P.call(H.ownerDocument||H,H,l.SHOW_TEXT|l.SHOW_COMMENT|l.SHOW_CDATA_SECTION|l.SHOW_PROCESSING_INSTRUCTION,null);let je=tt.nextNode();for(;je;){let Tt=je.data;wr([T,z,U],Cn=>{Tt=Zr(Tt,Cn," ")}),je.data=Tt,je=tt.nextNode()}const ut=(Z=(he=H.querySelectorAll)===null||he===void 0?void 0:he.call(H,"template"))!==null&&Z!==void 0?Z:[];wr(Array.from(ut),Tt=>{nr(Tt.content)&&Is(Tt.content)})},Br=function(H){const Z=S?S(H):null;return typeof Z!="string"||qe(Z)!=="form"?!1:typeof H.nodeName!="string"||typeof H.textContent!="string"||typeof H.removeChild!="function"||H.attributes!==x(H)||typeof H.removeAttribute!="function"||typeof H.setAttribute!="function"||typeof H.namespaceURI!="string"||typeof H.insertBefore!="function"||typeof H.hasChildNodes!="function"||H.nodeType!==M(H)||H.childNodes!==y(H)},nr=function(H){if(!M||typeof H!="object"||H===null)return!1;try{return M(H)===$n.documentFragment}catch{return!1}},rr=function(H){if(!M||typeof H!="object"||H===null)return!1;try{return typeof M(H)=="number"}catch{return!1}};function kn(se,H,Z){wr(se,he=>{he.call(t,H,Z,dt)})}const jo=function(H){let Z=null;if(kn(O.beforeSanitizeElements,H,null),Br(H))return an(H),!0;const he=qe(S?S(H):H.nodeName);if(kn(O.uponSanitizeElement,H,{tagName:he,allowedTags:X}),ke&&H.hasChildNodes()&&!rr(H.firstElementChild)&&$t(/<[/\w!]/g,H.innerHTML)&&$t(/<[/\w!]/g,H.textContent)||ke&&H.namespaceURI===He&&he==="style"&&rr(H.firstElementChild)||H.nodeType===$n.progressingInstruction||ke&&H.nodeType===$n.comment&&$t(/<[/\w]/g,H.data))return an(H),!0;if(fe[he]||!(le.tagCheck instanceof Function&&le.tagCheck(he))&&!X[he]){if(!fe[he]&&Ha(he)&&(ee.tagNameCheck instanceof RegExp&&$t(ee.tagNameCheck,he)||ee.tagNameCheck instanceof Function&&ee.tagNameCheck(he)))return!1;if(ot&&!de[he]){const je=b(H),ut=y(H);if(ut&&je){const Tt=ut.length;for(let Cn=Tt-1;Cn>=0;--Cn){const In=g(ut[Cn],!0);je.insertBefore(In,p(H))}}}return an(H),!0}return(M?M(H):H.nodeType)===$n.element&&!xr(H)||(he==="noscript"||he==="noembed"||he==="noframes")&&$t(/<\/no(script|embed|frames)/i,H.innerHTML)?(an(H),!0):(pe&&H.nodeType===$n.text&&(Z=H.textContent,wr([T,z,U],je=>{Z=Zr(Z,je," ")}),H.textContent!==Z&&(qr(t.removed,{element:H.cloneNode()}),H.textContent=Z)),kn(O.afterSanitizeElements,H,null),!1)},Wo=function(H,Z,he){if(te[Z]||gt&&(Z==="id"||Z==="name")&&(he in n||he in Zt))return!1;const tt=q[Z]||le.attributeCheck instanceof Function&&le.attributeCheck(Z,H);if(!(ce&&!te[Z]&&$t(W,Z))){if(!(ge&&$t(G,Z))){if(!tt||te[Z]){if(!(Ha(H)&&(ee.tagNameCheck instanceof RegExp&&$t(ee.tagNameCheck,H)||ee.tagNameCheck instanceof Function&&ee.tagNameCheck(H))&&(ee.attributeNameCheck instanceof RegExp&&$t(ee.attributeNameCheck,Z)||ee.attributeNameCheck instanceof Function&&ee.attributeNameCheck(Z,H))||Z==="is"&&ee.allowCustomizedBuiltInElements&&(ee.tagNameCheck instanceof RegExp&&$t(ee.tagNameCheck,he)||ee.tagNameCheck instanceof Function&&ee.tagNameCheck(he))))return!1}else if(!Ve[Z]){if(!$t(V,Zr(he,j,""))){if(!((Z==="src"||Z==="xlink:href"||Z==="href")&&H!=="script"&&Zd(he,"data:")===0&&ve[H])){if(!(ye&&!$t(B,Zr(he,j,"")))){if(he)return!1}}}}}}return!0},Id=Ue({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Ha=function(H){return!Id[Ps(H)]&&$t(Y,H)},Ua=function(H){kn(O.beforeSanitizeAttributes,H,null);const Z=H.attributes;if(!Z||Br(H))return;const he={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:q,forceKeepAttr:void 0};let tt=Z.length;for(;tt--;){const je=Z[tt],ut=je.name,Tt=je.namespaceURI,Cn=je.value,In=qe(ut),Ld=Cn;let Pt=ut==="value"?Ld:jb(Ld);if(he.attrName=In,he.attrValue=Pt,he.keepAttr=!0,he.forceKeepAttr=void 0,kn(O.uponSanitizeAttribute,H,he),Pt=he.attrValue,mt&&(In==="id"||In==="name")&&Zd(Pt,Ge)!==0&&(tr(ut,H),Pt=Ge+Pt),ke&&$t(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,Pt)){tr(ut,H);continue}if(In==="attributename"&&qd(Pt,"href")){tr(ut,H);continue}if(he.forceKeepAttr)continue;if(!he.keepAttr){tr(ut,H);continue}if(!me&&$t(/\/>/i,Pt)){tr(ut,H);continue}pe&&wr([T,z,U],ub=>{Pt=Zr(Pt,ub," ")});const db=qe(H.nodeName);if(!Wo(db,In,Pt)){tr(ut,H);continue}if(k&&typeof u=="object"&&typeof u.getAttributeType=="function"&&!Tt)switch(u.getAttributeType(db,In)){case"TrustedHTML":{Pt=w(Pt);break}case"TrustedScriptURL":{Pt=k.createScriptURL(Pt);break}}if(Pt!==Ld)try{Tt?H.setAttributeNS(Tt,ut,Pt):H.setAttribute(ut,Pt),Br(H)?an(H):Kd(t.removed)}catch{tr(ut,H)}}kn(O.afterSanitizeAttributes,H,null)},Q=function(H){let Z=null;const he=Bo(H);for(kn(O.beforeSanitizeShadowDOM,H,null);Z=he.nextNode();)if(kn(O.uponSanitizeShadowNode,Z,null),jo(Z),Ua(Z),nr(Z.content)&&Q(Z.content),(M?M(Z):Z.nodeType)===$n.element){const je=_?_(Z):Z.shadowRoot;nr(je)&&(be(je),Q(je))}kn(O.afterSanitizeShadowDOM,H,null)},be=function(H){const Z=M?M(H):H.nodeType;if(Z===$n.element){const je=_?_(H):H.shadowRoot;nr(je)&&(be(je),Q(je))}const he=y?y(H):H.childNodes;if(!he)return;const tt=[];wr(he,je=>{qr(tt,je)});for(const je of tt)be(je);if(Z===$n.element){const je=S?S(H):null;if(typeof je=="string"&&qe(je)==="template"){const ut=H.content;nr(ut)&&be(ut)}}};return t.sanitize=function(se){let H=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},Z=null,he=null,tt=null,je=null;if(Le=!se,Le&&(se=""),typeof se!="string"&&!rr(se)&&(se=Kb(se),typeof se!="string"))throw Qr("dirty is not a string, aborting");if(!t.isSupported)return se;if(ie||Je(H),t.removed=[],typeof se=="string"&&(Fe=!1),Fe){const Cn=S?S(se):se.nodeName;if(typeof Cn=="string"){const In=qe(Cn);if(!X[In]||fe[In])throw Qr("root node is forbidden and cannot be sanitized in-place")}if(Br(se))throw Qr("root node is clobbered and cannot be sanitized in-place");be(se)}else if(rr(se))Z=Va(""),he=Z.ownerDocument.importNode(se,!0),he.nodeType===$n.element&&he.nodeName==="BODY"||he.nodeName==="HTML"?Z=he:Z.appendChild(he),be(he);else{if(!Ae&&!pe&&!$e&&se.indexOf("<")===-1)return k&&Be?w(se):se;if(Z=Va(se),!Z)return Ae?null:Be?N:""}Z&&_e&&an(Z.firstChild);const ut=Bo(Fe?se:Z);for(;tt=ut.nextNode();)jo(tt),Ua(tt),nr(tt.content)&&Q(tt.content);if(Fe)return pe&&Is(se),se;if(Ae){if(pe&&Is(Z),Ie)for(je=L.call(Z.ownerDocument);Z.firstChild;)je.appendChild(Z.firstChild);else je=Z;return(q.shadowroot||q.shadowrootmode)&&(je=D.call(r,je,!0)),je}let Tt=$e?Z.outerHTML:Z.innerHTML;return $e&&X["!doctype"]&&Z.ownerDocument&&Z.ownerDocument.doctype&&Z.ownerDocument.doctype.name&&$t(oy,Z.ownerDocument.doctype.name)&&(Tt=" +`+Tt),pe&&wr([T,z,U],Cn=>{Tt=Zr(Tt,Cn," ")}),k&&Be?w(Tt):Tt},t.setConfig=function(){let se=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Je(se),ie=!0},t.clearConfig=function(){dt=null,ie=!1},t.isValidAttribute=function(se,H,Z){dt||Je({});const he=qe(se),tt=qe(H);return Wo(he,tt,Z)},t.addHook=function(se,H){typeof H=="function"&&qr(O[se],H)},t.removeHook=function(se,H){if(H!==void 0){const Z=Ub(O[se],H);return Z===-1?void 0:Bb(O[se],Z,1)[0]}return Kd(O[se])},t.removeHooks=function(se){O[se]=[]},t.removeAllHooks=function(){O=su()},t}var cy=ou();const ol=({content:e,text:t,isHtml:n=!0,className:r="",purifyConfig:s,id:o,editorAttrs:i})=>{const a=t??e??"";if(!a||a.trim()==="")return null;if(!n){const u=` whitespace-pre-wrap text-gray-900 dark:text-gray-100 font-sans ${r} - `.trim().replace(/\s+/g," ");return h.jsx(I,{className:u,id:o,...i,children:a})}const l={FORBID_TAGS:["script","noscript","iframe","frame","frameset","object","embed","applet","portal","form","input","textarea","select","button","style","link","meta","base","body","head","html","title","svg","math","audio","video","source","track","canvas","details","dialog","plaintext","xmp","listing","marquee","noframes","noembed","template","slot"],FORBID_ATTR:["onerror","onload","onclick","onmouseover","onfocus","onblur","ontoggle","oncanplay","onloadstart","formaction","xlink:href","action"],ALLOW_DATA_ATTR:!0,ADD_ATTR:["target"]},c=v.useMemo(()=>{const u=s?{...l,...s,FORBID_TAGS:[...l.FORBID_TAGS,...s.FORBID_TAGS||[]],FORBID_ATTR:[...l.FORBID_ATTR,...s.FORBID_ATTR||[]]}:l;return Bb.sanitize(a,u).replace(/]*?)href=["']([^"']+)["']([^>]*?)>/gi,(g,m,p,y)=>(p.startsWith("http://")||p.startsWith("https://"))&&!g.includes("rel=")?``:g)},[a,s]),d=` + `.trim().replace(/\s+/g," ");return h.jsx(I,{className:u,id:o,...i,children:a})}const l={FORBID_TAGS:["script","noscript","iframe","frame","frameset","object","embed","applet","portal","form","input","textarea","select","button","style","link","meta","base","body","head","html","title","svg","math","audio","video","source","track","canvas","details","dialog","plaintext","xmp","listing","marquee","noframes","noembed","template","slot"],FORBID_ATTR:["onerror","onload","onclick","onmouseover","onfocus","onblur","ontoggle","oncanplay","onloadstart","formaction","xlink:href","action"],ALLOW_DATA_ATTR:!0,ADD_ATTR:["target"]},c=v.useMemo(()=>{const u=s?{...l,...s,FORBID_TAGS:[...l.FORBID_TAGS,...s.FORBID_TAGS||[]],FORBID_ATTR:[...l.FORBID_ATTR,...s.FORBID_ATTR||[]]}:l;return cy.sanitize(a,u).replace(/]*?)href=["']([^"']+)["']([^>]*?)>/gi,(g,m,p,y)=>(p.startsWith("http://")||p.startsWith("https://"))&&!g.includes("rel=")?``:g)},[a,s]),d=` prose dark:prose-invert max-w-none prose-p:my-2 prose-headings:font-bold prose-headings:mt-6 prose-headings:mb-4 @@ -35,67 +36,67 @@ var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var U5=Object.definePrope [&_tr:hover]:bg-gray-50 dark:[&_tr:hover]:bg-gray-700/50 after:content-[''] after:block after:clear-both ${r} - `.trim().replace(/\s+/g," ");return h.jsx(I,{className:d,dangerouslySetInnerHTML:{__html:c},id:o,...i})},Uo=window.G7Core?.createLogger?.("Comp:DataGrid")??{log:(...t)=>console.log("[Comp:DataGrid]",...t),warn:(...t)=>console.warn("[Comp:DataGrid]",...t),error:(...t)=>console.error("[Comp:DataGrid]",...t)},Es=(t,e)=>window.G7Core?.t?.(t,e)??t,jb={Div:I,Span:Q,Button:oe,Icon:re,Checkbox:vn,P:yt,A:Lr,Label:sn,Input:kt,H1:Da,H2:Jn,H3:Ir,H4:Md,Img:yn,Select:Pr,Textarea:Qn,Table:$a,Thead:Fa,Tbody:za,Tfoot:Va,Tr:Rn,Th:$r,Td:Zt,MultilingualInput:Ha,Toggle:Ua,StatusBadge:Fo,Badge:Ld,HtmlContent:Za,ActionMenu:xs},Qa=()=>{const t=window.G7Core?.getComponentMap?.();return t&&Object.keys(t).length>0?t:jb},Ja=t=>{if(t==null)return"";if(typeof t=="string"||typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.map(e=>Ja(e)).join(", ");if(typeof t=="object"){if(v.isValidElement(t))return t;if(Object.keys(t).length===0)return"";try{return JSON.stringify(t)}catch{return Uo.warn("객체를 문자열로 변환할 수 없습니다:",t),"[Object]"}}return String(t)},Xd=(t,e)=>{if(!t)return!0;const n=window.G7Core;return typeof n?.evaluateCondition=="function"?n.evaluateCondition(t,{row:e,item:e,$item:e}):(Uo.warn("G7Core.evaluateCondition 미노출 — subRow 조건을 평가할 수 없습니다:",t),!0)},zr=(t,e,n,r="",s)=>{const o=window.G7Core;if(o?.renderItemChildren){const i={row:e,value:n,$value:n};return o.renderItemChildren(t,i,Qa(),r,s?{componentContext:s}:void 0)}return Uo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},qd=(t,e,n)=>t?!t.includes("{{")&&!t.includes("$t:")?t:zr([{type:"basic",name:"Span",text:t}],{},null,e,n):"",Ra=(t,e,n="",r)=>{const s=window.G7Core;if(s?.renderItemChildren){const o={row:e,$value:e};return s.renderItemChildren(t,o,Qa(),n,r?{componentContext:r}:void 0)}return Uo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},Wb=({id:t,columns:e,data:n,sortable:r=!0,pagination:s=!1,pageSize:o=10,onRowClick:i,className:a="",style:l,sortField:c,sortDirection:d,onSortChange:u,selectable:f=!1,selectedIds:g=[],onSelectionChange:m,idField:p="id",showColumnSelector:y=!1,visibleColumns:b,onColumnVisibilityChange:_,responsiveBreakpoint:x=768,cardRenderer:E,rowActions:C,onRowAction:k,alwaysShowPagination:N=!1,serverSidePagination:S=!1,serverCurrentPage:w,serverTotalPages:M,onPageChange:T,prevText:P,nextText:L,columnSelectorText:$,emptyMessage:A,actionsColumnHeader:D,requiredText:O,selectedCountText:z,showFirstLast:H=!0,expandable:j=!1,expandedRowIds:W,onExpandChange:U,expandChildren:B,expandedRowRender:G,expandColumnWidth:V="48px",expandIconExpanded:X=de.ChevronDown,expandIconCollapsed:K=de.ChevronRight,expandConditionField:Z,dynamicColumns:ee,dynamicColumnsInsertAfter:ne,expandContext:he,__componentContext:J,subRowChildren:le,subRowCondition:ue,subRowClassName:ae="px-6 py-2 bg-gray-50 dark:bg-gray-900",subRowRender:ve,footerCells:fe,footerClassName:ye="bg-gray-50 dark:bg-gray-800/50 font-medium",footerCardChildren:we,editorAttrs:He})=>{const se=P||void 0,Oe=L||void 0,Le=$??Es("common.column_selector"),Ie=A??Es("common.no_data"),Be=D??Es("common.actions"),st=O??Es("common.required"),ut=z??Es("common.selected_count"),je=window.G7Core,nt=je?.useControllableState,Ye=je?.shallowArrayEqual,[te,ge]=nt?nt(c,null):[c??null,()=>{}],[me,pe]=nt?nt(d,"asc"):[d??"asc",()=>{}],[be,Ue]=v.useState(1),[Ce,ze]=nt?nt(W,[],U,{isEqual:Ye}):[W??[],()=>{}],[Se,Re]=v.useState(!1),ot=v.useRef(null),Pe=je?.useResponsive,Te=Pe?.(),Ve=Te?Te.width{if(!ee||!Array.isArray(ee)||ee.length===0)return e;if(ne){const q=e.findIndex(ce=>ce.field===ne);if(q!==-1)return[...e.slice(0,q+1),...ee,...e.slice(q+1)]}return[...e,...ee]},[e,ee,ne]),[qe,it]=v.useState(()=>b&&b.length>0?b:e.filter(q=>!q.hidden).map(q=>q.field));v.useEffect(()=>{b&&b.length>0&&it(b)},[b]);const pt=v.useRef("");v.useEffect(()=>{const q=e.map(ce=>ce.field).join(",");pt.current&&q!==pt.current&&(!b||b.length===0)&&it(e.filter(ce=>!ce.hidden).map(ce=>ce.field)),pt.current=q},[e,b]);const rn=v.useRef([]),bt=v.useRef(null);v.useEffect(()=>{if(ee&&ee.length>0){const q=ee.map(Ze=>Ze.field);if(bt.current===null&&(bt.current=b!==void 0&&b.length>0,bt.current)){rn.current=q;return}const ce=rn.current,Ee=q.filter(Ze=>!ce.includes(Ze));if(Ee.length>0){const Ze=Ee.filter(Fe=>!qe.includes(Fe));if(Ze.length>0){const Fe=[...qe,...Ze];it(Fe),_?.(Fe)}}rn.current=q}},[ee]),v.useEffect(()=>{S||Ue(1)},[n,S]),v.useEffect(()=>{const q=ce=>{ot.current&&!ot.current.contains(ce.target)&&Re(!1)};return Se&&document.addEventListener("mousedown",q),()=>document.removeEventListener("mousedown",q)},[Se]);const wt=v.useMemo(()=>!y&&(!b||b.length===0)?De.filter(q=>!q.hidden):De.filter(q=>qe.includes(q.field)),[De,qe,y,b]),jt=r&&!(S&&!u),ft=v.useMemo(()=>!te||!r||!n?n||[]:u||S?n:[...n].sort((q,ce)=>{const Ee=q[te],Ze=ce[te];if(Ee===Ze)return 0;const Fe=Ee>Ze?1:-1;return me==="asc"?Fe:-Fe}),[n,te,me,r,u,S]),Ke=v.useMemo(()=>{if(S||!s)return ft;const q=(be-1)*o,ce=q+o;return ft.slice(q,ce)},[ft,be,o,s,S]),Nn=S?w||1:be,Wt=S?M||1:Math.ceil((ft?.length||0)/o),bn=v.useCallback(q=>{S&&T?T(q):Ue(q)},[S,T]),hr=q=>{if(!jt||e.find(Ze=>Ze.field===q)?.sortable===!1)return;const Ee=te===q&&me==="asc"?"desc":"asc";ge(q),pe(Ee),u?.(q,Ee)},On=v.useCallback(()=>{if(!m||!Ke)return;const q=Ke.map(Ee=>Ee[p]);if(q.every(Ee=>g.includes(Ee)))m(g.filter(Ee=>!q.includes(Ee)));else{const Ee=[...new Set([...g,...q])];m(Ee)}},[Ke,g,m,p]),Ao=v.useCallback(q=>{m&&(g.includes(q)?m(g.filter(ce=>ce!==q)):m([...g,q]))},[g,m]),Do=v.useCallback(q=>{if(De.find(Ze=>Ze.field===q)?.required)return;const Ee=qe.includes(q)?qe.filter(Ze=>Ze!==q):[...qe,q];it(Ee),_?.(Ee)},[De,qe,_]),Io=v.useCallback((q,ce)=>{k?.(q,ce)},[k]),Lo=v.useCallback(q=>C?C.map(ce=>{let Ee=ce.disabled??!1;if(ce.disabledField&&!Ee){const Ze=ce.disabledField.split(".");let Fe=q;for(const Nt of Ze)Fe=Fe?.[Nt];Fe||(Ee=!0)}return{...ce,disabled:Ee,onClick:()=>Io(ce.id,q)}}):[],[C,Io]),vs=v.useCallback(q=>{if(!Z)return!0;const ce=q[Z];return Array.isArray(ce)?ce.length>0:!!ce},[Z]),ke=v.useCallback(q=>{const ce=Ce.includes(q)?Ce.filter(Ee=>Ee!==q):[...Ce,q];nt?ze(ce):U&&U(ce)},[Ce,U,nt,ze]),Y=v.useCallback(q=>Ce.includes(q),[Ce]),ie=v.useCallback(q=>G?G(q):B&&B.length>0&&je?.renderExpandContent?je.renderExpandContent({children:B,row:q,expandContext:he,componentContext:J,componentMap:Qa(),keyPrefix:`expand-${q[p]}`}):null,[G,B,p,he,J,J?.state]),xe=v.useMemo(()=>!Ke||Ke.length===0?!1:Ke.every(q=>g.includes(q[p])),[Ke,g,p]),ht=v.useMemo(()=>{if(!Ke||Ke.length===0)return!1;const q=Ke.filter(ce=>g.includes(ce[p])).length;return q>0&&q{const Ee=q[p],Ze=Y(Ee);return h.jsxs(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4 mb-4",children:[h.jsxs(I,{className:"flex items-start justify-between",children:[j&&vs(q)&&h.jsx(oe,{onClick:()=>ke(Ee),className:"p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors mr-2","aria-label":Ze?"Collapse card":"Expand card",children:h.jsx(re,{name:Ze?X:K,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})}),f&&h.jsx(vn,{checked:g.includes(q[p]),onChange:()=>Ao(q[p]),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded mr-3 mt-1"}),h.jsx(I,{className:"flex-1",children:h.jsx(I,{className:"grid grid-cols-2 gap-x-4 gap-y-2",children:ce.map(Fe=>h.jsxs(I,{className:"min-w-0",children:[h.jsx(Q,{className:"text-xs font-medium text-gray-500 dark:text-gray-400 uppercase",children:Fe.header}),h.jsx(I,{className:"text-sm text-gray-900 dark:text-gray-200 mt-0.5 break-words",children:Fe.cellChildren&&Fe.cellChildren.length>0?zr(Fe.cellChildren,q,q[Fe.field],`card-${q[p]}-${Fe.field}`,J):Fe.render?Fe.render(q[Fe.field],q):Ja(q[Fe.field])})]},Fe.field))})}),C&&C.length>0&&h.jsx(I,{className:"ml-2",children:h.jsx(xs,{items:Lo(q),triggerLabel:"",triggerIconName:de.EllipsisHorizontal})})]}),j&&Ze&&vs(q)&&h.jsx(I,{className:"mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:ie(q)}),(le&&le.length>0||ve)&&Xd(ue,q)&&h.jsx(I,{className:ae?`mt-3 ${ae}`:"mt-3 pt-3 border-t border-gray-100 dark:border-gray-700",children:ve?ve(q):Ra(le,q,`card-subrow-${Ee}`,J)})]})};return Ve?h.jsxs(I,{id:t,className:`w-full ${a}`,style:l,...He,children:[y&&h.jsx(I,{className:"mb-4 flex justify-end",children:h.jsxs(I,{ref:ot,className:"relative",children:[h.jsxs(oe,{onClick:()=>Re(!Se),className:"flex items-center gap-2 px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700",children:[h.jsx(re,{name:de.Cog,className:"w-4 h-4 text-gray-500 dark:text-gray-400"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:Le})]}),Se&&h.jsx(I,{className:"absolute right-0 z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",children:h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:De.map(q=>h.jsxs(I,{className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${q.required?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:()=>!q.required&&Do(q.field),children:[h.jsx(vn,{checked:qe.includes(q.field),disabled:q.required,onChange:()=>{},className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"}),h.jsxs(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:[q.header,q.required&&h.jsxs(Q,{className:"text-xs text-gray-500 dark:text-gray-400 ml-1",children:["(",st,")"]})]})]},q.field))})})]})}),f&&g.length>0&&ut&&h.jsx(I,{className:"mb-4 text-sm text-gray-600 dark:text-gray-400",children:ut.replace("{count}",String(g.length))}),h.jsx(I,{id:t?`${t}__body`:void 0,children:Ke&&Ke.length>0?Ke.map((q,ce)=>h.jsx(I,{children:E?E(q,wt):Lt(q,wt)},q[p]||ce)):h.jsx(I,{className:"text-center py-8 text-gray-500 dark:text-gray-400",children:Ie})}),fe&&fe.length>0&&Ke&&Ke.length>0&&h.jsx(I,{className:`rounded-lg p-4 mt-2 ${ye}`,children:we&&we.length>0?zr(we,{},null,"footer-card",J):h.jsx(I,{className:"space-y-1",children:fe.filter(q=>q.text||q.children&&q.children.length>0).map((q,ce)=>{const Ee=wt.find(Ze=>Ze.field===q.field);return h.jsxs(I,{className:"flex justify-between text-sm",children:[h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:Ee?.header||""}),h.jsx(Q,{className:q.className||"text-gray-900 dark:text-gray-100",children:q.children&&q.children.length>0?zr(q.children,{},null,`footer-mobile-${ce}`,J):qd(q.text,`footer-mobile-text-${ce}`,J)})]},`footer-mobile-${ce}`)})})}),s&&(N||Wt>1)&&h.jsx(I,{className:"flex justify-center mt-4",children:h.jsx($o,{currentPage:Nn,totalPages:Wt,onPageChange:bn,showFirstLast:H,maxVisiblePages:5,prevText:se,nextText:Oe})})]}):h.jsxs(I,{id:t,className:`w-full ${a}`,style:l,...He,children:[y&&h.jsx(I,{className:"mb-4 flex justify-end",children:h.jsxs(I,{ref:ot,className:"relative",children:[h.jsxs(oe,{onClick:()=>Re(!Se),className:"flex items-center gap-2 px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700",children:[h.jsx(re,{name:de.Cog,className:"w-4 h-4 text-gray-500 dark:text-gray-400"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:Le})]}),Se&&h.jsx(I,{className:"absolute right-0 z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",children:h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:De.map(q=>h.jsxs(I,{className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${q.required?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:()=>!q.required&&Do(q.field),children:[h.jsx(vn,{checked:qe.includes(q.field),disabled:q.required,onChange:()=>{},className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"}),h.jsxs(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:[q.header,q.required&&h.jsxs(Q,{className:"text-xs text-gray-500 dark:text-gray-400 ml-1",children:["(",st,")"]})]})]},q.field))})})]})}),f&&g.length>0&&ut&&h.jsx(I,{className:"mb-4 text-sm text-gray-600 dark:text-gray-400",children:ut.replace("{count}",String(g.length))}),h.jsx(I,{id:t?`${t}__body`:void 0,className:"overflow-x-auto border border-gray-200 dark:border-gray-700 rounded-lg",children:h.jsxs($a,{className:"w-full",children:[h.jsx(Fa,{className:"bg-gray-50 dark:bg-gray-700",children:h.jsxs(Rn,{children:[j&&h.jsx($r,{className:"px-2 py-3",style:{width:V}}),f&&h.jsx($r,{className:"px-4 py-3 w-10",children:h.jsx(vn,{checked:xe,ref:q=>{q&&(q.indeterminate=ht)},onChange:On,className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"})}),wt.map(q=>h.jsxs($r,{className:`px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider ${jt&&q.sortable!==!1?"cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600":""}`,style:{width:q.width,minWidth:q.width},onClick:jt?()=>hr(q.field):void 0,children:[q.header,te===q.field&&h.jsx(Q,{className:"ml-1",children:me==="asc"?"↑":"↓"})]},q.field)),C&&C.length>0&&h.jsx($r,{className:"px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-24",children:Be})]})}),h.jsx(za,{className:`bg-white dark:bg-gray-800 ${ae?"":"divide-y divide-gray-200 dark:divide-gray-700"}`,children:Ke&&Ke.length>0?Ke.map((q,ce)=>{const Ee=q[p]||ce,Ze=Y(Ee);return h.jsxs(v.Fragment,{children:[h.jsxs(Rn,{className:`${i?"cursor-pointer":""} hover:bg-gray-50 dark:hover:bg-gray-700 ${g.includes(q[p])?"bg-blue-50 dark:bg-blue-900/20":""} ${ae&&ce>0?"border-t border-gray-200 dark:border-gray-700":""}`,onClick:()=>i?.(q),children:[j&&h.jsx(Zt,{className:"px-2 py-4 text-center",onClick:Fe=>Fe.stopPropagation(),children:vs(q)?h.jsx(oe,{onClick:()=>ke(Ee),className:"p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors","aria-label":Ze?"Collapse row":"Expand row",children:h.jsx(re,{name:Ze?X:K,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})}):null}),f&&h.jsx(Zt,{className:"px-4 py-4",onClick:Fe=>Fe.stopPropagation(),children:h.jsx(vn,{checked:g.includes(q[p]),onChange:()=>Ao(q[p]),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"})}),wt.map(Fe=>h.jsx(Zt,{className:"px-6 py-4 align-top whitespace-nowrap text-sm text-gray-900 dark:text-gray-200",style:{width:Fe.width,minWidth:Fe.width},children:Fe.cellChildren&&Fe.cellChildren.length>0?zr(Fe.cellChildren,q,q[Fe.field],`${q[p]}-${Fe.field}`,J):Fe.render?Fe.render(q[Fe.field],q):Ja(q[Fe.field])},Fe.field)),C&&C.length>0&&h.jsx(Zt,{className:"px-6 py-4 text-right",onClick:Fe=>Fe.stopPropagation(),children:h.jsx(xs,{items:Lo(q),triggerLabel:"",triggerIconName:de.EllipsisHorizontal,position:"right"})})]}),j&&Ze&&vs(q)&&h.jsx(Rn,{className:"bg-gray-50 dark:bg-gray-900",children:h.jsx(Zt,{colSpan:wt.length+1+(f?1:0)+(C&&C.length>0?1:0),className:"px-6 py-4",children:ie(q)})}),(le&&le.length>0||ve)&&Xd(ue,q)&&h.jsx(Rn,{className:ae?"!border-t-0":"",children:h.jsx(Zt,{colSpan:wt.length+(j?1:0)+(f?1:0)+(C&&C.length>0?1:0),className:ae?"p-2":ae||"px-6 py-2 bg-gray-50 dark:bg-gray-900",children:ae?h.jsx(I,{className:ae,children:ve?ve(q):Ra(le,q,`subrow-${Ee}`,J)}):ve?ve(q):Ra(le,q,`subrow-${Ee}`,J)})})]},Ee)}):h.jsx(Rn,{children:h.jsx(Zt,{colSpan:wt.length+(j?1:0)+(f?1:0)+(C&&C.length>0?1:0),className:"px-6 py-8 text-center text-gray-500 dark:text-gray-400",children:Ie})})}),fe&&fe.length>0&&Ke&&Ke.length>0&&h.jsx(Va,{children:h.jsxs(Rn,{className:ye,children:[j&&h.jsx(Zt,{className:"px-2 py-4"}),f&&h.jsx(Zt,{className:"px-4 py-4"}),(()=>{const q=new Map(fe.filter(ce=>ce.field).map(ce=>[ce.field,ce]));return wt.map(ce=>{const Ee=q.get(ce.field);return Ee?h.jsx(Zt,{className:`px-6 py-4 text-sm text-gray-900 dark:text-gray-200 ${Ee.className||""}`,colSpan:Ee.colSpan,style:{width:ce.width,minWidth:ce.width},children:Ee.children&&Ee.children.length>0?zr(Ee.children,{},null,`footer-${ce.field}`,J):qd(Ee.text,`footer-text-${ce.field}`,J)},`footer-${ce.field}`):h.jsx(Zt,{className:"px-6 py-4",style:{width:ce.width,minWidth:ce.width}},`footer-${ce.field}`)})})(),C&&C.length>0&&h.jsx(Zt,{className:"px-6 py-4"})]})})]})}),s&&(N||Wt>1)&&h.jsx(I,{className:"flex justify-center mt-4",children:h.jsx($o,{currentPage:Nn,totalPages:Wt,onPageChange:bn,showFirstLast:H,maxVisiblePages:5,prevText:se,nextText:Oe})})]})},Gb=window.G7Core?.createLogger?.("Comp:CardGrid")??{log:(...t)=>console.log("[Comp:CardGrid]",...t),warn:(...t)=>console.warn("[Comp:CardGrid]",...t),error:(...t)=>console.error("[Comp:CardGrid]",...t)},Yb=(t,e)=>window.G7Core?.t?.(t,e)??t,Kb=()=>window.G7Core?.getComponentMap?.()||{},Xb=(t,e)=>{if(!t)return!0;const n=window.G7Core;return typeof n?.evaluateCondition=="function"?n.evaluateCondition(t,{row:e,item:e,$item:e}):(Gb.warn("G7Core.evaluateCondition 미노출 — 조건을 평가할 수 없습니다:",t),!0)},Zd=(t,e,n,r,s)=>{const o=window.G7Core;if(t.name==="ActionMenu"){if(r&&r.length>0&&s)return{...t,props:{...t.props,items:r.map(i=>({...i,onClick:()=>s(i.id,e)}))}};if(t.actions&&Array.isArray(t.actions)){const i=t.props?.items||[],a=new Map(t.actions.map(c=>[c.id,c])),l=i.map(c=>{if(typeof c.onClick=="string"){const d=c.onClick,u=a.get(d);if(u&&o?.ActionDispatcher)return{...c,onClick:()=>{const f={...n};o.ActionDispatcher.dispatch(u,f)}}}return c});return{...t,props:{...t.props,items:l}}}}return t.children&&Array.isArray(t.children)?{...t,children:t.children.map(i=>Zd(i,e,n,r,s))}:t},qb=(t,e,n,r="",s,o)=>{const i=window.G7Core;if(i?.renderItemChildren){const a=t.filter(d=>{const u=d.condition||d.if;return u?Xb(u,e):!0}),l={row:e,$value:e,index:n},c=a.map(d=>Zd(d,e,l,s,o));try{const d=Kb();return i.renderItemChildren(c,l,d,r)}catch{return h.jsxs(I,{className:"text-red-600 dark:text-red-400 p-4",children:[h.jsx(yt,{children:"컴포넌트 로드 실패"}),h.jsx(yt,{className:"text-sm",children:"[CardGrid] 데이터를 표시할 수 없습니다."})]})}}return null},el=(t,e)=>{if(t.name==="Div"&&t.children&&t.children.length>0){const n=t.children.map((r,s)=>el(r,`${e}-child-${s}`)).filter(Boolean);return h.jsx(I,{className:t.props?.className||"",children:n},e)}return h.jsx(I,{className:"h-4 bg-gray-200 dark:bg-gray-700 rounded w-full animate-pulse"},e)},Qd=(t,e="skeleton")=>!t||t.length===0?null:t.map((n,r)=>{const s=`${e}-${n.id||n.name}-${r}`;return n.condition||n.if?null:(n.iteration,el(n,s))}).filter(Boolean),Zb=({data:t,cardColumns:e=[],gridColumns:n=3,gap:r=4,responsiveColumns:s,cardClassName:o="",idField:i="id",pagination:a=!0,pageSize:l=12,serverSidePagination:c=!1,serverCurrentPage:d,serverTotalPages:u,alwaysShowPagination:f=!1,onPageChange:g,paginationInfoText:m,rowActions:p,onRowAction:y,className:b="",style:_,emptyMessage:x,prevText:E,nextText:C,showSkeleton:k=!0,skeletonCount:N,skeletonCellChildren:S,id:w,editorAttrs:M})=>{const T=x??Yb("common.no_data"),P=e[0]?.cellChildren||[],[L,$]=v.useState(1);window.G7Core?.useResponsive?.();const D=c?d??1:L,O=c?u??1:Math.ceil((t?.length??0)/l),z=G=>{c?g?.(G):$(G)},H=v.useCallback((G,V)=>{y?.(G,V)},[y]),j=v.useMemo(()=>{if(!t)return[];if(!a||c)return t;const G=(L-1)*l,V=G+l;return t.slice(G,V)},[t,a,c,L,l]),W=v.useMemo(()=>{const G=[],V=s?.sm??1;G.push(`grid-cols-${V}`),s?.sm&&G.push(`sm:grid-cols-${s.sm}`);const X=s?.md??Math.min(n,2);G.push(`md:grid-cols-${X}`);const K=s?.lg??n;return G.push(`lg:grid-cols-${K}`),s?.xl&&G.push(`xl:grid-cols-${s.xl}`),G.join(" ")},[n,s]),U=v.useMemo(()=>k?S&&S.length>0?Qd(S,"custom-skeleton"):P&&P.length>0?Qd(P,"auto-skeleton"):null:null,[k,S,P]);if(!t){if(!k)return null;const G=N??n;return h.jsx(I,{className:`grid ${W} gap-${r} ${b}`,style:_,id:w,...M,children:Array.from({length:G}).map((V,X)=>h.jsx(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6 space-y-4",children:U},`skeleton-${X}`))})}if(j.length===0)return h.jsx(I,{className:b,style:_,id:w,...M,children:h.jsx(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-12",children:h.jsx(yt,{className:"text-center text-gray-600 dark:text-gray-400",children:T})})});const B=a&&(f||O>1);return h.jsxs(I,{className:b,style:_,id:w,...M,children:[h.jsx(I,{className:`grid ${W} gap-${r}`,children:j.map((G,V)=>h.jsx(I,{className:o,children:qb(P,G,V,`card-${V}`,p,H)},G[i]||V))}),B&&h.jsxs(I,{className:"mt-6 space-y-4 flex flex-col items-center",children:[h.jsx($o,{currentPage:D,totalPages:O,onPageChange:z,showFirstLast:!1,maxVisiblePages:5,prevText:E,nextText:C}),m&&h.jsx(I,{className:"text-sm text-gray-600 dark:text-gray-400 text-center",children:m})]})]})},Qb=(t,e)=>window.G7Core?.t?.(t,e)??t,Bo=({isOpen:t,onClose:e,title:n,titlePrefix:r,titleSuffix:s,closeOnBackdropClick:o=!0,closeOnOverlayClick:i,showCloseButton:a=!0,closeOnEscape:l=!0,children:c,width:d="500px",className:u="",style:f,__dataContext:g,__translationContext:m})=>{const p=i!==void 0?i:o,y=v.useRef(null),b=v.useRef(null);if(v.useEffect(()=>{const E=C=>{C.key==="Escape"&&t&&l&&e()};if(t&&l)return document.addEventListener("keydown",E),()=>document.removeEventListener("keydown",E)},[t,e,l]),v.useEffect(()=>{if(t&&y.current){b.current=document.activeElement;const E=y.current.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),C=E[0],k=E[E.length-1];C?.focus();const N=S=>{S.key==="Tab"&&(S.shiftKey?document.activeElement===C&&(S.preventDefault(),k?.focus()):document.activeElement===k&&(S.preventDefault(),C?.focus()))};return document.addEventListener("keydown",N),()=>{document.removeEventListener("keydown",N),b.current?.focus()}}},[t]),v.useEffect(()=>{if(t)return document.body.style.overflow="hidden",()=>{document.body.style.overflow=""}},[t]),!t)return null;const _=f?.zIndex??50,x=(E,C)=>{if(!E||E.length===0)return null;const k=window.G7Core;if(!k)return null;const N=k.getDynamicRenderer?.();return N?E.map((S,w)=>h.jsx(N,{componentDef:S,dataContext:g||{},translationContext:m,registry:k.getComponentRegistry(),bindingEngine:k.getDataBindingEngine(),translationEngine:k.getTranslationEngine(),actionDispatcher:k.getActionDispatcher(),isRootRenderer:!1},`${C}-${w}`)):null};return h.jsxs(I,{className:"modal-wrapper",style:{zIndex:_},onClick:p?e:void 0,children:[h.jsx(I,{className:"modal-backdrop","aria-hidden":"true"}),h.jsxs(I,{ref:y,className:`modal-container ${u}`,style:{width:d,...f?{...f,zIndex:void 0}:{}},onClick:E=>E.stopPropagation(),role:"dialog","aria-modal":"true","aria-labelledby":n?"modal-title":void 0,children:[(n||r||s||a)&&h.jsxs(I,{className:"modal-header-row",children:[h.jsxs(I,{className:"flex-center gap-sm",children:[x(r,"title-prefix"),n&&h.jsx(I,{id:"modal-title",className:"modal-title",children:n}),x(s,"title-suffix")]}),a&&h.jsx(oe,{onClick:e,className:"modal-close-btn","aria-label":Qb("common.close_modal"),children:h.jsx("svg",{className:"icon-lg",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),h.jsx(I,{className:"modal-body-content",children:c})]})]})},Jb=({label:t,items:e,onItemClick:n,position:r="bottom-left",variant:s="default",className:o="",style:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(!1),[u,f]=v.useState(-1),g=v.useRef(null),m=v.useRef(null),[p,y]=v.useState(r);v.useEffect(()=>{const x=E=>{g.current&&!g.current.contains(E.target)&&(d(!1),f(-1))};if(c)return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[c]),v.useEffect(()=>{const x=E=>{if(!c)return;const C=e.map((k,N)=>k.disabled?-1:N).filter(k=>k!==-1);switch(E.key){case"ArrowDown":E.preventDefault(),f(k=>{const S=(C.indexOf(k)+1)%C.length;return C[S]});break;case"ArrowUp":E.preventDefault(),f(k=>{const N=C.indexOf(k),S=N<=0?C.length-1:N-1;return C[S]});break;case"Enter":E.preventDefault(),u>=0&&!e[u].disabled&&b(e[u]);break;case"Escape":E.preventDefault(),d(!1),f(-1);break}};if(c)return document.addEventListener("keydown",x),()=>document.removeEventListener("keydown",x)},[c,u,e]),v.useEffect(()=>{if(c&&m.current&&g.current){const x=m.current.getBoundingClientRect(),E=g.current.getBoundingClientRect(),C=window.innerWidth,k=window.innerHeight,N=x.width||224,S=x.height||200;let w=r;r.includes("left")?E.left+N>C&&(w=w.replace("left","right")):E.right-N<0&&(w=w.replace("right","left")),r.includes("top")?E.top-S<0&&(w=w.replace("top","bottom")):E.bottom+S>k&&(w=w.replace("bottom","top")),y(w)}},[c,r]);const b=x=>{x.disabled||(x.onClick?.(),n?.(x.value,x),d(!1),f(-1))},_={"bottom-left":"dropdown-menu-bottom-left","bottom-right":"dropdown-menu-bottom-right","top-left":"dropdown-menu-top-left","top-right":"dropdown-menu-top-right"};return h.jsxs(I,{ref:g,className:`dropdown ${o}`,style:i,id:a,...l,children:[h.jsxs(oe,{onClick:()=>d(!c),className:s==="text"?"dropdown-trigger-text":"dropdown-trigger","aria-haspopup":"true","aria-expanded":c,children:[t,s!=="text"&&h.jsx("svg",{className:`inline-block ml-2 w-4 h-4 transition-transform ${c?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),c&&h.jsx(I,{ref:m,className:`dropdown-menu ${_[p]}`,role:"menu","aria-orientation":"vertical",children:h.jsx(I,{className:"py-1",children:e.map((x,E)=>h.jsx(I,{className:`${x.disabled?"dropdown-item-disabled":"dropdown-item"} ${u===E?"dropdown-item-focused":""}`,onClick:()=>b(x),onMouseEnter:()=>!x.disabled&&f(E),role:"menuitem","aria-disabled":x.disabled,tabIndex:x.disabled?-1:0,children:x.label},x.value))})})]})},Rb=({imageUrl:t,imageAlt:e="",title:n,subtitle:r,description:s,price:o,originalPrice:i,discountRate:a,currency:l="₩",buttonText:c="자세히 보기",onButtonClick:d,className:u="",style:f,id:g,editorAttrs:m})=>{const p=a&&a>0,y=(o??0).toLocaleString(),b=i?.toLocaleString();return h.jsxs(I,{className:`rounded-lg bg-white dark:bg-gray-800 shadow-md hover:shadow-xl transition-shadow border border-gray-200 dark:border-gray-700 overflow-hidden ${u}`,style:f,id:g,...m,children:[t&&h.jsxs(I,{className:"relative w-full",children:[h.jsx(yn,{src:t,alt:e||n,className:"w-full h-64 object-cover"}),p&&h.jsx(I,{className:"absolute top-2 right-2 bg-red-500 text-white px-2 py-1 rounded-md",children:h.jsxs(Q,{className:"text-sm font-bold",children:["-",a,"%"]})})]}),h.jsxs(I,{className:"p-4",children:[r&&h.jsx(Ir,{className:"text-xs font-medium text-blue-600 dark:text-blue-400 uppercase tracking-wider mb-2",children:r}),h.jsx(Jn,{className:"text-xl font-bold text-gray-900 dark:text-white mb-2 line-clamp-2",children:n}),s&&h.jsx(yt,{className:"text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-3",children:s}),h.jsxs(I,{className:"flex items-center gap-2 mb-4",children:[h.jsxs(Q,{className:"text-2xl font-bold text-gray-900 dark:text-white",children:[l,y]}),i&&h.jsxs(Q,{className:"text-sm text-gray-400 dark:text-gray-500 line-through",children:[l,b]})]}),c&&h.jsx(oe,{onClick:d,className:"w-full bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg transition-colors",children:c})]})]})},ey=({value:t,label:e,change:n,changeLabel:r="전월 대비",iconName:s,trend:o="neutral",className:i="",style:a,id:l,editorAttrs:c})=>{const d=o==="up",u=o==="down",f=n!==void 0?Math.abs(n):0,g=d?"stats-trend-up":u?"stats-trend-down":"stats-trend-neutral",m=d?de.ArrowUp:u?de.ArrowDown:void 0;return h.jsxs(I,{className:`stat-card ${i}`,style:a,id:l,...c,children:[h.jsxs(I,{className:"flex-between mb-md",children:[s&&h.jsx(I,{className:"stats-icon stats-icon-blue",children:h.jsx(re,{name:s,className:"icon-lg"})}),n!==void 0&&h.jsxs(I,{className:`stats-change ${g}`,children:[m&&h.jsx(re,{name:m,className:"icon-sm"}),h.jsxs(Q,{className:"text-sm font-semibold",children:[f,"%"]})]})]}),h.jsx(Ir,{className:"stats-value-lg",children:typeof t=="number"?t.toLocaleString():t}),h.jsx(yt,{className:"stats-label",children:e}),n!==void 0&&r&&h.jsx(I,{className:"stats-sublabel",children:h.jsx(Q,{children:r})})]})},Jd=()=>window.G7Core,Rd=(t,e)=>Jd()?.t?.(t,e)??t,ty=({name:t="search",placeholder:e,value:n,onChange:r,onSubmit:s,showButton:o=!1,suggestions:i=[],onSuggestionClick:a,showSuggestions:l=!1,className:c="",style:d,id:u,editorAttrs:f})=>{const g=e??Rd("common.search_placeholder"),[m,p]=v.useState(""),[y,b]=v.useState(!1),_=v.useRef(null),x=v.useRef(""),E=n!==void 0?n:m,C=l&&y&&i.length>0&&E.length>0,k="w-64 pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm",N=Jd()?.style?.mergeClasses?.(k,c)??`${k} ${c}`;v.useEffect(()=>{n!==void 0&&(x.current=n)},[n]);const S=T=>{const P=T.target.value;p(P),r?.(T),x.current!==""&&P===""&&!T.nativeEvent.inputType&&setTimeout(()=>{const L=_.current?.querySelector("form");L&&L.requestSubmit()},0),x.current=P},w=T=>{p(T.text);const P={target:{value:T.text,name:t}};r?.(P),a?.(T),b(!1)},M=T=>{T.preventDefault(),s?.(T)};return h.jsxs(I,{ref:_,className:"relative",style:d,id:u,...f,children:[h.jsx(Pa,{onSubmit:M,className:"relative",children:h.jsxs(I,{className:`relative flex items-center ${o?"gap-2":""}`,children:[h.jsxs(I,{className:"relative flex-1",children:[h.jsx(I,{className:"absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",children:h.jsx(re,{name:de.Search,className:"w-5 h-5 text-gray-400 dark:text-gray-500"})}),h.jsx(kt,{type:"search",name:t,value:E,onChange:S,onFocus:()=>b(!0),onBlur:()=>setTimeout(()=>b(!1),200),placeholder:g,className:N})]}),o&&h.jsx(oe,{type:"submit",className:"px-4 py-2 bg-gray-800 dark:bg-gray-600 text-white rounded-lg hover:bg-gray-900 dark:hover:bg-gray-500 transition-colors text-sm font-medium cursor-pointer h-[42px]",children:Rd("common.search")})]})}),C&&h.jsx(I,{className:"absolute z-10 w-full mt-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-64 overflow-y-auto",children:i.map(T=>h.jsx(I,{className:"px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer border-b border-gray-100 dark:border-gray-700 last:border-b-0 transition-colors",onClick:()=>w(T),children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(re,{name:de.Search,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:T.text})]})},T.id))})]})},tl=(t,e)=>window.G7Core?.t?.(t,e)??t,ny=({title:t,filters:e,onChange:n,onReset:r,showResetButton:s=!0,className:o="",style:i,id:a,editorAttrs:l})=>{const c=t??tl("common.filter"),d=(f,g)=>{n?.(f,g)},u=(f,g,m)=>{const p=e.find(b=>b.id===f);if(!p)return;const y=Array.isArray(p.value)?p.value:[];if(typeof g=="string"){const b=y.filter(x=>typeof x=="string"),_=m?[...b,g]:b.filter(x=>x!==g);n?.(f,_)}else{const b=y.filter(x=>typeof x=="number"),_=m?[...b,g]:b.filter(x=>x!==g);n?.(f,_)}};return h.jsxs(I,{className:`rounded-lg bg-white dark:bg-gray-800 shadow-md border border-gray-200 dark:border-gray-700 p-4 ${o}`,style:i,id:a,...l,children:[h.jsxs(I,{className:"flex items-center justify-between mb-4 pb-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(Q,{className:"text-lg font-semibold text-gray-900 dark:text-white",children:c}),s&&r&&h.jsx(oe,{onClick:r,className:"text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium",children:tl("common.reset")})]}),h.jsx(I,{className:"space-y-4",children:e.map(f=>h.jsxs(I,{className:"space-y-2",children:[h.jsx(sn,{className:"block text-sm font-medium text-gray-700 dark:text-gray-300",children:f.label}),f.type==="select"&&h.jsxs(Pr,{value:f.value,onChange:g=>d(f.id,g.target.value),className:"w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 bg-white dark:bg-gray-700 text-gray-900 dark:text-white",children:[h.jsx("option",{value:"",children:tl("common.select_placeholder")}),f.options.map(g=>h.jsx("option",{value:g.value,children:g.label},g.id))]}),f.type==="checkbox"&&h.jsx(I,{className:"space-y-2",children:f.options.map(g=>{const m=Array.isArray(f.value)?f.value.some(p=>p===g.value):!1;return h.jsxs(sn,{className:"flex items-center gap-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 p-2 rounded-md transition-colors",children:[h.jsx(vn,{checked:m,onChange:p=>u(f.id,g.value,p.target.checked),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded focus:ring-blue-500"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:g.label})]},g.id)})})]},f.id))})]})},ry=({tabs:t,activeTabId:e,onTabChange:n,variant:r="default",className:s="",style:o,mobileBreakpoint:i=768,id:a,editorAttrs:l})=>{const d=window.G7Core?.useResponsive,u=d?.(),f=u?u.width{!k.disabled&&k.id!==e&&n?.(k.id)},p=(k,N)=>{const S=k.key==="ArrowRight"?1:k.key==="ArrowLeft"?-1:0;let w=null;if(S!==0)for(let M=1;M<=t.length;M+=1){const T=(N+S*M+t.length*M)%t.length;if(!t[T]?.disabled){w=T;break}}else if(k.key==="Home")w=t.findIndex(M=>!M.disabled);else if(k.key==="End"){for(let M=t.length-1;M>=0;M-=1)if(!t[M]?.disabled){w=M;break}}w===null||w<0||(k.preventDefault(),g.current[w]?.focus(),m(t[w]))},y=k=>{const N=k.target.value,S=Number(N),w=typeof N=="string"&&N!==""&&!Number.isNaN(S)?S:N,M=t.find(T=>String(T.id)===String(w));M&&m(M)};if(f)return h.jsx(I,{className:s,style:o,id:a,...l,children:h.jsx(Pr,{value:e!==void 0?String(e):"",onChange:y,options:t.map(k=>({value:String(k.id),label:k.badge!==void 0?`${k.label} (${k.badge})`:k.label,disabled:k.disabled})),className:"w-full"})});const b=k=>{const N=k.id===e;if(k.disabled)return"tab-btn-base tab-btn-disabled";switch(r){case"pills":return N?"tab-btn-base tab-btn-pills-active":"tab-btn-base tab-btn-pills";case"underline":return N?"tab-btn-base tab-btn-underline-active":"tab-btn-base tab-btn-underline";default:return N?"tab-btn-base tab-btn-default-active":"tab-btn-base tab-btn-default"}},_=r==="underline"?"tab-nav-underline":"tab-nav-default",x=l?.["data-editor-path"],E=t.findIndex(k=>k.id===e),C=E>=0?E:t.findIndex(k=>!k.disabled);return h.jsx(Po,{className:`${_} ${s}`,style:o,role:"tablist","aria-orientation":"horizontal",id:a,...l,children:t.map((k,N)=>h.jsxs(oe,{ref:S=>{g.current[N]=S},type:"button",role:"tab","aria-selected":k.id===e,tabIndex:N===C?0:-1,onClick:()=>m(k),onKeyDown:S=>p(S,N),disabled:k.disabled,className:b(k),...x?{"data-editor-item-path":`${x}.props.tabs.${N}`}:{},children:[k.iconName&&h.jsx(re,{name:k.iconName,className:"w-4 h-4"}),h.jsx(Q,{children:k.label}),k.badge!==void 0&&h.jsx(I,{className:"flex items-center justify-center min-w-[20px] h-5 px-1.5 bg-red-500 dark:bg-red-600 text-white dark:text-white text-xs font-bold rounded-full",children:h.jsx(Q,{children:k.badge})})]},k.id))})},nl=window.G7Core?.createLogger?.("Handler:ScrollToSection")??{log:(...t)=>console.log("[Handler:ScrollToSection]",...t),warn:(...t)=>console.warn("[Handler:ScrollToSection]",...t),error:(...t)=>console.error("[Handler:ScrollToSection]",...t)};async function eu(t,e){const n=t.params||{},{targetId:r,offset:s=120,delay:o=100,scrollContainerId:i}=n;if(!r){nl.warn("targetId is required");return}const a=await sy(r,o);if(!a){nl.warn(`Element not found: ${r}`);return}const l=oy(a,i),c=iy(a,l,s);ay(l,c)}async function sy(t,e){await new Promise(s=>setTimeout(s,e));const n=10,r=50;for(let s=0;ssetTimeout(i,50)),o;await new Promise(i=>setTimeout(i,r))}return null}function oy(t,e){if(e){const o=document.getElementById(e);if(o&&tu(o))return o;nl.warn(`Container '${e}' ${o?"has no scroll":"not found"}, trying auto-detect`)}let n=t.parentElement,r=0;const s=10;for(;n&&n!==document.body&&rt.clientHeight}function iy(t,e,n){if(e===window||!("getBoundingClientRect"in e))return t.getBoundingClientRect().top+window.pageYOffset-n;{const r=e.getBoundingClientRect(),s=t.getBoundingClientRect();return e.scrollTop+(s.top-r.top)-n}}function ay(t,e){t===window||!("scrollTo"in t)?window.scrollTo({top:e,behavior:"smooth"}):t.scrollTo({top:e,behavior:"smooth"})}window.G7Core?.createLogger?.("Comp:TabNavigation");const ly=({tabs:t,activeTabId:e,className:n="bg-white dark:bg-gray-800 flex gap-2 border-b border-gray-200 dark:border-gray-700 px-6",style:r,activeClassName:s="tab-btn-base tab-btn-default-active",inactiveClassName:o="tab-btn-base tab-btn-default",sectionIdPrefix:i="tab_content_",scrollOffset:a=120,scrollDelay:l=100,onTabChange:c,enableScrollSpy:d=!1,scrollSpyOffset:u=80,scrollContainerId:f,id:g,editorAttrs:m})=>{const[p,y]=v.useState(e??(t.length>0?t[0].id:"")),_=window.G7Core?.useResponsive,x=_?.(),E=x?x.width<768:typeof window<"u"&&window.innerWidth<768,C=v.useRef(!1),k=v.useRef(null),N=v.useRef([]),S=L=>{if(d&&(C.current=!0,k.current&&clearTimeout(k.current)),L.onClick){if(L.onClick(L.id),!d)return}else if(c){if(c(L.id),!d)return}else y(L.id);const $=L.scrollOffset??a,A=L.scrollDelay??l;eu({params:{targetId:`${i}${L.id}`,offset:$,delay:A,scrollContainerId:f}}),d&&(k.current=setTimeout(()=>{C.current=!1},A+500))},w=(L,$)=>{const A=L.key==="ArrowRight"?1:L.key==="ArrowLeft"?-1:0;let D=null;if(A!==0)for(let O=1;O<=t.length;O+=1){const z=($+A*O+t.length*O)%t.length;if(!t[z]?.disabled){D=z;break}}else if(L.key==="Home")D=t.findIndex(O=>!O.disabled);else if(L.key==="End"){for(let O=t.length-1;O>=0;O-=1)if(!t[O]?.disabled){D=O;break}}D===null||D<0||(L.preventDefault(),N.current[D]?.focus(),S(t[D]))};v.useEffect(()=>{if(!d)return;const L=f?document.getElementById(f):null,$={root:L??null,rootMargin:`-${u}px 0px -30% 0px`,threshold:[0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1]},A=new Map,D=()=>{const j=L,W=j?j.scrollTop:window.pageYOffset||document.documentElement.scrollTop,U=j?j.scrollHeight:document.documentElement.scrollHeight,B=j?j.clientHeight:document.documentElement.clientHeight;return W+B>=U-10},O=j=>{if(C.current)return;j.forEach(X=>{const K=X.target.id;X.isIntersecting?A.set(K,X.intersectionRatio):A.delete(K)});let W=0,U="";A.forEach((X,K)=>{X>W&&(W=X,U=K)});const B=t[t.length-1],G=`${i}${B?.id}`,V=A.has(G);if(D()&&V&&B){y(B.id);return}if(U){const X=U.replace(i,""),K=Number(X),Z=isNaN(K)?X:K;t.some(ne=>ne.id===Z)&&y(Z)}},z=()=>{},H=new IntersectionObserver(O,$);return t.forEach(j=>{const W=document.getElementById(`${i}${j.id}`);W&&H.observe(W)}),window.addEventListener("scroll",z,{passive:!0}),()=>{H.disconnect(),window.removeEventListener("scroll",z),k.current&&clearTimeout(k.current)}},[d,t,i,u,f]);const M=L=>{const $=L.target.value,A=Number($),D=isNaN(A)?$:A,O=t.find(z=>z.id===D);O&&S(O)};if(E)return h.jsx(I,{className:"bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-4 py-3",id:g,...m,children:h.jsx(Pr,{value:String(p),onChange:M,options:t.map(L=>({value:String(L.id),label:L.label,disabled:L.disabled})),className:"w-full"})});const T=t.findIndex(L=>L.id===p),P=T>=0?T:t.findIndex(L=>!L.disabled);return h.jsx(I,{className:n,style:r,role:"tablist","aria-orientation":"horizontal",id:g,...m,children:t.map((L,$)=>h.jsxs(oe,{ref:A=>{N.current[$]=A},type:"button",role:"tab","aria-selected":p===L.id,tabIndex:$===P?0:-1,className:p===L.id?s:o,disabled:L.disabled,onClick:()=>S(L),onKeyDown:A=>w(A,$),children:[L.iconName&&h.jsx(re,{name:L.iconName,size:"sm"}),h.jsx(Q,{children:L.label})]},L.id))})},cy=()=>window.G7Core,dy=({iconName:t,label:e,onClick:n,variant:r="primary",size:s="md",disabled:o=!1,className:i="",style:a,id:l,editorAttrs:c})=>{const d={sm:e?"px-3 py-1.5 text-sm":"w-8 h-8",md:e?"px-4 py-2 text-base":"w-10 h-10",lg:e?"px-5 py-2.5 text-lg":"w-12 h-12"},u={sm:"w-4 h-4",md:"w-5 h-5",lg:"w-6 h-6"},f={primary:"bg-blue-600 hover:bg-blue-700 text-white border-transparent",secondary:"bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600",danger:"bg-red-600 hover:bg-red-700 text-white border-transparent",ghost:"bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-transparent",outline:"bg-transparent hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600"},g="inline-flex items-center justify-center gap-2 font-medium rounded-lg border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-400",m=o?"opacity-50 cursor-not-allowed":"",p=`${g} ${d[s]} ${f[r]} ${m}`,y=cy()?.style?.mergeClasses?.(p,i)??`${p} ${i}`;return h.jsxs(oe,{onClick:n,disabled:o,className:y,style:a,id:l,...c,children:[h.jsx(re,{name:t,className:u[s]}),e]})},uy=(t,e)=>window.G7Core?.t?.(t,e)??t,nu=({isOpen:t,onClose:e,title:n,content:r,actions:s=[],width:o="500px",closeOnOverlay:i=!0,showCloseButton:a=!0,className:l="",style:c})=>{const d=()=>{i&&e()},u={primary:"dialog-btn-primary",secondary:"dialog-btn-secondary",danger:"dialog-btn-danger",ghost:"dialog-btn-ghost"};return h.jsxs(Bo,{isOpen:t,onClose:d,width:o,className:l,style:c,children:[h.jsxs(I,{className:"dialog-header",children:[h.jsx(Jn,{className:"dialog-title",children:n}),a&&h.jsx(oe,{onClick:e,className:"dialog-close","aria-label":uy("common.close_dialog"),children:h.jsx(re,{name:de.Times,className:"icon-md"})})]}),h.jsx(I,{className:"dialog-body",children:r}),s.length>0&&h.jsx(I,{className:"dialog-footer",children:s.map((f,g)=>h.jsxs(oe,{onClick:f.onClick,disabled:f.disabled,className:u[f.variant||"primary"],children:[f.iconName&&h.jsx(re,{name:f.iconName,className:"icon-sm mr-2 inline-block"}),f.label]},g))})]})},rl=(t,e)=>window.G7Core?.t?.(t,e)??t,ru=({isOpen:t,onClose:e,title:n,message:r,confirmText:s,cancelText:o,onConfirm:i,onCancel:a,confirmButtonVariant:l="primary",cancelButtonVariant:c="secondary",width:d="500px",closeOnOverlay:u=!0,isLoading:f=!1,loadingText:g})=>{const m=s??rl("common.confirm"),p=o??rl("common.cancel"),y=g??rl("common.loading"),b=()=>{i()},_=()=>{f||(a?.(),e())},x=()=>{f||e()},E={primary:"bg-blue-600 text-white hover:bg-blue-700",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600",danger:"bg-red-600 text-white hover:bg-red-700",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"};return h.jsxs(Bo,{isOpen:t,onClose:x,title:n,width:d,children:[h.jsx(I,{className:"py-2",children:typeof r=="string"?h.jsx(yt,{className:"text-gray-700 dark:text-gray-300",children:r}):r}),h.jsxs(I,{className:"flex justify-end gap-3 mt-6",children:[h.jsx(oe,{onClick:_,disabled:f,className:"px-4 py-2 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed",children:h.jsx(Q,{children:p})}),h.jsxs(oe,{onClick:b,disabled:f,className:`flex items-center gap-2 px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed ${E[l]}`,children:[f&&h.jsx(re,{name:de.Spinner,className:"w-4 h-4 animate-spin"}),h.jsx(Q,{children:f?y:m})]})]})]})},fy=(t,e)=>window.G7Core?.t?.(t,e)??t,hy=({isOpen:t,onClose:e,title:n,message:r,confirmText:s,onConfirm:o,confirmButtonVariant:i="primary",width:a="500px",closeOnOverlay:l=!0})=>{const u=[{label:s??fy("common.confirm"),onClick:()=>{o?.(),e()},variant:i}],f=typeof r=="string"?h.jsx(yt,{className:"text-gray-700 dark:text-gray-300",children:r}):r;return h.jsx(nu,{isOpen:t,onClose:e,title:n,content:f,actions:u,width:a,closeOnOverlay:l})},gy=(t,e)=>window.G7Core?.t?.(t,e)??t,py={success:de.CheckCircle,error:de.XCircle,warning:de.ExclamationTriangle,info:de.InfoCircle},su={success:{bg:"bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800",text:"text-green-800 dark:text-green-300"},error:{bg:"bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800",text:"text-red-800 dark:text-red-300"},warning:{bg:"bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800",text:"text-yellow-800 dark:text-yellow-300"},info:{bg:"bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800",text:"text-blue-800 dark:text-blue-300"}},my={"top-left":"top-4 left-4","top-center":"top-4 left-1/2 -translate-x-1/2","top-right":"top-4 right-4","bottom-left":"bottom-4 left-4","bottom-center":"bottom-4 left-1/2 -translate-x-1/2","bottom-right":"bottom-4 right-4"},by=({toasts:t,position:e="bottom-center",duration:n=3e3,onRemove:r})=>{const[s,o]=v.useState(new Map),i=v.useCallback(a=>{o(l=>{const c=new Map(l);return c.set(a,!1),c}),setTimeout(()=>{if(r)r(a);else{const l=window.G7Core;l?.state?.update&&l.state.update(c=>({toasts:(Array.isArray(c.toasts)?c.toasts:[]).filter(u=>u.id!==a)}))}},300)},[r]);return v.useEffect(()=>{!t||t.length===0||t.forEach(a=>{if(s.has(a.id))return;o(c=>{const d=new Map(c);return d.set(a.id,!0),d});const l=a.duration||n;l>0&&setTimeout(()=>{i(a.id)},l)})},[t,n,i,s]),!t||t.length===0?null:h.jsx(I,{className:`fixed z-[9999] flex flex-col gap-2 ${my[e]}`,"aria-live":"polite","aria-atomic":"true",children:t.map(a=>h.jsx(yy,{toast:a,position:e,isVisible:s.get(a.id)??!0,onClose:()=>i(a.id)},a.id))})},yy=({toast:t,position:e,isVisible:n,onClose:r})=>{const s=su[t.type]||su.info,o=t.icon?t.icon:py[t.type],i=e.startsWith("bottom")?"translate-y-2":"-translate-y-2";return h.jsxs(I,{className:` + `.trim().replace(/\s+/g," ");return h.jsx(I,{className:d,dangerouslySetInnerHTML:{__html:c},id:o,...i})},qo=window.G7Core?.createLogger?.("Comp:DataGrid")??{log:(...e)=>console.log("[Comp:DataGrid]",...e),warn:(...e)=>console.warn("[Comp:DataGrid]",...e),error:(...e)=>console.error("[Comp:DataGrid]",...e)},Fs=(e,t)=>window.G7Core?.t?.(e,t)??e,dy={Div:I,Span:J,Button:ae,Icon:oe,Checkbox:_n,P:yt,A:Wr,Label:ln,Input:kt,H1:Ba,H2:or,H3:jr,H4:$d,Img:Sn,Select:Gr,Textarea:sr,Table:Ya,Thead:Xa,Tbody:Ka,Tfoot:qa,Tr:ir,Th:Yr,Td:Jt,MultilingualInput:Za,Toggle:Qa,StatusBadge:Xo,Badge:jd,HtmlContent:ol,ActionMenu:Ls},il=()=>{const e=window.G7Core?.getComponentMap?.();return e&&Object.keys(e).length>0?e:dy},al=e=>{if(e==null)return"";if(typeof e=="string"||typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e))return e.map(t=>al(t)).join(", ");if(typeof e=="object"){if(v.isValidElement(e))return e;if(Object.keys(e).length===0)return"";try{return JSON.stringify(e)}catch{return qo.warn("객체를 문자열로 변환할 수 없습니다:",e),"[Object]"}}return String(e)},iu=(e,t)=>{if(!e)return!0;const n=window.G7Core;return typeof n?.evaluateCondition=="function"?n.evaluateCondition(e,{row:t,item:t,$item:t}):(qo.warn("G7Core.evaluateCondition 미노출 — subRow 조건을 평가할 수 없습니다:",e),!0)},Jr=(e,t,n,r="",s)=>{const o=window.G7Core;if(o?.renderItemChildren){const i={row:t,value:n,$value:n};return o.renderItemChildren(e,i,il(),r,s?{componentContext:s}:void 0)}return qo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},au=(e,t,n)=>e?!e.includes("{{")&&!e.includes("$t:")?e:Jr([{type:"basic",name:"Span",text:e}],{},null,t,n):"",ll=(e,t,n="",r)=>{const s=window.G7Core;if(s?.renderItemChildren){const o={row:t,$value:t};return s.renderItemChildren(e,o,il(),n,r?{componentContext:r}:void 0)}return qo.warn("G7Core.renderItemChildren을 사용할 수 없습니다."),null},uy=({id:e,columns:t,data:n,sortable:r=!0,pagination:s=!1,pageSize:o=10,onRowClick:i,className:a="",style:l,sortField:c,sortDirection:d,onSortChange:u,selectable:f=!1,selectedIds:g=[],onSelectionChange:m,idField:p="id",showColumnSelector:y=!1,visibleColumns:b,onColumnVisibilityChange:_,responsiveBreakpoint:x=768,cardRenderer:M,rowActions:S,onRowAction:k,alwaysShowPagination:N=!1,serverSidePagination:C=!1,serverCurrentPage:w,serverTotalPages:E,serverHasMorePages:A,onPageChange:P,prevText:L,nextText:$,columnSelectorText:D,emptyMessage:O,actionsColumnHeader:T,requiredText:z,selectedCountText:U,showFirstLast:W=!0,expandable:G=!1,expandedRowIds:B,onExpandChange:j,expandChildren:Y,expandedRowRender:V,expandColumnWidth:X="48px",expandIconExpanded:K=ue.ChevronDown,expandIconCollapsed:q=ue.ChevronRight,expandConditionField:ne,dynamicColumns:ee,dynamicColumnsInsertAfter:fe,expandContext:te,__componentContext:le,subRowChildren:ge,subRowCondition:ce,subRowClassName:ye="px-6 py-2 bg-gray-50 dark:bg-gray-900",subRowRender:me,footerCells:pe,footerClassName:ke="bg-gray-50 dark:bg-gray-800/50 font-medium",footerCardChildren:$e,editorAttrs:ie})=>{const _e=L||void 0,Ae=$||void 0,Ie=D??Fs("common.column_selector"),Be=O??Fs("common.no_data"),gt=T??Fs("common.actions"),mt=z??Fs("common.required"),Ge=U??Fs("common.selected_count"),ot=window.G7Core,Fe=ot?.useControllableState,re=ot?.shallowArrayEqual,[de,xe]=Fe?Fe(c,null):[c??null,()=>{}],[ve,we]=Fe?Fe(d,"asc"):[d??"asc",()=>{}],[Ve,De]=v.useState(1),[Ne,Te]=Fe?Fe(B,[],j,{isEqual:re}):[B??[],()=>{}],[He,Ke]=v.useState(!1),Le=v.useRef(null),Ee=ot?.useResponsive,ze=Ee?.(),rt=ze?ze.width{if(!ee||!Array.isArray(ee)||ee.length===0)return t;if(fe){const Q=t.findIndex(be=>be.field===fe);if(Q!==-1)return[...t.slice(0,Q+1),...ee,...t.slice(Q+1)]}return[...t,...ee]},[t,ee,fe]),[Ye,at]=v.useState(()=>b&&b.length>0?b:t.filter(Q=>!Q.hidden).map(Q=>Q.field));v.useEffect(()=>{b&&b.length>0&&at(b)},[b]);const xn=v.useRef("");v.useEffect(()=>{const Q=t.map(be=>be.field).join(",");xn.current&&Q!==xn.current&&(!b||b.length===0)&&at(t.filter(be=>!be.hidden).map(be=>be.field)),xn.current=Q},[t,b]);const bt=v.useRef([]),qe=v.useRef(null);v.useEffect(()=>{if(ee&&ee.length>0){const Q=ee.map(H=>H.field);if(qe.current===null&&(qe.current=b!==void 0&&b.length>0,qe.current)){bt.current=Q;return}const be=bt.current,se=Q.filter(H=>!be.includes(H));if(se.length>0){const H=se.filter(Z=>!Ye.includes(Z));if(H.length>0){const Z=[...Ye,...H];at(Z),_?.(Z)}}bt.current=Q}},[ee]),v.useEffect(()=>{C||De(1)},[n,C]),v.useEffect(()=>{const Q=be=>{Le.current&&!Le.current.contains(be.target)&&Ke(!1)};return He&&document.addEventListener("mousedown",Q),()=>document.removeEventListener("mousedown",Q)},[He]);const dt=v.useMemo(()=>!y&&(!b||b.length===0)?it.filter(Q=>!Q.hidden):it.filter(Q=>Ye.includes(Q.field)),[it,Ye,y,b]),Zt=r&&!(C&&!u),on=v.useMemo(()=>!de||!r||!n?n||[]:u||C?n:[...n].sort((Q,be)=>{const se=Q[de],H=be[de];if(se===H)return 0;const Z=se>H?1:-1;return ve==="asc"?Z:-Z}),[n,de,ve,r,u,C]),Je=v.useMemo(()=>{if(C||!s)return on;const Q=(Ve-1)*o,be=Q+o;return on.slice(Q,be)},[on,Ve,o,s,C]),Qt=C?w||1:Ve,wn=C?E??null:Math.ceil((on?.length||0)/o),xr=N||(wn??0)>1||A===!0||Qt>1,an=v.useCallback(Q=>{C&&P?P(Q):De(Q)},[C,P]),tr=Q=>{if(!Zt||t.find(H=>H.field===Q)?.sortable===!1)return;const se=de===Q&&ve==="asc"?"desc":"asc";xe(Q),we(se),u?.(Q,se)},Va=v.useCallback(()=>{if(!m||!Je)return;const Q=Je.map(se=>se[p]);if(Q.every(se=>g.includes(se)))m(g.filter(se=>!Q.includes(se)));else{const se=[...new Set([...g,...Q])];m(se)}},[Je,g,m,p]),Bo=v.useCallback(Q=>{m&&(g.includes(Q)?m(g.filter(be=>be!==Q)):m([...g,Q]))},[g,m]),Is=v.useCallback(Q=>{if(it.find(H=>H.field===Q)?.required)return;const se=Ye.includes(Q)?Ye.filter(H=>H!==Q):[...Ye,Q];at(se),_?.(se)},[it,Ye,_]),Br=v.useCallback((Q,be)=>{k?.(Q,be)},[k]),nr=v.useCallback(Q=>S?S.map(be=>{let se=be.disabled??!1;if(be.disabledField&&!se){const H=be.disabledField.split(".");let Z=Q;for(const he of H)Z=Z?.[he];Z||(se=!0)}return{...be,disabled:se,onClick:()=>Br(be.id,Q)}}):[],[S,Br]),rr=v.useCallback(Q=>{if(!ne)return!0;const be=Q[ne];return Array.isArray(be)?be.length>0:!!be},[ne]),kn=v.useCallback(Q=>{const be=Ne.includes(Q)?Ne.filter(se=>se!==Q):[...Ne,Q];Fe?Te(be):j&&j(be)},[Ne,j,Fe,Te]),jo=v.useCallback(Q=>Ne.includes(Q),[Ne]),Wo=v.useCallback(Q=>V?V(Q):Y&&Y.length>0&&ot?.renderExpandContent?ot.renderExpandContent({children:Y,row:Q,expandContext:te,componentContext:le,componentMap:il(),keyPrefix:`expand-${Q[p]}`}):null,[V,Y,p,te,le,le?.state]),Id=v.useMemo(()=>!Je||Je.length===0?!1:Je.every(Q=>g.includes(Q[p])),[Je,g,p]),Ha=v.useMemo(()=>{if(!Je||Je.length===0)return!1;const Q=Je.filter(be=>g.includes(be[p])).length;return Q>0&&Q{const se=Q[p],H=jo(se);return h.jsxs(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-4 mb-4",children:[h.jsxs(I,{className:"flex items-start justify-between",children:[G&&rr(Q)&&h.jsx(ae,{onClick:()=>kn(se),className:"p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors mr-2","aria-label":H?"Collapse card":"Expand card",children:h.jsx(oe,{name:H?K:q,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})}),f&&h.jsx(_n,{checked:g.includes(Q[p]),onChange:()=>Bo(Q[p]),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded mr-3 mt-1"}),h.jsx(I,{className:"flex-1",children:h.jsx(I,{className:"grid grid-cols-2 gap-x-4 gap-y-2",children:be.map(Z=>h.jsxs(I,{className:"min-w-0",children:[h.jsx(J,{className:"text-xs font-medium text-gray-500 dark:text-gray-400 uppercase",children:Z.header}),h.jsx(I,{className:"text-sm text-gray-900 dark:text-gray-200 mt-0.5 break-words",children:Z.cellChildren&&Z.cellChildren.length>0?Jr(Z.cellChildren,Q,Q[Z.field],`card-${Q[p]}-${Z.field}`,le):Z.render?Z.render(Q[Z.field],Q):al(Q[Z.field])})]},Z.field))})}),S&&S.length>0&&h.jsx(I,{className:"ml-2",children:h.jsx(Ls,{items:nr(Q),triggerLabel:"",triggerIconName:ue.EllipsisHorizontal})})]}),G&&H&&rr(Q)&&h.jsx(I,{className:"mt-4 pt-4 border-t border-gray-200 dark:border-gray-700",children:Wo(Q)}),(ge&&ge.length>0||me)&&iu(ce,Q)&&h.jsx(I,{className:ye?`mt-3 ${ye}`:"mt-3 pt-3 border-t border-gray-100 dark:border-gray-700",children:me?me(Q):ll(ge,Q,`card-subrow-${se}`,le)})]})};return rt?h.jsxs(I,{id:e,className:`w-full ${a}`,style:l,...ie,children:[y&&h.jsx(I,{className:"mb-4 flex justify-end",children:h.jsxs(I,{ref:Le,className:"relative",children:[h.jsxs(ae,{onClick:()=>Ke(!He),className:"flex items-center gap-2 px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700",children:[h.jsx(oe,{name:ue.Cog,className:"w-4 h-4 text-gray-500 dark:text-gray-400"}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:Ie})]}),He&&h.jsx(I,{className:"absolute right-0 z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",children:h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:it.map(Q=>h.jsxs(I,{className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${Q.required?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:()=>!Q.required&&Is(Q.field),children:[h.jsx(_n,{checked:Ye.includes(Q.field),disabled:Q.required,onChange:()=>{},className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"}),h.jsxs(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:[Q.header,Q.required&&h.jsxs(J,{className:"text-xs text-gray-500 dark:text-gray-400 ml-1",children:["(",mt,")"]})]})]},Q.field))})})]})}),f&&g.length>0&&Ge&&h.jsx(I,{className:"mb-4 text-sm text-gray-600 dark:text-gray-400",children:Ge.replace("{count}",String(g.length))}),h.jsx(I,{id:e?`${e}__body`:void 0,children:Je&&Je.length>0?Je.map((Q,be)=>h.jsx(I,{children:M?M(Q,dt):Ua(Q,dt)},Q[p]||be)):h.jsx(I,{className:"text-center py-8 text-gray-500 dark:text-gray-400",children:Be})}),pe&&pe.length>0&&Je&&Je.length>0&&h.jsx(I,{className:`rounded-lg p-4 mt-2 ${ke}`,children:$e&&$e.length>0?Jr($e,{},null,"footer-card",le):h.jsx(I,{className:"space-y-1",children:pe.filter(Q=>Q.text||Q.children&&Q.children.length>0).map((Q,be)=>{const se=dt.find(H=>H.field===Q.field);return h.jsxs(I,{className:"flex justify-between text-sm",children:[h.jsx(J,{className:"text-gray-500 dark:text-gray-400",children:se?.header||""}),h.jsx(J,{className:Q.className||"text-gray-900 dark:text-gray-100",children:Q.children&&Q.children.length>0?Jr(Q.children,{},null,`footer-mobile-${be}`,le):au(Q.text,`footer-mobile-text-${be}`,le)})]},`footer-mobile-${be}`)})})}),s&&xr&&h.jsx(I,{className:"flex justify-center mt-4",children:h.jsx(Yo,{currentPage:Qt,totalPages:wn,hasMorePages:C?A:void 0,onPageChange:an,showFirstLast:W,maxVisiblePages:5,prevText:_e,nextText:Ae})})]}):h.jsxs(I,{id:e,className:`w-full ${a}`,style:l,...ie,children:[y&&h.jsx(I,{className:"mb-4 flex justify-end",children:h.jsxs(I,{ref:Le,className:"relative",children:[h.jsxs(ae,{onClick:()=>Ke(!He),className:"flex items-center gap-2 px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700",children:[h.jsx(oe,{name:ue.Cog,className:"w-4 h-4 text-gray-500 dark:text-gray-400"}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:Ie})]}),He&&h.jsx(I,{className:"absolute right-0 z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",children:h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:it.map(Q=>h.jsxs(I,{className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${Q.required?"opacity-50 cursor-not-allowed":"cursor-pointer"}`,onClick:()=>!Q.required&&Is(Q.field),children:[h.jsx(_n,{checked:Ye.includes(Q.field),disabled:Q.required,onChange:()=>{},className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"}),h.jsxs(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:[Q.header,Q.required&&h.jsxs(J,{className:"text-xs text-gray-500 dark:text-gray-400 ml-1",children:["(",mt,")"]})]})]},Q.field))})})]})}),f&&g.length>0&&Ge&&h.jsx(I,{className:"mb-4 text-sm text-gray-600 dark:text-gray-400",children:Ge.replace("{count}",String(g.length))}),h.jsx(I,{id:e?`${e}__body`:void 0,className:"overflow-x-auto border border-gray-200 dark:border-gray-700 rounded-lg",children:h.jsxs(Ya,{className:"w-full",children:[h.jsx(Xa,{className:"bg-gray-50 dark:bg-gray-700",children:h.jsxs(ir,{children:[G&&h.jsx(Yr,{className:"px-2 py-3",style:{width:X}}),f&&h.jsx(Yr,{className:"px-4 py-3 w-10",children:h.jsx(_n,{checked:Id,ref:Q=>{Q&&(Q.indeterminate=Ha)},onChange:Va,className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"})}),dt.map(Q=>h.jsxs(Yr,{className:`px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider ${Zt&&Q.sortable!==!1?"cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600":""}`,style:{width:Q.width,minWidth:Q.width},onClick:Zt?()=>tr(Q.field):void 0,children:[Q.header,de===Q.field&&h.jsx(J,{className:"ml-1",children:ve==="asc"?"↑":"↓"})]},Q.field)),S&&S.length>0&&h.jsx(Yr,{className:"px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider w-24",children:gt})]})}),h.jsx(Ka,{className:`bg-white dark:bg-gray-800 ${ye?"":"divide-y divide-gray-200 dark:divide-gray-700"}`,children:Je&&Je.length>0?Je.map((Q,be)=>{const se=Q[p]||be,H=jo(se);return h.jsxs(v.Fragment,{children:[h.jsxs(ir,{className:`${i?"cursor-pointer":""} hover:bg-gray-50 dark:hover:bg-gray-700 ${g.includes(Q[p])?"bg-blue-50 dark:bg-blue-900/20":""} ${ye&&be>0?"border-t border-gray-200 dark:border-gray-700":""}`,onClick:()=>i?.(Q),children:[G&&h.jsx(Jt,{className:"px-2 py-4 text-center",onClick:Z=>Z.stopPropagation(),children:rr(Q)?h.jsx(ae,{onClick:()=>kn(se),className:"p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors","aria-label":H?"Collapse row":"Expand row",children:h.jsx(oe,{name:H?K:q,className:"w-4 h-4 text-gray-500 dark:text-gray-400"})}):null}),f&&h.jsx(Jt,{className:"px-4 py-4",onClick:Z=>Z.stopPropagation(),children:h.jsx(_n,{checked:g.includes(Q[p]),onChange:()=>Bo(Q[p]),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded"})}),dt.map(Z=>h.jsx(Jt,{className:"px-6 py-4 align-top whitespace-nowrap text-sm text-gray-900 dark:text-gray-200",style:{width:Z.width,minWidth:Z.width},children:Z.cellChildren&&Z.cellChildren.length>0?Jr(Z.cellChildren,Q,Q[Z.field],`${Q[p]}-${Z.field}`,le):Z.render?Z.render(Q[Z.field],Q):al(Q[Z.field])},Z.field)),S&&S.length>0&&h.jsx(Jt,{className:"px-6 py-4 text-right",onClick:Z=>Z.stopPropagation(),children:h.jsx(Ls,{items:nr(Q),triggerLabel:"",triggerIconName:ue.EllipsisHorizontal,position:"right"})})]}),G&&H&&rr(Q)&&h.jsx(ir,{className:"bg-gray-50 dark:bg-gray-900",children:h.jsx(Jt,{colSpan:dt.length+1+(f?1:0)+(S&&S.length>0?1:0),className:"px-6 py-4",children:Wo(Q)})}),(ge&&ge.length>0||me)&&iu(ce,Q)&&h.jsx(ir,{className:ye?"!border-t-0":"",children:h.jsx(Jt,{colSpan:dt.length+(G?1:0)+(f?1:0)+(S&&S.length>0?1:0),className:ye?"p-2":ye||"px-6 py-2 bg-gray-50 dark:bg-gray-900",children:ye?h.jsx(I,{className:ye,children:me?me(Q):ll(ge,Q,`subrow-${se}`,le)}):me?me(Q):ll(ge,Q,`subrow-${se}`,le)})})]},se)}):h.jsx(ir,{children:h.jsx(Jt,{colSpan:dt.length+(G?1:0)+(f?1:0)+(S&&S.length>0?1:0),className:"px-6 py-8 text-center text-gray-500 dark:text-gray-400",children:Be})})}),pe&&pe.length>0&&Je&&Je.length>0&&h.jsx(qa,{children:h.jsxs(ir,{className:ke,children:[G&&h.jsx(Jt,{className:"px-2 py-4"}),f&&h.jsx(Jt,{className:"px-4 py-4"}),(()=>{const Q=new Map(pe.filter(be=>be.field).map(be=>[be.field,be]));return dt.map(be=>{const se=Q.get(be.field);return se?h.jsx(Jt,{className:`px-6 py-4 text-sm text-gray-900 dark:text-gray-200 ${se.className||""}`,colSpan:se.colSpan,style:{width:be.width,minWidth:be.width},children:se.children&&se.children.length>0?Jr(se.children,{},null,`footer-${be.field}`,le):au(se.text,`footer-text-${be.field}`,le)},`footer-${be.field}`):h.jsx(Jt,{className:"px-6 py-4",style:{width:be.width,minWidth:be.width}},`footer-${be.field}`)})})(),S&&S.length>0&&h.jsx(Jt,{className:"px-6 py-4"})]})})]})}),s&&xr&&h.jsx(I,{className:"flex justify-center mt-4",children:h.jsx(Yo,{currentPage:Qt,totalPages:wn,hasMorePages:C?A:void 0,onPageChange:an,showFirstLast:W,maxVisiblePages:5,prevText:_e,nextText:Ae})})]})},fy=window.G7Core?.createLogger?.("Comp:CardGrid")??{log:(...e)=>console.log("[Comp:CardGrid]",...e),warn:(...e)=>console.warn("[Comp:CardGrid]",...e),error:(...e)=>console.error("[Comp:CardGrid]",...e)},hy=(e,t)=>window.G7Core?.t?.(e,t)??e,gy=()=>window.G7Core?.getComponentMap?.()||{},py=(e,t)=>{if(!e)return!0;const n=window.G7Core;return typeof n?.evaluateCondition=="function"?n.evaluateCondition(e,{row:t,item:t,$item:t}):(fy.warn("G7Core.evaluateCondition 미노출 — 조건을 평가할 수 없습니다:",e),!0)},lu=(e,t,n,r,s)=>{const o=window.G7Core;if(e.name==="ActionMenu"){if(r&&r.length>0&&s)return{...e,props:{...e.props,items:r.map(i=>({...i,onClick:()=>s(i.id,t)}))}};if(e.actions&&Array.isArray(e.actions)){const i=e.props?.items||[],a=new Map(e.actions.map(c=>[c.id,c])),l=i.map(c=>{if(typeof c.onClick=="string"){const d=c.onClick,u=a.get(d);if(u&&o?.ActionDispatcher)return{...c,onClick:()=>{const f={...n};o.ActionDispatcher.dispatch(u,f)}}}return c});return{...e,props:{...e.props,items:l}}}}return e.children&&Array.isArray(e.children)?{...e,children:e.children.map(i=>lu(i,t,n,r,s))}:e},my=(e,t,n,r="",s,o)=>{const i=window.G7Core;if(i?.renderItemChildren){const a=e.filter(d=>{const u=d.condition||d.if;return u?py(u,t):!0}),l={row:t,$value:t,index:n},c=a.map(d=>lu(d,t,l,s,o));try{const d=gy();return i.renderItemChildren(c,l,d,r)}catch{return h.jsxs(I,{className:"text-red-600 dark:text-red-400 p-4",children:[h.jsx(yt,{children:"컴포넌트 로드 실패"}),h.jsx(yt,{className:"text-sm",children:"[CardGrid] 데이터를 표시할 수 없습니다."})]})}}return null},cl=(e,t)=>{if(e.name==="Div"&&e.children&&e.children.length>0){const n=e.children.map((r,s)=>cl(r,`${t}-child-${s}`)).filter(Boolean);return h.jsx(I,{className:e.props?.className||"",children:n},t)}return h.jsx(I,{className:"h-4 bg-gray-200 dark:bg-gray-700 rounded w-full animate-pulse"},t)},cu=(e,t="skeleton")=>!e||e.length===0?null:e.map((n,r)=>{const s=`${t}-${n.id||n.name}-${r}`;return n.condition||n.if?null:(n.iteration,cl(n,s))}).filter(Boolean),by=({data:e,cardColumns:t=[],gridColumns:n=3,gap:r=4,responsiveColumns:s,cardClassName:o="",idField:i="id",pagination:a=!0,pageSize:l=12,serverSidePagination:c=!1,serverCurrentPage:d,serverTotalPages:u,alwaysShowPagination:f=!1,onPageChange:g,paginationInfoText:m,rowActions:p,onRowAction:y,className:b="",style:_,emptyMessage:x,prevText:M,nextText:S,showSkeleton:k=!0,skeletonCount:N,skeletonCellChildren:C,id:w,editorAttrs:E})=>{const A=x??hy("common.no_data"),P=t[0]?.cellChildren||[],[L,$]=v.useState(1);window.G7Core?.useResponsive?.();const O=c?d??1:L,T=c?u??1:Math.ceil((e?.length??0)/l),z=Y=>{c?g?.(Y):$(Y)},U=v.useCallback((Y,V)=>{y?.(Y,V)},[y]),W=v.useMemo(()=>{if(!e)return[];if(!a||c)return e;const Y=(L-1)*l,V=Y+l;return e.slice(Y,V)},[e,a,c,L,l]),G=v.useMemo(()=>{const Y=[],V=s?.sm??1;Y.push(`grid-cols-${V}`),s?.sm&&Y.push(`sm:grid-cols-${s.sm}`);const X=s?.md??Math.min(n,2);Y.push(`md:grid-cols-${X}`);const K=s?.lg??n;return Y.push(`lg:grid-cols-${K}`),s?.xl&&Y.push(`xl:grid-cols-${s.xl}`),Y.join(" ")},[n,s]),B=v.useMemo(()=>k?C&&C.length>0?cu(C,"custom-skeleton"):P&&P.length>0?cu(P,"auto-skeleton"):null:null,[k,C,P]);if(!e){if(!k)return null;const Y=N??n;return h.jsx(I,{className:`grid ${G} gap-${r} ${b}`,style:_,id:w,...E,children:Array.from({length:Y}).map((V,X)=>h.jsx(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6 space-y-4",children:B},`skeleton-${X}`))})}if(W.length===0)return h.jsx(I,{className:b,style:_,id:w,...E,children:h.jsx(I,{className:"bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-12",children:h.jsx(yt,{className:"text-center text-gray-600 dark:text-gray-400",children:A})})});const j=a&&(f||T>1);return h.jsxs(I,{className:b,style:_,id:w,...E,children:[h.jsx(I,{className:`grid ${G} gap-${r}`,children:W.map((Y,V)=>h.jsx(I,{className:o,children:my(P,Y,V,`card-${V}`,p,U)},Y[i]||V))}),j&&h.jsxs(I,{className:"mt-6 space-y-4 flex flex-col items-center",children:[h.jsx(Yo,{currentPage:O,totalPages:T,onPageChange:z,showFirstLast:!1,maxVisiblePages:5,prevText:M,nextText:S}),m&&h.jsx(I,{className:"text-sm text-gray-600 dark:text-gray-400 text-center",children:m})]})]})},yy=(e,t)=>window.G7Core?.t?.(e,t)??e,Zo=({isOpen:e,onClose:t,title:n,titlePrefix:r,titleSuffix:s,closeOnBackdropClick:o=!0,closeOnOverlayClick:i,showCloseButton:a=!0,closeOnEscape:l=!0,children:c,width:d="500px",className:u="",style:f,__dataContext:g,__translationContext:m})=>{const p=i!==void 0?i:o,y=v.useRef(null),b=v.useRef(null);if(v.useEffect(()=>{const M=S=>{S.key==="Escape"&&e&&l&&t()};if(e&&l)return document.addEventListener("keydown",M),()=>document.removeEventListener("keydown",M)},[e,t,l]),v.useEffect(()=>{if(e&&y.current){b.current=document.activeElement;const M=y.current.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),S=M[0],k=M[M.length-1];S?.focus();const N=C=>{C.key==="Tab"&&(C.shiftKey?document.activeElement===S&&(C.preventDefault(),k?.focus()):document.activeElement===k&&(C.preventDefault(),S?.focus()))};return document.addEventListener("keydown",N),()=>{document.removeEventListener("keydown",N),b.current?.focus()}}},[e]),v.useEffect(()=>{if(e)return document.body.style.overflow="hidden",()=>{document.body.style.overflow=""}},[e]),!e)return null;const _=f?.zIndex??50,x=(M,S)=>{if(!M||M.length===0)return null;const k=window.G7Core;if(!k)return null;const N=k.getDynamicRenderer?.();return N?M.map((C,w)=>h.jsx(N,{componentDef:C,dataContext:g||{},translationContext:m,registry:k.getComponentRegistry(),bindingEngine:k.getDataBindingEngine(),translationEngine:k.getTranslationEngine(),actionDispatcher:k.getActionDispatcher(),isRootRenderer:!1},`${S}-${w}`)):null};return h.jsxs(I,{className:"modal-wrapper",style:{zIndex:_},onClick:p?t:void 0,children:[h.jsx(I,{className:"modal-backdrop","aria-hidden":"true"}),h.jsxs(I,{ref:y,className:`modal-container ${u}`,style:{width:d,...f?{...f,zIndex:void 0}:{}},onClick:M=>M.stopPropagation(),role:"dialog","aria-modal":"true","aria-labelledby":n?"modal-title":void 0,children:[(n||r||s||a)&&h.jsxs(I,{className:"modal-header-row",children:[h.jsxs(I,{className:"flex-center gap-sm",children:[x(r,"title-prefix"),n&&h.jsx(I,{id:"modal-title",className:"modal-title",children:n}),x(s,"title-suffix")]}),a&&h.jsx(ae,{onClick:t,className:"modal-close-btn","aria-label":yy("common.close_modal"),children:h.jsx("svg",{className:"icon-lg",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]}),h.jsx(I,{className:"modal-body-content",children:c})]})]})},vy=({label:e,items:t,onItemClick:n,position:r="bottom-left",variant:s="default",className:o="",style:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(!1),[u,f]=v.useState(-1),g=v.useRef(null),m=v.useRef(null),[p,y]=v.useState(r);v.useEffect(()=>{const x=M=>{g.current&&!g.current.contains(M.target)&&(d(!1),f(-1))};if(c)return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[c]),v.useEffect(()=>{const x=M=>{if(!c)return;const S=t.map((k,N)=>k.disabled?-1:N).filter(k=>k!==-1);switch(M.key){case"ArrowDown":M.preventDefault(),f(k=>{const C=(S.indexOf(k)+1)%S.length;return S[C]});break;case"ArrowUp":M.preventDefault(),f(k=>{const N=S.indexOf(k),C=N<=0?S.length-1:N-1;return S[C]});break;case"Enter":M.preventDefault(),u>=0&&!t[u].disabled&&b(t[u]);break;case"Escape":M.preventDefault(),d(!1),f(-1);break}};if(c)return document.addEventListener("keydown",x),()=>document.removeEventListener("keydown",x)},[c,u,t]),v.useEffect(()=>{if(c&&m.current&&g.current){const x=m.current.getBoundingClientRect(),M=g.current.getBoundingClientRect(),S=window.innerWidth,k=window.innerHeight,N=x.width||224,C=x.height||200;let w=r;r.includes("left")?M.left+N>S&&(w=w.replace("left","right")):M.right-N<0&&(w=w.replace("right","left")),r.includes("top")?M.top-C<0&&(w=w.replace("top","bottom")):M.bottom+C>k&&(w=w.replace("bottom","top")),y(w)}},[c,r]);const b=x=>{x.disabled||(x.onClick?.(),n?.(x.value,x),d(!1),f(-1))},_={"bottom-left":"dropdown-menu-bottom-left","bottom-right":"dropdown-menu-bottom-right","top-left":"dropdown-menu-top-left","top-right":"dropdown-menu-top-right"};return h.jsxs(I,{ref:g,className:`dropdown ${o}`,style:i,id:a,...l,children:[h.jsxs(ae,{onClick:()=>d(!c),className:s==="text"?"dropdown-trigger-text":"dropdown-trigger","aria-haspopup":"true","aria-expanded":c,children:[e,s!=="text"&&h.jsx("svg",{className:`inline-block ml-2 w-4 h-4 transition-transform ${c?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),c&&h.jsx(I,{ref:m,className:`dropdown-menu ${_[p]}`,role:"menu","aria-orientation":"vertical",children:h.jsx(I,{className:"py-1",children:t.map((x,M)=>h.jsx(I,{className:`${x.disabled?"dropdown-item-disabled":"dropdown-item"} ${u===M?"dropdown-item-focused":""}`,onClick:()=>b(x),onMouseEnter:()=>!x.disabled&&f(M),role:"menuitem","aria-disabled":x.disabled,tabIndex:x.disabled?-1:0,children:x.label},x.value))})})]})},xy=({imageUrl:e,imageAlt:t="",title:n,subtitle:r,description:s,price:o,originalPrice:i,discountRate:a,currency:l="₩",buttonText:c="자세히 보기",onButtonClick:d,className:u="",style:f,id:g,editorAttrs:m})=>{const p=a&&a>0,y=(o??0).toLocaleString(),b=i?.toLocaleString();return h.jsxs(I,{className:`rounded-lg bg-white dark:bg-gray-800 shadow-md hover:shadow-xl transition-shadow border border-gray-200 dark:border-gray-700 overflow-hidden ${u}`,style:f,id:g,...m,children:[e&&h.jsxs(I,{className:"relative w-full",children:[h.jsx(Sn,{src:e,alt:t||n,className:"w-full h-64 object-cover"}),p&&h.jsx(I,{className:"absolute top-2 right-2 bg-red-500 text-white px-2 py-1 rounded-md",children:h.jsxs(J,{className:"text-sm font-bold",children:["-",a,"%"]})})]}),h.jsxs(I,{className:"p-4",children:[r&&h.jsx(jr,{className:"text-xs font-medium text-blue-600 dark:text-blue-400 uppercase tracking-wider mb-2",children:r}),h.jsx(or,{className:"text-xl font-bold text-gray-900 dark:text-white mb-2 line-clamp-2",children:n}),s&&h.jsx(yt,{className:"text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-3",children:s}),h.jsxs(I,{className:"flex items-center gap-2 mb-4",children:[h.jsxs(J,{className:"text-2xl font-bold text-gray-900 dark:text-white",children:[l,y]}),i&&h.jsxs(J,{className:"text-sm text-gray-400 dark:text-gray-500 line-through",children:[l,b]})]}),c&&h.jsx(ae,{onClick:d,className:"w-full bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg transition-colors",children:c})]})]})},wy=({value:e,label:t,change:n,changeLabel:r="전월 대비",iconName:s,trend:o="neutral",className:i="",style:a,id:l,editorAttrs:c})=>{const d=o==="up",u=o==="down",f=n!==void 0?Math.abs(n):0,g=d?"stats-trend-up":u?"stats-trend-down":"stats-trend-neutral",m=d?ue.ArrowUp:u?ue.ArrowDown:void 0;return h.jsxs(I,{className:`stat-card ${i}`,style:a,id:l,...c,children:[h.jsxs(I,{className:"flex-between mb-md",children:[s&&h.jsx(I,{className:"stats-icon stats-icon-blue",children:h.jsx(oe,{name:s,className:"icon-lg"})}),n!==void 0&&h.jsxs(I,{className:`stats-change ${g}`,children:[m&&h.jsx(oe,{name:m,className:"icon-sm"}),h.jsxs(J,{className:"text-sm font-semibold",children:[f,"%"]})]})]}),h.jsx(jr,{className:"stats-value-lg",children:typeof e=="number"?e.toLocaleString():e}),h.jsx(yt,{className:"stats-label",children:t}),n!==void 0&&r&&h.jsx(I,{className:"stats-sublabel",children:h.jsx(J,{children:r})})]})},du=()=>window.G7Core,uu=(e,t)=>du()?.t?.(e,t)??e,ky=({name:e="search",placeholder:t,value:n,onChange:r,onSubmit:s,showButton:o=!1,suggestions:i=[],onSuggestionClick:a,showSuggestions:l=!1,className:c="",style:d,id:u,editorAttrs:f})=>{const g=t??uu("common.search_placeholder"),[m,p]=v.useState(""),[y,b]=v.useState(!1),_=v.useRef(null),x=v.useRef(""),M=n!==void 0?n:m,S=l&&y&&i.length>0&&M.length>0,k="w-64 pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm",N=du()?.style?.mergeClasses?.(k,c)??`${k} ${c}`;v.useEffect(()=>{n!==void 0&&(x.current=n)},[n]);const C=A=>{const P=A.target.value;p(P),r?.(A),x.current!==""&&P===""&&!A.nativeEvent.inputType&&setTimeout(()=>{const L=_.current?.querySelector("form");L&&L.requestSubmit()},0),x.current=P},w=A=>{p(A.text);const P={target:{value:A.text,name:e}};r?.(P),a?.(A),b(!1)},E=A=>{A.preventDefault(),s?.(A)};return h.jsxs(I,{ref:_,className:"relative",style:d,id:u,...f,children:[h.jsx(Ga,{onSubmit:E,className:"relative",children:h.jsxs(I,{className:`relative flex items-center ${o?"gap-2":""}`,children:[h.jsxs(I,{className:"relative flex-1",children:[h.jsx(I,{className:"absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",children:h.jsx(oe,{name:ue.Search,className:"w-5 h-5 text-gray-400 dark:text-gray-500"})}),h.jsx(kt,{type:"search",name:e,value:M,onChange:C,onFocus:()=>b(!0),onBlur:()=>setTimeout(()=>b(!1),200),placeholder:g,className:N})]}),o&&h.jsx(ae,{type:"submit",className:"px-4 py-2 bg-gray-800 dark:bg-gray-600 text-white rounded-lg hover:bg-gray-900 dark:hover:bg-gray-500 transition-colors text-sm font-medium cursor-pointer h-[42px]",children:uu("common.search")})]})}),S&&h.jsx(I,{className:"absolute z-10 w-full mt-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-64 overflow-y-auto",children:i.map(A=>h.jsx(I,{className:"px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer border-b border-gray-100 dark:border-gray-700 last:border-b-0 transition-colors",onClick:()=>w(A),children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{name:ue.Search,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:A.text})]})},A.id))})]})},dl=(e,t)=>window.G7Core?.t?.(e,t)??e,Cy=({title:e,filters:t,onChange:n,onReset:r,showResetButton:s=!0,className:o="",style:i,id:a,editorAttrs:l})=>{const c=e??dl("common.filter"),d=(f,g)=>{n?.(f,g)},u=(f,g,m)=>{const p=t.find(b=>b.id===f);if(!p)return;const y=Array.isArray(p.value)?p.value:[];if(typeof g=="string"){const b=y.filter(x=>typeof x=="string"),_=m?[...b,g]:b.filter(x=>x!==g);n?.(f,_)}else{const b=y.filter(x=>typeof x=="number"),_=m?[...b,g]:b.filter(x=>x!==g);n?.(f,_)}};return h.jsxs(I,{className:`rounded-lg bg-white dark:bg-gray-800 shadow-md border border-gray-200 dark:border-gray-700 p-4 ${o}`,style:i,id:a,...l,children:[h.jsxs(I,{className:"flex items-center justify-between mb-4 pb-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(J,{className:"text-lg font-semibold text-gray-900 dark:text-white",children:c}),s&&r&&h.jsx(ae,{onClick:r,className:"text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium",children:dl("common.reset")})]}),h.jsx(I,{className:"space-y-4",children:t.map(f=>h.jsxs(I,{className:"space-y-2",children:[h.jsx(ln,{className:"block text-sm font-medium text-gray-700 dark:text-gray-300",children:f.label}),f.type==="select"&&h.jsxs(Gr,{value:f.value,onChange:g=>d(f.id,g.target.value),className:"w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 bg-white dark:bg-gray-700 text-gray-900 dark:text-white",children:[h.jsx("option",{value:"",children:dl("common.select_placeholder")}),f.options.map(g=>h.jsx("option",{value:g.value,children:g.label},g.id))]}),f.type==="checkbox"&&h.jsx(I,{className:"space-y-2",children:f.options.map(g=>{const m=Array.isArray(f.value)?f.value.some(p=>p===g.value):!1;return h.jsxs(ln,{className:"flex items-center gap-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 p-2 rounded-md transition-colors",children:[h.jsx(_n,{checked:m,onChange:p=>u(f.id,g.value,p.target.checked),className:"w-4 h-4 text-blue-600 border-gray-300 dark:border-gray-600 rounded focus:ring-blue-500"}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:g.label})]},g.id)})})]},f.id))})]})},Sy=({tabs:e,activeTabId:t,onTabChange:n,variant:r="default",className:s="",style:o,mobileBreakpoint:i=768,id:a,editorAttrs:l})=>{const d=window.G7Core?.useResponsive,u=d?.(),f=u?u.width{!k.disabled&&k.id!==t&&n?.(k.id)},p=(k,N)=>{const C=k.key==="ArrowRight"?1:k.key==="ArrowLeft"?-1:0;let w=null;if(C!==0)for(let E=1;E<=e.length;E+=1){const A=(N+C*E+e.length*E)%e.length;if(!e[A]?.disabled){w=A;break}}else if(k.key==="Home")w=e.findIndex(E=>!E.disabled);else if(k.key==="End"){for(let E=e.length-1;E>=0;E-=1)if(!e[E]?.disabled){w=E;break}}w===null||w<0||(k.preventDefault(),g.current[w]?.focus(),m(e[w]))},y=k=>{const N=k.target.value,C=Number(N),w=typeof N=="string"&&N!==""&&!Number.isNaN(C)?C:N,E=e.find(A=>String(A.id)===String(w));E&&m(E)};if(f)return h.jsx(I,{className:s,style:o,id:a,...l,children:h.jsx(Gr,{value:t!==void 0?String(t):"",onChange:y,options:e.map(k=>({value:String(k.id),label:k.badge!==void 0?`${k.label} (${k.badge})`:k.label,disabled:k.disabled})),className:"w-full"})});const b=k=>{const N=k.id===t;if(k.disabled)return"tab-btn-base tab-btn-disabled";switch(r){case"pills":return N?"tab-btn-base tab-btn-pills-active":"tab-btn-base tab-btn-pills";case"underline":return N?"tab-btn-base tab-btn-underline-active":"tab-btn-base tab-btn-underline";default:return N?"tab-btn-base tab-btn-default-active":"tab-btn-base tab-btn-default"}},_=r==="underline"?"tab-nav-underline":"tab-nav-default",x=l?.["data-editor-path"],M=e.findIndex(k=>k.id===t),S=M>=0?M:e.findIndex(k=>!k.disabled);return h.jsx(Go,{className:`${_} ${s}`,style:o,role:"tablist","aria-orientation":"horizontal",id:a,...l,children:e.map((k,N)=>h.jsxs(ae,{ref:C=>{g.current[N]=C},type:"button",role:"tab","aria-selected":k.id===t,tabIndex:N===S?0:-1,onClick:()=>m(k),onKeyDown:C=>p(C,N),disabled:k.disabled,className:b(k),...x?{"data-editor-item-path":`${x}.props.tabs.${N}`}:{},children:[k.iconName&&h.jsx(oe,{name:k.iconName,className:"w-4 h-4"}),h.jsx(J,{children:k.label}),k.badge!==void 0&&h.jsx(I,{className:"flex items-center justify-center min-w-[20px] h-5 px-1.5 bg-red-500 dark:bg-red-600 text-white dark:text-white text-xs font-bold rounded-full",children:h.jsx(J,{children:k.badge})})]},k.id))})},ul=window.G7Core?.createLogger?.("Handler:ScrollToSection")??{log:(...e)=>console.log("[Handler:ScrollToSection]",...e),warn:(...e)=>console.warn("[Handler:ScrollToSection]",...e),error:(...e)=>console.error("[Handler:ScrollToSection]",...e)};async function fu(e,t){const n=e.params||{},{targetId:r,offset:s=120,delay:o=100,scrollContainerId:i}=n;if(!r){ul.warn("targetId is required");return}const a=await _y(r,o);if(!a){ul.warn(`Element not found: ${r}`);return}const l=Ey(a,i),c=My(a,l,s);Ny(l,c)}async function _y(e,t){await new Promise(s=>setTimeout(s,t));const n=10,r=50;for(let s=0;ssetTimeout(i,50)),o;await new Promise(i=>setTimeout(i,r))}return null}function Ey(e,t){if(t){const o=document.getElementById(t);if(o&&hu(o))return o;ul.warn(`Container '${t}' ${o?"has no scroll":"not found"}, trying auto-detect`)}let n=e.parentElement,r=0;const s=10;for(;n&&n!==document.body&&re.clientHeight}function My(e,t,n){if(t===window||!("getBoundingClientRect"in t))return e.getBoundingClientRect().top+window.pageYOffset-n;{const r=t.getBoundingClientRect(),s=e.getBoundingClientRect();return t.scrollTop+(s.top-r.top)-n}}function Ny(e,t){e===window||!("scrollTo"in e)?window.scrollTo({top:t,behavior:"smooth"}):e.scrollTo({top:t,behavior:"smooth"})}window.G7Core?.createLogger?.("Comp:TabNavigation");const Ty=({tabs:e,activeTabId:t,className:n="bg-white dark:bg-gray-800 flex gap-2 border-b border-gray-200 dark:border-gray-700 px-6",style:r,activeClassName:s="tab-btn-base tab-btn-default-active",inactiveClassName:o="tab-btn-base tab-btn-default",sectionIdPrefix:i="tab_content_",scrollOffset:a=120,scrollDelay:l=100,onTabChange:c,enableScrollSpy:d=!1,scrollSpyOffset:u=80,scrollContainerId:f,id:g,editorAttrs:m})=>{const[p,y]=v.useState(t??(e.length>0?e[0].id:"")),_=window.G7Core?.useResponsive,x=_?.(),M=x?x.width<768:typeof window<"u"&&window.innerWidth<768,S=v.useRef(!1),k=v.useRef(null),N=v.useRef([]),C=L=>{if(d&&(S.current=!0,k.current&&clearTimeout(k.current)),L.onClick){if(L.onClick(L.id),!d)return}else if(c){if(c(L.id),!d)return}else y(L.id);const $=L.scrollOffset??a,D=L.scrollDelay??l;fu({params:{targetId:`${i}${L.id}`,offset:$,delay:D,scrollContainerId:f}}),d&&(k.current=setTimeout(()=>{S.current=!1},D+500))},w=(L,$)=>{const D=L.key==="ArrowRight"?1:L.key==="ArrowLeft"?-1:0;let O=null;if(D!==0)for(let T=1;T<=e.length;T+=1){const z=($+D*T+e.length*T)%e.length;if(!e[z]?.disabled){O=z;break}}else if(L.key==="Home")O=e.findIndex(T=>!T.disabled);else if(L.key==="End"){for(let T=e.length-1;T>=0;T-=1)if(!e[T]?.disabled){O=T;break}}O===null||O<0||(L.preventDefault(),N.current[O]?.focus(),C(e[O]))};v.useEffect(()=>{if(!d)return;const L=f?document.getElementById(f):null,$={root:L??null,rootMargin:`-${u}px 0px -30% 0px`,threshold:[0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1]},D=new Map,O=()=>{const W=L,G=W?W.scrollTop:window.pageYOffset||document.documentElement.scrollTop,B=W?W.scrollHeight:document.documentElement.scrollHeight,j=W?W.clientHeight:document.documentElement.clientHeight;return G+j>=B-10},T=W=>{if(S.current)return;W.forEach(X=>{const K=X.target.id;X.isIntersecting?D.set(K,X.intersectionRatio):D.delete(K)});let G=0,B="";D.forEach((X,K)=>{X>G&&(G=X,B=K)});const j=e[e.length-1],Y=`${i}${j?.id}`,V=D.has(Y);if(O()&&V&&j){y(j.id);return}if(B){const X=B.replace(i,""),K=Number(X),q=isNaN(K)?X:K;e.some(ee=>ee.id===q)&&y(q)}},z=()=>{},U=new IntersectionObserver(T,$);return e.forEach(W=>{const G=document.getElementById(`${i}${W.id}`);G&&U.observe(G)}),window.addEventListener("scroll",z,{passive:!0}),()=>{U.disconnect(),window.removeEventListener("scroll",z),k.current&&clearTimeout(k.current)}},[d,e,i,u,f]);const E=L=>{const $=L.target.value,D=Number($),O=isNaN(D)?$:D,T=e.find(z=>z.id===O);T&&C(T)};if(M)return h.jsx(I,{className:"bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-4 py-3",id:g,...m,children:h.jsx(Gr,{value:String(p),onChange:E,options:e.map(L=>({value:String(L.id),label:L.label,disabled:L.disabled})),className:"w-full"})});const A=e.findIndex(L=>L.id===p),P=A>=0?A:e.findIndex(L=>!L.disabled);return h.jsx(I,{className:n,style:r,role:"tablist","aria-orientation":"horizontal",id:g,...m,children:e.map((L,$)=>h.jsxs(ae,{ref:D=>{N.current[$]=D},type:"button",role:"tab","aria-selected":p===L.id,tabIndex:$===P?0:-1,className:p===L.id?s:o,disabled:L.disabled,onClick:()=>C(L),onKeyDown:D=>w(D,$),children:[L.iconName&&h.jsx(oe,{name:L.iconName,size:"sm"}),h.jsx(J,{children:L.label})]},L.id))})},Oy=()=>window.G7Core,Ay=({iconName:e,label:t,onClick:n,variant:r="primary",size:s="md",disabled:o=!1,className:i="",style:a,id:l,editorAttrs:c})=>{const d={sm:t?"px-3 py-1.5 text-sm":"w-8 h-8",md:t?"px-4 py-2 text-base":"w-10 h-10",lg:t?"px-5 py-2.5 text-lg":"w-12 h-12"},u={sm:"w-4 h-4",md:"w-5 h-5",lg:"w-6 h-6"},f={primary:"bg-blue-600 hover:bg-blue-700 text-white border-transparent",secondary:"bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600",danger:"bg-red-600 hover:bg-red-700 text-white border-transparent",ghost:"bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-transparent",outline:"bg-transparent hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600"},g="inline-flex items-center justify-center gap-2 font-medium rounded-lg border transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-400",m=o?"opacity-50 cursor-not-allowed":"",p=`${g} ${d[s]} ${f[r]} ${m}`,y=Oy()?.style?.mergeClasses?.(p,i)??`${p} ${i}`;return h.jsxs(ae,{onClick:n,disabled:o,className:y,style:a,id:l,...c,children:[h.jsx(oe,{name:e,className:u[s]}),t]})},Dy=(e,t)=>window.G7Core?.t?.(e,t)??e,gu=({isOpen:e,onClose:t,title:n,content:r,actions:s=[],width:o="500px",closeOnOverlay:i=!0,showCloseButton:a=!0,className:l="",style:c})=>{const d=()=>{i&&t()},u={primary:"dialog-btn-primary",secondary:"dialog-btn-secondary",danger:"dialog-btn-danger",ghost:"dialog-btn-ghost"};return h.jsxs(Zo,{isOpen:e,onClose:d,width:o,className:l,style:c,children:[h.jsxs(I,{className:"dialog-header",children:[h.jsx(or,{className:"dialog-title",children:n}),a&&h.jsx(ae,{onClick:t,className:"dialog-close","aria-label":Dy("common.close_dialog"),children:h.jsx(oe,{name:ue.Times,className:"icon-md"})})]}),h.jsx(I,{className:"dialog-body",children:r}),s.length>0&&h.jsx(I,{className:"dialog-footer",children:s.map((f,g)=>h.jsxs(ae,{onClick:f.onClick,disabled:f.disabled,className:u[f.variant||"primary"],children:[f.iconName&&h.jsx(oe,{name:f.iconName,className:"icon-sm mr-2 inline-block"}),f.label]},g))})]})},fl=(e,t)=>window.G7Core?.t?.(e,t)??e,pu=({isOpen:e,onClose:t,title:n,message:r,confirmText:s,cancelText:o,onConfirm:i,onCancel:a,confirmButtonVariant:l="primary",cancelButtonVariant:c="secondary",width:d="500px",closeOnOverlay:u=!0,isLoading:f=!1,loadingText:g})=>{const m=s??fl("common.confirm"),p=o??fl("common.cancel"),y=g??fl("common.loading"),b=()=>{i()},_=()=>{f||(a?.(),t())},x=()=>{f||t()},M={primary:"bg-blue-600 text-white hover:bg-blue-700",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600",danger:"bg-red-600 text-white hover:bg-red-700",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"};return h.jsxs(Zo,{isOpen:e,onClose:x,title:n,width:d,children:[h.jsx(I,{className:"py-2",children:typeof r=="string"?h.jsx(yt,{className:"text-gray-700 dark:text-gray-300",children:r}):r}),h.jsxs(I,{className:"flex justify-end gap-3 mt-6",children:[h.jsx(ae,{onClick:_,disabled:f,className:"px-4 py-2 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed",children:h.jsx(J,{children:p})}),h.jsxs(ae,{onClick:b,disabled:f,className:`flex items-center gap-2 px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed ${M[l]}`,children:[f&&h.jsx(oe,{name:ue.Spinner,className:"w-4 h-4 animate-spin"}),h.jsx(J,{children:f?y:m})]})]})]})},Iy=(e,t)=>window.G7Core?.t?.(e,t)??e,Ly=({isOpen:e,onClose:t,title:n,message:r,confirmText:s,onConfirm:o,confirmButtonVariant:i="primary",width:a="500px",closeOnOverlay:l=!0})=>{const u=[{label:s??Iy("common.confirm"),onClick:()=>{o?.(),t()},variant:i}],f=typeof r=="string"?h.jsx(yt,{className:"text-gray-700 dark:text-gray-300",children:r}):r;return h.jsx(gu,{isOpen:e,onClose:t,title:n,content:f,actions:u,width:a,closeOnOverlay:l})},Py=(e,t)=>window.G7Core?.t?.(e,t)??e,$y={success:ue.CheckCircle,error:ue.XCircle,warning:ue.ExclamationTriangle,info:ue.InfoCircle},mu={success:{bg:"bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800",text:"text-green-800 dark:text-green-300"},error:{bg:"bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800",text:"text-red-800 dark:text-red-300"},warning:{bg:"bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800",text:"text-yellow-800 dark:text-yellow-300"},info:{bg:"bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800",text:"text-blue-800 dark:text-blue-300"}},Fy={"top-left":"top-4 left-4","top-center":"top-4 left-1/2 -translate-x-1/2","top-right":"top-4 right-4","bottom-left":"bottom-4 left-4","bottom-center":"bottom-4 left-1/2 -translate-x-1/2","bottom-right":"bottom-4 right-4"},zy=({toasts:e,position:t="bottom-center",duration:n=3e3,onRemove:r})=>{const[s,o]=v.useState(new Map),i=v.useCallback(a=>{o(l=>{const c=new Map(l);return c.set(a,!1),c}),setTimeout(()=>{if(r)r(a);else{const l=window.G7Core;l?.state?.update&&l.state.update(c=>({toasts:(Array.isArray(c.toasts)?c.toasts:[]).filter(u=>u.id!==a)}))}},300)},[r]);return v.useEffect(()=>{!e||e.length===0||e.forEach(a=>{if(s.has(a.id))return;o(c=>{const d=new Map(c);return d.set(a.id,!0),d});const l=a.duration||n;l>0&&setTimeout(()=>{i(a.id)},l)})},[e,n,i,s]),!e||e.length===0?null:h.jsx(I,{className:`fixed z-[9999] flex flex-col gap-2 ${Fy[t]}`,"aria-live":"polite","aria-atomic":"true",children:e.map(a=>h.jsx(Vy,{toast:a,position:t,isVisible:s.get(a.id)??!0,onClose:()=>i(a.id)},a.id))})},Vy=({toast:e,position:t,isVisible:n,onClose:r})=>{const s=mu[e.type]||mu.info,o=e.icon?e.icon:$y[e.type],i=t.startsWith("bottom")?"translate-y-2":"-translate-y-2";return h.jsxs(I,{className:` flex items-center gap-3 p-4 rounded-lg border shadow-lg min-w-[300px] max-w-md transition-all duration-300 ${s.bg} ${n?"opacity-100 translate-y-0":`opacity-0 ${i}`} - `,role:"alert",children:[h.jsx(re,{name:o,className:`w-5 h-5 flex-shrink-0 ${s.text}`}),h.jsx(Q,{className:`flex-1 text-sm font-medium ${s.text}`,children:t.message}),h.jsx(oe,{onClick:r,className:`flex-shrink-0 p-1 rounded hover:bg-black/10 dark:hover:bg-white/10 ${s.text} focus:outline-none`,"aria-label":gy("common.close_notification"),children:h.jsx(re,{name:de.Times,className:"w-4 h-4"})})]})},ou=({items:t,separator:e=h.jsx(re,{name:de.ChevronRight,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),showHome:n=!1,homeHref:r="/",maxItems:s,className:o="",id:i,editorAttrs:a})=>{let c=n?[{label:"Home",href:r},...t]:t,d=c,u=!1;if(s&&c.length>s){u=!0;const f=c.slice(0,1),g=c.slice(-(s-2));d=[...f,...g]}return h.jsx(Po,{className:`flex items-center space-x-2 text-sm ${o}`,"aria-label":"Breadcrumb",id:i,...a,children:h.jsx(I,{className:"flex items-center space-x-2",children:d.map((f,g)=>{const m=g===d.length-1,p=g===0;return h.jsxs(v.Fragment,{children:[u&&p&&h.jsxs(h.Fragment,{children:[h.jsx(iu,{item:f,isLast:!1}),h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e}),h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:"..."}),d.length>1&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e})]}),(!u||!p)&&h.jsxs(h.Fragment,{children:[h.jsx(iu,{item:f,isLast:m}),!m&&h.jsx(Q,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:e})]})]},g)})})})},iu=({item:t,isLast:e})=>e||!t.href&&!t.onClick?h.jsx(Q,{className:"font-medium text-gray-900 dark:text-white","aria-current":e?"page":void 0,children:t.label}):t.onClick?h.jsx(Lr,{href:"#",onClick:n=>{n.preventDefault(),t.onClick?.()},className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:t.label}):h.jsx(Lr,{href:t.href,className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:t.label}),vy=({title:t,description:e,iconName:n,illustrationSrc:r,illustrationAlt:s="Empty state illustration",actions:o=[],className:i="",id:a,editorAttrs:l})=>{const c={primary:"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600 focus:ring-gray-500",danger:"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-gray-500"};return h.jsxs(I,{className:`flex flex-col items-center justify-center p-8 text-center ${i}`,role:"status","aria-live":"polite",id:a,...l,children:[r?h.jsx(yn,{src:r,alt:s,className:"w-48 h-48 mb-6 object-contain"}):n?h.jsx(I,{className:"mb-6 p-4 rounded-full bg-gray-100 dark:bg-gray-700",children:h.jsx(re,{name:n,className:"w-12 h-12 text-gray-400 dark:text-gray-500"})}):null,h.jsx(Jn,{className:"text-xl font-semibold text-gray-900 dark:text-white mb-2",children:t}),e&&h.jsx(yt,{className:"text-gray-600 dark:text-gray-400 mb-6 max-w-md",children:e}),o.length>0&&h.jsx(I,{className:"flex flex-wrap gap-3 justify-center",children:o.map((d,u)=>h.jsxs(oe,{onClick:d.onClick,className:` + `,role:"alert",children:[h.jsx(oe,{name:o,className:`w-5 h-5 flex-shrink-0 ${s.text}`}),h.jsx(J,{className:`flex-1 text-sm font-medium ${s.text}`,children:e.message}),h.jsx(ae,{onClick:r,className:`flex-shrink-0 p-1 rounded hover:bg-black/10 dark:hover:bg-white/10 ${s.text} focus:outline-none`,"aria-label":Py("common.close_notification"),children:h.jsx(oe,{name:ue.Times,className:"w-4 h-4"})})]})},bu=({items:e,separator:t=h.jsx(oe,{name:ue.ChevronRight,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),showHome:n=!1,homeHref:r="/",maxItems:s,className:o="",id:i,editorAttrs:a})=>{let c=n?[{label:"Home",href:r},...e]:e,d=c,u=!1;if(s&&c.length>s){u=!0;const f=c.slice(0,1),g=c.slice(-(s-2));d=[...f,...g]}return h.jsx(Go,{className:`flex items-center space-x-2 text-sm ${o}`,"aria-label":"Breadcrumb",id:i,...a,children:h.jsx(I,{className:"flex items-center space-x-2",children:d.map((f,g)=>{const m=g===d.length-1,p=g===0;return h.jsxs(v.Fragment,{children:[u&&p&&h.jsxs(h.Fragment,{children:[h.jsx(yu,{item:f,isLast:!1}),h.jsx(J,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t}),h.jsx(J,{className:"text-gray-500 dark:text-gray-400",children:"..."}),d.length>1&&h.jsx(J,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t})]}),(!u||!p)&&h.jsxs(h.Fragment,{children:[h.jsx(yu,{item:f,isLast:m}),!m&&h.jsx(J,{className:"text-gray-400 dark:text-gray-500","aria-hidden":"true",children:t})]})]},g)})})})},yu=({item:e,isLast:t})=>t||!e.href&&!e.onClick?h.jsx(J,{className:"font-medium text-gray-900 dark:text-white","aria-current":t?"page":void 0,children:e.label}):e.onClick?h.jsx(Wr,{href:"#",onClick:n=>{n.preventDefault(),e.onClick?.()},className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:e.label}):h.jsx(Wr,{href:e.href,className:"text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors",children:e.label}),Hy=({title:e,description:t,iconName:n,illustrationSrc:r,illustrationAlt:s="Empty state illustration",actions:o=[],className:i="",id:a,editorAttrs:l})=>{const c={primary:"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",secondary:"bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600 focus:ring-gray-500",danger:"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",ghost:"bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-gray-500"};return h.jsxs(I,{className:`flex flex-col items-center justify-center p-8 text-center ${i}`,role:"status","aria-live":"polite",id:a,...l,children:[r?h.jsx(Sn,{src:r,alt:s,className:"w-48 h-48 mb-6 object-contain"}):n?h.jsx(I,{className:"mb-6 p-4 rounded-full bg-gray-100 dark:bg-gray-700",children:h.jsx(oe,{name:n,className:"w-12 h-12 text-gray-400 dark:text-gray-500"})}):null,h.jsx(or,{className:"text-xl font-semibold text-gray-900 dark:text-white mb-2",children:e}),t&&h.jsx(yt,{className:"text-gray-600 dark:text-gray-400 mb-6 max-w-md",children:t}),o.length>0&&h.jsx(I,{className:"flex flex-wrap gap-3 justify-center",children:o.map((d,u)=>h.jsxs(ae,{onClick:d.onClick,className:` px-4 py-2 rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 ${c[d.variant||"primary"]} - `,children:[d.iconName&&h.jsx(re,{name:d.iconName,className:"w-4 h-4 mr-2 inline-block"}),d.label]},u))})]})},xy=(t,e)=>window.G7Core?.t?.(t,e)??t,wy=({id:t,size:e="md",color:n="text-blue-600",fullscreen:r=!1,text:s,className:o="",editorAttrs:i})=>{const a={sm:"w-4 h-4 border-2",md:"w-8 h-8 border-2",lg:"w-12 h-12 border-3",xl:"w-16 h-16 border-4"},l={sm:"text-xs",md:"text-sm",lg:"text-base",xl:"text-lg"},c=h.jsxs(I,{className:`flex flex-col items-center justify-center gap-3 ${o}`,children:[h.jsx(I,{className:` - ${a[e]} + `,children:[d.iconName&&h.jsx(oe,{name:d.iconName,className:"w-4 h-4 mr-2 inline-block"}),d.label]},u))})]})},Uy=(e,t)=>window.G7Core?.t?.(e,t)??e,By=({id:e,size:t="md",color:n="text-blue-600",fullscreen:r=!1,text:s,className:o="",editorAttrs:i})=>{const a={sm:"w-4 h-4 border-2",md:"w-8 h-8 border-2",lg:"w-12 h-12 border-3",xl:"w-16 h-16 border-4"},l={sm:"text-xs",md:"text-sm",lg:"text-base",xl:"text-lg"},c=h.jsxs(I,{className:`flex flex-col items-center justify-center gap-3 ${o}`,children:[h.jsx(I,{className:` + ${a[t]} border-gray-200 dark:border-gray-700 border-t-current rounded-full animate-spin ${n} - `,role:"status","aria-label":xy("common.loading")}),s&&h.jsx(Q,{className:`${l[e]} ${n} font-medium`,children:s})]});return r?h.jsx(I,{id:t,className:"fixed inset-0 z-50 flex items-center justify-center bg-white dark:bg-gray-900 bg-opacity-90 dark:bg-opacity-90","aria-live":"polite","aria-busy":"true",...i,children:c}):h.jsx(I,{id:t,className:"flex items-center justify-center p-4","aria-live":"polite","aria-busy":"true",...i,children:c})},Vr=()=>window.G7Core,sl=(t,e)=>window.G7Core?.t?.(t,e)??t,ky=({logo:t,logoAlt:e="Logo",menu:n=[],collapsed:r=!1,onToggleCollapse:s,className:o="",currentLocale:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(new Set),[u,f]=v.useState(i||Vr()?.locale?.current?.()||"ko"),[g,m]=v.useState(window.location.pathname),p=v.useMemo(()=>{const w=[],M=T=>{for(const P of T)P.url&&w.push(P.url),P.children&&M(P.children)};return M(n),w},[n]),y=v.useCallback(w=>{let M=null;for(const T of p)(w===T||w.startsWith(T+"/"))&&(!M||T.length>M.length)&&(M=T);return M},[p]);v.useEffect(()=>{const w=Vr();return w?.state?.subscribe?w.state.subscribe(T=>{const P=T?._global?.locale||w?.locale?.current?.()||"ko";f(P);const L=T?._global?.currentPath||window.location.pathname;m(L)}):void 0},[]),v.useEffect(()=>{const w=()=>{m(window.location.pathname)};return window.addEventListener("popstate",w),()=>window.removeEventListener("popstate",w)},[]),v.useEffect(()=>{i&&f(i)},[i]);const b=(w,M)=>!w||typeof M!="string"?!1:M===w?!0:M.startsWith(w+"/")?y(M)===w:!1,_=(w,M)=>{const T=new Set,P=L=>{for(const $ of L)if($.children&&$.children.length>0){if(P($.children)||b($.url,M))return T.add($.id),!0}else if(b($.url,M))return!0;return!1};return P(w),T};v.useEffect(()=>{if(n.length>0&&g){const w=_(n,g);w.size>0&&d(M=>new Set([...M,...w]))}},[g,n]);const x=w=>b(w,g),E=w=>w.children?w.children.some(M=>x(M.url)||E(M)):!1,C=w=>{d(M=>{const T=new Set(M);return T.has(w)?T.delete(w):T.add(w),T})},k=w=>typeof w=="string"?w:w[u]||w.en||Object.values(w)[0]||"",N=(w,M)=>{Vr()?.dispatch?(w.preventDefault(),Vr().navigation?.onComplete(()=>{Vr().dispatch({handler:"setState",params:{target:"global",sidebarOpen:!1}})}),Vr().dispatch({handler:"navigate",params:{path:M}})):window.location.href=M},S=(w,M=0,T=!1)=>{const P=w.children&&w.children.length>0,L=c.has(w.id),$=k(w.name),A=M>0,D=x(w.url),O=E(w),z=A?"admin-sidebar-subitem":"admin-sidebar-item",H="admin-sidebar-item-active",j="admin-sidebar-item-active-parent",W="admin-sidebar-item-default";return h.jsxs(I,{className:A?"relative group":"",children:[A&&h.jsxs(h.Fragment,{children:[!T&&h.jsx(I,{className:"admin-sidebar-tree-line-vertical"}),h.jsx(I,{className:"admin-sidebar-tree-line-curve"})]}),P&&w.url?h.jsxs(I,{className:` + `,role:"status","aria-label":Uy("common.loading")}),s&&h.jsx(J,{className:`${l[t]} ${n} font-medium`,children:s})]});return r?h.jsx(I,{id:e,className:"fixed inset-0 z-50 flex items-center justify-center bg-white dark:bg-gray-900 bg-opacity-90 dark:bg-opacity-90","aria-live":"polite","aria-busy":"true",...i,children:c}):h.jsx(I,{id:e,className:"flex items-center justify-center p-4","aria-live":"polite","aria-busy":"true",...i,children:c})},Rr=()=>window.G7Core,hl=(e,t)=>window.G7Core?.t?.(e,t)??e,jy=({logo:e,logoAlt:t="Logo",menu:n=[],collapsed:r=!1,onToggleCollapse:s,className:o="",currentLocale:i,id:a,editorAttrs:l})=>{const[c,d]=v.useState(new Set),[u,f]=v.useState(i||Rr()?.locale?.current?.()||"ko"),[g,m]=v.useState(window.location.pathname),p=v.useMemo(()=>{const w=[],E=A=>{for(const P of A)P.url&&w.push(P.url),P.children&&E(P.children)};return E(n),w},[n]),y=v.useCallback(w=>{let E=null;for(const A of p)(w===A||w.startsWith(A+"/"))&&(!E||A.length>E.length)&&(E=A);return E},[p]);v.useEffect(()=>{const w=Rr();return w?.state?.subscribe?w.state.subscribe(A=>{const P=A?._global?.locale||w?.locale?.current?.()||"ko";f(P);const L=A?._global?.currentPath||window.location.pathname;m(L)}):void 0},[]),v.useEffect(()=>{const w=()=>{m(window.location.pathname)};return window.addEventListener("popstate",w),()=>window.removeEventListener("popstate",w)},[]),v.useEffect(()=>{i&&f(i)},[i]);const b=(w,E)=>!w||typeof E!="string"?!1:E===w?!0:E.startsWith(w+"/")?y(E)===w:!1,_=(w,E)=>{const A=new Set,P=L=>{for(const $ of L)if($.children&&$.children.length>0){if(P($.children)||b($.url,E))return A.add($.id),!0}else if(b($.url,E))return!0;return!1};return P(w),A};v.useEffect(()=>{if(n.length>0&&g){const w=_(n,g);w.size>0&&d(E=>new Set([...E,...w]))}},[g,n]);const x=w=>b(w,g),M=w=>w.children?w.children.some(E=>x(E.url)||M(E)):!1,S=w=>{d(E=>{const A=new Set(E);return A.has(w)?A.delete(w):A.add(w),A})},k=w=>typeof w=="string"?w:w[u]||w.en||Object.values(w)[0]||"",N=(w,E)=>{Rr()?.dispatch?(w.preventDefault(),Rr().navigation?.onComplete(()=>{Rr().dispatch({handler:"setState",params:{target:"global",sidebarOpen:!1}})}),Rr().dispatch({handler:"navigate",params:{path:E}})):window.location.href=E},C=(w,E=0,A=!1)=>{const P=w.children&&w.children.length>0,L=c.has(w.id),$=k(w.name),D=E>0,O=x(w.url),T=M(w),z=D?"admin-sidebar-subitem":"admin-sidebar-item",U="admin-sidebar-item-active",W="admin-sidebar-item-active-parent",G="admin-sidebar-item-default";return h.jsxs(I,{className:D?"relative group":"",children:[D&&h.jsxs(h.Fragment,{children:[!A&&h.jsx(I,{className:"admin-sidebar-tree-line-vertical"}),h.jsx(I,{className:"admin-sidebar-tree-line-curve"})]}),P&&w.url?h.jsxs(I,{className:` ${z} - ${D?H:O?j:W} + ${O?U:T?W:G} ${r?"justify-center":""} - `,children:[h.jsxs(oe,{onClick:B=>N(B,w.url),className:"flex items-center justify-start flex-1 min-w-0",children:[w.icon&&h.jsx(mt,{className:`${w.icon} ${r?"":"mr-3"} w-5 h-5 flex items-center justify-center`}),!r&&h.jsx(Q,{className:"truncate",children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(oe,{onClick:()=>C(w.id),className:"p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600","aria-label":sl(L?"common.collapse":"common.expand"),children:h.jsx(mt,{className:`fas fa-chevron-down w-4 h-4 transition-transform ${L?"rotate-180":""}`})})]})]}):P?h.jsxs(oe,{onClick:()=>C(w.id),className:` + `,children:[h.jsxs(ae,{onClick:j=>N(j,w.url),className:"flex items-center justify-start flex-1 min-w-0",children:[w.icon&&h.jsx(pt,{className:`${w.icon} ${r?"":"mr-3"} w-5 h-5 flex items-center justify-center`}),!r&&h.jsx(J,{className:"truncate",children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(pt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(ae,{onClick:()=>S(w.id),className:"p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600","aria-label":hl(L?"common.collapse":"common.expand"),children:h.jsx(pt,{className:`fas fa-chevron-down w-4 h-4 transition-transform ${L?"rotate-180":""}`})})]})]}):P?h.jsxs(ae,{onClick:()=>S(w.id),className:` ${z} - ${O?j:W} + ${T?W:G} ${r?"justify-center":""} - `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(mt,{className:`fas fa-chevron-right w-4 h-4 text-slate-400 transition-transform duration-200 ${L?"rotate-90":""}`})]})]}):w.url?h.jsxs(oe,{onClick:B=>N(B,w.url),className:` + `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(pt,{className:w.icon})}),!r&&h.jsx(J,{children:$})]}),!r&&h.jsxs(I,{className:"admin-sidebar-chevron-trail",children:[w.module_id&&h.jsx(pt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"}),h.jsx(pt,{className:`fas fa-chevron-right w-4 h-4 text-slate-400 transition-transform duration-200 ${L?"rotate-90":""}`})]})]}):w.url?h.jsxs(ae,{onClick:j=>N(j,w.url),className:` ${z} - ${D?H:W} + ${O?U:G} ${r?"justify-center":""} text-left - `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}):h.jsxs(oe,{className:` + `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(pt,{className:w.icon})}),!r&&h.jsx(J,{children:$})]}),!r&&w.module_id&&h.jsx(pt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}):h.jsxs(ae,{className:` ${z} admin-sidebar-item-disabled ${r?"justify-center":""} text-left - `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(mt,{className:w.icon})}),!r&&h.jsx(Q,{children:$})]}),!r&&w.module_id&&h.jsx(mt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}),P&&!r&&L&&h.jsx(I,{className:"relative ml-2",children:w.children.map((B,G)=>S(B,M+1,G===w.children.length-1))})]},w.id)};return h.jsxs(I,{className:o,id:a,...l,children:[(t||s)&&h.jsxs(I,{className:"admin-sidebar-header",children:[t&&!r&&h.jsx(yn,{src:t,alt:e,className:"h-8"}),s&&h.jsx(oe,{onClick:s,className:"admin-sidebar-collapse-btn","aria-label":sl(r?"common.expand_sidebar":"common.collapse_sidebar"),children:h.jsx(mt,{className:`${r?"fas fa-chevron-right":"fas fa-chevron-left"} w-5 h-5 text-slate-600 dark:text-slate-400`})})]}),h.jsx(I,{className:"admin-sidebar-menu-list",children:n.map((w,M)=>S(w,0,M===n.length-1))})]})},Ms=(t,e)=>window.G7Core?.t?.(t,e)??t,Cy=({user:t={name:"",email:""},notifications:e=[],onNotificationClick:n,onProfileClick:r,onLogoutClick:s,className:o=""})=>{const[i,a]=v.useState(!1),[l,c]=v.useState(!1),d=v.useRef(null),u=v.useRef(null),f=e.filter(g=>!g.read).length;return v.useEffect(()=>{const g=m=>{d.current&&!d.current.contains(m.target)&&a(!1),u.current&&!u.current.contains(m.target)&&c(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[]),h.jsxs(I,{className:` + `,children:[h.jsxs(I,{className:"admin-sidebar-item-content",children:[w.icon&&h.jsx(I,{className:"admin-sidebar-icon-box",children:h.jsx(pt,{className:w.icon})}),!r&&h.jsx(J,{children:$})]}),!r&&w.module_id&&h.jsx(pt,{className:"fas fa-cube admin-sidebar-module-icon",title:"모듈 메뉴"})]}),P&&!r&&L&&h.jsx(I,{className:"relative ml-2",children:w.children.map((j,Y)=>C(j,E+1,Y===w.children.length-1))})]},w.id)};return h.jsxs(I,{className:o,id:a,...l,children:[(e||s)&&h.jsxs(I,{className:"admin-sidebar-header",children:[e&&!r&&h.jsx(Sn,{src:e,alt:t,className:"h-8"}),s&&h.jsx(ae,{onClick:s,className:"admin-sidebar-collapse-btn","aria-label":hl(r?"common.expand_sidebar":"common.collapse_sidebar"),children:h.jsx(pt,{className:`${r?"fas fa-chevron-right":"fas fa-chevron-left"} w-5 h-5 text-slate-600 dark:text-slate-400`})})]}),h.jsx(I,{className:"admin-sidebar-menu-list",children:n.map((w,E)=>C(w,0,E===n.length-1))})]})},zs=(e,t)=>window.G7Core?.t?.(e,t)??e,Wy=({user:e={name:"",email:""},notifications:t=[],onNotificationClick:n,onProfileClick:r,onLogoutClick:s,className:o=""})=>{const[i,a]=v.useState(!1),[l,c]=v.useState(!1),d=v.useRef(null),u=v.useRef(null),f=t.filter(g=>!g.read).length;return v.useEffect(()=>{const g=m=>{d.current&&!d.current.contains(m.target)&&a(!1),u.current&&!u.current.contains(m.target)&&c(!1)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[]),h.jsxs(I,{className:` bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4 flex items-center justify-end gap-4 ${o} - `,children:[h.jsxs(I,{ref:d,className:"relative",children:[h.jsxs(oe,{onClick:()=>a(!i),className:"relative p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors","aria-label":Ms("common.notifications"),children:[h.jsx(re,{name:de.Bell,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),f>0&&h.jsx(Q,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full",children:f>9?"9+":f})]}),i&&h.jsxs(I,{className:"absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsx(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx(Q,{className:"font-semibold text-gray-900 dark:text-white",children:Ms("common.notifications")})}),h.jsx(I,{className:"max-h-96 overflow-y-auto",children:e.length===0?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(Q,{children:Ms("common.no_notifications")})}):e.map(g=>h.jsx(oe,{onClick:()=>{g.onClick?.(),n?.(g.id),a(!1)},className:` + `,children:[h.jsxs(I,{ref:d,className:"relative",children:[h.jsxs(ae,{onClick:()=>a(!i),className:"relative p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors","aria-label":zs("common.notifications"),children:[h.jsx(oe,{name:ue.Bell,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),f>0&&h.jsx(J,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full",children:f>9?"9+":f})]}),i&&h.jsxs(I,{className:"absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsx(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx(J,{className:"font-semibold text-gray-900 dark:text-white",children:zs("common.notifications")})}),h.jsx(I,{className:"max-h-96 overflow-y-auto",children:t.length===0?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(J,{children:zs("common.no_notifications")})}):t.map(g=>h.jsx(ae,{onClick:()=>{g.onClick?.(),n?.(g.id),a(!1)},className:` w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 border-b border-gray-100 dark:border-gray-700 transition-colors ${g.read?"":"bg-blue-50 dark:bg-blue-900/20"} - `,children:h.jsxs(I,{className:"flex items-start gap-3",children:[g.iconName&&h.jsx(re,{name:g.iconName,className:"w-5 h-5 text-gray-600 dark:text-gray-400 mt-0.5"}),h.jsxs(I,{className:"flex-1",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:g.title}),h.jsx(I,{className:"text-gray-600 dark:text-gray-400 text-sm mt-1",children:g.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1",children:g.time})]}),!g.read&&h.jsx(Q,{className:"w-2 h-2 bg-blue-500 rounded-full mt-1.5"})]})},g.id))})]})]}),h.jsxs(I,{ref:u,className:"relative",children:[h.jsxs(oe,{onClick:()=>c(!l),className:"flex items-center gap-3 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[t.avatar?h.jsx(yn,{src:t.avatar,alt:t.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(re,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"hidden md:block text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:t.name}),t.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:t.role})]}),h.jsx(re,{name:de.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]}),l&&h.jsxs(I,{className:"absolute right-0 mt-2 w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:t.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:t.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(oe,{onClick:()=>{r?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors text-gray-900 dark:text-white",children:[h.jsx(re,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{children:Ms("common.profile_settings")})]}),h.jsxs(oe,{onClick:()=>{s?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 text-red-600 dark:text-red-400 transition-colors",children:[h.jsx(re,{name:de.ArrowRight,className:"w-5 h-5"}),h.jsx(Q,{children:Ms("common.logout")})]})]})]})]})]})},Sy=({notifications:t=[],unreadCount:e=0,hasMore:n=!1,loading:r=!1,titleText:s="Notifications",emptyText:o="No notifications",markAllReadText:i="Mark all as read",deleteAllText:a="Delete all",unreadOnlyText:l="Unread only",unreadOnly:c=!1,onNotificationClick:d,onClose:u,onLoadMore:f,onMarkAllRead:g,onDeleteAll:m,onDelete:p,onUnreadOnlyToggle:y,dropdownAlign:b="right",className:_="",id:x,editorAttrs:E})=>{const[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),w=v.useRef(null),M=v.useRef(null),T=v.useRef(new Set),P=v.useRef(f),L=v.useRef(n),$=v.useRef(r),A=v.useRef(t.length),D=v.useRef(-1);v.useEffect(()=>{P.current=f,L.current=n,$.current=r,A.current=t.length}),v.useEffect(()=>{if(!M.current||!w.current||!C)return;D.current=-1;const j=new IntersectionObserver(([W])=>{W.isIntersecting&&($.current||!L.current||D.current!==A.current&&(D.current=A.current,P.current?.()))},{root:w.current,threshold:.1});return j.observe(M.current),()=>j.disconnect()},[C]),v.useEffect(()=>{if(!w.current||!C)return;const j=new IntersectionObserver(U=>{U.forEach(B=>{const G=B.target.getAttribute("data-notification-id"),V=B.target.getAttribute("data-unread")==="true";G&&V&&B.isIntersecting&&T.current.add(G)})},{root:w.current,threshold:.5});return w.current.querySelectorAll("[data-notification-id]").forEach(U=>j.observe(U)),()=>j.disconnect()},[C,t]);const O=e>0?e:t.filter(j=>!j.read).length,z=v.useCallback(()=>{if(C){if(O>0){const j=Array.from(T.current).filter(W=>{const U=t.find(B=>String(B.id)===String(W));return U&&!U.read});j.length>0&&u?.(j)}T.current.clear(),k(!1)}},[C,u,O,t]);v.useEffect(()=>{if(!C)return;const j=W=>{N.current&&!N.current.contains(W.target)&&z()};return document.addEventListener("mousedown",j),()=>document.removeEventListener("mousedown",j)},[C,z]);const H=v.useCallback(()=>{C?z():k(!0)},[C,z]);return v.useLayoutEffect(()=>{const j=S.current;if(!C||!j)return;j.style.transform="";const W=j.getBoundingClientRect(),U=typeof window<"u"?window.innerWidth:0;if(W.width===0&&W.height===0)return;const B=8;let G=0;W.right>U-B?G=-(W.right-(U-B)):W.left0&&h.jsx(Q,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 dark:bg-red-600 rounded-full",children:O>99?"99+":O})]}),C&&h.jsxs(I,{ref:S,className:`absolute ${b==="left"?"left-0":"right-0"} mt-2 w-80 max-w-[calc(100vw-1rem)] bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50`,children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between gap-2",children:[h.jsxs(I,{className:"flex items-center gap-3 min-w-0",children:[h.jsx(Q,{className:"font-semibold text-gray-900 dark:text-white",children:s}),y&&h.jsxs(I,{className:"flex items-center gap-1.5 cursor-pointer select-none",onClick:()=>y(!c),children:[h.jsx(kt,{type:"checkbox",checked:c,onChange:j=>{j.stopPropagation(),y(j.target.checked)},onClick:j=>j.stopPropagation(),className:"w-3.5 h-3.5 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-400 focus:ring-blue-500 dark:focus:ring-blue-400 cursor-pointer"}),h.jsx(Q,{className:"text-xs text-gray-600 dark:text-gray-400",children:l})]})]}),h.jsxs(I,{className:"flex items-center gap-3 flex-shrink-0",children:[g&&O>0&&h.jsx(oe,{onClick:()=>{g(),T.current.clear()},className:"text-xs text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:underline cursor-pointer",children:i}),m&&t.length>0&&h.jsx(oe,{onClick:()=>{m()},className:"text-xs text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:underline cursor-pointer",children:a})]})]}),h.jsx(I,{ref:w,className:"max-h-96 overflow-y-auto",children:t.length===0&&!r?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(Q,{children:o})}):h.jsxs(h.Fragment,{children:[t.map(j=>{const W=!j.read;return h.jsxs(I,{"data-notification-id":j.id,"data-unread":W?"true":"false",onClick:()=>{j.onClick?.(),d?.(j),k(!1)},className:`group flex items-start gap-3 px-4 py-3 text-left border-b border-gray-100 dark:border-gray-700 transition-colors cursor-pointer ${W?"bg-blue-50/70 dark:bg-blue-900/20 hover:bg-blue-50 dark:hover:bg-blue-900/30":"bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700/50 opacity-70"}`,children:[j.iconName&&h.jsx(re,{name:j.iconName,className:`w-5 h-5 mt-0.5 flex-shrink-0 ${W?"text-blue-600 dark:text-blue-400":"text-gray-400 dark:text-gray-500"}`}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`text-sm truncate ${W?"font-semibold text-gray-900 dark:text-white":"font-normal text-gray-600 dark:text-gray-400"}`,children:j.title}),h.jsx(I,{className:`text-sm mt-1 line-clamp-2 ${W?"text-gray-700 dark:text-gray-300":"text-gray-500 dark:text-gray-500"}`,children:j.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1 font-mono",children:j.time})]}),h.jsxs(I,{className:"flex flex-col items-center gap-2 flex-shrink-0",children:[W&&h.jsx(Q,{className:"w-2 h-2 bg-blue-500 dark:bg-blue-400 rounded-full mt-1.5"}),p&&h.jsx(oe,{onClick:U=>{U.stopPropagation(),p(j)},className:"p-1 text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400 rounded hover:bg-red-50 dark:hover:bg-red-900/20 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity","aria-label":"Delete notification",children:h.jsx(re,{name:de.Trash,className:"w-4 h-4"})})]})]},j.id)}),n&&h.jsx(I,{ref:M,className:"px-4 py-3 text-center",children:r?h.jsx(Q,{className:"text-sm text-gray-400 dark:text-gray-500",children:"..."}):h.jsx(Q,{className:"text-sm text-gray-400 dark:text-gray-500"})}),r&&!n&&t.length>0&&h.jsx(I,{className:"px-4 py-2 text-center",children:h.jsx(Q,{className:"text-sm text-gray-400 dark:text-gray-500",children:"..."})})]})})]})]})},au=window.G7Core?.createLogger?.("Comp:UserProfile")??{log:(...t)=>console.log("[Comp:UserProfile]",...t),warn:(...t)=>console.warn("[Comp:UserProfile]",...t),error:(...t)=>console.error("[Comp:UserProfile]",...t)},_y=({user:t={name:"",email:""},profileText:e="Profile Settings",logoutText:n="Logout",onProfileClick:r,onLogoutClick:s,className:o="",logoutEndpoint:i="/api/admin/auth/logout",redirectPath:a="/admin/login",showChevron:l=!0,dropdownDirection:c="up",dropdownOnly:d=!1,id:u,editorAttrs:f})=>{const[g,m]=v.useState(!1),[p,y]=v.useState(!1),b=v.useRef(null);v.useEffect(()=>{const C=k=>{b.current&&!b.current.contains(k.target)&&m(!1)};return document.addEventListener("mousedown",C),()=>document.removeEventListener("mousedown",C)},[]);const _=()=>{const k=`; ${document.cookie}`.split("; XSRF-TOKEN=");return k.length===2?decodeURIComponent(k.pop()?.split(";").shift()||""):null},x=()=>localStorage.getItem("auth_token"),E=async()=>{if(!p)try{if(y(!0),m(!1),s){s();return}const C=_(),k=x(),N=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",...C&&{"X-XSRF-TOKEN":C},...k&&{Authorization:`Bearer ${k}`}},credentials:"include"});N.ok?(localStorage.removeItem("auth_token"),window.location.href=a):(au.error("로그아웃 실패:",N.statusText),localStorage.removeItem("auth_token"),window.location.href=a)}catch(C){au.error("로그아웃 오류:",C),localStorage.removeItem("auth_token"),window.location.href=a}finally{y(!1)}};return h.jsxs(I,{ref:b,className:d?`absolute inset-0 ${o}`:`relative ${o}`,id:u,...f,children:[h.jsx(oe,{onClick:()=>m(!g),className:d?"w-full h-full opacity-0 cursor-pointer":"flex items-center gap-2 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors w-full",children:!d&&h.jsxs(h.Fragment,{children:[t.avatar?h.jsx(yn,{src:t.avatar,alt:t.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(re,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"flex-1 text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:t.name}),t.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:t.role})]}),l&&h.jsx(re,{name:de.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]})}),g&&h.jsxs(I,{className:`absolute ${c==="down"?"top-full mt-2":"bottom-full mb-2"} ${d?"right-0 w-72":"left-0 w-full"} bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50`,children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors",onClick:()=>{m(!1),window.G7Core?.dispatch({handler:"navigate",params:{path:`/admin/users/${t.uuid}`}})},children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:t.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:t.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(oe,{onClick:()=>{m(!1),r?r():window.G7Core?.dispatch({handler:"navigate",params:{path:`/admin/users/${t.uuid}/edit`}})},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-start gap-3 transition-colors",children:[h.jsx(re,{name:de.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"text-gray-900 dark:text-white",children:e})]}),h.jsxs(oe,{onClick:E,disabled:p,className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-start gap-3 text-red-600 dark:text-red-400 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[h.jsx(re,{name:de.ArrowRight,className:"w-5 h-5"}),h.jsx(Q,{children:p?"Logging out...":n})]})]})]})]})},jo=window.G7Core?.createLogger?.("Comp:LanguageSelector")??{log:(...t)=>console.log("[Comp:LanguageSelector]",...t),warn:(...t)=>console.warn("[Comp:LanguageSelector]",...t),error:(...t)=>console.error("[Comp:LanguageSelector]",...t)},lu=t=>{const e=window.G7Core?.state?.get?.("_global.appConfig.localeNames")?.[t];return e||{ko:"한국어",en:"English",ja:"日本語",zh:"中文",es:"Español",fr:"Français",de:"Deutsch"}[t]||t},Ey=({availableLocales:t=["ko","en"],languageText:e="Language",apiEndpoint:n="/api/admin/users/me/language",onLanguageChange:r,className:s="",inline:o=!1,showCode:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(!1),[u,f]=v.useState(!1),[g,m]=v.useState(()=>localStorage.getItem("g7_locale")||"ko"),p=v.useRef(null);v.useEffect(()=>{if(o)return;const x=E=>{p.current&&!p.current.contains(E.target)&&d(!1)};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[o]);const y=()=>{const E=`; ${document.cookie}`.split("; XSRF-TOKEN=");return E.length===2?decodeURIComponent(E.pop()?.split(";").shift()||""):null},b=()=>localStorage.getItem("auth_token"),_=async x=>{if(!(u||x===g))try{f(!0);const E=y(),C=b();try{const k=await fetch(n,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json",...E&&{"X-XSRF-TOKEN":E},...C&&{Authorization:`Bearer ${C}`}},credentials:"include",body:JSON.stringify({language:x})});k.ok||jo.warn("언어 설정 저장 실패 (UI는 변경됨):",k.statusText)}catch(k){jo.warn("언어 설정 API 호출 실패 (UI는 변경됨):",k)}m(x),d(!1),r?.(x),window.__templateApp?await window.__templateApp.changeLocale(x):(jo.warn("TemplateApp not found, reloading page"),window.location.reload())}catch(E){jo.error("언어 변경 오류:",E)}finally{f(!1)}};return o?h.jsxs(I,{className:`${s}`,id:a,...l,children:[h.jsxs(oe,{onClick:()=>d(!c),disabled:u,className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors",children:[h.jsx(re,{name:de.Globe,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"text-gray-900 dark:text-white flex-1",children:e}),h.jsx(Q,{className:"px-2 py-0.5 text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded",children:g.toUpperCase()}),h.jsx(re,{name:de.ChevronDown,className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${c?"rotate-180":""}`})]}),c&&h.jsx(I,{className:"px-4 py-2 flex flex-wrap gap-2",children:t.map(x=>h.jsx(oe,{onClick:()=>_(x),disabled:u,className:` + `,children:h.jsxs(I,{className:"flex items-start gap-3",children:[g.iconName&&h.jsx(oe,{name:g.iconName,className:"w-5 h-5 text-gray-600 dark:text-gray-400 mt-0.5"}),h.jsxs(I,{className:"flex-1",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:g.title}),h.jsx(I,{className:"text-gray-600 dark:text-gray-400 text-sm mt-1",children:g.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1",children:g.time})]}),!g.read&&h.jsx(J,{className:"w-2 h-2 bg-blue-500 rounded-full mt-1.5"})]})},g.id))})]})]}),h.jsxs(I,{ref:u,className:"relative",children:[h.jsxs(ae,{onClick:()=>c(!l),className:"flex items-center gap-3 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[e.avatar?h.jsx(Sn,{src:e.avatar,alt:e.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(oe,{name:ue.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"hidden md:block text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:e.name}),e.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:e.role})]}),h.jsx(oe,{name:ue.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]}),l&&h.jsxs(I,{className:"absolute right-0 mt-2 w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:e.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:e.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(ae,{onClick:()=>{r?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors text-gray-900 dark:text-white",children:[h.jsx(oe,{name:ue.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{children:zs("common.profile_settings")})]}),h.jsxs(ae,{onClick:()=>{s?.(),c(!1)},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 text-red-600 dark:text-red-400 transition-colors",children:[h.jsx(oe,{name:ue.ArrowRight,className:"w-5 h-5"}),h.jsx(J,{children:zs("common.logout")})]})]})]})]})]})},Gy=({notifications:e=[],unreadCount:t=0,hasMore:n=!1,loading:r=!1,titleText:s="Notifications",emptyText:o="No notifications",markAllReadText:i="Mark all as read",deleteAllText:a="Delete all",unreadOnlyText:l="Unread only",unreadOnly:c=!1,onNotificationClick:d,onClose:u,onLoadMore:f,onMarkAllRead:g,onDeleteAll:m,onDelete:p,onUnreadOnlyToggle:y,dropdownAlign:b="right",className:_="",id:x,editorAttrs:M})=>{const[S,k]=v.useState(!1),N=v.useRef(null),C=v.useRef(null),w=v.useRef(null),E=v.useRef(null),A=v.useRef(new Set),P=v.useRef(f),L=v.useRef(n),$=v.useRef(r),D=v.useRef(e.length),O=v.useRef(-1);v.useEffect(()=>{P.current=f,L.current=n,$.current=r,D.current=e.length}),v.useEffect(()=>{if(!E.current||!w.current||!S)return;O.current=-1;const W=new IntersectionObserver(([G])=>{G.isIntersecting&&($.current||!L.current||O.current!==D.current&&(O.current=D.current,P.current?.()))},{root:w.current,threshold:.1});return W.observe(E.current),()=>W.disconnect()},[S]),v.useEffect(()=>{if(!w.current||!S)return;const W=new IntersectionObserver(B=>{B.forEach(j=>{const Y=j.target.getAttribute("data-notification-id"),V=j.target.getAttribute("data-unread")==="true";Y&&V&&j.isIntersecting&&A.current.add(Y)})},{root:w.current,threshold:.5});return w.current.querySelectorAll("[data-notification-id]").forEach(B=>W.observe(B)),()=>W.disconnect()},[S,e]);const T=t>0?t:e.filter(W=>!W.read).length,z=v.useCallback(()=>{if(S){if(T>0){const W=Array.from(A.current).filter(G=>{const B=e.find(j=>String(j.id)===String(G));return B&&!B.read});W.length>0&&u?.(W)}A.current.clear(),k(!1)}},[S,u,T,e]);v.useEffect(()=>{if(!S)return;const W=G=>{N.current&&!N.current.contains(G.target)&&z()};return document.addEventListener("mousedown",W),()=>document.removeEventListener("mousedown",W)},[S,z]);const U=v.useCallback(()=>{S?z():k(!0)},[S,z]);return v.useLayoutEffect(()=>{const W=C.current;if(!S||!W)return;W.style.transform="";const G=W.getBoundingClientRect(),B=typeof window<"u"?window.innerWidth:0;if(G.width===0&&G.height===0)return;const j=8;let Y=0;G.right>B-j?Y=-(G.right-(B-j)):G.left0&&h.jsx(J,{className:"absolute top-0 right-0 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 dark:bg-red-600 rounded-full",children:T>99?"99+":T})]}),S&&h.jsxs(I,{ref:C,className:`absolute ${b==="left"?"left-0":"right-0"} mt-2 w-80 max-w-[calc(100vw-1rem)] bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50`,children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between gap-2",children:[h.jsxs(I,{className:"flex items-center gap-3 min-w-0",children:[h.jsx(J,{className:"font-semibold text-gray-900 dark:text-white",children:s}),y&&h.jsxs(I,{className:"flex items-center gap-1.5 cursor-pointer select-none",onClick:()=>y(!c),children:[h.jsx(kt,{type:"checkbox",checked:c,onChange:W=>{W.stopPropagation(),y(W.target.checked)},onClick:W=>W.stopPropagation(),className:"w-3.5 h-3.5 rounded border-gray-300 dark:border-gray-600 text-blue-600 dark:text-blue-400 focus:ring-blue-500 dark:focus:ring-blue-400 cursor-pointer"}),h.jsx(J,{className:"text-xs text-gray-600 dark:text-gray-400",children:l})]})]}),h.jsxs(I,{className:"flex items-center gap-3 flex-shrink-0",children:[g&&T>0&&h.jsx(ae,{onClick:()=>{g(),A.current.clear()},className:"text-xs text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:underline cursor-pointer",children:i}),m&&e.length>0&&h.jsx(ae,{onClick:()=>{m()},className:"text-xs text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:underline cursor-pointer",children:a})]})]}),h.jsx(I,{ref:w,className:"max-h-96 overflow-y-auto",children:e.length===0&&!r?h.jsx(I,{className:"px-4 py-8 text-center text-gray-500 dark:text-gray-400",children:h.jsx(J,{children:o})}):h.jsxs(h.Fragment,{children:[e.map(W=>{const G=!W.read;return h.jsxs(I,{"data-notification-id":W.id,"data-unread":G?"true":"false",onClick:()=>{W.onClick?.(),d?.(W),k(!1)},className:`group flex items-start gap-3 px-4 py-3 text-left border-b border-gray-100 dark:border-gray-700 transition-colors cursor-pointer ${G?"bg-blue-50/70 dark:bg-blue-900/20 hover:bg-blue-50 dark:hover:bg-blue-900/30":"bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700/50 opacity-70"}`,children:[W.iconName&&h.jsx(oe,{name:W.iconName,className:`w-5 h-5 mt-0.5 flex-shrink-0 ${G?"text-blue-600 dark:text-blue-400":"text-gray-400 dark:text-gray-500"}`}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`text-sm truncate ${G?"font-semibold text-gray-900 dark:text-white":"font-normal text-gray-600 dark:text-gray-400"}`,children:W.title}),h.jsx(I,{className:`text-sm mt-1 line-clamp-2 ${G?"text-gray-700 dark:text-gray-300":"text-gray-500 dark:text-gray-500"}`,children:W.message}),h.jsx(I,{className:"text-gray-400 dark:text-gray-500 text-xs mt-1 font-mono",children:W.time})]}),h.jsxs(I,{className:"flex flex-col items-center gap-2 flex-shrink-0",children:[G&&h.jsx(J,{className:"w-2 h-2 bg-blue-500 dark:bg-blue-400 rounded-full mt-1.5"}),p&&h.jsx(ae,{onClick:B=>{B.stopPropagation(),p(W)},className:"p-1 text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400 rounded hover:bg-red-50 dark:hover:bg-red-900/20 cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity","aria-label":"Delete notification",children:h.jsx(oe,{name:ue.Trash,className:"w-4 h-4"})})]})]},W.id)}),n&&h.jsx(I,{ref:E,className:"px-4 py-3 text-center",children:r?h.jsx(J,{className:"text-sm text-gray-400 dark:text-gray-500",children:"..."}):h.jsx(J,{className:"text-sm text-gray-400 dark:text-gray-500"})}),r&&!n&&e.length>0&&h.jsx(I,{className:"px-4 py-2 text-center",children:h.jsx(J,{className:"text-sm text-gray-400 dark:text-gray-500",children:"..."})})]})})]})]})},vu=window.G7Core?.createLogger?.("Comp:UserProfile")??{log:(...e)=>console.log("[Comp:UserProfile]",...e),warn:(...e)=>console.warn("[Comp:UserProfile]",...e),error:(...e)=>console.error("[Comp:UserProfile]",...e)},Yy=({user:e={name:"",email:""},profileText:t="Profile Settings",logoutText:n="Logout",onProfileClick:r,onLogoutClick:s,className:o="",logoutEndpoint:i="/api/admin/auth/logout",redirectPath:a="/admin/login",showChevron:l=!0,dropdownDirection:c="up",dropdownOnly:d=!1,id:u,editorAttrs:f})=>{const[g,m]=v.useState(!1),[p,y]=v.useState(!1),b=v.useRef(null);v.useEffect(()=>{const S=k=>{b.current&&!b.current.contains(k.target)&&m(!1)};return document.addEventListener("mousedown",S),()=>document.removeEventListener("mousedown",S)},[]);const _=()=>{const k=`; ${document.cookie}`.split("; XSRF-TOKEN=");return k.length===2?decodeURIComponent(k.pop()?.split(";").shift()||""):null},x=()=>localStorage.getItem("auth_token"),M=async()=>{if(!p)try{if(y(!0),m(!1),s){s();return}const S=_(),k=x(),N=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",...S&&{"X-XSRF-TOKEN":S},...k&&{Authorization:`Bearer ${k}`}},credentials:"include"});N.ok?(localStorage.removeItem("auth_token"),window.location.href=a):(vu.error("로그아웃 실패:",N.statusText),localStorage.removeItem("auth_token"),window.location.href=a)}catch(S){vu.error("로그아웃 오류:",S),localStorage.removeItem("auth_token"),window.location.href=a}finally{y(!1)}};return h.jsxs(I,{ref:b,className:d?`absolute inset-0 ${o}`:`relative ${o}`,id:u,...f,children:[h.jsx(ae,{onClick:()=>m(!g),className:d?"w-full h-full opacity-0 cursor-pointer":"flex items-center gap-2 px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors w-full",children:!d&&h.jsxs(h.Fragment,{children:[e.avatar?h.jsx(Sn,{src:e.avatar,alt:e.name,className:"w-8 h-8 rounded-full object-cover"}):h.jsx(I,{className:"w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center",children:h.jsx(oe,{name:ue.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:"flex-1 text-left",children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white text-sm",children:e.name}),e.role&&h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-xs",children:e.role})]}),l&&h.jsx(oe,{name:ue.ChevronDown,className:"w-4 h-4 text-gray-600 dark:text-gray-400"})]})}),g&&h.jsxs(I,{className:`absolute ${c==="down"?"top-full mt-2":"bottom-full mb-2"} ${d?"right-0 w-72":"left-0 w-full"} bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50`,children:[h.jsxs(I,{className:"px-4 py-3 border-b border-gray-200 dark:border-gray-700 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors",onClick:()=>{m(!1),window.G7Core?.dispatch({handler:"navigate",params:{path:`/admin/users/${e.uuid}`}})},children:[h.jsx(I,{className:"font-semibold text-gray-900 dark:text-white",children:e.name}),h.jsx(I,{className:"text-gray-500 dark:text-gray-400 text-sm",children:e.email})]}),h.jsxs(I,{className:"py-2",children:[h.jsxs(ae,{onClick:()=>{m(!1),r?r():window.G7Core?.dispatch({handler:"navigate",params:{path:`/admin/users/${e.uuid}/edit`}})},className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-start gap-3 transition-colors",children:[h.jsx(oe,{name:ue.User,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"text-gray-900 dark:text-white",children:t})]}),h.jsxs(ae,{onClick:M,disabled:p,className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center justify-start gap-3 text-red-600 dark:text-red-400 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[h.jsx(oe,{name:ue.ArrowRight,className:"w-5 h-5"}),h.jsx(J,{children:p?"Logging out...":n})]})]})]})]})},Qo=window.G7Core?.createLogger?.("Comp:LanguageSelector")??{log:(...e)=>console.log("[Comp:LanguageSelector]",...e),warn:(...e)=>console.warn("[Comp:LanguageSelector]",...e),error:(...e)=>console.error("[Comp:LanguageSelector]",...e)},xu=e=>{const t=window.G7Core?.state?.get?.("_global.appConfig.localeNames")?.[e];return t||{ko:"한국어",en:"English",ja:"日本語",zh:"中文",es:"Español",fr:"Français",de:"Deutsch"}[e]||e},Xy=({availableLocales:e=["ko","en"],languageText:t="Language",apiEndpoint:n="/api/admin/users/me/language",onLanguageChange:r,className:s="",inline:o=!1,showCode:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(!1),[u,f]=v.useState(!1),[g,m]=v.useState(()=>localStorage.getItem("g7_locale")||"ko"),p=v.useRef(null);v.useEffect(()=>{if(o)return;const x=M=>{p.current&&!p.current.contains(M.target)&&d(!1)};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[o]);const y=()=>{const M=`; ${document.cookie}`.split("; XSRF-TOKEN=");return M.length===2?decodeURIComponent(M.pop()?.split(";").shift()||""):null},b=()=>localStorage.getItem("auth_token"),_=async x=>{if(!(u||x===g))try{f(!0);const M=y(),S=b();try{const k=await fetch(n,{method:"PATCH",headers:{"Content-Type":"application/json",Accept:"application/json",...M&&{"X-XSRF-TOKEN":M},...S&&{Authorization:`Bearer ${S}`}},credentials:"include",body:JSON.stringify({language:x})});k.ok||Qo.warn("언어 설정 저장 실패 (UI는 변경됨):",k.statusText)}catch(k){Qo.warn("언어 설정 API 호출 실패 (UI는 변경됨):",k)}m(x),d(!1),r?.(x),window.__templateApp?await window.__templateApp.changeLocale(x):(Qo.warn("TemplateApp not found, reloading page"),window.location.reload())}catch(M){Qo.error("언어 변경 오류:",M)}finally{f(!1)}};return o?h.jsxs(I,{className:`${s}`,id:a,...l,children:[h.jsxs(ae,{onClick:()=>d(!c),disabled:u,className:"w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors",children:[h.jsx(oe,{name:ue.Globe,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"text-gray-900 dark:text-white flex-1",children:t}),h.jsx(J,{className:"px-2 py-0.5 text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded",children:g.toUpperCase()}),h.jsx(oe,{name:ue.ChevronDown,className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${c?"rotate-180":""}`})]}),c&&h.jsx(I,{className:"px-4 py-2 flex flex-wrap gap-2",children:e.map(x=>h.jsx(ae,{onClick:()=>_(x),disabled:u,className:` px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${g===x?"bg-blue-600 text-white":"bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"} ${u?"opacity-50 cursor-not-allowed":""} - `,children:lu(x)},x))})]}):h.jsxs(I,{ref:p,className:`relative ${s}`,id:a,...l,children:[h.jsxs(oe,{onClick:()=>d(!c),className:i?"flex items-center gap-1.5 px-2.5 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer":"p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors","aria-haspopup":"listbox","aria-expanded":c,"aria-label":e,children:[h.jsx(re,{name:de.Globe,className:i?"w-4 h-4":"w-5 h-5 text-gray-600 dark:text-gray-400"}),i&&h.jsx(Q,{className:"font-medium uppercase",children:g}),i&&h.jsx(re,{name:de.ChevronDown,className:"w-3 h-3"})]}),c&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsx(I,{className:"py-2",children:t.map(x=>h.jsxs(oe,{onClick:()=>_(x),disabled:u,className:` + `,children:xu(x)},x))})]}):h.jsxs(I,{ref:p,className:`relative ${s}`,id:a,...l,children:[h.jsxs(ae,{onClick:()=>d(!c),className:i?"flex items-center gap-1.5 px-2.5 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer":"p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors","aria-haspopup":"listbox","aria-expanded":c,"aria-label":t,children:[h.jsx(oe,{name:ue.Globe,className:i?"w-4 h-4":"w-5 h-5 text-gray-600 dark:text-gray-400"}),i&&h.jsx(J,{className:"font-medium uppercase",children:g}),i&&h.jsx(oe,{name:ue.ChevronDown,className:"w-3 h-3"})]}),c&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsx(I,{className:"py-2",children:e.map(x=>h.jsxs(ae,{onClick:()=>_(x),disabled:u,className:` w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors ${g===x?"bg-gray-50 dark:bg-gray-700":""} ${u?"opacity-50 cursor-not-allowed":""} - `,children:[h.jsx(Q,{className:"text-gray-900 dark:text-white flex-1",children:lu(x)}),g===x&&h.jsx(re,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400"})]},x))})})]})},My=({copyright:t="© 2026 G7",version:e,quickLinks:n=[],className:r="",changelogModalId:s,copyrightModalId:o,id:i,editorAttrs:a})=>{const l=v.useCallback(()=>{s&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/changelog",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreChangelogContent:"{{response.data.content}}"}},{handler:"openModal",target:s}],onError:[{handler:"openModal",target:s}]}]})},[s]),c=v.useCallback(()=>{o&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/license",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreLicenseContent:"{{response.data.content}}"}},{handler:"openModal",target:o}],onError:[{handler:"openModal",target:o}]}]})},[o]);return h.jsx(I,{className:`admin-footer ${r}`,id:i,...a,children:h.jsxs(I,{className:"admin-footer-row",children:[h.jsxs(I,{className:"admin-footer-group",children:[h.jsx(Q,{className:o?"admin-footer-link-hover":"",onClick:o?c:void 0,children:t}),e&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{className:"hidden md:inline",children:"•"}),h.jsxs(Q,{className:`flex-center gap-2 ${s?"admin-footer-link-hover":""}`,onClick:s?l:void 0,children:[h.jsx(re,{name:de.Tag,className:"w-4 h-4"}),h.jsxs(Q,{children:["v",e]})]})]})]}),n.length>0&&h.jsx(I,{className:"flex-center gap-4",children:n.map(d=>h.jsxs(Lr,{href:d.url,className:"admin-footer-link",children:[d.iconName&&h.jsx(re,{name:d.iconName,className:"w-4 h-4"}),h.jsx(Q,{children:d.label})]},d.id))})]})})},Ny=({title:t,description:e,breadcrumbItems:n,tabs:r,onTabChange:s,actions:o,className:i="",children:a,id:l,editorAttrs:c})=>{const d=(u="secondary")=>({primary:"page-action-primary",secondary:"page-action-secondary",danger:"page-action-danger",ghost:"page-action-ghost"})[u];return h.jsxs(I,{className:i,id:l,...c,children:[n&&n.length>0&&h.jsx(I,{className:"mb-md",children:h.jsx(ou,{items:n})}),h.jsxs(I,{className:"flex-between mb-lg",children:[h.jsxs(I,{children:[h.jsx(Da,{className:"page-title",children:t}),e&&h.jsx(yt,{className:"page-description",children:e})]}),a?h.jsx(I,{className:"flex-center page-actions",children:a}):o&&o.length>0?h.jsx(I,{className:"page-actions",children:o.map(u=>h.jsxs(oe,{onClick:u.onClick,disabled:u.disabled,className:d(u.variant),children:[u.iconName&&h.jsx(re,{name:u.iconName,className:"icon-md"}),u.variant!=="ghost"&&u.label&&h.jsx(I,{children:u.label})]},u.id))}):null]}),r&&r.length>0&&h.jsx(I,{className:"tab-container",children:r.map(u=>h.jsxs(oe,{onClick:()=>s?.(u.value),className:u.active?"tab-btn-active":"tab-btn",children:[h.jsx(I,{children:u.label}),u.badge!==void 0&&h.jsx(I,{className:u.active?"tab-badge-active":"tab-badge",children:u.badge})]},u.id))})]})},Oy=()=>window.G7Core,Ty=({title:t,description:e,className:n="",style:r,children:s,id:o,editorAttrs:i})=>{const a="flex items-center justify-between",l=Oy()?.style?.mergeClasses?.(a,n)??`${a} ${n}`;return h.jsxs(I,{className:l,style:r,id:o,...i,children:[h.jsxs(I,{children:[t&&h.jsx(Jn,{className:"text-base font-semibold text-gray-900 dark:text-white",children:t}),e&&h.jsx(yt,{className:"mt-1 text-sm text-gray-500 dark:text-gray-400",children:e})]}),s&&h.jsx(I,{className:"flex items-center gap-2",children:s})]})},er=(t,e)=>window.G7Core?.t?.(t,e)??t,Ay=({image:t,imageAlt:e,vendor:n,name:r,version:s,status:o,updateAvailable:i=!1,latestVersion:a,dependencies:l=[],showLayoutEditButton:c=!1,actions:d=[],onLayoutEditClick:u,className:f="",id:g,editorAttrs:m})=>{const p=b=>({active:"success",inactive:"default",pending:"pending",error:"error"})[b],y=b=>({active:er("common.status_active"),inactive:er("common.status_inactive"),pending:er("common.status_pending"),error:er("common.status_error")})[b];return h.jsxs(I,{className:` + `,children:[h.jsx(J,{className:"text-gray-900 dark:text-white flex-1",children:xu(x)}),g===x&&h.jsx(oe,{name:ue.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400"})]},x))})})]})},Ky=({copyright:e="© 2026 G7",version:t,quickLinks:n=[],className:r="",changelogModalId:s,copyrightModalId:o,id:i,editorAttrs:a})=>{const l=v.useCallback(()=>{s&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/changelog",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreChangelogContent:"{{response.data.content}}"}},{handler:"openModal",target:s}],onError:[{handler:"openModal",target:s}]}]})},[s]),c=v.useCallback(()=>{o&&window.G7Core?.dispatch?.({handler:"sequence",actions:[{handler:"apiCall",auth_required:!0,target:"/api/admin/license",params:{method:"GET"},onSuccess:[{handler:"setState",params:{target:"global",coreLicenseContent:"{{response.data.content}}"}},{handler:"openModal",target:o}],onError:[{handler:"openModal",target:o}]}]})},[o]);return h.jsx(I,{className:`admin-footer ${r}`,id:i,...a,children:h.jsxs(I,{className:"admin-footer-row",children:[h.jsxs(I,{className:"admin-footer-group",children:[h.jsx(J,{className:o?"admin-footer-link-hover":"",onClick:o?c:void 0,children:e}),t&&h.jsxs(h.Fragment,{children:[h.jsx(J,{className:"hidden md:inline",children:"•"}),h.jsxs(J,{className:`flex-center gap-2 ${s?"admin-footer-link-hover":""}`,onClick:s?l:void 0,children:[h.jsx(oe,{name:ue.Tag,className:"w-4 h-4"}),h.jsxs(J,{children:["v",t]})]})]})]}),n.length>0&&h.jsx(I,{className:"flex-center gap-4",children:n.map(d=>h.jsxs(Wr,{href:d.url,className:"admin-footer-link",children:[d.iconName&&h.jsx(oe,{name:d.iconName,className:"w-4 h-4"}),h.jsx(J,{children:d.label})]},d.id))})]})})},qy=({title:e,description:t,breadcrumbItems:n,tabs:r,onTabChange:s,actions:o,className:i="",children:a,id:l,editorAttrs:c})=>{const d=(u="secondary")=>({primary:"page-action-primary",secondary:"page-action-secondary",danger:"page-action-danger",ghost:"page-action-ghost"})[u];return h.jsxs(I,{className:i,id:l,...c,children:[n&&n.length>0&&h.jsx(I,{className:"mb-md",children:h.jsx(bu,{items:n})}),h.jsxs(I,{className:"flex-between mb-lg",children:[h.jsxs(I,{children:[h.jsx(Ba,{className:"page-title",children:e}),t&&h.jsx(yt,{className:"page-description",children:t})]}),a?h.jsx(I,{className:"flex-center page-actions",children:a}):o&&o.length>0?h.jsx(I,{className:"page-actions",children:o.map(u=>h.jsxs(ae,{onClick:u.onClick,disabled:u.disabled,className:d(u.variant),children:[u.iconName&&h.jsx(oe,{name:u.iconName,className:"icon-md"}),u.variant!=="ghost"&&u.label&&h.jsx(I,{children:u.label})]},u.id))}):null]}),r&&r.length>0&&h.jsx(I,{className:"tab-container",children:r.map(u=>h.jsxs(ae,{onClick:()=>s?.(u.value),className:u.active?"tab-btn-active":"tab-btn",children:[h.jsx(I,{children:u.label}),u.badge!==void 0&&h.jsx(I,{className:u.active?"tab-badge-active":"tab-badge",children:u.badge})]},u.id))})]})},Zy=()=>window.G7Core,Qy=({title:e,description:t,className:n="",style:r,children:s,id:o,editorAttrs:i})=>{const a="flex items-center justify-between",l=Zy()?.style?.mergeClasses?.(a,n)??`${a} ${n}`;return h.jsxs(I,{className:l,style:r,id:o,...i,children:[h.jsxs(I,{children:[e&&h.jsx(or,{className:"text-base font-semibold text-gray-900 dark:text-white",children:e}),t&&h.jsx(yt,{className:"mt-1 text-sm text-gray-500 dark:text-gray-400",children:t})]}),s&&h.jsx(I,{className:"flex items-center gap-2",children:s})]})},ar=(e,t)=>window.G7Core?.t?.(e,t)??e,Jy=({image:e,imageAlt:t,vendor:n,name:r,version:s,status:o,updateAvailable:i=!1,latestVersion:a,dependencies:l=[],showLayoutEditButton:c=!1,actions:d=[],onLayoutEditClick:u,className:f="",id:g,editorAttrs:m})=>{const p=b=>({active:"success",inactive:"default",pending:"pending",error:"error"})[b],y=b=>({active:ar("common.status_active"),inactive:ar("common.status_inactive"),pending:ar("common.status_pending"),error:ar("common.status_error")})[b];return h.jsxs(I,{className:` bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden hover:shadow-lg transition-shadow ${f} - `,id:g,...m,children:[t&&h.jsxs(I,{className:"relative",children:[h.jsx(yn,{src:t,alt:e||er("common.template_preview",{vendor:n,name:r}),className:"w-full h-48 object-cover"}),h.jsx(I,{className:"absolute top-3 right-3",children:h.jsx(Fo,{status:p(o),label:y(o),showIcon:!0})}),i&&h.jsx(I,{className:"absolute top-3 left-3",children:h.jsxs(Q,{className:"flex items-center gap-1 px-2 py-1 bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border border-yellow-200 dark:border-yellow-800 rounded-md text-xs font-semibold",children:[h.jsx(re,{name:de.ExclamationTriangle,className:"w-3 h-3"}),h.jsx(Q,{children:er("common.update_available")})]})})]}),h.jsxs(I,{className:"p-4",children:[h.jsxs(I,{className:"flex items-start justify-between mb-3",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Ir,{className:"text-lg font-bold text-gray-900 dark:text-white",children:[n,"/",r]}),h.jsxs(I,{className:"flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-400",children:[h.jsxs(Q,{children:["v",s]}),i&&a&&h.jsxs(h.Fragment,{children:[h.jsx(Q,{children:"→"}),h.jsxs(Q,{className:"text-yellow-600 dark:text-yellow-400 font-semibold",children:["v",a]})]})]}),!t&&h.jsx(I,{className:"mt-2",children:h.jsx(Fo,{status:p(o),label:y(o),showIcon:!0})})]}),d.length>0&&h.jsx(xs,{items:d,triggerIconName:de.EllipsisVertical,position:"left"})]}),l.length>0&&h.jsxs(I,{className:"mb-3",children:[h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-400 mb-1",children:[er("common.dependencies"),":"]}),h.jsx(I,{className:"flex flex-wrap gap-1",children:l.map((b,_)=>h.jsx(Q,{className:"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs rounded",children:b},_))})]}),c&&u&&h.jsxs(oe,{onClick:u,className:"w-full px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors flex items-center justify-center gap-2 font-medium",children:[h.jsx(re,{name:de.Edit,className:"w-4 h-4"}),h.jsx(Q,{children:er("common.edit_layout")})]})]})]})},cu=({type:t,message:e,dismissible:n=!1,onDismiss:r,className:s="",id:o,editorAttrs:i})=>{const a={info:{containerClass:"alert-info",iconClass:"alert-icon-info",textClass:"alert-text-info",icon:de.InfoCircle},success:{containerClass:"alert-success",iconClass:"alert-icon-success",textClass:"alert-text-success",icon:de.CheckCircle},warning:{containerClass:"alert-warning",iconClass:"alert-icon-warning",textClass:"alert-text-warning",icon:de.ExclamationTriangle},error:{containerClass:"alert-error",iconClass:"alert-icon-error",textClass:"alert-text-error",icon:de.TimesCircle}},l=a[t]??a.info;return h.jsxs(I,{className:`${l.containerClass} ${s}`,role:"alert","aria-live":"polite",id:o,...i,children:[h.jsx(re,{name:l.icon,className:l.iconClass,ariaLabel:`${t} icon`}),h.jsx(yt,{className:l.textClass,children:e}),n&&h.jsx(oe,{onClick:r,className:`alert-dismiss ${l.textClass}`,"aria-label":"Close alert",children:h.jsx(re,{name:de.Times,className:l.iconClass,ariaLabel:"Close"})})]})},Dy=({layoutName:t,onBack:e,onPreview:n,onSave:r,isSaving:s=!1,className:o=""})=>{const i=` + `,id:g,...m,children:[e&&h.jsxs(I,{className:"relative",children:[h.jsx(Sn,{src:e,alt:t||ar("common.template_preview",{vendor:n,name:r}),className:"w-full h-48 object-cover"}),h.jsx(I,{className:"absolute top-3 right-3",children:h.jsx(Xo,{status:p(o),label:y(o),showIcon:!0})}),i&&h.jsx(I,{className:"absolute top-3 left-3",children:h.jsxs(J,{className:"flex items-center gap-1 px-2 py-1 bg-yellow-50 dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 border border-yellow-200 dark:border-yellow-800 rounded-md text-xs font-semibold",children:[h.jsx(oe,{name:ue.ExclamationTriangle,className:"w-3 h-3"}),h.jsx(J,{children:ar("common.update_available")})]})})]}),h.jsxs(I,{className:"p-4",children:[h.jsxs(I,{className:"flex items-start justify-between mb-3",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(jr,{className:"text-lg font-bold text-gray-900 dark:text-white",children:[n,"/",r]}),h.jsxs(I,{className:"flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-400",children:[h.jsxs(J,{children:["v",s]}),i&&a&&h.jsxs(h.Fragment,{children:[h.jsx(J,{children:"→"}),h.jsxs(J,{className:"text-yellow-600 dark:text-yellow-400 font-semibold",children:["v",a]})]})]}),!e&&h.jsx(I,{className:"mt-2",children:h.jsx(Xo,{status:p(o),label:y(o),showIcon:!0})})]}),d.length>0&&h.jsx(Ls,{items:d,triggerIconName:ue.EllipsisVertical,position:"left"})]}),l.length>0&&h.jsxs(I,{className:"mb-3",children:[h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-400 mb-1",children:[ar("common.dependencies"),":"]}),h.jsx(I,{className:"flex flex-wrap gap-1",children:l.map((b,_)=>h.jsx(J,{className:"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs rounded",children:b},_))})]}),c&&u&&h.jsxs(ae,{onClick:u,className:"w-full px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors flex items-center justify-center gap-2 font-medium",children:[h.jsx(oe,{name:ue.Edit,className:"w-4 h-4"}),h.jsx(J,{children:ar("common.edit_layout")})]})]})]})},wu=({type:e,message:t,dismissible:n=!1,onDismiss:r,className:s="",id:o,editorAttrs:i})=>{const a={info:{containerClass:"alert-info",iconClass:"alert-icon-info",textClass:"alert-text-info",icon:ue.InfoCircle},success:{containerClass:"alert-success",iconClass:"alert-icon-success",textClass:"alert-text-success",icon:ue.CheckCircle},warning:{containerClass:"alert-warning",iconClass:"alert-icon-warning",textClass:"alert-text-warning",icon:ue.ExclamationTriangle},error:{containerClass:"alert-error",iconClass:"alert-icon-error",textClass:"alert-text-error",icon:ue.TimesCircle}},l=a[e]??a.info;return h.jsxs(I,{className:`${l.containerClass} ${s}`,role:"alert","aria-live":"polite",id:o,...i,children:[h.jsx(oe,{name:l.icon,className:l.iconClass,ariaLabel:`${e} icon`}),h.jsx(yt,{className:l.textClass,children:t}),n&&h.jsx(ae,{onClick:r,className:`alert-dismiss ${l.textClass}`,"aria-label":"Close alert",children:h.jsx(oe,{name:ue.Times,className:l.iconClass,ariaLabel:"Close"})})]})},Ry=({layoutName:e,onBack:t,onPreview:n,onSave:r,isSaving:s=!1,className:o=""})=>{const i=` flex items-center justify-between gap-4 p-4 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 ${o} - `.trim().replace(/\s+/g," ");return h.jsxs(I,{className:i,children:[h.jsxs(I,{className:"flex items-center gap-3",children:[h.jsx(oe,{onClick:e,className:"p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors bg-transparent border-0 cursor-pointer","aria-label":"뒤로가기",children:h.jsx(re,{name:de.ArrowLeft,className:"text-gray-700 dark:text-gray-300",ariaLabel:"뒤로가기"})}),h.jsx(Jn,{className:"text-xl font-semibold text-gray-900 dark:text-gray-100",children:t})]}),h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{onClick:n,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg transition-colors border-0 cursor-pointer","aria-label":"미리보기",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(re,{name:de.Eye,className:"text-gray-700 dark:text-gray-300",ariaLabel:"미리보기 아이콘"}),h.jsx("span",{children:"미리보기"})]})}),h.jsx(oe,{onClick:r,disabled:s,className:` + `.trim().replace(/\s+/g," ");return h.jsxs(I,{className:i,children:[h.jsxs(I,{className:"flex items-center gap-3",children:[h.jsx(ae,{onClick:t,className:"p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors bg-transparent border-0 cursor-pointer","aria-label":"뒤로가기",children:h.jsx(oe,{name:ue.ArrowLeft,className:"text-gray-700 dark:text-gray-300",ariaLabel:"뒤로가기"})}),h.jsx(or,{className:"text-xl font-semibold text-gray-900 dark:text-gray-100",children:e})]}),h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(ae,{onClick:n,className:"px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg transition-colors border-0 cursor-pointer","aria-label":"미리보기",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{name:ue.Eye,className:"text-gray-700 dark:text-gray-300",ariaLabel:"미리보기 아이콘"}),h.jsx("span",{children:"미리보기"})]})}),h.jsx(ae,{onClick:r,disabled:s,className:` px-4 py-2 rounded-lg transition-colors border-0 cursor-pointer ${s?"bg-blue-400 dark:bg-blue-600 cursor-not-allowed":"bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600"} text-white - `.trim().replace(/\s+/g," "),"aria-label":s?"저장 중...":"저장",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(re,{name:s?de.Spinner:de.Save,className:`text-white ${s?"animate-spin":""}`,ariaLabel:s?"저장 중 아이콘":"저장 아이콘"}),h.jsx("span",{children:s?"저장 중...":"저장"})]})})]})]})},Iy=({files:t,selectedId:e,onSelect:n,className:r=""})=>{const s=a=>{try{const l=new Date(a);if(isNaN(l.getTime()))return a;const c=l.getFullYear(),d=String(l.getMonth()+1).padStart(2,"0"),u=String(l.getDate()).padStart(2,"0"),f=String(l.getHours()).padStart(2,"0"),g=String(l.getMinutes()).padStart(2,"0");return`${c}-${d}-${u} ${f}:${g}`}catch{return a}},o=a=>{n(a)},i=` + `.trim().replace(/\s+/g," "),"aria-label":s?"저장 중...":"저장",children:h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(oe,{name:s?ue.Spinner:ue.Save,className:`text-white ${s?"animate-spin":""}`,ariaLabel:s?"저장 중 아이콘":"저장 아이콘"}),h.jsx("span",{children:s?"저장 중...":"저장"})]})})]})]})},ev=({files:e,selectedId:t,onSelect:n,className:r=""})=>{const s=a=>{try{const l=new Date(a);if(isNaN(l.getTime()))return a;const c=l.getFullYear(),d=String(l.getMonth()+1).padStart(2,"0"),u=String(l.getDate()).padStart(2,"0"),f=String(l.getHours()).padStart(2,"0"),g=String(l.getMinutes()).padStart(2,"0");return`${c}-${d}-${u} ${f}:${g}`}catch{return a}},o=a=>{n(a)},i=` w-full ${r} - `.trim().replace(/\s+/g," ");return h.jsx(I,{className:i,children:t.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"파일이 없습니다."}):h.jsx(Ia,{className:"divide-y divide-gray-200 dark:divide-gray-700",role:"listbox","aria-label":"레이아웃 파일 목록",children:t.map(a=>{const l=a.id===e,c=` + `.trim().replace(/\s+/g," ");return h.jsx(I,{className:i,children:e.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"파일이 없습니다."}):h.jsx(ja,{className:"divide-y divide-gray-200 dark:divide-gray-700",role:"listbox","aria-label":"레이아웃 파일 목록",children:e.map(a=>{const l=a.id===t,c=` flex items-center justify-between p-4 cursor-pointer transition-colors duration-150 ${l?"bg-blue-100 dark:bg-blue-900 border-l-4 border-blue-500":"hover:bg-gray-100 dark:hover:bg-gray-800"} @@ -105,8 +106,8 @@ var SirsoftAdminBasic=(function(R,h,v,Tn){"use strict";var U5=Object.definePrope `.trim().replace(/\s+/g," "),u=` text-sm ${l?"text-blue-700 dark:text-blue-300":"text-gray-500 dark:text-gray-400"} - `.trim().replace(/\s+/g," ");return h.jsx(La,{className:c,onClick:()=>o(a.id),role:"option","aria-selected":l,tabIndex:0,onKeyDown:f=>{(f.key==="Enter"||f.key===" ")&&(f.preventDefault(),o(a.id))},children:h.jsxs(I,{className:"flex flex-col gap-1",children:[h.jsx(Q,{className:d,children:a.name}),h.jsx(Q,{className:u,children:s(a.updated_at)})]})},a.id)})})})};function du(t,e){(e==null||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n=t.length?t.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l1&&arguments[1]!==void 0?arguments[1]:{};Go.initial(t),Go.handler(e);var n={current:t},r=Ns(rv)(n,e),s=Ns(nv)(n),o=Ns(Go.changes)(t),i=Ns(tv)(n);function a(){var c=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(d){return d};return Go.selector(c),c(n.current)}function l(c){Gy(r,s,o,i)(c)}return[a,l]}function tv(t,e){return Os(e)?e(t.current):e}function nv(t,e){return t.current=gu(gu({},t.current),e),e}function rv(t,e,n){return Os(e)?e(t.current):Object.keys(n).forEach(function(r){var s;return(s=e[r])===null||s===void 0?void 0:s.call(e,t.current[r])}),n}var sv={create:ev},ov={paths:{vs:"https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs"}};function iv(t){return function e(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o=t.length?t.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;lo(a.id),role:"option","aria-selected":l,tabIndex:0,onKeyDown:f=>{(f.key==="Enter"||f.key===" ")&&(f.preventDefault(),o(a.id))},children:h.jsxs(I,{className:"flex flex-col gap-1",children:[h.jsx(J,{className:d,children:a.name}),h.jsx(J,{className:u,children:s(a.updated_at)})]})},a.id)})})})};function ku(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?e.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l1&&arguments[1]!==void 0?arguments[1]:{};Ro.initial(e),Ro.handler(t);var n={current:e},r=Vs(Sv)(n,t),s=Vs(Cv)(n),o=Vs(Ro.changes)(e),i=Vs(kv)(n);function a(){var c=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(d){return d};return Ro.selector(c),c(n.current)}function l(c){fv(r,s,o,i)(c)}return[a,l]}function kv(e,t){return Hs(t)?t(e.current):t}function Cv(e,t){return e.current=Eu(Eu({},e.current),t),t}function Sv(e,t,n){return Hs(t)?t(e.current):Object.keys(n).forEach(function(r){var s;return(s=t[r])===null||s===void 0?void 0:s.call(t,e.current[r])}),n}var _v={create:wv},Ev={paths:{vs:"https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs"}};function Mv(e){return function t(){for(var n=this,r=arguments.length,s=new Array(r),o=0;o=e.length?e.apply(this,s):function(){for(var i=arguments.length,a=new Array(i),l=0;l{r.current=!1}:t,e)}var on=Dv;function As(){}function Hr(t,e,n,r){return Iv(t,r)||Lv(t,e,n,r)}function Iv(t,e){return t.editor.getModel(Cu(t,e))}function Lv(t,e,n,r){return t.editor.createModel(e,n,r?Cu(t,r):void 0)}function Cu(t,e){return t.Uri.parse(e)}function Pv({original:t,modified:e,language:n,originalLanguage:r,modifiedLanguage:s,originalModelPath:o,modifiedModelPath:i,keepCurrentOriginalModel:a=!1,keepCurrentModifiedModel:l=!1,theme:c="light",loading:d="Loading...",options:u={},height:f="100%",width:g="100%",className:m,wrapperProps:p={},beforeMount:y=As,onMount:b=As}){let[_,x]=v.useState(!1),[E,C]=v.useState(!0),k=v.useRef(null),N=v.useRef(null),S=v.useRef(null),w=v.useRef(b),M=v.useRef(y),T=v.useRef(!1);ku(()=>{let A=xu.init();return A.then(D=>(N.current=D)&&C(!1)).catch(D=>D?.type!=="cancelation"&&console.error("Monaco initialization: error:",D)),()=>k.current?$():A.cancel()}),on(()=>{if(k.current&&N.current){let A=k.current.getOriginalEditor(),D=Hr(N.current,t||"",r||n||"text",o||"");D!==A.getModel()&&A.setModel(D)}},[o],_),on(()=>{if(k.current&&N.current){let A=k.current.getModifiedEditor(),D=Hr(N.current,e||"",s||n||"text",i||"");D!==A.getModel()&&A.setModel(D)}},[i],_),on(()=>{let A=k.current.getModifiedEditor();A.getOption(N.current.editor.EditorOption.readOnly)?A.setValue(e||""):e!==A.getValue()&&(A.executeEdits("",[{range:A.getModel().getFullModelRange(),text:e||"",forceMoveMarkers:!0}]),A.pushUndoStop())},[e],_),on(()=>{k.current?.getModel()?.original.setValue(t||"")},[t],_),on(()=>{let{original:A,modified:D}=k.current.getModel();N.current.editor.setModelLanguage(A,r||n||"text"),N.current.editor.setModelLanguage(D,s||n||"text")},[n,r,s],_),on(()=>{N.current?.editor.setTheme(c)},[c],_),on(()=>{k.current?.updateOptions(u)},[u],_);let P=v.useCallback(()=>{if(!N.current)return;M.current(N.current);let A=Hr(N.current,t||"",r||n||"text",o||""),D=Hr(N.current,e||"",s||n||"text",i||"");k.current?.setModel({original:A,modified:D})},[n,e,s,t,r,o,i]),L=v.useCallback(()=>{!T.current&&S.current&&(k.current=N.current.editor.createDiffEditor(S.current,{automaticLayout:!0,...u}),P(),N.current?.editor.setTheme(c),x(!0),T.current=!0)},[u,c,P]);v.useEffect(()=>{_&&w.current(k.current,N.current)},[_]),v.useEffect(()=>{!E&&!_&&L()},[E,_,L]);function $(){let A=k.current?.getModel();a||A?.original?.dispose(),l||A?.modified?.dispose(),k.current?.dispose()}return v.createElement(wu,{width:g,height:f,isEditorReady:_,loading:d,_ref:S,className:m,wrapperProps:p})}var $v=Pv;v.memo($v);function Fv(t){let e=v.useRef();return v.useEffect(()=>{e.current=t},[t]),e.current}var zv=Fv,Ko=new Map;function Vv({defaultValue:t,defaultLanguage:e,defaultPath:n,value:r,language:s,path:o,theme:i="light",line:a,loading:l="Loading...",options:c={},overrideServices:d={},saveViewState:u=!0,keepCurrentModel:f=!1,width:g="100%",height:m="100%",className:p,wrapperProps:y={},beforeMount:b=As,onMount:_=As,onChange:x,onValidate:E=As}){let[C,k]=v.useState(!1),[N,S]=v.useState(!0),w=v.useRef(null),M=v.useRef(null),T=v.useRef(null),P=v.useRef(_),L=v.useRef(b),$=v.useRef(),A=v.useRef(r),D=zv(o),O=v.useRef(!1),z=v.useRef(!1);ku(()=>{let W=xu.init();return W.then(U=>(w.current=U)&&S(!1)).catch(U=>U?.type!=="cancelation"&&console.error("Monaco initialization: error:",U)),()=>M.current?j():W.cancel()}),on(()=>{let W=Hr(w.current,t||r||"",e||s||"",o||n||"");W!==M.current?.getModel()&&(u&&Ko.set(D,M.current?.saveViewState()),M.current?.setModel(W),u&&M.current?.restoreViewState(Ko.get(o)))},[o],C),on(()=>{M.current?.updateOptions(c)},[c],C),on(()=>{!M.current||r===void 0||(M.current.getOption(w.current.editor.EditorOption.readOnly)?M.current.setValue(r):r!==M.current.getValue()&&(z.current=!0,M.current.executeEdits("",[{range:M.current.getModel().getFullModelRange(),text:r,forceMoveMarkers:!0}]),M.current.pushUndoStop(),z.current=!1))},[r],C),on(()=>{let W=M.current?.getModel();W&&s&&w.current?.editor.setModelLanguage(W,s)},[s],C),on(()=>{a!==void 0&&M.current?.revealLine(a)},[a],C),on(()=>{w.current?.editor.setTheme(i)},[i],C);let H=v.useCallback(()=>{if(!(!T.current||!w.current)&&!O.current){L.current(w.current);let W=o||n,U=Hr(w.current,r||t||"",e||s||"",W||"");M.current=w.current?.editor.create(T.current,{model:U,automaticLayout:!0,...c},d),u&&M.current.restoreViewState(Ko.get(W)),w.current.editor.setTheme(i),a!==void 0&&M.current.revealLine(a),k(!0),O.current=!0}},[t,e,n,r,s,o,c,d,u,i,a]);v.useEffect(()=>{C&&P.current(M.current,w.current)},[C]),v.useEffect(()=>{!N&&!C&&H()},[N,C,H]),A.current=r,v.useEffect(()=>{C&&x&&($.current?.dispose(),$.current=M.current?.onDidChangeModelContent(W=>{z.current||x(M.current.getValue(),W)}))},[C,x]),v.useEffect(()=>{if(C){let W=w.current.editor.onDidChangeMarkers(U=>{let B=M.current.getModel()?.uri;if(B&&U.find(G=>G.path===B.path)){let G=w.current.editor.getModelMarkers({resource:B});E?.(G)}});return()=>{W?.dispose()}}return()=>{}},[C,E]);function j(){$.current?.dispose(),f?u&&Ko.set(o,M.current.saveViewState()):M.current.getModel()?.dispose(),M.current.dispose()}return v.createElement(wu,{width:g,height:m,isEditorReady:C,loading:l,_ref:T,className:p,wrapperProps:y})}var Hv=Vv,Uv=v.memo(Hv),Bv=Uv;const jv=({value:t,onChange:e,language:n="json",height:r="100%",readOnly:s=!1,theme:o="vs-dark"})=>{const i=v.useRef(null),a=(c,d)=>{i.current=c,n==="json"&&d.languages.json.jsonDefaults.setDiagnosticsOptions({validate:!0,schemaValidation:"error",allowComments:!1}),c.updateOptions({readOnly:s,minimap:{enabled:!1},scrollBeyondLastLine:!1,fontSize:14,lineNumbers:"on",renderWhitespace:"selection",automaticLayout:!0})},l=c=>{e&&c!==void 0&&e({target:{value:c}})};return v.useEffect(()=>{i.current&&i.current.updateOptions({readOnly:s})},[s]),h.jsx("div",{className:"border border-gray-300 dark:border-gray-700 rounded-lg overflow-hidden",children:h.jsx(Bv,{height:r,language:n,value:t,theme:o,onChange:l,onMount:a,options:{readOnly:s,minimap:{enabled:!1},scrollBeyondLastLine:!1,fontSize:14,lineNumbers:"on",renderWhitespace:"selection",automaticLayout:!0}})})},Wv=t=>{const e=new Date(t);return new Intl.DateTimeFormat("ko-KR",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(e)},Su=v.memo(({version:t,isSelected:e,onSelect:n})=>{const r=v.useCallback(()=>{n?.(t.id)},[n,t.id]);return h.jsx(La,{role:"listitem","aria-selected":e,className:` + `},Nu=Mv(Av)(Mu),Dv={config:Tv},Iv=function(){for(var t=arguments.length,n=new Array(t),r=0;r{r.current=!1}:e,t)}var cn=Rv;function Bs(){}function es(e,t,n,r){return ex(e,r)||tx(e,t,n,r)}function ex(e,t){return e.editor.getModel(Pu(e,t))}function tx(e,t,n,r){return e.editor.createModel(t,n,r?Pu(e,r):void 0)}function Pu(e,t){return e.Uri.parse(t)}function nx({original:e,modified:t,language:n,originalLanguage:r,modifiedLanguage:s,originalModelPath:o,modifiedModelPath:i,keepCurrentOriginalModel:a=!1,keepCurrentModifiedModel:l=!1,theme:c="light",loading:d="Loading...",options:u={},height:f="100%",width:g="100%",className:m,wrapperProps:p={},beforeMount:y=Bs,onMount:b=Bs}){let[_,x]=v.useState(!1),[M,S]=v.useState(!0),k=v.useRef(null),N=v.useRef(null),C=v.useRef(null),w=v.useRef(b),E=v.useRef(y),A=v.useRef(!1);Lu(()=>{let D=Du.init();return D.then(O=>(N.current=O)&&S(!1)).catch(O=>O?.type!=="cancelation"&&console.error("Monaco initialization: error:",O)),()=>k.current?$():D.cancel()}),cn(()=>{if(k.current&&N.current){let D=k.current.getOriginalEditor(),O=es(N.current,e||"",r||n||"text",o||"");O!==D.getModel()&&D.setModel(O)}},[o],_),cn(()=>{if(k.current&&N.current){let D=k.current.getModifiedEditor(),O=es(N.current,t||"",s||n||"text",i||"");O!==D.getModel()&&D.setModel(O)}},[i],_),cn(()=>{let D=k.current.getModifiedEditor();D.getOption(N.current.editor.EditorOption.readOnly)?D.setValue(t||""):t!==D.getValue()&&(D.executeEdits("",[{range:D.getModel().getFullModelRange(),text:t||"",forceMoveMarkers:!0}]),D.pushUndoStop())},[t],_),cn(()=>{k.current?.getModel()?.original.setValue(e||"")},[e],_),cn(()=>{let{original:D,modified:O}=k.current.getModel();N.current.editor.setModelLanguage(D,r||n||"text"),N.current.editor.setModelLanguage(O,s||n||"text")},[n,r,s],_),cn(()=>{N.current?.editor.setTheme(c)},[c],_),cn(()=>{k.current?.updateOptions(u)},[u],_);let P=v.useCallback(()=>{if(!N.current)return;E.current(N.current);let D=es(N.current,e||"",r||n||"text",o||""),O=es(N.current,t||"",s||n||"text",i||"");k.current?.setModel({original:D,modified:O})},[n,t,s,e,r,o,i]),L=v.useCallback(()=>{!A.current&&C.current&&(k.current=N.current.editor.createDiffEditor(C.current,{automaticLayout:!0,...u}),P(),N.current?.editor.setTheme(c),x(!0),A.current=!0)},[u,c,P]);v.useEffect(()=>{_&&w.current(k.current,N.current)},[_]),v.useEffect(()=>{!M&&!_&&L()},[M,_,L]);function $(){let D=k.current?.getModel();a||D?.original?.dispose(),l||D?.modified?.dispose(),k.current?.dispose()}return v.createElement(Iu,{width:g,height:f,isEditorReady:_,loading:d,_ref:C,className:m,wrapperProps:p})}var rx=nx;v.memo(rx);function sx(e){let t=v.useRef();return v.useEffect(()=>{t.current=e},[e]),t.current}var ox=sx,ti=new Map;function ix({defaultValue:e,defaultLanguage:t,defaultPath:n,value:r,language:s,path:o,theme:i="light",line:a,loading:l="Loading...",options:c={},overrideServices:d={},saveViewState:u=!0,keepCurrentModel:f=!1,width:g="100%",height:m="100%",className:p,wrapperProps:y={},beforeMount:b=Bs,onMount:_=Bs,onChange:x,onValidate:M=Bs}){let[S,k]=v.useState(!1),[N,C]=v.useState(!0),w=v.useRef(null),E=v.useRef(null),A=v.useRef(null),P=v.useRef(_),L=v.useRef(b),$=v.useRef(),D=v.useRef(r),O=ox(o),T=v.useRef(!1),z=v.useRef(!1);Lu(()=>{let G=Du.init();return G.then(B=>(w.current=B)&&C(!1)).catch(B=>B?.type!=="cancelation"&&console.error("Monaco initialization: error:",B)),()=>E.current?W():G.cancel()}),cn(()=>{let G=es(w.current,e||r||"",t||s||"",o||n||"");G!==E.current?.getModel()&&(u&&ti.set(O,E.current?.saveViewState()),E.current?.setModel(G),u&&E.current?.restoreViewState(ti.get(o)))},[o],S),cn(()=>{E.current?.updateOptions(c)},[c],S),cn(()=>{!E.current||r===void 0||(E.current.getOption(w.current.editor.EditorOption.readOnly)?E.current.setValue(r):r!==E.current.getValue()&&(z.current=!0,E.current.executeEdits("",[{range:E.current.getModel().getFullModelRange(),text:r,forceMoveMarkers:!0}]),E.current.pushUndoStop(),z.current=!1))},[r],S),cn(()=>{let G=E.current?.getModel();G&&s&&w.current?.editor.setModelLanguage(G,s)},[s],S),cn(()=>{a!==void 0&&E.current?.revealLine(a)},[a],S),cn(()=>{w.current?.editor.setTheme(i)},[i],S);let U=v.useCallback(()=>{if(!(!A.current||!w.current)&&!T.current){L.current(w.current);let G=o||n,B=es(w.current,r||e||"",t||s||"",G||"");E.current=w.current?.editor.create(A.current,{model:B,automaticLayout:!0,...c},d),u&&E.current.restoreViewState(ti.get(G)),w.current.editor.setTheme(i),a!==void 0&&E.current.revealLine(a),k(!0),T.current=!0}},[e,t,n,r,s,o,c,d,u,i,a]);v.useEffect(()=>{S&&P.current(E.current,w.current)},[S]),v.useEffect(()=>{!N&&!S&&U()},[N,S,U]),D.current=r,v.useEffect(()=>{S&&x&&($.current?.dispose(),$.current=E.current?.onDidChangeModelContent(G=>{z.current||x(E.current.getValue(),G)}))},[S,x]),v.useEffect(()=>{if(S){let G=w.current.editor.onDidChangeMarkers(B=>{let j=E.current.getModel()?.uri;if(j&&B.find(Y=>Y.path===j.path)){let Y=w.current.editor.getModelMarkers({resource:j});M?.(Y)}});return()=>{G?.dispose()}}return()=>{}},[S,M]);function W(){$.current?.dispose(),f?u&&ti.set(o,E.current.saveViewState()):E.current.getModel()?.dispose(),E.current.dispose()}return v.createElement(Iu,{width:g,height:m,isEditorReady:S,loading:l,_ref:A,className:p,wrapperProps:y})}var ax=ix,lx=v.memo(ax),cx=lx;const dx=({value:e,onChange:t,language:n="json",height:r="100%",readOnly:s=!1,theme:o="vs-dark"})=>{const i=v.useRef(null),a=(c,d)=>{i.current=c,n==="json"&&d.languages.json.jsonDefaults.setDiagnosticsOptions({validate:!0,schemaValidation:"error",allowComments:!1}),c.updateOptions({readOnly:s,minimap:{enabled:!1},scrollBeyondLastLine:!1,fontSize:14,lineNumbers:"on",renderWhitespace:"selection",automaticLayout:!0})},l=c=>{t&&c!==void 0&&t({target:{value:c}})};return v.useEffect(()=>{i.current&&i.current.updateOptions({readOnly:s})},[s]),h.jsx("div",{className:"border border-gray-300 dark:border-gray-700 rounded-lg overflow-hidden",children:h.jsx(cx,{height:r,language:n,value:e,theme:o,onChange:l,onMount:a,options:{readOnly:s,minimap:{enabled:!1},scrollBeyondLastLine:!1,fontSize:14,lineNumbers:"on",renderWhitespace:"selection",automaticLayout:!0}})})},ux=e=>{const t=new Date(e);return new Intl.DateTimeFormat("ko-KR",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(t)},$u=v.memo(({version:e,isSelected:t,onSelect:n})=>{const r=v.useCallback(()=>{n?.(e.id)},[n,e.id]);return h.jsx(Wa,{role:"listitem","aria-selected":t,className:` p-3 rounded-lg border cursor-pointer transition-colors - ${e?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500"} - `,onClick:r,children:h.jsxs(I,{className:"flex justify-between items-start",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(Q,{className:"font-semibold text-gray-900 dark:text-gray-100",children:["버전 ",t.version]}),h.jsx(Q,{className:"ml-2 text-sm text-gray-500 dark:text-gray-400",children:Wv(t.created_at)})]}),h.jsxs(I,{className:"flex gap-3 text-sm",children:[h.jsxs(Q,{className:"text-green-600 dark:text-green-400",children:["+",t.changes_summary.added]}),h.jsxs(Q,{className:"text-red-600 dark:text-red-400",children:["-",t.changes_summary.removed]})]})]})})});Su.displayName="VersionListItem";const _u=({versions:t,selectedId:e,onSelect:n})=>t.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"버전이 없습니다."}):h.jsx(Ia,{role:"list",className:"space-y-2",children:t.map(r=>h.jsx(Su,{version:r,isSelected:e===r.id,onSelect:n},r.id))}),Gv=({layoutId:t,versions:e,onRestore:n})=>{const[r,s]=v.useState(),o=l=>{s(l)},i=()=>{r!==void 0&&n(r)},a=r===void 0;return h.jsxs(I,{className:"space-y-4",role:"region","aria-label":"레이아웃 히스토리 패널",children:[h.jsx(I,{className:"border border-gray-300 dark:border-gray-600 rounded-lg p-4",children:h.jsx(_u,{versions:e,selectedId:r,onSelect:o})}),h.jsx(I,{className:"flex justify-end",children:h.jsx(oe,{variant:"primary",disabled:a,onClick:i,"aria-label":"선택한 버전으로 복원",children:"복원"})})]})},Yv=({emailLabel:t="Email",passwordLabel:e="Password",submitButtonText:n="Sign In",processingText:r="Processing...",emailPlaceholder:s="Email",passwordPlaceholder:o="Password",forgotPasswordText:i="Forgot your password?",forgotPasswordUrl:a="/admin/password/reset",emailRequiredError:l="Please enter your email.",emailInvalidError:c="Please enter a valid email address.",passwordRequiredError:d="Please enter your password.",onSubmit:u,loadingActions:f={},isLoading:g=!1,disabled:m=!1,apiError:p="",className:y="",style:b})=>{const[_,x]=v.useState(""),[E,C]=v.useState(""),[k,N]=v.useState({}),S=p,w=Object.values(f).some($=>$===!0),M=g||w,T=m||M,P=()=>{const $={},A=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;return!_||_.trim()===""?$.email=l:A.test(_)||($.email=c),(!E||E.trim()==="")&&($.password=d),N($),Object.keys($).length===0},L=async $=>{if(!P()){$.preventDefault();return}N({}),u?await u($):$.preventDefault()};return h.jsx(I,{className:`w-full ${y}`,style:b,children:h.jsxs(Pa,{onSubmit:L,className:"space-y-6",noValidate:!0,children:[S&&h.jsx(I,{className:"p-4 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800",children:h.jsx(yt,{className:"text-sm text-red-600 dark:text-red-400",children:S})}),h.jsxs(I,{children:[h.jsx(sn,{htmlFor:"email",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:t}),h.jsx(kt,{id:"email",name:"email",type:"email",value:_,onChange:$=>x($.target.value),placeholder:s,disabled:T,className:` + ${t?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500"} + `,onClick:r,children:h.jsxs(I,{className:"flex justify-between items-start",children:[h.jsxs(I,{className:"flex-1",children:[h.jsxs(J,{className:"font-semibold text-gray-900 dark:text-gray-100",children:["버전 ",e.version]}),h.jsx(J,{className:"ml-2 text-sm text-gray-500 dark:text-gray-400",children:ux(e.created_at)})]}),h.jsxs(I,{className:"flex gap-3 text-sm",children:[h.jsxs(J,{className:"text-green-600 dark:text-green-400",children:["+",e.changes_summary.added]}),h.jsxs(J,{className:"text-red-600 dark:text-red-400",children:["-",e.changes_summary.removed]})]})]})})});$u.displayName="VersionListItem";const Fu=({versions:e,selectedId:t,onSelect:n})=>e.length===0?h.jsx(I,{className:"p-4 text-center text-gray-500 dark:text-gray-400",children:"버전이 없습니다."}):h.jsx(ja,{role:"list",className:"space-y-2",children:e.map(r=>h.jsx($u,{version:r,isSelected:t===r.id,onSelect:n},r.id))}),fx=({layoutId:e,versions:t,onRestore:n})=>{const[r,s]=v.useState(),o=l=>{s(l)},i=()=>{r!==void 0&&n(r)},a=r===void 0;return h.jsxs(I,{className:"space-y-4",role:"region","aria-label":"레이아웃 히스토리 패널",children:[h.jsx(I,{className:"border border-gray-300 dark:border-gray-600 rounded-lg p-4",children:h.jsx(Fu,{versions:t,selectedId:r,onSelect:o})}),h.jsx(I,{className:"flex justify-end",children:h.jsx(ae,{variant:"primary",disabled:a,onClick:i,"aria-label":"선택한 버전으로 복원",children:"복원"})})]})},hx=({emailLabel:e="Email",passwordLabel:t="Password",submitButtonText:n="Sign In",processingText:r="Processing...",emailPlaceholder:s="Email",passwordPlaceholder:o="Password",forgotPasswordText:i="Forgot your password?",forgotPasswordUrl:a="/admin/password/reset",emailRequiredError:l="Please enter your email.",emailInvalidError:c="Please enter a valid email address.",passwordRequiredError:d="Please enter your password.",onSubmit:u,loadingActions:f={},isLoading:g=!1,disabled:m=!1,apiError:p="",className:y="",style:b})=>{const[_,x]=v.useState(""),[M,S]=v.useState(""),[k,N]=v.useState({}),C=p,w=Object.values(f).some($=>$===!0),E=g||w,A=m||E,P=()=>{const $={},D=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;return!_||_.trim()===""?$.email=l:D.test(_)||($.email=c),(!M||M.trim()==="")&&($.password=d),N($),Object.keys($).length===0},L=async $=>{if(!P()){$.preventDefault();return}N({}),u?await u($):$.preventDefault()};return h.jsx(I,{className:`w-full ${y}`,style:b,children:h.jsxs(Ga,{onSubmit:L,className:"space-y-6",noValidate:!0,children:[C&&h.jsx(I,{className:"p-4 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800",children:h.jsx(yt,{className:"text-sm text-red-600 dark:text-red-400",children:C})}),h.jsxs(I,{children:[h.jsx(ln,{htmlFor:"email",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:e}),h.jsx(kt,{id:"email",name:"email",type:"email",value:_,onChange:$=>x($.target.value),placeholder:s,disabled:A,className:` w-full px-4 py-3 rounded-lg border ${k.email?"border-red-500 focus:border-red-500 focus:ring-red-500":"border-gray-300 dark:border-gray-600 focus:border-blue-500 focus:ring-blue-500"} bg-white dark:bg-gray-800 @@ -127,7 +128,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 - `}),k.email&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.email})]}),h.jsxs(I,{children:[h.jsx(sn,{htmlFor:"password",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:e}),h.jsx(kt,{id:"password",name:"password",type:"password",value:E,onChange:$=>C($.target.value),placeholder:o,disabled:T,className:` + `}),k.email&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.email})]}),h.jsxs(I,{children:[h.jsx(ln,{htmlFor:"password",className:"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2",children:t}),h.jsx(kt,{id:"password",name:"password",type:"password",value:M,onChange:$=>S($.target.value),placeholder:o,disabled:A,className:` w-full px-4 py-3 rounded-lg border ${k.password?"border-red-500 focus:border-red-500 focus:ring-red-500":"border-gray-300 dark:border-gray-600 focus:border-blue-500 focus:ring-blue-500"} bg-white dark:bg-gray-800 @@ -136,7 +137,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 - `}),k.password&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.password})]}),a&&h.jsx(I,{className:"flex items-center justify-end",children:h.jsx(Lr,{href:a,className:"text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 transition-colors duration-200",children:i})}),h.jsx(oe,{type:"submit",disabled:T,className:` + `}),k.password&&h.jsx(yt,{className:"mt-2 text-sm text-red-600 dark:text-red-400",children:k.password})]}),a&&h.jsx(I,{className:"flex items-center justify-end",children:h.jsx(Wr,{href:a,className:"text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 transition-colors duration-200",children:i})}),h.jsx(ae,{type:"submit",disabled:A,className:` w-full px-4 py-3 rounded-lg font-medium bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 @@ -146,16 +147,16 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center - `,children:M?h.jsxs(h.Fragment,{children:[h.jsxs(Un,{className:"animate-spin -ml-1 mr-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[h.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),h.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),r]}):n})]})})},Eu=t=>t==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":t,ll=t=>{const e=Eu(t);document.documentElement.setAttribute("data-theme",e),e==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")},Kv=()=>{const t=localStorage.getItem("g7_color_scheme");return t&&["auto","light","dark"].includes(t)?t:"auto"},Xv=({onThemeChange:t,className:e="",autoText:n="System",lightText:r="Light",darkText:s="Dark",id:o,editorAttrs:i})=>{const a=Kv(),[l,c]=v.useState(a),[d,u]=v.useState(!1),f=v.useRef(null);v.useEffect(()=>{ll(l)},[]),v.useEffect(()=>{if(l!=="auto")return;const p=window.matchMedia("(prefers-color-scheme: dark)"),y=()=>{ll("auto")};return p.addEventListener("change",y),()=>p.removeEventListener("change",y)},[l]),v.useEffect(()=>{const p=y=>{f.current&&!f.current.contains(y.target)&&u(!1)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[]);const g=p=>{c(p),localStorage.setItem("g7_color_scheme",p),ll(p),u(!1),t?.(p)},m=()=>Eu(l)==="dark"?de.Moon:de.Sun;return h.jsxs(I,{ref:f,className:`relative ${e}`,id:o,...i,children:[h.jsx(oe,{onClick:()=>u(!d),className:"w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors","aria-label":"Toggle theme",children:h.jsx(re,{name:m(),className:"text-sm text-slate-500 dark:text-slate-400"})}),d&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsxs(I,{className:"py-2",children:[h.jsxs(oe,{onClick:()=>g("auto"),className:` + `,children:E?h.jsxs(h.Fragment,{children:[h.jsxs(Xn,{className:"animate-spin -ml-1 mr-3 h-5 w-5 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[h.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),h.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),r]}):n})]})})},zu=e=>e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e,bl=e=>{const t=zu(e);document.documentElement.setAttribute("data-theme",t),t==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")},gx=()=>{const e=localStorage.getItem("g7_color_scheme");return e&&["auto","light","dark"].includes(e)?e:"auto"},px=({onThemeChange:e,className:t="",autoText:n="System",lightText:r="Light",darkText:s="Dark",id:o,editorAttrs:i})=>{const a=gx(),[l,c]=v.useState(a),[d,u]=v.useState(!1),f=v.useRef(null);v.useEffect(()=>{bl(l)},[]),v.useEffect(()=>{if(l!=="auto")return;const p=window.matchMedia("(prefers-color-scheme: dark)"),y=()=>{bl("auto")};return p.addEventListener("change",y),()=>p.removeEventListener("change",y)},[l]),v.useEffect(()=>{const p=y=>{f.current&&!f.current.contains(y.target)&&u(!1)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[]);const g=p=>{c(p),localStorage.setItem("g7_color_scheme",p),bl(p),u(!1),e?.(p)},m=()=>zu(l)==="dark"?ue.Moon:ue.Sun;return h.jsxs(I,{ref:f,className:`relative ${t}`,id:o,...i,children:[h.jsx(ae,{onClick:()=>u(!d),className:"w-9 h-9 flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full transition-colors","aria-label":"Toggle theme",children:h.jsx(oe,{name:m(),className:"text-sm text-slate-500 dark:text-slate-400"})}),d&&h.jsx(I,{className:"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50",children:h.jsxs(I,{className:"py-2",children:[h.jsxs(ae,{onClick:()=>g("auto"),className:` w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors ${l==="auto"?"bg-gray-50 dark:bg-gray-700":""} - `,children:[h.jsx(re,{name:de.Settings,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:n}),l==="auto"&&h.jsx(re,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(oe,{onClick:()=>g("light"),className:` + `,children:[h.jsx(oe,{name:ue.Settings,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"flex-1 text-left text-gray-900 dark:text-white",children:n}),l==="auto"&&h.jsx(oe,{name:ue.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(ae,{onClick:()=>g("light"),className:` w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors ${l==="light"?"bg-gray-50 dark:bg-gray-700":""} - `,children:[h.jsx(re,{name:de.Sun,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:r}),l==="light"&&h.jsx(re,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(oe,{onClick:()=>g("dark"),className:` + `,children:[h.jsx(oe,{name:ue.Sun,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"flex-1 text-left text-gray-900 dark:text-white",children:r}),l==="light"&&h.jsx(oe,{name:ue.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]}),h.jsxs(ae,{onClick:()=>g("dark"),className:` w-full px-4 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center gap-3 transition-colors ${l==="dark"?"bg-gray-50 dark:bg-gray-700":""} - `,children:[h.jsx(re,{name:de.Moon,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"flex-1 text-left text-gray-900 dark:text-white",children:s}),l==="dark"&&h.jsx(re,{name:de.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]})]})})]})},Mu=window.G7Core?.createLogger?.("Comp:ColumnSelector")??{log:(...t)=>console.log("[Comp:ColumnSelector]",...t),warn:(...t)=>console.warn("[Comp:ColumnSelector]",...t),error:(...t)=>console.error("[Comp:ColumnSelector]",...t)},Nu=(t,e)=>window.G7Core?.t?.(t,e)??t,qv=()=>window.G7Core,Zv=({id:t,columns:e,visibleColumns:n,onColumnVisibilityChange:r,triggerLabel:s,triggerIconName:o=de.Sliders,position:i="right",className:a="",style:l,requiredText:c,menuTitle:d,menuDescription:u,defaultColumns:f,triggerClassName:g,labelClassName:m,dynamicColumns:p,dynamicColumnsInsertAfter:y,editorAttrs:b})=>{const _=s??Nu("common.column_selector"),x=c??Nu("common.required"),E=v.useMemo(()=>{if(!p||!Array.isArray(p)||p.length===0)return e;if(y){const V=e.findIndex(X=>X.field===y);if(V!==-1)return[...e.slice(0,V+1),...p,...e.slice(V+1)]}return[...e,...p]},[e,p,y]),[C,k]=v.useState(!1),N=v.useRef(null),S=v.useRef(null),[w,M]=v.useState(!1),[T,P]=v.useState(i),L=v.useMemo(()=>{try{const V=window.G7Core?.AuthManager?.getInstance();return V?V.getUser()?.uuid:void 0}catch{return}},[]),$=t?`g7_columns_${t}${L!==void 0?`_${L}`:""}`:null,A=()=>{if(!$)return null;try{const V=localStorage.getItem($);if(V){const X=JSON.parse(V);if(Array.isArray(X)&&X.length>0)return X}}catch(V){Mu.warn("Failed to load from localStorage:",V)}return null},D=V=>{if($)try{localStorage.setItem($,JSON.stringify(V))}catch(X){Mu.warn("Failed to save to localStorage:",X)}},O=()=>{const V=A();return V||(n&&n.length>0?n:f&&f.length>0?f:E.filter(X=>X.visible!==!1).map(X=>X.field))},[z,H]=v.useState(O);v.useEffect(()=>{w||(r&&z.length>0&&r(z),M(!0))},[w,r,z]),v.useEffect(()=>{w&&n&&n.length>0&&(A()||H(n))},[n,w]);const j=v.useRef([]),W=v.useRef(null);v.useEffect(()=>{if(w&&p&&p.length>0){const V=p.map(Z=>Z.field);if(W.current===null){const Z=A();if(W.current=Z!==null,Z!==null){j.current=V;return}}const X=j.current,K=V.filter(Z=>!X.includes(Z));if(K.length>0){const Z=n&&n.length>0?n:z,ee=K.filter(ne=>!Z.includes(ne));if(ee.length>0){const ne=[...Z,...ee];H(ne),D(ne),r?.(ne)}}j.current=V}},[p,w]);const U=n&&n.length>0?n:z;v.useEffect(()=>{const V=X=>{N.current&&!N.current.contains(X.target)&&k(!1)};return C&&document.addEventListener("mousedown",V),()=>{document.removeEventListener("mousedown",V)}},[C]),v.useEffect(()=>{if(C&&S.current&&N.current){const V=S.current.getBoundingClientRect(),X=N.current.getBoundingClientRect(),K=window.innerWidth,Z=V.width||256;i==="right"?X.right-Z<0?P("left"):P("right"):X.left+Z>K?P("right"):P("left")}},[C,i]);const B=V=>{if(E.find(Z=>Z.field===V)?.required)return;const K=U.includes(V)?U.filter(Z=>Z!==V):[...U,V];H(K),D(K),r?.(K)},G=T==="left"?"left-0":"right-0";return h.jsxs(I,{ref:N,className:`relative inline-block ${a}`,style:l,id:t,...b,children:[h.jsxs(oe,{onClick:()=>k(!C),className:qv()?.style?.mergeClasses?.("flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",g)??`flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors ${g??""}`,children:[h.jsx(re,{name:o,className:"w-5 h-5"}),_&&h.jsx(Q,{className:`${m||"hidden sm:inline"} text-sm`,children:_})]}),C&&h.jsxs(I,{ref:S,className:`absolute ${G} z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg`,children:[(d||u)&&h.jsxs(I,{className:"px-4 pt-3 pb-2 border-b border-gray-200 dark:border-gray-700",children:[d&&h.jsx(Q,{className:"block text-sm font-medium text-gray-900 dark:text-white",children:d}),u&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-1",children:u})]}),h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:E.map(V=>{const X=U.includes(V.field),K=V.required===!0;return h.jsxs(I,{onClick:()=>!K&&B(V.field),className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${K?"cursor-default":"cursor-pointer"}`,children:[h.jsx(vn,{checked:X,disabled:K,readOnly:!0,className:"h-4 w-4 text-blue-600 rounded border-gray-300 dark:border-gray-600 pointer-events-none"}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300",children:V.header}),K&&x&&h.jsx(Q,{className:"ml-auto text-xs text-gray-400 dark:text-gray-500",children:x})]},V.field)})})]})]})},Qv=window.G7Core?.createLogger?.("Comp:PageTransition")??{log:(...t)=>console.log("[Comp:PageTransition]",...t),warn:(...t)=>console.warn("[Comp:PageTransition]",...t),error:(...t)=>console.error("[Comp:PageTransition]",...t)},Jv=({className:t="",style:e})=>{const[n,r]=v.useState(!1);return v.useEffect(()=>{const s=window.G7Core?.TransitionManager;if(!s){Qv.warn("TransitionManager를 찾을 수 없습니다.");return}return r(s.getIsPending()),s.subscribe(i=>{r(i)})},[]),n?h.jsxs(I,{className:`fixed top-0 left-0 right-0 z-[999] ${t}`,style:e,role:"progressbar","aria-label":"페이지 로딩 중",children:[h.jsx(I,{className:"h-1 bg-gradient-to-r from-blue-500 via-blue-600 to-blue-500 animate-pulse",children:h.jsx(I,{className:"h-full bg-blue-700 animate-[loading_1.5s_ease-in-out_infinite]",style:{width:"30%",animation:"loading 1.5s ease-in-out infinite"}})}),h.jsx("style",{children:` + `,children:[h.jsx(oe,{name:ue.Moon,className:"w-5 h-5 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"flex-1 text-left text-gray-900 dark:text-white",children:s}),l==="dark"&&h.jsx(oe,{name:ue.Check,className:"w-4 h-4 text-blue-600 dark:text-blue-400 ml-auto"})]})]})})]})},Vu=window.G7Core?.createLogger?.("Comp:ColumnSelector")??{log:(...e)=>console.log("[Comp:ColumnSelector]",...e),warn:(...e)=>console.warn("[Comp:ColumnSelector]",...e),error:(...e)=>console.error("[Comp:ColumnSelector]",...e)},Hu=(e,t)=>window.G7Core?.t?.(e,t)??e,mx=()=>window.G7Core,bx=({id:e,columns:t,visibleColumns:n,onColumnVisibilityChange:r,triggerLabel:s,triggerIconName:o=ue.Sliders,position:i="right",className:a="",style:l,requiredText:c,menuTitle:d,menuDescription:u,defaultColumns:f,triggerClassName:g,labelClassName:m,dynamicColumns:p,dynamicColumnsInsertAfter:y,editorAttrs:b})=>{const _=s??Hu("common.column_selector"),x=c??Hu("common.required"),M=v.useMemo(()=>{if(!p||!Array.isArray(p)||p.length===0)return t;if(y){const V=t.findIndex(X=>X.field===y);if(V!==-1)return[...t.slice(0,V+1),...p,...t.slice(V+1)]}return[...t,...p]},[t,p,y]),[S,k]=v.useState(!1),N=v.useRef(null),C=v.useRef(null),[w,E]=v.useState(!1),[A,P]=v.useState(i),L=v.useMemo(()=>{try{const V=window.G7Core?.AuthManager?.getInstance();return V?V.getUser()?.uuid:void 0}catch{return}},[]),$=e?`g7_columns_${e}${L!==void 0?`_${L}`:""}`:null,D=()=>{if(!$)return null;try{const V=localStorage.getItem($);if(V){const X=JSON.parse(V);if(Array.isArray(X)&&X.length>0)return X}}catch(V){Vu.warn("Failed to load from localStorage:",V)}return null},O=V=>{if($)try{localStorage.setItem($,JSON.stringify(V))}catch(X){Vu.warn("Failed to save to localStorage:",X)}},T=()=>{const V=D();return V||(n&&n.length>0?n:f&&f.length>0?f:M.filter(X=>X.visible!==!1).map(X=>X.field))},[z,U]=v.useState(T);v.useEffect(()=>{w||(r&&z.length>0&&r(z),E(!0))},[w,r,z]),v.useEffect(()=>{w&&n&&n.length>0&&(D()||U(n))},[n,w]);const W=v.useRef([]),G=v.useRef(null);v.useEffect(()=>{if(w&&p&&p.length>0){const V=p.map(q=>q.field);if(G.current===null){const q=D();if(G.current=q!==null,q!==null){W.current=V;return}}const X=W.current,K=V.filter(q=>!X.includes(q));if(K.length>0){const q=n&&n.length>0?n:z,ne=K.filter(ee=>!q.includes(ee));if(ne.length>0){const ee=[...q,...ne];U(ee),O(ee),r?.(ee)}}W.current=V}},[p,w]);const B=n&&n.length>0?n:z;v.useEffect(()=>{const V=X=>{N.current&&!N.current.contains(X.target)&&k(!1)};return S&&document.addEventListener("mousedown",V),()=>{document.removeEventListener("mousedown",V)}},[S]),v.useEffect(()=>{if(S&&C.current&&N.current){const V=C.current.getBoundingClientRect(),X=N.current.getBoundingClientRect(),K=window.innerWidth,q=V.width||256;i==="right"?X.right-q<0?P("left"):P("right"):X.left+q>K?P("right"):P("left")}},[S,i]);const j=V=>{if(M.find(q=>q.field===V)?.required)return;const K=B.includes(V)?B.filter(q=>q!==V):[...B,V];U(K),O(K),r?.(K)},Y=A==="left"?"left-0":"right-0";return h.jsxs(I,{ref:N,className:`relative inline-block ${a}`,style:l,id:e,...b,children:[h.jsxs(ae,{onClick:()=>k(!S),className:mx()?.style?.mergeClasses?.("flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors",g)??`flex items-center gap-2 px-3 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors ${g??""}`,children:[h.jsx(oe,{name:o,className:"w-5 h-5"}),_&&h.jsx(J,{className:`${m||"hidden sm:inline"} text-sm`,children:_})]}),S&&h.jsxs(I,{ref:C,className:`absolute ${Y} z-50 mt-2 w-64 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg`,children:[(d||u)&&h.jsxs(I,{className:"px-4 pt-3 pb-2 border-b border-gray-200 dark:border-gray-700",children:[d&&h.jsx(J,{className:"block text-sm font-medium text-gray-900 dark:text-white",children:d}),u&&h.jsx(J,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-1",children:u})]}),h.jsx(I,{className:"p-2 max-h-64 overflow-y-auto",children:M.map(V=>{const X=B.includes(V.field),K=V.required===!0;return h.jsxs(I,{onClick:()=>!K&&j(V.field),className:`flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 ${K?"cursor-default":"cursor-pointer"}`,children:[h.jsx(_n,{checked:X,disabled:K,readOnly:!0,className:"h-4 w-4 text-blue-600 rounded border-gray-300 dark:border-gray-600 pointer-events-none"}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300",children:V.header}),K&&x&&h.jsx(J,{className:"ml-auto text-xs text-gray-400 dark:text-gray-500",children:x})]},V.field)})})]})]})},yx=window.G7Core?.createLogger?.("Comp:PageTransition")??{log:(...e)=>console.log("[Comp:PageTransition]",...e),warn:(...e)=>console.warn("[Comp:PageTransition]",...e),error:(...e)=>console.error("[Comp:PageTransition]",...e)},vx=({className:e="",style:t})=>{const[n,r]=v.useState(!1);return v.useEffect(()=>{const s=window.G7Core?.TransitionManager;if(!s){yx.warn("TransitionManager를 찾을 수 없습니다.");return}return r(s.getIsPending()),s.subscribe(i=>{r(i)})},[]),n?h.jsxs(I,{className:`fixed top-0 left-0 right-0 z-[999] ${e}`,style:t,role:"progressbar","aria-label":"페이지 로딩 중",children:[h.jsx(I,{className:"h-1 bg-gradient-to-r from-blue-500 via-blue-600 to-blue-500 animate-pulse",children:h.jsx(I,{className:"h-full bg-blue-700 animate-[loading_1.5s_ease-in-out_infinite]",style:{width:"30%",animation:"loading 1.5s ease-in-out infinite"}})}),h.jsx("style",{children:` @keyframes loading { 0% { transform: translateX(-100%); @@ -167,12 +168,12 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho transform: translateX(-100%); } } - `})]}):null},Rv=(t,e)=>window.G7Core?.t?.(t,e)??t,ex=({options:t})=>{const e=document.documentElement.classList.contains("dark"),n=e?"rgb(17,24,39)":"rgb(249,250,251)",r=e?"#6b7280":"#9ca3af",s=e?"#9ca3af":"#6b7280";return h.jsxs("div",{style:{position:"absolute",inset:0,zIndex:2147483647,overflow:"hidden",background:n,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",paddingTop:"15%",gap:"12px"},children:[h.jsx("div",{style:{width:"32px",height:"32px",border:`3px solid ${r}`,borderTopColor:"transparent",borderRadius:"50%",animation:"g7-spin 0.8s linear infinite"}}),h.jsx("span",{style:{color:s,fontSize:"14px"},children:t?.text||Rv("nav.loading")})]})};function gr(t){"@babel/helpers - typeof";return gr=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},gr(t)}function tx(t,e){if(gr(t)!="object"||!t)return t;var n=t[Symbol.toPrimitive];if(n!==void 0){var r=n.call(t,e);if(gr(r)!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(t)}function Ou(t){var e=tx(t,"string");return gr(e)=="symbol"?e:e+""}function Ds(t,e,n){return(e=Ou(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function Tu(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(t,s).enumerable})),n.push.apply(n,r)}return n}function Me(t){for(var e=1;et.length)&&(e=t.length);for(var n=0,r=Array(e);n0?Pt(jr,--Qt):0,Br--,Ct===10&&(Br=1,Jo--),Ct}function an(){return Ct=Qt2||$s(Ct)>3?"":" "}function Ox(t,e){for(;--e&&an()&&!(Ct<48||Ct>102||Ct>57&&Ct<65||Ct>70&&Ct<97););return Ps(t,ei()+(e<6&&In()==32&&an()==32))}function pl(t){for(;an();)switch(Ct){case t:return Qt;case 34:case 39:t!==34&&t!==39&&pl(Ct);break;case 40:t===41&&pl(t);break;case 92:an();break}return Qt}function Tx(t,e){for(;an()&&t+Ct!==57;)if(t+Ct===84&&In()===47)break;return"/*"+Ps(e,Qt-1)+"*"+Zo(t===47?t:an())}function Ax(t){for(;!$s(In());)an();return Ps(t,Qt)}function Dx(t){return Hu(ni("",null,null,null,[""],t=Vu(t),0,[0],t))}function ni(t,e,n,r,s,o,i,a,l){for(var c=0,d=0,u=i,f=0,g=0,m=0,p=1,y=1,b=1,_=0,x="",E=s,C=o,k=r,N=x;y;)switch(m=_,_=an()){case 40:if(m!=108&&Pt(N,u-1)==58){hl(N+=tt(ti(_),"&","&\f"),"&\f")!=-1&&(b=-1);break}case 34:case 39:case 91:N+=ti(_);break;case 9:case 10:case 13:case 32:N+=Nx(m);break;case 92:N+=Ox(ei()-1,7);continue;case 47:switch(In()){case 42:case 47:Qo(Ix(Tx(an(),ei()),e,n),l);break;default:N+="/"}break;case 123*p:a[c++]=Dn(N)*b;case 125*p:case 59:case 0:switch(_){case 0:case 125:y=0;case 59+d:b==-1&&(N=tt(N,/\f/g,"")),g>0&&Dn(N)-u&&Qo(g>32?Bu(N+";",r,n,u-1):Bu(tt(N," ","")+";",r,n,u-2),l);break;case 59:N+=";";default:if(Qo(k=Uu(N,e,n,c,d,s,a,x,E=[],C=[],u),o),_===123)if(d===0)ni(N,e,k,k,E,o,u,a,C);else switch(f===99&&Pt(N,3)===110?100:f){case 100:case 108:case 109:case 115:ni(t,k,k,r&&Qo(Uu(t,k,k,0,0,s,a,x,s,E=[],u),C),s,C,u,a,r?E:C);break;default:ni(N,k,k,k,[""],C,0,a,C)}}c=d=g=0,p=b=1,x=N="",u=i;break;case 58:u=1+Dn(N),g=m;default:if(p<1){if(_==123)--p;else if(_==125&&p++==0&&Mx()==125)continue}switch(N+=Zo(_),_*p){case 38:b=d>0?1:(N+="\f",-1);break;case 44:a[c++]=(Dn(N)-1)*b,b=1;break;case 64:In()===45&&(N+=ti(an())),f=In(),d=u=Dn(x=N+=Ax(ei())),_++;break;case 45:m===45&&Dn(N)==2&&(p=0)}}return o}function Uu(t,e,n,r,s,o,i,a,l,c,d){for(var u=s-1,f=s===0?o:[""],g=gl(f),m=0,p=0,y=0;m0?f[b]+" "+_:tt(_,/&\f/g,f[b])))&&(l[y++]=x);return Ro(t,e,n,s===0?ul:a,l,c,d)}function Ix(t,e,n){return Ro(t,e,n,Pu,Zo(Ex()),Is(t,2,-2),0)}function Bu(t,e,n,r){return Ro(t,e,n,fl,Is(t,0,r),Is(t,r+1,-1),r)}function Wr(t,e){for(var n="",r=gl(t),s=0;s6)switch(Pt(t,e+1)){case 109:if(Pt(t,e+4)!==45)break;case 102:return tt(t,/(.+:)(.+)-([^]+)/,"$1"+et+"$2-$3$1"+qo+(Pt(t,e+3)==108?"$3":"$2-$3"))+t;case 115:return~hl(t,"stretch")?Wu(tt(t,"stretch","fill-available"),e)+t:t}break;case 4949:if(Pt(t,e+1)!==115)break;case 6444:switch(Pt(t,Dn(t)-3-(~hl(t,"!important")&&10))){case 107:return tt(t,":",":"+et)+t;case 101:return tt(t,/(.+:)([^;!]+)(;|!.+)?/,"$1"+et+(Pt(t,14)===45?"inline-":"")+"box$3$1"+et+"$2$3$1"+Ft+"$2box$3")+t}break;case 5936:switch(Pt(t,e+11)){case 114:return et+t+Ft+tt(t,/[svh]\w+-[tblr]{2}/,"tb")+t;case 108:return et+t+Ft+tt(t,/[svh]\w+-[tblr]{2}/,"tb-rl")+t;case 45:return et+t+Ft+tt(t,/[svh]\w+-[tblr]{2}/,"lr")+t}return et+t+Ft+t+t}return t}var jx=function(e,n,r,s){if(e.length>-1&&!e.return)switch(e.type){case fl:e.return=Wu(e.value,e.length);break;case $u:return Wr([Ls(e,{value:tt(e.value,"@","@"+et)})],s);case ul:if(e.length)return _x(e.props,function(o){switch(Sx(o,/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":return Wr([Ls(e,{props:[tt(o,/:(read-\w+)/,":"+qo+"$1")]})],s);case"::placeholder":return Wr([Ls(e,{props:[tt(o,/:(plac\w+)/,":"+et+"input-$1")]}),Ls(e,{props:[tt(o,/:(plac\w+)/,":"+qo+"$1")]}),Ls(e,{props:[tt(o,/:(plac\w+)/,Ft+"input-$1")]})],s)}return""})}},Wx=[jx],Gx=function(e){var n=e.key;if(n==="css"){var r=document.querySelectorAll("style[data-emotion]:not([data-s])");Array.prototype.forEach.call(r,function(p){var y=p.getAttribute("data-emotion");y.indexOf(" ")!==-1&&(document.head.appendChild(p),p.setAttribute("data-s",""))})}var s=e.stylisPlugins||Wx,o={},i,a=[];i=e.container||document.head,Array.prototype.forEach.call(document.querySelectorAll('style[data-emotion^="'+n+' "]'),function(p){for(var y=p.getAttribute("data-emotion").split(" "),b=1;b=4;++r,s-=4)n=t.charCodeAt(r)&255|(t.charCodeAt(++r)&255)<<8|(t.charCodeAt(++r)&255)<<16|(t.charCodeAt(++r)&255)<<24,n=(n&65535)*1540483477+((n>>>16)*59797<<16),n^=n>>>24,e=(n&65535)*1540483477+((n>>>16)*59797<<16)^(e&65535)*1540483477+((e>>>16)*59797<<16);switch(s){case 3:e^=(t.charCodeAt(r+2)&255)<<16;case 2:e^=(t.charCodeAt(r+1)&255)<<8;case 1:e^=t.charCodeAt(r)&255,e=(e&65535)*1540483477+((e>>>16)*59797<<16)}return e^=e>>>13,e=(e&65535)*1540483477+((e>>>16)*59797<<16),((e^e>>>15)>>>0).toString(36)}var Rx={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,scale:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},ew=/[A-Z]|^ms/g,tw=/_EMO_([^_]+?)_([^]*?)_EMO_/g,qu=function(e){return e.charCodeAt(1)===45},Zu=function(e){return e!=null&&typeof e!="boolean"},yl=Fx(function(t){return qu(t)?t:t.replace(ew,"-$&").toLowerCase()}),Qu=function(e,n){switch(e){case"animation":case"animationName":if(typeof n=="string")return n.replace(tw,function(r,s,o){return Ln={name:s,styles:o,next:Ln},s})}return Rx[e]!==1&&!qu(e)&&typeof n=="number"&&n!==0?n+"px":n};function Fs(t,e,n){if(n==null)return"";var r=n;if(r.__emotion_styles!==void 0)return r;switch(typeof n){case"boolean":return"";case"object":{var s=n;if(s.anim===1)return Ln={name:s.name,styles:s.styles,next:Ln},s.name;var o=n;if(o.styles!==void 0){var i=o.next;if(i!==void 0)for(;i!==void 0;)Ln={name:i.name,styles:i.styles,next:Ln},i=i.next;var a=o.styles+";";return a}return nw(t,e,n)}case"function":{if(t!==void 0){var l=Ln,c=n(t);return Ln=l,Fs(t,e,c)}break}}var d=n;return d}function nw(t,e,n){var r="";if(Array.isArray(n))for(var s=0;s({x:t,y:t});function mw(t){const{x:e,y:n,width:r,height:s}=t;return{width:r,height:s,top:n,left:e,right:e+r,bottom:n+s,x:e,y:n}}function ii(){return typeof window<"u"}function tf(t){return rf(t)?(t.nodeName||"").toLowerCase():"#document"}function Wn(t){var e;return(t==null||(e=t.ownerDocument)==null?void 0:e.defaultView)||window}function nf(t){var e;return(e=(rf(t)?t.ownerDocument:t.document)||window.document)==null?void 0:e.documentElement}function rf(t){return ii()?t instanceof Node||t instanceof Wn(t).Node:!1}function bw(t){return ii()?t instanceof Element||t instanceof Wn(t).Element:!1}function kl(t){return ii()?t instanceof HTMLElement||t instanceof Wn(t).HTMLElement:!1}function sf(t){return!ii()||typeof ShadowRoot>"u"?!1:t instanceof ShadowRoot||t instanceof Wn(t).ShadowRoot}function of(t){const{overflow:e,overflowX:n,overflowY:r,display:s}=Sl(t);return/auto|scroll|overlay|hidden|clip/.test(e+r+n)&&s!=="inline"&&s!=="contents"}let Cl;function yw(){return Cl==null&&(Cl=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),Cl}function vw(t){return/^(html|body|#document)$/.test(tf(t))}function Sl(t){return Wn(t).getComputedStyle(t)}function xw(t){if(tf(t)==="html")return t;const e=t.assignedSlot||t.parentNode||sf(t)&&t.host||nf(t);return sf(e)?e.host:e}function af(t){const e=xw(t);return vw(e)?t.ownerDocument?t.ownerDocument.body:t.body:kl(e)&&of(e)?e:af(e)}function ai(t,e,n){var r;e===void 0&&(e=[]),n===void 0&&(n=!0);const s=af(t),o=s===((r=t.ownerDocument)==null?void 0:r.body),i=Wn(s);if(o){const a=_l(i);return e.concat(i,i.visualViewport||[],of(s)?s:[],a&&n?ai(a):[])}else return e.concat(s,ai(s,[],n))}function _l(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function ww(t){const e=Sl(t);let n=parseFloat(e.width)||0,r=parseFloat(e.height)||0;const s=kl(t),o=s?t.offsetWidth:n,i=s?t.offsetHeight:r,a=ri(n)!==o||ri(r)!==i;return a&&(n=o,r=i),{width:n,height:r,$:a}}function El(t){return bw(t)?t:t.contextElement}function lf(t){const e=El(t);if(!kl(e))return oi(1);const n=e.getBoundingClientRect(),{width:r,height:s,$:o}=ww(e);let i=(o?ri(n.width):n.width)/r,a=(o?ri(n.height):n.height)/s;return(!i||!Number.isFinite(i))&&(i=1),(!a||!Number.isFinite(a))&&(a=1),{x:i,y:a}}const kw=oi(0);function Cw(t){const e=Wn(t);return!yw()||!e.visualViewport?kw:{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}}function Sw(t,e,n){return!1}function cf(t,e,n,r){e===void 0&&(e=!1);const s=t.getBoundingClientRect(),o=El(t);let i=oi(1);e&&(i=lf(t));const a=Sw()?Cw(o):oi(0);let l=(s.left+a.x)/i.x,c=(s.top+a.y)/i.y,d=s.width/i.x,u=s.height/i.y;if(o){const f=Wn(o),g=r;let m=f,p=_l(m);for(;p&&r&&g!==m;){const y=lf(p),b=p.getBoundingClientRect(),_=Sl(p),x=b.left+(p.clientLeft+parseFloat(_.paddingLeft))*y.x,E=b.top+(p.clientTop+parseFloat(_.paddingTop))*y.y;l*=y.x,c*=y.y,d*=y.x,u*=y.y,l+=x,c+=E,m=Wn(p),p=_l(m)}}return mw({width:d,height:u,x:l,y:c})}function df(t,e){return t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}function _w(t,e){let n=null,r;const s=nf(t);function o(){var a;clearTimeout(r),(a=n)==null||a.disconnect(),n=null}function i(a,l){a===void 0&&(a=!1),l===void 0&&(l=1),o();const c=t.getBoundingClientRect(),{left:d,top:u,width:f,height:g}=c;if(a||e(),!f||!g)return;const m=si(u),p=si(s.clientWidth-(d+f)),y=si(s.clientHeight-(u+g)),b=si(d),x={rootMargin:-m+"px "+-p+"px "+-y+"px "+-b+"px",threshold:pw(0,gw(1,l))||1};let E=!0;function C(k){const N=k[0].intersectionRatio;if(N!==l){if(!E)return i();N?i(!1,N):r=setTimeout(()=>{i(!1,1e-7)},1e3)}N===1&&!df(c,t.getBoundingClientRect())&&i(),E=!1}try{n=new IntersectionObserver(C,{...x,root:s.ownerDocument})}catch{n=new IntersectionObserver(C,x)}n.observe(t)}return i(!0),o}function Ew(t,e,n,r){r===void 0&&(r={});const{ancestorScroll:s=!0,ancestorResize:o=!0,elementResize:i=typeof ResizeObserver=="function",layoutShift:a=typeof IntersectionObserver=="function",animationFrame:l=!1}=r,c=El(t),d=s||o?[...c?ai(c):[],...e?ai(e):[]]:[];d.forEach(b=>{s&&b.addEventListener("scroll",n,{passive:!0}),o&&b.addEventListener("resize",n)});const u=c&&a?_w(c,n):null;let f=-1,g=null;i&&(g=new ResizeObserver(b=>{let[_]=b;_&&_.target===c&&g&&e&&(g.unobserve(e),cancelAnimationFrame(f),f=requestAnimationFrame(()=>{var x;(x=g)==null||x.observe(e)})),n()}),c&&!l&&g.observe(c),e&&g.observe(e));let m,p=l?cf(t):null;l&&y();function y(){const b=cf(t);p&&!df(p,b)&&n(),p=b,m=requestAnimationFrame(y)}return n(),()=>{var b;d.forEach(_=>{s&&_.removeEventListener("scroll",n),o&&_.removeEventListener("resize",n)}),u?.(),(b=g)==null||b.disconnect(),g=null,l&&cancelAnimationFrame(m)}}var Ml=v.useLayoutEffect,Mw=["className","clearValue","cx","getStyles","getClassNames","getValue","hasValue","isMulti","isRtl","options","selectOption","selectProps","setValue","theme"],li=function(){};function Nw(t,e){return e?e[0]==="-"?t+e:t+"__"+e:t}function Ow(t,e){for(var n=arguments.length,r=new Array(n>2?n-2:0),s=2;s-1}function Tw(t){return di(t)?window.innerHeight:t.clientHeight}function ff(t){return di(t)?window.pageYOffset:t.scrollTop}function ui(t,e){if(di(t)){window.scrollTo(0,e);return}t.scrollTop=e}function Aw(t){var e=getComputedStyle(t),n=e.position==="absolute",r=/(auto|scroll)/;if(e.position==="fixed")return document.documentElement;for(var s=t;s=s.parentElement;)if(e=getComputedStyle(s),!(n&&e.position==="static")&&r.test(e.overflow+e.overflowY+e.overflowX))return s;return document.documentElement}function Dw(t,e,n,r){return n*((t=t/r-1)*t*t+1)+e}function fi(t,e){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:200,r=arguments.length>3&&arguments[3]!==void 0?arguments[3]:li,s=ff(t),o=e-s,i=10,a=0;function l(){a+=i;var c=Dw(a,s,o,n);ui(t,c),an.bottom?ui(t,Math.min(e.offsetTop+e.clientHeight-t.offsetHeight+s,t.scrollHeight)):r.top-s1?n-1:0),s=1;s=m)return{placement:"bottom",maxHeight:e};if(w>=m&&!i)return o&&fi(l,M,P),{placement:"bottom",maxHeight:e};if(!i&&w>=r||i&&N>=r){o&&fi(l,M,P);var L=i?N-E:w-E;return{placement:"bottom",maxHeight:L}}if(s==="auto"||i){var $=e,A=i?k:S;return A>=r&&($=Math.min(A-E-a,e)),{placement:"top",maxHeight:$}}if(s==="bottom")return o&&ui(l,M),{placement:"bottom",maxHeight:e};break;case"top":if(k>=m)return{placement:"top",maxHeight:e};if(S>=m&&!i)return o&&fi(l,T,P),{placement:"top",maxHeight:e};if(!i&&S>=r||i&&k>=r){var D=e;return(!i&&S>=r||i&&k>=r)&&(D=i?k-C:S-C),o&&fi(l,T,P),{placement:"top",maxHeight:D}}return{placement:"bottom",maxHeight:e};default:throw new Error('Invalid placement provided "'.concat(s,'".'))}return c}function jw(t){var e={bottom:"top",top:"bottom"};return t?e[t]:"bottom"}var mf=function(e){return e==="auto"?"bottom":e},Ww=function(e,n){var r,s=e.placement,o=e.theme,i=o.borderRadius,a=o.spacing,l=o.colors;return Me((r={label:"menu"},Ds(r,jw(s),"100%"),Ds(r,"position","absolute"),Ds(r,"width","100%"),Ds(r,"zIndex",1),r),n?{}:{backgroundColor:l.neutral0,borderRadius:i,boxShadow:"0 0 0 1px hsla(0, 0%, 0%, 0.1), 0 4px 11px hsla(0, 0%, 0%, 0.1)",marginBottom:a.menuGutter,marginTop:a.menuGutter})},bf=v.createContext(null),Gw=function(e){var n=e.children,r=e.minMenuHeight,s=e.maxMenuHeight,o=e.menuPlacement,i=e.menuPosition,a=e.menuShouldScrollIntoView,l=e.theme,c=v.useContext(bf)||{},d=c.setPortalPlacement,u=v.useRef(null),f=v.useState(s),g=jn(f,2),m=g[0],p=g[1],y=v.useState(null),b=jn(y,2),_=b[0],x=b[1],E=l.spacing.controlHeight;return Ml(function(){var C=u.current;if(C){var k=i==="fixed",N=a&&!k,S=Bw({maxHeight:s,menuEl:C,minHeight:r,placement:o,shouldScroll:N,isFixedPosition:k,controlHeight:E});p(S.maxHeight),x(S.placement),d?.(S.placement)}},[s,o,i,a,r,d,E]),n({ref:u,placerProps:Me(Me({},e),{},{placement:_||mf(o),maxHeight:m})})},Yw=function(e){var n=e.children,r=e.innerRef,s=e.innerProps;return _e("div",Ae({},vt(e,"menu",{menu:!0}),{ref:r},s),n)},Kw=Yw,Xw=function(e,n){var r=e.maxHeight,s=e.theme.spacing.baseUnit;return Me({maxHeight:r,overflowY:"auto",position:"relative",WebkitOverflowScrolling:"touch"},n?{}:{paddingBottom:s,paddingTop:s})},qw=function(e){var n=e.children,r=e.innerProps,s=e.innerRef,o=e.isMulti;return _e("div",Ae({},vt(e,"menuList",{"menu-list":!0,"menu-list--is-multi":o}),{ref:s},r),n)},yf=function(e,n){var r=e.theme,s=r.spacing.baseUnit,o=r.colors;return Me({textAlign:"center"},n?{}:{color:o.neutral40,padding:"".concat(s*2,"px ").concat(s*3,"px")})},Zw=yf,Qw=yf,Jw=function(e){var n=e.children,r=n===void 0?"No options":n,s=e.innerProps,o=An(e,Hw);return _e("div",Ae({},vt(Me(Me({},o),{},{children:r,innerProps:s}),"noOptionsMessage",{"menu-notice":!0,"menu-notice--no-options":!0}),s),r)},Rw=function(e){var n=e.children,r=n===void 0?"Loading...":n,s=e.innerProps,o=An(e,Uw);return _e("div",Ae({},vt(Me(Me({},o),{},{children:r,innerProps:s}),"loadingMessage",{"menu-notice":!0,"menu-notice--loading":!0}),s),r)},e1=function(e){var n=e.rect,r=e.offset,s=e.position;return{left:n.left,position:s,top:r,width:n.width,zIndex:1}},t1=function(e){var n=e.appendTo,r=e.children,s=e.controlElement,o=e.innerProps,i=e.menuPlacement,a=e.menuPosition,l=v.useRef(null),c=v.useRef(null),d=v.useState(mf(i)),u=jn(d,2),f=u[0],g=u[1],m=v.useMemo(function(){return{setPortalPlacement:g}},[]),p=v.useState(null),y=jn(p,2),b=y[0],_=y[1],x=v.useCallback(function(){if(s){var N=Iw(s),S=a==="fixed"?0:window.pageYOffset,w=N[f]+S;(w!==b?.offset||N.left!==b?.rect.left||N.width!==b?.rect.width)&&_({offset:w,rect:N})}},[s,a,f,b?.offset,b?.rect.left,b?.rect.width]);Ml(function(){x()},[x]);var E=v.useCallback(function(){typeof c.current=="function"&&(c.current(),c.current=null),s&&l.current&&(c.current=Ew(s,l.current,x,{elementResize:"ResizeObserver"in window}))},[s,x]);Ml(function(){E()},[E]);var C=v.useCallback(function(N){l.current=N,E()},[E]);if(!n&&a!=="fixed"||!b)return null;var k=_e("div",Ae({ref:C},vt(Me(Me({},e),{},{offset:b.offset,position:a,rect:b.rect}),"menuPortal",{"menu-portal":!0}),o),r);return _e(bf.Provider,{value:m},n?Tn.createPortal(k,n):k)},n1=function(e){var n=e.isDisabled,r=e.isRtl;return{label:"container",direction:r?"rtl":void 0,pointerEvents:n?"none":void 0,position:"relative"}},r1=function(e){var n=e.children,r=e.innerProps,s=e.isDisabled,o=e.isRtl;return _e("div",Ae({},vt(e,"container",{"--is-disabled":s,"--is-rtl":o}),r),n)},s1=function(e,n){var r=e.theme.spacing,s=e.isMulti,o=e.hasValue,i=e.selectProps.controlShouldRenderValue;return Me({alignItems:"center",display:s&&o&&i?"flex":"grid",flex:1,flexWrap:"wrap",WebkitOverflowScrolling:"touch",position:"relative",overflow:"hidden"},n?{}:{padding:"".concat(r.baseUnit/2,"px ").concat(r.baseUnit*2,"px")})},o1=function(e){var n=e.children,r=e.innerProps,s=e.isMulti,o=e.hasValue;return _e("div",Ae({},vt(e,"valueContainer",{"value-container":!0,"value-container--is-multi":s,"value-container--has-value":o}),r),n)},i1=function(){return{alignItems:"center",alignSelf:"stretch",display:"flex",flexShrink:0}},a1=function(e){var n=e.children,r=e.innerProps;return _e("div",Ae({},vt(e,"indicatorsContainer",{indicators:!0}),r),n)},vf,l1=["size"],c1=["innerProps","isRtl","size"],d1={name:"8mmkcg",styles:"display:inline-block;fill:currentColor;line-height:1;stroke:currentColor;stroke-width:0"},xf=function(e){var n=e.size,r=An(e,l1);return _e("svg",Ae({height:n,width:n,viewBox:"0 0 20 20","aria-hidden":"true",focusable:"false",css:d1},r))},Nl=function(e){return _e(xf,Ae({size:20},e),_e("path",{d:"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"}))},wf=function(e){return _e(xf,Ae({size:20},e),_e("path",{d:"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"}))},kf=function(e,n){var r=e.isFocused,s=e.theme,o=s.spacing.baseUnit,i=s.colors;return Me({label:"indicatorContainer",display:"flex",transition:"color 150ms"},n?{}:{color:r?i.neutral60:i.neutral20,padding:o*2,":hover":{color:r?i.neutral80:i.neutral40}})},u1=kf,f1=function(e){var n=e.children,r=e.innerProps;return _e("div",Ae({},vt(e,"dropdownIndicator",{indicator:!0,"dropdown-indicator":!0}),r),n||_e(wf,null))},h1=kf,g1=function(e){var n=e.children,r=e.innerProps;return _e("div",Ae({},vt(e,"clearIndicator",{indicator:!0,"clear-indicator":!0}),r),n||_e(Nl,null))},p1=function(e,n){var r=e.isDisabled,s=e.theme,o=s.spacing.baseUnit,i=s.colors;return Me({label:"indicatorSeparator",alignSelf:"stretch",width:1},n?{}:{backgroundColor:r?i.neutral10:i.neutral20,marginBottom:o*2,marginTop:o*2})},m1=function(e){var n=e.innerProps;return _e("span",Ae({},n,vt(e,"indicatorSeparator",{"indicator-separator":!0})))},b1=fw(vf||(vf=hw([` + `})]}):null},xx=(e,t)=>window.G7Core?.t?.(e,t)??e,wx=({options:e})=>{const t=document.documentElement.classList.contains("dark"),n=t?"rgb(17,24,39)":"rgb(249,250,251)",r=t?"#6b7280":"#9ca3af",s=t?"#9ca3af":"#6b7280";return h.jsxs("div",{style:{position:"absolute",inset:0,zIndex:2147483647,overflow:"hidden",background:n,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",paddingTop:"15%",gap:"12px"},children:[h.jsx("div",{style:{width:"32px",height:"32px",border:`3px solid ${r}`,borderTopColor:"transparent",borderRadius:"50%",animation:"g7-spin 0.8s linear infinite"}}),h.jsx("span",{style:{color:s,fontSize:"14px"},children:e?.text||xx("nav.loading")})]})};function kr(e){"@babel/helpers - typeof";return kr=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},kr(e)}function kx(e,t){if(kr(e)!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(kr(r)!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function Uu(e){var t=kx(e,"string");return kr(t)=="symbol"?t:t+""}function js(e,t,n){return(t=Uu(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Bu(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(s){return Object.getOwnPropertyDescriptor(e,s).enumerable})),n.push.apply(n,r)}return n}function Se(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0?Ft(rs,--Rt):0,ns--,St===10&&(ns=1,ii--),St}function dn(){return St=Rt2||Xs(St)>3?"":" "}function Zx(e,t){for(;--t&&dn()&&!(St<48||St>102||St>57&&St<65||St>70&&St<97););return Ys(e,li()+(t<6&&Vn()==32&&dn()==32))}function Sl(e){for(;dn();)switch(St){case e:return Rt;case 34:case 39:e!==34&&e!==39&&Sl(St);break;case 40:e===41&&Sl(e);break;case 92:dn();break}return Rt}function Qx(e,t){for(;dn()&&e+St!==57;)if(e+St===84&&Vn()===47)break;return"/*"+Ys(t,Rt-1)+"*"+si(e===47?e:dn())}function Jx(e){for(;!Xs(Vn());)dn();return Ys(e,Rt)}function Rx(e){return Ju(di("",null,null,null,[""],e=Qu(e),0,[0],e))}function di(e,t,n,r,s,o,i,a,l){for(var c=0,d=0,u=i,f=0,g=0,m=0,p=1,y=1,b=1,_=0,x="",M=s,S=o,k=r,N=x;y;)switch(m=_,_=dn()){case 40:if(m!=108&&Ft(N,u-1)==58){kl(N+=et(ci(_),"&","&\f"),"&\f")!=-1&&(b=-1);break}case 34:case 39:case 91:N+=ci(_);break;case 9:case 10:case 13:case 32:N+=qx(m);break;case 92:N+=Zx(li()-1,7);continue;case 47:switch(Vn()){case 42:case 47:oi(ew(Qx(dn(),li()),t,n),l);break;default:N+="/"}break;case 123*p:a[c++]=zn(N)*b;case 125*p:case 59:case 0:switch(_){case 0:case 125:y=0;case 59+d:b==-1&&(N=et(N,/\f/g,"")),g>0&&zn(N)-u&&oi(g>32?ef(N+";",r,n,u-1):ef(et(N," ","")+";",r,n,u-2),l);break;case 59:N+=";";default:if(oi(k=Ru(N,t,n,c,d,s,a,x,M=[],S=[],u),o),_===123)if(d===0)di(N,t,k,k,M,o,u,a,S);else switch(f===99&&Ft(N,3)===110?100:f){case 100:case 108:case 109:case 115:di(e,k,k,r&&oi(Ru(e,k,k,0,0,s,a,x,s,M=[],u),S),s,S,u,a,r?M:S);break;default:di(N,k,k,k,[""],S,0,a,S)}}c=d=g=0,p=b=1,x=N="",u=i;break;case 58:u=1+zn(N),g=m;default:if(p<1){if(_==123)--p;else if(_==125&&p++==0&&Kx()==125)continue}switch(N+=si(_),_*p){case 38:b=d>0?1:(N+="\f",-1);break;case 44:a[c++]=(zn(N)-1)*b,b=1;break;case 64:Vn()===45&&(N+=ci(dn())),f=Vn(),d=u=zn(x=N+=Jx(li())),_++;break;case 45:m===45&&zn(N)==2&&(p=0)}}return o}function Ru(e,t,n,r,s,o,i,a,l,c,d){for(var u=s-1,f=s===0?o:[""],g=Cl(f),m=0,p=0,y=0;m0?f[b]+" "+_:et(_,/&\f/g,f[b])))&&(l[y++]=x);return ai(e,t,n,s===0?xl:a,l,c,d)}function ew(e,t,n){return ai(e,t,n,Xu,si(Xx()),Ws(e,2,-2),0)}function ef(e,t,n,r){return ai(e,t,n,wl,Ws(e,0,r),Ws(e,r+1,-1),r)}function ss(e,t){for(var n="",r=Cl(e),s=0;s6)switch(Ft(e,t+1)){case 109:if(Ft(e,t+4)!==45)break;case 102:return et(e,/(.+:)(.+)-([^]+)/,"$1"+Re+"$2-$3$1"+ri+(Ft(e,t+3)==108?"$3":"$2-$3"))+e;case 115:return~kl(e,"stretch")?nf(et(e,"stretch","fill-available"),t)+e:e}break;case 4949:if(Ft(e,t+1)!==115)break;case 6444:switch(Ft(e,zn(e)-3-(~kl(e,"!important")&&10))){case 107:return et(e,":",":"+Re)+e;case 101:return et(e,/(.+:)([^;!]+)(;|!.+)?/,"$1"+Re+(Ft(e,14)===45?"inline-":"")+"box$3$1"+Re+"$2$3$1"+Ht+"$2box$3")+e}break;case 5936:switch(Ft(e,t+11)){case 114:return Re+e+Ht+et(e,/[svh]\w+-[tblr]{2}/,"tb")+e;case 108:return Re+e+Ht+et(e,/[svh]\w+-[tblr]{2}/,"tb-rl")+e;case 45:return Re+e+Ht+et(e,/[svh]\w+-[tblr]{2}/,"lr")+e}return Re+e+Ht+e+e}return e}var dw=function(t,n,r,s){if(t.length>-1&&!t.return)switch(t.type){case wl:t.return=nf(t.value,t.length);break;case Ku:return ss([Gs(t,{value:et(t.value,"@","@"+Re)})],s);case xl:if(t.length)return Yx(t.props,function(o){switch(Gx(o,/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":return ss([Gs(t,{props:[et(o,/:(read-\w+)/,":"+ri+"$1")]})],s);case"::placeholder":return ss([Gs(t,{props:[et(o,/:(plac\w+)/,":"+Re+"input-$1")]}),Gs(t,{props:[et(o,/:(plac\w+)/,":"+ri+"$1")]}),Gs(t,{props:[et(o,/:(plac\w+)/,Ht+"input-$1")]})],s)}return""})}},uw=[dw],fw=function(t){var n=t.key;if(n==="css"){var r=document.querySelectorAll("style[data-emotion]:not([data-s])");Array.prototype.forEach.call(r,function(p){var y=p.getAttribute("data-emotion");y.indexOf(" ")!==-1&&(document.head.appendChild(p),p.setAttribute("data-s",""))})}var s=t.stylisPlugins||uw,o={},i,a=[];i=t.container||document.head,Array.prototype.forEach.call(document.querySelectorAll('style[data-emotion^="'+n+' "]'),function(p){for(var y=p.getAttribute("data-emotion").split(" "),b=1;b=4;++r,s-=4)n=e.charCodeAt(r)&255|(e.charCodeAt(++r)&255)<<8|(e.charCodeAt(++r)&255)<<16|(e.charCodeAt(++r)&255)<<24,n=(n&65535)*1540483477+((n>>>16)*59797<<16),n^=n>>>24,t=(n&65535)*1540483477+((n>>>16)*59797<<16)^(t&65535)*1540483477+((t>>>16)*59797<<16);switch(s){case 3:t^=(e.charCodeAt(r+2)&255)<<16;case 2:t^=(e.charCodeAt(r+1)&255)<<8;case 1:t^=e.charCodeAt(r)&255,t=(t&65535)*1540483477+((t>>>16)*59797<<16)}return t^=t>>>13,t=(t&65535)*1540483477+((t>>>16)*59797<<16),((t^t>>>15)>>>0).toString(36)}var xw={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,scale:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},ww=/[A-Z]|^ms/g,kw=/_EMO_([^_]+?)_([^]*?)_EMO_/g,lf=function(t){return t.charCodeAt(1)===45},cf=function(t){return t!=null&&typeof t!="boolean"},Ml=sw(function(e){return lf(e)?e:e.replace(ww,"-$&").toLowerCase()}),df=function(t,n){switch(t){case"animation":case"animationName":if(typeof n=="string")return n.replace(kw,function(r,s,o){return Hn={name:s,styles:o,next:Hn},s})}return xw[t]!==1&&!lf(t)&&typeof n=="number"&&n!==0?n+"px":n};function Ks(e,t,n){if(n==null)return"";var r=n;if(r.__emotion_styles!==void 0)return r;switch(typeof n){case"boolean":return"";case"object":{var s=n;if(s.anim===1)return Hn={name:s.name,styles:s.styles,next:Hn},s.name;var o=n;if(o.styles!==void 0){var i=o.next;if(i!==void 0)for(;i!==void 0;)Hn={name:i.name,styles:i.styles,next:Hn},i=i.next;var a=o.styles+";";return a}return Cw(e,t,n)}case"function":{if(e!==void 0){var l=Hn,c=n(e);return Hn=l,Ks(e,t,c)}break}}var d=n;return d}function Cw(e,t,n){var r="";if(Array.isArray(n))for(var s=0;s({x:e,y:e});function Fw(e){const{x:t,y:n,width:r,height:s}=e;return{width:r,height:s,top:n,left:t,right:t+r,bottom:n+s,x:t,y:n}}function hi(){return typeof window<"u"}function gf(e){return mf(e)?(e.nodeName||"").toLowerCase():"#document"}function Cr(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function pf(e){var t;return(t=(mf(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function mf(e){return hi()?e instanceof Node||e instanceof Cr(e).Node:!1}function zw(e){return hi()?e instanceof Element||e instanceof Cr(e).Element:!1}function Dl(e){return hi()?e instanceof HTMLElement||e instanceof Cr(e).HTMLElement:!1}function bf(e){return!hi()||typeof ShadowRoot>"u"?!1:e instanceof ShadowRoot||e instanceof Cr(e).ShadowRoot}function yf(e){const{overflow:t,overflowX:n,overflowY:r,display:s}=vf(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&s!=="inline"&&s!=="contents"}function Vw(e){return/^(html|body|#document)$/.test(gf(e))}function vf(e){return Cr(e).getComputedStyle(e)}function Hw(e){if(gf(e)==="html")return e;const t=e.assignedSlot||e.parentNode||bf(e)&&e.host||pf(e);return bf(t)?t.host:t}function xf(e){const t=Hw(e);return Vw(t)?(e.ownerDocument||e).body:Dl(t)&&yf(t)?t:xf(t)}function gi(e,t,n){var r;t===void 0&&(t=[]),n===void 0&&(n=!0);const s=xf(e),o=s===((r=e.ownerDocument)==null?void 0:r.body),i=Cr(s);if(o){const a=Uw(i);return t.concat(i,i.visualViewport||[],yf(s)?s:[],a&&n?gi(a):[])}else return t.concat(s,gi(s,[],n))}function Uw(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function Bw(e){const t=vf(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const s=Dl(e),o=s?e.offsetWidth:n,i=s?e.offsetHeight:r,a=ui(n)!==o||ui(r)!==i;return a&&(n=o,r=i),{width:n,height:r,$:a}}function Il(e){return zw(e)?e:e.contextElement}function jw(e){const t=Il(e);if(!Dl(t))return Al(1);const n=t.getBoundingClientRect(),{width:r,height:s,$:o}=Bw(t);let i=(o?ui(n.width):n.width)/r,a=(o?ui(n.height):n.height)/s;return(!i||!Number.isFinite(i))&&(i=1),(!a||!Number.isFinite(a))&&(a=1),{x:i,y:a}}function wf(e,t,n,r){t===void 0&&(t=!1);const s=e.getBoundingClientRect();Il(e);let o=Al(1);t&&(o=jw(e));const i=Al(0);let a=(s.left+i.x)/o.x,l=(s.top+i.y)/o.y,c=s.width/o.x,d=s.height/o.y;return Fw({width:c,height:d,x:a,y:l})}function kf(e,t){return e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height}function Ww(e,t,n){let r=null,s;const o=pf(e);function i(){var d;clearTimeout(s),(d=r)==null||d.disconnect(),r=null}function a(d,u){d===void 0&&(d=!1),u===void 0&&(u=1),i();const f=e.getBoundingClientRect(),{left:g,top:m,width:p,height:y}=f;if(d||t(),!p||!y)return;const b=fi(m),_=fi(o.clientWidth-(g+p)),x=fi(o.clientHeight-(m+y)),M=fi(g),k={rootMargin:-b+"px "+-_+"px "+-x+"px "+-M+"px",threshold:$w(0,Pw(1,u))||1};let N=!0;function C(w){const E=w[0].intersectionRatio;if(!kf(f,e.getBoundingClientRect()))return a();if(E!==u){if(!N)return a();E?a(!1,E):s=setTimeout(()=>{a(!1,1e-7)},1e3)}N=!1}try{r=new IntersectionObserver(C,{...k,root:o.ownerDocument})}catch{r=new IntersectionObserver(C,k)}r.observe(e)}const l=Cr(e),c=()=>a(n);return l.addEventListener("resize",c),a(!0),()=>{l.removeEventListener("resize",c),i()}}function Gw(e,t,n,r){r===void 0&&(r={});const{ancestorScroll:s=!0,ancestorResize:o=!0,elementResize:i=typeof ResizeObserver=="function",layoutShift:a=typeof IntersectionObserver=="function",animationFrame:l=!1}=r,c=Il(e),d=s||o?[...c?gi(c):[],...t?gi(t):[]]:[];d.forEach(b=>{s&&b.addEventListener("scroll",n),o&&b.addEventListener("resize",n)});const u=c&&a?Ww(c,n,o):null;let f=-1,g=null;i&&(g=new ResizeObserver(b=>{let[_]=b;_&&_.target===c&&g&&t&&(g.unobserve(t),cancelAnimationFrame(f),f=requestAnimationFrame(()=>{var x;(x=g)==null||x.observe(t)})),n()}),c&&!l&&g.observe(c),t&&g.observe(t));let m,p=l?wf(e):null;l&&y();function y(){const b=wf(e);p&&!kf(p,b)&&n(),p=b,m=requestAnimationFrame(y)}return n(),()=>{var b;d.forEach(_=>{s&&_.removeEventListener("scroll",n),o&&_.removeEventListener("resize",n)}),u?.(),(b=g)==null||b.disconnect(),g=null,l&&cancelAnimationFrame(m)}}var Ll=v.useLayoutEffect,Yw=["className","clearValue","cx","getStyles","getClassNames","getValue","hasValue","isMulti","isRtl","options","selectOption","selectProps","setValue","theme"],pi=function(){};function Xw(e,t){return t?t[0]==="-"?e+t:e+"__"+t:e}function Kw(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),s=2;s-1}function qw(e){return bi(e)?window.innerHeight:e.clientHeight}function Sf(e){return bi(e)?window.pageYOffset:e.scrollTop}function yi(e,t){if(bi(e)){window.scrollTo(0,t);return}e.scrollTop=t}function Zw(e){var t=getComputedStyle(e),n=t.position==="absolute",r=/(auto|scroll)/;if(t.position==="fixed")return document.documentElement;for(var s=e;s=s.parentElement;)if(t=getComputedStyle(s),!(n&&t.position==="static")&&r.test(t.overflow+t.overflowY+t.overflowX))return s;return document.documentElement}function Qw(e,t,n,r){return n*((e=e/r-1)*e*e+1)+t}function vi(e,t){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:200,r=arguments.length>3&&arguments[3]!==void 0?arguments[3]:pi,s=Sf(e),o=t-s,i=10,a=0;function l(){a+=i;var c=Qw(a,s,o,n);yi(e,c),an.bottom?yi(e,Math.min(t.offsetTop+t.clientHeight-e.offsetHeight+s,e.scrollHeight)):r.top-s1?n-1:0),s=1;s=m)return{placement:"bottom",maxHeight:t};if(w>=m&&!i)return o&&vi(l,E,P),{placement:"bottom",maxHeight:t};if(!i&&w>=r||i&&N>=r){o&&vi(l,E,P);var L=i?N-M:w-M;return{placement:"bottom",maxHeight:L}}if(s==="auto"||i){var $=t,D=i?k:C;return D>=r&&($=Math.min(D-M-a,t)),{placement:"top",maxHeight:$}}if(s==="bottom")return o&&yi(l,E),{placement:"bottom",maxHeight:t};break;case"top":if(k>=m)return{placement:"top",maxHeight:t};if(C>=m&&!i)return o&&vi(l,A,P),{placement:"top",maxHeight:t};if(!i&&C>=r||i&&k>=r){var O=t;return(!i&&C>=r||i&&k>=r)&&(O=i?k-S:C-S),o&&vi(l,A,P),{placement:"top",maxHeight:O}}return{placement:"bottom",maxHeight:t};default:throw new Error('Invalid placement provided "'.concat(s,'".'))}return c}function l1(e){var t={bottom:"top",top:"bottom"};return e?t[e]:"bottom"}var Nf=function(t){return t==="auto"?"bottom":t},c1=function(t,n){var r,s=t.placement,o=t.theme,i=o.borderRadius,a=o.spacing,l=o.colors;return Se((r={label:"menu"},js(r,l1(s),"100%"),js(r,"position","absolute"),js(r,"width","100%"),js(r,"zIndex",1),r),n?{}:{backgroundColor:l.neutral0,borderRadius:i,boxShadow:"0 0 0 1px hsla(0, 0%, 0%, 0.1), 0 4px 11px hsla(0, 0%, 0%, 0.1)",marginBottom:a.menuGutter,marginTop:a.menuGutter})},Tf=v.createContext(null),d1=function(t){var n=t.children,r=t.minMenuHeight,s=t.maxMenuHeight,o=t.menuPlacement,i=t.menuPosition,a=t.menuShouldScrollIntoView,l=t.theme,c=v.useContext(Tf)||{},d=c.setPortalPlacement,u=v.useRef(null),f=v.useState(s),g=Kn(f,2),m=g[0],p=g[1],y=v.useState(null),b=Kn(y,2),_=b[0],x=b[1],M=l.spacing.controlHeight;return Ll(function(){var S=u.current;if(S){var k=i==="fixed",N=a&&!k,C=a1({maxHeight:s,menuEl:S,minHeight:r,placement:o,shouldScroll:N,isFixedPosition:k,controlHeight:M});p(C.maxHeight),x(C.placement),d?.(C.placement)}},[s,o,i,a,r,d,M]),n({ref:u,placerProps:Se(Se({},t),{},{placement:_||Nf(o),maxHeight:m})})},u1=function(t){var n=t.children,r=t.innerRef,s=t.innerProps;return Ce("div",Oe({},xt(t,"menu",{menu:!0}),{ref:r},s),n)},f1=u1,h1=function(t,n){var r=t.maxHeight,s=t.theme.spacing.baseUnit;return Se({maxHeight:r,overflowY:"auto",position:"relative",WebkitOverflowScrolling:"touch"},n?{}:{paddingBottom:s,paddingTop:s})},g1=function(t){var n=t.children,r=t.innerProps,s=t.innerRef,o=t.isMulti;return Ce("div",Oe({},xt(t,"menuList",{"menu-list":!0,"menu-list--is-multi":o}),{ref:s},r),n)},Of=function(t,n){var r=t.theme,s=r.spacing.baseUnit,o=r.colors;return Se({textAlign:"center"},n?{}:{color:o.neutral40,padding:"".concat(s*2,"px ").concat(s*3,"px")})},p1=Of,m1=Of,b1=function(t){var n=t.children,r=n===void 0?"No options":n,s=t.innerProps,o=Fn(t,o1);return Ce("div",Oe({},xt(Se(Se({},o),{},{children:r,innerProps:s}),"noOptionsMessage",{"menu-notice":!0,"menu-notice--no-options":!0}),s),r)},y1=function(t){var n=t.children,r=n===void 0?"Loading...":n,s=t.innerProps,o=Fn(t,i1);return Ce("div",Oe({},xt(Se(Se({},o),{},{children:r,innerProps:s}),"loadingMessage",{"menu-notice":!0,"menu-notice--loading":!0}),s),r)},v1=function(t){var n=t.rect,r=t.offset,s=t.position;return{left:n.left,position:s,top:r,width:n.width,zIndex:1}},x1=function(t){var n=t.appendTo,r=t.children,s=t.controlElement,o=t.innerProps,i=t.menuPlacement,a=t.menuPosition,l=v.useRef(null),c=v.useRef(null),d=v.useState(Nf(i)),u=Kn(d,2),f=u[0],g=u[1],m=v.useMemo(function(){return{setPortalPlacement:g}},[]),p=v.useState(null),y=Kn(p,2),b=y[0],_=y[1],x=v.useCallback(function(){if(s){var N=Jw(s),C=a==="fixed"?0:window.pageYOffset,w=N[f]+C;(w!==b?.offset||N.left!==b?.rect.left||N.width!==b?.rect.width)&&_({offset:w,rect:N})}},[s,a,f,b?.offset,b?.rect.left,b?.rect.width]);Ll(function(){x()},[x]);var M=v.useCallback(function(){typeof c.current=="function"&&(c.current(),c.current=null),s&&l.current&&(c.current=Gw(s,l.current,x,{elementResize:"ResizeObserver"in window}))},[s,x]);Ll(function(){M()},[M]);var S=v.useCallback(function(N){l.current=N,M()},[M]);if(!n&&a!=="fixed"||!b)return null;var k=Ce("div",Oe({ref:S},xt(Se(Se({},t),{},{offset:b.offset,position:a,rect:b.rect}),"menuPortal",{"menu-portal":!0}),o),r);return Ce(Tf.Provider,{value:m},n?Ln.createPortal(k,n):k)},w1=function(t){var n=t.isDisabled,r=t.isRtl;return{label:"container",direction:r?"rtl":void 0,pointerEvents:n?"none":void 0,position:"relative"}},k1=function(t){var n=t.children,r=t.innerProps,s=t.isDisabled,o=t.isRtl;return Ce("div",Oe({},xt(t,"container",{"--is-disabled":s,"--is-rtl":o}),r),n)},C1=function(t,n){var r=t.theme.spacing,s=t.isMulti,o=t.hasValue,i=t.selectProps.controlShouldRenderValue;return Se({alignItems:"center",display:s&&o&&i?"flex":"grid",flex:1,flexWrap:"wrap",WebkitOverflowScrolling:"touch",position:"relative",overflow:"hidden"},n?{}:{padding:"".concat(r.baseUnit/2,"px ").concat(r.baseUnit*2,"px")})},S1=function(t){var n=t.children,r=t.innerProps,s=t.isMulti,o=t.hasValue;return Ce("div",Oe({},xt(t,"valueContainer",{"value-container":!0,"value-container--is-multi":s,"value-container--has-value":o}),r),n)},_1=function(){return{alignItems:"center",alignSelf:"stretch",display:"flex",flexShrink:0}},E1=function(t){var n=t.children,r=t.innerProps;return Ce("div",Oe({},xt(t,"indicatorsContainer",{indicators:!0}),r),n)},Af,M1=["size"],N1=["innerProps","isRtl","size"],T1={name:"8mmkcg",styles:"display:inline-block;fill:currentColor;line-height:1;stroke:currentColor;stroke-width:0"},Df=function(t){var n=t.size,r=Fn(t,M1);return Ce("svg",Oe({height:n,width:n,viewBox:"0 0 20 20","aria-hidden":"true",focusable:"false",css:T1},r))},Pl=function(t){return Ce(Df,Oe({size:20},t),Ce("path",{d:"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"}))},If=function(t){return Ce(Df,Oe({size:20},t),Ce("path",{d:"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"}))},Lf=function(t,n){var r=t.isFocused,s=t.theme,o=s.spacing.baseUnit,i=s.colors;return Se({label:"indicatorContainer",display:"flex",transition:"color 150ms"},n?{}:{color:r?i.neutral60:i.neutral20,padding:o*2,":hover":{color:r?i.neutral80:i.neutral40}})},O1=Lf,A1=function(t){var n=t.children,r=t.innerProps;return Ce("div",Oe({},xt(t,"dropdownIndicator",{indicator:!0,"dropdown-indicator":!0}),r),n||Ce(If,null))},D1=Lf,I1=function(t){var n=t.children,r=t.innerProps;return Ce("div",Oe({},xt(t,"clearIndicator",{indicator:!0,"clear-indicator":!0}),r),n||Ce(Pl,null))},L1=function(t,n){var r=t.isDisabled,s=t.theme,o=s.spacing.baseUnit,i=s.colors;return Se({label:"indicatorSeparator",alignSelf:"stretch",width:1},n?{}:{backgroundColor:r?i.neutral10:i.neutral20,marginBottom:o*2,marginTop:o*2})},P1=function(t){var n=t.innerProps;return Ce("span",Oe({},n,xt(t,"indicatorSeparator",{"indicator-separator":!0})))},$1=Iw(Af||(Af=Lw([` 0%, 80%, 100% { opacity: 0; } 40% { opacity: 1; } -`]))),y1=function(e,n){var r=e.isFocused,s=e.size,o=e.theme,i=o.colors,a=o.spacing.baseUnit;return Me({label:"loadingIndicator",display:"flex",transition:"color 150ms",alignSelf:"center",fontSize:s,lineHeight:1,marginRight:s,textAlign:"center",verticalAlign:"middle"},n?{}:{color:r?i.neutral60:i.neutral20,padding:a*2})},Ol=function(e){var n=e.delay,r=e.offset;return _e("span",{css:wl({animation:"".concat(b1," 1s ease-in-out ").concat(n,"ms infinite;"),backgroundColor:"currentColor",borderRadius:"1em",display:"inline-block",marginLeft:r?"1em":void 0,height:"1em",verticalAlign:"top",width:"1em"},"","")})},v1=function(e){var n=e.innerProps,r=e.isRtl,s=e.size,o=s===void 0?4:s,i=An(e,c1);return _e("div",Ae({},vt(Me(Me({},i),{},{innerProps:n,isRtl:r,size:o}),"loadingIndicator",{indicator:!0,"loading-indicator":!0}),n),_e(Ol,{delay:0,offset:r}),_e(Ol,{delay:160,offset:!0}),_e(Ol,{delay:320,offset:!r}))},x1=function(e,n){var r=e.isDisabled,s=e.isFocused,o=e.theme,i=o.colors,a=o.borderRadius,l=o.spacing;return Me({label:"control",alignItems:"center",cursor:"default",display:"flex",flexWrap:"wrap",justifyContent:"space-between",minHeight:l.controlHeight,outline:"0 !important",position:"relative",transition:"all 100ms"},n?{}:{backgroundColor:r?i.neutral5:i.neutral0,borderColor:r?i.neutral10:s?i.primary:i.neutral20,borderRadius:a,borderStyle:"solid",borderWidth:1,boxShadow:s?"0 0 0 1px ".concat(i.primary):void 0,"&:hover":{borderColor:s?i.primary:i.neutral30}})},w1=function(e){var n=e.children,r=e.isDisabled,s=e.isFocused,o=e.innerRef,i=e.innerProps,a=e.menuIsOpen;return _e("div",Ae({ref:o},vt(e,"control",{control:!0,"control--is-disabled":r,"control--is-focused":s,"control--menu-is-open":a}),i,{"aria-disabled":r||void 0}),n)},k1=w1,C1=["data"],S1=function(e,n){var r=e.theme.spacing;return n?{}:{paddingBottom:r.baseUnit*2,paddingTop:r.baseUnit*2}},_1=function(e){var n=e.children,r=e.cx,s=e.getStyles,o=e.getClassNames,i=e.Heading,a=e.headingProps,l=e.innerProps,c=e.label,d=e.theme,u=e.selectProps;return _e("div",Ae({},vt(e,"group",{group:!0}),l),_e(i,Ae({},a,{selectProps:u,theme:d,getStyles:s,getClassNames:o,cx:r}),c),_e("div",null,n))},E1=function(e,n){var r=e.theme,s=r.colors,o=r.spacing;return Me({label:"group",cursor:"default",display:"block"},n?{}:{color:s.neutral40,fontSize:"75%",fontWeight:500,marginBottom:"0.25em",paddingLeft:o.baseUnit*3,paddingRight:o.baseUnit*3,textTransform:"uppercase"})},M1=function(e){var n=uf(e);n.data;var r=An(n,C1);return _e("div",Ae({},vt(e,"groupHeading",{"group-heading":!0}),r))},N1=_1,O1=["innerRef","isDisabled","isHidden","inputClassName"],T1=function(e,n){var r=e.isDisabled,s=e.value,o=e.theme,i=o.spacing,a=o.colors;return Me(Me({visibility:r?"hidden":"visible",transform:s?"translateZ(0)":""},A1),n?{}:{margin:i.baseUnit/2,paddingBottom:i.baseUnit/2,paddingTop:i.baseUnit/2,color:a.neutral80})},Cf={gridArea:"1 / 2",font:"inherit",minWidth:"2px",border:0,margin:0,outline:0,padding:0},A1={flex:"1 1 auto",display:"inline-grid",gridArea:"1 / 1 / 2 / 3",gridTemplateColumns:"0 min-content","&:after":Me({content:'attr(data-value) " "',visibility:"hidden",whiteSpace:"pre"},Cf)},D1=function(e){return Me({label:"input",color:"inherit",background:0,opacity:e?0:1,width:"100%"},Cf)},I1=function(e){var n=e.cx,r=e.value,s=uf(e),o=s.innerRef,i=s.isDisabled,a=s.isHidden,l=s.inputClassName,c=An(s,O1);return _e("div",Ae({},vt(e,"input",{"input-container":!0}),{"data-value":r||""}),_e("input",Ae({className:n({input:!0},l),ref:o,style:D1(a),disabled:i},c)))},L1=I1,P1=function(e,n){var r=e.theme,s=r.spacing,o=r.borderRadius,i=r.colors;return Me({label:"multiValue",display:"flex",minWidth:0},n?{}:{backgroundColor:i.neutral10,borderRadius:o/2,margin:s.baseUnit/2})},$1=function(e,n){var r=e.theme,s=r.borderRadius,o=r.colors,i=e.cropWithEllipsis;return Me({overflow:"hidden",textOverflow:i||i===void 0?"ellipsis":void 0,whiteSpace:"nowrap"},n?{}:{borderRadius:s/2,color:o.neutral80,fontSize:"85%",padding:3,paddingLeft:6})},F1=function(e,n){var r=e.theme,s=r.spacing,o=r.borderRadius,i=r.colors,a=e.isFocused;return Me({alignItems:"center",display:"flex"},n?{}:{borderRadius:o/2,backgroundColor:a?i.dangerLight:void 0,paddingLeft:s.baseUnit,paddingRight:s.baseUnit,":hover":{backgroundColor:i.dangerLight,color:i.danger}})},Sf=function(e){var n=e.children,r=e.innerProps;return _e("div",r,n)},z1=Sf,V1=Sf;function H1(t){var e=t.children,n=t.innerProps;return _e("div",Ae({role:"button"},n),e||_e(Nl,{size:14}))}var U1=function(e){var n=e.children,r=e.components,s=e.data,o=e.innerProps,i=e.isDisabled,a=e.removeProps,l=e.selectProps,c=r.Container,d=r.Label,u=r.Remove;return _e(c,{data:s,innerProps:Me(Me({},vt(e,"multiValue",{"multi-value":!0,"multi-value--is-disabled":i})),o),selectProps:l},_e(d,{data:s,innerProps:Me({},vt(e,"multiValueLabel",{"multi-value__label":!0})),selectProps:l},n),_e(u,{data:s,innerProps:Me(Me({},vt(e,"multiValueRemove",{"multi-value__remove":!0})),{},{"aria-label":"Remove ".concat(n||"option")},a),selectProps:l}))},B1=U1,j1=function(e,n){var r=e.isDisabled,s=e.isFocused,o=e.isSelected,i=e.theme,a=i.spacing,l=i.colors;return Me({label:"option",cursor:"default",display:"block",fontSize:"inherit",width:"100%",userSelect:"none",WebkitTapHighlightColor:"rgba(0, 0, 0, 0)"},n?{}:{backgroundColor:o?l.primary:s?l.primary25:"transparent",color:r?l.neutral20:o?l.neutral0:"inherit",padding:"".concat(a.baseUnit*2,"px ").concat(a.baseUnit*3,"px"),":active":{backgroundColor:r?void 0:o?l.primary:l.primary50}})},W1=function(e){var n=e.children,r=e.isDisabled,s=e.isFocused,o=e.isSelected,i=e.innerRef,a=e.innerProps;return _e("div",Ae({},vt(e,"option",{option:!0,"option--is-disabled":r,"option--is-focused":s,"option--is-selected":o}),{ref:i,"aria-disabled":r},a),n)},G1=W1,Y1=function(e,n){var r=e.theme,s=r.spacing,o=r.colors;return Me({label:"placeholder",gridArea:"1 / 1 / 2 / 3"},n?{}:{color:o.neutral50,marginLeft:s.baseUnit/2,marginRight:s.baseUnit/2})},K1=function(e){var n=e.children,r=e.innerProps;return _e("div",Ae({},vt(e,"placeholder",{placeholder:!0}),r),n)},X1=K1,q1=function(e,n){var r=e.isDisabled,s=e.theme,o=s.spacing,i=s.colors;return Me({label:"singleValue",gridArea:"1 / 1 / 2 / 3",maxWidth:"100%",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},n?{}:{color:r?i.neutral40:i.neutral80,marginLeft:o.baseUnit/2,marginRight:o.baseUnit/2})},Z1=function(e){var n=e.children,r=e.isDisabled,s=e.innerProps;return _e("div",Ae({},vt(e,"singleValue",{"single-value":!0,"single-value--is-disabled":r}),s),n)},Q1=Z1,gi={ClearIndicator:g1,Control:k1,DropdownIndicator:f1,DownChevron:wf,CrossIcon:Nl,Group:N1,GroupHeading:M1,IndicatorsContainer:a1,IndicatorSeparator:m1,Input:L1,LoadingIndicator:v1,Menu:Kw,MenuList:qw,MenuPortal:t1,LoadingMessage:Rw,NoOptionsMessage:Jw,MultiValue:B1,MultiValueContainer:z1,MultiValueLabel:V1,MultiValueRemove:H1,Option:G1,Placeholder:X1,SelectContainer:r1,SingleValue:Q1,ValueContainer:o1},J1=function(e){return Me(Me({},gi),e.components)},_f=Number.isNaN||function(e){return typeof e=="number"&&e!==e};function R1(t,e){return!!(t===e||_f(t)&&_f(e))}function ek(t,e){if(t.length!==e.length)return!1;for(var n=0;n1?"s":""," ").concat(o.join(","),", selected.");case"select-option":return i?"option ".concat(s," is disabled. Select another option."):"option ".concat(s,", selected.");default:return""}},onFocus:function(e){var n=e.context,r=e.focused,s=e.options,o=e.label,i=o===void 0?"":o,a=e.selectValue,l=e.isDisabled,c=e.isSelected,d=e.isAppleDevice,u=function(p,y){return p&&p.length?"".concat(p.indexOf(y)+1," of ").concat(p.length):""};if(n==="value"&&a)return"value ".concat(i," focused, ").concat(u(a,r),".");if(n==="menu"&&d){var f=l?" disabled":"",g="".concat(c?" selected":"").concat(f);return"".concat(i).concat(g,", ").concat(u(s,r),".")}return""},onFilter:function(e){var n=e.inputValue,r=e.resultsMessage;return"".concat(r).concat(n?" for search term "+n:"",".")}},ok=function(e){var n=e.ariaSelection,r=e.focusedOption,s=e.focusedValue,o=e.focusableOptions,i=e.isFocused,a=e.selectValue,l=e.selectProps,c=e.id,d=e.isAppleDevice,u=l.ariaLiveMessages,f=l.getOptionLabel,g=l.inputValue,m=l.isMulti,p=l.isOptionDisabled,y=l.isSearchable,b=l.menuIsOpen,_=l.options,x=l.screenReaderStatus,E=l.tabSelectsValue,C=l.isLoading,k=l["aria-label"],N=l["aria-live"],S=v.useMemo(function(){return Me(Me({},sk),u||{})},[u]),w=v.useMemo(function(){var A="";if(n&&S.onChange){var D=n.option,O=n.options,z=n.removedValue,H=n.removedValues,j=n.value,W=function(Z){return Array.isArray(Z)?null:Z},U=z||D||W(j),B=U?f(U):"",G=O||H||void 0,V=G?G.map(f):[],X=Me({isDisabled:U&&p(U,a),label:B,labels:V},n);A=S.onChange(X)}return A},[n,S,p,a,f]),M=v.useMemo(function(){var A="",D=r||s,O=!!(r&&a&&a.includes(r));if(D&&S.onFocus){var z={focused:D,label:f(D),isDisabled:p(D,a),isSelected:O,options:o,context:D===r?"menu":"value",selectValue:a,isAppleDevice:d};A=S.onFocus(z)}return A},[r,s,f,p,S,o,a,d]),T=v.useMemo(function(){var A="";if(b&&_.length&&!C&&S.onFilter){var D=x({count:o.length});A=S.onFilter({inputValue:g,resultsMessage:D})}return A},[o,g,b,S,_,x,C]),P=n?.action==="initial-input-focus",L=v.useMemo(function(){var A="";if(S.guidance){var D=s?"value":b?"menu":"input";A=S.guidance({"aria-label":k,context:D,isDisabled:r&&p(r,a),isMulti:m,isSearchable:y,tabSelectsValue:E,isInitialFocus:P})}return A},[k,r,s,m,p,y,b,S,a,E,P]),$=_e(v.Fragment,null,_e("span",{id:"aria-selection"},w),_e("span",{id:"aria-focused"},M),_e("span",{id:"aria-results"},T),_e("span",{id:"aria-guidance"},L));return _e(v.Fragment,null,_e(Ef,{id:c},P&&$),_e(Ef,{"aria-live":N,"aria-atomic":"false","aria-relevant":"additions text",role:"log"},i&&!P&&$))},ik=ok,Tl=[{base:"A",letters:"AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ"},{base:"AA",letters:"Ꜳ"},{base:"AE",letters:"ÆǼǢ"},{base:"AO",letters:"Ꜵ"},{base:"AU",letters:"Ꜷ"},{base:"AV",letters:"ꜸꜺ"},{base:"AY",letters:"Ꜽ"},{base:"B",letters:"BⒷBḂḄḆɃƂƁ"},{base:"C",letters:"CⒸCĆĈĊČÇḈƇȻꜾ"},{base:"D",letters:"DⒹDḊĎḌḐḒḎĐƋƊƉꝹ"},{base:"DZ",letters:"DZDŽ"},{base:"Dz",letters:"DzDž"},{base:"E",letters:"EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ"},{base:"F",letters:"FⒻFḞƑꝻ"},{base:"G",letters:"GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ"},{base:"H",letters:"HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ"},{base:"I",letters:"IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ"},{base:"J",letters:"JⒿJĴɈ"},{base:"K",letters:"KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ"},{base:"L",letters:"LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ"},{base:"LJ",letters:"LJ"},{base:"Lj",letters:"Lj"},{base:"M",letters:"MⓂMḾṀṂⱮƜ"},{base:"N",letters:"NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ"},{base:"NJ",letters:"NJ"},{base:"Nj",letters:"Nj"},{base:"O",letters:"OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ"},{base:"OI",letters:"Ƣ"},{base:"OO",letters:"Ꝏ"},{base:"OU",letters:"Ȣ"},{base:"P",letters:"PⓅPṔṖƤⱣꝐꝒꝔ"},{base:"Q",letters:"QⓆQꝖꝘɊ"},{base:"R",letters:"RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ"},{base:"S",letters:"SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ"},{base:"T",letters:"TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ"},{base:"TZ",letters:"Ꜩ"},{base:"U",letters:"UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ"},{base:"V",letters:"VⓋVṼṾƲꝞɅ"},{base:"VY",letters:"Ꝡ"},{base:"W",letters:"WⓌWẀẂŴẆẄẈⱲ"},{base:"X",letters:"XⓍXẊẌ"},{base:"Y",letters:"YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ"},{base:"Z",letters:"ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ"},{base:"a",letters:"aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ"},{base:"aa",letters:"ꜳ"},{base:"ae",letters:"æǽǣ"},{base:"ao",letters:"ꜵ"},{base:"au",letters:"ꜷ"},{base:"av",letters:"ꜹꜻ"},{base:"ay",letters:"ꜽ"},{base:"b",letters:"bⓑbḃḅḇƀƃɓ"},{base:"c",letters:"cⓒcćĉċčçḉƈȼꜿↄ"},{base:"d",letters:"dⓓdḋďḍḑḓḏđƌɖɗꝺ"},{base:"dz",letters:"dzdž"},{base:"e",letters:"eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ"},{base:"f",letters:"fⓕfḟƒꝼ"},{base:"g",letters:"gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ"},{base:"h",letters:"hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ"},{base:"hv",letters:"ƕ"},{base:"i",letters:"iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı"},{base:"j",letters:"jⓙjĵǰɉ"},{base:"k",letters:"kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ"},{base:"l",letters:"lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ"},{base:"lj",letters:"lj"},{base:"m",letters:"mⓜmḿṁṃɱɯ"},{base:"n",letters:"nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ"},{base:"nj",letters:"nj"},{base:"o",letters:"oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ"},{base:"oi",letters:"ƣ"},{base:"ou",letters:"ȣ"},{base:"oo",letters:"ꝏ"},{base:"p",letters:"pⓟpṕṗƥᵽꝑꝓꝕ"},{base:"q",letters:"qⓠqɋꝗꝙ"},{base:"r",letters:"rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ"},{base:"s",letters:"sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ"},{base:"t",letters:"tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ"},{base:"tz",letters:"ꜩ"},{base:"u",letters:"uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ"},{base:"v",letters:"vⓥvṽṿʋꝟʌ"},{base:"vy",letters:"ꝡ"},{base:"w",letters:"wⓦwẁẃŵẇẅẘẉⱳ"},{base:"x",letters:"xⓧxẋẍ"},{base:"y",letters:"yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ"},{base:"z",letters:"zⓩzźẑżžẓẕƶȥɀⱬꝣ"}],ak=new RegExp("["+Tl.map(function(t){return t.letters}).join("")+"]","g"),Mf={},Al=0;Al-1}},uk=["innerRef"];function fk(t){var e=t.innerRef,n=An(t,uk),r=Vw(n,"onExited","in","enter","exit","appear");return _e("input",Ae({ref:e},r,{css:wl({label:"dummyInput",background:0,border:0,caretColor:"transparent",fontSize:"inherit",gridArea:"1 / 1 / 2 / 3",outline:0,padding:0,width:1,color:"transparent",left:-100,opacity:0,position:"relative",transform:"scale(.01)"},"","")}))}var hk=function(e){e.cancelable&&e.preventDefault(),e.stopPropagation()};function gk(t){var e=t.isEnabled,n=t.onBottomArrive,r=t.onBottomLeave,s=t.onTopArrive,o=t.onTopLeave,i=v.useRef(!1),a=v.useRef(!1),l=v.useRef(0),c=v.useRef(null),d=v.useCallback(function(y,b){if(c.current!==null){var _=c.current,x=_.scrollTop,E=_.scrollHeight,C=_.clientHeight,k=c.current,N=b>0,S=E-C-x,w=!1;S>b&&i.current&&(r&&r(y),i.current=!1),N&&a.current&&(o&&o(y),a.current=!1),N&&b>S?(n&&!i.current&&n(y),k.scrollTop=E,w=!0,i.current=!0):!N&&-b>x&&(s&&!a.current&&s(y),k.scrollTop=0,w=!0,a.current=!0),w&&hk(y)}},[n,r,s,o]),u=v.useCallback(function(y){d(y,y.deltaY)},[d]),f=v.useCallback(function(y){l.current=y.changedTouches[0].clientY},[]),g=v.useCallback(function(y){var b=l.current-y.changedTouches[0].clientY;d(y,b)},[d]),m=v.useCallback(function(y){if(y){var b=$w?{passive:!1}:!1;y.addEventListener("wheel",u,b),y.addEventListener("touchstart",f,b),y.addEventListener("touchmove",g,b)}},[g,f,u]),p=v.useCallback(function(y){y&&(y.removeEventListener("wheel",u,!1),y.removeEventListener("touchstart",f,!1),y.removeEventListener("touchmove",g,!1))},[g,f,u]);return v.useEffect(function(){if(e){var y=c.current;return m(y),function(){p(y)}}},[e,m,p]),function(y){c.current=y}}var Tf=["boxSizing","height","overflow","paddingRight","position"],Af={boxSizing:"border-box",overflow:"hidden",position:"relative",height:"100%"};function Df(t){t.cancelable&&t.preventDefault()}function If(t){t.stopPropagation()}function Lf(){var t=this.scrollTop,e=this.scrollHeight,n=t+this.offsetHeight;t===0?this.scrollTop=1:n===e&&(this.scrollTop=t-1)}function Pf(){return"ontouchstart"in window||navigator.maxTouchPoints}var $f=!!(typeof window<"u"&&window.document&&window.document.createElement),Vs=0,Gr={capture:!1,passive:!1};function pk(t){var e=t.isEnabled,n=t.accountForScrollbars,r=n===void 0?!0:n,s=v.useRef({}),o=v.useRef(null),i=v.useCallback(function(l){if($f){var c=document.body,d=c&&c.style;if(r&&Tf.forEach(function(m){var p=d&&d[m];s.current[m]=p}),r&&Vs<1){var u=parseInt(s.current.paddingRight,10)||0,f=document.body?document.body.clientWidth:0,g=window.innerWidth-f+u||0;Object.keys(Af).forEach(function(m){var p=Af[m];d&&(d[m]=p)}),d&&(d.paddingRight="".concat(g,"px"))}c&&Pf()&&(c.addEventListener("touchmove",Df,Gr),l&&(l.addEventListener("touchstart",Lf,Gr),l.addEventListener("touchmove",If,Gr))),Vs+=1}},[r]),a=v.useCallback(function(l){if($f){var c=document.body,d=c&&c.style;Vs=Math.max(Vs-1,0),r&&Vs<1&&Tf.forEach(function(u){var f=s.current[u];d&&(d[u]=f)}),c&&Pf()&&(c.removeEventListener("touchmove",Df,Gr),l&&(l.removeEventListener("touchstart",Lf,Gr),l.removeEventListener("touchmove",If,Gr)))}},[r]);return v.useEffect(function(){if(e){var l=o.current;return i(l),function(){a(l)}}},[e,i,a]),function(l){o.current=l}}var mk=function(e){var n=e.target;return n.ownerDocument.activeElement&&n.ownerDocument.activeElement.blur()},bk={name:"1kfdb0e",styles:"position:fixed;left:0;bottom:0;right:0;top:0"};function yk(t){var e=t.children,n=t.lockEnabled,r=t.captureEnabled,s=r===void 0?!0:r,o=t.onBottomArrive,i=t.onBottomLeave,a=t.onTopArrive,l=t.onTopLeave,c=gk({isEnabled:s,onBottomArrive:o,onBottomLeave:i,onTopArrive:a,onTopLeave:l}),d=pk({isEnabled:n}),u=function(g){c(g),d(g)};return _e(v.Fragment,null,n&&_e("div",{onClick:mk,css:bk}),e(u))}var vk={name:"1a0ro4n-requiredInput",styles:"label:requiredInput;opacity:0;pointer-events:none;position:absolute;bottom:0;left:0;right:0;width:100%"},xk=function(e){var n=e.name,r=e.onFocus;return _e("input",{required:!0,name:n,tabIndex:-1,"aria-hidden":"true",onFocus:r,css:vk,value:"",onChange:function(){}})},wk=xk;function Ll(t){var e;return typeof window<"u"&&window.navigator!=null?t.test(((e=window.navigator.userAgentData)===null||e===void 0?void 0:e.platform)||window.navigator.platform):!1}function kk(){return Ll(/^iPhone/i)}function Ff(){return Ll(/^Mac/i)}function Ck(){return Ll(/^iPad/i)||Ff()&&navigator.maxTouchPoints>1}function Sk(){return kk()||Ck()}function _k(){return Ff()||Sk()}var Ek=function(e){return e.label},zf=function(e){return e.label},Vf=function(e){return e.value},Mk=function(e){return!!e.isDisabled},Nk={clearIndicator:h1,container:n1,control:x1,dropdownIndicator:u1,group:S1,groupHeading:E1,indicatorsContainer:i1,indicatorSeparator:p1,input:T1,loadingIndicator:y1,loadingMessage:Qw,menu:Ww,menuList:Xw,menuPortal:e1,multiValue:P1,multiValueLabel:$1,multiValueRemove:F1,noOptionsMessage:Zw,option:j1,placeholder:Y1,singleValue:q1,valueContainer:s1},Ok={primary:"#2684FF",primary75:"#4C9AFF",primary50:"#B2D4FF",primary25:"#DEEBFF",danger:"#DE350B",dangerLight:"#FFBDAD",neutral0:"hsl(0, 0%, 100%)",neutral5:"hsl(0, 0%, 95%)",neutral10:"hsl(0, 0%, 90%)",neutral20:"hsl(0, 0%, 80%)",neutral30:"hsl(0, 0%, 70%)",neutral40:"hsl(0, 0%, 60%)",neutral50:"hsl(0, 0%, 50%)",neutral60:"hsl(0, 0%, 40%)",neutral70:"hsl(0, 0%, 30%)",neutral80:"hsl(0, 0%, 20%)",neutral90:"hsl(0, 0%, 10%)"},Tk=4,Hf=4,Ak=38,Dk=Hf*2,Ik={baseUnit:Hf,controlHeight:Ak,menuGutter:Dk},Pl={borderRadius:Tk,colors:Ok,spacing:Ik},Lk={"aria-live":"polite",backspaceRemovesValue:!0,blurInputOnSelect:gf(),captureMenuScroll:!gf(),classNames:{},closeMenuOnSelect:!0,closeMenuOnScroll:!1,components:{},controlShouldRenderValue:!0,escapeClearsValue:!1,filterOption:dk(),formatGroupLabel:Ek,getOptionLabel:zf,getOptionValue:Vf,isDisabled:!1,isLoading:!1,isMulti:!1,isRtl:!1,isSearchable:!0,isOptionDisabled:Mk,loadingMessage:function(){return"Loading..."},maxMenuHeight:300,minMenuHeight:140,menuIsOpen:!1,menuPlacement:"bottom",menuPosition:"absolute",menuShouldBlockScroll:!1,menuShouldScrollIntoView:!Lw(),noOptionsMessage:function(){return"No options"},openMenuOnFocus:!1,openMenuOnClick:!0,options:[],pageSize:5,placeholder:"Select...",screenReaderStatus:function(e){var n=e.count;return"".concat(n," result").concat(n!==1?"s":""," available")},styles:{},tabIndex:0,tabSelectsValue:!0,unstyled:!1};function Uf(t,e,n,r){var s=Yf(t,e,n),o=Kf(t,e,n),i=Gf(t,e),a=mi(t,e);return{type:"option",data:e,isDisabled:s,isSelected:o,label:i,value:a,index:r}}function pi(t,e){return t.options.map(function(n,r){if("options"in n){var s=n.options.map(function(i,a){return Uf(t,i,e,a)}).filter(function(i){return Wf(t,i)});return s.length>0?{type:"group",data:n,options:s,index:r}:void 0}var o=Uf(t,n,e,r);return Wf(t,o)?o:void 0}).filter(Fw)}function Bf(t){return t.reduce(function(e,n){return n.type==="group"?e.push.apply(e,Ur(n.options.map(function(r){return r.data}))):e.push(n.data),e},[])}function jf(t,e){return t.reduce(function(n,r){return r.type==="group"?n.push.apply(n,Ur(r.options.map(function(s){return{data:s.data,id:"".concat(e,"-").concat(r.index,"-").concat(s.index)}}))):n.push({data:r.data,id:"".concat(e,"-").concat(r.index)}),n},[])}function Pk(t,e){return Bf(pi(t,e))}function Wf(t,e){var n=t.inputValue,r=n===void 0?"":n,s=e.data,o=e.isSelected,i=e.label,a=e.value;return(!qf(t)||!o)&&Xf(t,{label:i,value:a,data:s},r)}function $k(t,e){var n=t.focusedValue,r=t.selectValue,s=r.indexOf(n);if(s>-1){var o=e.indexOf(n);if(o>-1)return n;if(s-1?n:e[0]}var $l=function(e,n){var r,s=(r=e.find(function(o){return o.data===n}))===null||r===void 0?void 0:r.id;return s||null},Gf=function(e,n){return e.getOptionLabel(n)},mi=function(e,n){return e.getOptionValue(n)};function Yf(t,e,n){return typeof t.isOptionDisabled=="function"?t.isOptionDisabled(e,n):!1}function Kf(t,e,n){if(n.indexOf(e)>-1)return!0;if(typeof t.isOptionSelected=="function")return t.isOptionSelected(e,n);var r=mi(t,e);return n.some(function(s){return mi(t,s)===r})}function Xf(t,e,n){return t.filterOption?t.filterOption(e,n):!0}var qf=function(e){var n=e.hideSelectedOptions,r=e.isMulti;return n===void 0?r:n},zk=1,Fl=(function(t){cx(n,t);var e=fx(n);function n(r){var s;if(ax(this,n),s=e.call(this,r),s.state={ariaSelection:null,focusedOption:null,focusedOptionId:null,focusableOptionsWithIds:[],focusedValue:null,inputIsHidden:!1,isFocused:!1,selectValue:[],clearFocusValueOnUpdate:!1,prevWasFocused:!1,inputIsHiddenAfterUpdate:void 0,prevProps:void 0,instancePrefix:"",isAppleDevice:!1},s.blockOptionHover=!1,s.isComposing=!1,s.commonProps=void 0,s.initialTouchX=0,s.initialTouchY=0,s.openAfterFocus=!1,s.scrollToFocusedOptionOnUpdate=!1,s.userIsDragging=void 0,s.controlRef=null,s.getControlRef=function(l){s.controlRef=l},s.focusedOptionRef=null,s.getFocusedOptionRef=function(l){s.focusedOptionRef=l},s.menuListRef=null,s.getMenuListRef=function(l){s.menuListRef=l},s.inputRef=null,s.getInputRef=function(l){s.inputRef=l},s.focus=s.focusInput,s.blur=s.blurInput,s.onChange=function(l,c){var d=s.props,u=d.onChange,f=d.name;c.name=f,s.ariaOnChange(l,c),u(l,c)},s.setValue=function(l,c,d){var u=s.props,f=u.closeMenuOnSelect,g=u.isMulti,m=u.inputValue;s.onInputChange("",{action:"set-value",prevInputValue:m}),f&&(s.setState({inputIsHiddenAfterUpdate:!g}),s.onMenuClose()),s.setState({clearFocusValueOnUpdate:!0}),s.onChange(l,{action:c,option:d})},s.selectOption=function(l){var c=s.props,d=c.blurInputOnSelect,u=c.isMulti,f=c.name,g=s.state.selectValue,m=u&&s.isOptionSelected(l,g),p=s.isOptionDisabled(l,g);if(m){var y=s.getOptionValue(l);s.setValue(g.filter(function(b){return s.getOptionValue(b)!==y}),"deselect-option",l)}else if(!p)u?s.setValue([].concat(Ur(g),[l]),"select-option",l):s.setValue(l,"select-option");else{s.ariaOnChange(l,{action:"select-option",option:l,name:f});return}d&&s.blurInput()},s.removeValue=function(l){var c=s.props.isMulti,d=s.state.selectValue,u=s.getOptionValue(l),f=d.filter(function(m){return s.getOptionValue(m)!==u}),g=zs(c,f,f[0]||null);s.onChange(g,{action:"remove-value",removedValue:l}),s.focusInput()},s.clearValue=function(){var l=s.state.selectValue;s.onChange(zs(s.props.isMulti,[],null),{action:"clear",removedValues:l})},s.popValue=function(){var l=s.props.isMulti,c=s.state.selectValue,d=c[c.length-1],u=c.slice(0,c.length-1),f=zs(l,u,u[0]||null);d&&s.onChange(f,{action:"pop-value",removedValue:d})},s.getFocusedOptionId=function(l){return $l(s.state.focusableOptionsWithIds,l)},s.getFocusableOptionsWithIds=function(){return jf(pi(s.props,s.state.selectValue),s.getElementId("option"))},s.getValue=function(){return s.state.selectValue},s.cx=function(){for(var l=arguments.length,c=new Array(l),d=0;dg||f>g}},s.onTouchEnd=function(l){s.userIsDragging||(s.controlRef&&!s.controlRef.contains(l.target)&&s.menuListRef&&!s.menuListRef.contains(l.target)&&s.blurInput(),s.initialTouchX=0,s.initialTouchY=0)},s.onControlTouchEnd=function(l){s.userIsDragging||s.onControlMouseDown(l)},s.onClearIndicatorTouchEnd=function(l){s.userIsDragging||s.onClearIndicatorMouseDown(l)},s.onDropdownIndicatorTouchEnd=function(l){s.userIsDragging||s.onDropdownIndicatorMouseDown(l)},s.handleInputChange=function(l){var c=s.props.inputValue,d=l.currentTarget.value;s.setState({inputIsHiddenAfterUpdate:!1}),s.onInputChange(d,{action:"input-change",prevInputValue:c}),s.props.menuIsOpen||s.onMenuOpen()},s.onInputFocus=function(l){s.props.onFocus&&s.props.onFocus(l),s.setState({inputIsHiddenAfterUpdate:!1,isFocused:!0}),(s.openAfterFocus||s.props.openMenuOnFocus)&&s.openMenu("first"),s.openAfterFocus=!1},s.onInputBlur=function(l){var c=s.props.inputValue;if(s.menuListRef&&s.menuListRef.contains(document.activeElement)){s.inputRef.focus();return}s.props.onBlur&&s.props.onBlur(l),s.onInputChange("",{action:"input-blur",prevInputValue:c}),s.onMenuClose(),s.setState({focusedValue:null,isFocused:!1})},s.onOptionHover=function(l){if(!(s.blockOptionHover||s.state.focusedOption===l)){var c=s.getFocusableOptions(),d=c.indexOf(l);s.setState({focusedOption:l,focusedOptionId:d>-1?s.getFocusedOptionId(l):null})}},s.shouldHideSelectedOptions=function(){return qf(s.props)},s.onValueInputFocus=function(l){l.preventDefault(),l.stopPropagation(),s.focus()},s.onKeyDown=function(l){var c=s.props,d=c.isMulti,u=c.backspaceRemovesValue,f=c.escapeClearsValue,g=c.inputValue,m=c.isClearable,p=c.isDisabled,y=c.menuIsOpen,b=c.onKeyDown,_=c.tabSelectsValue,x=c.openMenuOnFocus,E=s.state,C=E.focusedOption,k=E.focusedValue,N=E.selectValue;if(!p&&!(typeof b=="function"&&(b(l),l.defaultPrevented))){switch(s.blockOptionHover=!0,l.key){case"ArrowLeft":if(!d||g)return;s.focusValue("previous");break;case"ArrowRight":if(!d||g)return;s.focusValue("next");break;case"Delete":case"Backspace":if(g)return;if(k)s.removeValue(k);else{if(!u)return;d?s.popValue():m&&s.clearValue()}break;case"Tab":if(s.isComposing||l.shiftKey||!y||!_||!C||x&&s.isOptionSelected(C,N))return;s.selectOption(C);break;case"Enter":if(l.keyCode===229)break;if(y){if(!C||s.isComposing)return;s.selectOption(C);break}return;case"Escape":y?(s.setState({inputIsHiddenAfterUpdate:!1}),s.onInputChange("",{action:"menu-close",prevInputValue:g}),s.onMenuClose()):m&&f&&s.clearValue();break;case" ":if(g)return;if(!y){s.openMenu("first");break}if(!C)return;s.selectOption(C);break;case"ArrowUp":y?s.focusOption("up"):s.openMenu("last");break;case"ArrowDown":y?s.focusOption("down"):s.openMenu("first");break;case"PageUp":if(!y)return;s.focusOption("pageup");break;case"PageDown":if(!y)return;s.focusOption("pagedown");break;case"Home":if(!y)return;s.focusOption("first");break;case"End":if(!y)return;s.focusOption("last");break;default:return}l.preventDefault()}},s.state.instancePrefix="react-select-"+(s.props.instanceId||++zk),s.state.selectValue=ci(r.value),r.menuIsOpen&&s.state.selectValue.length){var o=s.getFocusableOptionsWithIds(),i=s.buildFocusableOptions(),a=i.indexOf(s.state.selectValue[0]);s.state.focusableOptionsWithIds=o,s.state.focusedOption=i[a],s.state.focusedOptionId=$l(o,i[a])}return s}return lx(n,[{key:"componentDidMount",value:function(){this.startListeningComposition(),this.startListeningToTouch(),this.props.closeMenuOnScroll&&document&&document.addEventListener&&document.addEventListener("scroll",this.onScroll,!0),this.props.autoFocus&&this.focusInput(),this.props.menuIsOpen&&this.state.focusedOption&&this.menuListRef&&this.focusedOptionRef&&hf(this.menuListRef,this.focusedOptionRef),_k()&&this.setState({isAppleDevice:!0})}},{key:"componentDidUpdate",value:function(s){var o=this.props,i=o.isDisabled,a=o.menuIsOpen,l=this.state.isFocused;(l&&!i&&s.isDisabled||l&&a&&!s.menuIsOpen)&&this.focusInput(),l&&i&&!s.isDisabled?this.setState({isFocused:!1},this.onMenuClose):!l&&!i&&s.isDisabled&&this.inputRef===document.activeElement&&this.setState({isFocused:!0}),this.menuListRef&&this.focusedOptionRef&&this.scrollToFocusedOptionOnUpdate&&(hf(this.menuListRef,this.focusedOptionRef),this.scrollToFocusedOptionOnUpdate=!1)}},{key:"componentWillUnmount",value:function(){this.stopListeningComposition(),this.stopListeningToTouch(),document.removeEventListener("scroll",this.onScroll,!0)}},{key:"onMenuOpen",value:function(){this.props.onMenuOpen()}},{key:"onMenuClose",value:function(){this.onInputChange("",{action:"menu-close",prevInputValue:this.props.inputValue}),this.props.onMenuClose()}},{key:"onInputChange",value:function(s,o){this.props.onInputChange(s,o)}},{key:"focusInput",value:function(){this.inputRef&&this.inputRef.focus()}},{key:"blurInput",value:function(){this.inputRef&&this.inputRef.blur()}},{key:"openMenu",value:function(s){var o=this,i=this.state,a=i.selectValue,l=i.isFocused,c=this.buildFocusableOptions(),d=s==="first"?0:c.length-1;if(!this.props.isMulti){var u=c.indexOf(a[0]);u>-1&&(d=u)}this.scrollToFocusedOptionOnUpdate=!(l&&this.menuListRef),this.setState({inputIsHiddenAfterUpdate:!1,focusedValue:null,focusedOption:c[d],focusedOptionId:this.getFocusedOptionId(c[d])},function(){return o.onMenuOpen()})}},{key:"focusValue",value:function(s){var o=this.state,i=o.selectValue,a=o.focusedValue;if(this.props.isMulti){this.setState({focusedOption:null});var l=i.indexOf(a);a||(l=-1);var c=i.length-1,d=-1;if(i.length){switch(s){case"previous":l===0?d=0:l===-1?d=c:d=l-1;break;case"next":l>-1&&l0&&arguments[0]!==void 0?arguments[0]:"first",o=this.props.pageSize,i=this.state.focusedOption,a=this.getFocusableOptions();if(a.length){var l=0,c=a.indexOf(i);i||(c=-1),s==="up"?l=c>0?c-1:a.length-1:s==="down"?l=(c+1)%a.length:s==="pageup"?(l=c-o,l<0&&(l=0)):s==="pagedown"?(l=c+o,l>a.length-1&&(l=a.length-1)):s==="last"&&(l=a.length-1),this.scrollToFocusedOptionOnUpdate=!0,this.setState({focusedOption:a[l],focusedValue:null,focusedOptionId:this.getFocusedOptionId(a[l])})}}},{key:"getTheme",value:(function(){return this.props.theme?typeof this.props.theme=="function"?this.props.theme(Pl):Me(Me({},Pl),this.props.theme):Pl})},{key:"getCommonProps",value:function(){var s=this.clearValue,o=this.cx,i=this.getStyles,a=this.getClassNames,l=this.getValue,c=this.selectOption,d=this.setValue,u=this.props,f=u.isMulti,g=u.isRtl,m=u.options,p=this.hasValue();return{clearValue:s,cx:o,getStyles:i,getClassNames:a,getValue:l,hasValue:p,isMulti:f,isRtl:g,options:m,selectOption:c,selectProps:u,setValue:d,theme:this.getTheme()}}},{key:"hasValue",value:function(){var s=this.state.selectValue;return s.length>0}},{key:"hasOptions",value:function(){return!!this.getFocusableOptions().length}},{key:"isClearable",value:function(){var s=this.props,o=s.isClearable,i=s.isMulti;return o===void 0?i:o}},{key:"isOptionDisabled",value:function(s,o){return Yf(this.props,s,o)}},{key:"isOptionSelected",value:function(s,o){return Kf(this.props,s,o)}},{key:"filterOption",value:function(s,o){return Xf(this.props,s,o)}},{key:"formatOptionLabel",value:function(s,o){if(typeof this.props.formatOptionLabel=="function"){var i=this.props.inputValue,a=this.state.selectValue;return this.props.formatOptionLabel(s,{context:o,inputValue:i,selectValue:a})}else return this.getOptionLabel(s)}},{key:"formatGroupLabel",value:function(s){return this.props.formatGroupLabel(s)}},{key:"startListeningComposition",value:(function(){document&&document.addEventListener&&(document.addEventListener("compositionstart",this.onCompositionStart,!1),document.addEventListener("compositionend",this.onCompositionEnd,!1))})},{key:"stopListeningComposition",value:function(){document&&document.removeEventListener&&(document.removeEventListener("compositionstart",this.onCompositionStart),document.removeEventListener("compositionend",this.onCompositionEnd))}},{key:"startListeningToTouch",value:(function(){document&&document.addEventListener&&(document.addEventListener("touchstart",this.onTouchStart,!1),document.addEventListener("touchmove",this.onTouchMove,!1),document.addEventListener("touchend",this.onTouchEnd,!1))})},{key:"stopListeningToTouch",value:function(){document&&document.removeEventListener&&(document.removeEventListener("touchstart",this.onTouchStart),document.removeEventListener("touchmove",this.onTouchMove),document.removeEventListener("touchend",this.onTouchEnd))}},{key:"renderInput",value:(function(){var s=this.props,o=s.isDisabled,i=s.isSearchable,a=s.inputId,l=s.inputValue,c=s.tabIndex,d=s.form,u=s.menuIsOpen,f=s.required,g=this.getComponents(),m=g.Input,p=this.state,y=p.inputIsHidden,b=p.ariaSelection,_=this.commonProps,x=a||this.getElementId("input"),E=Me(Me(Me({"aria-autocomplete":"list","aria-expanded":u,"aria-haspopup":!0,"aria-errormessage":this.props["aria-errormessage"],"aria-invalid":this.props["aria-invalid"],"aria-label":this.props["aria-label"],"aria-labelledby":this.props["aria-labelledby"],"aria-required":f,role:"combobox","aria-activedescendant":this.state.isAppleDevice?void 0:this.state.focusedOptionId||""},u&&{"aria-controls":this.getElementId("listbox")}),!i&&{"aria-readonly":!0}),this.hasValue()?b?.action==="initial-input-focus"&&{"aria-describedby":this.getElementId("live-region")}:{"aria-describedby":this.getElementId("placeholder")});return i?F.createElement(m,Ae({},_,{autoCapitalize:"none",autoComplete:"off",autoCorrect:"off",id:x,innerRef:this.getInputRef,isDisabled:o,isHidden:y,onBlur:this.onInputBlur,onChange:this.handleInputChange,onFocus:this.onInputFocus,spellCheck:"false",tabIndex:c,form:d,type:"text",value:l},E)):F.createElement(fk,Ae({id:x,innerRef:this.getInputRef,onBlur:this.onInputBlur,onChange:li,onFocus:this.onInputFocus,disabled:o,tabIndex:c,inputMode:"none",form:d,value:""},E))})},{key:"renderPlaceholderOrValue",value:function(){var s=this,o=this.getComponents(),i=o.MultiValue,a=o.MultiValueContainer,l=o.MultiValueLabel,c=o.MultiValueRemove,d=o.SingleValue,u=o.Placeholder,f=this.commonProps,g=this.props,m=g.controlShouldRenderValue,p=g.isDisabled,y=g.isMulti,b=g.inputValue,_=g.placeholder,x=this.state,E=x.selectValue,C=x.focusedValue,k=x.isFocused;if(!this.hasValue()||!m)return b?null:F.createElement(u,Ae({},f,{key:"placeholder",isDisabled:p,isFocused:k,innerProps:{id:this.getElementId("placeholder")}}),_);if(y)return E.map(function(S,w){var M=S===C,T="".concat(s.getOptionLabel(S),"-").concat(s.getOptionValue(S));return F.createElement(i,Ae({},f,{components:{Container:a,Label:l,Remove:c},isFocused:M,isDisabled:p,key:T,index:w,removeProps:{onClick:function(){return s.removeValue(S)},onTouchEnd:function(){return s.removeValue(S)},onMouseDown:function(L){L.preventDefault()}},data:S}),s.formatOptionLabel(S,"value"))});if(b)return null;var N=E[0];return F.createElement(d,Ae({},f,{data:N,isDisabled:p}),this.formatOptionLabel(N,"value"))}},{key:"renderClearIndicator",value:function(){var s=this.getComponents(),o=s.ClearIndicator,i=this.commonProps,a=this.props,l=a.isDisabled,c=a.isLoading,d=this.state.isFocused;if(!this.isClearable()||!o||l||!this.hasValue()||c)return null;var u={onMouseDown:this.onClearIndicatorMouseDown,onTouchEnd:this.onClearIndicatorTouchEnd,"aria-hidden":"true"};return F.createElement(o,Ae({},i,{innerProps:u,isFocused:d}))}},{key:"renderLoadingIndicator",value:function(){var s=this.getComponents(),o=s.LoadingIndicator,i=this.commonProps,a=this.props,l=a.isDisabled,c=a.isLoading,d=this.state.isFocused;if(!o||!c)return null;var u={"aria-hidden":"true"};return F.createElement(o,Ae({},i,{innerProps:u,isDisabled:l,isFocused:d}))}},{key:"renderIndicatorSeparator",value:function(){var s=this.getComponents(),o=s.DropdownIndicator,i=s.IndicatorSeparator;if(!o||!i)return null;var a=this.commonProps,l=this.props.isDisabled,c=this.state.isFocused;return F.createElement(i,Ae({},a,{isDisabled:l,isFocused:c}))}},{key:"renderDropdownIndicator",value:function(){var s=this.getComponents(),o=s.DropdownIndicator;if(!o)return null;var i=this.commonProps,a=this.props.isDisabled,l=this.state.isFocused,c={onMouseDown:this.onDropdownIndicatorMouseDown,onTouchEnd:this.onDropdownIndicatorTouchEnd,"aria-hidden":"true"};return F.createElement(o,Ae({},i,{innerProps:c,isDisabled:a,isFocused:l}))}},{key:"renderMenu",value:function(){var s=this,o=this.getComponents(),i=o.Group,a=o.GroupHeading,l=o.Menu,c=o.MenuList,d=o.MenuPortal,u=o.LoadingMessage,f=o.NoOptionsMessage,g=o.Option,m=this.commonProps,p=this.state.focusedOption,y=this.props,b=y.captureMenuScroll,_=y.inputValue,x=y.isLoading,E=y.loadingMessage,C=y.minMenuHeight,k=y.maxMenuHeight,N=y.menuIsOpen,S=y.menuPlacement,w=y.menuPosition,M=y.menuPortalTarget,T=y.menuShouldBlockScroll,P=y.menuShouldScrollIntoView,L=y.noOptionsMessage,$=y.onMenuScrollToTop,A=y.onMenuScrollToBottom;if(!N)return null;var D=function(B,G){var V=B.type,X=B.data,K=B.isDisabled,Z=B.isSelected,ee=B.label,ne=B.value,he=p===X,J=K?void 0:function(){return s.onOptionHover(X)},le=K?void 0:function(){return s.selectOption(X)},ue="".concat(s.getElementId("option"),"-").concat(G),ae={id:ue,onClick:le,onMouseMove:J,onMouseOver:J,tabIndex:-1,role:"option","aria-selected":s.state.isAppleDevice?void 0:Z};return F.createElement(g,Ae({},m,{innerProps:ae,data:X,isDisabled:K,isSelected:Z,key:ue,label:ee,type:V,value:ne,isFocused:he,innerRef:he?s.getFocusedOptionRef:void 0}),s.formatOptionLabel(B.data,"menu"))},O;if(this.hasOptions())O=this.getCategorizedOptions().map(function(U){if(U.type==="group"){var B=U.data,G=U.options,V=U.index,X="".concat(s.getElementId("group"),"-").concat(V),K="".concat(X,"-heading");return F.createElement(i,Ae({},m,{key:X,data:B,options:G,Heading:a,headingProps:{id:K,data:U.data},label:s.formatGroupLabel(U.data)}),U.options.map(function(Z){return D(Z,"".concat(V,"-").concat(Z.index))}))}else if(U.type==="option")return D(U,"".concat(U.index))});else if(x){var z=E({inputValue:_});if(z===null)return null;O=F.createElement(u,m,z)}else{var H=L({inputValue:_});if(H===null)return null;O=F.createElement(f,m,H)}var j={minMenuHeight:C,maxMenuHeight:k,menuPlacement:S,menuPosition:w,menuShouldScrollIntoView:P},W=F.createElement(Gw,Ae({},m,j),function(U){var B=U.ref,G=U.placerProps,V=G.placement,X=G.maxHeight;return F.createElement(l,Ae({},m,j,{innerRef:B,innerProps:{onMouseDown:s.onMenuMouseDown,onMouseMove:s.onMenuMouseMove},isLoading:x,placement:V}),F.createElement(yk,{captureEnabled:b,onTopArrive:$,onBottomArrive:A,lockEnabled:T},function(K){return F.createElement(c,Ae({},m,{innerRef:function(ee){s.getMenuListRef(ee),K(ee)},innerProps:{role:"listbox","aria-multiselectable":m.isMulti,id:s.getElementId("listbox")},isLoading:x,maxHeight:X,focusedOption:p}),O)}))});return M||w==="fixed"?F.createElement(d,Ae({},m,{appendTo:M,controlElement:this.controlRef,menuPlacement:S,menuPosition:w}),W):W}},{key:"renderFormField",value:function(){var s=this,o=this.props,i=o.delimiter,a=o.isDisabled,l=o.isMulti,c=o.name,d=o.required,u=this.state.selectValue;if(d&&!this.hasValue()&&!a)return F.createElement(wk,{name:c,onFocus:this.onValueInputFocus});if(!(!c||a))if(l)if(i){var f=u.map(function(p){return s.getOptionValue(p)}).join(i);return F.createElement("input",{name:c,type:"hidden",value:f})}else{var g=u.length>0?u.map(function(p,y){return F.createElement("input",{key:"i-".concat(y),name:c,type:"hidden",value:s.getOptionValue(p)})}):F.createElement("input",{name:c,type:"hidden",value:""});return F.createElement("div",null,g)}else{var m=u[0]?this.getOptionValue(u[0]):"";return F.createElement("input",{name:c,type:"hidden",value:m})}}},{key:"renderLiveRegion",value:function(){var s=this.commonProps,o=this.state,i=o.ariaSelection,a=o.focusedOption,l=o.focusedValue,c=o.isFocused,d=o.selectValue,u=this.getFocusableOptions();return F.createElement(ik,Ae({},s,{id:this.getElementId("live-region"),ariaSelection:i,focusedOption:a,focusedValue:l,isFocused:c,selectValue:d,focusableOptions:u,isAppleDevice:this.state.isAppleDevice}))}},{key:"render",value:function(){var s=this.getComponents(),o=s.Control,i=s.IndicatorsContainer,a=s.SelectContainer,l=s.ValueContainer,c=this.props,d=c.className,u=c.id,f=c.isDisabled,g=c.menuIsOpen,m=this.state.isFocused,p=this.commonProps=this.getCommonProps();return F.createElement(a,Ae({},p,{className:d,innerProps:{id:u,onKeyDown:this.onKeyDown},isDisabled:f,isFocused:m}),this.renderLiveRegion(),F.createElement(o,Ae({},p,{innerRef:this.getControlRef,innerProps:{onMouseDown:this.onControlMouseDown,onTouchEnd:this.onControlTouchEnd},isDisabled:f,isFocused:m,menuIsOpen:g}),F.createElement(l,Ae({},p,{isDisabled:f}),this.renderPlaceholderOrValue(),this.renderInput()),F.createElement(i,Ae({},p,{isDisabled:f}),this.renderClearIndicator(),this.renderLoadingIndicator(),this.renderIndicatorSeparator(),this.renderDropdownIndicator())),this.renderMenu(),this.renderFormField())}}],[{key:"getDerivedStateFromProps",value:function(s,o){var i=o.prevProps,a=o.clearFocusValueOnUpdate,l=o.inputIsHiddenAfterUpdate,c=o.ariaSelection,d=o.isFocused,u=o.prevWasFocused,f=o.instancePrefix,g=s.options,m=s.value,p=s.menuIsOpen,y=s.inputValue,b=s.isMulti,_=ci(m),x={};if(i&&(m!==i.value||g!==i.options||p!==i.menuIsOpen||y!==i.inputValue)){var E=p?Pk(s,_):[],C=p?jf(pi(s,_),"".concat(f,"-option")):[],k=a?$k(o,_):null,N=Fk(o,E),S=$l(C,N);x={selectValue:_,focusedOption:N,focusedOptionId:S,focusableOptionsWithIds:C,focusedValue:k,clearFocusValueOnUpdate:!1}}var w=l!=null&&s!==i?{inputIsHidden:l,inputIsHiddenAfterUpdate:void 0}:{},M=c,T=d&&u;return d&&!T&&(M={value:zs(b,_,_[0]||null),options:_,action:"initial-input-focus"},T=!u),c?.action==="initial-input-focus"&&(M=null),Me(Me(Me({},x),w),{},{prevProps:s,ariaSelection:M,prevWasFocused:T})}}]),n})(v.Component);Fl.defaultProps=Lk;var Vk=v.forwardRef(function(t,e){var n=Du(t);return F.createElement(Fl,Ae({ref:e},n))}),Zf=Vk,Hk=["allowCreateWhileLoading","createOptionPosition","formatCreateLabel","isValidNewOption","getNewOptionData","onCreateOption","options","onChange"],Qf=function(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"",n=arguments.length>1?arguments[1]:void 0,r=arguments.length>2?arguments[2]:void 0,s=String(e).toLowerCase(),o=String(r.getOptionValue(n)).toLowerCase(),i=String(r.getOptionLabel(n)).toLowerCase();return o===s||i===s},zl={formatCreateLabel:function(e){return'Create "'.concat(e,'"')},isValidNewOption:function(e,n,r,s){return!(!e||n.some(function(o){return Qf(e,o,s)})||r.some(function(o){return Qf(e,o,s)}))},getNewOptionData:function(e,n){return{label:n,value:e,__isNew__:!0}}};function Uk(t){var e=t.allowCreateWhileLoading,n=e===void 0?!1:e,r=t.createOptionPosition,s=r===void 0?"last":r,o=t.formatCreateLabel,i=o===void 0?zl.formatCreateLabel:o,a=t.isValidNewOption,l=a===void 0?zl.isValidNewOption:a,c=t.getNewOptionData,d=c===void 0?zl.getNewOptionData:c,u=t.onCreateOption,f=t.options,g=f===void 0?[]:f,m=t.onChange,p=An(t,Hk),y=p.getOptionValue,b=y===void 0?Vf:y,_=p.getOptionLabel,x=_===void 0?zf:_,E=p.inputValue,C=p.isLoading,k=p.isMulti,N=p.value,S=p.name,w=v.useMemo(function(){return l(E,ci(N),g,{getOptionValue:b,getOptionLabel:x})?d(E,i(E)):void 0},[i,d,x,b,E,l,g,N]),M=v.useMemo(function(){return(n||!C)&&w?s==="first"?[w].concat(Ur(g)):[].concat(Ur(g),[w]):g},[n,s,C,w,g]),T=v.useCallback(function(P,L){if(L.action!=="select-option")return m(P,L);var $=Array.isArray(P)?P:[P];if($[$.length-1]===w){if(u)u(E);else{var A=d(E,E),D={action:"create-option",name:S,option:A};m(zs(k,[].concat(Ur(ci(N)),[A]),A),D)}return}m(P,L)},[d,E,k,S,w,u,m,N]);return Me(Me({},p),{},{options:M,onChange:T})}var Bk=v.forwardRef(function(t,e){var n=Du(t),r=Uk(n);return F.createElement(Fl,Ae({ref:e},r))}),Jf=Bk;const $t={control:"input-tag",valueContainer:"gap-1 flex flex-wrap",multiValue:{blue:"bg-blue-100 dark:bg-blue-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",green:"bg-green-100 dark:bg-green-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",amber:"bg-amber-100 dark:bg-amber-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",red:"bg-red-100 dark:bg-red-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",purple:"bg-purple-100 dark:bg-purple-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",gray:"bg-gray-100 dark:bg-gray-700/50 rounded px-1.5 py-0.5 flex items-center gap-1"},multiValueLabel:{blue:"text-blue-700 dark:text-blue-300 text-xs",green:"text-green-700 dark:text-green-300 text-xs font-semibold",amber:"text-amber-700 dark:text-amber-300 text-xs font-semibold",red:"text-red-700 dark:text-red-300 text-xs font-semibold",purple:"text-purple-700 dark:text-purple-300 text-xs font-semibold",gray:"text-gray-700 dark:text-gray-300 text-xs"},multiValueRemove:{blue:"text-blue-700 dark:text-blue-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",green:"text-green-700 dark:text-green-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",amber:"text-amber-700 dark:text-amber-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",red:"text-red-700 dark:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",purple:"text-purple-700 dark:text-purple-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",gray:"text-gray-700 dark:text-gray-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1"},input:"text-gray-900 dark:text-white text-sm",placeholder:"text-gray-400 dark:text-gray-500 text-sm",menu:"mt-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",menuList:"py-1",option:{base:"px-3 py-2 cursor-pointer text-gray-900 dark:text-white text-[13px]",selected:"bg-blue-100 dark:bg-blue-900/20",focused:"bg-gray-100 dark:bg-gray-700",default:"bg-white dark:bg-gray-800"},noOptionsMessage:"px-3 py-2 text-gray-500 dark:text-gray-400 text-[13px]",checkbox:{base:"w-4 h-4 rounded border flex items-center justify-center text-white text-[10px] shrink-0",selected:"bg-blue-500 border-blue-500",unselected:"border-gray-400 dark:border-gray-500"}},Rf=t=>{const{data:e,isSelected:n}=t;return h.jsx(gi.Option,{...t,children:h.jsxs("div",{className:"flex items-center gap-2",children:[h.jsx("span",{className:`${$t.checkbox.base} ${n?$t.checkbox.selected:$t.checkbox.unselected}`,children:n?"✓":""}),h.jsxs("div",{className:"flex-1 min-w-0",children:[h.jsxs("div",{className:"flex items-center gap-1",children:[h.jsx("span",{className:"text-[13px]",children:e.label}),e.count!==void 0&&e.count>0&&h.jsxs("span",{className:"text-gray-500 dark:text-gray-400 text-[10px]",children:["(",e.count,")"]})]}),e.description&&h.jsx("p",{className:"text-xs text-gray-500 dark:text-gray-400 truncate",children:e.description})]})]})})},eh=t=>h.jsx(gi.GroupHeading,{...t,children:h.jsx("div",{className:"px-3 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800/50 border-b border-gray-200 dark:border-gray-700",children:t.data.label})}),jk=t=>{const{data:e}=t;return h.jsxs(gi.MultiValueLabel,{...t,children:[h.jsx("span",{children:e.label}),e.count!==void 0&&e.count>0&&h.jsxs("span",{className:"text-xs opacity-60 ml-1",children:["(",e.count,")"]})]})},Wk=({value:t,options:e=[],onChange:n,creatable:r=!1,placeholder:s="검색 또는 입력...",noOptionsMessage:o="검색 결과가 없습니다",formatCreateLabel:i,createLabelSuffix:a="추가",maxItems:l,disabled:c=!1,onBeforeRemove:d,onCreateOption:u,className:f="",tagVariants:g,defaultVariant:m="blue",name:p,isMulti:y=!0,isClearable:b=!0,isSearchable:_=!0,delimiters:x=[","],splitOnPaste:E=!0,onInputChange:C,id:k,editorAttrs:N})=>{const S=c===!0||c==="true",w=v.useCallback(J=>J==null?[]:Array.isArray(J)?J:[J],[]),[M,T]=v.useState(()=>w(t)),[P,L]=v.useState(""),$=v.useRef(void 0);v.useEffect(()=>{const J=(ue,ae)=>!ue&&!ae?!0:!ue||!ae||ue.length!==ae.length?!1:ue.every((ve,fe)=>ve===ae[fe]),le=w(t);J(le,$.current)||(T(le),$.current=le)},[t,w]);const A=v.useMemo(()=>i?typeof i=="string"?J=>`${i} "${J}"`:i:J=>h.jsxs("span",{className:"text-[13px]",children:[h.jsx("strong",{children:J})," ",a]}),[i,a]),D=v.useMemo(()=>!M||!Array.isArray(M)?[]:M.map(J=>e.find(ue=>ue.value===J)||{value:J,label:String(J)}).filter(J=>J!==void 0),[M,e]),O=v.useMemo(()=>{const J=new Set(e.map(we=>we.value)),le=M?M.filter(we=>!J.has(we)).map(we=>({value:we,label:String(we)})):[],ue=[...e,...le];if(!ue.some(we=>we.group))return ue;const ve=new Map,fe=[];ue.forEach(we=>{if(we.group){const He=ve.get(we.group)||[];He.push(we),ve.set(we.group,He)}else fe.push(we)});const ye=[];return ve.forEach((we,He)=>{ye.push({label:He,options:we})}),fe.length>0&&ye.push({label:"",options:fe}),ye},[e,M]),z=v.useCallback(J=>({target:{value:J},currentTarget:{value:J},preventDefault:()=>{},stopPropagation:()=>{}}),[]),H=v.useCallback(J=>{if(!x?.length)return[J.trim()].filter(Boolean);const le=x.map(ae=>ae.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")),ue=new RegExp(`[${le.join("")}]`);return J.split(ue).map(ae=>ae.trim()).filter(Boolean)},[x]),j=v.useCallback((J,le)=>{if(le.action!=="input-change"){L(J);return}if(C&&C(z(J)),!x?.some(fe=>J.includes(fe))||!r||!y){L(J);return}const ve=H(J).filter(fe=>fe&&!M.includes(fe));if(ve.length>0){const fe=l?l-M.length:1/0,ye=ve.slice(0,fe);if(ye.length>0){const we=[...M,...ye];T(we),n(z(we))}}L("")},[x,r,y,H,M,l,n,z,C]),W=v.useCallback(J=>{if(!E||!r||!y)return;const le=J.clipboardData.getData("text"),ue=H(le);if(ue.length<=1)return;J.preventDefault();const ae=ue.filter(ve=>ve&&!M.includes(ve));if(ae.length>0){const ve=l?l-M.length:1/0,fe=ae.slice(0,ve);if(fe.length>0){const ye=[...M,...fe];T(ye),n(z(ye))}}},[E,r,y,H,M,l,n,z]),U=v.useCallback((J,le)=>{if((le.action==="remove-value"||le.action==="pop-value")&&le.removedValue&&d){const ae=d(le.removedValue);if(ae!==!0){const ve=typeof ae=="string"?ae:`"${le.removedValue.label}"은(는) 제거할 수 없습니다.`;alert(ve);return}}if(l&&J.length>l)return;const ue=J.map(ae=>ae.value);T(ue),n(z(ue))},[n,d,l,z]),B=v.useCallback((J,le)=>{if(le.action==="clear"&&M.length>0&&d){const ve=e.find(ye=>ye.value===M[0])||{value:M[0],label:String(M[0])},fe=d(ve);if(fe!==!0){const ye=typeof fe=="string"?fe:`"${ve.label}"은(는) 제거할 수 없습니다.`;alert(ye);return}}const ue=J?J.value:null,ae=J?[J.value]:[];T(ae),n(z(ue))},[n,d,M,e,z]),G=v.useCallback(J=>{u&&u(J);const le=[...M||[],J];T(le),n(z(le))},[n,u,M,z]),V=v.useCallback(J=>!J||!J.trim()?!1:!M?.includes(J.trim()),[M]),X=v.useCallback(J=>{const le=P.trim();if(J.key==="Tab"&&r&&y&&le){if(M.includes(le))J.preventDefault(),L("");else if(!l||M.lengthg&&g[J]?g[J]:m,[g,m]),Z={control:()=>`${$t.control} ${f} ${S?"opacity-60 cursor-not-allowed":""}`,valueContainer:()=>$t.valueContainer,multiValue:J=>{const le=K(J.data.value);return $t.multiValue[le]},multiValueLabel:J=>{const le=K(J.data.value);return $t.multiValueLabel[le]},multiValueRemove:J=>{const le=K(J.data.value);return`${$t.multiValueRemove[le]} ${S?"pointer-events-none":""}`},singleValue:()=>"text-gray-900 dark:text-white text-sm",input:()=>$t.input,placeholder:()=>$t.placeholder,menu:()=>$t.menu,menuList:()=>$t.menuList,menuPortal:()=>"z-[9999]",option:J=>`${$t.option.base} ${J.isSelected?$t.option.selected:J.isFocused?$t.option.focused:$t.option.default}`,noOptionsMessage:()=>$t.noOptionsMessage,clearIndicator:()=>"text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-pointer px-1",dropdownIndicator:()=>"text-gray-400 dark:text-gray-500 px-1"},ee={options:O,onKeyDown:X,placeholder:s,noOptionsMessage:()=>o,isDisabled:S,isSearchable:_,menuIsOpen:void 0,openMenuOnClick:!0,openMenuOnFocus:!0,menuPortalTarget:typeof document<"u"?document.body:null,menuPosition:"fixed",classNamePrefix:"tag-input",unstyled:!0,classNames:Z,...r&&y||C?{inputValue:P,onInputChange:j}:{}},ne=y?M.join(","):M[0]??"";if(!y){const J={...ee,isMulti:!1,value:D[0]||null,onChange:B,isClearable:b,closeMenuOnSelect:!0,components:{Option:Rf,GroupHeading:eh,IndicatorSeparator:()=>null,...b?{}:{ClearIndicator:()=>null}}};return r?h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ne}),h.jsx(Jf,{...J,onCreateOption:G,formatCreateLabel:A,isValidNewOption:V,createOptionPosition:"first"})]}):h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ne}),h.jsx(Zf,{...J})]})}const he={...ee,isMulti:!0,value:D,onChange:U,closeMenuOnSelect:!1,hideSelectedOptions:!1,backspaceRemovesValue:!1,isClearable:!1,components:{Option:Rf,GroupHeading:eh,MultiValueLabel:jk,ClearIndicator:()=>null,DropdownIndicator:()=>null,IndicatorSeparator:()=>null}};return r?h.jsxs(I,{id:k,...N,onPaste:W,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ne}),h.jsx(Jf,{...he,onCreateOption:G,formatCreateLabel:A,isValidNewOption:V,createOptionPosition:"first"})]}):h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ne}),h.jsx(Zf,{...he})]})},Yr=(t,e)=>window.G7Core?.t?.(t,e)??t,Gk=()=>window.G7Core?.locale?.supported?.()??["ko","en"],Yk=()=>window.G7Core?.locale?.current?.()??"ko",Vl=t=>{const e=Yr(`common.language_${t}`);return e===`common.language_${t}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[t]||t.toUpperCase():e},Kk=({value:t=[],onChange:e,placeholder:n="입력 후 Enter",disabled:r=!1,creatable:s=!0,delimiters:o=[","],className:i="",name:a,error:l,maxItems:c,modalId:d,statePath:u,__componentContext:f,id:g,editorAttrs:m})=>{const p=v.useMemo(()=>Gk(),[]),y=v.useMemo(()=>Yk(),[]),[b,_]=v.useState(()=>t||[]),[x,E]=v.useState(""),[C,k]=v.useState(!1),[N,S]=v.useState(null),[w,M]=v.useState({}),T=v.useRef(null),P=v.useRef(void 0);v.useEffect(()=>{const V=(K,Z)=>!K&&!Z?!0:!K||!Z||K.length!==Z.length?!1:JSON.stringify(K)===JSON.stringify(Z),X=t||[];V(X,P.current)||(_(X),P.current=X)},[t]);const L=v.useCallback(V=>({target:{name:a,value:V},currentTarget:{name:a,value:V},preventDefault:()=>{},stopPropagation:()=>{}}),[a]),$=v.useCallback(V=>{const X=[];for(const K of p){const Z=V[K];Z?.trim()&&X.push(`${K.toUpperCase()}: ${Z}`)}return X.length>0?X.join(" / "):""},[p]),A=v.useCallback(V=>{const X=b.filter((K,Z)=>Z!==V);_(X),e?.(L(X))},[b,e,L]),D=v.useCallback((V,X,K)=>{const Z=window.G7Core;if(!Z?.dispatch)return;const ee=p.map(ne=>({code:ne,label:Vl(ne)}));Z.dispatch({handler:"setState",params:{target:"global",multilingualTagEdit:{fieldName:a,editingIndex:V,values:X,supportedLocales:ee,defaultLocale:y,statePath:u,currentTags:K}}}),Z.dispatch({handler:"openModal",target:d},{componentContext:f})},[a,p,y,d,u,f]),O=v.useCallback(V=>{r||(d?D(V,{...b[V]},b):(S(V),M({...b[V]}),k(!0)))},[r,b,d,D]),z=v.useCallback((V="")=>{const X={};p.forEach(K=>{X[K]=K===y?V:""}),d?(D(null,X,b),E("")):(S(null),M(X),k(!0),E(""))},[p,y,d,D,b]),H=v.useCallback(()=>{if(!Object.values(w).some(K=>K.trim()!=="")){k(!1);return}let X;N!==null?X=b.map((K,Z)=>Z===N?w:K):X=[...b,w],_(X),e?.(L(X)),k(!1),M({}),S(null)},[w,N,b,e,L]),j=v.useCallback(()=>{k(!1),M({}),S(null)},[]),W=v.useCallback(V=>{const X=V.target.value;if(o.some(Z=>X.includes(Z))&&s){const Z=o.map(he=>he.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")),ee=new RegExp(`[${Z.join("")}]`),ne=X.split(ee).map(he=>he.trim()).filter(Boolean);if(ne.length>0){z(ne[0]);return}}E(X)},[o,s,z]),U=v.useCallback(V=>{if(V.key==="Enter"&&x.trim()&&s){if(V.preventDefault(),c&&b.length>=c)return;z(x.trim())}},[x,s,c,b.length,z]),B=v.useCallback((V,X)=>{M(K=>({...K,[V]:X}))},[]),G=!!l;return h.jsxs(I,{className:i,id:g,...m,children:[h.jsxs(I,{className:`flex flex-wrap gap-1.5 p-2 border rounded-lg bg-white dark:bg-gray-700 min-h-[42px] ${G?"border-red-500 dark:border-red-400":"border-gray-300 dark:border-gray-600 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500"} ${r?"opacity-60 bg-gray-100 dark:bg-gray-800 cursor-not-allowed":""}`,children:[b.map((V,X)=>h.jsxs(I,{className:"flex items-center gap-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded px-2 py-0.5 text-sm",children:[h.jsx(Q,{className:`cursor-pointer hover:underline ${r?"cursor-not-allowed":""}`,onClick:()=>O(X),children:$(V)}),!r&&h.jsx(oe,{type:"button",className:"p-0 h-4 w-4 min-w-0 border-0 bg-transparent text-blue-600 dark:text-blue-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-transparent",onClick:K=>{K.stopPropagation(),A(X)},children:h.jsx(re,{name:"times",className:"text-xs"})})]},X)),s&&!r&&(!c||b.lengthh.jsxs(I,{className:"space-y-1",children:[h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(Q,{className:"text-sm font-medium text-gray-700 dark:text-gray-300 w-16",children:Vl(V)}),h.jsxs(Q,{className:"text-xs text-gray-400 dark:text-gray-500",children:["(",V.toUpperCase(),")"]})]}),h.jsx(kt,{type:"text",value:w[V]??"",onChange:X=>B(V,X.target.value),placeholder:`${Vl(V)} 값 입력`,className:"w-full",autoFocus:V===y})]},V)),h.jsxs(I,{className:"flex justify-end gap-2 pt-4 border-t border-gray-200 dark:border-gray-700",children:[h.jsx(oe,{type:"button",variant:"secondary",onClick:j,children:Yr("common.cancel")}),h.jsx(oe,{type:"button",variant:"primary",onClick:H,children:Yr(N!==null?"common.save":"common.add")})]})]})}),a&&h.jsx("input",{type:"hidden",name:a,value:JSON.stringify(b)})]})},th=(t,e)=>window.G7Core?.t?.(t,e)??t,nh=({type:t,identifier:e,name:n,installedModules:r=[],installedPlugins:s=[],className:o="",style:i,id:a,editorAttrs:l})=>{const c={module:de.Cubes,plugin:de.PuzzlePiece},d={module:th("common.module"),plugin:th("common.plugin")},u=v.useMemo(()=>{if(n)return n;if(e){const p=(t==="module"?r:s)?.find(y=>y.identifier===e);if(p)return p.name}},[n,e,t,r,s]),f=c[t],g=u?`${u} ${d[t]}`:d[t];return h.jsxs(Q,{className:`inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium text-gray-400 dark:text-gray-500 ${o}`,style:i,id:a,...l,children:[h.jsx(re,{name:f,className:"w-3.5 h-3.5"}),h.jsx(Q,{children:g})]})},Xk=({options:t=[],value:e,onChange:n,multiple:r=!1,searchPlaceholder:s="Search...",onSearch:o,noResultsText:i="No results found",initialText:a="Type to search",triggerLabel:l="Select",triggerIcon:c,disabled:d=!1,className:u="",id:f,editorAttrs:g})=>{const[m,p]=v.useState(!1),[y,b]=v.useState(""),_=v.useRef(null),x=v.useRef(null),E=v.useRef(`searchable-dropdown-input-${Math.random().toString(36).slice(2,9)}`).current,C=v.useMemo(()=>e==null?[]:Array.isArray(e)?e:[e],[e]),k=v.useMemo(()=>{if(!y.trim())return t;const A=y.toLowerCase();return t.filter(D=>D.label.toLowerCase().includes(A)||D.description?.toLowerCase().includes(A))},[t,y]),N=v.useMemo(()=>{if(C.length===0)return l;if(r){const D=C.length;return D===1?t.find(z=>String(z.value)===String(C[0]))?.label||l:`${D} selected`}return t.find(D=>String(D.value)===String(C[0]))?.label||l},[C,t,r,l]);v.useEffect(()=>{const A=D=>{_.current&&!_.current.contains(D.target)&&p(!1)};return m&&document.addEventListener("mousedown",A),()=>{document.removeEventListener("mousedown",A)}},[m]),v.useEffect(()=>{const A=D=>{D.key==="Escape"&&m&&(p(!1),x.current?.focus())};return document.addEventListener("keydown",A),()=>document.removeEventListener("keydown",A)},[m]),v.useEffect(()=>{if(!m){b("");return}const A=setTimeout(()=>{document.getElementById(E)?.focus()},50);return()=>{clearTimeout(A)}},[m,E]);const S=v.useCallback(()=>{d||p(A=>!A)},[d]),w=v.useCallback(A=>{const D=A.target.value;b(D),o?.(D)},[o]),M=v.useCallback(A=>{if(r){const O=C.some(z=>String(z)===String(A))?C.filter(z=>String(z)!==String(A)):[...C,A];n?.(O)}else n?.(A),p(!1),x.current?.focus()},[r,C,n]),T=A=>C.some(D=>String(D)===String(A)),P=y.length===0&&t.length===0,L=y.length>0&&k.length===0,$=k.length>0;return h.jsxs(I,{ref:_,className:`relative inline-block ${u}`,id:f,...g,children:[h.jsxs(oe,{ref:x,type:"button",onClick:S,disabled:d,className:` +`]))),F1=function(t,n){var r=t.isFocused,s=t.size,o=t.theme,i=o.colors,a=o.spacing.baseUnit;return Se({label:"loadingIndicator",display:"flex",transition:"color 150ms",alignSelf:"center",fontSize:s,lineHeight:1,marginRight:s,textAlign:"center",verticalAlign:"middle"},n?{}:{color:r?i.neutral60:i.neutral20,padding:a*2})},$l=function(t){var n=t.delay,r=t.offset;return Ce("span",{css:Ol({animation:"".concat($1," 1s ease-in-out ").concat(n,"ms infinite;"),backgroundColor:"currentColor",borderRadius:"1em",display:"inline-block",marginLeft:r?"1em":void 0,height:"1em",verticalAlign:"top",width:"1em"},"","")})},z1=function(t){var n=t.innerProps,r=t.isRtl,s=t.size,o=s===void 0?4:s,i=Fn(t,N1);return Ce("div",Oe({},xt(Se(Se({},i),{},{innerProps:n,isRtl:r,size:o}),"loadingIndicator",{indicator:!0,"loading-indicator":!0}),n),Ce($l,{delay:0,offset:r}),Ce($l,{delay:160,offset:!0}),Ce($l,{delay:320,offset:!r}))},V1=function(t,n){var r=t.isDisabled,s=t.isFocused,o=t.theme,i=o.colors,a=o.borderRadius,l=o.spacing;return Se({label:"control",alignItems:"center",cursor:"default",display:"flex",flexWrap:"wrap",justifyContent:"space-between",minHeight:l.controlHeight,outline:"0 !important",position:"relative",transition:"all 100ms"},n?{}:{backgroundColor:r?i.neutral5:i.neutral0,borderColor:r?i.neutral10:s?i.primary:i.neutral20,borderRadius:a,borderStyle:"solid",borderWidth:1,boxShadow:s?"0 0 0 1px ".concat(i.primary):void 0,"&:hover":{borderColor:s?i.primary:i.neutral30}})},H1=function(t){var n=t.children,r=t.isDisabled,s=t.isFocused,o=t.innerRef,i=t.innerProps,a=t.menuIsOpen;return Ce("div",Oe({ref:o},xt(t,"control",{control:!0,"control--is-disabled":r,"control--is-focused":s,"control--menu-is-open":a}),i,{"aria-disabled":r||void 0}),n)},U1=H1,B1=["data"],j1=function(t,n){var r=t.theme.spacing;return n?{}:{paddingBottom:r.baseUnit*2,paddingTop:r.baseUnit*2}},W1=function(t){var n=t.children,r=t.cx,s=t.getStyles,o=t.getClassNames,i=t.Heading,a=t.headingProps,l=t.innerProps,c=t.label,d=t.theme,u=t.selectProps;return Ce("div",Oe({},xt(t,"group",{group:!0}),l),Ce(i,Oe({},a,{selectProps:u,theme:d,getStyles:s,getClassNames:o,cx:r}),c),Ce("div",null,n))},G1=function(t,n){var r=t.theme,s=r.colors,o=r.spacing;return Se({label:"group",cursor:"default",display:"block"},n?{}:{color:s.neutral40,fontSize:"75%",fontWeight:500,marginBottom:"0.25em",paddingLeft:o.baseUnit*3,paddingRight:o.baseUnit*3,textTransform:"uppercase"})},Y1=function(t){var n=Cf(t);n.data;var r=Fn(n,B1);return Ce("div",Oe({},xt(t,"groupHeading",{"group-heading":!0}),r))},X1=W1,K1=["innerRef","isDisabled","isHidden","inputClassName"],q1=function(t,n){var r=t.isDisabled,s=t.value,o=t.theme,i=o.spacing,a=o.colors;return Se(Se({visibility:r?"hidden":"visible",transform:s?"translateZ(0)":""},Z1),n?{}:{margin:i.baseUnit/2,paddingBottom:i.baseUnit/2,paddingTop:i.baseUnit/2,color:a.neutral80})},Pf={gridArea:"1 / 2",font:"inherit",minWidth:"2px",border:0,margin:0,outline:0,padding:0},Z1={flex:"1 1 auto",display:"inline-grid",gridArea:"1 / 1 / 2 / 3",gridTemplateColumns:"0 min-content","&:after":Se({content:'attr(data-value) " "',visibility:"hidden",whiteSpace:"pre"},Pf)},Q1=function(t){return Se({label:"input",color:"inherit",background:0,opacity:t?0:1,width:"100%"},Pf)},J1=function(t){var n=t.cx,r=t.value,s=Cf(t),o=s.innerRef,i=s.isDisabled,a=s.isHidden,l=s.inputClassName,c=Fn(s,K1);return Ce("div",Oe({},xt(t,"input",{"input-container":!0}),{"data-value":r||""}),Ce("input",Oe({className:n({input:!0},l),ref:o,style:Q1(a),disabled:i},c)))},R1=J1,ek=function(t,n){var r=t.theme,s=r.spacing,o=r.borderRadius,i=r.colors;return Se({label:"multiValue",display:"flex",minWidth:0},n?{}:{backgroundColor:i.neutral10,borderRadius:o/2,margin:s.baseUnit/2})},tk=function(t,n){var r=t.theme,s=r.borderRadius,o=r.colors,i=t.cropWithEllipsis;return Se({overflow:"hidden",textOverflow:i||i===void 0?"ellipsis":void 0,whiteSpace:"nowrap"},n?{}:{borderRadius:s/2,color:o.neutral80,fontSize:"85%",padding:3,paddingLeft:6})},nk=function(t,n){var r=t.theme,s=r.spacing,o=r.borderRadius,i=r.colors,a=t.isFocused;return Se({alignItems:"center",display:"flex"},n?{}:{borderRadius:o/2,backgroundColor:a?i.dangerLight:void 0,paddingLeft:s.baseUnit,paddingRight:s.baseUnit,":hover":{backgroundColor:i.dangerLight,color:i.danger}})},$f=function(t){var n=t.children,r=t.innerProps;return Ce("div",r,n)},rk=$f,sk=$f;function ok(e){var t=e.children,n=e.innerProps;return Ce("div",Oe({role:"button"},n),t||Ce(Pl,{size:14}))}var ik=function(t){var n=t.children,r=t.components,s=t.data,o=t.innerProps,i=t.isDisabled,a=t.removeProps,l=t.selectProps,c=r.Container,d=r.Label,u=r.Remove;return Ce(c,{data:s,innerProps:Se(Se({},xt(t,"multiValue",{"multi-value":!0,"multi-value--is-disabled":i})),o),selectProps:l},Ce(d,{data:s,innerProps:Se({},xt(t,"multiValueLabel",{"multi-value__label":!0})),selectProps:l},n),Ce(u,{data:s,innerProps:Se(Se({},xt(t,"multiValueRemove",{"multi-value__remove":!0})),{},{"aria-label":"Remove ".concat(n||"option")},a),selectProps:l}))},ak=ik,lk=function(t,n){var r=t.isDisabled,s=t.isFocused,o=t.isSelected,i=t.theme,a=i.spacing,l=i.colors;return Se({label:"option",cursor:"default",display:"block",fontSize:"inherit",width:"100%",userSelect:"none",WebkitTapHighlightColor:"rgba(0, 0, 0, 0)"},n?{}:{backgroundColor:o?l.primary:s?l.primary25:"transparent",color:r?l.neutral20:o?l.neutral0:"inherit",padding:"".concat(a.baseUnit*2,"px ").concat(a.baseUnit*3,"px"),":active":{backgroundColor:r?void 0:o?l.primary:l.primary50}})},ck=function(t){var n=t.children,r=t.isDisabled,s=t.isFocused,o=t.isSelected,i=t.innerRef,a=t.innerProps;return Ce("div",Oe({},xt(t,"option",{option:!0,"option--is-disabled":r,"option--is-focused":s,"option--is-selected":o}),{ref:i,"aria-disabled":r},a),n)},dk=ck,uk=function(t,n){var r=t.theme,s=r.spacing,o=r.colors;return Se({label:"placeholder",gridArea:"1 / 1 / 2 / 3"},n?{}:{color:o.neutral50,marginLeft:s.baseUnit/2,marginRight:s.baseUnit/2})},fk=function(t){var n=t.children,r=t.innerProps;return Ce("div",Oe({},xt(t,"placeholder",{placeholder:!0}),r),n)},hk=fk,gk=function(t,n){var r=t.isDisabled,s=t.theme,o=s.spacing,i=s.colors;return Se({label:"singleValue",gridArea:"1 / 1 / 2 / 3",maxWidth:"100%",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},n?{}:{color:r?i.neutral40:i.neutral80,marginLeft:o.baseUnit/2,marginRight:o.baseUnit/2})},pk=function(t){var n=t.children,r=t.isDisabled,s=t.innerProps;return Ce("div",Oe({},xt(t,"singleValue",{"single-value":!0,"single-value--is-disabled":r}),s),n)},mk=pk,wi={ClearIndicator:I1,Control:U1,DropdownIndicator:A1,DownChevron:If,CrossIcon:Pl,Group:X1,GroupHeading:Y1,IndicatorsContainer:E1,IndicatorSeparator:P1,Input:R1,LoadingIndicator:z1,Menu:f1,MenuList:g1,MenuPortal:x1,LoadingMessage:y1,NoOptionsMessage:b1,MultiValue:ak,MultiValueContainer:rk,MultiValueLabel:sk,MultiValueRemove:ok,Option:dk,Placeholder:hk,SelectContainer:k1,SingleValue:mk,ValueContainer:S1},bk=function(t){return Se(Se({},wi),t.components)},Ff=Number.isNaN||function(t){return typeof t=="number"&&t!==t};function yk(e,t){return!!(e===t||Ff(e)&&Ff(t))}function vk(e,t){if(e.length!==t.length)return!1;for(var n=0;n1?"s":""," ").concat(o.join(","),", selected.");case"select-option":return i?"option ".concat(s," is disabled. Select another option."):"option ".concat(s,", selected.");default:return""}},onFocus:function(t){var n=t.context,r=t.focused,s=t.options,o=t.label,i=o===void 0?"":o,a=t.selectValue,l=t.isDisabled,c=t.isSelected,d=t.isAppleDevice,u=function(p,y){return p&&p.length?"".concat(p.indexOf(y)+1," of ").concat(p.length):""};if(n==="value"&&a)return"value ".concat(i," focused, ").concat(u(a,r),".");if(n==="menu"&&d){var f=l?" disabled":"",g="".concat(c?" selected":"").concat(f);return"".concat(i).concat(g,", ").concat(u(s,r),".")}return""},onFilter:function(t){var n=t.inputValue,r=t.resultsMessage;return"".concat(r).concat(n?" for search term "+n:"",".")}},Sk=function(t){var n=t.ariaSelection,r=t.focusedOption,s=t.focusedValue,o=t.focusableOptions,i=t.isFocused,a=t.selectValue,l=t.selectProps,c=t.id,d=t.isAppleDevice,u=l.ariaLiveMessages,f=l.getOptionLabel,g=l.inputValue,m=l.isMulti,p=l.isOptionDisabled,y=l.isSearchable,b=l.menuIsOpen,_=l.options,x=l.screenReaderStatus,M=l.tabSelectsValue,S=l.isLoading,k=l["aria-label"],N=l["aria-live"],C=v.useMemo(function(){return Se(Se({},Ck),u||{})},[u]),w=v.useMemo(function(){var D="";if(n&&C.onChange){var O=n.option,T=n.options,z=n.removedValue,U=n.removedValues,W=n.value,G=function(q){return Array.isArray(q)?null:q},B=z||O||G(W),j=B?f(B):"",Y=T||U||void 0,V=Y?Y.map(f):[],X=Se({isDisabled:B&&p(B,a),label:j,labels:V},n);D=C.onChange(X)}return D},[n,C,p,a,f]),E=v.useMemo(function(){var D="",O=r||s,T=!!(r&&a&&a.includes(r));if(O&&C.onFocus){var z={focused:O,label:f(O),isDisabled:p(O,a),isSelected:T,options:o,context:O===r?"menu":"value",selectValue:a,isAppleDevice:d};D=C.onFocus(z)}return D},[r,s,f,p,C,o,a,d]),A=v.useMemo(function(){var D="";if(b&&_.length&&!S&&C.onFilter){var O=x({count:o.length});D=C.onFilter({inputValue:g,resultsMessage:O})}return D},[o,g,b,C,_,x,S]),P=n?.action==="initial-input-focus",L=v.useMemo(function(){var D="";if(C.guidance){var O=s?"value":b?"menu":"input";D=C.guidance({"aria-label":k,context:O,isDisabled:r&&p(r,a),isMulti:m,isSearchable:y,tabSelectsValue:M,isInitialFocus:P})}return D},[k,r,s,m,p,y,b,C,a,M,P]),$=Ce(v.Fragment,null,Ce("span",{id:"aria-selection"},w),Ce("span",{id:"aria-focused"},E),Ce("span",{id:"aria-results"},A),Ce("span",{id:"aria-guidance"},L));return Ce(v.Fragment,null,Ce(zf,{id:c},P&&$),Ce(zf,{"aria-live":N,"aria-atomic":"false","aria-relevant":"additions text",role:"log"},i&&!P&&$))},_k=Sk,Fl=[{base:"A",letters:"AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ"},{base:"AA",letters:"Ꜳ"},{base:"AE",letters:"ÆǼǢ"},{base:"AO",letters:"Ꜵ"},{base:"AU",letters:"Ꜷ"},{base:"AV",letters:"ꜸꜺ"},{base:"AY",letters:"Ꜽ"},{base:"B",letters:"BⒷBḂḄḆɃƂƁ"},{base:"C",letters:"CⒸCĆĈĊČÇḈƇȻꜾ"},{base:"D",letters:"DⒹDḊĎḌḐḒḎĐƋƊƉꝹ"},{base:"DZ",letters:"DZDŽ"},{base:"Dz",letters:"DzDž"},{base:"E",letters:"EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ"},{base:"F",letters:"FⒻFḞƑꝻ"},{base:"G",letters:"GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ"},{base:"H",letters:"HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ"},{base:"I",letters:"IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ"},{base:"J",letters:"JⒿJĴɈ"},{base:"K",letters:"KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ"},{base:"L",letters:"LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ"},{base:"LJ",letters:"LJ"},{base:"Lj",letters:"Lj"},{base:"M",letters:"MⓂMḾṀṂⱮƜ"},{base:"N",letters:"NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ"},{base:"NJ",letters:"NJ"},{base:"Nj",letters:"Nj"},{base:"O",letters:"OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ"},{base:"OI",letters:"Ƣ"},{base:"OO",letters:"Ꝏ"},{base:"OU",letters:"Ȣ"},{base:"P",letters:"PⓅPṔṖƤⱣꝐꝒꝔ"},{base:"Q",letters:"QⓆQꝖꝘɊ"},{base:"R",letters:"RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ"},{base:"S",letters:"SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ"},{base:"T",letters:"TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ"},{base:"TZ",letters:"Ꜩ"},{base:"U",letters:"UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ"},{base:"V",letters:"VⓋVṼṾƲꝞɅ"},{base:"VY",letters:"Ꝡ"},{base:"W",letters:"WⓌWẀẂŴẆẄẈⱲ"},{base:"X",letters:"XⓍXẊẌ"},{base:"Y",letters:"YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ"},{base:"Z",letters:"ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ"},{base:"a",letters:"aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ"},{base:"aa",letters:"ꜳ"},{base:"ae",letters:"æǽǣ"},{base:"ao",letters:"ꜵ"},{base:"au",letters:"ꜷ"},{base:"av",letters:"ꜹꜻ"},{base:"ay",letters:"ꜽ"},{base:"b",letters:"bⓑbḃḅḇƀƃɓ"},{base:"c",letters:"cⓒcćĉċčçḉƈȼꜿↄ"},{base:"d",letters:"dⓓdḋďḍḑḓḏđƌɖɗꝺ"},{base:"dz",letters:"dzdž"},{base:"e",letters:"eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ"},{base:"f",letters:"fⓕfḟƒꝼ"},{base:"g",letters:"gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ"},{base:"h",letters:"hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ"},{base:"hv",letters:"ƕ"},{base:"i",letters:"iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı"},{base:"j",letters:"jⓙjĵǰɉ"},{base:"k",letters:"kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ"},{base:"l",letters:"lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ"},{base:"lj",letters:"lj"},{base:"m",letters:"mⓜmḿṁṃɱɯ"},{base:"n",letters:"nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ"},{base:"nj",letters:"nj"},{base:"o",letters:"oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ"},{base:"oi",letters:"ƣ"},{base:"ou",letters:"ȣ"},{base:"oo",letters:"ꝏ"},{base:"p",letters:"pⓟpṕṗƥᵽꝑꝓꝕ"},{base:"q",letters:"qⓠqɋꝗꝙ"},{base:"r",letters:"rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ"},{base:"s",letters:"sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ"},{base:"t",letters:"tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ"},{base:"tz",letters:"ꜩ"},{base:"u",letters:"uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ"},{base:"v",letters:"vⓥvṽṿʋꝟʌ"},{base:"vy",letters:"ꝡ"},{base:"w",letters:"wⓦwẁẃŵẇẅẘẉⱳ"},{base:"x",letters:"xⓧxẋẍ"},{base:"y",letters:"yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ"},{base:"z",letters:"zⓩzźẑżžẓẕƶȥɀⱬꝣ"}],Ek=new RegExp("["+Fl.map(function(e){return e.letters}).join("")+"]","g"),Vf={},zl=0;zl-1}},Ok=["innerRef"];function Ak(e){var t=e.innerRef,n=Fn(e,Ok),r=s1(n,"onExited","in","enter","exit","appear");return Ce("input",Oe({ref:t},r,{css:Ol({label:"dummyInput",background:0,border:0,caretColor:"transparent",fontSize:"inherit",gridArea:"1 / 1 / 2 / 3",outline:0,padding:0,width:1,color:"transparent",left:-100,opacity:0,position:"relative",transform:"scale(.01)"},"","")}))}var Dk=function(t){t.cancelable&&t.preventDefault(),t.stopPropagation()};function Ik(e){var t=e.isEnabled,n=e.onBottomArrive,r=e.onBottomLeave,s=e.onTopArrive,o=e.onTopLeave,i=v.useRef(!1),a=v.useRef(!1),l=v.useRef(0),c=v.useRef(null),d=v.useCallback(function(y,b){if(c.current!==null){var _=c.current,x=_.scrollTop,M=_.scrollHeight,S=_.clientHeight,k=c.current,N=b>0,C=M-S-x,w=!1;C>b&&i.current&&(r&&r(y),i.current=!1),N&&a.current&&(o&&o(y),a.current=!1),N&&b>C?(n&&!i.current&&n(y),k.scrollTop=M,w=!0,i.current=!0):!N&&-b>x&&(s&&!a.current&&s(y),k.scrollTop=0,w=!0,a.current=!0),w&&Dk(y)}},[n,r,s,o]),u=v.useCallback(function(y){d(y,y.deltaY)},[d]),f=v.useCallback(function(y){l.current=y.changedTouches[0].clientY},[]),g=v.useCallback(function(y){var b=l.current-y.changedTouches[0].clientY;d(y,b)},[d]),m=v.useCallback(function(y){if(y){var b=t1?{passive:!1}:!1;y.addEventListener("wheel",u,b),y.addEventListener("touchstart",f,b),y.addEventListener("touchmove",g,b)}},[g,f,u]),p=v.useCallback(function(y){y&&(y.removeEventListener("wheel",u,!1),y.removeEventListener("touchstart",f,!1),y.removeEventListener("touchmove",g,!1))},[g,f,u]);return v.useEffect(function(){if(t){var y=c.current;return m(y),function(){p(y)}}},[t,m,p]),function(y){c.current=y}}var Bf=["boxSizing","height","overflow","paddingRight","position"],jf={boxSizing:"border-box",overflow:"hidden",position:"relative",height:"100%"};function Wf(e){e.cancelable&&e.preventDefault()}function Gf(e){e.stopPropagation()}function Yf(){var e=this.scrollTop,t=this.scrollHeight,n=e+this.offsetHeight;e===0?this.scrollTop=1:n===t&&(this.scrollTop=e-1)}function Xf(){return"ontouchstart"in window||navigator.maxTouchPoints}var Kf=!!(typeof window<"u"&&window.document&&window.document.createElement),Zs=0,os={capture:!1,passive:!1};function Lk(e){var t=e.isEnabled,n=e.accountForScrollbars,r=n===void 0?!0:n,s=v.useRef({}),o=v.useRef(null),i=v.useCallback(function(l){if(Kf){var c=document.body,d=c&&c.style;if(r&&Bf.forEach(function(m){var p=d&&d[m];s.current[m]=p}),r&&Zs<1){var u=parseInt(s.current.paddingRight,10)||0,f=document.body?document.body.clientWidth:0,g=window.innerWidth-f+u||0;Object.keys(jf).forEach(function(m){var p=jf[m];d&&(d[m]=p)}),d&&(d.paddingRight="".concat(g,"px"))}c&&Xf()&&(c.addEventListener("touchmove",Wf,os),l&&(l.addEventListener("touchstart",Yf,os),l.addEventListener("touchmove",Gf,os))),Zs+=1}},[r]),a=v.useCallback(function(l){if(Kf){var c=document.body,d=c&&c.style;Zs=Math.max(Zs-1,0),r&&Zs<1&&Bf.forEach(function(u){var f=s.current[u];d&&(d[u]=f)}),c&&Xf()&&(c.removeEventListener("touchmove",Wf,os),l&&(l.removeEventListener("touchstart",Yf,os),l.removeEventListener("touchmove",Gf,os)))}},[r]);return v.useEffect(function(){if(t){var l=o.current;return i(l),function(){a(l)}}},[t,i,a]),function(l){o.current=l}}var Pk=function(t){var n=t.target;return n.ownerDocument.activeElement&&n.ownerDocument.activeElement.blur()},$k={name:"1kfdb0e",styles:"position:fixed;left:0;bottom:0;right:0;top:0"};function Fk(e){var t=e.children,n=e.lockEnabled,r=e.captureEnabled,s=r===void 0?!0:r,o=e.onBottomArrive,i=e.onBottomLeave,a=e.onTopArrive,l=e.onTopLeave,c=Ik({isEnabled:s,onBottomArrive:o,onBottomLeave:i,onTopArrive:a,onTopLeave:l}),d=Lk({isEnabled:n}),u=function(g){c(g),d(g)};return Ce(v.Fragment,null,n&&Ce("div",{onClick:Pk,css:$k}),t(u))}var zk={name:"1a0ro4n-requiredInput",styles:"label:requiredInput;opacity:0;pointer-events:none;position:absolute;bottom:0;left:0;right:0;width:100%"},Vk=function(t){var n=t.name,r=t.onFocus;return Ce("input",{required:!0,name:n,tabIndex:-1,"aria-hidden":"true",onFocus:r,css:zk,value:"",onChange:function(){}})},Hk=Vk;function Ul(e){var t;return typeof window<"u"&&window.navigator!=null?e.test(((t=window.navigator.userAgentData)===null||t===void 0?void 0:t.platform)||window.navigator.platform):!1}function Uk(){return Ul(/^iPhone/i)}function qf(){return Ul(/^Mac/i)}function Bk(){return Ul(/^iPad/i)||qf()&&navigator.maxTouchPoints>1}function jk(){return Uk()||Bk()}function Wk(){return qf()||jk()}var Gk=function(t){return t.label},Zf=function(t){return t.label},Qf=function(t){return t.value},Yk=function(t){return!!t.isDisabled},Xk={clearIndicator:D1,container:w1,control:V1,dropdownIndicator:O1,group:j1,groupHeading:G1,indicatorsContainer:_1,indicatorSeparator:L1,input:q1,loadingIndicator:F1,loadingMessage:m1,menu:c1,menuList:h1,menuPortal:v1,multiValue:ek,multiValueLabel:tk,multiValueRemove:nk,noOptionsMessage:p1,option:lk,placeholder:uk,singleValue:gk,valueContainer:C1},Kk={primary:"#2684FF",primary75:"#4C9AFF",primary50:"#B2D4FF",primary25:"#DEEBFF",danger:"#DE350B",dangerLight:"#FFBDAD",neutral0:"hsl(0, 0%, 100%)",neutral5:"hsl(0, 0%, 95%)",neutral10:"hsl(0, 0%, 90%)",neutral20:"hsl(0, 0%, 80%)",neutral30:"hsl(0, 0%, 70%)",neutral40:"hsl(0, 0%, 60%)",neutral50:"hsl(0, 0%, 50%)",neutral60:"hsl(0, 0%, 40%)",neutral70:"hsl(0, 0%, 30%)",neutral80:"hsl(0, 0%, 20%)",neutral90:"hsl(0, 0%, 10%)"},qk=4,Jf=4,Zk=38,Qk=Jf*2,Jk={baseUnit:Jf,controlHeight:Zk,menuGutter:Qk},Bl={borderRadius:qk,colors:Kk,spacing:Jk},Rk={"aria-live":"polite",backspaceRemovesValue:!0,blurInputOnSelect:Ef(),captureMenuScroll:!Ef(),classNames:{},closeMenuOnSelect:!0,closeMenuOnScroll:!1,components:{},controlShouldRenderValue:!0,escapeClearsValue:!1,filterOption:Tk(),formatGroupLabel:Gk,getOptionLabel:Zf,getOptionValue:Qf,isDisabled:!1,isLoading:!1,isMulti:!1,isRtl:!1,isSearchable:!0,isOptionDisabled:Yk,loadingMessage:function(){return"Loading..."},maxMenuHeight:300,minMenuHeight:140,menuIsOpen:!1,menuPlacement:"bottom",menuPosition:"absolute",menuShouldBlockScroll:!1,menuShouldScrollIntoView:!Rw(),noOptionsMessage:function(){return"No options"},openMenuOnFocus:!1,openMenuOnClick:!0,options:[],pageSize:5,placeholder:"Select...",screenReaderStatus:function(t){var n=t.count;return"".concat(n," result").concat(n!==1?"s":""," available")},styles:{},tabIndex:0,tabSelectsValue:!0,unstyled:!1};function Rf(e,t,n,r){var s=sh(e,t,n),o=oh(e,t,n),i=rh(e,t),a=Ci(e,t);return{type:"option",data:t,isDisabled:s,isSelected:o,label:i,value:a,index:r}}function ki(e,t){return e.options.map(function(n,r){if("options"in n){var s=n.options.map(function(i,a){return Rf(e,i,t,a)}).filter(function(i){return nh(e,i)});return s.length>0?{type:"group",data:n,options:s,index:r}:void 0}var o=Rf(e,n,t,r);return nh(e,o)?o:void 0}).filter(n1)}function eh(e){return e.reduce(function(t,n){return n.type==="group"?t.push.apply(t,ts(n.options.map(function(r){return r.data}))):t.push(n.data),t},[])}function th(e,t){return e.reduce(function(n,r){return r.type==="group"?n.push.apply(n,ts(r.options.map(function(s){return{data:s.data,id:"".concat(t,"-").concat(r.index,"-").concat(s.index)}}))):n.push({data:r.data,id:"".concat(t,"-").concat(r.index)}),n},[])}function eC(e,t){return eh(ki(e,t))}function nh(e,t){var n=e.inputValue,r=n===void 0?"":n,s=t.data,o=t.isSelected,i=t.label,a=t.value;return(!ah(e)||!o)&&ih(e,{label:i,value:a,data:s},r)}function tC(e,t){var n=e.focusedValue,r=e.selectValue,s=r.indexOf(n);if(s>-1){var o=t.indexOf(n);if(o>-1)return n;if(s-1?n:t[0]}var jl=function(t,n){var r,s=(r=t.find(function(o){return o.data===n}))===null||r===void 0?void 0:r.id;return s||null},rh=function(t,n){return t.getOptionLabel(n)},Ci=function(t,n){return t.getOptionValue(n)};function sh(e,t,n){return typeof e.isOptionDisabled=="function"?e.isOptionDisabled(t,n):!1}function oh(e,t,n){if(n.indexOf(t)>-1)return!0;if(typeof e.isOptionSelected=="function")return e.isOptionSelected(t,n);var r=Ci(e,t);return n.some(function(s){return Ci(e,s)===r})}function ih(e,t,n){return e.filterOption?e.filterOption(t,n):!0}var ah=function(t){var n=t.hideSelectedOptions,r=t.isMulti;return n===void 0?r:n},rC=1,Wl=(function(e){Ox(n,e);var t=Ix(n);function n(r){var s;if(Nx(this,n),s=t.call(this,r),s.state={ariaSelection:null,focusedOption:null,focusedOptionId:null,focusableOptionsWithIds:[],focusedValue:null,inputIsHidden:!1,isFocused:!1,selectValue:[],clearFocusValueOnUpdate:!1,prevWasFocused:!1,inputIsHiddenAfterUpdate:void 0,prevProps:void 0,instancePrefix:"",isAppleDevice:!1},s.blockOptionHover=!1,s.isComposing=!1,s.commonProps=void 0,s.initialTouchX=0,s.initialTouchY=0,s.openAfterFocus=!1,s.scrollToFocusedOptionOnUpdate=!1,s.userIsDragging=void 0,s.controlRef=null,s.getControlRef=function(l){s.controlRef=l},s.focusedOptionRef=null,s.getFocusedOptionRef=function(l){s.focusedOptionRef=l},s.menuListRef=null,s.getMenuListRef=function(l){s.menuListRef=l},s.inputRef=null,s.getInputRef=function(l){s.inputRef=l},s.focus=s.focusInput,s.blur=s.blurInput,s.onChange=function(l,c){var d=s.props,u=d.onChange,f=d.name;c.name=f,s.ariaOnChange(l,c),u(l,c)},s.setValue=function(l,c,d){var u=s.props,f=u.closeMenuOnSelect,g=u.isMulti,m=u.inputValue;s.onInputChange("",{action:"set-value",prevInputValue:m}),f&&(s.setState({inputIsHiddenAfterUpdate:!g}),s.onMenuClose()),s.setState({clearFocusValueOnUpdate:!0}),s.onChange(l,{action:c,option:d})},s.selectOption=function(l){var c=s.props,d=c.blurInputOnSelect,u=c.isMulti,f=c.name,g=s.state.selectValue,m=u&&s.isOptionSelected(l,g),p=s.isOptionDisabled(l,g);if(m){var y=s.getOptionValue(l);s.setValue(g.filter(function(b){return s.getOptionValue(b)!==y}),"deselect-option",l)}else if(!p)u?s.setValue([].concat(ts(g),[l]),"select-option",l):s.setValue(l,"select-option");else{s.ariaOnChange(l,{action:"select-option",option:l,name:f});return}d&&s.blurInput()},s.removeValue=function(l){var c=s.props.isMulti,d=s.state.selectValue,u=s.getOptionValue(l),f=d.filter(function(m){return s.getOptionValue(m)!==u}),g=qs(c,f,f[0]||null);s.onChange(g,{action:"remove-value",removedValue:l}),s.focusInput()},s.clearValue=function(){var l=s.state.selectValue;s.onChange(qs(s.props.isMulti,[],null),{action:"clear",removedValues:l})},s.popValue=function(){var l=s.props.isMulti,c=s.state.selectValue,d=c[c.length-1],u=c.slice(0,c.length-1),f=qs(l,u,u[0]||null);d&&s.onChange(f,{action:"pop-value",removedValue:d})},s.getFocusedOptionId=function(l){return jl(s.state.focusableOptionsWithIds,l)},s.getFocusableOptionsWithIds=function(){return th(ki(s.props,s.state.selectValue),s.getElementId("option"))},s.getValue=function(){return s.state.selectValue},s.cx=function(){for(var l=arguments.length,c=new Array(l),d=0;dg||f>g}},s.onTouchEnd=function(l){s.userIsDragging||(s.controlRef&&!s.controlRef.contains(l.target)&&s.menuListRef&&!s.menuListRef.contains(l.target)&&s.blurInput(),s.initialTouchX=0,s.initialTouchY=0)},s.onControlTouchEnd=function(l){s.userIsDragging||s.onControlMouseDown(l)},s.onClearIndicatorTouchEnd=function(l){s.userIsDragging||s.onClearIndicatorMouseDown(l)},s.onDropdownIndicatorTouchEnd=function(l){s.userIsDragging||s.onDropdownIndicatorMouseDown(l)},s.handleInputChange=function(l){var c=s.props.inputValue,d=l.currentTarget.value;s.setState({inputIsHiddenAfterUpdate:!1}),s.onInputChange(d,{action:"input-change",prevInputValue:c}),s.props.menuIsOpen||s.onMenuOpen()},s.onInputFocus=function(l){s.props.onFocus&&s.props.onFocus(l),s.setState({inputIsHiddenAfterUpdate:!1,isFocused:!0}),(s.openAfterFocus||s.props.openMenuOnFocus)&&s.openMenu("first"),s.openAfterFocus=!1},s.onInputBlur=function(l){var c=s.props.inputValue;if(s.menuListRef&&s.menuListRef.contains(document.activeElement)){s.inputRef.focus();return}s.props.onBlur&&s.props.onBlur(l),s.onInputChange("",{action:"input-blur",prevInputValue:c}),s.onMenuClose(),s.setState({focusedValue:null,isFocused:!1})},s.onOptionHover=function(l){if(!(s.blockOptionHover||s.state.focusedOption===l)){var c=s.getFocusableOptions(),d=c.indexOf(l);s.setState({focusedOption:l,focusedOptionId:d>-1?s.getFocusedOptionId(l):null})}},s.shouldHideSelectedOptions=function(){return ah(s.props)},s.onValueInputFocus=function(l){l.preventDefault(),l.stopPropagation(),s.focus()},s.onKeyDown=function(l){var c=s.props,d=c.isMulti,u=c.backspaceRemovesValue,f=c.escapeClearsValue,g=c.inputValue,m=c.isClearable,p=c.isDisabled,y=c.menuIsOpen,b=c.onKeyDown,_=c.tabSelectsValue,x=c.openMenuOnFocus,M=s.state,S=M.focusedOption,k=M.focusedValue,N=M.selectValue;if(!p&&!(typeof b=="function"&&(b(l),l.defaultPrevented))){switch(s.blockOptionHover=!0,l.key){case"ArrowLeft":if(!d||g)return;s.focusValue("previous");break;case"ArrowRight":if(!d||g)return;s.focusValue("next");break;case"Delete":case"Backspace":if(g)return;if(k)s.removeValue(k);else{if(!u)return;d?s.popValue():m&&s.clearValue()}break;case"Tab":if(s.isComposing||l.shiftKey||!y||!_||!S||x&&s.isOptionSelected(S,N))return;s.selectOption(S);break;case"Enter":if(l.keyCode===229)break;if(y){if(!S||s.isComposing)return;s.selectOption(S);break}return;case"Escape":y?(s.setState({inputIsHiddenAfterUpdate:!1}),s.onInputChange("",{action:"menu-close",prevInputValue:g}),s.onMenuClose()):m&&f&&s.clearValue();break;case" ":if(g)return;if(!y){s.openMenu("first");break}if(!S)return;s.selectOption(S);break;case"ArrowUp":y?s.focusOption("up"):s.openMenu("last");break;case"ArrowDown":y?s.focusOption("down"):s.openMenu("first");break;case"PageUp":if(!y)return;s.focusOption("pageup");break;case"PageDown":if(!y)return;s.focusOption("pagedown");break;case"Home":if(!y)return;s.focusOption("first");break;case"End":if(!y)return;s.focusOption("last");break;default:return}l.preventDefault()}},s.state.instancePrefix="react-select-"+(s.props.instanceId||++rC),s.state.selectValue=mi(r.value),r.menuIsOpen&&s.state.selectValue.length){var o=s.getFocusableOptionsWithIds(),i=s.buildFocusableOptions(),a=i.indexOf(s.state.selectValue[0]);s.state.focusableOptionsWithIds=o,s.state.focusedOption=i[a],s.state.focusedOptionId=jl(o,i[a])}return s}return Tx(n,[{key:"componentDidMount",value:function(){this.startListeningComposition(),this.startListeningToTouch(),this.props.closeMenuOnScroll&&document&&document.addEventListener&&document.addEventListener("scroll",this.onScroll,!0),this.props.autoFocus&&this.focusInput(),this.props.menuIsOpen&&this.state.focusedOption&&this.menuListRef&&this.focusedOptionRef&&_f(this.menuListRef,this.focusedOptionRef),Wk()&&this.setState({isAppleDevice:!0})}},{key:"componentDidUpdate",value:function(s){var o=this.props,i=o.isDisabled,a=o.menuIsOpen,l=this.state.isFocused;(l&&!i&&s.isDisabled||l&&a&&!s.menuIsOpen)&&this.focusInput(),l&&i&&!s.isDisabled?this.setState({isFocused:!1},this.onMenuClose):!l&&!i&&s.isDisabled&&this.inputRef===document.activeElement&&this.setState({isFocused:!0}),this.menuListRef&&this.focusedOptionRef&&this.scrollToFocusedOptionOnUpdate&&(_f(this.menuListRef,this.focusedOptionRef),this.scrollToFocusedOptionOnUpdate=!1)}},{key:"componentWillUnmount",value:function(){this.stopListeningComposition(),this.stopListeningToTouch(),document.removeEventListener("scroll",this.onScroll,!0)}},{key:"onMenuOpen",value:function(){this.props.onMenuOpen()}},{key:"onMenuClose",value:function(){this.onInputChange("",{action:"menu-close",prevInputValue:this.props.inputValue}),this.props.onMenuClose()}},{key:"onInputChange",value:function(s,o){this.props.onInputChange(s,o)}},{key:"focusInput",value:function(){this.inputRef&&this.inputRef.focus()}},{key:"blurInput",value:function(){this.inputRef&&this.inputRef.blur()}},{key:"openMenu",value:function(s){var o=this,i=this.state,a=i.selectValue,l=i.isFocused,c=this.buildFocusableOptions(),d=s==="first"?0:c.length-1;if(!this.props.isMulti){var u=c.indexOf(a[0]);u>-1&&(d=u)}this.scrollToFocusedOptionOnUpdate=!(l&&this.menuListRef),this.setState({inputIsHiddenAfterUpdate:!1,focusedValue:null,focusedOption:c[d],focusedOptionId:this.getFocusedOptionId(c[d])},function(){return o.onMenuOpen()})}},{key:"focusValue",value:function(s){var o=this.state,i=o.selectValue,a=o.focusedValue;if(this.props.isMulti){this.setState({focusedOption:null});var l=i.indexOf(a);a||(l=-1);var c=i.length-1,d=-1;if(i.length){switch(s){case"previous":l===0?d=0:l===-1?d=c:d=l-1;break;case"next":l>-1&&l0&&arguments[0]!==void 0?arguments[0]:"first",o=this.props.pageSize,i=this.state.focusedOption,a=this.getFocusableOptions();if(a.length){var l=0,c=a.indexOf(i);i||(c=-1),s==="up"?l=c>0?c-1:a.length-1:s==="down"?l=(c+1)%a.length:s==="pageup"?(l=c-o,l<0&&(l=0)):s==="pagedown"?(l=c+o,l>a.length-1&&(l=a.length-1)):s==="last"&&(l=a.length-1),this.scrollToFocusedOptionOnUpdate=!0,this.setState({focusedOption:a[l],focusedValue:null,focusedOptionId:this.getFocusedOptionId(a[l])})}}},{key:"getTheme",value:(function(){return this.props.theme?typeof this.props.theme=="function"?this.props.theme(Bl):Se(Se({},Bl),this.props.theme):Bl})},{key:"getCommonProps",value:function(){var s=this.clearValue,o=this.cx,i=this.getStyles,a=this.getClassNames,l=this.getValue,c=this.selectOption,d=this.setValue,u=this.props,f=u.isMulti,g=u.isRtl,m=u.options,p=this.hasValue();return{clearValue:s,cx:o,getStyles:i,getClassNames:a,getValue:l,hasValue:p,isMulti:f,isRtl:g,options:m,selectOption:c,selectProps:u,setValue:d,theme:this.getTheme()}}},{key:"hasValue",value:function(){var s=this.state.selectValue;return s.length>0}},{key:"hasOptions",value:function(){return!!this.getFocusableOptions().length}},{key:"isClearable",value:function(){var s=this.props,o=s.isClearable,i=s.isMulti;return o===void 0?i:o}},{key:"isOptionDisabled",value:function(s,o){return sh(this.props,s,o)}},{key:"isOptionSelected",value:function(s,o){return oh(this.props,s,o)}},{key:"filterOption",value:function(s,o){return ih(this.props,s,o)}},{key:"formatOptionLabel",value:function(s,o){if(typeof this.props.formatOptionLabel=="function"){var i=this.props.inputValue,a=this.state.selectValue;return this.props.formatOptionLabel(s,{context:o,inputValue:i,selectValue:a})}else return this.getOptionLabel(s)}},{key:"formatGroupLabel",value:function(s){return this.props.formatGroupLabel(s)}},{key:"startListeningComposition",value:(function(){document&&document.addEventListener&&(document.addEventListener("compositionstart",this.onCompositionStart,!1),document.addEventListener("compositionend",this.onCompositionEnd,!1))})},{key:"stopListeningComposition",value:function(){document&&document.removeEventListener&&(document.removeEventListener("compositionstart",this.onCompositionStart),document.removeEventListener("compositionend",this.onCompositionEnd))}},{key:"startListeningToTouch",value:(function(){document&&document.addEventListener&&(document.addEventListener("touchstart",this.onTouchStart,!1),document.addEventListener("touchmove",this.onTouchMove,!1),document.addEventListener("touchend",this.onTouchEnd,!1))})},{key:"stopListeningToTouch",value:function(){document&&document.removeEventListener&&(document.removeEventListener("touchstart",this.onTouchStart),document.removeEventListener("touchmove",this.onTouchMove),document.removeEventListener("touchend",this.onTouchEnd))}},{key:"renderInput",value:(function(){var s=this.props,o=s.isDisabled,i=s.isSearchable,a=s.inputId,l=s.inputValue,c=s.tabIndex,d=s.form,u=s.menuIsOpen,f=s.required,g=this.getComponents(),m=g.Input,p=this.state,y=p.inputIsHidden,b=p.ariaSelection,_=this.commonProps,x=a||this.getElementId("input"),M=Se(Se(Se({"aria-autocomplete":"list","aria-expanded":u,"aria-haspopup":!0,"aria-errormessage":this.props["aria-errormessage"],"aria-invalid":this.props["aria-invalid"],"aria-label":this.props["aria-label"],"aria-labelledby":this.props["aria-labelledby"],"aria-required":f,role:"combobox","aria-activedescendant":this.state.isAppleDevice?void 0:this.state.focusedOptionId||""},u&&{"aria-controls":this.getElementId("listbox")}),!i&&{"aria-readonly":!0}),this.hasValue()?b?.action==="initial-input-focus"&&{"aria-describedby":this.getElementId("live-region")}:{"aria-describedby":this.getElementId("placeholder")});return i?F.createElement(m,Oe({},_,{autoCapitalize:"none",autoComplete:"off",autoCorrect:"off",id:x,innerRef:this.getInputRef,isDisabled:o,isHidden:y,onBlur:this.onInputBlur,onChange:this.handleInputChange,onFocus:this.onInputFocus,spellCheck:"false",tabIndex:c,form:d,type:"text",value:l},M)):F.createElement(Ak,Oe({id:x,innerRef:this.getInputRef,onBlur:this.onInputBlur,onChange:pi,onFocus:this.onInputFocus,disabled:o,tabIndex:c,inputMode:"none",form:d,value:""},M))})},{key:"renderPlaceholderOrValue",value:function(){var s=this,o=this.getComponents(),i=o.MultiValue,a=o.MultiValueContainer,l=o.MultiValueLabel,c=o.MultiValueRemove,d=o.SingleValue,u=o.Placeholder,f=this.commonProps,g=this.props,m=g.controlShouldRenderValue,p=g.isDisabled,y=g.isMulti,b=g.inputValue,_=g.placeholder,x=this.state,M=x.selectValue,S=x.focusedValue,k=x.isFocused;if(!this.hasValue()||!m)return b?null:F.createElement(u,Oe({},f,{key:"placeholder",isDisabled:p,isFocused:k,innerProps:{id:this.getElementId("placeholder")}}),_);if(y)return M.map(function(C,w){var E=C===S,A="".concat(s.getOptionLabel(C),"-").concat(s.getOptionValue(C));return F.createElement(i,Oe({},f,{components:{Container:a,Label:l,Remove:c},isFocused:E,isDisabled:p,key:A,index:w,removeProps:{onClick:function(){return s.removeValue(C)},onTouchEnd:function(){return s.removeValue(C)},onMouseDown:function(L){L.preventDefault()}},data:C}),s.formatOptionLabel(C,"value"))});if(b)return null;var N=M[0];return F.createElement(d,Oe({},f,{data:N,isDisabled:p}),this.formatOptionLabel(N,"value"))}},{key:"renderClearIndicator",value:function(){var s=this.getComponents(),o=s.ClearIndicator,i=this.commonProps,a=this.props,l=a.isDisabled,c=a.isLoading,d=this.state.isFocused;if(!this.isClearable()||!o||l||!this.hasValue()||c)return null;var u={onMouseDown:this.onClearIndicatorMouseDown,onTouchEnd:this.onClearIndicatorTouchEnd,"aria-hidden":"true"};return F.createElement(o,Oe({},i,{innerProps:u,isFocused:d}))}},{key:"renderLoadingIndicator",value:function(){var s=this.getComponents(),o=s.LoadingIndicator,i=this.commonProps,a=this.props,l=a.isDisabled,c=a.isLoading,d=this.state.isFocused;if(!o||!c)return null;var u={"aria-hidden":"true"};return F.createElement(o,Oe({},i,{innerProps:u,isDisabled:l,isFocused:d}))}},{key:"renderIndicatorSeparator",value:function(){var s=this.getComponents(),o=s.DropdownIndicator,i=s.IndicatorSeparator;if(!o||!i)return null;var a=this.commonProps,l=this.props.isDisabled,c=this.state.isFocused;return F.createElement(i,Oe({},a,{isDisabled:l,isFocused:c}))}},{key:"renderDropdownIndicator",value:function(){var s=this.getComponents(),o=s.DropdownIndicator;if(!o)return null;var i=this.commonProps,a=this.props.isDisabled,l=this.state.isFocused,c={onMouseDown:this.onDropdownIndicatorMouseDown,onTouchEnd:this.onDropdownIndicatorTouchEnd,"aria-hidden":"true"};return F.createElement(o,Oe({},i,{innerProps:c,isDisabled:a,isFocused:l}))}},{key:"renderMenu",value:function(){var s=this,o=this.getComponents(),i=o.Group,a=o.GroupHeading,l=o.Menu,c=o.MenuList,d=o.MenuPortal,u=o.LoadingMessage,f=o.NoOptionsMessage,g=o.Option,m=this.commonProps,p=this.state.focusedOption,y=this.props,b=y.captureMenuScroll,_=y.inputValue,x=y.isLoading,M=y.loadingMessage,S=y.minMenuHeight,k=y.maxMenuHeight,N=y.menuIsOpen,C=y.menuPlacement,w=y.menuPosition,E=y.menuPortalTarget,A=y.menuShouldBlockScroll,P=y.menuShouldScrollIntoView,L=y.noOptionsMessage,$=y.onMenuScrollToTop,D=y.onMenuScrollToBottom;if(!N)return null;var O=function(j,Y){var V=j.type,X=j.data,K=j.isDisabled,q=j.isSelected,ne=j.label,ee=j.value,fe=p===X,te=K?void 0:function(){return s.onOptionHover(X)},le=K?void 0:function(){return s.selectOption(X)},ge="".concat(s.getElementId("option"),"-").concat(Y),ce={id:ge,onClick:le,onMouseMove:te,onMouseOver:te,tabIndex:-1,role:"option","aria-selected":s.state.isAppleDevice?void 0:q};return F.createElement(g,Oe({},m,{innerProps:ce,data:X,isDisabled:K,isSelected:q,key:ge,label:ne,type:V,value:ee,isFocused:fe,innerRef:fe?s.getFocusedOptionRef:void 0}),s.formatOptionLabel(j.data,"menu"))},T;if(this.hasOptions())T=this.getCategorizedOptions().map(function(B){if(B.type==="group"){var j=B.data,Y=B.options,V=B.index,X="".concat(s.getElementId("group"),"-").concat(V),K="".concat(X,"-heading");return F.createElement(i,Oe({},m,{key:X,data:j,options:Y,Heading:a,headingProps:{id:K,data:B.data},label:s.formatGroupLabel(B.data)}),B.options.map(function(q){return O(q,"".concat(V,"-").concat(q.index))}))}else if(B.type==="option")return O(B,"".concat(B.index))});else if(x){var z=M({inputValue:_});if(z===null)return null;T=F.createElement(u,m,z)}else{var U=L({inputValue:_});if(U===null)return null;T=F.createElement(f,m,U)}var W={minMenuHeight:S,maxMenuHeight:k,menuPlacement:C,menuPosition:w,menuShouldScrollIntoView:P},G=F.createElement(d1,Oe({},m,W),function(B){var j=B.ref,Y=B.placerProps,V=Y.placement,X=Y.maxHeight;return F.createElement(l,Oe({},m,W,{innerRef:j,innerProps:{onMouseDown:s.onMenuMouseDown,onMouseMove:s.onMenuMouseMove},isLoading:x,placement:V}),F.createElement(Fk,{captureEnabled:b,onTopArrive:$,onBottomArrive:D,lockEnabled:A},function(K){return F.createElement(c,Oe({},m,{innerRef:function(ne){s.getMenuListRef(ne),K(ne)},innerProps:{role:"listbox","aria-multiselectable":m.isMulti,id:s.getElementId("listbox")},isLoading:x,maxHeight:X,focusedOption:p}),T)}))});return E||w==="fixed"?F.createElement(d,Oe({},m,{appendTo:E,controlElement:this.controlRef,menuPlacement:C,menuPosition:w}),G):G}},{key:"renderFormField",value:function(){var s=this,o=this.props,i=o.delimiter,a=o.isDisabled,l=o.isMulti,c=o.name,d=o.required,u=this.state.selectValue;if(d&&!this.hasValue()&&!a)return F.createElement(Hk,{name:c,onFocus:this.onValueInputFocus});if(!(!c||a))if(l)if(i){var f=u.map(function(p){return s.getOptionValue(p)}).join(i);return F.createElement("input",{name:c,type:"hidden",value:f})}else{var g=u.length>0?u.map(function(p,y){return F.createElement("input",{key:"i-".concat(y),name:c,type:"hidden",value:s.getOptionValue(p)})}):F.createElement("input",{name:c,type:"hidden",value:""});return F.createElement("div",null,g)}else{var m=u[0]?this.getOptionValue(u[0]):"";return F.createElement("input",{name:c,type:"hidden",value:m})}}},{key:"renderLiveRegion",value:function(){var s=this.commonProps,o=this.state,i=o.ariaSelection,a=o.focusedOption,l=o.focusedValue,c=o.isFocused,d=o.selectValue,u=this.getFocusableOptions();return F.createElement(_k,Oe({},s,{id:this.getElementId("live-region"),ariaSelection:i,focusedOption:a,focusedValue:l,isFocused:c,selectValue:d,focusableOptions:u,isAppleDevice:this.state.isAppleDevice}))}},{key:"render",value:function(){var s=this.getComponents(),o=s.Control,i=s.IndicatorsContainer,a=s.SelectContainer,l=s.ValueContainer,c=this.props,d=c.className,u=c.id,f=c.isDisabled,g=c.menuIsOpen,m=this.state.isFocused,p=this.commonProps=this.getCommonProps();return F.createElement(a,Oe({},p,{className:d,innerProps:{id:u,onKeyDown:this.onKeyDown},isDisabled:f,isFocused:m}),this.renderLiveRegion(),F.createElement(o,Oe({},p,{innerRef:this.getControlRef,innerProps:{onMouseDown:this.onControlMouseDown,onTouchEnd:this.onControlTouchEnd},isDisabled:f,isFocused:m,menuIsOpen:g}),F.createElement(l,Oe({},p,{isDisabled:f}),this.renderPlaceholderOrValue(),this.renderInput()),F.createElement(i,Oe({},p,{isDisabled:f}),this.renderClearIndicator(),this.renderLoadingIndicator(),this.renderIndicatorSeparator(),this.renderDropdownIndicator())),this.renderMenu(),this.renderFormField())}}],[{key:"getDerivedStateFromProps",value:function(s,o){var i=o.prevProps,a=o.clearFocusValueOnUpdate,l=o.inputIsHiddenAfterUpdate,c=o.ariaSelection,d=o.isFocused,u=o.prevWasFocused,f=o.instancePrefix,g=s.options,m=s.value,p=s.menuIsOpen,y=s.inputValue,b=s.isMulti,_=mi(m),x={};if(i&&(m!==i.value||g!==i.options||p!==i.menuIsOpen||y!==i.inputValue)){var M=p?eC(s,_):[],S=p?th(ki(s,_),"".concat(f,"-option")):[],k=a?tC(o,_):null,N=nC(o,M),C=jl(S,N);x={selectValue:_,focusedOption:N,focusedOptionId:C,focusableOptionsWithIds:S,focusedValue:k,clearFocusValueOnUpdate:!1}}var w=l!=null&&s!==i?{inputIsHidden:l,inputIsHiddenAfterUpdate:void 0}:{},E=c,A=d&&u;return d&&!A&&(E={value:qs(b,_,_[0]||null),options:_,action:"initial-input-focus"},A=!u),c?.action==="initial-input-focus"&&(E=null),Se(Se(Se({},x),w),{},{prevProps:s,ariaSelection:E,prevWasFocused:A})}}]),n})(v.Component);Wl.defaultProps=Rk;var sC=v.forwardRef(function(e,t){var n=Wu(e);return F.createElement(Wl,Oe({ref:t},n))}),lh=sC,oC=["allowCreateWhileLoading","createOptionPosition","formatCreateLabel","isValidNewOption","getNewOptionData","onCreateOption","options","onChange"],ch=function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:"",n=arguments.length>1?arguments[1]:void 0,r=arguments.length>2?arguments[2]:void 0,s=String(t).toLowerCase(),o=String(r.getOptionValue(n)).toLowerCase(),i=String(r.getOptionLabel(n)).toLowerCase();return o===s||i===s},Gl={formatCreateLabel:function(t){return'Create "'.concat(t,'"')},isValidNewOption:function(t,n,r,s){return!(!t||n.some(function(o){return ch(t,o,s)})||r.some(function(o){return ch(t,o,s)}))},getNewOptionData:function(t,n){return{label:n,value:t,__isNew__:!0}}};function iC(e){var t=e.allowCreateWhileLoading,n=t===void 0?!1:t,r=e.createOptionPosition,s=r===void 0?"last":r,o=e.formatCreateLabel,i=o===void 0?Gl.formatCreateLabel:o,a=e.isValidNewOption,l=a===void 0?Gl.isValidNewOption:a,c=e.getNewOptionData,d=c===void 0?Gl.getNewOptionData:c,u=e.onCreateOption,f=e.options,g=f===void 0?[]:f,m=e.onChange,p=Fn(e,oC),y=p.getOptionValue,b=y===void 0?Qf:y,_=p.getOptionLabel,x=_===void 0?Zf:_,M=p.inputValue,S=p.isLoading,k=p.isMulti,N=p.value,C=p.name,w=v.useMemo(function(){return l(M,mi(N),g,{getOptionValue:b,getOptionLabel:x})?d(M,i(M)):void 0},[i,d,x,b,M,l,g,N]),E=v.useMemo(function(){return(n||!S)&&w?s==="first"?[w].concat(ts(g)):[].concat(ts(g),[w]):g},[n,s,S,w,g]),A=v.useCallback(function(P,L){if(L.action!=="select-option")return m(P,L);var $=Array.isArray(P)?P:[P];if($[$.length-1]===w){if(u)u(M);else{var D=d(M,M),O={action:"create-option",name:C,option:D};m(qs(k,[].concat(ts(mi(N)),[D]),D),O)}return}m(P,L)},[d,M,k,C,w,u,m,N]);return Se(Se({},p),{},{options:E,onChange:A})}var aC=v.forwardRef(function(e,t){var n=Wu(e),r=iC(n);return F.createElement(Wl,Oe({ref:t},r))}),dh=aC;const zt={control:"input-tag",valueContainer:"gap-1 flex flex-wrap",multiValue:{blue:"bg-blue-100 dark:bg-blue-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",green:"bg-green-100 dark:bg-green-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",amber:"bg-amber-100 dark:bg-amber-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",red:"bg-red-100 dark:bg-red-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",purple:"bg-purple-100 dark:bg-purple-900/20 rounded px-1.5 py-0.5 flex items-center gap-1",gray:"bg-gray-100 dark:bg-gray-700/50 rounded px-1.5 py-0.5 flex items-center gap-1"},multiValueLabel:{blue:"text-blue-700 dark:text-blue-300 text-xs",green:"text-green-700 dark:text-green-300 text-xs font-semibold",amber:"text-amber-700 dark:text-amber-300 text-xs font-semibold",red:"text-red-700 dark:text-red-300 text-xs font-semibold",purple:"text-purple-700 dark:text-purple-300 text-xs font-semibold",gray:"text-gray-700 dark:text-gray-300 text-xs"},multiValueRemove:{blue:"text-blue-700 dark:text-blue-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",green:"text-green-700 dark:text-green-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",amber:"text-amber-700 dark:text-amber-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",red:"text-red-700 dark:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",purple:"text-purple-700 dark:text-purple-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1",gray:"text-gray-700 dark:text-gray-300 hover:bg-red-100 dark:hover:bg-red-900/30 hover:text-red-600 dark:hover:text-red-400 rounded px-1"},input:"text-gray-900 dark:text-white text-sm",placeholder:"text-gray-400 dark:text-gray-500 text-sm",menu:"mt-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg",menuList:"py-1",option:{base:"px-3 py-2 cursor-pointer text-gray-900 dark:text-white text-[13px]",selected:"bg-blue-100 dark:bg-blue-900/20",focused:"bg-gray-100 dark:bg-gray-700",default:"bg-white dark:bg-gray-800"},noOptionsMessage:"px-3 py-2 text-gray-500 dark:text-gray-400 text-[13px]",checkbox:{base:"w-4 h-4 rounded border flex items-center justify-center text-white text-[10px] shrink-0",selected:"bg-blue-500 border-blue-500",unselected:"border-gray-400 dark:border-gray-500"}},uh=e=>{const{data:t,isSelected:n}=e;return h.jsx(wi.Option,{...e,children:h.jsxs("div",{className:"flex items-center gap-2",children:[h.jsx("span",{className:`${zt.checkbox.base} ${n?zt.checkbox.selected:zt.checkbox.unselected}`,children:n?"✓":""}),h.jsxs("div",{className:"flex-1 min-w-0",children:[h.jsxs("div",{className:"flex items-center gap-1",children:[h.jsx("span",{className:"text-[13px]",children:t.label}),t.count!==void 0&&t.count>0&&h.jsxs("span",{className:"text-gray-500 dark:text-gray-400 text-[10px]",children:["(",t.count,")"]})]}),t.description&&h.jsx("p",{className:"text-xs text-gray-500 dark:text-gray-400 truncate",children:t.description})]})]})})},fh=e=>h.jsx(wi.GroupHeading,{...e,children:h.jsx("div",{className:"px-3 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800/50 border-b border-gray-200 dark:border-gray-700",children:e.data.label})}),lC=e=>{const{data:t}=e;return h.jsxs(wi.MultiValueLabel,{...e,children:[h.jsx("span",{children:t.label}),t.count!==void 0&&t.count>0&&h.jsxs("span",{className:"text-xs opacity-60 ml-1",children:["(",t.count,")"]})]})},cC=({value:e,options:t=[],onChange:n,creatable:r=!1,placeholder:s="검색 또는 입력...",noOptionsMessage:o="검색 결과가 없습니다",formatCreateLabel:i,createLabelSuffix:a="추가",maxItems:l,disabled:c=!1,onBeforeRemove:d,onCreateOption:u,className:f="",tagVariants:g,defaultVariant:m="blue",name:p,isMulti:y=!0,isClearable:b=!0,isSearchable:_=!0,delimiters:x=[","],splitOnPaste:M=!0,onInputChange:S,id:k,editorAttrs:N})=>{const C=c===!0||c==="true",w=v.useCallback(te=>te==null?[]:Array.isArray(te)?te:[te],[]),[E,A]=v.useState(()=>w(e)),[P,L]=v.useState(""),$=v.useRef(void 0);v.useEffect(()=>{const te=(ge,ce)=>!ge&&!ce?!0:!ge||!ce||ge.length!==ce.length?!1:ge.every((ye,me)=>ye===ce[me]),le=w(e);te(le,$.current)||(A(le),$.current=le)},[e,w]);const D=v.useMemo(()=>i?typeof i=="string"?te=>`${i} "${te}"`:i:te=>h.jsxs("span",{className:"text-[13px]",children:[h.jsx("strong",{children:te})," ",a]}),[i,a]),O=v.useMemo(()=>!E||!Array.isArray(E)?[]:E.map(te=>t.find(ge=>ge.value===te)||{value:te,label:String(te)}).filter(te=>te!==void 0),[E,t]),T=v.useMemo(()=>{const te=new Set(t.map(ke=>ke.value)),le=E?E.filter(ke=>!te.has(ke)).map(ke=>({value:ke,label:String(ke)})):[],ge=[...t,...le];if(!ge.some(ke=>ke.group))return ge;const ye=new Map,me=[];ge.forEach(ke=>{if(ke.group){const $e=ye.get(ke.group)||[];$e.push(ke),ye.set(ke.group,$e)}else me.push(ke)});const pe=[];return ye.forEach((ke,$e)=>{pe.push({label:$e,options:ke})}),me.length>0&&pe.push({label:"",options:me}),pe},[t,E]),z=v.useCallback(te=>({target:{value:te},currentTarget:{value:te},preventDefault:()=>{},stopPropagation:()=>{}}),[]),U=v.useCallback(te=>{if(!x?.length)return[te.trim()].filter(Boolean);const le=x.map(ce=>ce.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")),ge=new RegExp(`[${le.join("")}]`);return te.split(ge).map(ce=>ce.trim()).filter(Boolean)},[x]),W=v.useCallback((te,le)=>{if(le.action!=="input-change"){L(te);return}if(S&&S(z(te)),!x?.some(me=>te.includes(me))||!r||!y){L(te);return}const ye=U(te).filter(me=>me&&!E.includes(me));if(ye.length>0){const me=l?l-E.length:1/0,pe=ye.slice(0,me);if(pe.length>0){const ke=[...E,...pe];A(ke),n(z(ke))}}L("")},[x,r,y,U,E,l,n,z,S]),G=v.useCallback(te=>{if(!M||!r||!y)return;const le=te.clipboardData.getData("text"),ge=U(le);if(ge.length<=1)return;te.preventDefault();const ce=ge.filter(ye=>ye&&!E.includes(ye));if(ce.length>0){const ye=l?l-E.length:1/0,me=ce.slice(0,ye);if(me.length>0){const pe=[...E,...me];A(pe),n(z(pe))}}},[M,r,y,U,E,l,n,z]),B=v.useCallback((te,le)=>{if((le.action==="remove-value"||le.action==="pop-value")&&le.removedValue&&d){const ce=d(le.removedValue);if(ce!==!0){const ye=typeof ce=="string"?ce:`"${le.removedValue.label}"은(는) 제거할 수 없습니다.`;alert(ye);return}}if(l&&te.length>l)return;const ge=te.map(ce=>ce.value);A(ge),n(z(ge))},[n,d,l,z]),j=v.useCallback((te,le)=>{if(le.action==="clear"&&E.length>0&&d){const ye=t.find(pe=>pe.value===E[0])||{value:E[0],label:String(E[0])},me=d(ye);if(me!==!0){const pe=typeof me=="string"?me:`"${ye.label}"은(는) 제거할 수 없습니다.`;alert(pe);return}}const ge=te?te.value:null,ce=te?[te.value]:[];A(ce),n(z(ge))},[n,d,E,t,z]),Y=v.useCallback(te=>{u&&u(te);const le=[...E||[],te];A(le),n(z(le))},[n,u,E,z]),V=v.useCallback(te=>!te||!te.trim()?!1:!E?.includes(te.trim()),[E]),X=v.useCallback(te=>{const le=P.trim();if(te.key==="Tab"&&r&&y&&le){if(E.includes(le))te.preventDefault(),L("");else if(!l||E.lengthg&&g[te]?g[te]:m,[g,m]),q={control:()=>`${zt.control} ${f} ${C?"opacity-60 cursor-not-allowed":""}`,valueContainer:()=>zt.valueContainer,multiValue:te=>{const le=K(te.data.value);return zt.multiValue[le]},multiValueLabel:te=>{const le=K(te.data.value);return zt.multiValueLabel[le]},multiValueRemove:te=>{const le=K(te.data.value);return`${zt.multiValueRemove[le]} ${C?"pointer-events-none":""}`},singleValue:()=>"text-gray-900 dark:text-white text-sm",input:()=>zt.input,placeholder:()=>zt.placeholder,menu:()=>zt.menu,menuList:()=>zt.menuList,menuPortal:()=>"z-[9999]",option:te=>`${zt.option.base} ${te.isSelected?zt.option.selected:te.isFocused?zt.option.focused:zt.option.default}`,noOptionsMessage:()=>zt.noOptionsMessage,clearIndicator:()=>"text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-pointer px-1",dropdownIndicator:()=>"text-gray-400 dark:text-gray-500 px-1"},ne={options:T,onKeyDown:X,placeholder:s,noOptionsMessage:()=>o,isDisabled:C,isSearchable:_,menuIsOpen:void 0,openMenuOnClick:!0,openMenuOnFocus:!0,menuPortalTarget:typeof document<"u"?document.body:null,menuPosition:"fixed",classNamePrefix:"tag-input",unstyled:!0,classNames:q,...r&&y||S?{inputValue:P,onInputChange:W}:{}},ee=y?E.join(","):E[0]??"";if(!y){const te={...ne,isMulti:!1,value:O[0]||null,onChange:j,isClearable:b,closeMenuOnSelect:!0,components:{Option:uh,GroupHeading:fh,IndicatorSeparator:()=>null,...b?{}:{ClearIndicator:()=>null}}};return r?h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ee}),h.jsx(dh,{...te,onCreateOption:Y,formatCreateLabel:D,isValidNewOption:V,createOptionPosition:"first"})]}):h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ee}),h.jsx(lh,{...te})]})}const fe={...ne,isMulti:!0,value:O,onChange:B,closeMenuOnSelect:!1,hideSelectedOptions:!1,backspaceRemovesValue:!1,isClearable:!1,components:{Option:uh,GroupHeading:fh,MultiValueLabel:lC,ClearIndicator:()=>null,DropdownIndicator:()=>null,IndicatorSeparator:()=>null}};return r?h.jsxs(I,{id:k,...N,onPaste:G,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ee}),h.jsx(dh,{...fe,onCreateOption:Y,formatCreateLabel:D,isValidNewOption:V,createOptionPosition:"first"})]}):h.jsxs(I,{id:k,...N,children:[p&&h.jsx("input",{type:"hidden",name:p,value:ee}),h.jsx(lh,{...fe})]})},is=(e,t)=>window.G7Core?.t?.(e,t)??e,dC=()=>window.G7Core?.locale?.supported?.()??["ko","en"],uC=()=>window.G7Core?.locale?.current?.()??"ko",Yl=e=>{const t=is(`common.language_${e}`);return t===`common.language_${e}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[e]||e.toUpperCase():t},fC=({value:e=[],onChange:t,placeholder:n="입력 후 Enter",disabled:r=!1,creatable:s=!0,delimiters:o=[","],className:i="",name:a,error:l,maxItems:c,modalId:d,statePath:u,__componentContext:f,id:g,editorAttrs:m})=>{const p=v.useMemo(()=>dC(),[]),y=v.useMemo(()=>uC(),[]),[b,_]=v.useState(()=>e||[]),[x,M]=v.useState(""),[S,k]=v.useState(!1),[N,C]=v.useState(null),[w,E]=v.useState({}),A=v.useRef(null),P=v.useRef(void 0);v.useEffect(()=>{const V=(K,q)=>!K&&!q?!0:!K||!q||K.length!==q.length?!1:JSON.stringify(K)===JSON.stringify(q),X=e||[];V(X,P.current)||(_(X),P.current=X)},[e]);const L=v.useCallback(V=>({target:{name:a,value:V},currentTarget:{name:a,value:V},preventDefault:()=>{},stopPropagation:()=>{}}),[a]),$=v.useCallback(V=>{const X=[];for(const K of p){const q=V[K];q?.trim()&&X.push(`${K.toUpperCase()}: ${q}`)}return X.length>0?X.join(" / "):""},[p]),D=v.useCallback(V=>{const X=b.filter((K,q)=>q!==V);_(X),t?.(L(X))},[b,t,L]),O=v.useCallback((V,X,K)=>{const q=window.G7Core;if(!q?.dispatch)return;const ne=p.map(ee=>({code:ee,label:Yl(ee)}));q.dispatch({handler:"setState",params:{target:"global",multilingualTagEdit:{fieldName:a,editingIndex:V,values:X,supportedLocales:ne,defaultLocale:y,statePath:u,currentTags:K}}}),q.dispatch({handler:"openModal",target:d},{componentContext:f})},[a,p,y,d,u,f]),T=v.useCallback(V=>{r||(d?O(V,{...b[V]},b):(C(V),E({...b[V]}),k(!0)))},[r,b,d,O]),z=v.useCallback((V="")=>{const X={};p.forEach(K=>{X[K]=K===y?V:""}),d?(O(null,X,b),M("")):(C(null),E(X),k(!0),M(""))},[p,y,d,O,b]),U=v.useCallback(()=>{if(!Object.values(w).some(K=>K.trim()!=="")){k(!1);return}let X;N!==null?X=b.map((K,q)=>q===N?w:K):X=[...b,w],_(X),t?.(L(X)),k(!1),E({}),C(null)},[w,N,b,t,L]),W=v.useCallback(()=>{k(!1),E({}),C(null)},[]),G=v.useCallback(V=>{const X=V.target.value;if(o.some(q=>X.includes(q))&&s){const q=o.map(fe=>fe.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")),ne=new RegExp(`[${q.join("")}]`),ee=X.split(ne).map(fe=>fe.trim()).filter(Boolean);if(ee.length>0){z(ee[0]);return}}M(X)},[o,s,z]),B=v.useCallback(V=>{if(V.key==="Enter"&&x.trim()&&s){if(V.preventDefault(),c&&b.length>=c)return;z(x.trim())}},[x,s,c,b.length,z]),j=v.useCallback((V,X)=>{E(K=>({...K,[V]:X}))},[]),Y=!!l;return h.jsxs(I,{className:i,id:g,...m,children:[h.jsxs(I,{className:`flex flex-wrap gap-1.5 p-2 border rounded-lg bg-white dark:bg-gray-700 min-h-[42px] ${Y?"border-red-500 dark:border-red-400":"border-gray-300 dark:border-gray-600 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500"} ${r?"opacity-60 bg-gray-100 dark:bg-gray-800 cursor-not-allowed":""}`,children:[b.map((V,X)=>h.jsxs(I,{className:"flex items-center gap-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded px-2 py-0.5 text-sm",children:[h.jsx(J,{className:`cursor-pointer hover:underline ${r?"cursor-not-allowed":""}`,onClick:()=>T(X),children:$(V)}),!r&&h.jsx(ae,{type:"button",className:"p-0 h-4 w-4 min-w-0 border-0 bg-transparent text-blue-600 dark:text-blue-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-transparent",onClick:K=>{K.stopPropagation(),D(X)},children:h.jsx(oe,{name:"times",className:"text-xs"})})]},X)),s&&!r&&(!c||b.lengthh.jsxs(I,{className:"space-y-1",children:[h.jsxs(I,{className:"flex items-center gap-2",children:[h.jsx(J,{className:"text-sm font-medium text-gray-700 dark:text-gray-300 w-16",children:Yl(V)}),h.jsxs(J,{className:"text-xs text-gray-400 dark:text-gray-500",children:["(",V.toUpperCase(),")"]})]}),h.jsx(kt,{type:"text",value:w[V]??"",onChange:X=>j(V,X.target.value),placeholder:`${Yl(V)} 값 입력`,className:"w-full",autoFocus:V===y})]},V)),h.jsxs(I,{className:"flex justify-end gap-2 pt-4 border-t border-gray-200 dark:border-gray-700",children:[h.jsx(ae,{type:"button",variant:"secondary",onClick:W,children:is("common.cancel")}),h.jsx(ae,{type:"button",variant:"primary",onClick:U,children:is(N!==null?"common.save":"common.add")})]})]})}),a&&h.jsx("input",{type:"hidden",name:a,value:JSON.stringify(b)})]})},hh=(e,t)=>window.G7Core?.t?.(e,t)??e,gh=({type:e,identifier:t,name:n,installedModules:r=[],installedPlugins:s=[],className:o="",style:i,id:a,editorAttrs:l})=>{const c={module:ue.Cubes,plugin:ue.PuzzlePiece},d={module:hh("common.module"),plugin:hh("common.plugin")},u=v.useMemo(()=>{if(n)return n;if(t){const p=(e==="module"?r:s)?.find(y=>y.identifier===t);if(p)return p.name}},[n,t,e,r,s]),f=c[e],g=u?`${u} ${d[e]}`:d[e];return h.jsxs(J,{className:`inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium text-gray-400 dark:text-gray-500 ${o}`,style:i,id:a,...l,children:[h.jsx(oe,{name:f,className:"w-3.5 h-3.5"}),h.jsx(J,{children:g})]})},hC=({options:e=[],value:t,onChange:n,multiple:r=!1,searchPlaceholder:s="Search...",onSearch:o,noResultsText:i="No results found",initialText:a="Type to search",triggerLabel:l="Select",triggerIcon:c,disabled:d=!1,className:u="",id:f,editorAttrs:g})=>{const[m,p]=v.useState(!1),[y,b]=v.useState(""),_=v.useRef(null),x=v.useRef(null),M=v.useRef(`searchable-dropdown-input-${Math.random().toString(36).slice(2,9)}`).current,S=v.useMemo(()=>t==null?[]:Array.isArray(t)?t:[t],[t]),k=v.useMemo(()=>{if(!y.trim())return e;const D=y.toLowerCase();return e.filter(O=>O.label.toLowerCase().includes(D)||O.description?.toLowerCase().includes(D))},[e,y]),N=v.useMemo(()=>{if(S.length===0)return l;if(r){const O=S.length;return O===1?e.find(z=>String(z.value)===String(S[0]))?.label||l:`${O} selected`}return e.find(O=>String(O.value)===String(S[0]))?.label||l},[S,e,r,l]);v.useEffect(()=>{const D=O=>{_.current&&!_.current.contains(O.target)&&p(!1)};return m&&document.addEventListener("mousedown",D),()=>{document.removeEventListener("mousedown",D)}},[m]),v.useEffect(()=>{const D=O=>{O.key==="Escape"&&m&&(p(!1),x.current?.focus())};return document.addEventListener("keydown",D),()=>document.removeEventListener("keydown",D)},[m]),v.useEffect(()=>{if(!m){b("");return}const D=setTimeout(()=>{document.getElementById(M)?.focus()},50);return()=>{clearTimeout(D)}},[m,M]);const C=v.useCallback(()=>{d||p(D=>!D)},[d]),w=v.useCallback(D=>{const O=D.target.value;b(O),o?.(O)},[o]),E=v.useCallback(D=>{if(r){const T=S.some(z=>String(z)===String(D))?S.filter(z=>String(z)!==String(D)):[...S,D];n?.(T)}else n?.(D),p(!1),x.current?.focus()},[r,S,n]),A=D=>S.some(O=>String(O)===String(D)),P=y.length===0&&e.length===0,L=y.length>0&&k.length===0,$=k.length>0;return h.jsxs(I,{ref:_,className:`relative inline-block ${u}`,id:f,...g,children:[h.jsxs(ae,{ref:x,type:"button",onClick:C,disabled:d,className:` px-4 py-2.5 bg-gray-100 dark:bg-gray-700 border-0 rounded-xl text-gray-700 dark:text-gray-200 font-medium @@ -181,27 +182,27 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho text-left cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed min-w-[200px] - `,"aria-haspopup":"listbox","aria-expanded":m,children:[h.jsxs(I,{className:"flex items-center gap-2",children:[c,h.jsx(Q,{className:"truncate",children:N})]}),h.jsx(Un,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${m?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),m&&h.jsxs(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden min-w-[280px]",role:"listbox",children:[h.jsx(I,{className:"p-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx("input",{id:E,type:"text",value:y,onChange:w,placeholder:s,className:"w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent focus:outline-none"})}),h.jsxs(I,{className:"max-h-80 overflow-auto",children:[P&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:a}),L&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:i}),$&&h.jsx(I,{className:"py-2",children:k.map(A=>{const D=T(A.value);return h.jsxs(oe,{type:"button",onClick:()=>!A.disabled&&M(A.value),disabled:A.disabled,className:` + `,"aria-haspopup":"listbox","aria-expanded":m,children:[h.jsxs(I,{className:"flex items-center gap-2",children:[c,h.jsx(J,{className:"truncate",children:N})]}),h.jsx(Xn,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${m?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),m&&h.jsxs(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden min-w-[280px]",role:"listbox",children:[h.jsx(I,{className:"p-3 border-b border-gray-200 dark:border-gray-700",children:h.jsx("input",{id:M,type:"text",value:y,onChange:w,placeholder:s,className:"w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:ring-2 focus:ring-blue-500 focus:border-transparent focus:outline-none"})}),h.jsxs(I,{className:"max-h-80 overflow-auto",children:[P&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:a}),L&&h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:i}),$&&h.jsx(I,{className:"py-2",children:k.map(D=>{const O=A(D.value);return h.jsxs(ae,{type:"button",onClick:()=>!D.disabled&&E(D.value),disabled:D.disabled,className:` w-full px-4 py-3 text-left flex items-start gap-3 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors - ${A.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"} - `,role:"option","aria-selected":D,children:[h.jsx(I,{className:"w-5 h-5 flex-shrink-0 mt-0.5",children:D&&h.jsx(Un,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`font-medium ${D?"text-blue-600 dark:text-blue-400":"text-gray-900 dark:text-white"}`,children:A.label}),A.description&&h.jsx(I,{className:"text-sm text-gray-500 dark:text-gray-400 mt-0.5",children:A.description})]})]},A.value)})})]})]})]})},Hs={container:{base:"tag-select",disabled:"opacity-50 cursor-not-allowed"},tag:"tag",tagRemove:"tag-remove",placeholder:"tag-select-placeholder"},qk=({options:t=[],value:e=[],onChange:n,placeholder:r="No items selected",disabled:s=!1,className:o="",id:i,editorAttrs:a})=>{const l=v.useCallback(f=>t.find(m=>String(m.value)===String(f))?.label||String(f),[t]),c=v.useCallback(f=>{if(s)return;const g=e.filter(m=>String(m)!==String(f));n?.(g)},[e,n,s]),d=v.useMemo(()=>e.map(f=>({value:f,label:l(f)})),[e,l]),u=`${Hs.container.base} ${s?Hs.container.disabled:""} ${o}`.trim();return h.jsx(I,{className:u,id:i,...a,children:d.length===0?h.jsx(Q,{className:Hs.placeholder,children:r}):d.map(f=>h.jsxs(Q,{className:Hs.tag,children:[f.label,!s&&h.jsx(Q,{className:Hs.tagRemove,onClick:()=>c(f.value),role:"button","aria-label":`Remove ${f.label}`,children:h.jsx(Un,{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]},f.value))})};function Zk(){for(var t=arguments.length,e=new Array(t),n=0;nr=>{e.forEach(s=>s(r))},e)}const bi=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Kr(t){const e=Object.prototype.toString.call(t);return e==="[object Window]"||e==="[object global]"}function Hl(t){return"nodeType"in t}function Xt(t){var e,n;return t?Kr(t)?t:Hl(t)&&(e=(n=t.ownerDocument)==null?void 0:n.defaultView)!=null?e:window:window}function Ul(t){const{Document:e}=Xt(t);return t instanceof e}function Us(t){return Kr(t)?!1:t instanceof Xt(t).HTMLElement}function rh(t){return t instanceof Xt(t).SVGElement}function Xr(t){return t?Kr(t)?t.document:Hl(t)?Ul(t)?t:Us(t)||rh(t)?t.ownerDocument:document:document:document}const wn=bi?v.useLayoutEffect:v.useEffect;function yi(t){const e=v.useRef(t);return wn(()=>{e.current=t}),v.useCallback(function(){for(var n=arguments.length,r=new Array(n),s=0;s{t.current=setInterval(r,s)},[]),n=v.useCallback(()=>{t.current!==null&&(clearInterval(t.current),t.current=null)},[]);return[e,n]}function Bs(t,e){e===void 0&&(e=[t]);const n=v.useRef(t);return wn(()=>{n.current!==t&&(n.current=t)},e),n}function js(t,e){const n=v.useRef();return v.useMemo(()=>{const r=t(n.current);return n.current=r,r},[...e])}function vi(t){const e=yi(t),n=v.useRef(null),r=v.useCallback(s=>{s!==n.current&&e?.(s,n.current),n.current=s},[]);return[n,r]}function xi(t){const e=v.useRef();return v.useEffect(()=>{e.current=t},[t]),e.current}let Bl={};function Ws(t,e){return v.useMemo(()=>{if(e)return e;const n=Bl[t]==null?0:Bl[t]+1;return Bl[t]=n,t+"-"+n},[t,e])}function sh(t){return function(e){for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s{const a=Object.entries(i);for(const[l,c]of a){const d=o[l];d!=null&&(o[l]=d+t*c)}return o},{...e})}}const qr=sh(1),Gs=sh(-1);function Jk(t){return"clientX"in t&&"clientY"in t}function wi(t){if(!t)return!1;const{KeyboardEvent:e}=Xt(t.target);return e&&t instanceof e}function Rk(t){if(!t)return!1;const{TouchEvent:e}=Xt(t.target);return e&&t instanceof e}function ki(t){if(Rk(t)){if(t.touches&&t.touches.length){const{clientX:e,clientY:n}=t.touches[0];return{x:e,y:n}}else if(t.changedTouches&&t.changedTouches.length){const{clientX:e,clientY:n}=t.changedTouches[0];return{x:e,y:n}}}return Jk(t)?{x:t.clientX,y:t.clientY}:null}const Pn=Object.freeze({Translate:{toString(t){if(!t)return;const{x:e,y:n}=t;return"translate3d("+(e?Math.round(e):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(t){if(!t)return;const{scaleX:e,scaleY:n}=t;return"scaleX("+e+") scaleY("+n+")"}},Transform:{toString(t){if(t)return[Pn.Translate.toString(t),Pn.Scale.toString(t)].join(" ")}},Transition:{toString(t){let{property:e,duration:n,easing:r}=t;return e+" "+n+"ms "+r}}}),oh="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function eC(t){return t.matches(oh)?t:t.querySelector(oh)}const tC={display:"none"};function nC(t){let{id:e,value:n}=t;return v.createElement("div",{id:e,style:tC},n)}function rC(t){let{id:e,announcement:n,ariaLiveType:r="assertive"}=t;const s={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return v.createElement("div",{id:e,style:s,role:"status","aria-live":r,"aria-atomic":!0},n)}function sC(){const[t,e]=v.useState("");return{announce:v.useCallback(r=>{r!=null&&e(r)},[]),announcement:t}}const ih=v.createContext(null);function oC(t){const e=v.useContext(ih);v.useEffect(()=>{if(!e)throw new Error("useDndMonitor must be used within a children of ");return e(t)},[t,e])}function iC(){const[t]=v.useState(()=>new Set),e=v.useCallback(r=>(t.add(r),()=>t.delete(r)),[t]);return[v.useCallback(r=>{let{type:s,event:o}=r;t.forEach(i=>{var a;return(a=i[s])==null?void 0:a.call(i,o)})},[t]),e]}const aC={draggable:` + ${D.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"} + `,role:"option","aria-selected":O,children:[h.jsx(I,{className:"w-5 h-5 flex-shrink-0 mt-0.5",children:O&&h.jsx(Xn,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsx(I,{className:`font-medium ${O?"text-blue-600 dark:text-blue-400":"text-gray-900 dark:text-white"}`,children:D.label}),D.description&&h.jsx(I,{className:"text-sm text-gray-500 dark:text-gray-400 mt-0.5",children:D.description})]})]},D.value)})})]})]})]})},Qs={container:{base:"tag-select",disabled:"opacity-50 cursor-not-allowed"},tag:"tag",tagRemove:"tag-remove",placeholder:"tag-select-placeholder"},gC=({options:e=[],value:t=[],onChange:n,placeholder:r="No items selected",disabled:s=!1,className:o="",id:i,editorAttrs:a})=>{const l=v.useCallback(f=>e.find(m=>String(m.value)===String(f))?.label||String(f),[e]),c=v.useCallback(f=>{if(s)return;const g=t.filter(m=>String(m)!==String(f));n?.(g)},[t,n,s]),d=v.useMemo(()=>t.map(f=>({value:f,label:l(f)})),[t,l]),u=`${Qs.container.base} ${s?Qs.container.disabled:""} ${o}`.trim();return h.jsx(I,{className:u,id:i,...a,children:d.length===0?h.jsx(J,{className:Qs.placeholder,children:r}):d.map(f=>h.jsxs(J,{className:Qs.tag,children:[f.label,!s&&h.jsx(J,{className:Qs.tagRemove,onClick:()=>c(f.value),role:"button","aria-label":`Remove ${f.label}`,children:h.jsx(Xn,{className:"w-3 h-3",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]},f.value))})};function pC(){for(var e=arguments.length,t=new Array(e),n=0;nr=>{t.forEach(s=>s(r))},t)}const Si=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function as(e){const t=Object.prototype.toString.call(e);return t==="[object Window]"||t==="[object global]"}function Xl(e){return"nodeType"in e}function Kt(e){var t,n;return e?as(e)?e:Xl(e)&&(t=(n=e.ownerDocument)==null?void 0:n.defaultView)!=null?t:window:window}function Kl(e){const{Document:t}=Kt(e);return e instanceof t}function Js(e){return as(e)?!1:e instanceof Kt(e).HTMLElement}function ph(e){return e instanceof Kt(e).SVGElement}function ls(e){return e?as(e)?e.document:Xl(e)?Kl(e)?e:Js(e)||ph(e)?e.ownerDocument:document:document:document}const En=Si?v.useLayoutEffect:v.useEffect;function _i(e){const t=v.useRef(e);return En(()=>{t.current=e}),v.useCallback(function(){for(var n=arguments.length,r=new Array(n),s=0;s{e.current=setInterval(r,s)},[]),n=v.useCallback(()=>{e.current!==null&&(clearInterval(e.current),e.current=null)},[]);return[t,n]}function Rs(e,t){t===void 0&&(t=[e]);const n=v.useRef(e);return En(()=>{n.current!==e&&(n.current=e)},t),n}function eo(e,t){const n=v.useRef();return v.useMemo(()=>{const r=e(n.current);return n.current=r,r},[...t])}function Ei(e){const t=_i(e),n=v.useRef(null),r=v.useCallback(s=>{s!==n.current&&t?.(s,n.current),n.current=s},[]);return[n,r]}function Mi(e){const t=v.useRef();return v.useEffect(()=>{t.current=e},[e]),t.current}let ql={};function to(e,t){return v.useMemo(()=>{if(t)return t;const n=ql[e]==null?0:ql[e]+1;return ql[e]=n,e+"-"+n},[e,t])}function mh(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),s=1;s{const a=Object.entries(i);for(const[l,c]of a){const d=o[l];d!=null&&(o[l]=d+e*c)}return o},{...t})}}const cs=mh(1),no=mh(-1);function bC(e){return"clientX"in e&&"clientY"in e}function Ni(e){if(!e)return!1;const{KeyboardEvent:t}=Kt(e.target);return t&&e instanceof t}function yC(e){if(!e)return!1;const{TouchEvent:t}=Kt(e.target);return t&&e instanceof t}function Ti(e){if(yC(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}else if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return bC(e)?{x:e.clientX,y:e.clientY}:null}const Un=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return"translate3d("+(t?Math.round(t):0)+"px, "+(n?Math.round(n):0)+"px, 0)"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return"scaleX("+t+") scaleY("+n+")"}},Transform:{toString(e){if(e)return[Un.Translate.toString(e),Un.Scale.toString(e)].join(" ")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+" "+n+"ms "+r}}}),bh="a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]";function vC(e){return e.matches(bh)?e:e.querySelector(bh)}const xC={display:"none"};function wC(e){let{id:t,value:n}=e;return v.createElement("div",{id:t,style:xC},n)}function kC(e){let{id:t,announcement:n,ariaLiveType:r="assertive"}=e;const s={position:"fixed",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0 0 0 0)",clipPath:"inset(100%)",whiteSpace:"nowrap"};return v.createElement("div",{id:t,style:s,role:"status","aria-live":r,"aria-atomic":!0},n)}function CC(){const[e,t]=v.useState("");return{announce:v.useCallback(r=>{r!=null&&t(r)},[]),announcement:e}}const yh=v.createContext(null);function SC(e){const t=v.useContext(yh);v.useEffect(()=>{if(!t)throw new Error("useDndMonitor must be used within a children of ");return t(e)},[e,t])}function _C(){const[e]=v.useState(()=>new Set),t=v.useCallback(r=>(e.add(r),()=>e.delete(r)),[e]);return[v.useCallback(r=>{let{type:s,event:o}=r;e.forEach(i=>{var a;return(a=i[s])==null?void 0:a.call(i,o)})},[e]),t]}const EC={draggable:` To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel. - `},lC={onDragStart(t){let{active:e}=t;return"Picked up draggable item "+e.id+"."},onDragOver(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was moved over droppable area "+n.id+".":"Draggable item "+e.id+" is no longer over a droppable area."},onDragEnd(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was dropped over droppable area "+n.id:"Draggable item "+e.id+" was dropped."},onDragCancel(t){let{active:e}=t;return"Dragging was cancelled. Draggable item "+e.id+" was dropped."}};function cC(t){let{announcements:e=lC,container:n,hiddenTextDescribedById:r,screenReaderInstructions:s=aC}=t;const{announce:o,announcement:i}=sC(),a=Ws("DndLiveRegion"),[l,c]=v.useState(!1);if(v.useEffect(()=>{c(!0)},[]),oC(v.useMemo(()=>({onDragStart(u){let{active:f}=u;o(e.onDragStart({active:f}))},onDragMove(u){let{active:f,over:g}=u;e.onDragMove&&o(e.onDragMove({active:f,over:g}))},onDragOver(u){let{active:f,over:g}=u;o(e.onDragOver({active:f,over:g}))},onDragEnd(u){let{active:f,over:g}=u;o(e.onDragEnd({active:f,over:g}))},onDragCancel(u){let{active:f,over:g}=u;o(e.onDragCancel({active:f,over:g}))}}),[o,e])),!l)return null;const d=v.createElement(v.Fragment,null,v.createElement(nC,{id:r,value:s.draggable}),v.createElement(rC,{id:a,announcement:i}));return n?Tn.createPortal(d,n):d}var _t;(function(t){t.DragStart="dragStart",t.DragMove="dragMove",t.DragEnd="dragEnd",t.DragCancel="dragCancel",t.DragOver="dragOver",t.RegisterDroppable="registerDroppable",t.SetDroppableDisabled="setDroppableDisabled",t.UnregisterDroppable="unregisterDroppable"})(_t||(_t={}));function Ci(){}function Zr(t,e){return v.useMemo(()=>({sensor:t,options:e??{}}),[t,e])}function jl(){for(var t=arguments.length,e=new Array(t),n=0;n[...e].filter(r=>r!=null),[...e])}const kn=Object.freeze({x:0,y:0});function ah(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))}function dC(t,e){const n=ki(t);if(!n)return"0 0";const r={x:(n.x-e.left)/e.width*100,y:(n.y-e.top)/e.height*100};return r.x+"% "+r.y+"%"}function lh(t,e){let{data:{value:n}}=t,{data:{value:r}}=e;return n-r}function uC(t,e){let{data:{value:n}}=t,{data:{value:r}}=e;return r-n}function ch(t){let{left:e,top:n,height:r,width:s}=t;return[{x:e,y:n},{x:e+s,y:n},{x:e,y:n+r},{x:e+s,y:n+r}]}function dh(t,e){if(!t||t.length===0)return null;const[n]=t;return n[e]}function uh(t,e,n){return e===void 0&&(e=t.left),n===void 0&&(n=t.top),{x:e+t.width*.5,y:n+t.height*.5}}const Wl=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=uh(e,e.left,e.top),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=ah(uh(l),s);o.push({id:a,data:{droppableContainer:i,value:c}})}}return o.sort(lh)},fC=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=ch(e),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=ch(l),d=s.reduce((f,g,m)=>f+ah(c[m],g),0),u=Number((d/4).toFixed(4));o.push({id:a,data:{droppableContainer:i,value:u}})}}return o.sort(lh)};function hC(t,e){const n=Math.max(e.top,t.top),r=Math.max(e.left,t.left),s=Math.min(e.left+e.width,t.left+t.width),o=Math.min(e.top+e.height,t.top+t.height),i=s-r,a=o-n;if(r{let{collisionRect:e,droppableRects:n,droppableContainers:r}=t;const s=[];for(const o of r){const{id:i}=o,a=n.get(i);if(a){const l=hC(a,e);l>0&&s.push({id:i,data:{droppableContainer:o,value:l}})}}return s.sort(uC)};function pC(t,e,n){return{...t,scaleX:e&&n?e.width/n.width:1,scaleY:e&&n?e.height/n.height:1}}function fh(t,e){return t&&e?{x:t.left-e.left,y:t.top-e.top}:kn}function mC(t){return function(n){for(var r=arguments.length,s=new Array(r>1?r-1:0),o=1;o({...i,top:i.top+t*a.y,bottom:i.bottom+t*a.y,left:i.left+t*a.x,right:i.right+t*a.x}),{...n})}}const bC=mC(1);function hh(t){if(t.startsWith("matrix3d(")){const e=t.slice(9,-1).split(/, /);return{x:+e[12],y:+e[13],scaleX:+e[0],scaleY:+e[5]}}else if(t.startsWith("matrix(")){const e=t.slice(7,-1).split(/, /);return{x:+e[4],y:+e[5],scaleX:+e[0],scaleY:+e[3]}}return null}function yC(t,e,n){const r=hh(e);if(!r)return t;const{scaleX:s,scaleY:o,x:i,y:a}=r,l=t.left-i-(1-s)*parseFloat(n),c=t.top-a-(1-o)*parseFloat(n.slice(n.indexOf(" ")+1)),d=s?t.width/s:t.width,u=o?t.height/o:t.height;return{width:d,height:u,top:c,right:l+d,bottom:c+u,left:l}}const vC={ignoreTransform:!1};function Qr(t,e){e===void 0&&(e=vC);let n=t.getBoundingClientRect();if(e.ignoreTransform){const{transform:c,transformOrigin:d}=Xt(t).getComputedStyle(t);c&&(n=yC(n,c,d))}const{top:r,left:s,width:o,height:i,bottom:a,right:l}=n;return{top:r,left:s,width:o,height:i,bottom:a,right:l}}function gh(t){return Qr(t,{ignoreTransform:!0})}function xC(t){const e=t.innerWidth,n=t.innerHeight;return{top:0,left:0,right:e,bottom:n,width:e,height:n}}function wC(t,e){return e===void 0&&(e=Xt(t).getComputedStyle(t)),e.position==="fixed"}function kC(t,e){e===void 0&&(e=Xt(t).getComputedStyle(t));const n=/(auto|scroll|overlay)/;return["overflow","overflowX","overflowY"].some(s=>{const o=e[s];return typeof o=="string"?n.test(o):!1})}function Si(t,e){const n=[];function r(s){if(e!=null&&n.length>=e||!s)return n;if(Ul(s)&&s.scrollingElement!=null&&!n.includes(s.scrollingElement))return n.push(s.scrollingElement),n;if(!Us(s)||rh(s)||n.includes(s))return n;const o=Xt(t).getComputedStyle(s);return s!==t&&kC(s,o)&&n.push(s),wC(s,o)?n:r(s.parentNode)}return t?r(t):n}function ph(t){const[e]=Si(t,1);return e??null}function Gl(t){return!bi||!t?null:Kr(t)?t:Hl(t)?Ul(t)||t===Xr(t).scrollingElement?window:Us(t)?t:null:null}function mh(t){return Kr(t)?t.scrollX:t.scrollLeft}function bh(t){return Kr(t)?t.scrollY:t.scrollTop}function Yl(t){return{x:mh(t),y:bh(t)}}var Ot;(function(t){t[t.Forward=1]="Forward",t[t.Backward=-1]="Backward"})(Ot||(Ot={}));function yh(t){return!bi||!t?!1:t===document.scrollingElement}function vh(t){const e={x:0,y:0},n=yh(t)?{height:window.innerHeight,width:window.innerWidth}:{height:t.clientHeight,width:t.clientWidth},r={x:t.scrollWidth-n.width,y:t.scrollHeight-n.height},s=t.scrollTop<=e.y,o=t.scrollLeft<=e.x,i=t.scrollTop>=r.y,a=t.scrollLeft>=r.x;return{isTop:s,isLeft:o,isBottom:i,isRight:a,maxScroll:r,minScroll:e}}const CC={x:.2,y:.2};function SC(t,e,n,r,s){let{top:o,left:i,right:a,bottom:l}=n;r===void 0&&(r=10),s===void 0&&(s=CC);const{isTop:c,isBottom:d,isLeft:u,isRight:f}=vh(t),g={x:0,y:0},m={x:0,y:0},p={height:e.height*s.y,width:e.width*s.x};return!c&&o<=e.top+p.height?(g.y=Ot.Backward,m.y=r*Math.abs((e.top+p.height-o)/p.height)):!d&&l>=e.bottom-p.height&&(g.y=Ot.Forward,m.y=r*Math.abs((e.bottom-p.height-l)/p.height)),!f&&a>=e.right-p.width?(g.x=Ot.Forward,m.x=r*Math.abs((e.right-p.width-a)/p.width)):!u&&i<=e.left+p.width&&(g.x=Ot.Backward,m.x=r*Math.abs((e.left+p.width-i)/p.width)),{direction:g,speed:m}}function _C(t){if(t===document.scrollingElement){const{innerWidth:o,innerHeight:i}=window;return{top:0,left:0,right:o,bottom:i,width:o,height:i}}const{top:e,left:n,right:r,bottom:s}=t.getBoundingClientRect();return{top:e,left:n,right:r,bottom:s,width:t.clientWidth,height:t.clientHeight}}function xh(t){return t.reduce((e,n)=>qr(e,Yl(n)),kn)}function EC(t){return t.reduce((e,n)=>e+mh(n),0)}function MC(t){return t.reduce((e,n)=>e+bh(n),0)}function wh(t,e){if(e===void 0&&(e=Qr),!t)return;const{top:n,left:r,bottom:s,right:o}=e(t);ph(t)&&(s<=0||o<=0||n>=window.innerHeight||r>=window.innerWidth)&&t.scrollIntoView({block:"center",inline:"center"})}const NC=[["x",["left","right"],EC],["y",["top","bottom"],MC]];class Kl{constructor(e,n){this.rect=void 0,this.width=void 0,this.height=void 0,this.top=void 0,this.bottom=void 0,this.right=void 0,this.left=void 0;const r=Si(n),s=xh(r);this.rect={...e},this.width=e.width,this.height=e.height;for(const[o,i,a]of NC)for(const l of i)Object.defineProperty(this,l,{get:()=>{const c=a(r),d=s[o]-c;return this.rect[l]+d},enumerable:!0});Object.defineProperty(this,"rect",{enumerable:!1})}}class Ys{constructor(e){this.target=void 0,this.listeners=[],this.removeAll=()=>{this.listeners.forEach(n=>{var r;return(r=this.target)==null?void 0:r.removeEventListener(...n)})},this.target=e}add(e,n,r){var s;(s=this.target)==null||s.addEventListener(e,n,r),this.listeners.push([e,n,r])}}function OC(t){const{EventTarget:e}=Xt(t);return t instanceof e?t:Xr(t)}function Xl(t,e){const n=Math.abs(t.x),r=Math.abs(t.y);return typeof e=="number"?Math.sqrt(n**2+r**2)>e:"x"in e&&"y"in e?n>e.x&&r>e.y:"x"in e?n>e.x:"y"in e?r>e.y:!1}var dn;(function(t){t.Click="click",t.DragStart="dragstart",t.Keydown="keydown",t.ContextMenu="contextmenu",t.Resize="resize",t.SelectionChange="selectionchange",t.VisibilityChange="visibilitychange"})(dn||(dn={}));function kh(t){t.preventDefault()}function TC(t){t.stopPropagation()}var Xe;(function(t){t.Space="Space",t.Down="ArrowDown",t.Right="ArrowRight",t.Left="ArrowLeft",t.Up="ArrowUp",t.Esc="Escape",t.Enter="Enter",t.Tab="Tab"})(Xe||(Xe={}));const Ch={start:[Xe.Space,Xe.Enter],cancel:[Xe.Esc],end:[Xe.Space,Xe.Enter,Xe.Tab]},AC=(t,e)=>{let{currentCoordinates:n}=e;switch(t.code){case Xe.Right:return{...n,x:n.x+25};case Xe.Left:return{...n,x:n.x-25};case Xe.Down:return{...n,y:n.y+25};case Xe.Up:return{...n,y:n.y-25}}};class Ks{constructor(e){this.props=void 0,this.autoScrollEnabled=!1,this.referenceCoordinates=void 0,this.listeners=void 0,this.windowListeners=void 0,this.props=e;const{event:{target:n}}=e;this.props=e,this.listeners=new Ys(Xr(n)),this.windowListeners=new Ys(Xt(n)),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleCancel=this.handleCancel.bind(this),this.attach()}attach(){this.handleStart(),this.windowListeners.add(dn.Resize,this.handleCancel),this.windowListeners.add(dn.VisibilityChange,this.handleCancel),setTimeout(()=>this.listeners.add(dn.Keydown,this.handleKeyDown))}handleStart(){const{activeNode:e,onStart:n}=this.props,r=e.node.current;r&&wh(r),n(kn)}handleKeyDown(e){if(wi(e)){const{active:n,context:r,options:s}=this.props,{keyboardCodes:o=Ch,coordinateGetter:i=AC,scrollBehavior:a="smooth"}=s,{code:l}=e;if(o.end.includes(l)){this.handleEnd(e);return}if(o.cancel.includes(l)){this.handleCancel(e);return}const{collisionRect:c}=r.current,d=c?{x:c.left,y:c.top}:kn;this.referenceCoordinates||(this.referenceCoordinates=d);const u=i(e,{active:n,context:r.current,currentCoordinates:d});if(u){const f=Gs(u,d),g={x:0,y:0},{scrollableAncestors:m}=r.current;for(const p of m){const y=e.code,{isTop:b,isRight:_,isLeft:x,isBottom:E,maxScroll:C,minScroll:k}=vh(p),N=_C(p),S={x:Math.min(y===Xe.Right?N.right-N.width/2:N.right,Math.max(y===Xe.Right?N.left:N.left+N.width/2,u.x)),y:Math.min(y===Xe.Down?N.bottom-N.height/2:N.bottom,Math.max(y===Xe.Down?N.top:N.top+N.height/2,u.y))},w=y===Xe.Right&&!_||y===Xe.Left&&!x,M=y===Xe.Down&&!E||y===Xe.Up&&!b;if(w&&S.x!==u.x){const T=p.scrollLeft+f.x,P=y===Xe.Right&&T<=C.x||y===Xe.Left&&T>=k.x;if(P&&!f.y){p.scrollTo({left:T,behavior:a});return}P?g.x=p.scrollLeft-T:g.x=y===Xe.Right?p.scrollLeft-C.x:p.scrollLeft-k.x,g.x&&p.scrollBy({left:-g.x,behavior:a});break}else if(M&&S.y!==u.y){const T=p.scrollTop+f.y,P=y===Xe.Down&&T<=C.y||y===Xe.Up&&T>=k.y;if(P&&!f.x){p.scrollTo({top:T,behavior:a});return}P?g.y=p.scrollTop-T:g.y=y===Xe.Down?p.scrollTop-C.y:p.scrollTop-k.y,g.y&&p.scrollBy({top:-g.y,behavior:a});break}}this.handleMove(e,qr(Gs(u,this.referenceCoordinates),g))}}}handleMove(e,n){const{onMove:r}=this.props;e.preventDefault(),r(n)}handleEnd(e){const{onEnd:n}=this.props;e.preventDefault(),this.detach(),n()}handleCancel(e){const{onCancel:n}=this.props;e.preventDefault(),this.detach(),n()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll()}}Ks.activators=[{eventName:"onKeyDown",handler:(t,e,n)=>{let{keyboardCodes:r=Ch,onActivation:s}=e,{active:o}=n;const{code:i}=t.nativeEvent;if(r.start.includes(i)){const a=o.activatorNode.current;return a&&t.target!==a?!1:(t.preventDefault(),s?.({event:t.nativeEvent}),!0)}return!1}}];function Sh(t){return!!(t&&"distance"in t)}function _h(t){return!!(t&&"delay"in t)}class ql{constructor(e,n,r){var s;r===void 0&&(r=OC(e.event.target)),this.props=void 0,this.events=void 0,this.autoScrollEnabled=!0,this.document=void 0,this.activated=!1,this.initialCoordinates=void 0,this.timeoutId=null,this.listeners=void 0,this.documentListeners=void 0,this.windowListeners=void 0,this.props=e,this.events=n;const{event:o}=e,{target:i}=o;this.props=e,this.events=n,this.document=Xr(i),this.documentListeners=new Ys(this.document),this.listeners=new Ys(r),this.windowListeners=new Ys(Xt(i)),this.initialCoordinates=(s=ki(o))!=null?s:kn,this.handleStart=this.handleStart.bind(this),this.handleMove=this.handleMove.bind(this),this.handleEnd=this.handleEnd.bind(this),this.handleCancel=this.handleCancel.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.removeTextSelection=this.removeTextSelection.bind(this),this.attach()}attach(){const{events:e,props:{options:{activationConstraint:n,bypassActivationConstraint:r}}}=this;if(this.listeners.add(e.move.name,this.handleMove,{passive:!1}),this.listeners.add(e.end.name,this.handleEnd),e.cancel&&this.listeners.add(e.cancel.name,this.handleCancel),this.windowListeners.add(dn.Resize,this.handleCancel),this.windowListeners.add(dn.DragStart,kh),this.windowListeners.add(dn.VisibilityChange,this.handleCancel),this.windowListeners.add(dn.ContextMenu,kh),this.documentListeners.add(dn.Keydown,this.handleKeydown),n){if(r!=null&&r({event:this.props.event,activeNode:this.props.activeNode,options:this.props.options}))return this.handleStart();if(_h(n)){this.timeoutId=setTimeout(this.handleStart,n.delay),this.handlePending(n);return}if(Sh(n)){this.handlePending(n);return}}this.handleStart()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll(),setTimeout(this.documentListeners.removeAll,50),this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}handlePending(e,n){const{active:r,onPending:s}=this.props;s(r,e,this.initialCoordinates,n)}handleStart(){const{initialCoordinates:e}=this,{onStart:n}=this.props;e&&(this.activated=!0,this.documentListeners.add(dn.Click,TC,{capture:!0}),this.removeTextSelection(),this.documentListeners.add(dn.SelectionChange,this.removeTextSelection),n(e))}handleMove(e){var n;const{activated:r,initialCoordinates:s,props:o}=this,{onMove:i,options:{activationConstraint:a}}=o;if(!s)return;const l=(n=ki(e))!=null?n:kn,c=Gs(s,l);if(!r&&a){if(Sh(a)){if(a.tolerance!=null&&Xl(c,a.tolerance))return this.handleCancel();if(Xl(c,a.distance))return this.handleStart()}if(_h(a)&&Xl(c,a.tolerance))return this.handleCancel();this.handlePending(a,c);return}e.cancelable&&e.preventDefault(),i(l)}handleEnd(){const{onAbort:e,onEnd:n}=this.props;this.detach(),this.activated||e(this.props.active),n()}handleCancel(){const{onAbort:e,onCancel:n}=this.props;this.detach(),this.activated||e(this.props.active),n()}handleKeydown(e){e.code===Xe.Esc&&this.handleCancel()}removeTextSelection(){var e;(e=this.document.getSelection())==null||e.removeAllRanges()}}const DC={cancel:{name:"pointercancel"},move:{name:"pointermove"},end:{name:"pointerup"}};class Xs extends ql{constructor(e){const{event:n}=e,r=Xr(n.target);super(e,DC,r)}}Xs.activators=[{eventName:"onPointerDown",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:r}=e;return!n.isPrimary||n.button!==0?!1:(r?.({event:n}),!0)}}];const IC={move:{name:"mousemove"},end:{name:"mouseup"}};var Zl;(function(t){t[t.RightClick=2]="RightClick"})(Zl||(Zl={}));class LC extends ql{constructor(e){super(e,IC,Xr(e.event.target))}}LC.activators=[{eventName:"onMouseDown",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:r}=e;return n.button===Zl.RightClick?!1:(r?.({event:n}),!0)}}];const Ql={cancel:{name:"touchcancel"},move:{name:"touchmove"},end:{name:"touchend"}};class PC extends ql{constructor(e){super(e,Ql)}static setup(){return window.addEventListener(Ql.move.name,e,{capture:!1,passive:!1}),function(){window.removeEventListener(Ql.move.name,e)};function e(){}}}PC.activators=[{eventName:"onTouchStart",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:r}=e;const{touches:s}=n;return s.length>1?!1:(r?.({event:n}),!0)}}];var qs;(function(t){t[t.Pointer=0]="Pointer",t[t.DraggableRect=1]="DraggableRect"})(qs||(qs={}));var _i;(function(t){t[t.TreeOrder=0]="TreeOrder",t[t.ReversedTreeOrder=1]="ReversedTreeOrder"})(_i||(_i={}));function $C(t){let{acceleration:e,activator:n=qs.Pointer,canScroll:r,draggingRect:s,enabled:o,interval:i=5,order:a=_i.TreeOrder,pointerCoordinates:l,scrollableAncestors:c,scrollableAncestorRects:d,delta:u,threshold:f}=t;const g=zC({delta:u,disabled:!o}),[m,p]=Qk(),y=v.useRef({x:0,y:0}),b=v.useRef({x:0,y:0}),_=v.useMemo(()=>{switch(n){case qs.Pointer:return l?{top:l.y,bottom:l.y,left:l.x,right:l.x}:null;case qs.DraggableRect:return s}},[n,s,l]),x=v.useRef(null),E=v.useCallback(()=>{const k=x.current;if(!k)return;const N=y.current.x*b.current.x,S=y.current.y*b.current.y;k.scrollBy(N,S)},[]),C=v.useMemo(()=>a===_i.TreeOrder?[...c].reverse():c,[a,c]);v.useEffect(()=>{if(!o||!c.length||!_){p();return}for(const k of C){if(r?.(k)===!1)continue;const N=c.indexOf(k),S=d[N];if(!S)continue;const{direction:w,speed:M}=SC(k,S,_,e,f);for(const T of["x","y"])g[T][w[T]]||(M[T]=0,w[T]=0);if(M.x>0||M.y>0){p(),x.current=k,m(E,i),y.current=M,b.current=w;return}}y.current={x:0,y:0},b.current={x:0,y:0},p()},[e,E,r,p,o,i,JSON.stringify(_),JSON.stringify(g),m,c,C,d,JSON.stringify(f)])}const FC={x:{[Ot.Backward]:!1,[Ot.Forward]:!1},y:{[Ot.Backward]:!1,[Ot.Forward]:!1}};function zC(t){let{delta:e,disabled:n}=t;const r=xi(e);return js(s=>{if(n||!r||!s)return FC;const o={x:Math.sign(e.x-r.x),y:Math.sign(e.y-r.y)};return{x:{[Ot.Backward]:s.x[Ot.Backward]||o.x===-1,[Ot.Forward]:s.x[Ot.Forward]||o.x===1},y:{[Ot.Backward]:s.y[Ot.Backward]||o.y===-1,[Ot.Forward]:s.y[Ot.Forward]||o.y===1}}},[n,e,r])}function VC(t,e){const n=e!=null?t.get(e):void 0,r=n?n.node.current:null;return js(s=>{var o;return e==null?null:(o=r??s)!=null?o:null},[r,e])}function HC(t,e){return v.useMemo(()=>t.reduce((n,r)=>{const{sensor:s}=r,o=s.activators.map(i=>({eventName:i.eventName,handler:e(i.handler,r)}));return[...n,...o]},[]),[t,e])}var Zs;(function(t){t[t.Always=0]="Always",t[t.BeforeDragging=1]="BeforeDragging",t[t.WhileDragging=2]="WhileDragging"})(Zs||(Zs={}));var Jl;(function(t){t.Optimized="optimized"})(Jl||(Jl={}));const Eh=new Map;function UC(t,e){let{dragging:n,dependencies:r,config:s}=e;const[o,i]=v.useState(null),{frequency:a,measure:l,strategy:c}=s,d=v.useRef(t),u=y(),f=Bs(u),g=v.useCallback(function(b){b===void 0&&(b=[]),!f.current&&i(_=>_===null?b:_.concat(b.filter(x=>!_.includes(x))))},[f]),m=v.useRef(null),p=js(b=>{if(u&&!n)return Eh;if(!b||b===Eh||d.current!==t||o!=null){const _=new Map;for(let x of t){if(!x)continue;if(o&&o.length>0&&!o.includes(x.id)&&x.rect.current){_.set(x.id,x.rect.current);continue}const E=x.node.current,C=E?new Kl(l(E),E):null;x.rect.current=C,C&&_.set(x.id,C)}return _}return b},[t,o,n,u,l]);return v.useEffect(()=>{d.current=t},[t]),v.useEffect(()=>{u||g()},[n,u]),v.useEffect(()=>{o&&o.length>0&&i(null)},[JSON.stringify(o)]),v.useEffect(()=>{u||typeof a!="number"||m.current!==null||(m.current=setTimeout(()=>{g(),m.current=null},a))},[a,u,g,...r]),{droppableRects:p,measureDroppableContainers:g,measuringScheduled:o!=null};function y(){switch(c){case Zs.Always:return!1;case Zs.BeforeDragging:return n;default:return!n}}}function Rl(t,e){return js(n=>t?n||(typeof e=="function"?e(t):t):null,[e,t])}function BC(t,e){return Rl(t,e)}function jC(t){let{callback:e,disabled:n}=t;const r=yi(e),s=v.useMemo(()=>{if(n||typeof window>"u"||typeof window.MutationObserver>"u")return;const{MutationObserver:o}=window;return new o(r)},[r,n]);return v.useEffect(()=>()=>s?.disconnect(),[s]),s}function Ei(t){let{callback:e,disabled:n}=t;const r=yi(e),s=v.useMemo(()=>{if(n||typeof window>"u"||typeof window.ResizeObserver>"u")return;const{ResizeObserver:o}=window;return new o(r)},[n]);return v.useEffect(()=>()=>s?.disconnect(),[s]),s}function WC(t){return new Kl(Qr(t),t)}function Mh(t,e,n){e===void 0&&(e=WC);const[r,s]=v.useState(null);function o(){s(l=>{if(!t)return null;if(t.isConnected===!1){var c;return(c=l??n)!=null?c:null}const d=e(t);return JSON.stringify(l)===JSON.stringify(d)?l:d})}const i=jC({callback(l){if(t)for(const c of l){const{type:d,target:u}=c;if(d==="childList"&&u instanceof HTMLElement&&u.contains(t)){o();break}}}}),a=Ei({callback:o});return wn(()=>{o(),t?(a?.observe(t),i?.observe(document.body,{childList:!0,subtree:!0})):(a?.disconnect(),i?.disconnect())},[t]),r}function GC(t){const e=Rl(t);return fh(t,e)}const Nh=[];function YC(t){const e=v.useRef(t),n=js(r=>t?r&&r!==Nh&&t&&e.current&&t.parentNode===e.current.parentNode?r:Si(t):Nh,[t]);return v.useEffect(()=>{e.current=t},[t]),n}function KC(t){const[e,n]=v.useState(null),r=v.useRef(t),s=v.useCallback(o=>{const i=Gl(o.target);i&&n(a=>a?(a.set(i,Yl(i)),new Map(a)):null)},[]);return v.useEffect(()=>{const o=r.current;if(t!==o){i(o);const a=t.map(l=>{const c=Gl(l);return c?(c.addEventListener("scroll",s,{passive:!0}),[c,Yl(c)]):null}).filter(l=>l!=null);n(a.length?new Map(a):null),r.current=t}return()=>{i(t),i(o)};function i(a){a.forEach(l=>{const c=Gl(l);c?.removeEventListener("scroll",s)})}},[s,t]),v.useMemo(()=>t.length?e?Array.from(e.values()).reduce((o,i)=>qr(o,i),kn):xh(t):kn,[t,e])}function Oh(t,e){e===void 0&&(e=[]);const n=v.useRef(null);return v.useEffect(()=>{n.current=null},e),v.useEffect(()=>{const r=t!==kn;r&&!n.current&&(n.current=t),!r&&n.current&&(n.current=null)},[t]),n.current?Gs(t,n.current):kn}function XC(t){v.useEffect(()=>{if(!bi)return;const e=t.map(n=>{let{sensor:r}=n;return r.setup==null?void 0:r.setup()});return()=>{for(const n of e)n?.()}},t.map(e=>{let{sensor:n}=e;return n}))}function qC(t,e){return v.useMemo(()=>t.reduce((n,r)=>{let{eventName:s,handler:o}=r;return n[s]=i=>{o(i,e)},n},{}),[t,e])}function Th(t){return v.useMemo(()=>t?xC(t):null,[t])}const Ah=[];function ZC(t,e){e===void 0&&(e=Qr);const[n]=t,r=Th(n?Xt(n):null),[s,o]=v.useState(Ah);function i(){o(()=>t.length?t.map(l=>yh(l)?r:new Kl(e(l),l)):Ah)}const a=Ei({callback:i});return wn(()=>{a?.disconnect(),i(),t.forEach(l=>a?.observe(l))},[t]),s}function Dh(t){if(!t)return null;if(t.children.length>1)return t;const e=t.children[0];return Us(e)?e:t}function QC(t){let{measure:e}=t;const[n,r]=v.useState(null),s=v.useCallback(c=>{for(const{target:d}of c)if(Us(d)){r(u=>{const f=e(d);return u?{...u,width:f.width,height:f.height}:f});break}},[e]),o=Ei({callback:s}),i=v.useCallback(c=>{const d=Dh(c);o?.disconnect(),d&&o?.observe(d),r(d?e(d):null)},[e,o]),[a,l]=vi(i);return v.useMemo(()=>({nodeRef:a,rect:n,setRef:l}),[n,a,l])}const JC=[{sensor:Xs,options:{}},{sensor:Ks,options:{}}],RC={current:{}},Mi={draggable:{measure:gh},droppable:{measure:gh,strategy:Zs.WhileDragging,frequency:Jl.Optimized},dragOverlay:{measure:Qr}};class Qs extends Map{get(e){var n;return e!=null&&(n=super.get(e))!=null?n:void 0}toArray(){return Array.from(this.values())}getEnabled(){return this.toArray().filter(e=>{let{disabled:n}=e;return!n})}getNodeFor(e){var n,r;return(n=(r=this.get(e))==null?void 0:r.node.current)!=null?n:void 0}}const eS={activatorEvent:null,active:null,activeNode:null,activeNodeRect:null,collisions:null,containerNodeRect:null,draggableNodes:new Map,droppableRects:new Map,droppableContainers:new Qs,over:null,dragOverlay:{nodeRef:{current:null},rect:null,setRef:Ci},scrollableAncestors:[],scrollableAncestorRects:[],measuringConfiguration:Mi,measureDroppableContainers:Ci,windowRect:null,measuringScheduled:!1},Ih={activatorEvent:null,activators:[],active:null,activeNodeRect:null,ariaDescribedById:{draggable:""},dispatch:Ci,draggableNodes:new Map,over:null,measureDroppableContainers:Ci},Js=v.createContext(Ih),Lh=v.createContext(eS);function tS(){return{draggable:{active:null,initialCoordinates:{x:0,y:0},nodes:new Map,translate:{x:0,y:0}},droppable:{containers:new Qs}}}function nS(t,e){switch(e.type){case _t.DragStart:return{...t,draggable:{...t.draggable,initialCoordinates:e.initialCoordinates,active:e.active}};case _t.DragMove:return t.draggable.active==null?t:{...t,draggable:{...t.draggable,translate:{x:e.coordinates.x-t.draggable.initialCoordinates.x,y:e.coordinates.y-t.draggable.initialCoordinates.y}}};case _t.DragEnd:case _t.DragCancel:return{...t,draggable:{...t.draggable,active:null,initialCoordinates:{x:0,y:0},translate:{x:0,y:0}}};case _t.RegisterDroppable:{const{element:n}=e,{id:r}=n,s=new Qs(t.droppable.containers);return s.set(r,n),{...t,droppable:{...t.droppable,containers:s}}}case _t.SetDroppableDisabled:{const{id:n,key:r,disabled:s}=e,o=t.droppable.containers.get(n);if(!o||r!==o.key)return t;const i=new Qs(t.droppable.containers);return i.set(n,{...o,disabled:s}),{...t,droppable:{...t.droppable,containers:i}}}case _t.UnregisterDroppable:{const{id:n,key:r}=e,s=t.droppable.containers.get(n);if(!s||r!==s.key)return t;const o=new Qs(t.droppable.containers);return o.delete(n),{...t,droppable:{...t.droppable,containers:o}}}default:return t}}function rS(t){let{disabled:e}=t;const{active:n,activatorEvent:r,draggableNodes:s}=v.useContext(Js),o=xi(r),i=xi(n?.id);return v.useEffect(()=>{if(!e&&!r&&o&&i!=null){if(!wi(o)||document.activeElement===o.target)return;const a=s.get(i);if(!a)return;const{activatorNode:l,node:c}=a;if(!l.current&&!c.current)return;requestAnimationFrame(()=>{for(const d of[l.current,c.current]){if(!d)continue;const u=eC(d);if(u){u.focus();break}}})}},[r,e,s,i,o]),null}function Ph(t,e){let{transform:n,...r}=e;return t!=null&&t.length?t.reduce((s,o)=>o({transform:s,...r}),n):n}function sS(t){return v.useMemo(()=>({draggable:{...Mi.draggable,...t?.draggable},droppable:{...Mi.droppable,...t?.droppable},dragOverlay:{...Mi.dragOverlay,...t?.dragOverlay}}),[t?.draggable,t?.droppable,t?.dragOverlay])}function oS(t){let{activeNode:e,measure:n,initialRect:r,config:s=!0}=t;const o=v.useRef(!1),{x:i,y:a}=typeof s=="boolean"?{x:s,y:s}:s;wn(()=>{if(!i&&!a||!e){o.current=!1;return}if(o.current||!r)return;const c=e?.node.current;if(!c||c.isConnected===!1)return;const d=n(c),u=fh(d,r);if(i||(u.x=0),a||(u.y=0),o.current=!0,Math.abs(u.x)>0||Math.abs(u.y)>0){const f=ph(c);f&&f.scrollBy({top:u.y,left:u.x})}},[e,i,a,r,n])}const Ni=v.createContext({...kn,scaleX:1,scaleY:1});var nr;(function(t){t[t.Uninitialized=0]="Uninitialized",t[t.Initializing=1]="Initializing",t[t.Initialized=2]="Initialized"})(nr||(nr={}));const ec=v.memo(function(e){var n,r,s,o;let{id:i,accessibility:a,autoScroll:l=!0,children:c,sensors:d=JC,collisionDetection:u=gC,measuring:f,modifiers:g,...m}=e;const p=v.useReducer(nS,void 0,tS),[y,b]=p,[_,x]=iC(),[E,C]=v.useState(nr.Uninitialized),k=E===nr.Initialized,{draggable:{active:N,nodes:S,translate:w},droppable:{containers:M}}=y,T=N!=null?S.get(N):null,P=v.useRef({initial:null,translated:null}),L=v.useMemo(()=>{var Pe;return N!=null?{id:N,data:(Pe=T?.data)!=null?Pe:RC,rect:P}:null},[N,T]),$=v.useRef(null),[A,D]=v.useState(null),[O,z]=v.useState(null),H=Bs(m,Object.values(m)),j=Ws("DndDescribedBy",i),W=v.useMemo(()=>M.getEnabled(),[M]),U=sS(f),{droppableRects:B,measureDroppableContainers:G,measuringScheduled:V}=UC(W,{dragging:k,dependencies:[w.x,w.y],config:U.droppable}),X=VC(S,N),K=v.useMemo(()=>O?ki(O):null,[O]),Z=ot(),ee=BC(X,U.draggable.measure);oS({activeNode:N!=null?S.get(N):null,config:Z.layoutShiftCompensation,initialRect:ee,measure:U.draggable.measure});const ne=Mh(X,U.draggable.measure,ee),he=Mh(X?X.parentElement:null),J=v.useRef({activatorEvent:null,active:null,activeNode:X,collisionRect:null,collisions:null,droppableRects:B,draggableNodes:S,draggingNode:null,draggingNodeRect:null,droppableContainers:M,over:null,scrollableAncestors:[],scrollAdjustedTranslate:null}),le=M.getNodeFor((n=J.current.over)==null?void 0:n.id),ue=QC({measure:U.dragOverlay.measure}),ae=(r=ue.nodeRef.current)!=null?r:X,ve=k?(s=ue.rect)!=null?s:ne:null,fe=!!(ue.nodeRef.current&&ue.rect),ye=GC(fe?null:ne),we=Th(ae?Xt(ae):null),He=YC(k?le??X:null),se=ZC(He),Oe=Ph(g,{transform:{x:w.x-ye.x,y:w.y-ye.y,scaleX:1,scaleY:1},activatorEvent:O,active:L,activeNodeRect:ne,containerNodeRect:he,draggingNodeRect:ve,over:J.current.over,overlayNodeRect:ue.rect,scrollableAncestors:He,scrollableAncestorRects:se,windowRect:we}),Le=K?qr(K,w):null,Ie=KC(He),Be=Oh(Ie),st=Oh(Ie,[ne]),ut=qr(Oe,Be),je=ve?bC(ve,Oe):null,nt=L&&je?u({active:L,collisionRect:je,droppableRects:B,droppableContainers:W,pointerCoordinates:Le}):null,Ye=dh(nt,"id"),[te,ge]=v.useState(null),me=fe?Oe:qr(Oe,st),pe=pC(me,(o=te?.rect)!=null?o:null,ne),be=v.useRef(null),Ue=v.useCallback((Pe,Te)=>{let{sensor:Ve,options:De}=Te;if($.current==null)return;const qe=S.get($.current);if(!qe)return;const it=Pe.nativeEvent,pt=new Ve({active:$.current,activeNode:qe,event:it,options:De,context:J,onAbort(bt){if(!S.get(bt))return;const{onDragAbort:jt}=H.current,ft={id:bt};jt?.(ft),_({type:"onDragAbort",event:ft})},onPending(bt,wt,jt,ft){if(!S.get(bt))return;const{onDragPending:Nn}=H.current,Wt={id:bt,constraint:wt,initialCoordinates:jt,offset:ft};Nn?.(Wt),_({type:"onDragPending",event:Wt})},onStart(bt){const wt=$.current;if(wt==null)return;const jt=S.get(wt);if(!jt)return;const{onDragStart:ft}=H.current,Ke={activatorEvent:it,active:{id:wt,data:jt.data,rect:P}};Tn.unstable_batchedUpdates(()=>{ft?.(Ke),C(nr.Initializing),b({type:_t.DragStart,initialCoordinates:bt,active:wt}),_({type:"onDragStart",event:Ke}),D(be.current),z(it)})},onMove(bt){b({type:_t.DragMove,coordinates:bt})},onEnd:rn(_t.DragEnd),onCancel:rn(_t.DragCancel)});be.current=pt;function rn(bt){return async function(){const{active:jt,collisions:ft,over:Ke,scrollAdjustedTranslate:Nn}=J.current;let Wt=null;if(jt&&Nn){const{cancelDrop:bn}=H.current;Wt={activatorEvent:it,active:jt,collisions:ft,delta:Nn,over:Ke},bt===_t.DragEnd&&typeof bn=="function"&&await Promise.resolve(bn(Wt))&&(bt=_t.DragCancel)}$.current=null,Tn.unstable_batchedUpdates(()=>{b({type:bt}),C(nr.Uninitialized),ge(null),D(null),z(null),be.current=null;const bn=bt===_t.DragEnd?"onDragEnd":"onDragCancel";if(Wt){const hr=H.current[bn];hr?.(Wt),_({type:bn,event:Wt})}})}}},[S]),Ce=v.useCallback((Pe,Te)=>(Ve,De)=>{const qe=Ve.nativeEvent,it=S.get(De);if($.current!==null||!it||qe.dndKit||qe.defaultPrevented)return;const pt={active:it};Pe(Ve,Te.options,pt)===!0&&(qe.dndKit={capturedBy:Te.sensor},$.current=De,Ue(Ve,Te))},[S,Ue]),ze=HC(d,Ce);XC(d),wn(()=>{ne&&E===nr.Initializing&&C(nr.Initialized)},[ne,E]),v.useEffect(()=>{const{onDragMove:Pe}=H.current,{active:Te,activatorEvent:Ve,collisions:De,over:qe}=J.current;if(!Te||!Ve)return;const it={active:Te,activatorEvent:Ve,collisions:De,delta:{x:ut.x,y:ut.y},over:qe};Tn.unstable_batchedUpdates(()=>{Pe?.(it),_({type:"onDragMove",event:it})})},[ut.x,ut.y]),v.useEffect(()=>{const{active:Pe,activatorEvent:Te,collisions:Ve,droppableContainers:De,scrollAdjustedTranslate:qe}=J.current;if(!Pe||$.current==null||!Te||!qe)return;const{onDragOver:it}=H.current,pt=De.get(Ye),rn=pt&&pt.rect.current?{id:pt.id,rect:pt.rect.current,data:pt.data,disabled:pt.disabled}:null,bt={active:Pe,activatorEvent:Te,collisions:Ve,delta:{x:qe.x,y:qe.y},over:rn};Tn.unstable_batchedUpdates(()=>{ge(rn),it?.(bt),_({type:"onDragOver",event:bt})})},[Ye]),wn(()=>{J.current={activatorEvent:O,active:L,activeNode:X,collisionRect:je,collisions:nt,droppableRects:B,draggableNodes:S,draggingNode:ae,draggingNodeRect:ve,droppableContainers:M,over:te,scrollableAncestors:He,scrollAdjustedTranslate:ut},P.current={initial:ve,translated:je}},[L,X,nt,je,S,ae,ve,B,M,te,He,ut]),$C({...Z,delta:w,draggingRect:je,pointerCoordinates:Le,scrollableAncestors:He,scrollableAncestorRects:se});const Se=v.useMemo(()=>({active:L,activeNode:X,activeNodeRect:ne,activatorEvent:O,collisions:nt,containerNodeRect:he,dragOverlay:ue,draggableNodes:S,droppableContainers:M,droppableRects:B,over:te,measureDroppableContainers:G,scrollableAncestors:He,scrollableAncestorRects:se,measuringConfiguration:U,measuringScheduled:V,windowRect:we}),[L,X,ne,O,nt,he,ue,S,M,B,te,G,He,se,U,V,we]),Re=v.useMemo(()=>({activatorEvent:O,activators:ze,active:L,activeNodeRect:ne,ariaDescribedById:{draggable:j},dispatch:b,draggableNodes:S,over:te,measureDroppableContainers:G}),[O,ze,L,ne,b,j,S,te,G]);return v.createElement(ih.Provider,{value:x},v.createElement(Js.Provider,{value:Re},v.createElement(Lh.Provider,{value:Se},v.createElement(Ni.Provider,{value:pe},c)),v.createElement(rS,{disabled:a?.restoreFocus===!1})),v.createElement(cC,{...a,hiddenTextDescribedById:j}));function ot(){const Pe=A?.autoScrollEnabled===!1,Te=typeof l=="object"?l.enabled===!1:l===!1,Ve=k&&!Pe&&!Te;return typeof l=="object"?{...l,enabled:Ve}:{enabled:Ve}}}),iS=v.createContext(null),$h="button",aS="Draggable";function lS(t){let{id:e,data:n,disabled:r=!1,attributes:s}=t;const o=Ws(aS),{activators:i,activatorEvent:a,active:l,activeNodeRect:c,ariaDescribedById:d,draggableNodes:u,over:f}=v.useContext(Js),{role:g=$h,roleDescription:m="draggable",tabIndex:p=0}=s??{},y=l?.id===e,b=v.useContext(y?Ni:iS),[_,x]=vi(),[E,C]=vi(),k=qC(i,e),N=Bs(n);wn(()=>(u.set(e,{id:e,key:o,node:_,activatorNode:E,data:N}),()=>{const w=u.get(e);w&&w.key===o&&u.delete(e)}),[u,e]);const S=v.useMemo(()=>({role:g,tabIndex:p,"aria-disabled":r,"aria-pressed":y&&g===$h?!0:void 0,"aria-roledescription":m,"aria-describedby":d.draggable}),[r,g,p,y,m,d.draggable]);return{active:l,activatorEvent:a,activeNodeRect:c,attributes:S,isDragging:y,listeners:r?void 0:k,node:_,over:f,setNodeRef:x,setActivatorNodeRef:C,transform:b}}function Fh(){return v.useContext(Lh)}const cS="Droppable",dS={timeout:25};function uS(t){let{data:e,disabled:n=!1,id:r,resizeObserverConfig:s}=t;const o=Ws(cS),{active:i,dispatch:a,over:l,measureDroppableContainers:c}=v.useContext(Js),d=v.useRef({disabled:n}),u=v.useRef(!1),f=v.useRef(null),g=v.useRef(null),{disabled:m,updateMeasurementsFor:p,timeout:y}={...dS,...s},b=Bs(p??r),_=v.useCallback(()=>{if(!u.current){u.current=!0;return}g.current!=null&&clearTimeout(g.current),g.current=setTimeout(()=>{c(Array.isArray(b.current)?b.current:[b.current]),g.current=null},y)},[y]),x=Ei({callback:_,disabled:m||!i}),E=v.useCallback((S,w)=>{x&&(w&&(x.unobserve(w),u.current=!1),S&&x.observe(S))},[x]),[C,k]=vi(E),N=Bs(e);return v.useEffect(()=>{!x||!C.current||(x.disconnect(),u.current=!1,x.observe(C.current))},[C,x]),v.useEffect(()=>(a({type:_t.RegisterDroppable,element:{id:r,key:o,disabled:n,node:C,rect:f,data:N}}),()=>a({type:_t.UnregisterDroppable,key:o,id:r})),[r]),v.useEffect(()=>{n!==d.current.disabled&&(a({type:_t.SetDroppableDisabled,id:r,key:o,disabled:n}),d.current.disabled=n)},[r,o,n,a]),{active:i,rect:f,isOver:l?.id===r,node:C,over:l,setNodeRef:k}}function fS(t){let{animation:e,children:n}=t;const[r,s]=v.useState(null),[o,i]=v.useState(null),a=xi(n);return!n&&!r&&a&&s(a),wn(()=>{if(!o)return;const l=r?.key,c=r?.props.id;if(l==null||c==null){s(null);return}Promise.resolve(e(c,o)).then(()=>{s(null)})},[e,r,o]),v.createElement(v.Fragment,null,n,r?v.cloneElement(r,{ref:i}):null)}const hS={x:0,y:0,scaleX:1,scaleY:1};function gS(t){let{children:e}=t;return v.createElement(Js.Provider,{value:Ih},v.createElement(Ni.Provider,{value:hS},e))}const pS={position:"fixed",touchAction:"none"},mS=t=>wi(t)?"transform 250ms ease":void 0,bS=v.forwardRef((t,e)=>{let{as:n,activatorEvent:r,adjustScale:s,children:o,className:i,rect:a,style:l,transform:c,transition:d=mS}=t;if(!a)return null;const u=s?c:{...c,scaleX:1,scaleY:1},f={...pS,width:a.width,height:a.height,top:a.top,left:a.left,transform:Pn.Transform.toString(u),transformOrigin:s&&r?dC(r,a):void 0,transition:typeof d=="function"?d(r):d,...l};return v.createElement(n,{className:i,style:f,ref:e},o)}),yS={duration:250,easing:"ease",keyframes:t=>{let{transform:{initial:e,final:n}}=t;return[{transform:Pn.Transform.toString(e)},{transform:Pn.Transform.toString(n)}]},sideEffects:(t=>e=>{let{active:n,dragOverlay:r}=e;const s={},{styles:o,className:i}=t;if(o!=null&&o.active)for(const[a,l]of Object.entries(o.active))l!==void 0&&(s[a]=n.node.style.getPropertyValue(a),n.node.style.setProperty(a,l));if(o!=null&&o.dragOverlay)for(const[a,l]of Object.entries(o.dragOverlay))l!==void 0&&r.node.style.setProperty(a,l);return i!=null&&i.active&&n.node.classList.add(i.active),i!=null&&i.dragOverlay&&r.node.classList.add(i.dragOverlay),function(){for(const[l,c]of Object.entries(s))n.node.style.setProperty(l,c);i!=null&&i.active&&n.node.classList.remove(i.active)}})({styles:{active:{opacity:"0"}}})};function vS(t){let{config:e,draggableNodes:n,droppableContainers:r,measuringConfiguration:s}=t;return yi((o,i)=>{if(e===null)return;const a=n.get(o);if(!a)return;const l=a.node.current;if(!l)return;const c=Dh(i);if(!c)return;const{transform:d}=Xt(i).getComputedStyle(i),u=hh(d);if(!u)return;const f=typeof e=="function"?e:xS(e);return wh(l,s.draggable.measure),f({active:{id:o,data:a.data,node:l,rect:s.draggable.measure(l)},draggableNodes:n,dragOverlay:{node:i,rect:s.dragOverlay.measure(c)},droppableContainers:r,measuringConfiguration:s,transform:u})})}function xS(t){const{duration:e,easing:n,sideEffects:r,keyframes:s}={...yS,...t};return o=>{let{active:i,dragOverlay:a,transform:l,...c}=o;if(!e)return;const d={x:a.rect.left-i.rect.left,y:a.rect.top-i.rect.top},u={scaleX:l.scaleX!==1?i.rect.width*l.scaleX/a.rect.width:1,scaleY:l.scaleY!==1?i.rect.height*l.scaleY/a.rect.height:1},f={x:l.x-d.x,y:l.y-d.y,...u},g=s({...c,active:i,dragOverlay:a,transform:{initial:l,final:f}}),[m]=g,p=g[g.length-1];if(JSON.stringify(m)===JSON.stringify(p))return;const y=r?.({active:i,dragOverlay:a,...c}),b=a.node.animate(g,{duration:e,easing:n,fill:"forwards"});return new Promise(_=>{b.onfinish=()=>{y?.(),_()}})}}let zh=0;function wS(t){return v.useMemo(()=>{if(t!=null)return zh++,zh},[t])}const kS=v.memo(t=>{let{adjustScale:e=!1,children:n,dropAnimation:r,style:s,transition:o,modifiers:i,wrapperElement:a="div",className:l,zIndex:c=999}=t;const{activatorEvent:d,active:u,activeNodeRect:f,containerNodeRect:g,draggableNodes:m,droppableContainers:p,dragOverlay:y,over:b,measuringConfiguration:_,scrollableAncestors:x,scrollableAncestorRects:E,windowRect:C}=Fh(),k=v.useContext(Ni),N=wS(u?.id),S=Ph(i,{activatorEvent:d,active:u,activeNodeRect:f,containerNodeRect:g,draggingNodeRect:y.rect,over:b,overlayNodeRect:y.rect,scrollableAncestors:x,scrollableAncestorRects:E,transform:k,windowRect:C}),w=Rl(f),M=vS({config:r,draggableNodes:m,droppableContainers:p,measuringConfiguration:_}),T=w?y.setRef:void 0;return v.createElement(gS,null,v.createElement(fS,{animation:M},u&&N?v.createElement(bS,{key:N,id:u.id,ref:T,as:a,activatorEvent:d,adjustScale:e,className:l,transition:o,rect:w,style:{zIndex:c,...s},transform:S},n):null))});function Jr(t,e,n){const r=t.slice();return r.splice(n<0?r.length+n:n,0,r.splice(e,1)[0]),r}function CS(t,e){return t.reduce((n,r,s)=>{const o=e.get(r);return o&&(n[s]=o),n},Array(t.length))}function Oi(t){return t!==null&&t>=0}function SS(t,e){if(t===e)return!0;if(t.length!==e.length)return!1;for(let n=0;n{let{rects:e,activeIndex:n,overIndex:r,index:s}=t;const o=Jr(e,r,n),i=e[s],a=o[s];return!a||!i?null:{x:a.left-i.left,y:a.top-i.top,scaleX:a.width/i.width,scaleY:a.height/i.height}},Ai={scaleX:1,scaleY:1},tc=t=>{var e;let{activeIndex:n,activeNodeRect:r,index:s,rects:o,overIndex:i}=t;const a=(e=o[n])!=null?e:r;if(!a)return null;if(s===n){const c=o[i];return c?{x:0,y:nn&&s<=i?{x:0,y:-a.height-l,...Ai}:s=i?{x:0,y:a.height+l,...Ai}:{x:0,y:0,...Ai}};function ES(t,e,n){const r=t[e],s=t[e-1],o=t[e+1];return r?nr.map(k=>typeof k=="object"&&"id"in k?k.id:k),[r]),m=i!=null,p=i?g.indexOf(i.id):-1,y=c?g.indexOf(c.id):-1,b=v.useRef(g),_=!SS(g,b.current),x=y!==-1&&p===-1||_,E=_S(o);wn(()=>{_&&m&&d(g)},[_,g,m,d]),v.useEffect(()=>{b.current=g},[g]);const C=v.useMemo(()=>({activeIndex:p,containerId:u,disabled:E,disableTransforms:x,items:g,overIndex:y,useDragOverlay:f,sortedRects:CS(g,l),strategy:s}),[p,u,E.draggable,E.droppable,x,g,y,l,f,s]);return v.createElement(Hh.Provider,{value:C},e)}const Uh=t=>{let{id:e,items:n,activeIndex:r,overIndex:s}=t;return Jr(n,r,s).indexOf(e)},Bh=t=>{let{containerId:e,isSorting:n,wasDragging:r,index:s,items:o,newIndex:i,previousItems:a,previousContainerId:l,transition:c}=t;return!c||!r||a!==o&&s===i?!1:n?!0:i!==s&&e===l},MS={duration:200,easing:"ease"},jh="transform",NS=Pn.Transition.toString({property:jh,duration:0,easing:"linear"}),OS={roleDescription:"sortable"};function TS(t){let{disabled:e,index:n,node:r,rect:s}=t;const[o,i]=v.useState(null),a=v.useRef(n);return wn(()=>{if(!e&&n!==a.current&&r.current){const l=s.current;if(l){const c=Qr(r.current,{ignoreTransform:!0}),d={x:l.left-c.left,y:l.top-c.top,scaleX:l.width/c.width,scaleY:l.height/c.height};(d.x||d.y)&&i(d)}}n!==a.current&&(a.current=n)},[e,n,r,s]),v.useEffect(()=>{o&&i(null)},[o]),o}function Ii(t){let{animateLayoutChanges:e=Bh,attributes:n,disabled:r,data:s,getNewIndex:o=Uh,id:i,strategy:a,resizeObserverConfig:l,transition:c=MS}=t;const{items:d,containerId:u,activeIndex:f,disabled:g,disableTransforms:m,sortedRects:p,overIndex:y,useDragOverlay:b,strategy:_}=v.useContext(Hh),x=AS(r,g),E=d.indexOf(i),C=v.useMemo(()=>({sortable:{containerId:u,index:E,items:d},...s}),[u,s,E,d]),k=v.useMemo(()=>d.slice(d.indexOf(i)),[d,i]),{rect:N,node:S,isOver:w,setNodeRef:M}=uS({id:i,data:C,disabled:x.droppable,resizeObserverConfig:{updateMeasurementsFor:k,...l}}),{active:T,activatorEvent:P,activeNodeRect:L,attributes:$,setNodeRef:A,listeners:D,isDragging:O,over:z,setActivatorNodeRef:H,transform:j}=lS({id:i,data:C,attributes:{...OS,...n},disabled:x.draggable}),W=Zk(M,A),U=!!T,B=U&&!m&&Oi(f)&&Oi(y),G=!b&&O,V=G&&B?j:null,K=B?V??(a??_)({rects:p,activeNodeRect:L,activeIndex:f,overIndex:y,index:E}):null,Z=Oi(f)&&Oi(y)?o({id:i,items:d,activeIndex:f,overIndex:y}):E,ee=T?.id,ne=v.useRef({activeId:ee,items:d,newIndex:Z,containerId:u}),he=d!==ne.current.items,J=e({active:T,containerId:u,isDragging:O,isSorting:U,id:i,index:E,items:d,newIndex:ne.current.newIndex,previousItems:ne.current.items,previousContainerId:ne.current.containerId,transition:c,wasDragging:ne.current.activeId!=null}),le=TS({disabled:!J,index:E,node:S,rect:N});return v.useEffect(()=>{U&&ne.current.newIndex!==Z&&(ne.current.newIndex=Z),u!==ne.current.containerId&&(ne.current.containerId=u),d!==ne.current.items&&(ne.current.items=d)},[U,Z,u,d]),v.useEffect(()=>{if(ee===ne.current.activeId)return;if(ee!=null&&ne.current.activeId==null){ne.current.activeId=ee;return}const ae=setTimeout(()=>{ne.current.activeId=ee},50);return()=>clearTimeout(ae)},[ee]),{active:T,activeIndex:f,attributes:$,data:C,rect:N,index:E,newIndex:Z,items:d,isOver:w,isSorting:U,isDragging:O,listeners:D,node:S,overIndex:y,over:z,setNodeRef:W,setActivatorNodeRef:H,setDroppableNodeRef:M,setDraggableNodeRef:A,transform:le??K,transition:ue()};function ue(){if(le||he&&ne.current.newIndex===E)return NS;if(!(G&&!wi(P)||!c)&&(U||J))return Pn.Transition.toString({...c,property:jh})}}function AS(t,e){var n,r;return typeof t=="boolean"?{draggable:t,droppable:!1}:{draggable:(n=t?.draggable)!=null?n:e.draggable,droppable:(r=t?.droppable)!=null?r:e.droppable}}function Rs(t){if(!t)return!1;const e=t.data.current;return!!(e&&"sortable"in e&&typeof e.sortable=="object"&&"containerId"in e.sortable&&"items"in e.sortable&&"index"in e.sortable)}const DS=[Xe.Down,Xe.Right,Xe.Up,Xe.Left],Li=(t,e)=>{let{context:{active:n,collisionRect:r,droppableRects:s,droppableContainers:o,over:i,scrollableAncestors:a}}=e;if(DS.includes(t.code)){if(t.preventDefault(),!n||!r)return;const l=[];o.getEnabled().forEach(u=>{if(!u||u!=null&&u.disabled)return;const f=s.get(u.id);if(f)switch(t.code){case Xe.Down:r.topf.top&&l.push(u);break;case Xe.Left:r.left>f.left&&l.push(u);break;case Xe.Right:r.left1&&(d=c[1].id),d!=null){const u=o.get(n.id),f=o.get(d),g=f?s.get(f.id):null,m=f?.node.current;if(m&&g&&u&&f){const y=Si(m).some((k,N)=>a[N]!==k),b=Wh(u,f),_=IS(u,f),x=y||!b?{x:0,y:0}:{x:_?r.width-g.width:0,y:_?r.height-g.height:0},E={x:g.left,y:g.top};return x.x&&x.y?E:Gs(E,x)}}}};function Wh(t,e){return!Rs(t)||!Rs(e)?!1:t.data.current.sortable.containerId===e.data.current.sortable.containerId}function IS(t,e){return!Rs(t)||!Rs(e)||!Wh(t,e)?!1:t.data.current.sortable.indexwindow.G7Core,Gh=({item:t,isSelected:e=!1,isExpanded:n=!1,level:r=0,onClick:s,onToggle:o,onExpandToggle:i,className:a="",selectedClassName:l="bg-blue-50 dark:bg-blue-900/30 border-blue-500",defaultClassName:c="bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600",dragHandleClassName:d="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",iconContainerClassName:u="bg-gray-100 dark:bg-gray-700",contentClassName:f="",enableDrag:g=!0,toggleDisabled:m=!1})=>{const{attributes:p,listeners:y,setNodeRef:b,transform:_,transition:x,isDragging:E}=Ii({id:t.id}),C={transform:Pn.Transform.toString(_),transition:x,opacity:E?.5:1},k=r*24+12,S=`flex items-center gap-2 cursor-pointer transition-colors ${e?l:c}`,w=PS()?.style?.mergeClasses?.(S,a)??`${S} ${a}`.trim();return h.jsxs(I,{ref:b,style:C,className:w,onClick:s,children:[g&&h.jsx(I,{...p,...y,className:`flex-shrink-0 cursor-grab active:cursor-grabbing p-1 ${d}`,style:{paddingLeft:`${k}px`},children:h.jsx(re,{name:"grip-vertical",className:"w-4 h-4"})}),!g&&r>0&&h.jsx(I,{style:{paddingLeft:`${k}px`}}),g&&h.jsx(h.Fragment,{children:t.hasChildren?h.jsx(oe,{type:"button",onClick:M=>{M.stopPropagation(),i?.()},className:`flex-shrink-0 p-1 ${d}`,children:h.jsx(re,{name:n?"chevron-down":"chevron-right",className:"w-4 h-4"})}):h.jsx(I,{className:"flex-shrink-0 w-6"})}),h.jsx(I,{className:`flex-shrink-0 w-8 h-8 flex items-center justify-center rounded ${u}`,children:h.jsx(re,{name:t.icon||"file",className:"w-4 h-4 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:`flex-1 min-w-0 ${f}`,children:[h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(Q,{className:"text-sm font-medium text-gray-900 dark:text-white truncate",children:t.name}),t.isModuleMenu&&h.jsx(re,{name:"cube",className:"w-2 h-2 text-gray-300 dark:text-gray-500 ml-2",title:t.moduleName})]}),h.jsx(Q,{className:"text-xs text-gray-500 dark:text-gray-400 truncate block",children:t.url})]}),h.jsx(I,{className:"flex-shrink-0",onClick:M=>M.stopPropagation(),children:h.jsx(Ua,{checked:t.isActive,onChange:()=>o?.(new MouseEvent("click")),size:"sm",disabled:m})})]})},$S=()=>window.G7Core;function Yh(t,e,n=0,r=null){const s=[];for(const o of t)s.push({item:o,depth:n,parentId:r}),o.children?.length>0&&e.has(o.id)&&s.push(...Yh(o.children,e,n+1,o.id));return s}function nc(t,e){for(const n of t){if(n.id===e)return n;if(n.children){const r=nc(n.children,e);if(r)return r}}return null}function Kh(t){const e=[];if(t.children)for(const n of t.children)e.push(n.id),e.push(...Kh(n));return e}function Xh(t,e){return t.filter(n=>n.id!==e).map(n=>n.children?.length>0?{...n,children:Xh(n.children,e)}:n)}function qh(t,e,n,r){if(n===null){const s=[...t];return s.splice(r,0,e),s}return t.map(s=>{if(s.id===n){const o=[...s.children||[]];return o.splice(r,0,e),{...s,children:o}}return s.children?.length>0?{...s,children:qh(s.children,e,n,r)}:s})}function Zh(t,e,n,r){if(e===null){const s=t.findIndex(i=>i.id===n),o=t.findIndex(i=>i.id===r);return s===-1||o===-1?t:Jr(t,s,o)}return t.map(s=>{if(s.id===e&&s.children){const o=s.children.findIndex(a=>a.id===n),i=s.children.findIndex(a=>a.id===r);return o===-1||i===-1?s:{...s,children:Jr(s.children,o,i)}}return s.children?.length>0?{...s,children:Zh(s.children,e,n,r)}:s})}function Qh(t){const e=t.map((s,o)=>({id:s.id,order:o+1})),n={},r=s=>{s.forEach(o=>{o.children?.length>0&&(n[o.id]=o.children.map((i,a)=>({id:i.id,order:a+1})),r(o.children))})};return r(t),{parent_menus:e,child_menus:n}}const FS=({items:t=[],selectedId:e,onSelect:n,onOrderChange:r,onToggleStatus:s,title:o,className:i="",listClassName:a="",childrenContainerClassName:l="bg-gray-50 dark:bg-gray-900/50",itemClassName:c="py-2.5 px-3 mx-4 my-2 rounded-lg border",itemSelectedClassName:d,itemDefaultClassName:u,titleClassName:f="px-4 py-3 border-b border-gray-200 dark:border-gray-700",enableDrag:g=!0,toggleDisabled:m=!1,id:p,editorAttrs:y})=>{const[b,_]=v.useState(new Set),[x,E]=v.useState(null),[C,k]=v.useState(t),N=v.useRef(!1),S=v.useRef(""),w=v.useRef(""),M=v.useCallback(B=>JSON.stringify(B),[]),T=v.useCallback(B=>B.map(G=>{const V=G.children?.map(X=>X.id).join(",")||"";return`${G.id}:${V}`}).join("|"),[]);v.useEffect(()=>{const B=T(t),G=M(t);if(N.current){const V=T(C);(B===V||B!==S.current)&&(N.current=!1,S.current=B,w.current=G,(B!==V||G!==M(C))&&k(t));return}G!==w.current&&(S.current=B,w.current=G,k(t))},[t,T,M,C]);const P=jl(Zr(Xs,{activationConstraint:{distance:8}}),Zr(Ks,{coordinateGetter:Li})),L=v.useCallback(B=>{if(typeof B=="string")return B;const V=$S()?.locale?.current?.()||"ko";return B[V]||B.en||Object.values(B)[0]||""},[]),$=v.useCallback(B=>{_(G=>{const V=new Set(G);return V.has(B)?V.delete(B):V.add(B),V})},[]),A=v.useCallback(B=>{n?.(B)},[n]),D=v.useCallback((B,G,V)=>{V.stopPropagation(),s?.(B,G)},[s]),O=B=>{E(B.active.id)},z=B=>{const{active:G,over:V}=B;if(E(null),!V||G.id===V.id)return;const X=j.find(Z=>Z.item.id===Number(G.id)),K=j.find(Z=>Z.item.id===Number(V.id));if(!(!X||!K))if(X.parentId===K.parentId){const Z=Zh(C,X.parentId,Number(G.id),Number(V.id));k(Z),N.current=!0,r?.(Qh(Z))}else{const Z=X.item;if(Kh(Z).includes(Number(V.id)))return;let ne=Xh(C,Z.id);const he=j.findIndex(ye=>ye.item.id===Number(G.id)),J=j.findIndex(ye=>ye.item.id===Number(V.id)),le=he>J,ue=K.parentId;let ae;if(ue===null)ae=ne.findIndex(ye=>ye.id===Number(V.id)),ae===-1?ae=ne.length:le||(ae+=1);else{const we=nc(ne,ue)?.children||[];ae=we.findIndex(He=>He.id===Number(V.id)),ae===-1?ae=we.length:le||(ae+=1)}const ve={...Z,parent_id:ue,children:Z.children||[]};ne=qh(ne,ve,ue,ae),k(ne),N.current=!0;const fe=Qh(ne);fe.moved_items=[{id:Z.id,new_parent_id:ue}],r?.(fe)}},H=B=>({id:B.id,name:L(B.name),slug:B.slug,url:B.url,icon:B.icon,isActive:B.is_active,isModuleMenu:!!B.module_id,moduleName:B.module?L(B.module.name):void 0,hasChildren:B.children&&B.children.length>0}),j=v.useMemo(()=>Yh(C,b),[C,b]),W=v.useMemo(()=>j.map(B=>B.item.id),[j]),U=x?nc(C,Number(x)):null;return h.jsxs(I,{className:i,id:p,...y,children:[o&&h.jsx(I,{className:f,children:h.jsx(Ir,{className:"text-sm font-medium text-gray-900 dark:text-white",children:o})}),h.jsxs(ec,{sensors:P,collisionDetection:Wl,onDragStart:O,onDragEnd:z,children:[h.jsx(Di,{items:W,strategy:tc,children:h.jsx(I,{className:a,children:j.map(B=>h.jsx(I,{className:B.depth>0?l:"",children:h.jsx(Gh,{item:H(B.item),isSelected:e===B.item.id,isExpanded:b.has(B.item.id),level:B.depth,onClick:()=>A(B.item),onToggle:G=>D(B.item.id,B.item.is_active,G),onExpandToggle:()=>$(B.item.id),className:c,selectedClassName:d,defaultClassName:u,enableDrag:g,toggleDisabled:m})},B.item.id))})}),h.jsx(kS,{children:U?h.jsx(I,{className:"bg-white dark:bg-gray-800 shadow-lg rounded-lg border border-blue-500 p-3",children:h.jsx(I,{className:"flex items-center gap-2",children:h.jsx(I,{className:"text-sm font-medium text-gray-900 dark:text-white",children:L(U.name)})})}):null})]})]})},zS=[{value:"LayoutDashboard",label:"LayoutDashboard",faIcon:"tachometer-alt"},{value:"Settings",label:"Settings",faIcon:"cog"},{value:"Menu",label:"Menu",faIcon:"bars"},{value:"Users",label:"Users",faIcon:"users"},{value:"Package",label:"Package",faIcon:"cube"},{value:"Puzzle",label:"Puzzle",faIcon:"puzzle-piece"},{value:"FileText",label:"FileText",faIcon:"file-alt"},{value:"ShoppingCart",label:"ShoppingCart",faIcon:"shopping-cart"},{value:"MessageSquare",label:"MessageSquare",faIcon:"comment"},{value:"Home",label:"Home",faIcon:"home"},{value:"User",label:"User",faIcon:"user"},{value:"Search",label:"Search",faIcon:"search"},{value:"Bell",label:"Bell",faIcon:"bell"},{value:"Mail",label:"Mail",faIcon:"envelope"},{value:"Calendar",label:"Calendar",faIcon:"calendar"},{value:"Chart",label:"Chart",faIcon:"chart-bar"},{value:"Database",label:"Database",faIcon:"database"},{value:"Lock",label:"Lock",faIcon:"lock"},{value:"Globe",label:"Globe",faIcon:"globe"},{value:"Image",label:"Image",faIcon:"image"}],VS=({value:t,onChange:e,options:n=zS,placeholder:r,searchPlaceholder:s,noResultsText:o,className:i="",disabled:a=!1,name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),[g,m]=v.useState(""),p=v.useRef(null),y=n.find(x=>{if(!t)return!1;if(t.includes("fa-")){const E=t.replace(/^(fas|far|fab|fal|fad)\s+fa-/,"");return x.faIcon===E||x.faIcon===t}return x.value===t||x.faIcon===t}),b=n.filter(x=>x.label.toLowerCase().includes(g.toLowerCase()));v.useEffect(()=>{const x=E=>{p.current&&!p.current.contains(E.target)&&(f(!1),m(""))};if(u)return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[u]);const _=x=>{const E=`fas fa-${x.faIcon}`;e?.(E),f(!1),m("")};return h.jsxs(I,{ref:p,className:`relative ${i}`,id:c,...d,children:[h.jsxs(oe,{type:"button",onClick:()=>!a&&f(!u),disabled:a,className:` + `},MC={onDragStart(e){let{active:t}=e;return"Picked up draggable item "+t.id+"."},onDragOver(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was moved over droppable area "+n.id+".":"Draggable item "+t.id+" is no longer over a droppable area."},onDragEnd(e){let{active:t,over:n}=e;return n?"Draggable item "+t.id+" was dropped over droppable area "+n.id:"Draggable item "+t.id+" was dropped."},onDragCancel(e){let{active:t}=e;return"Dragging was cancelled. Draggable item "+t.id+" was dropped."}};function NC(e){let{announcements:t=MC,container:n,hiddenTextDescribedById:r,screenReaderInstructions:s=EC}=e;const{announce:o,announcement:i}=CC(),a=to("DndLiveRegion"),[l,c]=v.useState(!1);if(v.useEffect(()=>{c(!0)},[]),SC(v.useMemo(()=>({onDragStart(u){let{active:f}=u;o(t.onDragStart({active:f}))},onDragMove(u){let{active:f,over:g}=u;t.onDragMove&&o(t.onDragMove({active:f,over:g}))},onDragOver(u){let{active:f,over:g}=u;o(t.onDragOver({active:f,over:g}))},onDragEnd(u){let{active:f,over:g}=u;o(t.onDragEnd({active:f,over:g}))},onDragCancel(u){let{active:f,over:g}=u;o(t.onDragCancel({active:f,over:g}))}}),[o,t])),!l)return null;const d=v.createElement(v.Fragment,null,v.createElement(wC,{id:r,value:s.draggable}),v.createElement(kC,{id:a,announcement:i}));return n?Ln.createPortal(d,n):d}var Et;(function(e){e.DragStart="dragStart",e.DragMove="dragMove",e.DragEnd="dragEnd",e.DragCancel="dragCancel",e.DragOver="dragOver",e.RegisterDroppable="registerDroppable",e.SetDroppableDisabled="setDroppableDisabled",e.UnregisterDroppable="unregisterDroppable"})(Et||(Et={}));function Oi(){}function ds(e,t){return v.useMemo(()=>({sensor:e,options:t??{}}),[e,t])}function Zl(){for(var e=arguments.length,t=new Array(e),n=0;n[...t].filter(r=>r!=null),[...t])}const Mn=Object.freeze({x:0,y:0});function vh(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function TC(e,t){const n=Ti(e);if(!n)return"0 0";const r={x:(n.x-t.left)/t.width*100,y:(n.y-t.top)/t.height*100};return r.x+"% "+r.y+"%"}function xh(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return n-r}function OC(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return r-n}function wh(e){let{left:t,top:n,height:r,width:s}=e;return[{x:t,y:n},{x:t+s,y:n},{x:t,y:n+r},{x:t+s,y:n+r}]}function kh(e,t){if(!e||e.length===0)return null;const[n]=e;return n[t]}function Ch(e,t,n){return t===void 0&&(t=e.left),n===void 0&&(n=e.top),{x:t+e.width*.5,y:n+e.height*.5}}const Ql=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=Ch(t,t.left,t.top),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=vh(Ch(l),s);o.push({id:a,data:{droppableContainer:i,value:c}})}}return o.sort(xh)},AC=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=wh(t),o=[];for(const i of r){const{id:a}=i,l=n.get(a);if(l){const c=wh(l),d=s.reduce((f,g,m)=>f+vh(c[m],g),0),u=Number((d/4).toFixed(4));o.push({id:a,data:{droppableContainer:i,value:u}})}}return o.sort(xh)};function DC(e,t){const n=Math.max(t.top,e.top),r=Math.max(t.left,e.left),s=Math.min(t.left+t.width,e.left+e.width),o=Math.min(t.top+t.height,e.top+e.height),i=s-r,a=o-n;if(r{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const s=[];for(const o of r){const{id:i}=o,a=n.get(i);if(a){const l=DC(a,t);l>0&&s.push({id:i,data:{droppableContainer:o,value:l}})}}return s.sort(OC)};function LC(e,t,n){return{...e,scaleX:t&&n?t.width/n.width:1,scaleY:t&&n?t.height/n.height:1}}function Sh(e,t){return e&&t?{x:e.left-t.left,y:e.top-t.top}:Mn}function PC(e){return function(n){for(var r=arguments.length,s=new Array(r>1?r-1:0),o=1;o({...i,top:i.top+e*a.y,bottom:i.bottom+e*a.y,left:i.left+e*a.x,right:i.right+e*a.x}),{...n})}}const $C=PC(1);function _h(e){if(e.startsWith("matrix3d(")){const t=e.slice(9,-1).split(/, /);return{x:+t[12],y:+t[13],scaleX:+t[0],scaleY:+t[5]}}else if(e.startsWith("matrix(")){const t=e.slice(7,-1).split(/, /);return{x:+t[4],y:+t[5],scaleX:+t[0],scaleY:+t[3]}}return null}function FC(e,t,n){const r=_h(t);if(!r)return e;const{scaleX:s,scaleY:o,x:i,y:a}=r,l=e.left-i-(1-s)*parseFloat(n),c=e.top-a-(1-o)*parseFloat(n.slice(n.indexOf(" ")+1)),d=s?e.width/s:e.width,u=o?e.height/o:e.height;return{width:d,height:u,top:c,right:l+d,bottom:c+u,left:l}}const zC={ignoreTransform:!1};function us(e,t){t===void 0&&(t=zC);let n=e.getBoundingClientRect();if(t.ignoreTransform){const{transform:c,transformOrigin:d}=Kt(e).getComputedStyle(e);c&&(n=FC(n,c,d))}const{top:r,left:s,width:o,height:i,bottom:a,right:l}=n;return{top:r,left:s,width:o,height:i,bottom:a,right:l}}function Eh(e){return us(e,{ignoreTransform:!0})}function VC(e){const t=e.innerWidth,n=e.innerHeight;return{top:0,left:0,right:t,bottom:n,width:t,height:n}}function HC(e,t){return t===void 0&&(t=Kt(e).getComputedStyle(e)),t.position==="fixed"}function UC(e,t){t===void 0&&(t=Kt(e).getComputedStyle(e));const n=/(auto|scroll|overlay)/;return["overflow","overflowX","overflowY"].some(s=>{const o=t[s];return typeof o=="string"?n.test(o):!1})}function Ai(e,t){const n=[];function r(s){if(t!=null&&n.length>=t||!s)return n;if(Kl(s)&&s.scrollingElement!=null&&!n.includes(s.scrollingElement))return n.push(s.scrollingElement),n;if(!Js(s)||ph(s)||n.includes(s))return n;const o=Kt(e).getComputedStyle(s);return s!==e&&UC(s,o)&&n.push(s),HC(s,o)?n:r(s.parentNode)}return e?r(e):n}function Mh(e){const[t]=Ai(e,1);return t??null}function Jl(e){return!Si||!e?null:as(e)?e:Xl(e)?Kl(e)||e===ls(e).scrollingElement?window:Js(e)?e:null:null}function Nh(e){return as(e)?e.scrollX:e.scrollLeft}function Th(e){return as(e)?e.scrollY:e.scrollTop}function Rl(e){return{x:Nh(e),y:Th(e)}}var Ot;(function(e){e[e.Forward=1]="Forward",e[e.Backward=-1]="Backward"})(Ot||(Ot={}));function Oh(e){return!Si||!e?!1:e===document.scrollingElement}function Ah(e){const t={x:0,y:0},n=Oh(e)?{height:window.innerHeight,width:window.innerWidth}:{height:e.clientHeight,width:e.clientWidth},r={x:e.scrollWidth-n.width,y:e.scrollHeight-n.height},s=e.scrollTop<=t.y,o=e.scrollLeft<=t.x,i=e.scrollTop>=r.y,a=e.scrollLeft>=r.x;return{isTop:s,isLeft:o,isBottom:i,isRight:a,maxScroll:r,minScroll:t}}const BC={x:.2,y:.2};function jC(e,t,n,r,s){let{top:o,left:i,right:a,bottom:l}=n;r===void 0&&(r=10),s===void 0&&(s=BC);const{isTop:c,isBottom:d,isLeft:u,isRight:f}=Ah(e),g={x:0,y:0},m={x:0,y:0},p={height:t.height*s.y,width:t.width*s.x};return!c&&o<=t.top+p.height?(g.y=Ot.Backward,m.y=r*Math.abs((t.top+p.height-o)/p.height)):!d&&l>=t.bottom-p.height&&(g.y=Ot.Forward,m.y=r*Math.abs((t.bottom-p.height-l)/p.height)),!f&&a>=t.right-p.width?(g.x=Ot.Forward,m.x=r*Math.abs((t.right-p.width-a)/p.width)):!u&&i<=t.left+p.width&&(g.x=Ot.Backward,m.x=r*Math.abs((t.left+p.width-i)/p.width)),{direction:g,speed:m}}function WC(e){if(e===document.scrollingElement){const{innerWidth:o,innerHeight:i}=window;return{top:0,left:0,right:o,bottom:i,width:o,height:i}}const{top:t,left:n,right:r,bottom:s}=e.getBoundingClientRect();return{top:t,left:n,right:r,bottom:s,width:e.clientWidth,height:e.clientHeight}}function Dh(e){return e.reduce((t,n)=>cs(t,Rl(n)),Mn)}function GC(e){return e.reduce((t,n)=>t+Nh(n),0)}function YC(e){return e.reduce((t,n)=>t+Th(n),0)}function Ih(e,t){if(t===void 0&&(t=us),!e)return;const{top:n,left:r,bottom:s,right:o}=t(e);Mh(e)&&(s<=0||o<=0||n>=window.innerHeight||r>=window.innerWidth)&&e.scrollIntoView({block:"center",inline:"center"})}const XC=[["x",["left","right"],GC],["y",["top","bottom"],YC]];class ec{constructor(t,n){this.rect=void 0,this.width=void 0,this.height=void 0,this.top=void 0,this.bottom=void 0,this.right=void 0,this.left=void 0;const r=Ai(n),s=Dh(r);this.rect={...t},this.width=t.width,this.height=t.height;for(const[o,i,a]of XC)for(const l of i)Object.defineProperty(this,l,{get:()=>{const c=a(r),d=s[o]-c;return this.rect[l]+d},enumerable:!0});Object.defineProperty(this,"rect",{enumerable:!1})}}class ro{constructor(t){this.target=void 0,this.listeners=[],this.removeAll=()=>{this.listeners.forEach(n=>{var r;return(r=this.target)==null?void 0:r.removeEventListener(...n)})},this.target=t}add(t,n,r){var s;(s=this.target)==null||s.addEventListener(t,n,r),this.listeners.push([t,n,r])}}function KC(e){const{EventTarget:t}=Kt(e);return e instanceof t?e:ls(e)}function tc(e,t){const n=Math.abs(e.x),r=Math.abs(e.y);return typeof t=="number"?Math.sqrt(n**2+r**2)>t:"x"in t&&"y"in t?n>t.x&&r>t.y:"x"in t?n>t.x:"y"in t?r>t.y:!1}var hn;(function(e){e.Click="click",e.DragStart="dragstart",e.Keydown="keydown",e.ContextMenu="contextmenu",e.Resize="resize",e.SelectionChange="selectionchange",e.VisibilityChange="visibilitychange"})(hn||(hn={}));function Lh(e){e.preventDefault()}function qC(e){e.stopPropagation()}var Xe;(function(e){e.Space="Space",e.Down="ArrowDown",e.Right="ArrowRight",e.Left="ArrowLeft",e.Up="ArrowUp",e.Esc="Escape",e.Enter="Enter",e.Tab="Tab"})(Xe||(Xe={}));const Ph={start:[Xe.Space,Xe.Enter],cancel:[Xe.Esc],end:[Xe.Space,Xe.Enter,Xe.Tab]},ZC=(e,t)=>{let{currentCoordinates:n}=t;switch(e.code){case Xe.Right:return{...n,x:n.x+25};case Xe.Left:return{...n,x:n.x-25};case Xe.Down:return{...n,y:n.y+25};case Xe.Up:return{...n,y:n.y-25}}};class so{constructor(t){this.props=void 0,this.autoScrollEnabled=!1,this.referenceCoordinates=void 0,this.listeners=void 0,this.windowListeners=void 0,this.props=t;const{event:{target:n}}=t;this.props=t,this.listeners=new ro(ls(n)),this.windowListeners=new ro(Kt(n)),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleCancel=this.handleCancel.bind(this),this.attach()}attach(){this.handleStart(),this.windowListeners.add(hn.Resize,this.handleCancel),this.windowListeners.add(hn.VisibilityChange,this.handleCancel),setTimeout(()=>this.listeners.add(hn.Keydown,this.handleKeyDown))}handleStart(){const{activeNode:t,onStart:n}=this.props,r=t.node.current;r&&Ih(r),n(Mn)}handleKeyDown(t){if(Ni(t)){const{active:n,context:r,options:s}=this.props,{keyboardCodes:o=Ph,coordinateGetter:i=ZC,scrollBehavior:a="smooth"}=s,{code:l}=t;if(o.end.includes(l)){this.handleEnd(t);return}if(o.cancel.includes(l)){this.handleCancel(t);return}const{collisionRect:c}=r.current,d=c?{x:c.left,y:c.top}:Mn;this.referenceCoordinates||(this.referenceCoordinates=d);const u=i(t,{active:n,context:r.current,currentCoordinates:d});if(u){const f=no(u,d),g={x:0,y:0},{scrollableAncestors:m}=r.current;for(const p of m){const y=t.code,{isTop:b,isRight:_,isLeft:x,isBottom:M,maxScroll:S,minScroll:k}=Ah(p),N=WC(p),C={x:Math.min(y===Xe.Right?N.right-N.width/2:N.right,Math.max(y===Xe.Right?N.left:N.left+N.width/2,u.x)),y:Math.min(y===Xe.Down?N.bottom-N.height/2:N.bottom,Math.max(y===Xe.Down?N.top:N.top+N.height/2,u.y))},w=y===Xe.Right&&!_||y===Xe.Left&&!x,E=y===Xe.Down&&!M||y===Xe.Up&&!b;if(w&&C.x!==u.x){const A=p.scrollLeft+f.x,P=y===Xe.Right&&A<=S.x||y===Xe.Left&&A>=k.x;if(P&&!f.y){p.scrollTo({left:A,behavior:a});return}P?g.x=p.scrollLeft-A:g.x=y===Xe.Right?p.scrollLeft-S.x:p.scrollLeft-k.x,g.x&&p.scrollBy({left:-g.x,behavior:a});break}else if(E&&C.y!==u.y){const A=p.scrollTop+f.y,P=y===Xe.Down&&A<=S.y||y===Xe.Up&&A>=k.y;if(P&&!f.x){p.scrollTo({top:A,behavior:a});return}P?g.y=p.scrollTop-A:g.y=y===Xe.Down?p.scrollTop-S.y:p.scrollTop-k.y,g.y&&p.scrollBy({top:-g.y,behavior:a});break}}this.handleMove(t,cs(no(u,this.referenceCoordinates),g))}}}handleMove(t,n){const{onMove:r}=this.props;t.preventDefault(),r(n)}handleEnd(t){const{onEnd:n}=this.props;t.preventDefault(),this.detach(),n()}handleCancel(t){const{onCancel:n}=this.props;t.preventDefault(),this.detach(),n()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll()}}so.activators=[{eventName:"onKeyDown",handler:(e,t,n)=>{let{keyboardCodes:r=Ph,onActivation:s}=t,{active:o}=n;const{code:i}=e.nativeEvent;if(r.start.includes(i)){const a=o.activatorNode.current;return a&&e.target!==a?!1:(e.preventDefault(),s?.({event:e.nativeEvent}),!0)}return!1}}];function $h(e){return!!(e&&"distance"in e)}function Fh(e){return!!(e&&"delay"in e)}class nc{constructor(t,n,r){var s;r===void 0&&(r=KC(t.event.target)),this.props=void 0,this.events=void 0,this.autoScrollEnabled=!0,this.document=void 0,this.activated=!1,this.initialCoordinates=void 0,this.timeoutId=null,this.listeners=void 0,this.documentListeners=void 0,this.windowListeners=void 0,this.props=t,this.events=n;const{event:o}=t,{target:i}=o;this.props=t,this.events=n,this.document=ls(i),this.documentListeners=new ro(this.document),this.listeners=new ro(r),this.windowListeners=new ro(Kt(i)),this.initialCoordinates=(s=Ti(o))!=null?s:Mn,this.handleStart=this.handleStart.bind(this),this.handleMove=this.handleMove.bind(this),this.handleEnd=this.handleEnd.bind(this),this.handleCancel=this.handleCancel.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.removeTextSelection=this.removeTextSelection.bind(this),this.attach()}attach(){const{events:t,props:{options:{activationConstraint:n,bypassActivationConstraint:r}}}=this;if(this.listeners.add(t.move.name,this.handleMove,{passive:!1}),this.listeners.add(t.end.name,this.handleEnd),t.cancel&&this.listeners.add(t.cancel.name,this.handleCancel),this.windowListeners.add(hn.Resize,this.handleCancel),this.windowListeners.add(hn.DragStart,Lh),this.windowListeners.add(hn.VisibilityChange,this.handleCancel),this.windowListeners.add(hn.ContextMenu,Lh),this.documentListeners.add(hn.Keydown,this.handleKeydown),n){if(r!=null&&r({event:this.props.event,activeNode:this.props.activeNode,options:this.props.options}))return this.handleStart();if(Fh(n)){this.timeoutId=setTimeout(this.handleStart,n.delay),this.handlePending(n);return}if($h(n)){this.handlePending(n);return}}this.handleStart()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll(),setTimeout(this.documentListeners.removeAll,50),this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}handlePending(t,n){const{active:r,onPending:s}=this.props;s(r,t,this.initialCoordinates,n)}handleStart(){const{initialCoordinates:t}=this,{onStart:n}=this.props;t&&(this.activated=!0,this.documentListeners.add(hn.Click,qC,{capture:!0}),this.removeTextSelection(),this.documentListeners.add(hn.SelectionChange,this.removeTextSelection),n(t))}handleMove(t){var n;const{activated:r,initialCoordinates:s,props:o}=this,{onMove:i,options:{activationConstraint:a}}=o;if(!s)return;const l=(n=Ti(t))!=null?n:Mn,c=no(s,l);if(!r&&a){if($h(a)){if(a.tolerance!=null&&tc(c,a.tolerance))return this.handleCancel();if(tc(c,a.distance))return this.handleStart()}if(Fh(a)&&tc(c,a.tolerance))return this.handleCancel();this.handlePending(a,c);return}t.cancelable&&t.preventDefault(),i(l)}handleEnd(){const{onAbort:t,onEnd:n}=this.props;this.detach(),this.activated||t(this.props.active),n()}handleCancel(){const{onAbort:t,onCancel:n}=this.props;this.detach(),this.activated||t(this.props.active),n()}handleKeydown(t){t.code===Xe.Esc&&this.handleCancel()}removeTextSelection(){var t;(t=this.document.getSelection())==null||t.removeAllRanges()}}const QC={cancel:{name:"pointercancel"},move:{name:"pointermove"},end:{name:"pointerup"}};class oo extends nc{constructor(t){const{event:n}=t,r=ls(n.target);super(t,QC,r)}}oo.activators=[{eventName:"onPointerDown",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;return!n.isPrimary||n.button!==0?!1:(r?.({event:n}),!0)}}];const JC={move:{name:"mousemove"},end:{name:"mouseup"}};var rc;(function(e){e[e.RightClick=2]="RightClick"})(rc||(rc={}));class RC extends nc{constructor(t){super(t,JC,ls(t.event.target))}}RC.activators=[{eventName:"onMouseDown",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;return n.button===rc.RightClick?!1:(r?.({event:n}),!0)}}];const sc={cancel:{name:"touchcancel"},move:{name:"touchmove"},end:{name:"touchend"}};class eS extends nc{constructor(t){super(t,sc)}static setup(){return window.addEventListener(sc.move.name,t,{capture:!1,passive:!1}),function(){window.removeEventListener(sc.move.name,t)};function t(){}}}eS.activators=[{eventName:"onTouchStart",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;const{touches:s}=n;return s.length>1?!1:(r?.({event:n}),!0)}}];var io;(function(e){e[e.Pointer=0]="Pointer",e[e.DraggableRect=1]="DraggableRect"})(io||(io={}));var Di;(function(e){e[e.TreeOrder=0]="TreeOrder",e[e.ReversedTreeOrder=1]="ReversedTreeOrder"})(Di||(Di={}));function tS(e){let{acceleration:t,activator:n=io.Pointer,canScroll:r,draggingRect:s,enabled:o,interval:i=5,order:a=Di.TreeOrder,pointerCoordinates:l,scrollableAncestors:c,scrollableAncestorRects:d,delta:u,threshold:f}=e;const g=rS({delta:u,disabled:!o}),[m,p]=mC(),y=v.useRef({x:0,y:0}),b=v.useRef({x:0,y:0}),_=v.useMemo(()=>{switch(n){case io.Pointer:return l?{top:l.y,bottom:l.y,left:l.x,right:l.x}:null;case io.DraggableRect:return s}},[n,s,l]),x=v.useRef(null),M=v.useCallback(()=>{const k=x.current;if(!k)return;const N=y.current.x*b.current.x,C=y.current.y*b.current.y;k.scrollBy(N,C)},[]),S=v.useMemo(()=>a===Di.TreeOrder?[...c].reverse():c,[a,c]);v.useEffect(()=>{if(!o||!c.length||!_){p();return}for(const k of S){if(r?.(k)===!1)continue;const N=c.indexOf(k),C=d[N];if(!C)continue;const{direction:w,speed:E}=jC(k,C,_,t,f);for(const A of["x","y"])g[A][w[A]]||(E[A]=0,w[A]=0);if(E.x>0||E.y>0){p(),x.current=k,m(M,i),y.current=E,b.current=w;return}}y.current={x:0,y:0},b.current={x:0,y:0},p()},[t,M,r,p,o,i,JSON.stringify(_),JSON.stringify(g),m,c,S,d,JSON.stringify(f)])}const nS={x:{[Ot.Backward]:!1,[Ot.Forward]:!1},y:{[Ot.Backward]:!1,[Ot.Forward]:!1}};function rS(e){let{delta:t,disabled:n}=e;const r=Mi(t);return eo(s=>{if(n||!r||!s)return nS;const o={x:Math.sign(t.x-r.x),y:Math.sign(t.y-r.y)};return{x:{[Ot.Backward]:s.x[Ot.Backward]||o.x===-1,[Ot.Forward]:s.x[Ot.Forward]||o.x===1},y:{[Ot.Backward]:s.y[Ot.Backward]||o.y===-1,[Ot.Forward]:s.y[Ot.Forward]||o.y===1}}},[n,t,r])}function sS(e,t){const n=t!=null?e.get(t):void 0,r=n?n.node.current:null;return eo(s=>{var o;return t==null?null:(o=r??s)!=null?o:null},[r,t])}function oS(e,t){return v.useMemo(()=>e.reduce((n,r)=>{const{sensor:s}=r,o=s.activators.map(i=>({eventName:i.eventName,handler:t(i.handler,r)}));return[...n,...o]},[]),[e,t])}var ao;(function(e){e[e.Always=0]="Always",e[e.BeforeDragging=1]="BeforeDragging",e[e.WhileDragging=2]="WhileDragging"})(ao||(ao={}));var oc;(function(e){e.Optimized="optimized"})(oc||(oc={}));const zh=new Map;function iS(e,t){let{dragging:n,dependencies:r,config:s}=t;const[o,i]=v.useState(null),{frequency:a,measure:l,strategy:c}=s,d=v.useRef(e),u=y(),f=Rs(u),g=v.useCallback(function(b){b===void 0&&(b=[]),!f.current&&i(_=>_===null?b:_.concat(b.filter(x=>!_.includes(x))))},[f]),m=v.useRef(null),p=eo(b=>{if(u&&!n)return zh;if(!b||b===zh||d.current!==e||o!=null){const _=new Map;for(let x of e){if(!x)continue;if(o&&o.length>0&&!o.includes(x.id)&&x.rect.current){_.set(x.id,x.rect.current);continue}const M=x.node.current,S=M?new ec(l(M),M):null;x.rect.current=S,S&&_.set(x.id,S)}return _}return b},[e,o,n,u,l]);return v.useEffect(()=>{d.current=e},[e]),v.useEffect(()=>{u||g()},[n,u]),v.useEffect(()=>{o&&o.length>0&&i(null)},[JSON.stringify(o)]),v.useEffect(()=>{u||typeof a!="number"||m.current!==null||(m.current=setTimeout(()=>{g(),m.current=null},a))},[a,u,g,...r]),{droppableRects:p,measureDroppableContainers:g,measuringScheduled:o!=null};function y(){switch(c){case ao.Always:return!1;case ao.BeforeDragging:return n;default:return!n}}}function ic(e,t){return eo(n=>e?n||(typeof t=="function"?t(e):e):null,[t,e])}function aS(e,t){return ic(e,t)}function lS(e){let{callback:t,disabled:n}=e;const r=_i(t),s=v.useMemo(()=>{if(n||typeof window>"u"||typeof window.MutationObserver>"u")return;const{MutationObserver:o}=window;return new o(r)},[r,n]);return v.useEffect(()=>()=>s?.disconnect(),[s]),s}function Ii(e){let{callback:t,disabled:n}=e;const r=_i(t),s=v.useMemo(()=>{if(n||typeof window>"u"||typeof window.ResizeObserver>"u")return;const{ResizeObserver:o}=window;return new o(r)},[n]);return v.useEffect(()=>()=>s?.disconnect(),[s]),s}function cS(e){return new ec(us(e),e)}function Vh(e,t,n){t===void 0&&(t=cS);const[r,s]=v.useState(null);function o(){s(l=>{if(!e)return null;if(e.isConnected===!1){var c;return(c=l??n)!=null?c:null}const d=t(e);return JSON.stringify(l)===JSON.stringify(d)?l:d})}const i=lS({callback(l){if(e)for(const c of l){const{type:d,target:u}=c;if(d==="childList"&&u instanceof HTMLElement&&u.contains(e)){o();break}}}}),a=Ii({callback:o});return En(()=>{o(),e?(a?.observe(e),i?.observe(document.body,{childList:!0,subtree:!0})):(a?.disconnect(),i?.disconnect())},[e]),r}function dS(e){const t=ic(e);return Sh(e,t)}const Hh=[];function uS(e){const t=v.useRef(e),n=eo(r=>e?r&&r!==Hh&&e&&t.current&&e.parentNode===t.current.parentNode?r:Ai(e):Hh,[e]);return v.useEffect(()=>{t.current=e},[e]),n}function fS(e){const[t,n]=v.useState(null),r=v.useRef(e),s=v.useCallback(o=>{const i=Jl(o.target);i&&n(a=>a?(a.set(i,Rl(i)),new Map(a)):null)},[]);return v.useEffect(()=>{const o=r.current;if(e!==o){i(o);const a=e.map(l=>{const c=Jl(l);return c?(c.addEventListener("scroll",s,{passive:!0}),[c,Rl(c)]):null}).filter(l=>l!=null);n(a.length?new Map(a):null),r.current=e}return()=>{i(e),i(o)};function i(a){a.forEach(l=>{const c=Jl(l);c?.removeEventListener("scroll",s)})}},[s,e]),v.useMemo(()=>e.length?t?Array.from(t.values()).reduce((o,i)=>cs(o,i),Mn):Dh(e):Mn,[e,t])}function Uh(e,t){t===void 0&&(t=[]);const n=v.useRef(null);return v.useEffect(()=>{n.current=null},t),v.useEffect(()=>{const r=e!==Mn;r&&!n.current&&(n.current=e),!r&&n.current&&(n.current=null)},[e]),n.current?no(e,n.current):Mn}function hS(e){v.useEffect(()=>{if(!Si)return;const t=e.map(n=>{let{sensor:r}=n;return r.setup==null?void 0:r.setup()});return()=>{for(const n of t)n?.()}},e.map(t=>{let{sensor:n}=t;return n}))}function gS(e,t){return v.useMemo(()=>e.reduce((n,r)=>{let{eventName:s,handler:o}=r;return n[s]=i=>{o(i,t)},n},{}),[e,t])}function Bh(e){return v.useMemo(()=>e?VC(e):null,[e])}const jh=[];function pS(e,t){t===void 0&&(t=us);const[n]=e,r=Bh(n?Kt(n):null),[s,o]=v.useState(jh);function i(){o(()=>e.length?e.map(l=>Oh(l)?r:new ec(t(l),l)):jh)}const a=Ii({callback:i});return En(()=>{a?.disconnect(),i(),e.forEach(l=>a?.observe(l))},[e]),s}function Wh(e){if(!e)return null;if(e.children.length>1)return e;const t=e.children[0];return Js(t)?t:e}function mS(e){let{measure:t}=e;const[n,r]=v.useState(null),s=v.useCallback(c=>{for(const{target:d}of c)if(Js(d)){r(u=>{const f=t(d);return u?{...u,width:f.width,height:f.height}:f});break}},[t]),o=Ii({callback:s}),i=v.useCallback(c=>{const d=Wh(c);o?.disconnect(),d&&o?.observe(d),r(d?t(d):null)},[t,o]),[a,l]=Ei(i);return v.useMemo(()=>({nodeRef:a,rect:n,setRef:l}),[n,a,l])}const bS=[{sensor:oo,options:{}},{sensor:so,options:{}}],yS={current:{}},Li={draggable:{measure:Eh},droppable:{measure:Eh,strategy:ao.WhileDragging,frequency:oc.Optimized},dragOverlay:{measure:us}};class lo extends Map{get(t){var n;return t!=null&&(n=super.get(t))!=null?n:void 0}toArray(){return Array.from(this.values())}getEnabled(){return this.toArray().filter(t=>{let{disabled:n}=t;return!n})}getNodeFor(t){var n,r;return(n=(r=this.get(t))==null?void 0:r.node.current)!=null?n:void 0}}const vS={activatorEvent:null,active:null,activeNode:null,activeNodeRect:null,collisions:null,containerNodeRect:null,draggableNodes:new Map,droppableRects:new Map,droppableContainers:new lo,over:null,dragOverlay:{nodeRef:{current:null},rect:null,setRef:Oi},scrollableAncestors:[],scrollableAncestorRects:[],measuringConfiguration:Li,measureDroppableContainers:Oi,windowRect:null,measuringScheduled:!1},Gh={activatorEvent:null,activators:[],active:null,activeNodeRect:null,ariaDescribedById:{draggable:""},dispatch:Oi,draggableNodes:new Map,over:null,measureDroppableContainers:Oi},co=v.createContext(Gh),Yh=v.createContext(vS);function xS(){return{draggable:{active:null,initialCoordinates:{x:0,y:0},nodes:new Map,translate:{x:0,y:0}},droppable:{containers:new lo}}}function wS(e,t){switch(t.type){case Et.DragStart:return{...e,draggable:{...e.draggable,initialCoordinates:t.initialCoordinates,active:t.active}};case Et.DragMove:return e.draggable.active==null?e:{...e,draggable:{...e.draggable,translate:{x:t.coordinates.x-e.draggable.initialCoordinates.x,y:t.coordinates.y-e.draggable.initialCoordinates.y}}};case Et.DragEnd:case Et.DragCancel:return{...e,draggable:{...e.draggable,active:null,initialCoordinates:{x:0,y:0},translate:{x:0,y:0}}};case Et.RegisterDroppable:{const{element:n}=t,{id:r}=n,s=new lo(e.droppable.containers);return s.set(r,n),{...e,droppable:{...e.droppable,containers:s}}}case Et.SetDroppableDisabled:{const{id:n,key:r,disabled:s}=t,o=e.droppable.containers.get(n);if(!o||r!==o.key)return e;const i=new lo(e.droppable.containers);return i.set(n,{...o,disabled:s}),{...e,droppable:{...e.droppable,containers:i}}}case Et.UnregisterDroppable:{const{id:n,key:r}=t,s=e.droppable.containers.get(n);if(!s||r!==s.key)return e;const o=new lo(e.droppable.containers);return o.delete(n),{...e,droppable:{...e.droppable,containers:o}}}default:return e}}function kS(e){let{disabled:t}=e;const{active:n,activatorEvent:r,draggableNodes:s}=v.useContext(co),o=Mi(r),i=Mi(n?.id);return v.useEffect(()=>{if(!t&&!r&&o&&i!=null){if(!Ni(o)||document.activeElement===o.target)return;const a=s.get(i);if(!a)return;const{activatorNode:l,node:c}=a;if(!l.current&&!c.current)return;requestAnimationFrame(()=>{for(const d of[l.current,c.current]){if(!d)continue;const u=vC(d);if(u){u.focus();break}}})}},[r,t,s,i,o]),null}function Xh(e,t){let{transform:n,...r}=t;return e!=null&&e.length?e.reduce((s,o)=>o({transform:s,...r}),n):n}function CS(e){return v.useMemo(()=>({draggable:{...Li.draggable,...e?.draggable},droppable:{...Li.droppable,...e?.droppable},dragOverlay:{...Li.dragOverlay,...e?.dragOverlay}}),[e?.draggable,e?.droppable,e?.dragOverlay])}function SS(e){let{activeNode:t,measure:n,initialRect:r,config:s=!0}=e;const o=v.useRef(!1),{x:i,y:a}=typeof s=="boolean"?{x:s,y:s}:s;En(()=>{if(!i&&!a||!t){o.current=!1;return}if(o.current||!r)return;const c=t?.node.current;if(!c||c.isConnected===!1)return;const d=n(c),u=Sh(d,r);if(i||(u.x=0),a||(u.y=0),o.current=!0,Math.abs(u.x)>0||Math.abs(u.y)>0){const f=Mh(c);f&&f.scrollBy({top:u.y,left:u.x})}},[t,i,a,r,n])}const Pi=v.createContext({...Mn,scaleX:1,scaleY:1});var cr;(function(e){e[e.Uninitialized=0]="Uninitialized",e[e.Initializing=1]="Initializing",e[e.Initialized=2]="Initialized"})(cr||(cr={}));const ac=v.memo(function(t){var n,r,s,o;let{id:i,accessibility:a,autoScroll:l=!0,children:c,sensors:d=bS,collisionDetection:u=IC,measuring:f,modifiers:g,...m}=t;const p=v.useReducer(wS,void 0,xS),[y,b]=p,[_,x]=_C(),[M,S]=v.useState(cr.Uninitialized),k=M===cr.Initialized,{draggable:{active:N,nodes:C,translate:w},droppable:{containers:E}}=y,A=N!=null?C.get(N):null,P=v.useRef({initial:null,translated:null}),L=v.useMemo(()=>{var Le;return N!=null?{id:N,data:(Le=A?.data)!=null?Le:yS,rect:P}:null},[N,A]),$=v.useRef(null),[D,O]=v.useState(null),[T,z]=v.useState(null),U=Rs(m,Object.values(m)),W=to("DndDescribedBy",i),G=v.useMemo(()=>E.getEnabled(),[E]),B=CS(f),{droppableRects:j,measureDroppableContainers:Y,measuringScheduled:V}=iS(G,{dragging:k,dependencies:[w.x,w.y],config:B.droppable}),X=sS(C,N),K=v.useMemo(()=>T?Ti(T):null,[T]),q=Ke(),ne=aS(X,B.draggable.measure);SS({activeNode:N!=null?C.get(N):null,config:q.layoutShiftCompensation,initialRect:ne,measure:B.draggable.measure});const ee=Vh(X,B.draggable.measure,ne),fe=Vh(X?X.parentElement:null),te=v.useRef({activatorEvent:null,active:null,activeNode:X,collisionRect:null,collisions:null,droppableRects:j,draggableNodes:C,draggingNode:null,draggingNodeRect:null,droppableContainers:E,over:null,scrollableAncestors:[],scrollAdjustedTranslate:null}),le=E.getNodeFor((n=te.current.over)==null?void 0:n.id),ge=mS({measure:B.dragOverlay.measure}),ce=(r=ge.nodeRef.current)!=null?r:X,ye=k?(s=ge.rect)!=null?s:ee:null,me=!!(ge.nodeRef.current&&ge.rect),pe=dS(me?null:ee),ke=Bh(ce?Kt(ce):null),$e=uS(k?le??X:null),ie=pS($e),_e=Xh(g,{transform:{x:w.x-pe.x,y:w.y-pe.y,scaleX:1,scaleY:1},activatorEvent:T,active:L,activeNodeRect:ee,containerNodeRect:fe,draggingNodeRect:ye,over:te.current.over,overlayNodeRect:ge.rect,scrollableAncestors:$e,scrollableAncestorRects:ie,windowRect:ke}),Ae=K?cs(K,w):null,Ie=fS($e),Be=Uh(Ie),gt=Uh(Ie,[ee]),mt=cs(_e,Be),Ge=ye?$C(ye,_e):null,ot=L&&Ge?u({active:L,collisionRect:Ge,droppableRects:j,droppableContainers:G,pointerCoordinates:Ae}):null,Fe=kh(ot,"id"),[re,de]=v.useState(null),xe=me?_e:cs(_e,gt),ve=LC(xe,(o=re?.rect)!=null?o:null,ee),we=v.useRef(null),Ve=v.useCallback((Le,Ee)=>{let{sensor:ze,options:rt}=Ee;if($.current==null)return;const it=C.get($.current);if(!it)return;const Ye=Le.nativeEvent,at=new ze({active:$.current,activeNode:it,event:Ye,options:rt,context:te,onAbort(bt){if(!C.get(bt))return;const{onDragAbort:dt}=U.current,Zt={id:bt};dt?.(Zt),_({type:"onDragAbort",event:Zt})},onPending(bt,qe,dt,Zt){if(!C.get(bt))return;const{onDragPending:Je}=U.current,Qt={id:bt,constraint:qe,initialCoordinates:dt,offset:Zt};Je?.(Qt),_({type:"onDragPending",event:Qt})},onStart(bt){const qe=$.current;if(qe==null)return;const dt=C.get(qe);if(!dt)return;const{onDragStart:Zt}=U.current,on={activatorEvent:Ye,active:{id:qe,data:dt.data,rect:P}};Ln.unstable_batchedUpdates(()=>{Zt?.(on),S(cr.Initializing),b({type:Et.DragStart,initialCoordinates:bt,active:qe}),_({type:"onDragStart",event:on}),O(we.current),z(Ye)})},onMove(bt){b({type:Et.DragMove,coordinates:bt})},onEnd:xn(Et.DragEnd),onCancel:xn(Et.DragCancel)});we.current=at;function xn(bt){return async function(){const{active:dt,collisions:Zt,over:on,scrollAdjustedTranslate:Je}=te.current;let Qt=null;if(dt&&Je){const{cancelDrop:wn}=U.current;Qt={activatorEvent:Ye,active:dt,collisions:Zt,delta:Je,over:on},bt===Et.DragEnd&&typeof wn=="function"&&await Promise.resolve(wn(Qt))&&(bt=Et.DragCancel)}$.current=null,Ln.unstable_batchedUpdates(()=>{b({type:bt}),S(cr.Uninitialized),de(null),O(null),z(null),we.current=null;const wn=bt===Et.DragEnd?"onDragEnd":"onDragCancel";if(Qt){const xr=U.current[wn];xr?.(Qt),_({type:wn,event:Qt})}})}}},[C]),De=v.useCallback((Le,Ee)=>(ze,rt)=>{const it=ze.nativeEvent,Ye=C.get(rt);if($.current!==null||!Ye||it.dndKit||it.defaultPrevented)return;const at={active:Ye};Le(ze,Ee.options,at)===!0&&(it.dndKit={capturedBy:Ee.sensor},$.current=rt,Ve(ze,Ee))},[C,Ve]),Ne=oS(d,De);hS(d),En(()=>{ee&&M===cr.Initializing&&S(cr.Initialized)},[ee,M]),v.useEffect(()=>{const{onDragMove:Le}=U.current,{active:Ee,activatorEvent:ze,collisions:rt,over:it}=te.current;if(!Ee||!ze)return;const Ye={active:Ee,activatorEvent:ze,collisions:rt,delta:{x:mt.x,y:mt.y},over:it};Ln.unstable_batchedUpdates(()=>{Le?.(Ye),_({type:"onDragMove",event:Ye})})},[mt.x,mt.y]),v.useEffect(()=>{const{active:Le,activatorEvent:Ee,collisions:ze,droppableContainers:rt,scrollAdjustedTranslate:it}=te.current;if(!Le||$.current==null||!Ee||!it)return;const{onDragOver:Ye}=U.current,at=rt.get(Fe),xn=at&&at.rect.current?{id:at.id,rect:at.rect.current,data:at.data,disabled:at.disabled}:null,bt={active:Le,activatorEvent:Ee,collisions:ze,delta:{x:it.x,y:it.y},over:xn};Ln.unstable_batchedUpdates(()=>{de(xn),Ye?.(bt),_({type:"onDragOver",event:bt})})},[Fe]),En(()=>{te.current={activatorEvent:T,active:L,activeNode:X,collisionRect:Ge,collisions:ot,droppableRects:j,draggableNodes:C,draggingNode:ce,draggingNodeRect:ye,droppableContainers:E,over:re,scrollableAncestors:$e,scrollAdjustedTranslate:mt},P.current={initial:ye,translated:Ge}},[L,X,ot,Ge,C,ce,ye,j,E,re,$e,mt]),tS({...q,delta:w,draggingRect:Ge,pointerCoordinates:Ae,scrollableAncestors:$e,scrollableAncestorRects:ie});const Te=v.useMemo(()=>({active:L,activeNode:X,activeNodeRect:ee,activatorEvent:T,collisions:ot,containerNodeRect:fe,dragOverlay:ge,draggableNodes:C,droppableContainers:E,droppableRects:j,over:re,measureDroppableContainers:Y,scrollableAncestors:$e,scrollableAncestorRects:ie,measuringConfiguration:B,measuringScheduled:V,windowRect:ke}),[L,X,ee,T,ot,fe,ge,C,E,j,re,Y,$e,ie,B,V,ke]),He=v.useMemo(()=>({activatorEvent:T,activators:Ne,active:L,activeNodeRect:ee,ariaDescribedById:{draggable:W},dispatch:b,draggableNodes:C,over:re,measureDroppableContainers:Y}),[T,Ne,L,ee,b,W,C,re,Y]);return v.createElement(yh.Provider,{value:x},v.createElement(co.Provider,{value:He},v.createElement(Yh.Provider,{value:Te},v.createElement(Pi.Provider,{value:ve},c)),v.createElement(kS,{disabled:a?.restoreFocus===!1})),v.createElement(NC,{...a,hiddenTextDescribedById:W}));function Ke(){const Le=D?.autoScrollEnabled===!1,Ee=typeof l=="object"?l.enabled===!1:l===!1,ze=k&&!Le&&!Ee;return typeof l=="object"?{...l,enabled:ze}:{enabled:ze}}}),_S=v.createContext(null),Kh="button",ES="Draggable";function MS(e){let{id:t,data:n,disabled:r=!1,attributes:s}=e;const o=to(ES),{activators:i,activatorEvent:a,active:l,activeNodeRect:c,ariaDescribedById:d,draggableNodes:u,over:f}=v.useContext(co),{role:g=Kh,roleDescription:m="draggable",tabIndex:p=0}=s??{},y=l?.id===t,b=v.useContext(y?Pi:_S),[_,x]=Ei(),[M,S]=Ei(),k=gS(i,t),N=Rs(n);En(()=>(u.set(t,{id:t,key:o,node:_,activatorNode:M,data:N}),()=>{const w=u.get(t);w&&w.key===o&&u.delete(t)}),[u,t]);const C=v.useMemo(()=>({role:g,tabIndex:p,"aria-disabled":r,"aria-pressed":y&&g===Kh?!0:void 0,"aria-roledescription":m,"aria-describedby":d.draggable}),[r,g,p,y,m,d.draggable]);return{active:l,activatorEvent:a,activeNodeRect:c,attributes:C,isDragging:y,listeners:r?void 0:k,node:_,over:f,setNodeRef:x,setActivatorNodeRef:S,transform:b}}function qh(){return v.useContext(Yh)}const NS="Droppable",TS={timeout:25};function OS(e){let{data:t,disabled:n=!1,id:r,resizeObserverConfig:s}=e;const o=to(NS),{active:i,dispatch:a,over:l,measureDroppableContainers:c}=v.useContext(co),d=v.useRef({disabled:n}),u=v.useRef(!1),f=v.useRef(null),g=v.useRef(null),{disabled:m,updateMeasurementsFor:p,timeout:y}={...TS,...s},b=Rs(p??r),_=v.useCallback(()=>{if(!u.current){u.current=!0;return}g.current!=null&&clearTimeout(g.current),g.current=setTimeout(()=>{c(Array.isArray(b.current)?b.current:[b.current]),g.current=null},y)},[y]),x=Ii({callback:_,disabled:m||!i}),M=v.useCallback((C,w)=>{x&&(w&&(x.unobserve(w),u.current=!1),C&&x.observe(C))},[x]),[S,k]=Ei(M),N=Rs(t);return v.useEffect(()=>{!x||!S.current||(x.disconnect(),u.current=!1,x.observe(S.current))},[S,x]),v.useEffect(()=>(a({type:Et.RegisterDroppable,element:{id:r,key:o,disabled:n,node:S,rect:f,data:N}}),()=>a({type:Et.UnregisterDroppable,key:o,id:r})),[r]),v.useEffect(()=>{n!==d.current.disabled&&(a({type:Et.SetDroppableDisabled,id:r,key:o,disabled:n}),d.current.disabled=n)},[r,o,n,a]),{active:i,rect:f,isOver:l?.id===r,node:S,over:l,setNodeRef:k}}function AS(e){let{animation:t,children:n}=e;const[r,s]=v.useState(null),[o,i]=v.useState(null),a=Mi(n);return!n&&!r&&a&&s(a),En(()=>{if(!o)return;const l=r?.key,c=r?.props.id;if(l==null||c==null){s(null);return}Promise.resolve(t(c,o)).then(()=>{s(null)})},[t,r,o]),v.createElement(v.Fragment,null,n,r?v.cloneElement(r,{ref:i}):null)}const DS={x:0,y:0,scaleX:1,scaleY:1};function IS(e){let{children:t}=e;return v.createElement(co.Provider,{value:Gh},v.createElement(Pi.Provider,{value:DS},t))}const LS={position:"fixed",touchAction:"none"},PS=e=>Ni(e)?"transform 250ms ease":void 0,$S=v.forwardRef((e,t)=>{let{as:n,activatorEvent:r,adjustScale:s,children:o,className:i,rect:a,style:l,transform:c,transition:d=PS}=e;if(!a)return null;const u=s?c:{...c,scaleX:1,scaleY:1},f={...LS,width:a.width,height:a.height,top:a.top,left:a.left,transform:Un.Transform.toString(u),transformOrigin:s&&r?TC(r,a):void 0,transition:typeof d=="function"?d(r):d,...l};return v.createElement(n,{className:i,style:f,ref:t},o)}),FS={duration:250,easing:"ease",keyframes:e=>{let{transform:{initial:t,final:n}}=e;return[{transform:Un.Transform.toString(t)},{transform:Un.Transform.toString(n)}]},sideEffects:(e=>t=>{let{active:n,dragOverlay:r}=t;const s={},{styles:o,className:i}=e;if(o!=null&&o.active)for(const[a,l]of Object.entries(o.active))l!==void 0&&(s[a]=n.node.style.getPropertyValue(a),n.node.style.setProperty(a,l));if(o!=null&&o.dragOverlay)for(const[a,l]of Object.entries(o.dragOverlay))l!==void 0&&r.node.style.setProperty(a,l);return i!=null&&i.active&&n.node.classList.add(i.active),i!=null&&i.dragOverlay&&r.node.classList.add(i.dragOverlay),function(){for(const[l,c]of Object.entries(s))n.node.style.setProperty(l,c);i!=null&&i.active&&n.node.classList.remove(i.active)}})({styles:{active:{opacity:"0"}}})};function zS(e){let{config:t,draggableNodes:n,droppableContainers:r,measuringConfiguration:s}=e;return _i((o,i)=>{if(t===null)return;const a=n.get(o);if(!a)return;const l=a.node.current;if(!l)return;const c=Wh(i);if(!c)return;const{transform:d}=Kt(i).getComputedStyle(i),u=_h(d);if(!u)return;const f=typeof t=="function"?t:VS(t);return Ih(l,s.draggable.measure),f({active:{id:o,data:a.data,node:l,rect:s.draggable.measure(l)},draggableNodes:n,dragOverlay:{node:i,rect:s.dragOverlay.measure(c)},droppableContainers:r,measuringConfiguration:s,transform:u})})}function VS(e){const{duration:t,easing:n,sideEffects:r,keyframes:s}={...FS,...e};return o=>{let{active:i,dragOverlay:a,transform:l,...c}=o;if(!t)return;const d={x:a.rect.left-i.rect.left,y:a.rect.top-i.rect.top},u={scaleX:l.scaleX!==1?i.rect.width*l.scaleX/a.rect.width:1,scaleY:l.scaleY!==1?i.rect.height*l.scaleY/a.rect.height:1},f={x:l.x-d.x,y:l.y-d.y,...u},g=s({...c,active:i,dragOverlay:a,transform:{initial:l,final:f}}),[m]=g,p=g[g.length-1];if(JSON.stringify(m)===JSON.stringify(p))return;const y=r?.({active:i,dragOverlay:a,...c}),b=a.node.animate(g,{duration:t,easing:n,fill:"forwards"});return new Promise(_=>{b.onfinish=()=>{y?.(),_()}})}}let Zh=0;function HS(e){return v.useMemo(()=>{if(e!=null)return Zh++,Zh},[e])}const US=v.memo(e=>{let{adjustScale:t=!1,children:n,dropAnimation:r,style:s,transition:o,modifiers:i,wrapperElement:a="div",className:l,zIndex:c=999}=e;const{activatorEvent:d,active:u,activeNodeRect:f,containerNodeRect:g,draggableNodes:m,droppableContainers:p,dragOverlay:y,over:b,measuringConfiguration:_,scrollableAncestors:x,scrollableAncestorRects:M,windowRect:S}=qh(),k=v.useContext(Pi),N=HS(u?.id),C=Xh(i,{activatorEvent:d,active:u,activeNodeRect:f,containerNodeRect:g,draggingNodeRect:y.rect,over:b,overlayNodeRect:y.rect,scrollableAncestors:x,scrollableAncestorRects:M,transform:k,windowRect:S}),w=ic(f),E=zS({config:r,draggableNodes:m,droppableContainers:p,measuringConfiguration:_}),A=w?y.setRef:void 0;return v.createElement(IS,null,v.createElement(AS,{animation:E},u&&N?v.createElement($S,{key:N,id:u.id,ref:A,as:a,activatorEvent:d,adjustScale:t,className:l,transition:o,rect:w,style:{zIndex:c,...s},transform:C},n):null))});function fs(e,t,n){const r=e.slice();return r.splice(n<0?r.length+n:n,0,r.splice(t,1)[0]),r}function BS(e,t){return e.reduce((n,r,s)=>{const o=t.get(r);return o&&(n[s]=o),n},Array(e.length))}function $i(e){return e!==null&&e>=0}function jS(e,t){if(e===t)return!0;if(e.length!==t.length)return!1;for(let n=0;n{let{rects:t,activeIndex:n,overIndex:r,index:s}=e;const o=fs(t,r,n),i=t[s],a=o[s];return!a||!i?null:{x:a.left-i.left,y:a.top-i.top,scaleX:a.width/i.width,scaleY:a.height/i.height}},zi={scaleX:1,scaleY:1},lc=e=>{var t;let{activeIndex:n,activeNodeRect:r,index:s,rects:o,overIndex:i}=e;const a=(t=o[n])!=null?t:r;if(!a)return null;if(s===n){const c=o[i];return c?{x:0,y:nn&&s<=i?{x:0,y:-a.height-l,...zi}:s=i?{x:0,y:a.height+l,...zi}:{x:0,y:0,...zi}};function GS(e,t,n){const r=e[t],s=e[t-1],o=e[t+1];return r?nr.map(k=>typeof k=="object"&&"id"in k?k.id:k),[r]),m=i!=null,p=i?g.indexOf(i.id):-1,y=c?g.indexOf(c.id):-1,b=v.useRef(g),_=!jS(g,b.current),x=y!==-1&&p===-1||_,M=WS(o);En(()=>{_&&m&&d(g)},[_,g,m,d]),v.useEffect(()=>{b.current=g},[g]);const S=v.useMemo(()=>({activeIndex:p,containerId:u,disabled:M,disableTransforms:x,items:g,overIndex:y,useDragOverlay:f,sortedRects:BS(g,l),strategy:s}),[p,u,M.draggable,M.droppable,x,g,y,l,f,s]);return v.createElement(Jh.Provider,{value:S},t)}const Rh=e=>{let{id:t,items:n,activeIndex:r,overIndex:s}=e;return fs(n,r,s).indexOf(t)},eg=e=>{let{containerId:t,isSorting:n,wasDragging:r,index:s,items:o,newIndex:i,previousItems:a,previousContainerId:l,transition:c}=e;return!c||!r||a!==o&&s===i?!1:n?!0:i!==s&&t===l},YS={duration:200,easing:"ease"},tg="transform",XS=Un.Transition.toString({property:tg,duration:0,easing:"linear"}),KS={roleDescription:"sortable"};function qS(e){let{disabled:t,index:n,node:r,rect:s}=e;const[o,i]=v.useState(null),a=v.useRef(n);return En(()=>{if(!t&&n!==a.current&&r.current){const l=s.current;if(l){const c=us(r.current,{ignoreTransform:!0}),d={x:l.left-c.left,y:l.top-c.top,scaleX:l.width/c.width,scaleY:l.height/c.height};(d.x||d.y)&&i(d)}}n!==a.current&&(a.current=n)},[t,n,r,s]),v.useEffect(()=>{o&&i(null)},[o]),o}function Hi(e){let{animateLayoutChanges:t=eg,attributes:n,disabled:r,data:s,getNewIndex:o=Rh,id:i,strategy:a,resizeObserverConfig:l,transition:c=YS}=e;const{items:d,containerId:u,activeIndex:f,disabled:g,disableTransforms:m,sortedRects:p,overIndex:y,useDragOverlay:b,strategy:_}=v.useContext(Jh),x=ZS(r,g),M=d.indexOf(i),S=v.useMemo(()=>({sortable:{containerId:u,index:M,items:d},...s}),[u,s,M,d]),k=v.useMemo(()=>d.slice(d.indexOf(i)),[d,i]),{rect:N,node:C,isOver:w,setNodeRef:E}=OS({id:i,data:S,disabled:x.droppable,resizeObserverConfig:{updateMeasurementsFor:k,...l}}),{active:A,activatorEvent:P,activeNodeRect:L,attributes:$,setNodeRef:D,listeners:O,isDragging:T,over:z,setActivatorNodeRef:U,transform:W}=MS({id:i,data:S,attributes:{...KS,...n},disabled:x.draggable}),G=pC(E,D),B=!!A,j=B&&!m&&$i(f)&&$i(y),Y=!b&&T,V=Y&&j?W:null,K=j?V??(a??_)({rects:p,activeNodeRect:L,activeIndex:f,overIndex:y,index:M}):null,q=$i(f)&&$i(y)?o({id:i,items:d,activeIndex:f,overIndex:y}):M,ne=A?.id,ee=v.useRef({activeId:ne,items:d,newIndex:q,containerId:u}),fe=d!==ee.current.items,te=t({active:A,containerId:u,isDragging:T,isSorting:B,id:i,index:M,items:d,newIndex:ee.current.newIndex,previousItems:ee.current.items,previousContainerId:ee.current.containerId,transition:c,wasDragging:ee.current.activeId!=null}),le=qS({disabled:!te,index:M,node:C,rect:N});return v.useEffect(()=>{B&&ee.current.newIndex!==q&&(ee.current.newIndex=q),u!==ee.current.containerId&&(ee.current.containerId=u),d!==ee.current.items&&(ee.current.items=d)},[B,q,u,d]),v.useEffect(()=>{if(ne===ee.current.activeId)return;if(ne!=null&&ee.current.activeId==null){ee.current.activeId=ne;return}const ce=setTimeout(()=>{ee.current.activeId=ne},50);return()=>clearTimeout(ce)},[ne]),{active:A,activeIndex:f,attributes:$,data:S,rect:N,index:M,newIndex:q,items:d,isOver:w,isSorting:B,isDragging:T,listeners:O,node:C,overIndex:y,over:z,setNodeRef:G,setActivatorNodeRef:U,setDroppableNodeRef:E,setDraggableNodeRef:D,transform:le??K,transition:ge()};function ge(){if(le||fe&&ee.current.newIndex===M)return XS;if(!(Y&&!Ni(P)||!c)&&(B||te))return Un.Transition.toString({...c,property:tg})}}function ZS(e,t){var n,r;return typeof e=="boolean"?{draggable:e,droppable:!1}:{draggable:(n=e?.draggable)!=null?n:t.draggable,droppable:(r=e?.droppable)!=null?r:t.droppable}}function uo(e){if(!e)return!1;const t=e.data.current;return!!(t&&"sortable"in t&&typeof t.sortable=="object"&&"containerId"in t.sortable&&"items"in t.sortable&&"index"in t.sortable)}const QS=[Xe.Down,Xe.Right,Xe.Up,Xe.Left],Ui=(e,t)=>{let{context:{active:n,collisionRect:r,droppableRects:s,droppableContainers:o,over:i,scrollableAncestors:a}}=t;if(QS.includes(e.code)){if(e.preventDefault(),!n||!r)return;const l=[];o.getEnabled().forEach(u=>{if(!u||u!=null&&u.disabled)return;const f=s.get(u.id);if(f)switch(e.code){case Xe.Down:r.topf.top&&l.push(u);break;case Xe.Left:r.left>f.left&&l.push(u);break;case Xe.Right:r.left1&&(d=c[1].id),d!=null){const u=o.get(n.id),f=o.get(d),g=f?s.get(f.id):null,m=f?.node.current;if(m&&g&&u&&f){const y=Ai(m).some((k,N)=>a[N]!==k),b=ng(u,f),_=JS(u,f),x=y||!b?{x:0,y:0}:{x:_?r.width-g.width:0,y:_?r.height-g.height:0},M={x:g.left,y:g.top};return x.x&&x.y?M:no(M,x)}}}};function ng(e,t){return!uo(e)||!uo(t)?!1:e.data.current.sortable.containerId===t.data.current.sortable.containerId}function JS(e,t){return!uo(e)||!uo(t)||!ng(e,t)?!1:e.data.current.sortable.indexwindow.G7Core,rg=({item:e,isSelected:t=!1,isExpanded:n=!1,level:r=0,onClick:s,onToggle:o,onExpandToggle:i,className:a="",selectedClassName:l="bg-blue-50 dark:bg-blue-900/30 border-blue-500",defaultClassName:c="bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600",dragHandleClassName:d="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",iconContainerClassName:u="bg-gray-100 dark:bg-gray-700",contentClassName:f="",enableDrag:g=!0,toggleDisabled:m=!1})=>{const{attributes:p,listeners:y,setNodeRef:b,transform:_,transition:x,isDragging:M}=Hi({id:e.id}),S={transform:Un.Transform.toString(_),transition:x,opacity:M?.5:1},k=r*24+12,C=`flex items-center gap-2 cursor-pointer transition-colors ${t?l:c}`,w=e_()?.style?.mergeClasses?.(C,a)??`${C} ${a}`.trim();return h.jsxs(I,{ref:b,style:S,className:w,onClick:s,children:[g&&h.jsx(I,{...p,...y,className:`flex-shrink-0 cursor-grab active:cursor-grabbing p-1 ${d}`,style:{paddingLeft:`${k}px`},children:h.jsx(oe,{name:"grip-vertical",className:"w-4 h-4"})}),!g&&r>0&&h.jsx(I,{style:{paddingLeft:`${k}px`}}),g&&h.jsx(h.Fragment,{children:e.hasChildren?h.jsx(ae,{type:"button",onClick:E=>{E.stopPropagation(),i?.()},className:`flex-shrink-0 p-1 ${d}`,children:h.jsx(oe,{name:n?"chevron-down":"chevron-right",className:"w-4 h-4"})}):h.jsx(I,{className:"flex-shrink-0 w-6"})}),h.jsx(I,{className:`flex-shrink-0 w-8 h-8 flex items-center justify-center rounded ${u}`,children:h.jsx(oe,{name:e.icon||"file",className:"w-4 h-4 text-gray-600 dark:text-gray-400"})}),h.jsxs(I,{className:`flex-1 min-w-0 ${f}`,children:[h.jsxs(I,{className:"flex items-center gap-1",children:[h.jsx(J,{className:"text-sm font-medium text-gray-900 dark:text-white truncate",children:e.name}),e.isModuleMenu&&h.jsx(oe,{name:"cube",className:"w-2 h-2 text-gray-300 dark:text-gray-500 ml-2",title:e.moduleName})]}),h.jsx(J,{className:"text-xs text-gray-500 dark:text-gray-400 truncate block",children:e.url})]}),h.jsx(I,{className:"flex-shrink-0",onClick:E=>E.stopPropagation(),children:h.jsx(Qa,{checked:e.isActive,onChange:()=>o?.(new MouseEvent("click")),size:"sm",disabled:m})})]})},t_=()=>window.G7Core;function sg(e,t,n=0,r=null){const s=[];for(const o of e)s.push({item:o,depth:n,parentId:r}),o.children?.length>0&&t.has(o.id)&&s.push(...sg(o.children,t,n+1,o.id));return s}function cc(e,t){for(const n of e){if(n.id===t)return n;if(n.children){const r=cc(n.children,t);if(r)return r}}return null}function og(e){const t=[];if(e.children)for(const n of e.children)t.push(n.id),t.push(...og(n));return t}function ig(e,t){return e.filter(n=>n.id!==t).map(n=>n.children?.length>0?{...n,children:ig(n.children,t)}:n)}function ag(e,t,n,r){if(n===null){const s=[...e];return s.splice(r,0,t),s}return e.map(s=>{if(s.id===n){const o=[...s.children||[]];return o.splice(r,0,t),{...s,children:o}}return s.children?.length>0?{...s,children:ag(s.children,t,n,r)}:s})}function lg(e,t,n,r){if(t===null){const s=e.findIndex(i=>i.id===n),o=e.findIndex(i=>i.id===r);return s===-1||o===-1?e:fs(e,s,o)}return e.map(s=>{if(s.id===t&&s.children){const o=s.children.findIndex(a=>a.id===n),i=s.children.findIndex(a=>a.id===r);return o===-1||i===-1?s:{...s,children:fs(s.children,o,i)}}return s.children?.length>0?{...s,children:lg(s.children,t,n,r)}:s})}function cg(e){const t=e.map((s,o)=>({id:s.id,order:o+1})),n={},r=s=>{s.forEach(o=>{o.children?.length>0&&(n[o.id]=o.children.map((i,a)=>({id:i.id,order:a+1})),r(o.children))})};return r(e),{parent_menus:t,child_menus:n}}const n_=({items:e=[],selectedId:t,onSelect:n,onOrderChange:r,onToggleStatus:s,title:o,className:i="",listClassName:a="",childrenContainerClassName:l="bg-gray-50 dark:bg-gray-900/50",itemClassName:c="py-2.5 px-3 mx-4 my-2 rounded-lg border",itemSelectedClassName:d,itemDefaultClassName:u,titleClassName:f="px-4 py-3 border-b border-gray-200 dark:border-gray-700",enableDrag:g=!0,toggleDisabled:m=!1,id:p,editorAttrs:y})=>{const[b,_]=v.useState(new Set),[x,M]=v.useState(null),[S,k]=v.useState(e),N=v.useRef(!1),C=v.useRef(""),w=v.useRef(""),E=v.useCallback(j=>JSON.stringify(j),[]),A=v.useCallback(j=>j.map(Y=>{const V=Y.children?.map(X=>X.id).join(",")||"";return`${Y.id}:${V}`}).join("|"),[]);v.useEffect(()=>{const j=A(e),Y=E(e);if(N.current){const V=A(S);(j===V||j!==C.current)&&(N.current=!1,C.current=j,w.current=Y,(j!==V||Y!==E(S))&&k(e));return}Y!==w.current&&(C.current=j,w.current=Y,k(e))},[e,A,E,S]);const P=Zl(ds(oo,{activationConstraint:{distance:8}}),ds(so,{coordinateGetter:Ui})),L=v.useCallback(j=>{if(typeof j=="string")return j;const V=t_()?.locale?.current?.()||"ko";return j[V]||j.en||Object.values(j)[0]||""},[]),$=v.useCallback(j=>{_(Y=>{const V=new Set(Y);return V.has(j)?V.delete(j):V.add(j),V})},[]),D=v.useCallback(j=>{n?.(j)},[n]),O=v.useCallback((j,Y,V)=>{V.stopPropagation(),s?.(j,Y)},[s]),T=j=>{M(j.active.id)},z=j=>{const{active:Y,over:V}=j;if(M(null),!V||Y.id===V.id)return;const X=W.find(q=>q.item.id===Number(Y.id)),K=W.find(q=>q.item.id===Number(V.id));if(!(!X||!K))if(X.parentId===K.parentId){const q=lg(S,X.parentId,Number(Y.id),Number(V.id));k(q),N.current=!0,r?.(cg(q))}else{const q=X.item;if(og(q).includes(Number(V.id)))return;let ee=ig(S,q.id);const fe=W.findIndex(pe=>pe.item.id===Number(Y.id)),te=W.findIndex(pe=>pe.item.id===Number(V.id)),le=fe>te,ge=K.parentId;let ce;if(ge===null)ce=ee.findIndex(pe=>pe.id===Number(V.id)),ce===-1?ce=ee.length:le||(ce+=1);else{const ke=cc(ee,ge)?.children||[];ce=ke.findIndex($e=>$e.id===Number(V.id)),ce===-1?ce=ke.length:le||(ce+=1)}const ye={...q,parent_id:ge,children:q.children||[]};ee=ag(ee,ye,ge,ce),k(ee),N.current=!0;const me=cg(ee);me.moved_items=[{id:q.id,new_parent_id:ge}],r?.(me)}},U=j=>({id:j.id,name:L(j.name),slug:j.slug,url:j.url,icon:j.icon,isActive:j.is_active,isModuleMenu:!!j.module_id,moduleName:j.module?L(j.module.name):void 0,hasChildren:j.children&&j.children.length>0}),W=v.useMemo(()=>sg(S,b),[S,b]),G=v.useMemo(()=>W.map(j=>j.item.id),[W]),B=x?cc(S,Number(x)):null;return h.jsxs(I,{className:i,id:p,...y,children:[o&&h.jsx(I,{className:f,children:h.jsx(jr,{className:"text-sm font-medium text-gray-900 dark:text-white",children:o})}),h.jsxs(ac,{sensors:P,collisionDetection:Ql,onDragStart:T,onDragEnd:z,children:[h.jsx(Vi,{items:G,strategy:lc,children:h.jsx(I,{className:a,children:W.map(j=>h.jsx(I,{className:j.depth>0?l:"",children:h.jsx(rg,{item:U(j.item),isSelected:t===j.item.id,isExpanded:b.has(j.item.id),level:j.depth,onClick:()=>D(j.item),onToggle:Y=>O(j.item.id,j.item.is_active,Y),onExpandToggle:()=>$(j.item.id),className:c,selectedClassName:d,defaultClassName:u,enableDrag:g,toggleDisabled:m})},j.item.id))})}),h.jsx(US,{children:B?h.jsx(I,{className:"bg-white dark:bg-gray-800 shadow-lg rounded-lg border border-blue-500 p-3",children:h.jsx(I,{className:"flex items-center gap-2",children:h.jsx(I,{className:"text-sm font-medium text-gray-900 dark:text-white",children:L(B.name)})})}):null})]})]})},r_=[{value:"LayoutDashboard",label:"LayoutDashboard",faIcon:"tachometer-alt"},{value:"Settings",label:"Settings",faIcon:"cog"},{value:"Menu",label:"Menu",faIcon:"bars"},{value:"Users",label:"Users",faIcon:"users"},{value:"Package",label:"Package",faIcon:"cube"},{value:"Puzzle",label:"Puzzle",faIcon:"puzzle-piece"},{value:"FileText",label:"FileText",faIcon:"file-alt"},{value:"ShoppingCart",label:"ShoppingCart",faIcon:"shopping-cart"},{value:"MessageSquare",label:"MessageSquare",faIcon:"comment"},{value:"Home",label:"Home",faIcon:"home"},{value:"User",label:"User",faIcon:"user"},{value:"Search",label:"Search",faIcon:"search"},{value:"Bell",label:"Bell",faIcon:"bell"},{value:"Mail",label:"Mail",faIcon:"envelope"},{value:"Calendar",label:"Calendar",faIcon:"calendar"},{value:"Chart",label:"Chart",faIcon:"chart-bar"},{value:"Database",label:"Database",faIcon:"database"},{value:"Lock",label:"Lock",faIcon:"lock"},{value:"Globe",label:"Globe",faIcon:"globe"},{value:"Image",label:"Image",faIcon:"image"}],s_=({value:e,onChange:t,options:n=r_,placeholder:r,searchPlaceholder:s,noResultsText:o,className:i="",disabled:a=!1,name:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),[g,m]=v.useState(""),p=v.useRef(null),y=n.find(x=>{if(!e)return!1;if(e.includes("fa-")){const M=e.replace(/^(fas|far|fab|fal|fad)\s+fa-/,"");return x.faIcon===M||x.faIcon===e}return x.value===e||x.faIcon===e}),b=n.filter(x=>x.label.toLowerCase().includes(g.toLowerCase()));v.useEffect(()=>{const x=M=>{p.current&&!p.current.contains(M.target)&&(f(!1),m(""))};if(u)return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[u]);const _=x=>{const M=`fas fa-${x.faIcon}`;t?.(M),f(!1),m("")};return h.jsxs(I,{ref:p,className:`relative ${i}`,id:c,...d,children:[h.jsxs(ae,{type:"button",onClick:()=>!a&&f(!u),disabled:a,className:` w-full flex items-center justify-between px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-left text-sm ${a?"opacity-50 cursor-not-allowed":"hover:border-gray-400 dark:hover:border-gray-500"} focus:outline-none focus:ring-2 focus:ring-blue-500 - `,children:[h.jsx(I,{className:"flex items-center gap-2",children:y?h.jsxs(h.Fragment,{children:[h.jsx(re,{name:y.faIcon,className:"w-4 h-4 text-gray-600 dark:text-gray-400"}),h.jsx(Q,{className:"text-gray-900 dark:text-white",children:y.label})]}):h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:r})}),h.jsx(re,{name:"chevron-down",className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${u?"rotate-180":""}`})]}),l&&h.jsx("input",{type:"hidden",name:l,value:t||""}),u&&h.jsxs(I,{className:"absolute z-50 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-72 overflow-hidden",children:[h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-700",children:h.jsx(kt,{type:"text",value:g,onChange:x=>m(x.target.value),placeholder:s,className:"w-full px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white",autoFocus:!0})}),h.jsx(I,{className:"max-h-56 overflow-y-auto",children:b.length>0?b.map(x=>h.jsxs(oe,{type:"button",onClick:()=>_(x),className:` + `,children:[h.jsx(I,{className:"flex items-center gap-2",children:y?h.jsxs(h.Fragment,{children:[h.jsx(oe,{name:y.faIcon,className:"w-4 h-4 text-gray-600 dark:text-gray-400"}),h.jsx(J,{className:"text-gray-900 dark:text-white",children:y.label})]}):h.jsx(J,{className:"text-gray-500 dark:text-gray-400",children:r})}),h.jsx(oe,{name:"chevron-down",className:`w-4 h-4 text-gray-400 dark:text-gray-500 transition-transform ${u?"rotate-180":""}`})]}),l&&h.jsx("input",{type:"hidden",name:l,value:e||""}),u&&h.jsxs(I,{className:"absolute z-50 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg max-h-72 overflow-hidden",children:[h.jsx(I,{className:"p-2 border-b border-gray-200 dark:border-gray-700",children:h.jsx(kt,{type:"text",value:g,onChange:x=>m(x.target.value),placeholder:s,className:"w-full px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white",autoFocus:!0})}),h.jsx(I,{className:"max-h-56 overflow-y-auto",children:b.length>0?b.map(x=>h.jsxs(ae,{type:"button",onClick:()=>_(x),className:` w-full flex items-center gap-3 px-3 py-2 text-left text-sm ${y?.faIcon===x.faIcon?"bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700"} - `,children:[h.jsx(re,{name:x.faIcon,className:"w-4 h-4"}),h.jsx(Q,{children:x.label}),y?.faIcon===x.faIcon&&h.jsx(re,{name:"check",className:"w-4 h-4 ml-auto text-blue-600"})]},x.value)):h.jsx(I,{className:"px-3 py-4 text-sm text-gray-500 dark:text-gray-400 text-center",children:o})})]})]})},rc=({defaultOpen:t=!1,isOpen:e,onToggle:n,className:r="",style:s,children:o,disabled:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(t),u=e!==void 0,f=u?e:c,g=v.useRef(null),[m,p]=v.useState(t?void 0:0),y=()=>{if(i)return;const _=!f;u||d(_),n?.(_)};v.useEffect(()=>{if(g.current)if(f){p(g.current.scrollHeight);const _=setTimeout(()=>{p(void 0)},300);return()=>clearTimeout(_)}else p(g.current.scrollHeight),requestAnimationFrame(()=>{p(0)})},[f]);const b={};if(v.Children.forEach(o,_=>{if(v.isValidElement(_)){const x=_.props?.slot;x==="trigger"?b.trigger=_:x==="content"&&(b.content=_)}}),!b.trigger&&!b.content){const _=v.Children.toArray(o);_.length>=2?(b.trigger=_[0],b.content=_.slice(1)):_.length===1&&(b.content=_[0])}return h.jsxs(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden ${r}`,style:s,id:a,...l,children:[h.jsxs(oe,{type:"button",onClick:y,disabled:i,className:`w-full flex items-center justify-between p-3 text-left bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${i?"opacity-50 cursor-not-allowed":""} ${f?"border-b border-gray-200 dark:border-gray-700":""}`,"aria-expanded":f,children:[h.jsx(I,{className:"flex-1",children:b.trigger}),!b.trigger&&h.jsx("svg",{className:`w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),h.jsx(I,{ref:g,className:"overflow-hidden transition-all duration-300 ease-in-out",style:{height:m!==void 0?`${m}px`:"auto"},children:h.jsx(I,{className:"bg-white dark:bg-gray-800",children:b.content})})]})},eo=window.G7Core,Jh=eo?.useControllableState,Rh=(t,e)=>window.G7Core?.t?.(t,e)??t,HS={"":"common.scope_type_all_desc",self:"common.scope_type_self_desc",role:"common.scope_type_role_desc"},US=({options:t,value:e,onChange:n,disabled:r=!1})=>{const[s,o]=v.useState(!1),[i,a]=v.useState({}),l=v.useRef(null),c=t.find(u=>u.value===e)??t[0];v.useEffect(()=>{if(!s)return;const u=g=>{l.current&&!l.current.contains(g.target)&&o(!1)},f=()=>{if(!l.current)return;const g=l.current.getBoundingClientRect();a({position:"fixed",top:g.bottom+4,right:window.innerWidth-g.right,zIndex:9999})};return document.addEventListener("mousedown",u),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{document.removeEventListener("mousedown",u),window.removeEventListener("scroll",f,!0),window.removeEventListener("resize",f)}},[s]);const d=v.useCallback(()=>{if(r||!l.current)return;const u=l.current.getBoundingClientRect();a({position:"fixed",top:u.bottom+4,right:window.innerWidth-u.right,zIndex:9999}),o(f=>!f)},[r]);return h.jsxs(I,{ref:l,className:"relative flex-shrink-0",children:[h.jsxs(I,{role:"button",tabIndex:0,onClick:u=>{u.stopPropagation(),u.preventDefault(),d()},onMouseDown:u=>u.stopPropagation(),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.stopPropagation(),u.preventDefault(),d()),u.key==="Escape"&&o(!1)},className:`flex items-center gap-1 text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer select-none ${r?"opacity-50 cursor-not-allowed":"hover:border-blue-400 dark:hover:border-blue-500"}`,children:[h.jsx(Q,{children:c?.label??""}),h.jsx(re,{name:de.ChevronDown,className:"w-3 h-3 text-gray-400 dark:text-gray-500"})]}),s&&h.jsx(I,{style:i,className:"w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",children:t.map(u=>{const f=u.value??"",g=HS[f],m=g?Rh(g):"",p=u.value===e;return h.jsxs(I,{role:"option","aria-selected":p,onClick:y=>{y.stopPropagation(),y.preventDefault(),n(u.value),o(!1)},onMouseDown:y=>y.stopPropagation(),className:`px-3 py-2 cursor-pointer transition-colors ${p?"bg-blue-50 dark:bg-blue-900/30":"hover:bg-gray-50 dark:hover:bg-gray-700/50"}`,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[h.jsx(Q,{className:`text-xs font-medium ${p?"text-blue-700 dark:text-blue-300":"text-gray-900 dark:text-white"}`,children:u.label}),p&&h.jsx(re,{name:de.Check,className:"w-3.5 h-3.5 text-blue-600 dark:text-blue-400"})]}),m&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:m})]},f||"__null__")})})]})},BS=(t,e)=>{if(t.length!==e.length)return!1;const n=[...t].sort((s,o)=>s.id-o.id),r=[...e].sort((s,o)=>s.id-o.id);return n.every((s,o)=>s.id===r[o].id&&s.scope_type===r[o].scope_type)},jS=({data:t=[],value:e=[],onChange:n,scopeOptions:r=[],className:s="",disabled:o=!1,desktopColumns:i=2,installedModules:a=[],installedPlugins:l=[],id:c,editorAttrs:d})=>{const u=v.useCallback(M=>{if(n){const T=eo?.createChangeEvent?eo.createChangeEvent({value:M}):{target:{value:M}};n(T)}},[n]),[f,g]=Jh?Jh(e,[],u,{isEqual:BS}):(()=>{const[M,T]=v.useState(e),P=v.useRef(n);P.current=n;const L=v.useRef(!1);v.useEffect(()=>{if(L.current){L.current=!1;return}T(e)},[e]);const $=v.useCallback(A=>{L.current=!0,T(D=>{const O=typeof A=="function"?A(D):A;if(P.current){const z=eo?.createChangeEvent?eo.createChangeEvent({value:O}):{target:{value:O}};P.current(z)}return O})},[]);return[M,$]})(),m=v.useMemo(()=>new Set(f.map(M=>M.id)),[f]),p=v.useCallback(M=>M.is_assignable?[M.id]:M.children.flatMap(T=>p(T)),[]),y=v.useCallback(M=>{const T=p(M);return T.length>0&&T.every(P=>m.has(P))},[m,p]),b=v.useCallback(M=>{const T=p(M),P=T.filter(L=>m.has(L)).length;return P>0&&Pp(M).filter(P=>m.has(P)).length,[m,p]),x=v.useCallback((M,T)=>{o||g(P=>T?[...P,{id:M,scope_type:null}]:P.filter(L=>L.id!==M))},[o,g]),E=v.useCallback((M,T)=>{if(o)return;const P=p(M),L=new Set(P);g($=>{if(T){const A=new Set($.map(O=>O.id)),D=P.filter(O=>!A.has(O)).map(O=>({id:O,scope_type:null}));return[...$,...D]}else return $.filter(A=>!L.has(A.id))})},[o,p,g]),C=v.useCallback((M,T)=>{o||g(P=>P.map(L=>L.id===M?{...L,scope_type:T}:L))},[o,g]),k=M=>{const T=m.has(M.id),P=!!M.resource_route_key,L=T&&P&&r.length>0,$=f.find(A=>A.id===M.id)?.scope_type??null;return h.jsxs(sn,{className:`flex items-start gap-3 p-3 rounded-lg border cursor-pointer transition-colors ${T?"border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20":"border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50"} ${o?"opacity-50 cursor-not-allowed":""}`,children:[h.jsx(kt,{type:"checkbox",checked:T,onChange:A=>{A.stopPropagation(),x(M.id,A.target.checked)},disabled:o,className:"w-4 h-4 mt-0.5 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsxs(I,{className:"flex items-center justify-between gap-2",children:[h.jsx(Q,{className:"text-sm font-medium text-gray-900 dark:text-white",children:M.name}),L&&h.jsx(US,{options:r,value:$,onChange:A=>C(M.id,A),disabled:o})]}),M.description&&h.jsx(Q,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:M.description})]})]},M.id)},N=(M,T)=>{const P=y(M),L=b(M),$=_(M),A=M.children.filter(z=>z.is_assignable);if(A.length===0&&M.children.length===0)return null;const D=M.children.some(z=>!z.is_assignable),O=i===1?"grid-cols-1":i===3?"grid-cols-1 md:grid-cols-3":"grid-cols-1 md:grid-cols-2";return h.jsxs(rc,{defaultOpen:!1,className:"border-0 rounded-none",children:[h.jsxs(I,{slot:"trigger",className:"flex items-center gap-3 w-full py-2",children:[h.jsx("input",{type:"checkbox",checked:P,ref:z=>{z&&(z.indeterminate=L)},onChange:z=>{z.stopPropagation(),E(M,z.target.checked)},onClick:z=>z.stopPropagation(),onMouseDown:z=>z.stopPropagation(),disabled:o,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(re,{name:de.Folder,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),h.jsx(Q,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:M.name}),h.jsxs(Q,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 ml-1",children:[$,"/",M.leaf_count]}),h.jsx(re,{name:de.ChevronDown,className:"w-4 h-4 text-gray-400 dark:text-gray-500 ml-auto"})]}),h.jsx(I,{slot:"content",className:`pl-8 grid ${O} gap-2 py-2`,children:D?M.children.map(z=>z.is_assignable?k(z):N(z)):A.map(z=>k(z))})]},M.id)},S=M=>{if(M.extension_type==="core")return"core";if(M.extension_type==="module")return"module";if(M.extension_type==="plugin")return"plugin";const T=M.extension_identifier??M.module??"";return T==="core"||!T?"core":l?.some(L=>L.identifier===T)?"plugin":"module"},w=M=>{const T=y(M),P=b(M),L=_(M),$=S(M);return h.jsxs(rc,{defaultOpen:!1,className:"mb-2",children:[h.jsxs(I,{slot:"trigger",className:"flex items-center gap-3 w-full py-2",children:[h.jsx("input",{type:"checkbox",checked:T,ref:A=>{A&&(A.indeterminate=P)},onChange:A=>{A.stopPropagation(),E(M,A.target.checked)},onClick:A=>A.stopPropagation(),onMouseDown:A=>A.stopPropagation(),disabled:o,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(re,{name:$==="core"?de.Cog:de.Cube,className:"w-5 h-5 text-gray-400 dark:text-gray-500"}),h.jsx(Q,{className:"font-medium text-gray-900 dark:text-white",children:M.name}),$!=="core"&&h.jsx(nh,{type:$,identifier:M.extension_identifier??M.module,installedModules:a,installedPlugins:l}),h.jsxs(Q,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 ml-2",children:[L,"/",M.leaf_count]}),h.jsx(re,{name:de.ChevronDown,className:"w-4 h-4 text-gray-400 dark:text-gray-500 ml-auto"})]}),h.jsx(I,{slot:"content",className:"pl-8 space-y-2 pb-2",children:M.children.map(A=>A.is_assignable?k(A):N(A))})]},M.id)};return t.length===0?h.jsx(I,{className:`text-sm text-gray-400 dark:text-gray-500 py-4 text-center ${s}`,id:c,...d,children:Rh("common.no_data")??"데이터가 없습니다."}):h.jsx(I,{className:`space-y-2 ${s}`,id:c,...d,children:t.map(M=>w(M))})};function WS({text:t,keyword:e}){if(!e||!t)return h.jsx(h.Fragment,{children:t});const n=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),r=t.split(new RegExp(`(${n})`,"gi"));return h.jsx(h.Fragment,{children:r.map((s,o)=>s.toLowerCase()===e.toLowerCase()?h.jsx(Q,{className:"bg-yellow-200 dark:bg-yellow-700 rounded px-0.5",children:s},o):s)})}const GS=({data:t=[],expandedIds:e=[],selectedIds:n=[],searchKeyword:r="",showProductCount:s=!1,selectable:o=!1,className:i="",onToggle:a,onSelectionChange:l,id:c,editorAttrs:d})=>{const u=v.useCallback(y=>{if(!a)return;const b=e.includes(y)?e.filter(_=>_!==y):[...e,y];a(b)},[e,a]),f=v.useCallback((y,b)=>{if(!l)return;const _=b?[...n,y]:n.filter(x=>x!==y);l(_)},[n,l]),g=v.useMemo(()=>{if(!r)return null;const y=new Set,b=_=>{for(const x of _)(x.localized_name||x.name).toLowerCase().includes(r.toLowerCase())&&y.add(x.id),x.children&&b(x.children)};return b(t),y},[t,r]),m=v.useCallback(y=>!g||g.has(y.id)?!0:(y.children??[]).some(b=>m(b)),[g]),p=(y,b=0)=>{const _=(y.children??[]).length>0,x=e.includes(y.id),E=n.includes(y.id),C=y.localized_name||y.name;return r&&!m(y)?null:h.jsxs(I,{"data-testid":`category-node-${y.id}`,children:[h.jsxs(I,{className:`flex items-center gap-2 py-1.5 px-2 rounded cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors ${b>0?"ml-"+Math.min(b*5,20):""}`,style:b>0?{marginLeft:`${b*20}px`}:void 0,children:[_?h.jsx(I,{className:"flex-shrink-0 w-5 h-5 flex items-center justify-center text-gray-400 dark:text-gray-500",onClick:()=>u(y.id),"data-testid":`toggle-${y.id}`,children:h.jsx(re,{name:x?de.CaretDown:de.CaretRight,className:"text-xs"})}):h.jsx(I,{className:"flex-shrink-0 w-5 h-5"}),o&&h.jsx(vn,{checked:E,onChange:k=>{k.stopPropagation(),f(y.id,k.target.checked)},className:"flex-shrink-0","data-testid":`checkbox-${y.id}`}),h.jsx(re,{name:de.Folder,className:`flex-shrink-0 text-sm ${_?"text-yellow-500 dark:text-yellow-400":"text-gray-400 dark:text-gray-500"}`}),h.jsx(Q,{className:"text-sm text-gray-700 dark:text-gray-300 select-none",onClick:()=>_&&u(y.id),children:h.jsx(WS,{text:C,keyword:r})}),s&&y.products_count!==void 0&&h.jsx(Q,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-400 ml-1",children:y.products_count})]}),_&&x&&h.jsx(I,{"data-testid":`children-${y.id}`,children:(y.children??[]).map(k=>p(k,b+1))})]},y.id)};return t.length===0?h.jsx(I,{className:`text-sm text-gray-400 dark:text-gray-500 py-4 text-center ${i}`,id:c,...d,children:"데이터가 없습니다."}):h.jsx(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg p-2 max-h-80 overflow-y-auto ${i}`,id:c,...d,children:t.map(y=>p(y,0))})},eg="carousel",to="controller",YS="navigation",KS="no-scroll",sc="portal",XS="root",tg="toolbar",qS="counter",no="fullscreen",ng="slideshow",ro="thumbnails",rg="zoom",Rr="loading",oc="playing",so="error",oo="complete",ZS="placeholder",io=t=>`active-slide-${t}`,QS=io(Rr),JS=io(oc),RS=io(so),e_=io(oo),sg="fullsize",es="flex_center",t_="no_scroll",og="no_scroll_padding",ic="slide",ig="slide_wrapper",n_="slide_wrapper_interactive",rr="prev",sr="next",ac="swipe",ts="close",ag="onPointerDown",lg="onPointerMove",cg="onPointerUp",dg="onPointerLeave",ug="onPointerCancel",lc="onKeyDown",r_="onKeyUp",cc="onWheel",s_="Escape",o_="ArrowLeft",i_="ArrowRight",a_="button",Pi="icon",fg="contain",hg="cover",gg="Unknown action type",pg="yarl__";function zt(...t){return t.filter(Boolean).join(" ")}function Ne(t){return`${pg}${t}`}function dt(t){return`--${pg}${t}`}function pr(t,e){return`${t}${e?`_${e}`:""}`}function mr(t){return e=>pr(t,e)}function br(t,e){var n;return(n=t?.[e])!==null&&n!==void 0?n:e}function mg(t,e,n){return br(t,"{index} of {total}").replace(/\{index}/g,`${hc(n,e.length)+1}`).replace(/\{total}/g,`${e.length}`)}function ns(...t){return()=>{t.forEach(e=>{e()})}}function ln(t,e,n){return()=>{const r=F.useContext(n);if(!r)throw new Error(`${t} must be used within a ${e}.Provider`);return r}}function dc(){return typeof window<"u"}function uc(t,e=0){const n=10**e;return Math.round((t+Number.EPSILON)*n)/n}function or(t){return t.type===void 0||t.type==="image"}function fc(t,e){return t.imageFit===hg||t.imageFit!==fg&&e===hg}function $i(t){return typeof t=="string"?Number.parseInt(t,10):t}function Fi(t){if(typeof t=="number")return{pixel:t};if(typeof t=="string"){const e=$i(t);return t.endsWith("%")?{percent:e}:{pixel:e}}return{pixel:0}}function l_(t,e){const n=Fi(e),r=n.percent!==void 0?t.width/100*n.percent:n.pixel;return{width:Math.max(t.width-2*r,0),height:Math.max(t.height-2*r,0)}}function c_(){return(dc()?window?.devicePixelRatio:void 0)||1}function hc(t,e){return e>0?(t%e+e)%e:0}function gc(t){return t.length>0}function pc(t,e){return t[hc(e,t.length)]}function mc(t,e){return gc(t)?pc(t,e):void 0}function bg(t){return or(t)?t.src:void 0}function zi(t,e,n){if(!n)return t;const{buttons:r,...s}=t,o=r.findIndex(a=>a===e),i=F.isValidElement(n)?F.cloneElement(n,{key:e},null):n;if(o>=0){const a=[...r];return a.splice(o,1,i),{buttons:a,...s}}return{buttons:[i,...r],...s}}function yg(t,e,n=0){return Math.min(t.preload,Math.max(t.finite?e.length-1:Math.floor(e.length/2),n))}const d_=Number(F.version.split(".")[0])>=19;function u_(t){return{inert:d_?t:t?"":void 0}}function f_(t){t.scrollTop}const bc={open:!1,close:()=>{},index:0,slides:[],render:{},plugins:[],toolbar:{buttons:[ts]},labels:{},animation:{fade:250,swipe:500,easing:{fade:"ease",swipe:"ease-out",navigation:"ease-in-out"}},carousel:{finite:!1,preload:2,padding:"16px",spacing:"30%",imageFit:fg,imageProps:{}},controller:{ref:null,focus:!0,aria:!1,touchAction:"none",closeOnPullUp:!1,closeOnPullDown:!1,closeOnBackdropClick:!1,closeOnEscape:!0,preventDefaultWheelX:!0,preventDefaultWheelY:!1,disableSwipeNavigation:!1},portal:{},noScroll:{disabled:!1},on:{},styles:{},className:""};function un(t,e){return{name:t,component:e}}function Tt(t,e){return{module:t,children:e}}function vg(t,e,n){return t.module.name===e?n(t):t.children?[Tt(t.module,t.children.flatMap(r=>{var s;return(s=vg(r,e,n))!==null&&s!==void 0?s:[]}))]:[t]}function rs(t,e,n){return t.flatMap(r=>{var s;return(s=vg(r,e,n))!==null&&s!==void 0?s:[]})}function h_(t,e=[],n=[]){let r=t;const s=g=>{const m=[...r];for(;m.length>0;){const p=m.pop();if(p?.module.name===g)return!0;p?.children&&m.push(...p.children)}return!1},o=(g,m)=>{if(g===""){r=[Tt(m,r)];return}r=rs(r,g,p=>[Tt(m,[p])])},i=(g,m)=>{r=rs(r,g,p=>[Tt(p.module,[Tt(m,p.children)])])},a=(g,m,p)=>{r=rs(r,g,y=>{var b;return[Tt(y.module,[...p?[Tt(m)]:[],...(b=y.children)!==null&&b!==void 0?b:[],...p?[]:[Tt(m)]])]})},l=(g,m,p)=>{r=rs(r,g,y=>[...p?[Tt(m)]:[],y,...p?[]:[Tt(m)]])},c=g=>{i(to,g)},d=(g,m)=>{r=rs(r,g,p=>[Tt(m,p.children)])},u=g=>{r=rs(r,g,m=>m.children)},f=g=>{n.push(g)};return e.forEach(g=>{g({contains:s,addParent:o,append:i,addChild:a,addSibling:l,addModule:c,replace:d,remove:u,augment:f})}),{config:r,augmentation:g=>n.reduce((m,p)=>p(m),g)}}const xg=F.createContext(null),yc=ln("useA11yContext","A11yContext",xg);function g_({children:t}){const[e,n]=F.useState(!1),[r,s]=F.useState(!1),o=F.useMemo(()=>({focusWithin:e,trackFocusWithin:(a,l)=>{const c=d=>u=>{var f;u.currentTarget.contains(u.relatedTarget)||n(d),(f=d?a:l)===null||f===void 0||f(u)};return{onFocus:c(!0),onBlur:c(!1)}},autoPlaying:r,setAutoPlaying:s}),[e,r]);return F.createElement(xg.Provider,{value:o},t)}const wg=F.createContext(null),ss=ln("useDocument","DocumentContext",wg);function p_({nodeRef:t,children:e}){const n=F.useMemo(()=>{const r=o=>{var i;return((i=o||t.current)===null||i===void 0?void 0:i.ownerDocument)||document};return{getOwnerDocument:r,getOwnerWindow:o=>{var i;return((i=r(o))===null||i===void 0?void 0:i.defaultView)||window}}},[t]);return F.createElement(wg.Provider,{value:n},e)}const kg=F.createContext(null),os=ln("useEvents","EventsContext",kg);function m_({children:t}){const[e]=F.useState({});F.useEffect(()=>()=>{Object.keys(e).forEach(r=>delete e[r])},[e]);const n=F.useMemo(()=>{const r=(i,a)=>{var l;(l=e[i])===null||l===void 0||l.splice(0,e[i].length,...e[i].filter(c=>c!==a))};return{publish:(...[i,a])=>{var l;(l=e[i])===null||l===void 0||l.forEach(c=>c(a))},subscribe:(i,a)=>(e[i]||(e[i]=[]),e[i].push(a),()=>r(i,a)),unsubscribe:r}},[e]);return F.createElement(kg.Provider,{value:n},t)}const Cg=F.createContext(null),At=ln("useLightboxProps","LightboxPropsContext",Cg);function Sg({children:t,...e}){return F.createElement(Cg.Provider,{value:e},t)}const _g=F.createContext(null),fn=ln("useLightboxState","LightboxStateContext",_g),Eg=F.createContext(null),b_=ln("useLightboxDispatch","LightboxDispatchContext",Eg);function y_(t,e){switch(e.type){case"swipe":{const{slides:n}=t,r=e?.increment||0,s=t.globalIndex+r,o=hc(s,n.length),i=mc(n,o),a=r||e.duration!==void 0?{increment:r,duration:e.duration,easing:e.easing}:void 0;return{slides:n,currentIndex:o,globalIndex:s,currentSlide:i,animation:a}}case"update":return e.slides!==t.slides||e.index!==t.currentIndex?{slides:e.slides,currentIndex:e.index,globalIndex:e.index,currentSlide:mc(e.slides,e.index)}:t;default:throw new Error(gg)}}function v_({slides:t,index:e,children:n}){const[r,s]=F.useReducer(y_,{slides:t,currentIndex:e,globalIndex:e,currentSlide:mc(t,e)}),[o,i]=F.useState(t),[a,l]=F.useState(e);(t!==o||e!==a)&&(i(t),l(e),s({type:"update",slides:t,index:e}));const c=F.useMemo(()=>({...r,state:r,dispatch:s}),[r,s]);return F.createElement(Eg.Provider,{value:s},F.createElement(_g.Provider,{value:c},n))}const Mg=F.createContext(null),x_=ln("useRTLContext","RTLContext",Mg);function w_({isRTL:t,children:e}){const n=F.useMemo(()=>({isRTL:t}),[t]);return F.createElement(Mg.Provider,{value:n},e)}const Ng=F.createContext(null),ao=ln("useTimeouts","TimeoutsContext",Ng);function k_({children:t}){const[e]=F.useState([]);F.useEffect(()=>()=>{e.forEach(r=>window.clearTimeout(r)),e.splice(0,e.length)},[e]);const n=F.useMemo(()=>{const r=i=>{e.splice(0,e.length,...e.filter(a=>a!==i))};return{setTimeout:(i,a)=>{const l=window.setTimeout(()=>{r(l),i()},a);return e.push(l),l},clearTimeout:i=>{i!==void 0&&(r(i),window.clearTimeout(i))}}},[e]);return F.createElement(Ng.Provider,{value:n},t)}const is=F.forwardRef(function({label:e,className:n,icon:r,renderIcon:s,onClick:o,style:i,...a},l){const{styles:c,labels:d}=At(),u=br(d,e);return F.createElement("button",{ref:l,type:"button",title:u,"aria-label":u,className:zt(Ne(a_),n),onClick:o,style:{...i,...c.button},...a},s?s():F.createElement(r,{className:Ne(Pi),style:c.icon}))});function Og(t,e){const n=r=>F.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",...r},e);return n.displayName=t,n}function Jt(t,e){return Og(t,F.createElement("g",{fill:"currentColor"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"none"}),e))}function C_(t,e){return Og(t,F.createElement(F.Fragment,null,F.createElement("defs",null,F.createElement("mask",{id:"strike"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"white"}),F.createElement("path",{d:"M0 0L24 24",stroke:"black",strokeWidth:4}))),F.createElement("path",{d:"M0.70707 2.121320L21.878680 23.292883",stroke:"currentColor",strokeWidth:2}),F.createElement("g",{fill:"currentColor",mask:"url(#strike)"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"none"}),e)))}const S_=Jt("Close",F.createElement("path",{d:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"})),__=Jt("Previous",F.createElement("path",{d:"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"})),E_=Jt("Next",F.createElement("path",{d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"})),M_=Jt("Loading",F.createElement(F.Fragment,null,Array.from({length:8}).map((t,e,n)=>F.createElement("line",{key:e,x1:"12",y1:"6.5",x2:"12",y2:"1.8",strokeLinecap:"round",strokeWidth:"2.6",stroke:"currentColor",strokeOpacity:1/n.length*(e+1),transform:`rotate(${360/n.length*e}, 12, 12)`})))),N_=Jt("Error",F.createElement("path",{d:"M21.9,21.9l-8.49-8.49l0,0L3.59,3.59l0,0L2.1,2.1L0.69,3.51L3,5.83V19c0,1.1,0.9,2,2,2h13.17l2.31,2.31L21.9,21.9z M5,18 l3.5-4.5l2.5,3.01L12.17,15l3,3H5z M21,18.17L5.83,3H19c1.1,0,2,0.9,2,2V18.17z"})),Cn=dc()?F.useLayoutEffect:F.useEffect;function vc(){const[t,e]=F.useState(!1);return F.useEffect(()=>{var n,r;const s=(n=window.matchMedia)===null||n===void 0?void 0:n.call(window,"(prefers-reduced-motion: reduce)");e(s?.matches);const o=i=>e(i.matches);return(r=s?.addEventListener)===null||r===void 0||r.call(s,"change",o),()=>{var i;return(i=s?.removeEventListener)===null||i===void 0?void 0:i.call(s,"change",o)}},[]),t}function O_(t){let e=0,n=0,r=0;const o=window.getComputedStyle(t).transform.match(/matrix.*\((.+)\)/);if(o){const i=o[1].split(",").map($i);i.length===6?(e=i[4],n=i[5]):i.length===16&&(e=i[12],n=i[13],r=i[14])}return{x:e,y:n,z:r}}function xc(t,e){const n=F.useRef(void 0),r=F.useRef(void 0),s=vc();return Cn(()=>{var o,i,a;if(t.current&&n.current!==void 0&&!s){const{keyframes:l,duration:c,easing:d,onfinish:u}=e(n.current,t.current.getBoundingClientRect(),O_(t.current))||{};if(l&&c){(o=r.current)===null||o===void 0||o.cancel(),r.current=void 0;try{r.current=(a=(i=t.current).animate)===null||a===void 0?void 0:a.call(i,l,{duration:c,easing:d})}catch(f){console.error(f)}r.current&&(r.current.onfinish=()=>{r.current=void 0,u?.()})}}n.current=void 0}),{prepareAnimation:o=>{n.current=o},isAnimationPlaying:()=>{var o;return((o=r.current)===null||o===void 0?void 0:o.playState)==="running"}}}function Tg(){const t=F.useRef(null),e=F.useRef(void 0),[n,r]=F.useState();return{setContainerRef:F.useCallback(o=>{t.current=o,e.current&&(e.current.disconnect(),e.current=void 0);const i=()=>{if(o){const a=window.getComputedStyle(o),l=c=>parseFloat(c)||0;r({width:Math.round(o.clientWidth-l(a.paddingLeft)-l(a.paddingRight)),height:Math.round(o.clientHeight-l(a.paddingTop)-l(a.paddingBottom))})}else r(void 0)};i(),o&&typeof ResizeObserver<"u"&&(e.current=new ResizeObserver(i),e.current.observe(o))},[]),containerRef:t,containerRect:n}}function Vi(){const t=F.useRef(void 0),{setTimeout:e,clearTimeout:n}=ao();return F.useCallback((r,s)=>{n(t.current),t.current=e(r,s>0?s:0)},[e,n])}function We(t){const e=F.useRef(t);return Cn(()=>{e.current=t}),F.useCallback((...n)=>{var r;return(r=e.current)===null||r===void 0?void 0:r.call(e,...n)},[])}function Ag(t,e){typeof t=="function"?t(e):t&&(t.current=e)}function wc(t,e){return F.useMemo(()=>t==null&&e==null?null:n=>{Ag(t,n),Ag(e,n)},[t,e])}function Dg(t,e=!1){const n=F.useRef(!1);Cn(()=>{e&&n.current&&(n.current=!1,t())},[e,t]);const r=F.useCallback(()=>{n.current=!0},[]),s=F.useCallback(()=>{n.current=!1},[]);return{onFocus:r,onBlur:s}}function Hi(){return x_().isRTL}function Ig(){const[t]=F.useState({}),e=F.useCallback((s,o)=>{var i;(i=t[s])===null||i===void 0||i.forEach(a=>{o.isPropagationStopped()||a(o)})},[t]),n=F.useMemo(()=>({onPointerDown:s=>e(ag,s),onPointerMove:s=>e(lg,s),onPointerUp:s=>e(cg,s),onPointerLeave:s=>e(dg,s),onPointerCancel:s=>e(ug,s),onKeyDown:s=>e(lc,s),onKeyUp:s=>e(r_,s),onWheel:s=>e(cc,s)}),[e]),r=F.useCallback((s,o)=>(t[s]||(t[s]=[]),t[s].unshift(o),()=>{const i=t[s];i&&i.splice(0,i.length,...i.filter(a=>a!==o))}),[t]);return{registerSensors:n,subscribeSensors:r}}function Lg(t,e){const n=F.useRef(0),r=Vi(),s=We((...o)=>{n.current=Date.now(),t(o)});return F.useCallback((...o)=>{r(()=>{s(o)},e-(Date.now()-n.current))},[e,s,r])}const kc=mr("slide"),Cc=mr("slide_image");function yr({slide:t,offset:e,render:n,rect:r,imageFit:s,imageProps:o,onClick:i,onLoad:a,onError:l,style:c}){var d,u,f,g,m,p,y,b;const[_,x]=F.useState(Rr),{publish:E}=os(),{setTimeout:C}=ao(),k=F.useRef(null);F.useEffect(()=>{e===0&&E(io(_))},[e,_,E]);const N=We(U=>{("decode"in U?U.decode():Promise.resolve()).catch(()=>{}).then(()=>{U.parentNode&&(x(oo),C(()=>{a?.(U)},0))})}),S=F.useCallback(U=>{k.current=U,U?.complete&&N(U)},[N]),w=F.useCallback(U=>{N(U.currentTarget)},[N]),M=We(()=>{x(so),l?.()}),T=fc(t,s),P=(U,B)=>Number.isFinite(U)?U:B,L=P(Math.max(...((u=(d=t.srcSet)===null||d===void 0?void 0:d.map(U=>U.width))!==null&&u!==void 0?u:[]).concat(t.width?[t.width]:[]).filter(Boolean)),((f=k.current)===null||f===void 0?void 0:f.naturalWidth)||0),$=P(Math.max(...((m=(g=t.srcSet)===null||g===void 0?void 0:g.map(U=>U.height))!==null&&m!==void 0?m:[]).concat(t.height?[t.height]:[]).filter(Boolean)),((p=k.current)===null||p===void 0?void 0:p.naturalHeight)||0),A=L&&$?{maxWidth:`min(${L}px, 100%)`,maxHeight:`min(${$}px, 100%)`}:{maxWidth:"100%",maxHeight:"100%"},D=(y=t.srcSet)===null||y===void 0?void 0:y.slice().sort((U,B)=>U.width-B.width).map(U=>`${U.src} ${U.width}w`).join(", "),O=()=>r&&!T&&t.width&&t.height?r.height/t.height*t.width:Number.MAX_VALUE,z=D&&r&&dc()?`${Math.round(Math.min(O(),r.width))}px`:void 0,{style:H,className:j,...W}=(typeof o=="function"?o(t):o)||{};return F.createElement(F.Fragment,null,F.createElement("img",{ref:S,onLoad:w,onError:M,onClick:i,draggable:!1,className:zt(Ne(Cc()),T&&Ne(Cc("cover")),_!==oo&&Ne(Cc("loading")),j),style:{...A,...c,...H},...W,alt:(b=t.alt)!==null&&b!==void 0?b:"",sizes:z,srcSet:D,src:t.src}),_!==oo&&F.createElement("div",{className:Ne(kc(ZS))},_===Rr&&(n?.iconLoading?n.iconLoading():F.createElement(M_,{className:zt(Ne(Pi),Ne(kc(Rr)))})),_===so&&(n?.iconError?n.iconError():F.createElement(N_,{className:zt(Ne(Pi),Ne(kc(so)))}))))}const T_=F.forwardRef(function({className:e,children:n,onFocus:r,onBlur:s,...o},i){const a=F.useRef(null),[l,c]=F.useState(!1),{trackFocusWithin:d}=yc(),u=We(()=>{if(a.current){const f=window.getComputedStyle(a.current).direction==="rtl";f!==l&&c(f)}});return F.useEffect(u),F.createElement(p_,{nodeRef:a},F.createElement(w_,{isRTL:l},F.createElement("div",{ref:wc(i,a),className:zt(Ne("root"),e),...d(r,s),...o},n)))});var Vt;(function(t){t[t.NONE=0]="NONE",t[t.SWIPE=1]="SWIPE",t[t.PULL=2]="PULL",t[t.ANIMATION=3]="ANIMATION"})(Vt||(Vt={}));function Pg(t,e,n,r,s){F.useEffect(()=>s?()=>{}:ns(t(ag,e),t(lg,n),t(cg,r),t(dg,r),t(ug,r)),[t,e,n,r,s])}var Sn;(function(t){t[t.NONE=0]="NONE",t[t.SWIPE=1]="SWIPE",t[t.PULL=2]="PULL"})(Sn||(Sn={}));const Sc=30;function A_({disableSwipeNavigation:t,closeOnBackdropClick:e},n,r,s,o,i,a,l,c,d,u,f,g,m,p,y){const b=F.useRef(0),_=F.useRef([]),x=F.useRef(void 0),E=F.useRef(0),C=F.useRef(Sn.NONE),k=F.useCallback(L=>{x.current===L.pointerId&&(x.current=void 0,C.current=Sn.NONE);const $=_.current;$.splice(0,$.length,...$.filter(A=>A.pointerId!==L.pointerId))},[]),N=F.useCallback(L=>{k(L),L.persist(),_.current.push(L)},[k]),S=F.useCallback(L=>_.current.find(({pointerId:$})=>L.pointerId===$),[]),w=We(L=>{N(L)}),M=(L,$)=>u&&L>$||d&&L<-$,T=We(L=>{const $=S(L);if($)if(x.current===L.pointerId){const A=Date.now()-E.current,D=b.current;C.current===Sn.SWIPE?Math.abs(D)>.3*s||Math.abs(D)>5&&A{const $=S(L);if($){const A=x.current===L.pointerId;if(L.buttons===0){A&&b.current!==0?T(L):k($);return}const D=L.clientX-$.clientX,O=L.clientY-$.clientY;if(x.current===void 0){const z=H=>{N(L),x.current=L.pointerId,E.current=Date.now(),C.current=H};Math.abs(D)>Math.abs(O)&&Math.abs(D)>Sc&&r(D)?t||(z(Sn.SWIPE),i()):Math.abs(O)>Math.abs(D)&&M(O,Sc)&&(z(Sn.PULL),f())}else A&&(C.current===Sn.SWIPE?(b.current=D,a(D)):C.current===Sn.PULL&&(b.current=O,g(O)))}});Pg(n,w,P,T)}function D_({preventDefaultWheelX:t,preventDefaultWheelY:e}){const n=F.useRef(null),r=We(s=>{const o=Math.abs(s.deltaX)>Math.abs(s.deltaY);(o&&t||!o&&e||s.ctrlKey)&&s.preventDefault()});return F.useCallback(s=>{var o;s?s.addEventListener("wheel",r,{passive:!1}):(o=n.current)===null||o===void 0||o.removeEventListener("wheel",r),n.current=s},[r])}function I_(t,e,n,r,s,o,i,a,l){const c=F.useRef(0),d=F.useRef(0),u=F.useRef(void 0),f=F.useRef(void 0),g=F.useRef(0),m=F.useRef(void 0),p=F.useRef(0),{setTimeout:y,clearTimeout:b}=ao(),_=F.useCallback(()=>{u.current&&(b(u.current),u.current=void 0)},[b]),x=F.useCallback(()=>{f.current&&(b(f.current),f.current=void 0)},[b]),E=We(()=>{t!==Vt.SWIPE&&(c.current=0,p.current=0,_(),x())});F.useEffect(E,[t,E]);const C=We(N=>{f.current=void 0,c.current===N&&l(c.current)}),k=We(N=>{if(N.ctrlKey||Math.abs(N.deltaY)>Math.abs(N.deltaX))return;const S=w=>{g.current=w,b(m.current),m.current=w>0?y(()=>{g.current=0,m.current=void 0},300):void 0};if(t===Vt.NONE){if(Math.abs(N.deltaX)<=1.2*Math.abs(g.current)){S(N.deltaX);return}if(!n(-N.deltaX))return;if(d.current+=N.deltaX,_(),Math.abs(d.current)>30)d.current=0,S(0),p.current=Date.now(),o();else{const w=d.current;u.current=y(()=>{u.current=void 0,w===d.current&&(d.current=0)},s)}}else if(t===Vt.SWIPE){let w=c.current-N.deltaX;if(w=Math.min(Math.abs(w),r)*Math.sign(w),c.current=w,i(w),x(),Math.abs(w)>.2*r){S(N.deltaX),a(w,Date.now()-p.current);return}f.current=y(()=>C(w),2*s)}else S(N.deltaX)});F.useEffect(()=>e(cc,k),[e,k])}const $g=mr("container"),Fg=F.createContext(null),_n=ln("useController","ControllerContext",Fg);function L_({children:t,...e}){var n;const{carousel:r,animation:s,controller:o,on:i,styles:a,render:l}=e,{closeOnPullUp:c,closeOnPullDown:d,preventDefaultWheelX:u,preventDefaultWheelY:f}=o,[g,m]=F.useState(),p=fn(),y=b_(),[b,_]=F.useState(Vt.NONE),x=F.useRef(0),E=F.useRef(0),C=F.useRef(1),{registerSensors:k,subscribeSensors:N}=Ig(),{subscribe:S,publish:w}=os(),M=Vi(),T=Vi(),P=Vi(),{containerRef:L,setContainerRef:$,containerRect:A}=Tg(),D=wc(D_({preventDefaultWheelX:u,preventDefaultWheelY:f}),$),O=F.useRef(null),z=wc(O,void 0),{getOwnerDocument:H}=ss(),j=Hi(),W=se=>(j?-1:1)*(typeof se=="number"?se:1),U=We(()=>{var se;return(se=L.current)===null||se===void 0?void 0:se.focus()}),B=We(()=>e),G=We(()=>p),V=F.useCallback(se=>w(rr,se),[w]),X=F.useCallback(se=>w(sr,se),[w]),K=F.useCallback(()=>w(ts),[w]),Z=se=>!(r.finite&&(W(se)>0&&p.currentIndex===0||W(se)<0&&p.currentIndex===p.slides.length-1)),ee=se=>{var Oe;x.current=se,(Oe=L.current)===null||Oe===void 0||Oe.style.setProperty(dt("swipe_offset"),`${Math.round(se)}px`)},ne=se=>{var Oe,Le;E.current=se,C.current=(()=>{const st=d&&se>0?se:c&&se<0?-se:0;return Math.min(Math.max(uc(1-st/60*(1-.5),2),.5),1)})(),(Oe=L.current)===null||Oe===void 0||Oe.style.setProperty(dt("pull_offset"),`${Math.round(se)}px`),(Le=L.current)===null||Le===void 0||Le.style.setProperty(dt("pull_opacity"),`${C.current}`)},{prepareAnimation:he}=xc(O,(se,Oe,Le)=>{if(O.current&&A)return{keyframes:[{transform:`translate(0, ${se.rect.y-Oe.y+Le.y}px)`,opacity:se.opacity},{transform:"translate(0, 0)",opacity:1}],duration:se.duration,easing:s.easing.fade}}),J=(se,Oe)=>{if(c||d){ne(se);let Le=0;O.current&&(Le=s.fade*(Oe?2:1),he({rect:O.current.getBoundingClientRect(),opacity:C.current,duration:Le})),P(()=>{ne(0),_(Vt.NONE)},Le),_(Vt.ANIMATION),Oe||K()}},{prepareAnimation:le,isAnimationPlaying:ue}=xc(O,(se,Oe,Le)=>{var Ie;if(O.current&&A&&(!((Ie=p.animation)===null||Ie===void 0)&&Ie.duration)){const Be=Fi(r.spacing),st=(Be.percent?Be.percent*A.width/100:Be.pixel)||0;return{keyframes:[{transform:`translate(${W(p.globalIndex-se.index)*(A.width+st)+se.rect.x-Oe.x+Le.x}px, 0)`},{transform:"translate(0, 0)"}],duration:p.animation.duration,easing:p.animation.easing}}}),ae=We(se=>{var Oe,Le;const Ie=se.offset||0,Be=Ie?s.swipe:(Oe=s.navigation)!==null&&Oe!==void 0?Oe:s.swipe,st=!Ie&&!ue()?s.easing.navigation:s.easing.swipe;let{direction:ut}=se;const je=(Le=se.count)!==null&&Le!==void 0?Le:1;let nt=Vt.ANIMATION,Ye=Be*je;if(!ut){const ge=A?.width,me=se.duration||0,pe=ge?Be/ge*Math.abs(Ie):Be;je!==0?(me0?rr:sr):Ye=Be/2}let te=0;ut===rr?Z(W(1))?te=-je:(nt=Vt.NONE,Ye=Be):ut===sr&&(Z(W(-1))?te=je:(nt=Vt.NONE,Ye=Be)),Ye=Math.round(Ye),T(()=>{ee(0),_(Vt.NONE)},Ye),O.current&&le({rect:O.current.getBoundingClientRect(),index:p.globalIndex}),_(nt),w(ac,{type:"swipe",increment:te,duration:Ye,easing:st})});F.useEffect(()=>{var se,Oe;!((se=p.animation)===null||se===void 0)&&se.increment&&(!((Oe=p.animation)===null||Oe===void 0)&&Oe.duration)&&M(()=>y({type:"swipe",increment:0}),p.animation.duration)},[p.animation,y,M]);const ve=[N,Z,A?.width||0,s.swipe,()=>_(Vt.SWIPE),se=>ee(se),(se,Oe)=>ae({offset:se,duration:Oe,count:1}),se=>ae({offset:se,count:0})],fe=[()=>{d&&_(Vt.PULL)},se=>ne(se),se=>J(se),se=>J(se,!0)];A_(o,...ve,c,d,...fe,K),I_(b,...ve);const ye=We(()=>{o.focus&&H().querySelector(`.${Ne(sc)} .${Ne($g())}`)&&U()});F.useEffect(ye,[ye]);const we=We(()=>{var se;(se=i.view)===null||se===void 0||se.call(i,{index:p.currentIndex})});F.useEffect(we,[p.globalIndex,we]),F.useEffect(()=>ns(S(rr,se=>ae({direction:rr,...se})),S(sr,se=>ae({direction:sr,...se})),S(ac,se=>y(se))),[S,ae,y]);const He=F.useMemo(()=>({prev:V,next:X,close:K,focus:U,slideRect:A?l_(A,r.padding):{width:0,height:0},containerRect:A||{width:0,height:0},subscribeSensors:N,containerRef:L,setCarouselRef:z,toolbarWidth:g,setToolbarWidth:m}),[V,X,K,U,N,A,L,z,g,m,r.padding]);return F.useImperativeHandle(o.ref,()=>({prev:V,next:X,close:K,focus:U,getLightboxProps:B,getLightboxState:G}),[V,X,K,U,B,G]),F.createElement("div",{ref:D,className:zt(Ne($g()),Ne(es)),style:{...b===Vt.SWIPE?{[dt("swipe_offset")]:`${Math.round(x.current)}px`}:null,...b===Vt.PULL?{[dt("pull_offset")]:`${Math.round(E.current)}px`,[dt("pull_opacity")]:`${C.current}`}:null,...o.touchAction!=="none"?{[dt("controller_touch_action")]:o.touchAction}:null,...a.container},tabIndex:-1,...k},A&&F.createElement(Fg.Provider,{value:He},t,(n=l.controls)===null||n===void 0?void 0:n.call(l)))}const P_=un(to,L_);function vr(t){return pr(eg,t)}function zg(t){return pr(ic,t)}function $_({slide:t,offset:e}){const n=F.useRef(null),{currentIndex:r,slides:s}=fn(),{slideRect:o,focus:i}=_n(),{render:a,carousel:{imageFit:l,imageProps:c},on:{click:d},styles:{slide:u},labels:f}=At(),{getOwnerDocument:g}=ss(),m=e!==0;F.useEffect(()=>{var y;m&&(!((y=n.current)===null||y===void 0)&&y.contains(g().activeElement))&&i()},[m,i,g]);const p=()=>{var y,b,_,x;let E=(y=a.slide)===null||y===void 0?void 0:y.call(a,{slide:t,offset:e,rect:o});return!E&&or(t)&&(E=F.createElement(yr,{slide:t,offset:e,render:a,rect:o,imageFit:l,imageProps:c,onClick:m?void 0:()=>d?.({index:r})})),E?F.createElement(F.Fragment,null,(b=a.slideHeader)===null||b===void 0?void 0:b.call(a,{slide:t}),((_=a.slideContainer)!==null&&_!==void 0?_:(({children:C})=>C))({slide:t,children:E}),(x=a.slideFooter)===null||x===void 0?void 0:x.call(a,{slide:t})):null};return F.createElement("div",{ref:n,className:zt(Ne(zg()),!m&&Ne(zg("current")),Ne(es)),...u_(m),style:u,role:"group","aria-roledescription":br(f,"Slide"),"aria-label":mg(f,s,r+e)},p())}function F_(){const t=At().styles.slide;return F.createElement("div",{className:Ne(ic),style:t})}function z_({carousel:t,labels:e}){const{slides:n,currentIndex:r,globalIndex:s}=fn(),{setCarouselRef:o}=_n(),{autoPlaying:i,focusWithin:a}=yc(),l=Fi(t.spacing),c=Fi(t.padding),d=yg(t,n,1),u=[];if(gc(n))for(let f=r-d;f<=r+d;f+=1){const g=pc(n,f),m=s-r+f,p=t.finite&&(f<0||f>n.length-1);u.push(p?{key:m}:{key:[`${m}`,bg(g)].filter(Boolean).join("|"),offset:f-r,slide:g})}return F.createElement("div",{ref:o,className:zt(Ne(vr()),u.length>0&&Ne(vr("with_slides"))),style:{[`${dt(vr("slides_count"))}`]:u.length,[`${dt(vr("spacing_px"))}`]:l.pixel||0,[`${dt(vr("spacing_percent"))}`]:l.percent||0,[`${dt(vr("padding_px"))}`]:c.pixel||0,[`${dt(vr("padding_percent"))}`]:c.percent||0},role:"region","aria-live":i&&!a?"off":"polite","aria-roledescription":br(e,"Carousel"),"aria-label":br(e,"Photo gallery")},u.map(({key:f,slide:g,offset:m})=>g?F.createElement($_,{key:f,slide:g,offset:m}):F.createElement(F_,{key:f})))}const V_=un(eg,z_);function Vg(){const{carousel:t}=At(),{slides:e,currentIndex:n}=fn(),r=e.length===0||t.finite&&n===0,s=e.length===0||t.finite&&n===e.length-1;return{prevDisabled:r,nextDisabled:s}}function Hg(t){var e;const n=Hi(),{publish:r}=os(),{animation:s,controller:o}=At(),{prevDisabled:i,nextDisabled:a}=Vg(),l=((e=s.navigation)!==null&&e!==void 0?e:s.swipe)/2,c=Lg(()=>r(rr),l),d=Lg(()=>r(sr),l),u=We(f=>{switch(f.key){case s_:if(!o.closeOnEscape)return;r(ts);break;case o_:(n?a:i)||(n?d:c)();break;case i_:(n?i:a)||(n?c:d)();break;default:return}f.stopPropagation()});F.useEffect(()=>t(lc,u),[t,u])}function Ug({label:t,icon:e,renderIcon:n,action:r,onClick:s,disabled:o,style:i}){return F.createElement(is,{label:t,icon:e,renderIcon:n,className:Ne(`navigation_${r}`),disabled:o,onClick:s,style:i,...Dg(_n().focus,o)})}function H_({render:{buttonPrev:t,buttonNext:e,iconPrev:n,iconNext:r},styles:s}){const{prev:o,next:i,subscribeSensors:a}=_n(),{prevDisabled:l,nextDisabled:c}=Vg();return Hg(a),F.createElement(F.Fragment,null,t?t():F.createElement(Ug,{label:"Previous",action:rr,icon:__,renderIcon:n,style:s.navigationPrev,disabled:l,onClick:o}),e?e():F.createElement(Ug,{label:"Next",action:sr,icon:E_,renderIcon:r,style:s.navigationNext,disabled:c,onClick:i}))}const U_=un(YS,H_),Bg=Ne(t_),B_=Ne(og);function j_(t){return"style"in t}function jg(t,e,n){const r=window.getComputedStyle(t),s=n?"padding-left":"padding-right",o=n?r.paddingLeft:r.paddingRight,i=t.style.getPropertyValue(s);return t.style.setProperty(s,`${($i(o)||0)+e}px`),()=>{i?t.style.setProperty(s,i):t.style.removeProperty(s)}}function W_({noScroll:{disabled:t},children:e}){const n=Hi(),{getOwnerDocument:r,getOwnerWindow:s}=ss();return F.useEffect(()=>{if(t)return()=>{};const o=[],i=s(),{body:a,documentElement:l}=r(),c=Math.round(i.innerWidth-l.clientWidth);if(c>0){o.push(jg(a,c,n));const d=a.getElementsByTagName("*");for(let u=0;u{a.classList.remove(Bg),o.forEach(d=>d())}},[n,t,r,s]),F.createElement(F.Fragment,null,e)}const G_=un(KS,W_);function Wg(t){return pr(sc,t)}function Gg(t,e,n){const r=t.getAttribute(e);return t.setAttribute(e,n),()=>{r?t.setAttribute(e,r):t.removeAttribute(e)}}function Y_({portal:{root:t,container:{className:e,style:n,...r}={}},animation:s,styles:o,className:i,on:a,close:l,labels:c,children:d}){const[u,f]=F.useState(!1),[g,m]=F.useState(!1),p=F.useRef([]),y=F.useRef(null),{setTimeout:b}=ao(),{subscribe:_}=os(),E=vc()?0:s.fade;F.useEffect(()=>(f(!0),()=>{f(!1),m(!1)}),[]);const C=We(()=>{p.current.forEach(w=>w()),p.current=[]}),k=We(()=>{var w;m(!1),C(),(w=a.exiting)===null||w===void 0||w.call(a),b(()=>{var M;(M=a.exited)===null||M===void 0||M.call(a),l()},E)});F.useEffect(()=>_(ts,k),[_,k]);const N=We(w=>{var M,T,P;f_(w),m(!0),(M=a.entering)===null||M===void 0||M.call(a);const L=(P=(T=w.parentNode)===null||T===void 0?void 0:T.children)!==null&&P!==void 0?P:[];for(let $=0;${var $,A;(A=($=y.current)===null||$===void 0?void 0:$.focus)===null||A===void 0||A.call($)}),b(()=>{var $;($=a.entered)===null||$===void 0||$.call(a)},E)}),S=F.useCallback(w=>{w?N(w):C()},[N,C]);return u?Tn.createPortal(F.createElement(T_,{ref:S,className:zt(i,e,Ne(Wg()),Ne(og),g&&Ne(Wg("open"))),"aria-modal":!0,role:"dialog","aria-label":br(c,"Lightbox"),style:{...s.fade!==bc.animation.fade?{[dt("fade_animation_duration")]:`${E}ms`}:null,...s.easing.fade!==bc.animation.easing.fade?{[dt("fade_animation_timing_function")]:s.easing.fade}:null,...o.root,...n},onFocus:w=>{y.current||(y.current=w.relatedTarget)},...r},d),(typeof t=="function"?t():t)||document.body):null}const K_=un(sc,Y_);function X_({children:t}){return F.createElement(F.Fragment,null,t)}const q_=un(XS,X_);function Z_(t){return pr(tg,t)}function Q_({toolbar:{buttons:t},render:{buttonClose:e,iconClose:n},styles:r}){const{close:s,setToolbarWidth:o}=_n(),{setContainerRef:i,containerRect:a}=Tg();Cn(()=>{o(a?.width)},[o,a?.width]);const l=()=>e?e():F.createElement(is,{key:ts,label:"Close",icon:S_,renderIcon:n,onClick:s});return F.createElement("div",{ref:i,style:r.toolbar,className:Ne(Z_())},t?.map(c=>c===ts?l():c))}const J_=un(tg,Q_);function Yg(t,e){var n;return F.createElement(t.module.component,{key:t.module.name,...e},(n=t.children)===null||n===void 0?void 0:n.map(r=>Yg(r,e)))}function R_(t,e={}){const{easing:n,...r}=t,{easing:s,...o}=e;return{easing:{...n,...s},...r,...o}}function e2({carousel:t,animation:e,render:n,toolbar:r,controller:s,noScroll:o,on:i,plugins:a,slides:l,index:c,...d}){const{animation:u,carousel:f,render:g,toolbar:m,controller:p,noScroll:y,on:b,slides:_,index:x,plugins:E,...C}=bc,{config:k,augmentation:N}=h_([Tt(K_,[Tt(G_,[Tt(P_,[Tt(V_),Tt(J_),Tt(U_)])])])],a||E),S=N({animation:R_(u,e),carousel:{...f,...t},render:{...g,...n},toolbar:{...m,...r},controller:{...p,...s},noScroll:{...y,...o},on:{...b,...i},...C,...d});return S.open?F.createElement(Sg,{...S},F.createElement(v_,{slides:l||_,index:$i(c||x)},F.createElement(k_,null,F.createElement(m_,null,F.createElement(g_,null,Yg(Tt(q_,k),S)))))):null}const Kg={minZoom:1,maxZoomPixelRatio:1,zoomInMultiplier:2,doubleTapDelay:300,doubleClickDelay:500,doubleClickMaxStops:2,keyboardMoveDistance:50,wheelZoomDistanceFactor:100,pinchZoomDistanceFactor:100,pinchZoomV4:!1,scrollToZoom:!1,maxZoom:8};function t2(t){return Math.min(Math.max(t,Number.EPSILON),1)}function Xg(t){const{minZoom:e,...n}={...Kg,...t};return{minZoom:t2(e),...n}}function n2(t,e,n,r){const s=F.useRef(void 0),o=F.useRef(void 0),{zoom:i}=At().animation,a=vc(),l=We(()=>{var c,d,u;if((c=s.current)===null||c===void 0||c.cancel(),s.current=void 0,o.current&&r?.current){try{s.current=(u=(d=r.current).animate)===null||u===void 0?void 0:u.call(d,[{transform:o.current},{transform:`scale(${t}) translateX(${e}px) translateY(${n}px)`}],{duration:a?0:i??500,easing:s.current?"ease-out":"ease-in-out"})}catch(f){console.error(f)}o.current=void 0,s.current&&(s.current.onfinish=()=>{s.current=void 0})}});return Cn(l,[t,e,n,l]),F.useCallback(()=>{o.current=r?.current?window.getComputedStyle(r.current).transform:void 0},[r])}function r2(t,e){const{on:n}=At(),r=We(()=>{var s;e||(s=n.zoom)===null||s===void 0||s.call(n,{zoom:t})});F.useEffect(r,[t,r])}function Ui(){const{zoom:t}=At();return Xg(t)}function s2(t,e){var n;const r=typeof t=="function"?(n=t(e))!==null&&n!==void 0?n:Kg.maxZoom:t;return Math.max(r,1)}function o2(t,e){var n,r;let s={width:0,height:0},o={width:0,height:0};const{currentSlide:i}=fn(),{imageFit:a}=At().carousel,{maxZoomPixelRatio:l,maxZoom:c}=Ui();if(t&&i){const u={...i,...e};if(or(u)){const f=fc(u,a),g=Math.max(...(((n=u.srcSet)===null||n===void 0?void 0:n.map(p=>p.width))||[]).concat(u.width?[u.width]:[])),m=Math.max(...(((r=u.srcSet)===null||r===void 0?void 0:r.map(p=>p.height))||[]).concat(u.height?[u.height]:[]));g>0&&m>0&&t.width>0&&t.height>0&&(o=f?{width:Math.round(Math.min(g,t.width/t.height*m)),height:Math.round(Math.min(m,t.height/t.width*g))}:{width:g,height:m},o={width:o.width*l,height:o.height*l},s=f?{width:Math.min(t.width,o.width,g),height:Math.min(t.height,o.height,m)}:{width:Math.round(Math.min(t.width,t.height/m*g,g)),height:Math.round(Math.min(t.height,t.width/g*m,m))})}else t.width>0&&t.height>0&&(e&&e.width>0&&e.height>0?s={width:Math.min(t.width,e.width),height:Math.min(t.height,e.height)}:s={width:t.width,height:t.height})}const d=i&&s.width?or(i)?Math.max(uc(o.width/s.width,5),1):s2(c,i):1;return{imageRect:s,maxZoom:d}}function qg(t,e){return Math.hypot(t.clientX-e.clientX,t.clientY-e.clientY)}function Zg(t,e,n=100,r=2){return t*Math.min(1+Math.abs(e/n),r)**Math.sign(e)}function i2(t,e,n,r,s,o,i,a,l){const c=F.useRef([]),d=F.useRef(0),u=F.useRef(void 0),{globalIndex:f}=fn(),{getOwnerWindow:g}=ss(),{containerRef:m,subscribeSensors:p}=_n(),{keyboardMoveDistance:y,zoomInMultiplier:b,wheelZoomDistanceFactor:_,scrollToZoom:x,doubleTapDelay:E,doubleClickDelay:C,doubleClickMaxStops:k,pinchZoomDistanceFactor:N,pinchZoomV4:S}=Ui(),w=F.useCallback(z=>{if(m.current){const{pageX:H,pageY:j}=z,{scrollX:W,scrollY:U}=g(),{left:B,top:G,width:V,height:X}=m.current.getBoundingClientRect();return[H-B-W-V/2,j-G-U-X/2]}return[]},[m,g]),M=We(z=>{const{key:H,metaKey:j,ctrlKey:W}=z,U=j||W,B=()=>{z.preventDefault(),z.stopPropagation()};if(t>1){const G=(V,X)=>{B(),a(V,X)};H==="ArrowDown"?G(0,y):H==="ArrowUp"?G(0,-y):H==="ArrowLeft"?G(-y,0):H==="ArrowRight"&&G(y,0)}H==="+"||U&&H==="="?(B(),s()):H==="-"||U&&H==="_"?(B(),o()):U&&H==="0"&&(B(),i(1))}),T=We(z=>{if((z.ctrlKey||x)&&Math.abs(z.deltaY)>Math.abs(z.deltaX)){z.stopPropagation(),i(Zg(t,-z.deltaY,_),!0,...w(z));return}t>1&&(z.stopPropagation(),x||a(z.deltaX,z.deltaY))}),P=F.useCallback(z=>{const H=c.current;H.splice(0,H.length,...H.filter(j=>j.pointerId!==z.pointerId))},[]),L=F.useCallback(z=>{P(z),z.persist(),c.current.push(z)},[P]),$=We(z=>{var H;const j=c.current;if(z.pointerType==="mouse"&&z.buttons>1||!(!((H=l?.current)===null||H===void 0)&&H.contains(z.target)))return;t>1&&z.stopPropagation();const{timeStamp:W}=z;if(j.length===0&&W-d.current<(z.pointerType==="touch"?E:C)){d.current=0;const U=t>=1?t!==n?t*Math.max(n**(1/k),b):1:t!==e?t/Math.max(e**(-1/k),b):1;i(U,!1,...w(z))}else d.current=W;if(L(z),j.length===2){const U=qg(j[0],j[1]);u.current={previousDistance:U,initialDistance:Math.max(U,1),initialZoom:t}}}),A=We(z=>{const H=c.current,j=H.find(W=>W.pointerId===z.pointerId);if(H.length===2&&u.current){z.stopPropagation(),L(z);const W=qg(H[0],H[1]),U=S?u.current.initialZoom/u.current.initialDistance*W:Zg(t,W-u.current.previousDistance,N);i(U,!0,...H.map(B=>w(B)).reduce((B,G)=>G.map((V,X)=>B[X]+V/2))),u.current.previousDistance=W;return}t>1&&(z.stopPropagation(),j&&(H.length===1&&a((j.clientX-z.clientX)/t,(j.clientY-z.clientY)/t),L(z)))}),D=F.useCallback(z=>{const H=c.current;H.length===2&&H.find(j=>j.pointerId===z.pointerId)&&(u.current=void 0),P(z)},[P]),O=F.useCallback(()=>{const z=c.current;z.splice(0,z.length),d.current=0,u.current=void 0},[]);Pg(p,$,A,D,r),F.useEffect(O,[f,O]),F.useEffect(()=>r?()=>{}:ns(O,p(lc,M),p(cc,T)),[r,p,O,M,T])}function a2(t,e,n){const[r,s]=F.useState(1),[o,i]=F.useState(0),[a,l]=F.useState(0),c=n2(r,o,a,n),{currentSlide:d,globalIndex:u}=fn(),{containerRect:f,slideRect:g}=_n(),{minZoom:m,zoomInMultiplier:p}=Ui(),y=d&&or(d)?d.src:void 0,b=!n?.current;Cn(()=>{s(1),i(0),l(0)},[u,y]);const _=F.useCallback((N,S,w)=>{const M=w||r,T=o-(N||0),P=a-(S||0),L=(t.width*M-g.width)/2/M,$=(t.height*M-g.height)/2/M;i(Math.min(Math.abs(T),Math.max(L,0))*Math.sign(T)),l(Math.min(Math.abs(P),Math.max($,0))*Math.sign(P))},[r,o,a,g,t.width,t.height]),x=F.useCallback((N,S,w,M)=>{const T=uc(N+.01m?N:m:e,5);S||c(),_(w?w*(1/r-1/T):0,M?M*(1/r-1/T):0,T),s(T)},[r,m,e,_,c]),E=We(()=>{r>1&&(r>e&&x(e,!0),_())});Cn(E,[f.width,f.height,E]);const C=F.useCallback(()=>{const N=r*p;x(r<1&&N>1?1:N)},[r,p,x]),k=F.useCallback(()=>{const N=r/p;x(r>1&&N<1?1:N)},[r,p,x]);return{zoom:r,offsetX:o,offsetY:a,disabled:b,changeOffsets:_,changeZoom:x,zoomIn:C,zoomOut:k}}const Qg=F.createContext(null),_c=ln("useZoom","ZoomControllerContext",Qg);function l2({children:t}){const[e,n]=F.useState(),{slideRect:r}=_n(),{ref:s,minZoom:o}=Ui(),{imageRect:i,maxZoom:a}=o2(r,e?.imageDimensions),{zoom:l,offsetX:c,offsetY:d,disabled:u,changeZoom:f,changeOffsets:g,zoomIn:m,zoomOut:p}=a2(i,a,e?.zoomWrapperRef);r2(l,u),i2(l,o,a,u,m,p,f,g,e?.zoomWrapperRef);const y=F.useMemo(()=>({zoom:l,minZoom:o,maxZoom:a,offsetX:c,offsetY:d,disabled:u,zoomIn:m,zoomOut:p,changeZoom:f}),[l,o,a,c,d,u,m,p,f]);F.useImperativeHandle(s,()=>y,[y]);const b=F.useMemo(()=>({...y,setZoomWrapper:n}),[y,n]);return F.createElement(Qg.Provider,{value:b},t)}const c2=Jt("ZoomIn",F.createElement(F.Fragment,null,F.createElement("path",{d:"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"}),F.createElement("path",{d:"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"}))),d2=Jt("ZoomOut",F.createElement("path",{d:"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"})),Jg=F.forwardRef(function({zoomIn:e,onLoseFocus:n},r){const s=F.useRef(!1),o=F.useRef(!1),{zoom:i,minZoom:a,maxZoom:l,zoomIn:c,zoomOut:d,disabled:u}=_c(),{render:f}=At(),g=u||(e?i>=l:i<=a);return F.useEffect(()=>{g&&s.current&&o.current&&n(),g||(s.current=!0)},[g,n]),F.createElement(is,{ref:r,disabled:g,label:e?"Zoom in":"Zoom out",icon:e?c2:d2,renderIcon:e?f.iconZoomIn:f.iconZoomOut,onClick:e?c:d,onFocus:()=>{o.current=!0},onBlur:()=>{o.current=!1}})});function u2(){const t=F.useRef(null),e=F.useRef(null),{focus:n}=_n(),r=F.useCallback(i=>{var a,l;!((a=i.current)===null||a===void 0)&&a.disabled?n():(l=i.current)===null||l===void 0||l.focus()},[n]),s=F.useCallback(()=>r(t),[r]),o=F.useCallback(()=>r(e),[r]);return F.createElement(F.Fragment,null,F.createElement(Jg,{zoomIn:!0,ref:t,onLoseFocus:o}),F.createElement(Jg,{ref:e,onLoseFocus:s}))}function f2(){const{render:t}=At(),e=_c();return t.buttonZoom?F.createElement(F.Fragment,null,t.buttonZoom(e)):F.createElement(u2,null)}function h2(t){var e;return(((e=t.srcSet)===null||e===void 0?void 0:e.length)||0)>0}function g2({current:t,preload:e},{type:n,source:r}){switch(n){case"fetch":return t?{current:t,preload:r}:{current:r};case"done":return r===e?{current:r}:{current:t,preload:e};default:throw new Error(gg)}}function p2(t){var e,n;const[{current:r,preload:s},o]=F.useReducer(g2,{}),{slide:i,rect:a,imageFit:l,render:c,interactive:d}=t,u=i.srcSet.sort((C,k)=>C.width-k.width),f=(e=i.width)!==null&&e!==void 0?e:u[u.length-1].width,g=(n=i.height)!==null&&n!==void 0?n:u[u.length-1].height,m=fc(i,l),p=Math.max(...u.map(C=>C.width)),y=Math.min((m?Math.max:Math.min)(a.width,f*(a.height/g)),p),b=c_(),_=We(()=>{var C;const k=(C=u.find(N=>N.width>=y*b))!==null&&C!==void 0?C:u[u.length-1];(!r||u.findIndex(N=>N.src===r)N===k))&&o({type:"fetch",source:k.src})});Cn(_,[a.width,a.height,b,_]);const x=We(C=>o({type:"done",source:C})),E={WebkitTransform:d?"initial":"translateZ(0)"};return m||Object.assign(E,a.width/a.heightx(s),render:{...c,iconLoading:()=>null,iconError:()=>null}}),r&&F.createElement(yr,{key:"current",...t,slide:{...i,src:r,srcSet:void 0},style:E}))}function m2({render:t,slide:e,offset:n,rect:r}){var s;const[o,i]=F.useState(),a=F.useRef(null),l=or(e),{zoom:c,maxZoom:d,offsetX:u,offsetY:f,setZoomWrapper:g}=_c(),m=c>1,{carousel:p,on:y}=At(),{currentIndex:b}=fn();Cn(()=>{if(n!==0||l||!a.current)return()=>{};const x=()=>{const C=a.current;if(!C)return;let k=0,N=0;for(const S of C.children)S instanceof HTMLElement&&(k=Math.max(k,S.offsetWidth),N=Math.max(N,S.offsetHeight));i(S=>S&&S.width===k&&S.height===N?S:{width:k,height:N})};if(x(),typeof ResizeObserver>"u")return()=>{};const E=new ResizeObserver(x);for(const C of a.current.children)E.observe(C);return()=>E.disconnect()},[n,l,r]),Cn(()=>n===0?(g({zoomWrapperRef:a,imageDimensions:o}),()=>g(void 0)):()=>{},[n,o,g]);let _=(s=t.slide)===null||s===void 0?void 0:s.call(t,{slide:e,offset:n,rect:r,zoom:c,maxZoom:d});if(!_&&l){const x={slide:e,offset:n,rect:r,render:t,imageFit:p.imageFit,imageProps:p.imageProps,onClick:n===0?()=>{var E;return(E=y.click)===null||E===void 0?void 0:E.call(y,{index:b})}:void 0};_=h2(e)?F.createElement(p2,{...x,slide:e,interactive:m,rect:n===0?{width:r.width*c,height:r.height*c}:r}):F.createElement(yr,{onLoad:E=>i({width:E.naturalWidth,height:E.naturalHeight}),...x})}return _?F.createElement("div",{ref:a,className:zt(Ne(sg),Ne(es),Ne(ig),m&&Ne(n_)),style:n===0?{transform:`scale(${c}) translateX(${u}px) translateY(${f}px)`}:void 0},_):null}const b2=({augment:t,addModule:e})=>{t(({zoom:n,toolbar:r,render:s,controller:o,...i})=>{const a=Xg(n);return{zoom:a,toolbar:zi(r,rg,F.createElement(f2,null)),render:{...s,slide:l=>{var c,d;return or(l.slide)||l.slide.type!=null&&(!((c=a.supports)===null||c===void 0)&&c.includes(l.slide.type))?F.createElement(m2,{render:s,...l}):(d=s.slide)===null||d===void 0?void 0:d.call(s,l)}},controller:{...o,preventDefaultWheelY:a.scrollToZoom},...i}}),e(un(rg,l2))},y2={separator:"/",container:{}},Rg=t=>({...y2,...t});function v2({counter:t}){const{slides:e,currentIndex:n}=fn(),{separator:r,container:{className:s,...o},className:i,...a}=Rg(t);return e.length===0?null:F.createElement("div",{className:zt(Ne("counter"),s||i),...a,...o,"aria-hidden":!0},n+1," ",r," ",e.length)}function x2({augment:t,addChild:e}){t(({counter:n,...r})=>({counter:Rg(n),...r})),e(to,un(qS,v2))}const w2={autoplay:!1,delay:3e3,ref:null},ep=t=>({...w2,...t}),tp=F.createContext(null),k2=ln("useSlideshow","SlideshowContext",tp);function C2({slideshow:t,carousel:{finite:e},on:n,children:r}){const{autoplay:s,delay:o,ref:i}=ep(t),a=F.useRef(s),[l,c]=F.useState(s),d=F.useRef(void 0),u=F.useRef(void 0),{slides:f,currentIndex:g}=fn(),{setTimeout:m,clearTimeout:p}=ao(),{subscribe:y}=os(),{next:b}=_n(),{setAutoPlaying:_}=yc();F.useEffect(()=>_(l),[l,_]);const x=f.length===0||e&&g===f.length-1,E=F.useCallback(()=>{!l&&!x&&c(!0)},[l,x]),C=F.useCallback(()=>{l&&c(!1)},[l]),k=F.useCallback(()=>{p(d.current),d.current=void 0},[p]),N=We(()=>{k(),!(!l||x||u.current===Rr||u.current===oc)&&(d.current=m(()=>{l&&(u.current=void 0,b())},o))});F.useEffect(N,[g,l,N]),F.useEffect(()=>{l&&x&&c(!1)},[g,l,x]);const S=We(()=>{var T;return(T=n.slideshowStart)===null||T===void 0?void 0:T.call(n)}),w=We(()=>{var T;return(T=n.slideshowStop)===null||T===void 0?void 0:T.call(n)});F.useEffect(()=>{l?S():a.current&&w(),a.current=l},[l,S,w]),F.useEffect(()=>ns(k,y(QS,()=>{u.current=Rr,k()}),y(JS,()=>{u.current=oc,k()}),y(RS,()=>{u.current=so,N()}),y(e_,()=>{u.current=oo,N()})),[y,k,N]);const M=F.useMemo(()=>({playing:l,disabled:x,play:E,pause:C}),[l,x,E,C]);return F.useImperativeHandle(i,()=>M,[M]),F.createElement(tp.Provider,{value:M},r)}const S2=Jt("Play",F.createElement("path",{d:"M8 5v14l11-7z"})),_2=Jt("Pause",F.createElement("path",{d:"M6 19h4V5H6v14zm8-14v14h4V5h-4z"}));function E2(){const{playing:t,disabled:e,play:n,pause:r}=k2(),{render:s}=At(),o=Dg(_n().focus,e);return s.buttonSlideshow?F.createElement(F.Fragment,null,s.buttonSlideshow({playing:t,disabled:e,play:n,pause:r})):F.createElement(is,{label:t?"Pause":"Play",icon:t?_2:S2,renderIcon:t?s.iconSlideshowPause:s.iconSlideshowPlay,onClick:t?r:n,disabled:e,...o})}function M2({augment:t,addModule:e}){t(({slideshow:n,toolbar:r,...s})=>({toolbar:zi(r,ng,F.createElement(E2,null)),slideshow:ep(n),...s})),e(un(ng,C2))}const N2={auto:!1,ref:null},np=t=>({...N2,...t}),rp=F.createContext(null),O2=ln("useFullscreen","FullscreenContext",rp);function T2({fullscreen:t,on:e,children:n}){const{auto:r,ref:s}=np(t),o=F.useRef(null),[i,a]=F.useState(),[l,c]=F.useState(!1),d=F.useRef(!1),{getOwnerDocument:u}=ss();Cn(()=>{var x,E,C,k;const N=u();a(!((k=(C=(E=(x=N.fullscreenEnabled)!==null&&x!==void 0?x:N.webkitFullscreenEnabled)!==null&&E!==void 0?E:N.mozFullScreenEnabled)!==null&&C!==void 0?C:N.msFullscreenEnabled)!==null&&k!==void 0&&k))},[u]);const f=F.useCallback(()=>{var x;const E=u(),C=E.fullscreenElement||E.webkitFullscreenElement||E.mozFullScreenElement||E.msFullscreenElement;return((x=C?.shadowRoot)===null||x===void 0?void 0:x.fullscreenElement)||C},[u]),g=F.useCallback(()=>{const x=o.current;try{x.requestFullscreen?x.requestFullscreen().catch(()=>{}):x.webkitRequestFullscreen?x.webkitRequestFullscreen():x.mozRequestFullScreen?x.mozRequestFullScreen():x.msRequestFullscreen&&x.msRequestFullscreen()}catch{}},[]),m=F.useCallback(()=>{if(!f())return;const x=u();try{x.exitFullscreen?x.exitFullscreen().catch(()=>{}):x.webkitExitFullscreen?x.webkitExitFullscreen():x.mozCancelFullScreen?x.mozCancelFullScreen():x.msExitFullscreen&&x.msExitFullscreen()}catch{}},[f,u]);F.useEffect(()=>{const x=u(),E=()=>{c(f()===o.current)};return ns(...["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"].map(C=>(x.addEventListener(C,E),()=>x.removeEventListener(C,E))))},[f,u]);const p=We(()=>{var x;return(x=e.enterFullscreen)===null||x===void 0?void 0:x.call(e)}),y=We(()=>{var x;return(x=e.exitFullscreen)===null||x===void 0?void 0:x.call(e)});F.useEffect(()=>{l&&(d.current=!0),d.current&&(l?p:y)()},[l,p,y]);const b=We(()=>{var x;return(x=r?g:null)===null||x===void 0||x(),m});F.useEffect(b,[b]);const _=F.useMemo(()=>({fullscreen:l,disabled:i,enter:g,exit:m}),[l,i,g,m]);return F.useImperativeHandle(s,()=>_,[_]),F.createElement("div",{ref:o,className:zt(Ne(no),Ne(sg))},F.createElement(rp.Provider,{value:_},n))}const A2=Jt("EnterFullscreen",F.createElement("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})),D2=Jt("ExitFullscreen",F.createElement("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"}));function I2(){var t;const{fullscreen:e,disabled:n,enter:r,exit:s}=O2(),{render:o}=At();return n?null:o.buttonFullscreen?F.createElement(F.Fragment,null,(t=o.buttonFullscreen)===null||t===void 0?void 0:t.call(o,{fullscreen:e,disabled:n,enter:r,exit:s})):F.createElement(is,{disabled:n,label:e?"Exit Fullscreen":"Enter Fullscreen",icon:e?D2:A2,renderIcon:e?o.iconExitFullscreen:o.iconEnterFullscreen,onClick:e?s:r})}function L2({augment:t,contains:e,addParent:n}){t(({fullscreen:r,toolbar:s,...o})=>({toolbar:zi(s,no,F.createElement(I2,null)),fullscreen:np(r),...o})),n(e(ro)?ro:to,un(no,T2))}const xr={ref:null,position:"bottom",width:120,height:80,border:1,borderRadius:4,padding:4,gap:16,imageFit:"contain",vignette:!0,hidden:!1,showToggle:!1},Ec=t=>({...xr,...t});function sp(){const{thumbnails:t}=At();return Ec(t)}const wr=t=>pr(ro,t),Dt=t=>wr(pr("thumbnail",t)),P2=Jt("VideoThumbnail",F.createElement("path",{d:"M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"})),$2=Jt("UnknownThumbnail",F.createElement("path",{d:"M23 18V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zM8.5 12.5l2.5 3.01L14.5 11l4.5 6H5l3.5-4.5z"}));function F2({slide:t,render:e,rect:n,imageFit:r}){var s;const o=(s=e.thumbnail)===null||s===void 0?void 0:s.call(e,{slide:t,render:e,rect:n,imageFit:r});if(o)return o;const i={render:e,rect:n,imageFit:r};if(t.thumbnail)return F.createElement(yr,{slide:{src:t.thumbnail},...i});if(or(t))return F.createElement(yr,{slide:t,...i});const a=Ne(Dt(Pi));return t.type==="video"?F.createElement(F.Fragment,null,t.poster&&F.createElement(yr,{slide:{src:t.poster},...i}),F.createElement(P2,{className:a})):F.createElement($2,{className:a})}const z2=mr("active"),Mc=mr("fadein"),Nc=mr("fadeout"),V2=mr("placeholder"),op="delay",ip="duration";function H2({slide:t,index:e,onClick:n,fadeIn:r,fadeOut:s,placeholder:o,onLoseFocus:i}){const a=F.useRef(null),{render:l,styles:c,labels:d}=At(),{slides:u,globalIndex:f}=fn(),{getOwnerDocument:g}=ss(),{width:m,height:p,imageFit:y}=sp(),b={width:m,height:p},_=e===f,x=We(i);return F.useEffect(()=>{s&&g().activeElement===a.current&&x()},[s,x,g]),F.createElement("button",{ref:a,type:"button",className:zt(Ne(es),Ne(Dt()),_&&Ne(Dt(z2())),r&&Ne(Dt(Mc())),s&&Ne(Dt(Nc())),o&&Ne(Dt(V2()))),style:{...r?{[dt(Dt(Mc(ip)))]:`${r.duration}ms`,[dt(Dt(Mc(op)))]:`${r.delay}ms`}:null,...s?{[dt(Dt(Nc(ip)))]:`${s.duration}ms`,[dt(Dt(Nc(op)))]:`${s.delay}ms`}:null,...c.thumbnail},onClick:n,"aria-current":_?!0:void 0,"aria-label":mg(d,u,e)},t&&F2({slide:t,render:l,rect:b,imageFit:y}))}function ap(t){return["top","bottom"].includes(t)}function lp(t,e){return e+2*(t.border+t.padding)+t.gap}function U2(t){const{thumbnail:e,poster:n}=t||{thumbnail:"placeholder"};return typeof e=="string"&&e||typeof n=="string"&&n||t&&bg(t)||void 0}function cp({visible:t,containerRef:e}){const n=F.useRef(null),r=Hi(),{publish:s,subscribe:o}=os(),{carousel:i,styles:a,labels:l}=At(),{slides:c,globalIndex:d,animation:u}=fn(),{registerSensors:f,subscribeSensors:g}=Ig();Hg(g);const m=sp(),{position:p,width:y,height:b,border:_,borderStyle:x,borderColor:E,borderRadius:C,padding:k,gap:N,vignette:S}=m,w=u?.duration!==void 0&&u?.increment||0,M=u?.duration||0,{prepareAnimation:T}=xc(n,D=>({keyframes:ap(p)?[{transform:`translateX(${(r?-1:1)*lp(m,y)*w+D}px)`},{transform:"translateX(0)"}]:[{transform:`translateY(${lp(m,b)*w+D}px)`},{transform:"translateY(0)"}],duration:M,easing:u?.easing})),P=We(()=>{let D=0;if(e.current&&n.current){const O=e.current.getBoundingClientRect(),z=n.current.getBoundingClientRect();D=ap(p)?z.left-O.left-(O.width-z.width)/2:z.top-O.top-(O.height-z.height)/2}T(D)});F.useEffect(()=>ns(o(ac,P)),[o,P]);const L=yg(i,c),$=[];if(gc(c))for(let D=d-L-Math.abs(w);D<=d+L+Math.abs(w);D+=1){const z=i.finite&&(D<0||D>c.length-1)||w<0&&D0&&D>d+L?null:pc(c,D),H=[`${D}`,U2(z)].filter(Boolean).join("|");$.push({key:H,index:D,slide:z})}const A=D=>()=>{D>d?s(sr,{count:D-d}):D{const H=M/Math.abs(w||1),j=w>0&&O>d+L-w&&O<=d+L||w<0&&O=d-L?{duration:H,delay:((w>0?O-(d+L-w):d-L-w-O)-1)*H}:void 0,W=w>0&&Od+L?{duration:H,delay:(w>0?w-(d-L-O):-w-(O-(d+L)))*H}:void 0;return F.createElement(H2,{key:D,index:O,slide:z,fadeIn:j,fadeOut:W,placeholder:!z,onClick:A(O),onLoseFocus:()=>{var U;return(U=n.current)===null||U===void 0?void 0:U.focus()}})})),S&&F.createElement("div",{className:Ne(wr("vignette"))}))}const dp=F.createContext(null),B2=ln("useThumbnails","ThumbnailsContext",dp);function j2({children:t,...e}){const{ref:n,position:r,hidden:s}=Ec(e.thumbnails),[o,i]=F.useState(!s),a=F.useRef(null),l=F.useMemo(()=>({visible:o,show:()=>i(!0),hide:()=>i(!1)}),[o]);return F.useImperativeHandle(n,()=>l,[l]),F.createElement(Sg,{...e},F.createElement(dp.Provider,{value:l},F.createElement("div",{ref:a,className:zt(Ne(wr()),Ne(wr(`${r}`)))},["start","top"].includes(r)&&F.createElement(cp,{containerRef:a,visible:o}),F.createElement("div",{className:Ne(wr("wrapper"))},t),["end","bottom"].includes(r)&&F.createElement(cp,{containerRef:a,visible:o}))))}const up=()=>F.createElement(F.Fragment,null,F.createElement("path",{strokeWidth:2,stroke:"currentColor",strokeLinejoin:"round",fill:"none",d:"M3 5l18 0l0 14l-18 0l0-14z"}),F.createElement("path",{d:"M5 14h4v3h-4zM10 14h4v3h-4zM15 14h4v3h-4z"})),W2=Jt("ThumbnailsVisible",up()),G2=C_("ThumbnailsHidden",up());function Y2(){const{visible:t,show:e,hide:n}=B2(),{render:r}=At();return r.buttonThumbnails?F.createElement(F.Fragment,null,r.buttonThumbnails({visible:t,show:e,hide:n})):F.createElement(is,{label:t?"Hide thumbnails":"Show thumbnails",icon:t?W2:G2,renderIcon:t?r.iconThumbnailsVisible:r.iconThumbnailsHidden,onClick:t?n:e})}function K2({augment:t,contains:e,append:n,addParent:r}){t(({thumbnails:o,toolbar:i,...a})=>{const l=Ec(o);return{toolbar:zi(i,ro,l.showToggle?F.createElement(Y2,null):null),thumbnails:l,...a}});const s=un(ro,j2);e(no)?n(no,s):r(to,s)}const X2=window.G7Core?.createLogger?.("Comp:ImageGallery")??{log:(...t)=>console.log("[Comp:ImageGallery]",...t),warn:(...t)=>console.warn("[Comp:ImageGallery]",...t),error:(...t)=>console.error("[Comp:ImageGallery]",...t)},Oc=window.G7Core,Bi=(t,e)=>Oc?.t?.(t,e)??t,q2=async(t,e)=>{try{const n=await Oc.api.get(t,{responseType:"blob"});if(n){const r=URL.createObjectURL(n),s=document.createElement("a");s.href=r,s.download=e,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(r)}}catch(n){X2.error("Failed to download file:",n),Oc?.toast?.error?.(Bi("common.download_failed"))}},Z2=(t,e)=>{const n=document.createElement("a");n.href=t,n.download=e,n.target="_blank",document.body.appendChild(n),n.click(),document.body.removeChild(n)},Tc=async t=>{const e=t.downloadUrl||t.src,n=t.filename||t.title||"image";t.downloadRequiresAuth?await q2(e,n):Z2(e,n)},Q2=({image:t,index:e,onDownload:n})=>{const[r,s]=v.useState(!1),o=async i=>{if(i.stopPropagation(),n){n(t,e);return}s(!0);try{await Tc(t)}finally{s(!1)}};return h.jsx(oe,{type:"button",onClick:o,disabled:r,className:"yarl__button flex items-center justify-center","aria-label":Bi("common.download"),title:Bi("common.download"),children:r?h.jsx(mt,{className:"fa-solid fa-spinner fa-spin text-white"}):h.jsx(mt,{className:"fa-solid fa-download text-white"})})},ji=({images:t,isOpen:e,onClose:n,startIndex:r=0,enableZoom:s=!0,enableSlideshow:o=!1,enableFullscreen:i=!0,showCounter:a=!0,showDownload:l=!0,showThumbnails:c=!0,onDownload:d,editorAttrs:u,id:f})=>{const[g,m]=v.useState(r),p=v.useRef(r),y=t.map(x=>({src:x.src,title:x.title,description:x.description})),b=[];s&&b.push(b2),o&&b.push(M2),i&&b.push(L2),a&&b.push(x2),c&&b.push(K2);const _=t[g];if(v.useEffect(()=>{p.current=g},[g]),u){const x=(t??[]).slice(0,4);return h.jsx(I,{id:f,className:"grid grid-cols-2 gap-2 p-3 rounded-lg border border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800",...u,children:x.length>0?x.map((E,C)=>h.jsx(yn,{src:E.thumbnail||E.src,alt:E.title||"",className:"w-full aspect-square object-cover rounded bg-gray-200 dark:bg-gray-700"},C)):h.jsxs(I,{className:"col-span-2 flex items-center justify-center py-6 text-sm text-gray-400 dark:text-gray-500",children:[h.jsx(mt,{className:"fa-regular fa-images mr-2"}),Bi("editor.component.image_gallery")]})})}return h.jsx(e2,{open:e,close:n,slides:y,index:g,plugins:b,on:{view:({index:x})=>{x!==p.current&&m(x)}},zoom:{maxZoomPixelRatio:3,zoomInMultiplier:2,doubleTapDelay:300,doubleClickDelay:300,doubleClickMaxStops:2,keyboardMoveDistance:50,wheelZoomDistanceFactor:100,pinchZoomDistanceFactor:100,scrollToZoom:!0},carousel:{finite:!0,preload:2,padding:"16px",spacing:"30%"},animation:{fade:250,swipe:500,easing:{fade:"ease",swipe:"ease-out",navigation:"ease-in-out"}},controller:{closeOnBackdropClick:!0,closeOnPullDown:!0,closeOnPullUp:!0},thumbnails:{position:"bottom",width:120,height:80,border:2,borderRadius:4,padding:4,gap:16,showToggle:!1,vignette:!0},toolbar:{buttons:[l&&_&&h.jsx(Q2,{image:_,index:g,onDownload:d},"download"),"close"].filter(Boolean)},styles:{container:{backgroundColor:"rgba(0, 0, 0, 0.9)"}}})},fp=()=>{const[t,e]=v.useState(!1),[n,r]=v.useState([]),[s,o]=v.useState(0),i=v.useCallback((l,c=0)=>{r(l),o(c),e(!0)},[]),a=v.useCallback(()=>{e(!1)},[]);return{isOpen:t,openGallery:i,closeGallery:a,galleryProps:{images:n,isOpen:t,onClose:a,startIndex:s}}};ji.displayName="ImageGallery";const J2=Object.freeze(Object.defineProperty({__proto__:null,ImageGallery:ji,executeImageDownload:Tc,useImageGallery:fp},Symbol.toStringTag,{value:"Module"})),R2=v.createContext(null),eE=({value:t,children:e})=>h.jsx(R2.Provider,{value:t,children:e});function tE(t,e){return e.forEach((function(n){n&&typeof n!="string"&&!Array.isArray(n)&&Object.keys(n).forEach((function(r){if(r!=="default"&&!(r in t)){var s=Object.getOwnPropertyDescriptor(n,r);Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:function(){return n[r]}})}}))})),Object.freeze(t)}function hp(t,e){return new Promise((function(n,r){let s;return nE(t).then((function(o){try{return s=o,n(new Blob([e.slice(0,2),s,e.slice(2)],{type:"image/jpeg"}))}catch(i){return r(i)}}),r)}))}const nE=t=>new Promise(((e,n)=>{const r=new FileReader;r.addEventListener("load",(({target:{result:s}})=>{const o=new DataView(s);let i=0;if(o.getUint16(i)!==65496)return n("not a valid JPEG");for(i+=2;;){const a=o.getUint16(i);if(a===65498)break;const l=o.getUint16(i+2);if(a===65505&&o.getUint32(i+4)===1165519206){const c=i+10;let d;switch(o.getUint16(c)){case 18761:d=!0;break;case 19789:d=!1;break;default:return n("TIFF header contains invalid endian")}if(o.getUint16(c+2,d)!==42)return n("TIFF header contains invalid version");const u=o.getUint32(c+4,d),f=c+u+2+12*o.getUint16(c+u,d);for(let g=c+u+2;g>>24&255,a[i+1]=l>>>16&255,a[i+2]=l>>>8&255,a[i+3]=l>>>0&255,new Uint8Array(a.buffer,0,i+4)},r.deflateRaw=function(s,o){o==null&&(o={level:6});var i=new Uint8Array(50+Math.floor(1.1*s.length)),a=r.F.deflateRaw(s,i,a,o.level);return new Uint8Array(i.buffer,0,a)},r.encode=function(s,o){o==null&&(o=!1);var i=0,a=r.bin.writeUint,l=r.bin.writeUshort,c={};for(var d in s){var u=!r._noNeed(d)&&!o,f=s[d],g=r.crc.crc(f,0,f.length);c[d]={cpr:u,usize:f.length,crc:g,file:u?r.deflateRaw(f):f}}for(var d in c)i+=c[d].file.length+30+46+2*r.bin.sizeUTF8(d);i+=22;var m=new Uint8Array(i),p=0,y=[];for(var d in c){var b=c[d];y.push(p),p=r._writeHeader(m,p,d,b,0)}var _=0,x=p;for(var d in c)b=c[d],y.push(p),p=r._writeHeader(m,p,d,b,1,y[_++]);var E=p-x;return a(m,p,101010256),p+=4,l(m,p+=4,_),l(m,p+=2,_),a(m,p+=2,E),a(m,p+=4,x),p+=4,p+=2,m.buffer},r._noNeed=function(s){var o=s.split(".").pop().toLowerCase();return"png,jpg,jpeg,zip".indexOf(o)!=-1},r._writeHeader=function(s,o,i,a,l,c){var d=r.bin.writeUint,u=r.bin.writeUshort,f=a.file;return d(s,o,l==0?67324752:33639248),o+=4,l==1&&(o+=2),u(s,o,20),u(s,o+=2,0),u(s,o+=2,a.cpr?8:0),d(s,o+=2,0),d(s,o+=4,a.crc),d(s,o+=4,f.length),d(s,o+=4,a.usize),u(s,o+=4,r.bin.sizeUTF8(i)),u(s,o+=2,0),o+=2,l==1&&(o+=2,o+=2,d(s,o+=6,c),o+=4),o+=r.bin.writeUTF8(s,o,i),l==0&&(s.set(f,o),o+=f.length),o},r.crc={table:(function(){for(var s=new Uint32Array(256),o=0;o<256;o++){for(var i=o,a=0;a<8;a++)1&i?i=3988292384^i>>>1:i>>>=1;s[o]=i}return s})(),update:function(s,o,i,a){for(var l=0;l>>8;return s},crc:function(s,o,i){return 4294967295^r.crc.update(4294967295,s,o,i)}},r.adler=function(s,o,i){for(var a=1,l=0,c=o,d=o+i;c>8&255},readUint:function(s,o){return 16777216*s[o+3]+(s[o+2]<<16|s[o+1]<<8|s[o])},writeUint:function(s,o,i){s[o]=255&i,s[o+1]=i>>8&255,s[o+2]=i>>16&255,s[o+3]=i>>24&255},readASCII:function(s,o,i){for(var a="",l=0;l>6,s[o+l+1]=128|d>>0&63,l+=2;else if((4294901760&d)==0)s[o+l]=224|d>>12,s[o+l+1]=128|d>>6&63,s[o+l+2]=128|d>>0&63,l+=3;else{if((4292870144&d)!=0)throw"e";s[o+l]=240|d>>18,s[o+l+1]=128|d>>12&63,s[o+l+2]=128|d>>6&63,s[o+l+3]=128|d>>0&63,l+=4}}return l},sizeUTF8:function(s){for(var o=s.length,i=0,a=0;a>>3}var y=c.lits,b=c.strt,_=c.prev,x=0,E=0,C=0,k=0,N=0,S=0;for(p>2&&(b[S=r.F._hash(s,0)]=0),f=0;f14e3||E>26697)&&p-f>100&&(m>>16,P=65535&M;if(M!=0){P=65535&M;var L=d(T=M>>>16,c.of0);c.lhst[257+L]++;var $=d(P,c.df0);c.dhst[$]++,k+=c.exb[L]+c.dxb[$],y[x]=T<<23|f-m,y[x+1]=P<<16|L<<8|$,x+=2,m=f+T}else c.lhst[s[f]]++;E++}}for(C==f&&s.length!=0||(m>>3},r.F._bestMatch=function(s,o,i,a,l,c){var d=32767&o,u=i[d],f=d-u+32768&32767;if(u==d||a!=r.F._hash(s,o-f))return 0;for(var g=0,m=0,p=Math.min(32767,o);f<=p&&--c!=0&&u!=d;){if(g==0||s[o+g]==s[o+g-f]){var y=r.F._howLong(s,o,f);if(y>g){if(m=f,(g=y)>=l)break;f+2b&&(b=E,u=x)}}}f+=(d=u)-(u=i[d])+32768&32767}return g<<16|m},r.F._howLong=function(s,o,i){if(s[o]!=s[o-i]||s[o+1]!=s[o+1-i]||s[o+2]!=s[o+2-i])return 0;var a=o,l=Math.min(s.length,o+258);for(o+=3;o>>23,W=O+(8388607&H);O>16,G=U>>8&255,V=255&U;S(u,f=r.F._writeLit(257+G,$,u,f),j-k.of0[G]),f+=k.exb[G],N(u,f=r.F._writeLit(V,A,u,f),B-k.df0[V]),f+=k.dxb[V],O+=j}}f=r.F._writeLit(256,$,u,f)}return f},r.F._copyExact=function(s,o,i,a,l){var c=l>>>3;return a[c]=i,a[c+1]=i>>>8,a[c+2]=255-a[c],a[c+3]=255-a[c+1],c+=4,a.set(new Uint8Array(s.buffer,o,i),c),l+(i+4<<3)},r.F.getTrees=function(){for(var s=r.F.U,o=r.F._hufTree(s.lhst,s.ltree,15),i=r.F._hufTree(s.dhst,s.dtree,15),a=[],l=r.F._lenCodes(s.ltree,a),c=[],d=r.F._lenCodes(s.dtree,c),u=0;u4&&s.itree[1+(s.ordr[g-1]<<1)]==0;)g--;return[o,i,f,l,d,g,a,c]},r.F.getSecond=function(s){for(var o=[],i=0;i>1)+",");return o},r.F.contSize=function(s,o){for(var i=0,a=0;a15&&(r.F._putsE(i,a,d,u),a+=u)}return a},r.F._lenCodes=function(s,o){for(var i=s.length;i!=2&&s[i-1]==0;)i-=2;for(var a=0;a>>1,138))<11?o.push(17,g-3):o.push(18,g-11),a+=2*g-2}else if(l==u&&c==l&&d==l){for(f=a+5;f+2>>1,6);o.push(16,g-3),a+=2*g-2}else o.push(l,0)}return i>>>1},r.F._hufTree=function(s,o,i){var a=[],l=s.length,c=o.length,d=0;for(d=0;di&&(r.F.restrictDepth(f,i,x),x=i),d=0;do;a++){var d=s[a].d;s[a].d=o,c+=l-(1<>>=i-o;c>0;)(d=s[a].d)=0;a--)s[a].d==o&&c<0&&(s[a].d--,c++);c!=0&&console.log("debt left")},r.F._goodIndex=function(s,o){var i=0;return o[16|i]<=s&&(i|=16),o[8|i]<=s&&(i|=8),o[4|i]<=s&&(i|=4),o[2|i]<=s&&(i|=2),o[1|i]<=s&&(i|=1),i},r.F._writeLit=function(s,o,i,a){return r.F._putsF(i,a,o[s<<1]),a+o[1+(s<<1)]},r.F.inflate=function(s,o){var i=Uint8Array;if(s[0]==3&&s[1]==0)return o||new i(0);var a=r.F,l=a._bitsF,c=a._bitsE,d=a._decodeTiny,u=a.makeCodes,f=a.codes2map,g=a._get17,m=a.U,p=o==null;p&&(o=new i(s.length>>>2<<3));for(var y,b,_=0,x=0,E=0,C=0,k=0,N=0,S=0,w=0,M=0;_==0;)if(_=l(s,M,1),x=l(s,M+1,2),M+=3,x!=0){if(p&&(o=r.F._check(o,w+131072)),x==1&&(y=m.flmap,b=m.fdmap,N=511,S=31),x==2){E=c(s,M,5)+257,C=c(s,M+5,5)+1,k=c(s,M+10,4)+4,M+=14;for(var T=0;T<38;T+=2)m.itree[T]=0,m.itree[T+1]=0;var P=1;for(T=0;TP&&(P=L)}M+=3*k,u(m.itree,P),f(m.itree,P,m.imap),y=m.lmap,b=m.dmap,M=d(m.imap,(1<>>4;if(!(O>>>8))o[w++]=O;else{if(O==256)break;var z=w+O-254;if(O>264){var H=m.ldef[O-257];z=w+(H>>>3)+c(s,M,7&H),M+=7&H}var j=b[g(s,M)&S];M+=15&j;var W=j>>>4,U=m.ddef[W],B=(U>>>4)+l(s,M,15&U);for(M+=15&U,p&&(o=r.F._check(o,w+131072));w>>3),V=s[G-4]|s[G-3]<<8;p&&(o=r.F._check(o,w+V)),o.set(new i(s.buffer,s.byteOffset+G,V),w),M=G+V<<3,w+=V}return o.length==w?o:o.slice(0,w)},r.F._check=function(s,o){var i=s.length;if(o<=i)return s;var a=new Uint8Array(Math.max(i<<1,o));return a.set(s,0),a},r.F._decodeTiny=function(s,o,i,a,l,c){for(var d=r.F._bitsE,u=r.F._get17,f=0;f>>4;if(m<=15)c[f]=m,f++;else{var p=0,y=0;m==16?(y=3+d(a,l,2),l+=2,p=c[f-1]):m==17?(y=3+d(a,l,3),l+=3):m==18&&(y=11+d(a,l,7),l+=7);for(var b=f+y;f>>1;cl&&(l=u),c++}for(;c>1,u=s[c+1],f=d<<4|u,g=o-u,m=s[c]<>>15-o]=f,m++},r.F.revCodes=function(s,o){for(var i=r.F.U.rev15,a=15-o,l=0;l>>a}},r.F._putsE=function(s,o,i){i<<=7&o;var a=o>>>3;s[a]|=i,s[a+1]|=i>>>8},r.F._putsF=function(s,o,i){i<<=7&o;var a=o>>>3;s[a]|=i,s[a+1]|=i>>>8,s[a+2]|=i>>>16},r.F._bitsE=function(s,o,i){return(s[o>>>3]|s[1+(o>>>3)]<<8)>>>(7&o)&(1<>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16)>>>(7&o)&(1<>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16)>>>(7&o)},r.F._get25=function(s,o){return(s[o>>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16|s[3+(o>>>3)]<<24)>>>(7&o)},r.F.U=(e=Uint16Array,n=Uint32Array,{next_code:new e(16),bl_count:new e(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new e(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new n(32),flmap:new e(512),fltree:[],fdmap:new e(32),fdtree:[],lmap:new e(32768),ltree:[],ttree:[],dmap:new e(32768),dtree:[],imap:new e(512),itree:[],rev15:new e(32768),lhst:new n(286),dhst:new n(30),ihst:new n(19),lits:new n(15e3),strt:new e(65536),prev:new e(32768)}),(function(){for(var s=r.F.U,o=0;o<32768;o++){var i=o;i=(4278255360&(i=(4042322160&(i=(3435973836&(i=(2863311530&i)>>>1|(1431655765&i)<<1))>>>2|(858993459&i)<<2))>>>4|(252645135&i)<<4))>>>8|(16711935&i)<<8,s.rev15[o]=(i>>>16|i<<16)>>>17}function a(l,c,d){for(;c--!=0;)l.push(0,d)}for(o=0;o<32;o++)s.ldef[o]=s.of0[o]<<3|s.exb[o],s.ddef[o]=s.df0[o]<<4|s.dxb[o];a(s.fltree,144,8),a(s.fltree,112,9),a(s.fltree,24,7),a(s.fltree,8,8),r.F.makeCodes(s.fltree,9),r.F.codes2map(s.fltree,9,s.flmap),r.F.revCodes(s.fltree,9),a(s.fdtree,32,5),r.F.makeCodes(s.fdtree,5),r.F.codes2map(s.fdtree,5,s.fdmap),r.F.revCodes(s.fdtree,5),a(s.itree,19,0),a(s.ltree,286,0),a(s.dtree,30,0),a(s.ttree,320,0)})()})();var sE=tE({__proto__:null,default:Wi},[Wi]);const En=(function(){var t={nextZero(d,u){for(;d[u]!=0;)u++;return u},readUshort:(d,u)=>d[u]<<8|d[u+1],writeUshort(d,u,f){d[u]=f>>8&255,d[u+1]=255&f},readUint:(d,u)=>16777216*d[u]+(d[u+1]<<16|d[u+2]<<8|d[u+3]),writeUint(d,u,f){d[u]=f>>24&255,d[u+1]=f>>16&255,d[u+2]=f>>8&255,d[u+3]=255&f},readASCII(d,u,f){let g="";for(let m=0;md.length<2?`0${d}`:d,readUTF8(d,u,f){let g,m="";for(let p=0;p>3)]>>7-((7&k)<<0)&1);b[w]=H[L],b[w+1]=H[L+1],b[w+2]=H[L+2],b[w+3]=$>2)]>>6-((3&k)<<1)&3),b[w]=H[L],b[w+1]=H[L+1],b[w+2]=H[L+2],b[w+3]=$>1)]>>4-((1&k)<<2)&15),b[w]=H[L],b[w+1]=H[L+1],b[w+2]=H[L+2],b[w+3]=$>>3)]>>>7-(7&O)&1))==255*S?0:255;_[j+O]=z<<24|A<<16|A<<8|A}else if(E==2)for(O=0;O>>2)]>>>6-((3&O)<<1)&3))==85*S?0:255,_[j+O]=z<<24|A<<16|A<<8|A;else if(E==4)for(O=0;O>>1)]>>>4-((1&O)<<2)&15))==17*S?0:255,_[j+O]=z<<24|A<<16|A<<8|A;else if(E==8)for(O=0;O>3,S=Math.ceil(E*k/8),w=new Uint8Array(C*S);let M=0;const T=[0,0,4,0,2,0,1],P=[0,4,0,2,0,1,0],L=[8,8,8,4,4,2,2],$=[8,8,4,4,2,2,1];let A=0;for(;A<7;){const O=L[A],z=$[A];let H=0,j=0,W=T[A];for(;W>3])>>7-(7&K)&1,w[V*S+(X>>3)]|=D<<7-((7&X)<<0)),k==2&&(D=(D=_[K>>3])>>6-(7&K)&3,w[V*S+(X>>2)]|=D<<6-((3&X)<<1)),k==4&&(D=(D=_[K>>3])>>4-(7&K)&15,w[V*S+(X>>1)]|=D<<4-((1&X)<<2)),k>=8){const Z=V*S+X*N;for(let ee=0;ee>3)+ee]}K+=k,X+=z}G++,V+=O}H*j!=0&&(M+=j*(1+B)),A+=1}return w})(u,d)),u}function r(d,u){return s(new Uint8Array(d.buffer,2,d.length-6),u)}var s=(function(){const d={H:{}};return d.H.N=function(u,f){const g=Uint8Array;let m,p,y=0,b=0,_=0,x=0,E=0,C=0,k=0,N=0,S=0;if(u[0]==3&&u[1]==0)return f||new g(0);const w=d.H,M=w.b,T=w.e,P=w.R,L=w.n,$=w.A,A=w.Z,D=w.m,O=f==null;for(O&&(f=new g(u.length>>>2<<5));y==0;)if(y=M(u,S,1),b=M(u,S+1,2),S+=3,b!=0){if(O&&(f=d.H.W(f,N+(1<<17))),b==1&&(m=D.J,p=D.h,C=511,k=31),b==2){_=T(u,S,5)+257,x=T(u,S+5,5)+1,E=T(u,S+10,4)+4,S+=14;let H=1;for(var z=0;z<38;z+=2)D.Q[z]=0,D.Q[z+1]=0;for(z=0;zH&&(H=U)}S+=3*E,L(D.Q,H),$(D.Q,H,D.u),m=D.w,p=D.d,S=P(D.u,(1<>>4;if(!(j>>>8))f[N++]=j;else{if(j==256)break;{let W=N+j-254;if(j>264){const X=D.q[j-257];W=N+(X>>>3)+T(u,S,7&X),S+=7&X}const U=p[A(u,S)&k];S+=15&U;const B=U>>>4,G=D.c[B],V=(G>>>4)+M(u,S,15&G);for(S+=15&G;N>>3),j=u[H-4]|u[H-3]<<8;O&&(f=d.H.W(f,N+j)),f.set(new g(u.buffer,u.byteOffset+H,j),N),S=H+j<<3,N+=j}return f.length==N?f:f.slice(0,N)},d.H.W=function(u,f){const g=u.length;if(f<=g)return u;const m=new Uint8Array(g<<1);return m.set(u,0),m},d.H.R=function(u,f,g,m,p,y){const b=d.H.e,_=d.H.Z;let x=0;for(;x>>4;if(C<=15)y[x]=C,x++;else{let k=0,N=0;C==16?(N=3+b(m,p,2),p+=2,k=y[x-1]):C==17?(N=3+b(m,p,3),p+=3):C==18&&(N=11+b(m,p,7),p+=7);const S=x+N;for(;x>>1;for(;yp&&(p=_),y++}for(;y>1,_=u[y+1],x=b<<4|_,E=f-_;let C=u[y]<>>15-f]=x,C++}},d.H.l=function(u,f){const g=d.H.m.r,m=15-f;for(let p=0;p>>m}},d.H.M=function(u,f,g){g<<=7&f;const m=f>>>3;u[m]|=g,u[m+1]|=g>>>8},d.H.I=function(u,f,g){g<<=7&f;const m=f>>>3;u[m]|=g,u[m+1]|=g>>>8,u[m+2]|=g>>>16},d.H.e=function(u,f,g){return(u[f>>>3]|u[1+(f>>>3)]<<8)>>>(7&f)&(1<>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16)>>>(7&f)&(1<>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16)>>>(7&f)},d.H.i=function(u,f){return(u[f>>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16|u[3+(f>>>3)]<<24)>>>(7&f)},d.H.m=(function(){const u=Uint16Array,f=Uint32Array;return{K:new u(16),j:new u(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new u(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new f(32),J:new u(512),_:[],h:new u(32),$:[],w:new u(32768),C:[],v:[],d:new u(32768),D:[],u:new u(512),Q:[],r:new u(32768),s:new f(286),Y:new f(30),a:new f(19),t:new f(15e3),k:new u(65536),g:new u(32768)}})(),(function(){const u=d.H.m;for(var f=0;f<32768;f++){let m=f;m=(2863311530&m)>>>1|(1431655765&m)<<1,m=(3435973836&m)>>>2|(858993459&m)<<2,m=(4042322160&m)>>>4|(252645135&m)<<4,m=(4278255360&m)>>>8|(16711935&m)<<8,u.r[f]=(m>>>16|m<<16)>>>17}function g(m,p,y){for(;p--!=0;)m.push(0,y)}for(f=0;f<32;f++)u.q[f]=u.S[f]<<3|u.T[f],u.c[f]=u.p[f]<<4|u.z[f];g(u._,144,8),g(u._,112,9),g(u._,24,7),g(u._,8,8),d.H.n(u._,9),d.H.A(u._,9,u.J),d.H.l(u._,9),g(u.$,32,5),d.H.n(u.$,5),d.H.A(u.$,5,u.h),d.H.l(u.$,5),g(u.Q,19,0),g(u.C,286,0),g(u.D,30,0),g(u.v,320,0)})(),d.H.N})();function o(d){return[1,null,3,1,2,null,4][d.ctype]*d.depth}function i(d,u,f,g,m){let p=o(u);const y=Math.ceil(g*p/8);let b,_;p=Math.ceil(p/8);let x=d[f],E=0;if(x>1&&(d[f]=[0,0,1][x-2]),x==3)for(E=p;E>>1)&255;for(let C=0;C>>1);for(;E>>1)}else{for(;E=0&&b>=0?(C=A*u+D<<2,k=(b+A)*m+y+D<<2):(C=(-b+A)*u-y+D<<2,k=A*m+D<<2),_==0)g[k]=d[C],g[k+1]=d[C+1],g[k+2]=d[C+2],g[k+3]=d[C+3];else if(_==1){var N=d[C+3]*.00392156862745098,S=d[C]*N,w=d[C+1]*N,M=d[C+2]*N,T=g[k+3]*(1/255),P=g[k]*T,L=g[k+1]*T,$=g[k+2]*T;const O=1-N,z=N+T*O,H=z==0?0:1/z;g[k+3]=255*z,g[k+0]=(S+P*O)*H,g[k+1]=(w+L*O)*H,g[k+2]=(M+$*O)*H}else if(_==2)N=d[C+3],S=d[C],w=d[C+1],M=d[C+2],T=g[k+3],P=g[k],L=g[k+1],$=g[k+2],N==T&&S==P&&w==L&&M==$?(g[k]=0,g[k+1]=0,g[k+2]=0,g[k+3]=0):(g[k]=S,g[k+1]=w,g[k+2]=M,g[k+3]=N);else if(_==3){if(N=d[C+3],S=d[C],w=d[C+1],M=d[C+2],T=g[k+3],P=g[k],L=g[k+1],$=g[k+2],N==T&&S==P&&w==L&&M==$)continue;if(N<220&&T>20)return!1}return!0}return{decode:function(u){const f=new Uint8Array(u);let g=8;const m=t,p=m.readUshort,y=m.readUint,b={tabs:{},frames:[]},_=new Uint8Array(f.length);let x,E=0,C=0;const k=[137,80,78,71,13,10,26,10];for(var N=0;N<8;N++)if(f[N]!=k[N])throw"The input is not a PNG file!";for(;g>>1:M>>>=1;S[w]=M}return S})(),update(S,w,M,T){for(let P=0;P>>8;return S},crc:(S,w,M)=>4294967295^r.update(4294967295,S,w,M)};function s(S,w,M,T){w[M]+=S[0]*T>>4,w[M+1]+=S[1]*T>>4,w[M+2]+=S[2]*T>>4,w[M+3]+=S[3]*T>>4}function o(S){return Math.max(0,Math.min(255,S))}function i(S,w){const M=S[0]-w[0],T=S[1]-w[1],P=S[2]-w[2],L=S[3]-w[3];return M*M+T*T+P*P+L*L}function a(S,w,M,T,P,L,$){$==null&&($=1);const A=T.length,D=[];for(var O=0;O>>0&255,V>>>8&255,V>>>16&255,V>>>24&255])}for(O=0;O>2]=z,W[O>>2]=T[z]}}function l(S,w,M,T,P){P==null&&(P={});const{crc:L}=r,$=e.writeUint,A=e.writeUshort,D=e.writeASCII;let O=8;const z=S.frames.length>1;let H,j=!1,W=33+(z?20:0);if(P.sRGB!=null&&(W+=13),P.pHYs!=null&&(W+=21),P.iCCP!=null&&(H=pako.deflate(P.iCCP),W+=21+H.length+4),S.ctype==3){for(var U=S.plte.length,B=0;B>>24!=255&&(j=!0);W+=8+3*U+4+(j?8+1*U+4:0)}for(var G=0;G>>8&255,le=ne>>>16&255;V[O+ee+0]=he,V[O+ee+1]=J,V[O+ee+2]=le}if(O+=3*U,$(V,O,L(V,O-3*U-4,3*U+4)),O+=4,j){for($(V,O,U),O+=4,D(V,O,"tRNS"),O+=4,B=0;B>>24&255;O+=U,$(V,O,L(V,O-U-4,U+4)),O+=4}}let K=0;for(G=0;GVe&&(Ve=pt),itDe&&(De=it));Ve==-1&&(Pe=Te=Ve=De=0),se&&((1&Pe)==1&&Pe--,(1&Te)==1&&Te--);const qe=(Ve-Pe+1)*(De-Te+1);qe>2,He>>2);ee.push(ne);const se=new Uint8Array(ye.abuf,we,He);z&&a(he.img,he.rect.width,he.rect.height,Z,se,ne),he.img.set(se),we+=He}}else for(U=0;UJ&&fe==ve[G-J])ne[G]=ne[G-J];else{let ye=K[fe];if(ye==null&&(K[fe]=ye=Z.length,Z.push(fe),Z.length>=300))break;ne[G]=ye}}}const le=Z.length;for(le<=256&&O==0&&(j=le<=2?1:le<=4?2:le<=16?4:8,j=Math.max(j,D)),U=0;U>1)]|=He[Oe+ae]<<4-4*(1&ae);else if(j==2)for(ae=0;ae>2)]|=He[Oe+ae]<<6-2*(3&ae);else if(j==1)for(ae=0;ae>3)]|=He[Oe+ae]<<7-1*(7&ae)}fe=ue,H=3,we=1}else if(V==0&&X.length==1){ue=new Uint8Array(J*ve*3);const He=J*ve;for(G=0;GG&&(G=ee),neV&&(V=ne))}G==-1&&(U=B=G=V=0),$&&((1&U)==1&&U--,(1&B)==1&&B--),L={x:U,y:B,width:G-U+1,height:V-B+1};const X=T[P];X.rect=L,X.blend=1,X.img=new Uint8Array(L.width*L.height*4),T[P-1].dispose==0?(t(O,w,M,X.img,L.width,L.height,-L.x,-L.y,0),f(j,w,M,X.img,L)):t(j,w,M,X.img,L.width,L.height,-L.x,-L.y,0)}function f(S,w,M,T,P){t(S,w,M,T,P.width,P.height,-P.x,-P.y,2)}function g(S,w,M,T,P,L,$){const A=[];let D,O=[0,1,2,3,4];L!=-1?O=[L]:(w*T>5e5||M==1)&&(O=[0]),$&&(D={level:0});const z=sE;for(var H=0;H>1)+256&255;if(L==4)for(D=P;D>1)&255;for(D=P;D>1)&255}if(L==4){for(D=0;D>2);let z;if(M.length<2e7)for(var H=0;H>2]=z.ind,P[H>>2]=z.est.rgba;else for(H=0;H>2]=z.ind,P[H>>2]=z.est.rgba}return{abuf:T.buffer,inds:O,plte:A}}function y(S,w,M){M==null&&(M=1e-4);const T=new Uint32Array(S.buffer),P={i0:0,i1:S.length,bst:null,est:null,tdst:0,left:null,right:null};P.bst=C(S,P.i0,P.i1),P.est=k(P.bst);const L=[P];for(;L.lengthA&&(A=L[$].est.L,D=$);if(A=z||O.i1<=z){O.est.L=0;continue}const H={i0:O.i0,i1:z,bst:null,est:null,tdst:0,left:null,right:null};H.bst=C(S,H.i0,H.i1),H.est=k(H.bst);const j={i0:z,i1:O.i1,bst:null,est:null,tdst:0,left:null,right:null};for(j.bst={R:[],m:[],N:O.bst.N-H.bst.N},$=0;$<16;$++)j.bst.R[$]=O.bst.R[$]-H.bst.R[$];for($=0;$<4;$++)j.bst.m[$]=O.bst.m[$]-H.bst.m[$];j.est=k(j.bst),O.left=H,O.right=j,L[D]=H,L.push(j)}for(L.sort(((A,D)=>D.bst.N-A.bst.N)),$=0;$0&&($=S.right,A=S.left);const D=b($,w,M,T,P);if(D.tdst<=L*L)return D;const O=b(A,w,M,T,P);return O.tdstL;)T-=4;if(M>=T)break;const $=w[M>>2];w[M>>2]=w[T>>2],w[T>>2]=$,M+=4,T-=4}for(;E(S,M,P)>L;)M-=4;return M+4}function E(S,w,M){return S[w]*M[0]+S[w+1]*M[1]+S[w+2]*M[2]+S[w+3]*M[3]}function C(S,w,M){const T=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],P=[0,0,0,0],L=M-w>>2;for(let $=w;$>>0}}var N={multVec:(S,w)=>[S[0]*w[0]+S[1]*w[1]+S[2]*w[2]+S[3]*w[3],S[4]*w[0]+S[5]*w[1]+S[6]*w[2]+S[7]*w[3],S[8]*w[0]+S[9]*w[1]+S[10]*w[2]+S[11]*w[3],S[12]*w[0]+S[13]*w[1]+S[14]*w[2]+S[15]*w[3]],dot:(S,w)=>S[0]*w[0]+S[1]*w[1]+S[2]*w[2]+S[3]*w[3],sml:(S,w)=>[S*w[0],S*w[1],S*w[2],S*w[3]]};En.encode=function(w,M,T,P,L,$,A){P==null&&(P=0),A==null&&(A=!1);const D=d(w,M,T,P,[!1,!1,!1,0,A,!1]);return c(D,-1),l(D,M,T,L,$)},En.encodeLL=function(w,M,T,P,L,$,A,D){const O={ctype:0+(P==1?0:2)+(L==0?0:4),depth:$,frames:[]},z=(P+L)*$,H=z*M;for(let j=0;j>>0),C(1),C(32),k(3),k(l),k(2835),k(2835),N(8),k(16711680),k(65280),k(255),k(4278190080),k(1466527264),(function S(){for(;_0;){for(y=122+_*a,g=0;g>>24,u.setUint32(y+g,m<<8|p),g+=4;_++}E{e(new Blob([n],{type:"image/bmp"}))}))},_dly:9};var Rt={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},oE={[Rt.CHROME]:16384,[Rt.FIREFOX]:11180,[Rt.DESKTOP_SAFARI]:16384,[Rt.IE]:8192,[Rt.IOS]:4096,[Rt.ETC]:8192};const Ac=typeof window<"u",pp=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,Gi=Ac&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),iE=(Ac||pp)&&(Gi&&Gi.getOriginalSymbol(window,"File")||typeof File<"u"&&File),mp=(Ac||pp)&&(Gi&&Gi.getOriginalSymbol(window,"FileReader")||typeof FileReader<"u"&&FileReader);function Dc(t,e,n=Date.now()){return new Promise((r=>{const s=t.split(","),o=s[0].match(/:(.*?);/)[1],i=globalThis.atob(s[1]);let a=i.length;const l=new Uint8Array(a);for(;a--;)l[a]=i.charCodeAt(a);const c=new Blob([l],{type:o});c.name=e,c.lastModified=n,r(c)}))}function bp(t){return new Promise(((e,n)=>{const r=new mp;r.onload=()=>e(r.result),r.onerror=s=>n(s),r.readAsDataURL(t)}))}function yp(t){return new Promise(((e,n)=>{const r=new Image;r.onload=()=>e(r),r.onerror=s=>n(s),r.src=t}))}function kr(){if(kr.cachedResult!==void 0)return kr.cachedResult;let t=Rt.ETC;const{userAgent:e}=navigator;return/Chrom(e|ium)/i.test(e)?t=Rt.CHROME:/iP(ad|od|hone)/i.test(e)&&/WebKit/i.test(e)?t=Rt.IOS:/Safari/i.test(e)?t=Rt.DESKTOP_SAFARI:/Firefox/i.test(e)?t=Rt.FIREFOX:(/MSIE/i.test(e)||document.documentMode)&&(t=Rt.IE),kr.cachedResult=t,kr.cachedResult}function vp(t,e){const n=kr(),r=oE[n];let s=t,o=e,i=s*o;const a=s>o?o/s:s/o;for(;i>r*r;){const l=(r+s)/2,c=(r+o)/2;lgp.toBlob(t,d))).then(function(d){try{return a=d,a.name=n,a.lastModified=r,c.call(this)}catch(u){return i(u)}}.bind(this),i);{let d=function(){return c.call(this)};if(typeof OffscreenCanvas=="function"&&t instanceof OffscreenCanvas)return t.convertToBlob({type:e,quality:s}).then(function(u){try{return a=u,a.name=n,a.lastModified=r,d.call(this)}catch(f){return i(f)}}.bind(this),i);{let u;return u=t.toDataURL(e,s),Dc(u,n,r).then(function(f){try{return a=f,d.call(this)}catch(g){return i(g)}}.bind(this),i)}}}function l(){return o(a)}}))}function $n(t){t.width=0,t.height=0}function as(){return new Promise((function(t,e){let n,r,s,o;return as.cachedResult!==void 0?t(as.cachedResult):Dc("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(i){try{return n=i,Xi(n).then((function(a){try{return r=a[1],qi(r,n.type,n.name,n.lastModified).then((function(l){try{return s=l,$n(r),Xi(s).then((function(c){try{return o=c[0],as.cachedResult=o.width===1&&o.height===2,t(as.cachedResult)}catch(d){return e(d)}}),e)}catch(c){return e(c)}}),e)}catch(l){return e(l)}}),e)}catch(a){return e(a)}}),e)}))}function wp(t){return new Promise(((e,n)=>{const r=new mp;r.onload=s=>{const o=new DataView(s.target.result);if(o.getUint16(0,!1)!=65496)return e(-2);const i=o.byteLength;let a=2;for(;an(s),r.readAsArrayBuffer(t)}))}function kp(t,e){const{width:n}=t,{height:r}=t,{maxWidthOrHeight:s}=e;let o,i=t;return isFinite(s)&&(n>s||r>s)&&([i,o]=Yi(n,r),n>r?(i.width=s,i.height=r/n*s):(i.width=n/r*s,i.height=s),o.drawImage(t,0,0,i.width,i.height),$n(t)),i}function Cp(t,e){const{width:n}=t,{height:r}=t,[s,o]=Yi(n,r);switch(e>4&&e<9?(s.width=r,s.height=n):(s.width=n,s.height=r),e){case 2:o.transform(-1,0,0,1,n,0);break;case 3:o.transform(-1,0,0,-1,n,r);break;case 4:o.transform(1,0,0,-1,0,r);break;case 5:o.transform(0,1,1,0,0,0);break;case 6:o.transform(0,1,-1,0,r,0);break;case 7:o.transform(0,-1,-1,0,r,n);break;case 8:o.transform(0,-1,1,0,0,n)}return o.drawImage(t,0,0,n,r),$n(t),s}function Sp(t,e,n=0){return new Promise((function(r,s){let o,i,a,l,c,d,u,f,g,m,p,y,b,_,x,E,C,k,N,S;function w(T=5){if(e.signal&&e.signal.aborted)throw e.signal.reason;o+=T,e.onProgress(Math.min(o,100))}function M(T){if(e.signal&&e.signal.aborted)throw e.signal.reason;o=Math.min(Math.max(T,o),100),e.onProgress(o)}return o=n,i=e.maxIteration||10,a=1024*e.maxSizeMB*1024,w(),Xi(t,e).then(function(T){try{return[,l]=T,w(),c=kp(l,e),w(),new Promise((function(P,L){var $;if(!($=e.exifOrientation))return wp(t).then(function(D){try{return $=D,A.call(this)}catch(O){return L(O)}}.bind(this),L);function A(){return P($)}return A.call(this)})).then(function(P){try{return d=P,w(),as().then(function(L){try{return u=L?c:Cp(c,d),w(),f=e.initialQuality||1,g=e.fileType||t.type,qi(u,g,t.name,t.lastModified,f).then(function($){try{{let D=function(){if(i--&&(x>a||x>b)){let z,H;return z=S?.95*N.width:N.width,H=S?.95*N.height:N.height,[C,k]=Yi(z,H),k.drawImage(N,0,0,z,H),f*=g==="image/png"?.85:.95,qi(C,g,t.name,t.lastModified,f).then((function(j){try{return E=j,$n(N),N=C,x=E.size,M(Math.min(99,Math.floor((_-x)/(_-a)*100))),D}catch(W){return s(W)}}),s)}return[1]},O=function(){return $n(N),$n(C),$n(c),$n(u),$n(l),M(100),r(E)};if(m=$,w(),p=m.size>a,y=m.size>t.size,!p&&!y)return M(100),r(m);var A;return b=t.size,_=m.size,x=_,N=u,S=!e.alwaysKeepResolution&&p,(A=function(z){for(;z;){if(z.then)return void z.then(A,s);try{if(z.pop){if(z.length)return z.pop()?O.call(this):z;z=D}else z=z.call(this)}catch(H){return s(H)}}}.bind(this))(D)}}catch(D){return s(D)}}.bind(this),s)}catch($){return s($)}}.bind(this),s)}catch(L){return s(L)}}.bind(this),s)}catch(P){return s(P)}}.bind(this),s)}))}const aE=` + `,children:[h.jsx(oe,{name:x.faIcon,className:"w-4 h-4"}),h.jsx(J,{children:x.label}),y?.faIcon===x.faIcon&&h.jsx(oe,{name:"check",className:"w-4 h-4 ml-auto text-blue-600"})]},x.value)):h.jsx(I,{className:"px-3 py-4 text-sm text-gray-500 dark:text-gray-400 text-center",children:o})})]})]})},dc=({defaultOpen:e=!1,isOpen:t,onToggle:n,className:r="",style:s,children:o,disabled:i=!1,id:a,editorAttrs:l})=>{const[c,d]=v.useState(e),u=t!==void 0,f=u?t:c,g=v.useRef(null),[m,p]=v.useState(e?void 0:0),y=()=>{if(i)return;const _=!f;u||d(_),n?.(_)};v.useEffect(()=>{if(g.current)if(f){p(g.current.scrollHeight);const _=setTimeout(()=>{p(void 0)},300);return()=>clearTimeout(_)}else p(g.current.scrollHeight),requestAnimationFrame(()=>{p(0)})},[f]);const b={};if(v.Children.forEach(o,_=>{if(v.isValidElement(_)){const x=_.props?.slot;x==="trigger"?b.trigger=_:x==="content"&&(b.content=_)}}),!b.trigger&&!b.content){const _=v.Children.toArray(o);_.length>=2?(b.trigger=_[0],b.content=_.slice(1)):_.length===1&&(b.content=_[0])}return h.jsxs(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden ${r}`,style:s,id:a,...l,children:[h.jsxs(ae,{type:"button",onClick:y,disabled:i,className:`w-full flex items-center justify-between p-3 text-left bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${i?"opacity-50 cursor-not-allowed":""} ${f?"border-b border-gray-200 dark:border-gray-700":""}`,"aria-expanded":f,children:[h.jsx(I,{className:"flex-1",children:b.trigger}),!b.trigger&&h.jsx("svg",{className:`w-5 h-5 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),h.jsx(I,{ref:g,className:"overflow-hidden transition-all duration-300 ease-in-out",style:{height:m!==void 0?`${m}px`:"auto"},children:h.jsx(I,{className:"bg-white dark:bg-gray-800",children:b.content})})]})},fo=window.G7Core,dg=fo?.useControllableState,ug=(e,t)=>window.G7Core?.t?.(e,t)??e,o_={"":"common.scope_type_all_desc",self:"common.scope_type_self_desc",role:"common.scope_type_role_desc"},i_=({options:e,value:t,onChange:n,disabled:r=!1})=>{const[s,o]=v.useState(!1),[i,a]=v.useState({}),l=v.useRef(null),c=e.find(u=>u.value===t)??e[0];v.useEffect(()=>{if(!s)return;const u=g=>{l.current&&!l.current.contains(g.target)&&o(!1)},f=()=>{if(!l.current)return;const g=l.current.getBoundingClientRect();a({position:"fixed",top:g.bottom+4,right:window.innerWidth-g.right,zIndex:9999})};return document.addEventListener("mousedown",u),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{document.removeEventListener("mousedown",u),window.removeEventListener("scroll",f,!0),window.removeEventListener("resize",f)}},[s]);const d=v.useCallback(()=>{if(r||!l.current)return;const u=l.current.getBoundingClientRect();a({position:"fixed",top:u.bottom+4,right:window.innerWidth-u.right,zIndex:9999}),o(f=>!f)},[r]);return h.jsxs(I,{ref:l,className:"relative flex-shrink-0",children:[h.jsxs(I,{role:"button",tabIndex:0,onClick:u=>{u.stopPropagation(),u.preventDefault(),d()},onMouseDown:u=>u.stopPropagation(),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.stopPropagation(),u.preventDefault(),d()),u.key==="Escape"&&o(!1)},className:`flex items-center gap-1 text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer select-none ${r?"opacity-50 cursor-not-allowed":"hover:border-blue-400 dark:hover:border-blue-500"}`,children:[h.jsx(J,{children:c?.label??""}),h.jsx(oe,{name:ue.ChevronDown,className:"w-3 h-3 text-gray-400 dark:text-gray-500"})]}),s&&h.jsx(I,{style:i,className:"w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1",children:e.map(u=>{const f=u.value??"",g=o_[f],m=g?ug(g):"",p=u.value===t;return h.jsxs(I,{role:"option","aria-selected":p,onClick:y=>{y.stopPropagation(),y.preventDefault(),n(u.value),o(!1)},onMouseDown:y=>y.stopPropagation(),className:`px-3 py-2 cursor-pointer transition-colors ${p?"bg-blue-50 dark:bg-blue-900/30":"hover:bg-gray-50 dark:hover:bg-gray-700/50"}`,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[h.jsx(J,{className:`text-xs font-medium ${p?"text-blue-700 dark:text-blue-300":"text-gray-900 dark:text-white"}`,children:u.label}),p&&h.jsx(oe,{name:ue.Check,className:"w-3.5 h-3.5 text-blue-600 dark:text-blue-400"})]}),m&&h.jsx(J,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:m})]},f||"__null__")})})]})},a_=(e,t)=>{if(e.length!==t.length)return!1;const n=[...e].sort((s,o)=>s.id-o.id),r=[...t].sort((s,o)=>s.id-o.id);return n.every((s,o)=>s.id===r[o].id&&s.scope_type===r[o].scope_type)},l_=({data:e=[],value:t=[],onChange:n,scopeOptions:r=[],className:s="",disabled:o=!1,desktopColumns:i=2,installedModules:a=[],installedPlugins:l=[],id:c,editorAttrs:d})=>{const u=v.useCallback(E=>{if(n){const A=fo?.createChangeEvent?fo.createChangeEvent({value:E}):{target:{value:E}};n(A)}},[n]),[f,g]=dg?dg(t,[],u,{isEqual:a_}):(()=>{const[E,A]=v.useState(t),P=v.useRef(n);P.current=n;const L=v.useRef(!1);v.useEffect(()=>{if(L.current){L.current=!1;return}A(t)},[t]);const $=v.useCallback(D=>{L.current=!0,A(O=>{const T=typeof D=="function"?D(O):D;if(P.current){const z=fo?.createChangeEvent?fo.createChangeEvent({value:T}):{target:{value:T}};P.current(z)}return T})},[]);return[E,$]})(),m=v.useMemo(()=>new Set(f.map(E=>E.id)),[f]),p=v.useCallback(E=>E.is_assignable?[E.id]:E.children.flatMap(A=>p(A)),[]),y=v.useCallback(E=>{const A=p(E);return A.length>0&&A.every(P=>m.has(P))},[m,p]),b=v.useCallback(E=>{const A=p(E),P=A.filter(L=>m.has(L)).length;return P>0&&Pp(E).filter(P=>m.has(P)).length,[m,p]),x=v.useCallback((E,A)=>{o||g(P=>A?[...P,{id:E,scope_type:null}]:P.filter(L=>L.id!==E))},[o,g]),M=v.useCallback((E,A)=>{if(o)return;const P=p(E),L=new Set(P);g($=>{if(A){const D=new Set($.map(T=>T.id)),O=P.filter(T=>!D.has(T)).map(T=>({id:T,scope_type:null}));return[...$,...O]}else return $.filter(D=>!L.has(D.id))})},[o,p,g]),S=v.useCallback((E,A)=>{o||g(P=>P.map(L=>L.id===E?{...L,scope_type:A}:L))},[o,g]),k=E=>{const A=m.has(E.id),P=!!E.resource_route_key,L=A&&P&&r.length>0,$=f.find(D=>D.id===E.id)?.scope_type??null;return h.jsxs(ln,{className:`flex items-start gap-3 p-3 rounded-lg border cursor-pointer transition-colors ${A?"border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20":"border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50"} ${o?"opacity-50 cursor-not-allowed":""}`,children:[h.jsx(kt,{type:"checkbox",checked:A,onChange:D=>{D.stopPropagation(),x(E.id,D.target.checked)},disabled:o,className:"w-4 h-4 mt-0.5 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsxs(I,{className:"flex-1 min-w-0",children:[h.jsxs(I,{className:"flex items-center justify-between gap-2",children:[h.jsx(J,{className:"text-sm font-medium text-gray-900 dark:text-white",children:E.name}),L&&h.jsx(i_,{options:r,value:$,onChange:D=>S(E.id,D),disabled:o})]}),E.description&&h.jsx(J,{className:"block text-xs text-gray-500 dark:text-gray-400 mt-0.5",children:E.description})]})]},E.id)},N=(E,A)=>{const P=y(E),L=b(E),$=_(E),D=E.children.filter(z=>z.is_assignable);if(D.length===0&&E.children.length===0)return null;const O=E.children.some(z=>!z.is_assignable),T=i===1?"grid-cols-1":i===3?"grid-cols-1 md:grid-cols-3":"grid-cols-1 md:grid-cols-2";return h.jsxs(dc,{defaultOpen:!1,className:"border-0 rounded-none",children:[h.jsxs(I,{slot:"trigger",className:"flex items-center gap-3 w-full py-2",children:[h.jsx("input",{type:"checkbox",checked:P,ref:z=>{z&&(z.indeterminate=L)},onChange:z=>{z.stopPropagation(),M(E,z.target.checked)},onClick:z=>z.stopPropagation(),onMouseDown:z=>z.stopPropagation(),disabled:o,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(oe,{name:ue.Folder,className:"w-4 h-4 text-gray-400 dark:text-gray-500"}),h.jsx(J,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:E.name}),h.jsxs(J,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 ml-1",children:[$,"/",E.leaf_count]}),h.jsx(oe,{name:ue.ChevronDown,className:"w-4 h-4 text-gray-400 dark:text-gray-500 ml-auto"})]}),h.jsx(I,{slot:"content",className:`pl-8 grid ${T} gap-2 py-2`,children:O?E.children.map(z=>z.is_assignable?k(z):N(z)):D.map(z=>k(z))})]},E.id)},C=E=>{if(E.extension_type==="core")return"core";if(E.extension_type==="module")return"module";if(E.extension_type==="plugin")return"plugin";const A=E.extension_identifier??E.module??"";return A==="core"||!A?"core":l?.some(L=>L.identifier===A)?"plugin":"module"},w=E=>{const A=y(E),P=b(E),L=_(E),$=C(E);return h.jsxs(dc,{defaultOpen:!1,className:"mb-2",children:[h.jsxs(I,{slot:"trigger",className:"flex items-center gap-3 w-full py-2",children:[h.jsx("input",{type:"checkbox",checked:A,ref:D=>{D&&(D.indeterminate=P)},onChange:D=>{D.stopPropagation(),M(E,D.target.checked)},onClick:D=>D.stopPropagation(),onMouseDown:D=>D.stopPropagation(),disabled:o,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(oe,{name:$==="core"?ue.Cog:ue.Cube,className:"w-5 h-5 text-gray-400 dark:text-gray-500"}),h.jsx(J,{className:"font-medium text-gray-900 dark:text-white",children:E.name}),$!=="core"&&h.jsx(gh,{type:$,identifier:E.extension_identifier??E.module,installedModules:a,installedPlugins:l}),h.jsxs(J,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 ml-2",children:[L,"/",E.leaf_count]}),h.jsx(oe,{name:ue.ChevronDown,className:"w-4 h-4 text-gray-400 dark:text-gray-500 ml-auto"})]}),h.jsx(I,{slot:"content",className:"pl-8 space-y-2 pb-2",children:E.children.map(D=>D.is_assignable?k(D):N(D))})]},E.id)};return e.length===0?h.jsx(I,{className:`text-sm text-gray-400 dark:text-gray-500 py-4 text-center ${s}`,id:c,...d,children:ug("common.no_data")??"데이터가 없습니다."}):h.jsx(I,{className:`space-y-2 ${s}`,id:c,...d,children:e.map(E=>w(E))})};function c_({text:e,keyword:t}){if(!t||!e)return h.jsx(h.Fragment,{children:e});const n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),r=e.split(new RegExp(`(${n})`,"gi"));return h.jsx(h.Fragment,{children:r.map((s,o)=>s.toLowerCase()===t.toLowerCase()?h.jsx(J,{className:"bg-yellow-200 dark:bg-yellow-700 rounded px-0.5",children:s},o):s)})}const d_=({data:e=[],expandedIds:t=[],selectedIds:n=[],searchKeyword:r="",showProductCount:s=!1,selectable:o=!1,className:i="",onToggle:a,onSelectionChange:l,id:c,editorAttrs:d})=>{const u=v.useCallback(y=>{if(!a)return;const b=t.includes(y)?t.filter(_=>_!==y):[...t,y];a(b)},[t,a]),f=v.useCallback((y,b)=>{if(!l)return;const _=b?[...n,y]:n.filter(x=>x!==y);l(_)},[n,l]),g=v.useMemo(()=>{if(!r)return null;const y=new Set,b=_=>{for(const x of _)(x.localized_name||x.name).toLowerCase().includes(r.toLowerCase())&&y.add(x.id),x.children&&b(x.children)};return b(e),y},[e,r]),m=v.useCallback(y=>!g||g.has(y.id)?!0:(y.children??[]).some(b=>m(b)),[g]),p=(y,b=0)=>{const _=(y.children??[]).length>0,x=t.includes(y.id),M=n.includes(y.id),S=y.localized_name||y.name;return r&&!m(y)?null:h.jsxs(I,{"data-testid":`category-node-${y.id}`,children:[h.jsxs(I,{className:`flex items-center gap-2 py-1.5 px-2 rounded cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors ${b>0?"ml-"+Math.min(b*5,20):""}`,style:b>0?{marginLeft:`${b*20}px`}:void 0,children:[_?h.jsx(I,{className:"flex-shrink-0 w-5 h-5 flex items-center justify-center text-gray-400 dark:text-gray-500",onClick:()=>u(y.id),"data-testid":`toggle-${y.id}`,children:h.jsx(oe,{name:x?ue.CaretDown:ue.CaretRight,className:"text-xs"})}):h.jsx(I,{className:"flex-shrink-0 w-5 h-5"}),o&&h.jsx(_n,{checked:M,onChange:k=>{k.stopPropagation(),f(y.id,k.target.checked)},className:"flex-shrink-0","data-testid":`checkbox-${y.id}`}),h.jsx(oe,{name:ue.Folder,className:`flex-shrink-0 text-sm ${_?"text-yellow-500 dark:text-yellow-400":"text-gray-400 dark:text-gray-500"}`}),h.jsx(J,{className:"text-sm text-gray-700 dark:text-gray-300 select-none",onClick:()=>_&&u(y.id),children:h.jsx(c_,{text:S,keyword:r})}),s&&y.products_count!==void 0&&h.jsx(J,{className:"inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-400 ml-1",children:y.products_count})]}),_&&x&&h.jsx(I,{"data-testid":`children-${y.id}`,children:(y.children??[]).map(k=>p(k,b+1))})]},y.id)};return e.length===0?h.jsx(I,{className:`text-sm text-gray-400 dark:text-gray-500 py-4 text-center ${i}`,id:c,...d,children:"데이터가 없습니다."}):h.jsx(I,{className:`border border-gray-200 dark:border-gray-700 rounded-lg p-2 max-h-80 overflow-y-auto ${i}`,id:c,...d,children:e.map(y=>p(y,0))})},fg="carousel",ho="controller",u_="navigation",f_="no-scroll",uc="portal",h_="root",hg="toolbar",g_="counter",go="fullscreen",gg="slideshow",po="thumbnails",pg="zoom",hs="loading",fc="playing",mo="error",bo="complete",p_="placeholder",yo=e=>`active-slide-${e}`,m_=yo(hs),b_=yo(fc),y_=yo(mo),v_=yo(bo),mg="fullsize",gs="flex_center",x_="no_scroll",bg="no_scroll_padding",hc="slide",yg="slide_wrapper",w_="slide_wrapper_interactive",dr="prev",ur="next",gc="swipe",ps="close",vg="onPointerDown",xg="onPointerMove",wg="onPointerUp",kg="onPointerLeave",Cg="onPointerCancel",pc="onKeyDown",k_="onKeyUp",mc="onWheel",C_="Escape",S_="ArrowLeft",__="ArrowRight",E_="button",Bi="icon",Sg="contain",_g="cover",Eg="Unknown action type",Mg="yarl__";function Ut(...e){return e.filter(Boolean).join(" ")}function Me(e){return`${Mg}${e}`}function ft(e){return`--${Mg}${e}`}function Sr(e,t){return`${e}${t?`_${t}`:""}`}function _r(e){return t=>Sr(e,t)}function Er(e,t){var n;return(n=e?.[t])!==null&&n!==void 0?n:t}function Ng(e,t,n){return Er(e,"{index} of {total}").replace(/\{index}/g,`${xc(n,t.length)+1}`).replace(/\{total}/g,`${t.length}`)}function ms(...e){return()=>{e.forEach(t=>{t()})}}function un(e,t,n){return()=>{const r=F.useContext(n);if(!r)throw new Error(`${e} must be used within a ${t}.Provider`);return r}}function bc(){return typeof window<"u"}function yc(e,t=0){const n=10**t;return Math.round((e+Number.EPSILON)*n)/n}function fr(e){return e.type===void 0||e.type==="image"}function vc(e,t){return e.imageFit===_g||e.imageFit!==Sg&&t===_g}function ji(e){return typeof e=="string"?Number.parseInt(e,10):e}function Wi(e){if(typeof e=="number")return{pixel:e};if(typeof e=="string"){const t=ji(e);return e.endsWith("%")?{percent:t}:{pixel:t}}return{pixel:0}}function M_(e,t){const n=Wi(t),r=n.percent!==void 0?e.width/100*n.percent:n.pixel;return{width:Math.max(e.width-2*r,0),height:Math.max(e.height-2*r,0)}}function N_(){return(bc()?window?.devicePixelRatio:void 0)||1}function xc(e,t){return t>0?(e%t+t)%t:0}function wc(e){return e.length>0}function kc(e,t){return e[xc(t,e.length)]}function Cc(e,t){return wc(e)?kc(e,t):void 0}function Tg(e){return fr(e)?e.src:void 0}function Gi(e,t,n){if(!n)return e;const{buttons:r,...s}=e,o=r.findIndex(a=>a===t),i=F.isValidElement(n)?F.cloneElement(n,{key:t},null):n;if(o>=0){const a=[...r];return a.splice(o,1,i),{buttons:a,...s}}return{buttons:[i,...r],...s}}function Og(e,t,n=0){return Math.min(e.preload,Math.max(e.finite?t.length-1:Math.floor(t.length/2),n))}const T_=Number(F.version.split(".")[0])>=19;function O_(e){return{inert:T_?e:e?"":void 0}}function A_(e){e.scrollTop}const Sc={open:!1,close:()=>{},index:0,slides:[],render:{},plugins:[],toolbar:{buttons:[ps]},labels:{},animation:{fade:250,swipe:500,easing:{fade:"ease",swipe:"ease-out",navigation:"ease-in-out"}},carousel:{finite:!1,preload:2,padding:"16px",spacing:"30%",imageFit:Sg,imageProps:{}},controller:{ref:null,focus:!0,aria:!1,touchAction:"none",closeOnPullUp:!1,closeOnPullDown:!1,closeOnBackdropClick:!1,closeOnEscape:!0,preventDefaultWheelX:!0,preventDefaultWheelY:!1,disableSwipeNavigation:!1},portal:{},noScroll:{disabled:!1},on:{},styles:{},className:""};function gn(e,t){return{name:e,component:t}}function At(e,t){return{module:e,children:t}}function Ag(e,t,n){return e.module.name===t?n(e):e.children?[At(e.module,e.children.flatMap(r=>{var s;return(s=Ag(r,t,n))!==null&&s!==void 0?s:[]}))]:[e]}function bs(e,t,n){return e.flatMap(r=>{var s;return(s=Ag(r,t,n))!==null&&s!==void 0?s:[]})}function D_(e,t=[],n=[]){let r=e;const s=g=>{const m=[...r];for(;m.length>0;){const p=m.pop();if(p?.module.name===g)return!0;p?.children&&m.push(...p.children)}return!1},o=(g,m)=>{if(g===""){r=[At(m,r)];return}r=bs(r,g,p=>[At(m,[p])])},i=(g,m)=>{r=bs(r,g,p=>[At(p.module,[At(m,p.children)])])},a=(g,m,p)=>{r=bs(r,g,y=>{var b;return[At(y.module,[...p?[At(m)]:[],...(b=y.children)!==null&&b!==void 0?b:[],...p?[]:[At(m)]])]})},l=(g,m,p)=>{r=bs(r,g,y=>[...p?[At(m)]:[],y,...p?[]:[At(m)]])},c=g=>{i(ho,g)},d=(g,m)=>{r=bs(r,g,p=>[At(m,p.children)])},u=g=>{r=bs(r,g,m=>m.children)},f=g=>{n.push(g)};return t.forEach(g=>{g({contains:s,addParent:o,append:i,addChild:a,addSibling:l,addModule:c,replace:d,remove:u,augment:f})}),{config:r,augmentation:g=>n.reduce((m,p)=>p(m),g)}}const Dg=F.createContext(null),_c=un("useA11yContext","A11yContext",Dg);function I_({children:e}){const[t,n]=F.useState(!1),[r,s]=F.useState(!1),o=F.useMemo(()=>({focusWithin:t,trackFocusWithin:(a,l)=>{const c=d=>u=>{var f;u.currentTarget.contains(u.relatedTarget)||n(d),(f=d?a:l)===null||f===void 0||f(u)};return{onFocus:c(!0),onBlur:c(!1)}},autoPlaying:r,setAutoPlaying:s}),[t,r]);return F.createElement(Dg.Provider,{value:o},e)}const Ig=F.createContext(null),ys=un("useDocument","DocumentContext",Ig);function L_({nodeRef:e,children:t}){const n=F.useMemo(()=>{const r=o=>{var i;return((i=o||e.current)===null||i===void 0?void 0:i.ownerDocument)||document};return{getOwnerDocument:r,getOwnerWindow:o=>{var i;return((i=r(o))===null||i===void 0?void 0:i.defaultView)||window}}},[e]);return F.createElement(Ig.Provider,{value:n},t)}const Lg=F.createContext(null),vs=un("useEvents","EventsContext",Lg);function P_({children:e}){const[t]=F.useState({});F.useEffect(()=>()=>{Object.keys(t).forEach(r=>delete t[r])},[t]);const n=F.useMemo(()=>{const r=(i,a)=>{var l;(l=t[i])===null||l===void 0||l.splice(0,t[i].length,...t[i].filter(c=>c!==a))};return{publish:(...[i,a])=>{var l;(l=t[i])===null||l===void 0||l.forEach(c=>c(a))},subscribe:(i,a)=>(t[i]||(t[i]=[]),t[i].push(a),()=>r(i,a)),unsubscribe:r}},[t]);return F.createElement(Lg.Provider,{value:n},e)}const Pg=F.createContext(null),Dt=un("useLightboxProps","LightboxPropsContext",Pg);function $g({children:e,...t}){return F.createElement(Pg.Provider,{value:t},e)}const Fg=F.createContext(null),pn=un("useLightboxState","LightboxStateContext",Fg),zg=F.createContext(null),$_=un("useLightboxDispatch","LightboxDispatchContext",zg);function F_(e,t){switch(t.type){case"swipe":{const{slides:n}=e,r=t?.increment||0,s=e.globalIndex+r,o=xc(s,n.length),i=Cc(n,o),a=r||t.duration!==void 0?{increment:r,duration:t.duration,easing:t.easing}:void 0;return{slides:n,currentIndex:o,globalIndex:s,currentSlide:i,animation:a}}case"update":return t.slides!==e.slides||t.index!==e.currentIndex?{slides:t.slides,currentIndex:t.index,globalIndex:t.index,currentSlide:Cc(t.slides,t.index)}:e;default:throw new Error(Eg)}}function z_({slides:e,index:t,children:n}){const[r,s]=F.useReducer(F_,{slides:e,currentIndex:t,globalIndex:t,currentSlide:Cc(e,t)}),[o,i]=F.useState(e),[a,l]=F.useState(t);(e!==o||t!==a)&&(i(e),l(t),s({type:"update",slides:e,index:t}));const c=F.useMemo(()=>({...r,state:r,dispatch:s}),[r,s]);return F.createElement(zg.Provider,{value:s},F.createElement(Fg.Provider,{value:c},n))}const Vg=F.createContext(null),V_=un("useRTLContext","RTLContext",Vg);function H_({isRTL:e,children:t}){const n=F.useMemo(()=>({isRTL:e}),[e]);return F.createElement(Vg.Provider,{value:n},t)}const Hg=F.createContext(null),vo=un("useTimeouts","TimeoutsContext",Hg);function U_({children:e}){const[t]=F.useState([]);F.useEffect(()=>()=>{t.forEach(r=>window.clearTimeout(r)),t.splice(0,t.length)},[t]);const n=F.useMemo(()=>{const r=i=>{t.splice(0,t.length,...t.filter(a=>a!==i))};return{setTimeout:(i,a)=>{const l=window.setTimeout(()=>{r(l),i()},a);return t.push(l),l},clearTimeout:i=>{i!==void 0&&(r(i),window.clearTimeout(i))}}},[t]);return F.createElement(Hg.Provider,{value:n},e)}const xs=F.forwardRef(function({label:t,className:n,icon:r,renderIcon:s,onClick:o,style:i,...a},l){const{styles:c,labels:d}=Dt(),u=Er(d,t);return F.createElement("button",{ref:l,type:"button",title:u,"aria-label":u,className:Ut(Me(E_),n),onClick:o,style:{...i,...c.button},...a},s?s():F.createElement(r,{className:Me(Bi),style:c.icon}))});function Ug(e,t){const n=r=>F.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"24",height:"24","aria-hidden":"true",focusable:"false",...r},t);return n.displayName=e,n}function en(e,t){return Ug(e,F.createElement("g",{fill:"currentColor"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"none"}),t))}function B_(e,t){return Ug(e,F.createElement(F.Fragment,null,F.createElement("defs",null,F.createElement("mask",{id:"strike"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"white"}),F.createElement("path",{d:"M0 0L24 24",stroke:"black",strokeWidth:4}))),F.createElement("path",{d:"M0.70707 2.121320L21.878680 23.292883",stroke:"currentColor",strokeWidth:2}),F.createElement("g",{fill:"currentColor",mask:"url(#strike)"},F.createElement("path",{d:"M0 0h24v24H0z",fill:"none"}),t)))}const j_=en("Close",F.createElement("path",{d:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"})),W_=en("Previous",F.createElement("path",{d:"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"})),G_=en("Next",F.createElement("path",{d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"})),Y_=en("Loading",F.createElement(F.Fragment,null,Array.from({length:8}).map((e,t,n)=>F.createElement("line",{key:t,x1:"12",y1:"6.5",x2:"12",y2:"1.8",strokeLinecap:"round",strokeWidth:"2.6",stroke:"currentColor",strokeOpacity:1/n.length*(t+1),transform:`rotate(${360/n.length*t}, 12, 12)`})))),X_=en("Error",F.createElement("path",{d:"M21.9,21.9l-8.49-8.49l0,0L3.59,3.59l0,0L2.1,2.1L0.69,3.51L3,5.83V19c0,1.1,0.9,2,2,2h13.17l2.31,2.31L21.9,21.9z M5,18 l3.5-4.5l2.5,3.01L12.17,15l3,3H5z M21,18.17L5.83,3H19c1.1,0,2,0.9,2,2V18.17z"})),Nn=bc()?F.useLayoutEffect:F.useEffect;function Ec(){const[e,t]=F.useState(!1);return F.useEffect(()=>{var n,r;const s=(n=window.matchMedia)===null||n===void 0?void 0:n.call(window,"(prefers-reduced-motion: reduce)");t(s?.matches);const o=i=>t(i.matches);return(r=s?.addEventListener)===null||r===void 0||r.call(s,"change",o),()=>{var i;return(i=s?.removeEventListener)===null||i===void 0?void 0:i.call(s,"change",o)}},[]),e}function K_(e){let t=0,n=0,r=0;const o=window.getComputedStyle(e).transform.match(/matrix.*\((.+)\)/);if(o){const i=o[1].split(",").map(ji);i.length===6?(t=i[4],n=i[5]):i.length===16&&(t=i[12],n=i[13],r=i[14])}return{x:t,y:n,z:r}}function Mc(e,t){const n=F.useRef(void 0),r=F.useRef(void 0),s=Ec();return Nn(()=>{var o,i,a;if(e.current&&n.current!==void 0&&!s){const{keyframes:l,duration:c,easing:d,onfinish:u}=t(n.current,e.current.getBoundingClientRect(),K_(e.current))||{};if(l&&c){(o=r.current)===null||o===void 0||o.cancel(),r.current=void 0;try{r.current=(a=(i=e.current).animate)===null||a===void 0?void 0:a.call(i,l,{duration:c,easing:d})}catch(f){console.error(f)}r.current&&(r.current.onfinish=()=>{r.current=void 0,u?.()})}}n.current=void 0}),{prepareAnimation:o=>{n.current=o},isAnimationPlaying:()=>{var o;return((o=r.current)===null||o===void 0?void 0:o.playState)==="running"}}}function Bg(){const e=F.useRef(null),t=F.useRef(void 0),[n,r]=F.useState();return{setContainerRef:F.useCallback(o=>{e.current=o,t.current&&(t.current.disconnect(),t.current=void 0);const i=()=>{if(o){const a=window.getComputedStyle(o),l=c=>parseFloat(c)||0;r({width:Math.round(o.clientWidth-l(a.paddingLeft)-l(a.paddingRight)),height:Math.round(o.clientHeight-l(a.paddingTop)-l(a.paddingBottom))})}else r(void 0)};i(),o&&typeof ResizeObserver<"u"&&(t.current=new ResizeObserver(i),t.current.observe(o))},[]),containerRef:e,containerRect:n}}function Yi(){const e=F.useRef(void 0),{setTimeout:t,clearTimeout:n}=vo();return F.useCallback((r,s)=>{n(e.current),e.current=t(r,s>0?s:0)},[t,n])}function We(e){const t=F.useRef(e);return Nn(()=>{t.current=e}),F.useCallback((...n)=>{var r;return(r=t.current)===null||r===void 0?void 0:r.call(t,...n)},[])}function jg(e,t){typeof e=="function"?e(t):e&&(e.current=t)}function Nc(e,t){return F.useMemo(()=>e==null&&t==null?null:n=>{jg(e,n),jg(t,n)},[e,t])}function Wg(e,t=!1){const n=F.useRef(!1);Nn(()=>{t&&n.current&&(n.current=!1,e())},[t,e]);const r=F.useCallback(()=>{n.current=!0},[]),s=F.useCallback(()=>{n.current=!1},[]);return{onFocus:r,onBlur:s}}function Xi(){return V_().isRTL}function Gg(){const[e]=F.useState({}),t=F.useCallback((s,o)=>{var i;(i=e[s])===null||i===void 0||i.forEach(a=>{o.isPropagationStopped()||a(o)})},[e]),n=F.useMemo(()=>({onPointerDown:s=>t(vg,s),onPointerMove:s=>t(xg,s),onPointerUp:s=>t(wg,s),onPointerLeave:s=>t(kg,s),onPointerCancel:s=>t(Cg,s),onKeyDown:s=>t(pc,s),onKeyUp:s=>t(k_,s),onWheel:s=>t(mc,s)}),[t]),r=F.useCallback((s,o)=>(e[s]||(e[s]=[]),e[s].unshift(o),()=>{const i=e[s];i&&i.splice(0,i.length,...i.filter(a=>a!==o))}),[e]);return{registerSensors:n,subscribeSensors:r}}function Yg(e,t){const n=F.useRef(0),r=Yi(),s=We((...o)=>{n.current=Date.now(),e(o)});return F.useCallback((...o)=>{r(()=>{s(o)},t-(Date.now()-n.current))},[t,s,r])}const Tc=_r("slide"),Oc=_r("slide_image");function Mr({slide:e,offset:t,render:n,rect:r,imageFit:s,imageProps:o,onClick:i,onLoad:a,onError:l,style:c}){var d,u,f,g,m,p,y,b;const[_,x]=F.useState(hs),{publish:M}=vs(),{setTimeout:S}=vo(),k=F.useRef(null);F.useEffect(()=>{t===0&&M(yo(_))},[t,_,M]);const N=We(B=>{("decode"in B?B.decode():Promise.resolve()).catch(()=>{}).then(()=>{B.parentNode&&(x(bo),S(()=>{a?.(B)},0))})}),C=F.useCallback(B=>{k.current=B,B?.complete&&N(B)},[N]),w=F.useCallback(B=>{N(B.currentTarget)},[N]),E=We(()=>{x(mo),l?.()}),A=vc(e,s),P=(B,j)=>Number.isFinite(B)?B:j,L=P(Math.max(...((u=(d=e.srcSet)===null||d===void 0?void 0:d.map(B=>B.width))!==null&&u!==void 0?u:[]).concat(e.width?[e.width]:[]).filter(Boolean)),((f=k.current)===null||f===void 0?void 0:f.naturalWidth)||0),$=P(Math.max(...((m=(g=e.srcSet)===null||g===void 0?void 0:g.map(B=>B.height))!==null&&m!==void 0?m:[]).concat(e.height?[e.height]:[]).filter(Boolean)),((p=k.current)===null||p===void 0?void 0:p.naturalHeight)||0),D=L&&$?{maxWidth:`min(${L}px, 100%)`,maxHeight:`min(${$}px, 100%)`}:{maxWidth:"100%",maxHeight:"100%"},O=(y=e.srcSet)===null||y===void 0?void 0:y.slice().sort((B,j)=>B.width-j.width).map(B=>`${B.src} ${B.width}w`).join(", "),T=()=>r&&!A&&e.width&&e.height?r.height/e.height*e.width:Number.MAX_VALUE,z=O&&r&&bc()?`${Math.round(Math.min(T(),r.width))}px`:void 0,{style:U,className:W,...G}=(typeof o=="function"?o(e):o)||{};return F.createElement(F.Fragment,null,F.createElement("img",{ref:C,onLoad:w,onError:E,onClick:i,draggable:!1,className:Ut(Me(Oc()),A&&Me(Oc("cover")),_!==bo&&Me(Oc("loading")),W),style:{...D,...c,...U},...G,alt:(b=e.alt)!==null&&b!==void 0?b:"",sizes:z,srcSet:O,src:e.src}),_!==bo&&F.createElement("div",{className:Me(Tc(p_))},_===hs&&(n?.iconLoading?n.iconLoading():F.createElement(Y_,{className:Ut(Me(Bi),Me(Tc(hs)))})),_===mo&&(n?.iconError?n.iconError():F.createElement(X_,{className:Ut(Me(Bi),Me(Tc(mo)))}))))}const q_=F.forwardRef(function({className:t,children:n,onFocus:r,onBlur:s,...o},i){const a=F.useRef(null),[l,c]=F.useState(!1),{trackFocusWithin:d}=_c(),u=We(()=>{if(a.current){const f=window.getComputedStyle(a.current).direction==="rtl";f!==l&&c(f)}});return F.useEffect(u),F.createElement(L_,{nodeRef:a},F.createElement(H_,{isRTL:l},F.createElement("div",{ref:Nc(i,a),className:Ut(Me("root"),t),...d(r,s),...o},n)))});var Bt;(function(e){e[e.NONE=0]="NONE",e[e.SWIPE=1]="SWIPE",e[e.PULL=2]="PULL",e[e.ANIMATION=3]="ANIMATION"})(Bt||(Bt={}));function Xg(e,t,n,r,s){F.useEffect(()=>s?()=>{}:ms(e(vg,t),e(xg,n),e(wg,r),e(kg,r),e(Cg,r)),[e,t,n,r,s])}var Tn;(function(e){e[e.NONE=0]="NONE",e[e.SWIPE=1]="SWIPE",e[e.PULL=2]="PULL"})(Tn||(Tn={}));const Ac=30;function Z_({disableSwipeNavigation:e,closeOnBackdropClick:t},n,r,s,o,i,a,l,c,d,u,f,g,m,p,y){const b=F.useRef(0),_=F.useRef([]),x=F.useRef(void 0),M=F.useRef(0),S=F.useRef(Tn.NONE),k=F.useCallback(L=>{x.current===L.pointerId&&(x.current=void 0,S.current=Tn.NONE);const $=_.current;$.splice(0,$.length,...$.filter(D=>D.pointerId!==L.pointerId))},[]),N=F.useCallback(L=>{k(L),L.persist(),_.current.push(L)},[k]),C=F.useCallback(L=>_.current.find(({pointerId:$})=>L.pointerId===$),[]),w=We(L=>{N(L)}),E=(L,$)=>u&&L>$||d&&L<-$,A=We(L=>{const $=C(L);if($)if(x.current===L.pointerId){const D=Date.now()-M.current,O=b.current;S.current===Tn.SWIPE?Math.abs(O)>.3*s||Math.abs(O)>5&&D{const $=C(L);if($){const D=x.current===L.pointerId;if(L.buttons===0){D&&b.current!==0?A(L):k($);return}const O=L.clientX-$.clientX,T=L.clientY-$.clientY;if(x.current===void 0){const z=U=>{N(L),x.current=L.pointerId,M.current=Date.now(),S.current=U};Math.abs(O)>Math.abs(T)&&Math.abs(O)>Ac&&r(O)?e||(z(Tn.SWIPE),i()):Math.abs(T)>Math.abs(O)&&E(T,Ac)&&(z(Tn.PULL),f())}else D&&(S.current===Tn.SWIPE?(b.current=O,a(O)):S.current===Tn.PULL&&(b.current=T,g(T)))}});Xg(n,w,P,A)}function Q_({preventDefaultWheelX:e,preventDefaultWheelY:t}){const n=F.useRef(null),r=We(s=>{const o=Math.abs(s.deltaX)>Math.abs(s.deltaY);(o&&e||!o&&t||s.ctrlKey)&&s.preventDefault()});return F.useCallback(s=>{var o;s?s.addEventListener("wheel",r,{passive:!1}):(o=n.current)===null||o===void 0||o.removeEventListener("wheel",r),n.current=s},[r])}function J_(e,t,n,r,s,o,i,a,l){const c=F.useRef(0),d=F.useRef(0),u=F.useRef(void 0),f=F.useRef(void 0),g=F.useRef(0),m=F.useRef(void 0),p=F.useRef(0),{setTimeout:y,clearTimeout:b}=vo(),_=F.useCallback(()=>{u.current&&(b(u.current),u.current=void 0)},[b]),x=F.useCallback(()=>{f.current&&(b(f.current),f.current=void 0)},[b]),M=We(()=>{e!==Bt.SWIPE&&(c.current=0,p.current=0,_(),x())});F.useEffect(M,[e,M]);const S=We(N=>{f.current=void 0,c.current===N&&l(c.current)}),k=We(N=>{if(N.ctrlKey||Math.abs(N.deltaY)>Math.abs(N.deltaX))return;const C=w=>{g.current=w,b(m.current),m.current=w>0?y(()=>{g.current=0,m.current=void 0},300):void 0};if(e===Bt.NONE){if(Math.abs(N.deltaX)<=1.2*Math.abs(g.current)){C(N.deltaX);return}if(!n(-N.deltaX))return;if(d.current+=N.deltaX,_(),Math.abs(d.current)>30)d.current=0,C(0),p.current=Date.now(),o();else{const w=d.current;u.current=y(()=>{u.current=void 0,w===d.current&&(d.current=0)},s)}}else if(e===Bt.SWIPE){let w=c.current-N.deltaX;if(w=Math.min(Math.abs(w),r)*Math.sign(w),c.current=w,i(w),x(),Math.abs(w)>.2*r){C(N.deltaX),a(w,Date.now()-p.current);return}f.current=y(()=>S(w),2*s)}else C(N.deltaX)});F.useEffect(()=>t(mc,k),[t,k])}const Kg=_r("container"),qg=F.createContext(null),On=un("useController","ControllerContext",qg);function R_({children:e,...t}){var n;const{carousel:r,animation:s,controller:o,on:i,styles:a,render:l}=t,{closeOnPullUp:c,closeOnPullDown:d,preventDefaultWheelX:u,preventDefaultWheelY:f}=o,[g,m]=F.useState(),p=pn(),y=$_(),[b,_]=F.useState(Bt.NONE),x=F.useRef(0),M=F.useRef(0),S=F.useRef(1),{registerSensors:k,subscribeSensors:N}=Gg(),{subscribe:C,publish:w}=vs(),E=Yi(),A=Yi(),P=Yi(),{containerRef:L,setContainerRef:$,containerRect:D}=Bg(),O=Nc(Q_({preventDefaultWheelX:u,preventDefaultWheelY:f}),$),T=F.useRef(null),z=Nc(T,void 0),{getOwnerDocument:U}=ys(),W=Xi(),G=ie=>(W?-1:1)*(typeof ie=="number"?ie:1),B=We(()=>{var ie;return(ie=L.current)===null||ie===void 0?void 0:ie.focus()}),j=We(()=>t),Y=We(()=>p),V=F.useCallback(ie=>w(dr,ie),[w]),X=F.useCallback(ie=>w(ur,ie),[w]),K=F.useCallback(()=>w(ps),[w]),q=ie=>!(r.finite&&(G(ie)>0&&p.currentIndex===0||G(ie)<0&&p.currentIndex===p.slides.length-1)),ne=ie=>{var _e;x.current=ie,(_e=L.current)===null||_e===void 0||_e.style.setProperty(ft("swipe_offset"),`${Math.round(ie)}px`)},ee=ie=>{var _e,Ae;M.current=ie,S.current=(()=>{const gt=d&&ie>0?ie:c&&ie<0?-ie:0;return Math.min(Math.max(yc(1-gt/60*(1-.5),2),.5),1)})(),(_e=L.current)===null||_e===void 0||_e.style.setProperty(ft("pull_offset"),`${Math.round(ie)}px`),(Ae=L.current)===null||Ae===void 0||Ae.style.setProperty(ft("pull_opacity"),`${S.current}`)},{prepareAnimation:fe}=Mc(T,(ie,_e,Ae)=>{if(T.current&&D)return{keyframes:[{transform:`translate(0, ${ie.rect.y-_e.y+Ae.y}px)`,opacity:ie.opacity},{transform:"translate(0, 0)",opacity:1}],duration:ie.duration,easing:s.easing.fade}}),te=(ie,_e)=>{if(c||d){ee(ie);let Ae=0;T.current&&(Ae=s.fade*(_e?2:1),fe({rect:T.current.getBoundingClientRect(),opacity:S.current,duration:Ae})),P(()=>{ee(0),_(Bt.NONE)},Ae),_(Bt.ANIMATION),_e||K()}},{prepareAnimation:le,isAnimationPlaying:ge}=Mc(T,(ie,_e,Ae)=>{var Ie;if(T.current&&D&&(!((Ie=p.animation)===null||Ie===void 0)&&Ie.duration)){const Be=Wi(r.spacing),gt=(Be.percent?Be.percent*D.width/100:Be.pixel)||0;return{keyframes:[{transform:`translate(${G(p.globalIndex-ie.index)*(D.width+gt)+ie.rect.x-_e.x+Ae.x}px, 0)`},{transform:"translate(0, 0)"}],duration:p.animation.duration,easing:p.animation.easing}}}),ce=We(ie=>{var _e,Ae;const Ie=ie.offset||0,Be=Ie?s.swipe:(_e=s.navigation)!==null&&_e!==void 0?_e:s.swipe,gt=!Ie&&!ge()?s.easing.navigation:s.easing.swipe;let{direction:mt}=ie;const Ge=(Ae=ie.count)!==null&&Ae!==void 0?Ae:1;let ot=Bt.ANIMATION,Fe=Be*Ge;if(!mt){const de=D?.width,xe=ie.duration||0,ve=de?Be/de*Math.abs(Ie):Be;Ge!==0?(xe0?dr:ur):Fe=Be/2}let re=0;mt===dr?q(G(1))?re=-Ge:(ot=Bt.NONE,Fe=Be):mt===ur&&(q(G(-1))?re=Ge:(ot=Bt.NONE,Fe=Be)),Fe=Math.round(Fe),A(()=>{ne(0),_(Bt.NONE)},Fe),T.current&&le({rect:T.current.getBoundingClientRect(),index:p.globalIndex}),_(ot),w(gc,{type:"swipe",increment:re,duration:Fe,easing:gt})});F.useEffect(()=>{var ie,_e;!((ie=p.animation)===null||ie===void 0)&&ie.increment&&(!((_e=p.animation)===null||_e===void 0)&&_e.duration)&&E(()=>y({type:"swipe",increment:0}),p.animation.duration)},[p.animation,y,E]);const ye=[N,q,D?.width||0,s.swipe,()=>_(Bt.SWIPE),ie=>ne(ie),(ie,_e)=>ce({offset:ie,duration:_e,count:1}),ie=>ce({offset:ie,count:0})],me=[()=>{d&&_(Bt.PULL)},ie=>ee(ie),ie=>te(ie),ie=>te(ie,!0)];Z_(o,...ye,c,d,...me,K),J_(b,...ye);const pe=We(()=>{o.focus&&U().querySelector(`.${Me(uc)} .${Me(Kg())}`)&&B()});F.useEffect(pe,[pe]);const ke=We(()=>{var ie;(ie=i.view)===null||ie===void 0||ie.call(i,{index:p.currentIndex})});F.useEffect(ke,[p.globalIndex,ke]),F.useEffect(()=>ms(C(dr,ie=>ce({direction:dr,...ie})),C(ur,ie=>ce({direction:ur,...ie})),C(gc,ie=>y(ie))),[C,ce,y]);const $e=F.useMemo(()=>({prev:V,next:X,close:K,focus:B,slideRect:D?M_(D,r.padding):{width:0,height:0},containerRect:D||{width:0,height:0},subscribeSensors:N,containerRef:L,setCarouselRef:z,toolbarWidth:g,setToolbarWidth:m}),[V,X,K,B,N,D,L,z,g,m,r.padding]);return F.useImperativeHandle(o.ref,()=>({prev:V,next:X,close:K,focus:B,getLightboxProps:j,getLightboxState:Y}),[V,X,K,B,j,Y]),F.createElement("div",{ref:O,className:Ut(Me(Kg()),Me(gs)),style:{...b===Bt.SWIPE?{[ft("swipe_offset")]:`${Math.round(x.current)}px`}:null,...b===Bt.PULL?{[ft("pull_offset")]:`${Math.round(M.current)}px`,[ft("pull_opacity")]:`${S.current}`}:null,...o.touchAction!=="none"?{[ft("controller_touch_action")]:o.touchAction}:null,...a.container},tabIndex:-1,...k},D&&F.createElement(qg.Provider,{value:$e},e,(n=l.controls)===null||n===void 0?void 0:n.call(l)))}const e2=gn(ho,R_);function Nr(e){return Sr(fg,e)}function Zg(e){return Sr(hc,e)}function t2({slide:e,offset:t}){const n=F.useRef(null),{currentIndex:r,slides:s}=pn(),{slideRect:o,focus:i}=On(),{render:a,carousel:{imageFit:l,imageProps:c},on:{click:d},styles:{slide:u},labels:f}=Dt(),{getOwnerDocument:g}=ys(),m=t!==0;F.useEffect(()=>{var y;m&&(!((y=n.current)===null||y===void 0)&&y.contains(g().activeElement))&&i()},[m,i,g]);const p=()=>{var y,b,_,x;let M=(y=a.slide)===null||y===void 0?void 0:y.call(a,{slide:e,offset:t,rect:o});return!M&&fr(e)&&(M=F.createElement(Mr,{slide:e,offset:t,render:a,rect:o,imageFit:l,imageProps:c,onClick:m?void 0:()=>d?.({index:r})})),M?F.createElement(F.Fragment,null,(b=a.slideHeader)===null||b===void 0?void 0:b.call(a,{slide:e}),((_=a.slideContainer)!==null&&_!==void 0?_:(({children:S})=>S))({slide:e,children:M}),(x=a.slideFooter)===null||x===void 0?void 0:x.call(a,{slide:e})):null};return F.createElement("div",{ref:n,className:Ut(Me(Zg()),!m&&Me(Zg("current")),Me(gs)),...O_(m),style:u,role:"group","aria-roledescription":Er(f,"Slide"),"aria-label":Ng(f,s,r+t)},p())}function n2(){const e=Dt().styles.slide;return F.createElement("div",{className:Me(hc),style:e})}function r2({carousel:e,labels:t}){const{slides:n,currentIndex:r,globalIndex:s}=pn(),{setCarouselRef:o}=On(),{autoPlaying:i,focusWithin:a}=_c(),l=Wi(e.spacing),c=Wi(e.padding),d=Og(e,n,1),u=[];if(wc(n))for(let f=r-d;f<=r+d;f+=1){const g=kc(n,f),m=s-r+f,p=e.finite&&(f<0||f>n.length-1);u.push(p?{key:m}:{key:[`${m}`,Tg(g)].filter(Boolean).join("|"),offset:f-r,slide:g})}return F.createElement("div",{ref:o,className:Ut(Me(Nr()),u.length>0&&Me(Nr("with_slides"))),style:{[`${ft(Nr("slides_count"))}`]:u.length,[`${ft(Nr("spacing_px"))}`]:l.pixel||0,[`${ft(Nr("spacing_percent"))}`]:l.percent||0,[`${ft(Nr("padding_px"))}`]:c.pixel||0,[`${ft(Nr("padding_percent"))}`]:c.percent||0},role:"region","aria-live":i&&!a?"off":"polite","aria-roledescription":Er(t,"Carousel"),"aria-label":Er(t,"Photo gallery")},u.map(({key:f,slide:g,offset:m})=>g?F.createElement(t2,{key:f,slide:g,offset:m}):F.createElement(n2,{key:f})))}const s2=gn(fg,r2);function Qg(){const{carousel:e}=Dt(),{slides:t,currentIndex:n}=pn(),r=t.length===0||e.finite&&n===0,s=t.length===0||e.finite&&n===t.length-1;return{prevDisabled:r,nextDisabled:s}}function Jg(e){var t;const n=Xi(),{publish:r}=vs(),{animation:s,controller:o}=Dt(),{prevDisabled:i,nextDisabled:a}=Qg(),l=((t=s.navigation)!==null&&t!==void 0?t:s.swipe)/2,c=Yg(()=>r(dr),l),d=Yg(()=>r(ur),l),u=We(f=>{switch(f.key){case C_:if(!o.closeOnEscape)return;r(ps);break;case S_:(n?a:i)||(n?d:c)();break;case __:(n?i:a)||(n?c:d)();break;default:return}f.stopPropagation()});F.useEffect(()=>e(pc,u),[e,u])}function Rg({label:e,icon:t,renderIcon:n,action:r,onClick:s,disabled:o,style:i}){return F.createElement(xs,{label:e,icon:t,renderIcon:n,className:Me(`navigation_${r}`),disabled:o,onClick:s,style:i,...Wg(On().focus,o)})}function o2({render:{buttonPrev:e,buttonNext:t,iconPrev:n,iconNext:r},styles:s}){const{prev:o,next:i,subscribeSensors:a}=On(),{prevDisabled:l,nextDisabled:c}=Qg();return Jg(a),F.createElement(F.Fragment,null,e?e():F.createElement(Rg,{label:"Previous",action:dr,icon:W_,renderIcon:n,style:s.navigationPrev,disabled:l,onClick:o}),t?t():F.createElement(Rg,{label:"Next",action:ur,icon:G_,renderIcon:r,style:s.navigationNext,disabled:c,onClick:i}))}const i2=gn(u_,o2),ep=Me(x_),a2=Me(bg);function l2(e){return"style"in e}function tp(e,t,n){const r=window.getComputedStyle(e),s=n?"padding-left":"padding-right",o=n?r.paddingLeft:r.paddingRight,i=e.style.getPropertyValue(s);return e.style.setProperty(s,`${(ji(o)||0)+t}px`),()=>{i?e.style.setProperty(s,i):e.style.removeProperty(s)}}function c2({noScroll:{disabled:e},children:t}){const n=Xi(),{getOwnerDocument:r,getOwnerWindow:s}=ys();return F.useEffect(()=>{if(e)return()=>{};const o=[],i=s(),{body:a,documentElement:l}=r(),c=Math.round(i.innerWidth-l.clientWidth);if(c>0){o.push(tp(a,c,n));const d=a.getElementsByTagName("*");for(let u=0;u{a.classList.remove(ep),o.forEach(d=>d())}},[n,e,r,s]),F.createElement(F.Fragment,null,t)}const d2=gn(f_,c2);function np(e){return Sr(uc,e)}function rp(e,t,n){const r=e.getAttribute(t);return e.setAttribute(t,n),()=>{r?e.setAttribute(t,r):e.removeAttribute(t)}}function u2({portal:{root:e,container:{className:t,style:n,...r}={}},animation:s,styles:o,className:i,on:a,close:l,labels:c,children:d}){const[u,f]=F.useState(!1),[g,m]=F.useState(!1),p=F.useRef([]),y=F.useRef(null),{setTimeout:b}=vo(),{subscribe:_}=vs(),M=Ec()?0:s.fade;F.useEffect(()=>(f(!0),()=>{f(!1),m(!1)}),[]);const S=We(()=>{p.current.forEach(w=>w()),p.current=[]}),k=We(()=>{var w;m(!1),S(),(w=a.exiting)===null||w===void 0||w.call(a),b(()=>{var E;(E=a.exited)===null||E===void 0||E.call(a),l()},M)});F.useEffect(()=>_(ps,k),[_,k]);const N=We(w=>{var E,A,P;A_(w),m(!0),(E=a.entering)===null||E===void 0||E.call(a);const L=(P=(A=w.parentNode)===null||A===void 0?void 0:A.children)!==null&&P!==void 0?P:[];for(let $=0;${var $,D;(D=($=y.current)===null||$===void 0?void 0:$.focus)===null||D===void 0||D.call($)}),b(()=>{var $;($=a.entered)===null||$===void 0||$.call(a)},M)}),C=F.useCallback(w=>{w?N(w):S()},[N,S]);return u?Ln.createPortal(F.createElement(q_,{ref:C,className:Ut(i,t,Me(np()),Me(bg),g&&Me(np("open"))),"aria-modal":!0,role:"dialog","aria-label":Er(c,"Lightbox"),style:{...s.fade!==Sc.animation.fade?{[ft("fade_animation_duration")]:`${M}ms`}:null,...s.easing.fade!==Sc.animation.easing.fade?{[ft("fade_animation_timing_function")]:s.easing.fade}:null,...o.root,...n},onFocus:w=>{y.current||(y.current=w.relatedTarget)},...r},d),(typeof e=="function"?e():e)||document.body):null}const f2=gn(uc,u2);function h2({children:e}){return F.createElement(F.Fragment,null,e)}const g2=gn(h_,h2);function p2(e){return Sr(hg,e)}function m2({toolbar:{buttons:e},render:{buttonClose:t,iconClose:n},styles:r}){const{close:s,setToolbarWidth:o}=On(),{setContainerRef:i,containerRect:a}=Bg();Nn(()=>{o(a?.width)},[o,a?.width]);const l=()=>t?t():F.createElement(xs,{key:ps,label:"Close",icon:j_,renderIcon:n,onClick:s});return F.createElement("div",{ref:i,style:r.toolbar,className:Me(p2())},e?.map(c=>c===ps?l():c))}const b2=gn(hg,m2);function sp(e,t){var n;return F.createElement(e.module.component,{key:e.module.name,...t},(n=e.children)===null||n===void 0?void 0:n.map(r=>sp(r,t)))}function y2(e,t={}){const{easing:n,...r}=e,{easing:s,...o}=t;return{easing:{...n,...s},...r,...o}}function v2({carousel:e,animation:t,render:n,toolbar:r,controller:s,noScroll:o,on:i,plugins:a,slides:l,index:c,...d}){const{animation:u,carousel:f,render:g,toolbar:m,controller:p,noScroll:y,on:b,slides:_,index:x,plugins:M,...S}=Sc,{config:k,augmentation:N}=D_([At(f2,[At(d2,[At(e2,[At(s2),At(b2),At(i2)])])])],a||M),C=N({animation:y2(u,t),carousel:{...f,...e},render:{...g,...n},toolbar:{...m,...r},controller:{...p,...s},noScroll:{...y,...o},on:{...b,...i},...S,...d});return C.open?F.createElement($g,{...C},F.createElement(z_,{slides:l||_,index:ji(c||x)},F.createElement(U_,null,F.createElement(P_,null,F.createElement(I_,null,sp(At(g2,k),C)))))):null}const op={minZoom:1,maxZoomPixelRatio:1,zoomInMultiplier:2,doubleTapDelay:300,doubleClickDelay:500,doubleClickMaxStops:2,keyboardMoveDistance:50,wheelZoomDistanceFactor:100,pinchZoomDistanceFactor:100,pinchZoomV4:!1,scrollToZoom:!1,maxZoom:8};function x2(e){return Math.min(Math.max(e,Number.EPSILON),1)}function ip(e){const{minZoom:t,...n}={...op,...e};return{minZoom:x2(t),...n}}function w2(e,t,n,r){const s=F.useRef(void 0),o=F.useRef(void 0),{zoom:i}=Dt().animation,a=Ec(),l=We(()=>{var c,d,u;if((c=s.current)===null||c===void 0||c.cancel(),s.current=void 0,o.current&&r?.current){try{s.current=(u=(d=r.current).animate)===null||u===void 0?void 0:u.call(d,[{transform:o.current},{transform:`scale(${e}) translateX(${t}px) translateY(${n}px)`}],{duration:a?0:i??500,easing:s.current?"ease-out":"ease-in-out"})}catch(f){console.error(f)}o.current=void 0,s.current&&(s.current.onfinish=()=>{s.current=void 0})}});return Nn(l,[e,t,n,l]),F.useCallback(()=>{o.current=r?.current?window.getComputedStyle(r.current).transform:void 0},[r])}function k2(e,t){const{on:n}=Dt(),r=We(()=>{var s;t||(s=n.zoom)===null||s===void 0||s.call(n,{zoom:e})});F.useEffect(r,[e,r])}function Ki(){const{zoom:e}=Dt();return ip(e)}function C2(e,t){var n;const r=typeof e=="function"?(n=e(t))!==null&&n!==void 0?n:op.maxZoom:e;return Math.max(r,1)}function S2(e,t){var n,r;let s={width:0,height:0},o={width:0,height:0};const{currentSlide:i}=pn(),{imageFit:a}=Dt().carousel,{maxZoomPixelRatio:l,maxZoom:c}=Ki();if(e&&i){const u={...i,...t};if(fr(u)){const f=vc(u,a),g=Math.max(...(((n=u.srcSet)===null||n===void 0?void 0:n.map(p=>p.width))||[]).concat(u.width?[u.width]:[])),m=Math.max(...(((r=u.srcSet)===null||r===void 0?void 0:r.map(p=>p.height))||[]).concat(u.height?[u.height]:[]));g>0&&m>0&&e.width>0&&e.height>0&&(o=f?{width:Math.round(Math.min(g,e.width/e.height*m)),height:Math.round(Math.min(m,e.height/e.width*g))}:{width:g,height:m},o={width:o.width*l,height:o.height*l},s=f?{width:Math.min(e.width,o.width,g),height:Math.min(e.height,o.height,m)}:{width:Math.round(Math.min(e.width,e.height/m*g,g)),height:Math.round(Math.min(e.height,e.width/g*m,m))})}else e.width>0&&e.height>0&&(t&&t.width>0&&t.height>0?s={width:Math.min(e.width,t.width),height:Math.min(e.height,t.height)}:s={width:e.width,height:e.height})}const d=i&&s.width?fr(i)?Math.max(yc(o.width/s.width,5),1):C2(c,i):1;return{imageRect:s,maxZoom:d}}function ap(e,t){return Math.hypot(e.clientX-t.clientX,e.clientY-t.clientY)}function lp(e,t,n=100,r=2){return e*Math.min(1+Math.abs(t/n),r)**Math.sign(t)}function _2(e,t,n,r,s,o,i,a,l){const c=F.useRef([]),d=F.useRef(0),u=F.useRef(void 0),{globalIndex:f}=pn(),{getOwnerWindow:g}=ys(),{containerRef:m,subscribeSensors:p}=On(),{keyboardMoveDistance:y,zoomInMultiplier:b,wheelZoomDistanceFactor:_,scrollToZoom:x,doubleTapDelay:M,doubleClickDelay:S,doubleClickMaxStops:k,pinchZoomDistanceFactor:N,pinchZoomV4:C}=Ki(),w=F.useCallback(z=>{if(m.current){const{pageX:U,pageY:W}=z,{scrollX:G,scrollY:B}=g(),{left:j,top:Y,width:V,height:X}=m.current.getBoundingClientRect();return[U-j-G-V/2,W-Y-B-X/2]}return[]},[m,g]),E=We(z=>{const{key:U,metaKey:W,ctrlKey:G}=z,B=W||G,j=()=>{z.preventDefault(),z.stopPropagation()};if(e>1){const Y=(V,X)=>{j(),a(V,X)};U==="ArrowDown"?Y(0,y):U==="ArrowUp"?Y(0,-y):U==="ArrowLeft"?Y(-y,0):U==="ArrowRight"&&Y(y,0)}U==="+"||B&&U==="="?(j(),s()):U==="-"||B&&U==="_"?(j(),o()):B&&U==="0"&&(j(),i(1))}),A=We(z=>{if((z.ctrlKey||x)&&Math.abs(z.deltaY)>Math.abs(z.deltaX)){z.stopPropagation(),i(lp(e,-z.deltaY,_),!0,...w(z));return}e>1&&(z.stopPropagation(),x||a(z.deltaX,z.deltaY))}),P=F.useCallback(z=>{const U=c.current;U.splice(0,U.length,...U.filter(W=>W.pointerId!==z.pointerId))},[]),L=F.useCallback(z=>{P(z),z.persist(),c.current.push(z)},[P]),$=We(z=>{var U;const W=c.current;if(z.pointerType==="mouse"&&z.buttons>1||!(!((U=l?.current)===null||U===void 0)&&U.contains(z.target)))return;e>1&&z.stopPropagation();const{timeStamp:G}=z;if(W.length===0&&G-d.current<(z.pointerType==="touch"?M:S)){d.current=0;const B=e>=1?e!==n?e*Math.max(n**(1/k),b):1:e!==t?e/Math.max(t**(-1/k),b):1;i(B,!1,...w(z))}else d.current=G;if(L(z),W.length===2){const B=ap(W[0],W[1]);u.current={previousDistance:B,initialDistance:Math.max(B,1),initialZoom:e}}}),D=We(z=>{const U=c.current,W=U.find(G=>G.pointerId===z.pointerId);if(U.length===2&&u.current){z.stopPropagation(),L(z);const G=ap(U[0],U[1]),B=C?u.current.initialZoom/u.current.initialDistance*G:lp(e,G-u.current.previousDistance,N);i(B,!0,...U.map(j=>w(j)).reduce((j,Y)=>Y.map((V,X)=>j[X]+V/2))),u.current.previousDistance=G;return}e>1&&(z.stopPropagation(),W&&(U.length===1&&a((W.clientX-z.clientX)/e,(W.clientY-z.clientY)/e),L(z)))}),O=F.useCallback(z=>{const U=c.current;U.length===2&&U.find(W=>W.pointerId===z.pointerId)&&(u.current=void 0),P(z)},[P]),T=F.useCallback(()=>{const z=c.current;z.splice(0,z.length),d.current=0,u.current=void 0},[]);Xg(p,$,D,O,r),F.useEffect(T,[f,T]),F.useEffect(()=>r?()=>{}:ms(T,p(pc,E),p(mc,A)),[r,p,T,E,A])}function E2(e,t,n){const[r,s]=F.useState(1),[o,i]=F.useState(0),[a,l]=F.useState(0),c=w2(r,o,a,n),{currentSlide:d,globalIndex:u}=pn(),{containerRect:f,slideRect:g}=On(),{minZoom:m,zoomInMultiplier:p}=Ki(),y=d&&fr(d)?d.src:void 0,b=!n?.current;Nn(()=>{s(1),i(0),l(0)},[u,y]);const _=F.useCallback((N,C,w)=>{const E=w||r,A=o-(N||0),P=a-(C||0),L=(e.width*E-g.width)/2/E,$=(e.height*E-g.height)/2/E;i(Math.min(Math.abs(A),Math.max(L,0))*Math.sign(A)),l(Math.min(Math.abs(P),Math.max($,0))*Math.sign(P))},[r,o,a,g,e.width,e.height]),x=F.useCallback((N,C,w,E)=>{const A=yc(N+.01m?N:m:t,5);C||c(),_(w?w*(1/r-1/A):0,E?E*(1/r-1/A):0,A),s(A)},[r,m,t,_,c]),M=We(()=>{r>1&&(r>t&&x(t,!0),_())});Nn(M,[f.width,f.height,M]);const S=F.useCallback(()=>{const N=r*p;x(r<1&&N>1?1:N)},[r,p,x]),k=F.useCallback(()=>{const N=r/p;x(r>1&&N<1?1:N)},[r,p,x]);return{zoom:r,offsetX:o,offsetY:a,disabled:b,changeOffsets:_,changeZoom:x,zoomIn:S,zoomOut:k}}const cp=F.createContext(null),Dc=un("useZoom","ZoomControllerContext",cp);function M2({children:e}){const[t,n]=F.useState(),{slideRect:r}=On(),{ref:s,minZoom:o}=Ki(),{imageRect:i,maxZoom:a}=S2(r,t?.imageDimensions),{zoom:l,offsetX:c,offsetY:d,disabled:u,changeZoom:f,changeOffsets:g,zoomIn:m,zoomOut:p}=E2(i,a,t?.zoomWrapperRef);k2(l,u),_2(l,o,a,u,m,p,f,g,t?.zoomWrapperRef);const y=F.useMemo(()=>({zoom:l,minZoom:o,maxZoom:a,offsetX:c,offsetY:d,disabled:u,zoomIn:m,zoomOut:p,changeZoom:f}),[l,o,a,c,d,u,m,p,f]);F.useImperativeHandle(s,()=>y,[y]);const b=F.useMemo(()=>({...y,setZoomWrapper:n}),[y,n]);return F.createElement(cp.Provider,{value:b},e)}const N2=en("ZoomIn",F.createElement(F.Fragment,null,F.createElement("path",{d:"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"}),F.createElement("path",{d:"M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"}))),T2=en("ZoomOut",F.createElement("path",{d:"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"})),dp=F.forwardRef(function({zoomIn:t,onLoseFocus:n},r){const s=F.useRef(!1),o=F.useRef(!1),{zoom:i,minZoom:a,maxZoom:l,zoomIn:c,zoomOut:d,disabled:u}=Dc(),{render:f}=Dt(),g=u||(t?i>=l:i<=a);return F.useEffect(()=>{g&&s.current&&o.current&&n(),g||(s.current=!0)},[g,n]),F.createElement(xs,{ref:r,disabled:g,label:t?"Zoom in":"Zoom out",icon:t?N2:T2,renderIcon:t?f.iconZoomIn:f.iconZoomOut,onClick:t?c:d,onFocus:()=>{o.current=!0},onBlur:()=>{o.current=!1}})});function O2(){const e=F.useRef(null),t=F.useRef(null),{focus:n}=On(),r=F.useCallback(i=>{var a,l;!((a=i.current)===null||a===void 0)&&a.disabled?n():(l=i.current)===null||l===void 0||l.focus()},[n]),s=F.useCallback(()=>r(e),[r]),o=F.useCallback(()=>r(t),[r]);return F.createElement(F.Fragment,null,F.createElement(dp,{zoomIn:!0,ref:e,onLoseFocus:o}),F.createElement(dp,{ref:t,onLoseFocus:s}))}function A2(){const{render:e}=Dt(),t=Dc();return e.buttonZoom?F.createElement(F.Fragment,null,e.buttonZoom(t)):F.createElement(O2,null)}function D2(e){var t;return(((t=e.srcSet)===null||t===void 0?void 0:t.length)||0)>0}function I2({current:e,preload:t},{type:n,source:r}){switch(n){case"fetch":return e?{current:e,preload:r}:{current:r};case"done":return r===t?{current:r}:{current:e,preload:t};default:throw new Error(Eg)}}function L2(e){var t,n;const[{current:r,preload:s},o]=F.useReducer(I2,{}),{slide:i,rect:a,imageFit:l,render:c,interactive:d}=e,u=i.srcSet.sort((S,k)=>S.width-k.width),f=(t=i.width)!==null&&t!==void 0?t:u[u.length-1].width,g=(n=i.height)!==null&&n!==void 0?n:u[u.length-1].height,m=vc(i,l),p=Math.max(...u.map(S=>S.width)),y=Math.min((m?Math.max:Math.min)(a.width,f*(a.height/g)),p),b=N_(),_=We(()=>{var S;const k=(S=u.find(N=>N.width>=y*b))!==null&&S!==void 0?S:u[u.length-1];(!r||u.findIndex(N=>N.src===r)N===k))&&o({type:"fetch",source:k.src})});Nn(_,[a.width,a.height,b,_]);const x=We(S=>o({type:"done",source:S})),M={WebkitTransform:d?"initial":"translateZ(0)"};return m||Object.assign(M,a.width/a.heightx(s),render:{...c,iconLoading:()=>null,iconError:()=>null}}),r&&F.createElement(Mr,{key:"current",...e,slide:{...i,src:r,srcSet:void 0},style:M}))}function P2({render:e,slide:t,offset:n,rect:r}){var s;const[o,i]=F.useState(),a=F.useRef(null),l=fr(t),{zoom:c,maxZoom:d,offsetX:u,offsetY:f,setZoomWrapper:g}=Dc(),m=c>1,{carousel:p,on:y}=Dt(),{currentIndex:b}=pn();Nn(()=>{if(n!==0||l||!a.current)return()=>{};const x=()=>{const S=a.current;if(!S)return;let k=0,N=0;for(const C of S.children)C instanceof HTMLElement&&(k=Math.max(k,C.offsetWidth),N=Math.max(N,C.offsetHeight));i(C=>C&&C.width===k&&C.height===N?C:{width:k,height:N})};if(x(),typeof ResizeObserver>"u")return()=>{};const M=new ResizeObserver(x);for(const S of a.current.children)M.observe(S);return()=>M.disconnect()},[n,l,r]),Nn(()=>n===0?(g({zoomWrapperRef:a,imageDimensions:o}),()=>g(void 0)):()=>{},[n,o,g]);let _=(s=e.slide)===null||s===void 0?void 0:s.call(e,{slide:t,offset:n,rect:r,zoom:c,maxZoom:d});if(!_&&l){const x={slide:t,offset:n,rect:r,render:e,imageFit:p.imageFit,imageProps:p.imageProps,onClick:n===0?()=>{var M;return(M=y.click)===null||M===void 0?void 0:M.call(y,{index:b})}:void 0};_=D2(t)?F.createElement(L2,{...x,slide:t,interactive:m,rect:n===0?{width:r.width*c,height:r.height*c}:r}):F.createElement(Mr,{onLoad:M=>i({width:M.naturalWidth,height:M.naturalHeight}),...x})}return _?F.createElement("div",{ref:a,className:Ut(Me(mg),Me(gs),Me(yg),m&&Me(w_)),style:n===0?{transform:`scale(${c}) translateX(${u}px) translateY(${f}px)`}:void 0},_):null}const $2=({augment:e,addModule:t})=>{e(({zoom:n,toolbar:r,render:s,controller:o,...i})=>{const a=ip(n);return{zoom:a,toolbar:Gi(r,pg,F.createElement(A2,null)),render:{...s,slide:l=>{var c,d;return fr(l.slide)||l.slide.type!=null&&(!((c=a.supports)===null||c===void 0)&&c.includes(l.slide.type))?F.createElement(P2,{render:s,...l}):(d=s.slide)===null||d===void 0?void 0:d.call(s,l)}},controller:{...o,preventDefaultWheelY:a.scrollToZoom},...i}}),t(gn(pg,M2))},F2={separator:"/",container:{}},up=e=>({...F2,...e});function z2({counter:e}){const{slides:t,currentIndex:n}=pn(),{separator:r,container:{className:s,...o},className:i,...a}=up(e);return t.length===0?null:F.createElement("div",{className:Ut(Me("counter"),s||i),...a,...o,"aria-hidden":!0},n+1," ",r," ",t.length)}function V2({augment:e,addChild:t}){e(({counter:n,...r})=>({counter:up(n),...r})),t(ho,gn(g_,z2))}const H2={autoplay:!1,delay:3e3,ref:null},fp=e=>({...H2,...e}),hp=F.createContext(null),U2=un("useSlideshow","SlideshowContext",hp);function B2({slideshow:e,carousel:{finite:t},on:n,children:r}){const{autoplay:s,delay:o,ref:i}=fp(e),a=F.useRef(s),[l,c]=F.useState(s),d=F.useRef(void 0),u=F.useRef(void 0),{slides:f,currentIndex:g}=pn(),{setTimeout:m,clearTimeout:p}=vo(),{subscribe:y}=vs(),{next:b}=On(),{setAutoPlaying:_}=_c();F.useEffect(()=>_(l),[l,_]);const x=f.length===0||t&&g===f.length-1,M=F.useCallback(()=>{!l&&!x&&c(!0)},[l,x]),S=F.useCallback(()=>{l&&c(!1)},[l]),k=F.useCallback(()=>{p(d.current),d.current=void 0},[p]),N=We(()=>{k(),!(!l||x||u.current===hs||u.current===fc)&&(d.current=m(()=>{l&&(u.current=void 0,b())},o))});F.useEffect(N,[g,l,N]),F.useEffect(()=>{l&&x&&c(!1)},[g,l,x]);const C=We(()=>{var A;return(A=n.slideshowStart)===null||A===void 0?void 0:A.call(n)}),w=We(()=>{var A;return(A=n.slideshowStop)===null||A===void 0?void 0:A.call(n)});F.useEffect(()=>{l?C():a.current&&w(),a.current=l},[l,C,w]),F.useEffect(()=>ms(k,y(m_,()=>{u.current=hs,k()}),y(b_,()=>{u.current=fc,k()}),y(y_,()=>{u.current=mo,N()}),y(v_,()=>{u.current=bo,N()})),[y,k,N]);const E=F.useMemo(()=>({playing:l,disabled:x,play:M,pause:S}),[l,x,M,S]);return F.useImperativeHandle(i,()=>E,[E]),F.createElement(hp.Provider,{value:E},r)}const j2=en("Play",F.createElement("path",{d:"M8 5v14l11-7z"})),W2=en("Pause",F.createElement("path",{d:"M6 19h4V5H6v14zm8-14v14h4V5h-4z"}));function G2(){const{playing:e,disabled:t,play:n,pause:r}=U2(),{render:s}=Dt(),o=Wg(On().focus,t);return s.buttonSlideshow?F.createElement(F.Fragment,null,s.buttonSlideshow({playing:e,disabled:t,play:n,pause:r})):F.createElement(xs,{label:e?"Pause":"Play",icon:e?W2:j2,renderIcon:e?s.iconSlideshowPause:s.iconSlideshowPlay,onClick:e?r:n,disabled:t,...o})}function Y2({augment:e,addModule:t}){e(({slideshow:n,toolbar:r,...s})=>({toolbar:Gi(r,gg,F.createElement(G2,null)),slideshow:fp(n),...s})),t(gn(gg,B2))}const X2={auto:!1,ref:null},gp=e=>({...X2,...e}),pp=F.createContext(null),K2=un("useFullscreen","FullscreenContext",pp);function q2({fullscreen:e,on:t,children:n}){const{auto:r,ref:s}=gp(e),o=F.useRef(null),[i,a]=F.useState(),[l,c]=F.useState(!1),d=F.useRef(!1),{getOwnerDocument:u}=ys();Nn(()=>{var x,M,S,k;const N=u();a(!((k=(S=(M=(x=N.fullscreenEnabled)!==null&&x!==void 0?x:N.webkitFullscreenEnabled)!==null&&M!==void 0?M:N.mozFullScreenEnabled)!==null&&S!==void 0?S:N.msFullscreenEnabled)!==null&&k!==void 0&&k))},[u]);const f=F.useCallback(()=>{var x;const M=u(),S=M.fullscreenElement||M.webkitFullscreenElement||M.mozFullScreenElement||M.msFullscreenElement;return((x=S?.shadowRoot)===null||x===void 0?void 0:x.fullscreenElement)||S},[u]),g=F.useCallback(()=>{const x=o.current;try{x.requestFullscreen?x.requestFullscreen().catch(()=>{}):x.webkitRequestFullscreen?x.webkitRequestFullscreen():x.mozRequestFullScreen?x.mozRequestFullScreen():x.msRequestFullscreen&&x.msRequestFullscreen()}catch{}},[]),m=F.useCallback(()=>{if(!f())return;const x=u();try{x.exitFullscreen?x.exitFullscreen().catch(()=>{}):x.webkitExitFullscreen?x.webkitExitFullscreen():x.mozCancelFullScreen?x.mozCancelFullScreen():x.msExitFullscreen&&x.msExitFullscreen()}catch{}},[f,u]);F.useEffect(()=>{const x=u(),M=()=>{c(f()===o.current)};return ms(...["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"].map(S=>(x.addEventListener(S,M),()=>x.removeEventListener(S,M))))},[f,u]);const p=We(()=>{var x;return(x=t.enterFullscreen)===null||x===void 0?void 0:x.call(t)}),y=We(()=>{var x;return(x=t.exitFullscreen)===null||x===void 0?void 0:x.call(t)});F.useEffect(()=>{l&&(d.current=!0),d.current&&(l?p:y)()},[l,p,y]);const b=We(()=>{var x;return(x=r?g:null)===null||x===void 0||x(),m});F.useEffect(b,[b]);const _=F.useMemo(()=>({fullscreen:l,disabled:i,enter:g,exit:m}),[l,i,g,m]);return F.useImperativeHandle(s,()=>_,[_]),F.createElement("div",{ref:o,className:Ut(Me(go),Me(mg))},F.createElement(pp.Provider,{value:_},n))}const Z2=en("EnterFullscreen",F.createElement("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})),Q2=en("ExitFullscreen",F.createElement("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"}));function J2(){var e;const{fullscreen:t,disabled:n,enter:r,exit:s}=K2(),{render:o}=Dt();return n?null:o.buttonFullscreen?F.createElement(F.Fragment,null,(e=o.buttonFullscreen)===null||e===void 0?void 0:e.call(o,{fullscreen:t,disabled:n,enter:r,exit:s})):F.createElement(xs,{disabled:n,label:t?"Exit Fullscreen":"Enter Fullscreen",icon:t?Q2:Z2,renderIcon:t?o.iconExitFullscreen:o.iconEnterFullscreen,onClick:t?s:r})}function R2({augment:e,contains:t,addParent:n}){e(({fullscreen:r,toolbar:s,...o})=>({toolbar:Gi(s,go,F.createElement(J2,null)),fullscreen:gp(r),...o})),n(t(po)?po:ho,gn(go,q2))}const Tr={ref:null,position:"bottom",width:120,height:80,border:1,borderRadius:4,padding:4,gap:16,imageFit:"contain",vignette:!0,hidden:!1,showToggle:!1},Ic=e=>({...Tr,...e});function mp(){const{thumbnails:e}=Dt();return Ic(e)}const Or=e=>Sr(po,e),It=e=>Or(Sr("thumbnail",e)),eE=en("VideoThumbnail",F.createElement("path",{d:"M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"})),tE=en("UnknownThumbnail",F.createElement("path",{d:"M23 18V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zM8.5 12.5l2.5 3.01L14.5 11l4.5 6H5l3.5-4.5z"}));function nE({slide:e,render:t,rect:n,imageFit:r}){var s;const o=(s=t.thumbnail)===null||s===void 0?void 0:s.call(t,{slide:e,render:t,rect:n,imageFit:r});if(o)return o;const i={render:t,rect:n,imageFit:r};if(e.thumbnail)return F.createElement(Mr,{slide:{src:e.thumbnail},...i});if(fr(e))return F.createElement(Mr,{slide:e,...i});const a=Me(It(Bi));return e.type==="video"?F.createElement(F.Fragment,null,e.poster&&F.createElement(Mr,{slide:{src:e.poster},...i}),F.createElement(eE,{className:a})):F.createElement(tE,{className:a})}const rE=_r("active"),Lc=_r("fadein"),Pc=_r("fadeout"),sE=_r("placeholder"),bp="delay",yp="duration";function oE({slide:e,index:t,onClick:n,fadeIn:r,fadeOut:s,placeholder:o,onLoseFocus:i}){const a=F.useRef(null),{render:l,styles:c,labels:d}=Dt(),{slides:u,globalIndex:f}=pn(),{getOwnerDocument:g}=ys(),{width:m,height:p,imageFit:y}=mp(),b={width:m,height:p},_=t===f,x=We(i);return F.useEffect(()=>{s&&g().activeElement===a.current&&x()},[s,x,g]),F.createElement("button",{ref:a,type:"button",className:Ut(Me(gs),Me(It()),_&&Me(It(rE())),r&&Me(It(Lc())),s&&Me(It(Pc())),o&&Me(It(sE()))),style:{...r?{[ft(It(Lc(yp)))]:`${r.duration}ms`,[ft(It(Lc(bp)))]:`${r.delay}ms`}:null,...s?{[ft(It(Pc(yp)))]:`${s.duration}ms`,[ft(It(Pc(bp)))]:`${s.delay}ms`}:null,...c.thumbnail},onClick:n,"aria-current":_?!0:void 0,"aria-label":Ng(d,u,t)},e&&nE({slide:e,render:l,rect:b,imageFit:y}))}function vp(e){return["top","bottom"].includes(e)}function xp(e,t){return t+2*(e.border+e.padding)+e.gap}function iE(e){const{thumbnail:t,poster:n}=e||{thumbnail:"placeholder"};return typeof t=="string"&&t||typeof n=="string"&&n||e&&Tg(e)||void 0}function wp({visible:e,containerRef:t}){const n=F.useRef(null),r=Xi(),{publish:s,subscribe:o}=vs(),{carousel:i,styles:a,labels:l}=Dt(),{slides:c,globalIndex:d,animation:u}=pn(),{registerSensors:f,subscribeSensors:g}=Gg();Jg(g);const m=mp(),{position:p,width:y,height:b,border:_,borderStyle:x,borderColor:M,borderRadius:S,padding:k,gap:N,vignette:C}=m,w=u?.duration!==void 0&&u?.increment||0,E=u?.duration||0,{prepareAnimation:A}=Mc(n,O=>({keyframes:vp(p)?[{transform:`translateX(${(r?-1:1)*xp(m,y)*w+O}px)`},{transform:"translateX(0)"}]:[{transform:`translateY(${xp(m,b)*w+O}px)`},{transform:"translateY(0)"}],duration:E,easing:u?.easing})),P=We(()=>{let O=0;if(t.current&&n.current){const T=t.current.getBoundingClientRect(),z=n.current.getBoundingClientRect();O=vp(p)?z.left-T.left-(T.width-z.width)/2:z.top-T.top-(T.height-z.height)/2}A(O)});F.useEffect(()=>ms(o(gc,P)),[o,P]);const L=Og(i,c),$=[];if(wc(c))for(let O=d-L-Math.abs(w);O<=d+L+Math.abs(w);O+=1){const z=i.finite&&(O<0||O>c.length-1)||w<0&&O0&&O>d+L?null:kc(c,O),U=[`${O}`,iE(z)].filter(Boolean).join("|");$.push({key:U,index:O,slide:z})}const D=O=>()=>{O>d?s(ur,{count:O-d}):O{const U=E/Math.abs(w||1),W=w>0&&T>d+L-w&&T<=d+L||w<0&&T=d-L?{duration:U,delay:((w>0?T-(d+L-w):d-L-w-T)-1)*U}:void 0,G=w>0&&Td+L?{duration:U,delay:(w>0?w-(d-L-T):-w-(T-(d+L)))*U}:void 0;return F.createElement(oE,{key:O,index:T,slide:z,fadeIn:W,fadeOut:G,placeholder:!z,onClick:D(T),onLoseFocus:()=>{var B;return(B=n.current)===null||B===void 0?void 0:B.focus()}})})),C&&F.createElement("div",{className:Me(Or("vignette"))}))}const kp=F.createContext(null),aE=un("useThumbnails","ThumbnailsContext",kp);function lE({children:e,...t}){const{ref:n,position:r,hidden:s}=Ic(t.thumbnails),[o,i]=F.useState(!s),a=F.useRef(null),l=F.useMemo(()=>({visible:o,show:()=>i(!0),hide:()=>i(!1)}),[o]);return F.useImperativeHandle(n,()=>l,[l]),F.createElement($g,{...t},F.createElement(kp.Provider,{value:l},F.createElement("div",{ref:a,className:Ut(Me(Or()),Me(Or(`${r}`)))},["start","top"].includes(r)&&F.createElement(wp,{containerRef:a,visible:o}),F.createElement("div",{className:Me(Or("wrapper"))},e),["end","bottom"].includes(r)&&F.createElement(wp,{containerRef:a,visible:o}))))}const Cp=()=>F.createElement(F.Fragment,null,F.createElement("path",{strokeWidth:2,stroke:"currentColor",strokeLinejoin:"round",fill:"none",d:"M3 5l18 0l0 14l-18 0l0-14z"}),F.createElement("path",{d:"M5 14h4v3h-4zM10 14h4v3h-4zM15 14h4v3h-4z"})),cE=en("ThumbnailsVisible",Cp()),dE=B_("ThumbnailsHidden",Cp());function uE(){const{visible:e,show:t,hide:n}=aE(),{render:r}=Dt();return r.buttonThumbnails?F.createElement(F.Fragment,null,r.buttonThumbnails({visible:e,show:t,hide:n})):F.createElement(xs,{label:e?"Hide thumbnails":"Show thumbnails",icon:e?cE:dE,renderIcon:e?r.iconThumbnailsVisible:r.iconThumbnailsHidden,onClick:e?n:t})}function fE({augment:e,contains:t,append:n,addParent:r}){e(({thumbnails:o,toolbar:i,...a})=>{const l=Ic(o);return{toolbar:Gi(i,po,l.showToggle?F.createElement(uE,null):null),thumbnails:l,...a}});const s=gn(po,lE);t(go)?n(go,s):r(ho,s)}const hE=window.G7Core?.createLogger?.("Comp:ImageGallery")??{log:(...e)=>console.log("[Comp:ImageGallery]",...e),warn:(...e)=>console.warn("[Comp:ImageGallery]",...e),error:(...e)=>console.error("[Comp:ImageGallery]",...e)},$c=window.G7Core,qi=(e,t)=>$c?.t?.(e,t)??e,gE=async(e,t)=>{try{const n=await $c.api.get(e,{responseType:"blob"});if(n){const r=URL.createObjectURL(n),s=document.createElement("a");s.href=r,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(r)}}catch(n){hE.error("Failed to download file:",n),$c?.toast?.error?.(qi("common.download_failed"))}},pE=(e,t)=>{const n=document.createElement("a");n.href=e,n.download=t,n.target="_blank",document.body.appendChild(n),n.click(),document.body.removeChild(n)},Fc=async e=>{const t=e.downloadUrl||e.src,n=e.filename||e.title||"image";e.downloadRequiresAuth?await gE(t,n):pE(t,n)},mE=({image:e,index:t,onDownload:n})=>{const[r,s]=v.useState(!1),o=async i=>{if(i.stopPropagation(),n){n(e,t);return}s(!0);try{await Fc(e)}finally{s(!1)}};return h.jsx(ae,{type:"button",onClick:o,disabled:r,className:"yarl__button flex items-center justify-center","aria-label":qi("common.download"),title:qi("common.download"),children:r?h.jsx(pt,{className:"fa-solid fa-spinner fa-spin text-white"}):h.jsx(pt,{className:"fa-solid fa-download text-white"})})},Zi=({images:e,isOpen:t,onClose:n,startIndex:r=0,enableZoom:s=!0,enableSlideshow:o=!1,enableFullscreen:i=!0,showCounter:a=!0,showDownload:l=!0,showThumbnails:c=!0,onDownload:d,editorAttrs:u,id:f})=>{const[g,m]=v.useState(r),p=v.useRef(r),y=e.map(x=>({src:x.src,title:x.title,description:x.description})),b=[];s&&b.push($2),o&&b.push(Y2),i&&b.push(R2),a&&b.push(V2),c&&b.push(fE);const _=e[g];if(v.useEffect(()=>{p.current=g},[g]),u){const x=(e??[]).slice(0,4);return h.jsx(I,{id:f,className:"grid grid-cols-2 gap-2 p-3 rounded-lg border border-dashed border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800",...u,children:x.length>0?x.map((M,S)=>h.jsx(Sn,{src:M.thumbnail||M.src,alt:M.title||"",className:"w-full aspect-square object-cover rounded bg-gray-200 dark:bg-gray-700"},S)):h.jsxs(I,{className:"col-span-2 flex items-center justify-center py-6 text-sm text-gray-400 dark:text-gray-500",children:[h.jsx(pt,{className:"fa-regular fa-images mr-2"}),qi("editor.component.image_gallery")]})})}return h.jsx(v2,{open:t,close:n,slides:y,index:g,plugins:b,on:{view:({index:x})=>{x!==p.current&&m(x)}},zoom:{maxZoomPixelRatio:3,zoomInMultiplier:2,doubleTapDelay:300,doubleClickDelay:300,doubleClickMaxStops:2,keyboardMoveDistance:50,wheelZoomDistanceFactor:100,pinchZoomDistanceFactor:100,scrollToZoom:!0},carousel:{finite:!0,preload:2,padding:"16px",spacing:"30%"},animation:{fade:250,swipe:500,easing:{fade:"ease",swipe:"ease-out",navigation:"ease-in-out"}},controller:{closeOnBackdropClick:!0,closeOnPullDown:!0,closeOnPullUp:!0},thumbnails:{position:"bottom",width:120,height:80,border:2,borderRadius:4,padding:4,gap:16,showToggle:!1,vignette:!0},toolbar:{buttons:[l&&_&&h.jsx(mE,{image:_,index:g,onDownload:d},"download"),"close"].filter(Boolean)},styles:{container:{backgroundColor:"rgba(0, 0, 0, 0.9)"}}})},Sp=()=>{const[e,t]=v.useState(!1),[n,r]=v.useState([]),[s,o]=v.useState(0),i=v.useCallback((l,c=0)=>{r(l),o(c),t(!0)},[]),a=v.useCallback(()=>{t(!1)},[]);return{isOpen:e,openGallery:i,closeGallery:a,galleryProps:{images:n,isOpen:e,onClose:a,startIndex:s}}};Zi.displayName="ImageGallery";const bE=Object.freeze(Object.defineProperty({__proto__:null,ImageGallery:Zi,executeImageDownload:Fc,useImageGallery:Sp},Symbol.toStringTag,{value:"Module"})),yE=v.createContext(null),vE=({value:e,children:t})=>h.jsx(yE.Provider,{value:e,children:t});function xE(e,t){return t.forEach((function(n){n&&typeof n!="string"&&!Array.isArray(n)&&Object.keys(n).forEach((function(r){if(r!=="default"&&!(r in e)){var s=Object.getOwnPropertyDescriptor(n,r);Object.defineProperty(e,r,s.get?s:{enumerable:!0,get:function(){return n[r]}})}}))})),Object.freeze(e)}function _p(e,t){return new Promise((function(n,r){let s;return wE(e).then((function(o){try{return s=o,n(new Blob([t.slice(0,2),s,t.slice(2)],{type:"image/jpeg"}))}catch(i){return r(i)}}),r)}))}const wE=e=>new Promise(((t,n)=>{const r=new FileReader;r.addEventListener("load",(({target:{result:s}})=>{const o=new DataView(s);let i=0;if(o.getUint16(i)!==65496)return n("not a valid JPEG");for(i+=2;;){const a=o.getUint16(i);if(a===65498)break;const l=o.getUint16(i+2);if(a===65505&&o.getUint32(i+4)===1165519206){const c=i+10;let d;switch(o.getUint16(c)){case 18761:d=!0;break;case 19789:d=!1;break;default:return n("TIFF header contains invalid endian")}if(o.getUint16(c+2,d)!==42)return n("TIFF header contains invalid version");const u=o.getUint32(c+4,d),f=c+u+2+12*o.getUint16(c+u,d);for(let g=c+u+2;g>>24&255,a[i+1]=l>>>16&255,a[i+2]=l>>>8&255,a[i+3]=l>>>0&255,new Uint8Array(a.buffer,0,i+4)},r.deflateRaw=function(s,o){o==null&&(o={level:6});var i=new Uint8Array(50+Math.floor(1.1*s.length)),a=r.F.deflateRaw(s,i,a,o.level);return new Uint8Array(i.buffer,0,a)},r.encode=function(s,o){o==null&&(o=!1);var i=0,a=r.bin.writeUint,l=r.bin.writeUshort,c={};for(var d in s){var u=!r._noNeed(d)&&!o,f=s[d],g=r.crc.crc(f,0,f.length);c[d]={cpr:u,usize:f.length,crc:g,file:u?r.deflateRaw(f):f}}for(var d in c)i+=c[d].file.length+30+46+2*r.bin.sizeUTF8(d);i+=22;var m=new Uint8Array(i),p=0,y=[];for(var d in c){var b=c[d];y.push(p),p=r._writeHeader(m,p,d,b,0)}var _=0,x=p;for(var d in c)b=c[d],y.push(p),p=r._writeHeader(m,p,d,b,1,y[_++]);var M=p-x;return a(m,p,101010256),p+=4,l(m,p+=4,_),l(m,p+=2,_),a(m,p+=2,M),a(m,p+=4,x),p+=4,p+=2,m.buffer},r._noNeed=function(s){var o=s.split(".").pop().toLowerCase();return"png,jpg,jpeg,zip".indexOf(o)!=-1},r._writeHeader=function(s,o,i,a,l,c){var d=r.bin.writeUint,u=r.bin.writeUshort,f=a.file;return d(s,o,l==0?67324752:33639248),o+=4,l==1&&(o+=2),u(s,o,20),u(s,o+=2,0),u(s,o+=2,a.cpr?8:0),d(s,o+=2,0),d(s,o+=4,a.crc),d(s,o+=4,f.length),d(s,o+=4,a.usize),u(s,o+=4,r.bin.sizeUTF8(i)),u(s,o+=2,0),o+=2,l==1&&(o+=2,o+=2,d(s,o+=6,c),o+=4),o+=r.bin.writeUTF8(s,o,i),l==0&&(s.set(f,o),o+=f.length),o},r.crc={table:(function(){for(var s=new Uint32Array(256),o=0;o<256;o++){for(var i=o,a=0;a<8;a++)1&i?i=3988292384^i>>>1:i>>>=1;s[o]=i}return s})(),update:function(s,o,i,a){for(var l=0;l>>8;return s},crc:function(s,o,i){return 4294967295^r.crc.update(4294967295,s,o,i)}},r.adler=function(s,o,i){for(var a=1,l=0,c=o,d=o+i;c>8&255},readUint:function(s,o){return 16777216*s[o+3]+(s[o+2]<<16|s[o+1]<<8|s[o])},writeUint:function(s,o,i){s[o]=255&i,s[o+1]=i>>8&255,s[o+2]=i>>16&255,s[o+3]=i>>24&255},readASCII:function(s,o,i){for(var a="",l=0;l>6,s[o+l+1]=128|d>>0&63,l+=2;else if((4294901760&d)==0)s[o+l]=224|d>>12,s[o+l+1]=128|d>>6&63,s[o+l+2]=128|d>>0&63,l+=3;else{if((4292870144&d)!=0)throw"e";s[o+l]=240|d>>18,s[o+l+1]=128|d>>12&63,s[o+l+2]=128|d>>6&63,s[o+l+3]=128|d>>0&63,l+=4}}return l},sizeUTF8:function(s){for(var o=s.length,i=0,a=0;a>>3}var y=c.lits,b=c.strt,_=c.prev,x=0,M=0,S=0,k=0,N=0,C=0;for(p>2&&(b[C=r.F._hash(s,0)]=0),f=0;f14e3||M>26697)&&p-f>100&&(m>>16,P=65535&E;if(E!=0){P=65535&E;var L=d(A=E>>>16,c.of0);c.lhst[257+L]++;var $=d(P,c.df0);c.dhst[$]++,k+=c.exb[L]+c.dxb[$],y[x]=A<<23|f-m,y[x+1]=P<<16|L<<8|$,x+=2,m=f+A}else c.lhst[s[f]]++;M++}}for(S==f&&s.length!=0||(m>>3},r.F._bestMatch=function(s,o,i,a,l,c){var d=32767&o,u=i[d],f=d-u+32768&32767;if(u==d||a!=r.F._hash(s,o-f))return 0;for(var g=0,m=0,p=Math.min(32767,o);f<=p&&--c!=0&&u!=d;){if(g==0||s[o+g]==s[o+g-f]){var y=r.F._howLong(s,o,f);if(y>g){if(m=f,(g=y)>=l)break;f+2b&&(b=M,u=x)}}}f+=(d=u)-(u=i[d])+32768&32767}return g<<16|m},r.F._howLong=function(s,o,i){if(s[o]!=s[o-i]||s[o+1]!=s[o+1-i]||s[o+2]!=s[o+2-i])return 0;var a=o,l=Math.min(s.length,o+258);for(o+=3;o>>23,G=T+(8388607&U);T>16,Y=B>>8&255,V=255&B;C(u,f=r.F._writeLit(257+Y,$,u,f),W-k.of0[Y]),f+=k.exb[Y],N(u,f=r.F._writeLit(V,D,u,f),j-k.df0[V]),f+=k.dxb[V],T+=W}}f=r.F._writeLit(256,$,u,f)}return f},r.F._copyExact=function(s,o,i,a,l){var c=l>>>3;return a[c]=i,a[c+1]=i>>>8,a[c+2]=255-a[c],a[c+3]=255-a[c+1],c+=4,a.set(new Uint8Array(s.buffer,o,i),c),l+(i+4<<3)},r.F.getTrees=function(){for(var s=r.F.U,o=r.F._hufTree(s.lhst,s.ltree,15),i=r.F._hufTree(s.dhst,s.dtree,15),a=[],l=r.F._lenCodes(s.ltree,a),c=[],d=r.F._lenCodes(s.dtree,c),u=0;u4&&s.itree[1+(s.ordr[g-1]<<1)]==0;)g--;return[o,i,f,l,d,g,a,c]},r.F.getSecond=function(s){for(var o=[],i=0;i>1)+",");return o},r.F.contSize=function(s,o){for(var i=0,a=0;a15&&(r.F._putsE(i,a,d,u),a+=u)}return a},r.F._lenCodes=function(s,o){for(var i=s.length;i!=2&&s[i-1]==0;)i-=2;for(var a=0;a>>1,138))<11?o.push(17,g-3):o.push(18,g-11),a+=2*g-2}else if(l==u&&c==l&&d==l){for(f=a+5;f+2>>1,6);o.push(16,g-3),a+=2*g-2}else o.push(l,0)}return i>>>1},r.F._hufTree=function(s,o,i){var a=[],l=s.length,c=o.length,d=0;for(d=0;di&&(r.F.restrictDepth(f,i,x),x=i),d=0;do;a++){var d=s[a].d;s[a].d=o,c+=l-(1<>>=i-o;c>0;)(d=s[a].d)=0;a--)s[a].d==o&&c<0&&(s[a].d--,c++);c!=0&&console.log("debt left")},r.F._goodIndex=function(s,o){var i=0;return o[16|i]<=s&&(i|=16),o[8|i]<=s&&(i|=8),o[4|i]<=s&&(i|=4),o[2|i]<=s&&(i|=2),o[1|i]<=s&&(i|=1),i},r.F._writeLit=function(s,o,i,a){return r.F._putsF(i,a,o[s<<1]),a+o[1+(s<<1)]},r.F.inflate=function(s,o){var i=Uint8Array;if(s[0]==3&&s[1]==0)return o||new i(0);var a=r.F,l=a._bitsF,c=a._bitsE,d=a._decodeTiny,u=a.makeCodes,f=a.codes2map,g=a._get17,m=a.U,p=o==null;p&&(o=new i(s.length>>>2<<3));for(var y,b,_=0,x=0,M=0,S=0,k=0,N=0,C=0,w=0,E=0;_==0;)if(_=l(s,E,1),x=l(s,E+1,2),E+=3,x!=0){if(p&&(o=r.F._check(o,w+131072)),x==1&&(y=m.flmap,b=m.fdmap,N=511,C=31),x==2){M=c(s,E,5)+257,S=c(s,E+5,5)+1,k=c(s,E+10,4)+4,E+=14;for(var A=0;A<38;A+=2)m.itree[A]=0,m.itree[A+1]=0;var P=1;for(A=0;AP&&(P=L)}E+=3*k,u(m.itree,P),f(m.itree,P,m.imap),y=m.lmap,b=m.dmap,E=d(m.imap,(1<>>4;if(!(T>>>8))o[w++]=T;else{if(T==256)break;var z=w+T-254;if(T>264){var U=m.ldef[T-257];z=w+(U>>>3)+c(s,E,7&U),E+=7&U}var W=b[g(s,E)&C];E+=15&W;var G=W>>>4,B=m.ddef[G],j=(B>>>4)+l(s,E,15&B);for(E+=15&B,p&&(o=r.F._check(o,w+131072));w>>3),V=s[Y-4]|s[Y-3]<<8;p&&(o=r.F._check(o,w+V)),o.set(new i(s.buffer,s.byteOffset+Y,V),w),E=Y+V<<3,w+=V}return o.length==w?o:o.slice(0,w)},r.F._check=function(s,o){var i=s.length;if(o<=i)return s;var a=new Uint8Array(Math.max(i<<1,o));return a.set(s,0),a},r.F._decodeTiny=function(s,o,i,a,l,c){for(var d=r.F._bitsE,u=r.F._get17,f=0;f>>4;if(m<=15)c[f]=m,f++;else{var p=0,y=0;m==16?(y=3+d(a,l,2),l+=2,p=c[f-1]):m==17?(y=3+d(a,l,3),l+=3):m==18&&(y=11+d(a,l,7),l+=7);for(var b=f+y;f>>1;cl&&(l=u),c++}for(;c>1,u=s[c+1],f=d<<4|u,g=o-u,m=s[c]<>>15-o]=f,m++},r.F.revCodes=function(s,o){for(var i=r.F.U.rev15,a=15-o,l=0;l>>a}},r.F._putsE=function(s,o,i){i<<=7&o;var a=o>>>3;s[a]|=i,s[a+1]|=i>>>8},r.F._putsF=function(s,o,i){i<<=7&o;var a=o>>>3;s[a]|=i,s[a+1]|=i>>>8,s[a+2]|=i>>>16},r.F._bitsE=function(s,o,i){return(s[o>>>3]|s[1+(o>>>3)]<<8)>>>(7&o)&(1<>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16)>>>(7&o)&(1<>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16)>>>(7&o)},r.F._get25=function(s,o){return(s[o>>>3]|s[1+(o>>>3)]<<8|s[2+(o>>>3)]<<16|s[3+(o>>>3)]<<24)>>>(7&o)},r.F.U=(t=Uint16Array,n=Uint32Array,{next_code:new t(16),bl_count:new t(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new t(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new n(32),flmap:new t(512),fltree:[],fdmap:new t(32),fdtree:[],lmap:new t(32768),ltree:[],ttree:[],dmap:new t(32768),dtree:[],imap:new t(512),itree:[],rev15:new t(32768),lhst:new n(286),dhst:new n(30),ihst:new n(19),lits:new n(15e3),strt:new t(65536),prev:new t(32768)}),(function(){for(var s=r.F.U,o=0;o<32768;o++){var i=o;i=(4278255360&(i=(4042322160&(i=(3435973836&(i=(2863311530&i)>>>1|(1431655765&i)<<1))>>>2|(858993459&i)<<2))>>>4|(252645135&i)<<4))>>>8|(16711935&i)<<8,s.rev15[o]=(i>>>16|i<<16)>>>17}function a(l,c,d){for(;c--!=0;)l.push(0,d)}for(o=0;o<32;o++)s.ldef[o]=s.of0[o]<<3|s.exb[o],s.ddef[o]=s.df0[o]<<4|s.dxb[o];a(s.fltree,144,8),a(s.fltree,112,9),a(s.fltree,24,7),a(s.fltree,8,8),r.F.makeCodes(s.fltree,9),r.F.codes2map(s.fltree,9,s.flmap),r.F.revCodes(s.fltree,9),a(s.fdtree,32,5),r.F.makeCodes(s.fdtree,5),r.F.codes2map(s.fdtree,5,s.fdmap),r.F.revCodes(s.fdtree,5),a(s.itree,19,0),a(s.ltree,286,0),a(s.dtree,30,0),a(s.ttree,320,0)})()})();var CE=xE({__proto__:null,default:Qi},[Qi]);const An=(function(){var e={nextZero(d,u){for(;d[u]!=0;)u++;return u},readUshort:(d,u)=>d[u]<<8|d[u+1],writeUshort(d,u,f){d[u]=f>>8&255,d[u+1]=255&f},readUint:(d,u)=>16777216*d[u]+(d[u+1]<<16|d[u+2]<<8|d[u+3]),writeUint(d,u,f){d[u]=f>>24&255,d[u+1]=f>>16&255,d[u+2]=f>>8&255,d[u+3]=255&f},readASCII(d,u,f){let g="";for(let m=0;md.length<2?`0${d}`:d,readUTF8(d,u,f){let g,m="";for(let p=0;p>3)]>>7-((7&k)<<0)&1);b[w]=U[L],b[w+1]=U[L+1],b[w+2]=U[L+2],b[w+3]=$>2)]>>6-((3&k)<<1)&3),b[w]=U[L],b[w+1]=U[L+1],b[w+2]=U[L+2],b[w+3]=$>1)]>>4-((1&k)<<2)&15),b[w]=U[L],b[w+1]=U[L+1],b[w+2]=U[L+2],b[w+3]=$>>3)]>>>7-(7&T)&1))==255*C?0:255;_[W+T]=z<<24|D<<16|D<<8|D}else if(M==2)for(T=0;T>>2)]>>>6-((3&T)<<1)&3))==85*C?0:255,_[W+T]=z<<24|D<<16|D<<8|D;else if(M==4)for(T=0;T>>1)]>>>4-((1&T)<<2)&15))==17*C?0:255,_[W+T]=z<<24|D<<16|D<<8|D;else if(M==8)for(T=0;T>3,C=Math.ceil(M*k/8),w=new Uint8Array(S*C);let E=0;const A=[0,0,4,0,2,0,1],P=[0,4,0,2,0,1,0],L=[8,8,8,4,4,2,2],$=[8,8,4,4,2,2,1];let D=0;for(;D<7;){const T=L[D],z=$[D];let U=0,W=0,G=A[D];for(;G>3])>>7-(7&K)&1,w[V*C+(X>>3)]|=O<<7-((7&X)<<0)),k==2&&(O=(O=_[K>>3])>>6-(7&K)&3,w[V*C+(X>>2)]|=O<<6-((3&X)<<1)),k==4&&(O=(O=_[K>>3])>>4-(7&K)&15,w[V*C+(X>>1)]|=O<<4-((1&X)<<2)),k>=8){const q=V*C+X*N;for(let ne=0;ne>3)+ne]}K+=k,X+=z}Y++,V+=T}U*W!=0&&(E+=W*(1+j)),D+=1}return w})(u,d)),u}function r(d,u){return s(new Uint8Array(d.buffer,2,d.length-6),u)}var s=(function(){const d={H:{}};return d.H.N=function(u,f){const g=Uint8Array;let m,p,y=0,b=0,_=0,x=0,M=0,S=0,k=0,N=0,C=0;if(u[0]==3&&u[1]==0)return f||new g(0);const w=d.H,E=w.b,A=w.e,P=w.R,L=w.n,$=w.A,D=w.Z,O=w.m,T=f==null;for(T&&(f=new g(u.length>>>2<<5));y==0;)if(y=E(u,C,1),b=E(u,C+1,2),C+=3,b!=0){if(T&&(f=d.H.W(f,N+(1<<17))),b==1&&(m=O.J,p=O.h,S=511,k=31),b==2){_=A(u,C,5)+257,x=A(u,C+5,5)+1,M=A(u,C+10,4)+4,C+=14;let U=1;for(var z=0;z<38;z+=2)O.Q[z]=0,O.Q[z+1]=0;for(z=0;zU&&(U=B)}C+=3*M,L(O.Q,U),$(O.Q,U,O.u),m=O.w,p=O.d,C=P(O.u,(1<>>4;if(!(W>>>8))f[N++]=W;else{if(W==256)break;{let G=N+W-254;if(W>264){const X=O.q[W-257];G=N+(X>>>3)+A(u,C,7&X),C+=7&X}const B=p[D(u,C)&k];C+=15&B;const j=B>>>4,Y=O.c[j],V=(Y>>>4)+E(u,C,15&Y);for(C+=15&Y;N>>3),W=u[U-4]|u[U-3]<<8;T&&(f=d.H.W(f,N+W)),f.set(new g(u.buffer,u.byteOffset+U,W),N),C=U+W<<3,N+=W}return f.length==N?f:f.slice(0,N)},d.H.W=function(u,f){const g=u.length;if(f<=g)return u;const m=new Uint8Array(g<<1);return m.set(u,0),m},d.H.R=function(u,f,g,m,p,y){const b=d.H.e,_=d.H.Z;let x=0;for(;x>>4;if(S<=15)y[x]=S,x++;else{let k=0,N=0;S==16?(N=3+b(m,p,2),p+=2,k=y[x-1]):S==17?(N=3+b(m,p,3),p+=3):S==18&&(N=11+b(m,p,7),p+=7);const C=x+N;for(;x>>1;for(;yp&&(p=_),y++}for(;y>1,_=u[y+1],x=b<<4|_,M=f-_;let S=u[y]<>>15-f]=x,S++}},d.H.l=function(u,f){const g=d.H.m.r,m=15-f;for(let p=0;p>>m}},d.H.M=function(u,f,g){g<<=7&f;const m=f>>>3;u[m]|=g,u[m+1]|=g>>>8},d.H.I=function(u,f,g){g<<=7&f;const m=f>>>3;u[m]|=g,u[m+1]|=g>>>8,u[m+2]|=g>>>16},d.H.e=function(u,f,g){return(u[f>>>3]|u[1+(f>>>3)]<<8)>>>(7&f)&(1<>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16)>>>(7&f)&(1<>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16)>>>(7&f)},d.H.i=function(u,f){return(u[f>>>3]|u[1+(f>>>3)]<<8|u[2+(f>>>3)]<<16|u[3+(f>>>3)]<<24)>>>(7&f)},d.H.m=(function(){const u=Uint16Array,f=Uint32Array;return{K:new u(16),j:new u(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new u(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new f(32),J:new u(512),_:[],h:new u(32),$:[],w:new u(32768),C:[],v:[],d:new u(32768),D:[],u:new u(512),Q:[],r:new u(32768),s:new f(286),Y:new f(30),a:new f(19),t:new f(15e3),k:new u(65536),g:new u(32768)}})(),(function(){const u=d.H.m;for(var f=0;f<32768;f++){let m=f;m=(2863311530&m)>>>1|(1431655765&m)<<1,m=(3435973836&m)>>>2|(858993459&m)<<2,m=(4042322160&m)>>>4|(252645135&m)<<4,m=(4278255360&m)>>>8|(16711935&m)<<8,u.r[f]=(m>>>16|m<<16)>>>17}function g(m,p,y){for(;p--!=0;)m.push(0,y)}for(f=0;f<32;f++)u.q[f]=u.S[f]<<3|u.T[f],u.c[f]=u.p[f]<<4|u.z[f];g(u._,144,8),g(u._,112,9),g(u._,24,7),g(u._,8,8),d.H.n(u._,9),d.H.A(u._,9,u.J),d.H.l(u._,9),g(u.$,32,5),d.H.n(u.$,5),d.H.A(u.$,5,u.h),d.H.l(u.$,5),g(u.Q,19,0),g(u.C,286,0),g(u.D,30,0),g(u.v,320,0)})(),d.H.N})();function o(d){return[1,null,3,1,2,null,4][d.ctype]*d.depth}function i(d,u,f,g,m){let p=o(u);const y=Math.ceil(g*p/8);let b,_;p=Math.ceil(p/8);let x=d[f],M=0;if(x>1&&(d[f]=[0,0,1][x-2]),x==3)for(M=p;M>>1)&255;for(let S=0;S>>1);for(;M>>1)}else{for(;M=0&&b>=0?(S=D*u+O<<2,k=(b+D)*m+y+O<<2):(S=(-b+D)*u-y+O<<2,k=D*m+O<<2),_==0)g[k]=d[S],g[k+1]=d[S+1],g[k+2]=d[S+2],g[k+3]=d[S+3];else if(_==1){var N=d[S+3]*.00392156862745098,C=d[S]*N,w=d[S+1]*N,E=d[S+2]*N,A=g[k+3]*(1/255),P=g[k]*A,L=g[k+1]*A,$=g[k+2]*A;const T=1-N,z=N+A*T,U=z==0?0:1/z;g[k+3]=255*z,g[k+0]=(C+P*T)*U,g[k+1]=(w+L*T)*U,g[k+2]=(E+$*T)*U}else if(_==2)N=d[S+3],C=d[S],w=d[S+1],E=d[S+2],A=g[k+3],P=g[k],L=g[k+1],$=g[k+2],N==A&&C==P&&w==L&&E==$?(g[k]=0,g[k+1]=0,g[k+2]=0,g[k+3]=0):(g[k]=C,g[k+1]=w,g[k+2]=E,g[k+3]=N);else if(_==3){if(N=d[S+3],C=d[S],w=d[S+1],E=d[S+2],A=g[k+3],P=g[k],L=g[k+1],$=g[k+2],N==A&&C==P&&w==L&&E==$)continue;if(N<220&&A>20)return!1}return!0}return{decode:function(u){const f=new Uint8Array(u);let g=8;const m=e,p=m.readUshort,y=m.readUint,b={tabs:{},frames:[]},_=new Uint8Array(f.length);let x,M=0,S=0;const k=[137,80,78,71,13,10,26,10];for(var N=0;N<8;N++)if(f[N]!=k[N])throw"The input is not a PNG file!";for(;g>>1:E>>>=1;C[w]=E}return C})(),update(C,w,E,A){for(let P=0;P>>8;return C},crc:(C,w,E)=>4294967295^r.update(4294967295,C,w,E)};function s(C,w,E,A){w[E]+=C[0]*A>>4,w[E+1]+=C[1]*A>>4,w[E+2]+=C[2]*A>>4,w[E+3]+=C[3]*A>>4}function o(C){return Math.max(0,Math.min(255,C))}function i(C,w){const E=C[0]-w[0],A=C[1]-w[1],P=C[2]-w[2],L=C[3]-w[3];return E*E+A*A+P*P+L*L}function a(C,w,E,A,P,L,$){$==null&&($=1);const D=A.length,O=[];for(var T=0;T>>0&255,V>>>8&255,V>>>16&255,V>>>24&255])}for(T=0;T>2]=z,G[T>>2]=A[z]}}function l(C,w,E,A,P){P==null&&(P={});const{crc:L}=r,$=t.writeUint,D=t.writeUshort,O=t.writeASCII;let T=8;const z=C.frames.length>1;let U,W=!1,G=33+(z?20:0);if(P.sRGB!=null&&(G+=13),P.pHYs!=null&&(G+=21),P.iCCP!=null&&(U=pako.deflate(P.iCCP),G+=21+U.length+4),C.ctype==3){for(var B=C.plte.length,j=0;j>>24!=255&&(W=!0);G+=8+3*B+4+(W?8+1*B+4:0)}for(var Y=0;Y>>8&255,le=ee>>>16&255;V[T+ne+0]=fe,V[T+ne+1]=te,V[T+ne+2]=le}if(T+=3*B,$(V,T,L(V,T-3*B-4,3*B+4)),T+=4,W){for($(V,T,B),T+=4,O(V,T,"tRNS"),T+=4,j=0;j>>24&255;T+=B,$(V,T,L(V,T-B-4,B+4)),T+=4}}let K=0;for(Y=0;Yze&&(ze=at),Yert&&(rt=Ye));ze==-1&&(Le=Ee=ze=rt=0),ie&&((1&Le)==1&&Le--,(1&Ee)==1&&Ee--);const it=(ze-Le+1)*(rt-Ee+1);it>2,$e>>2);ne.push(ee);const ie=new Uint8Array(pe.abuf,ke,$e);z&&a(fe.img,fe.rect.width,fe.rect.height,q,ie,ee),fe.img.set(ie),ke+=$e}}else for(B=0;Bte&&me==ye[Y-te])ee[Y]=ee[Y-te];else{let pe=K[me];if(pe==null&&(K[me]=pe=q.length,q.push(me),q.length>=300))break;ee[Y]=pe}}}const le=q.length;for(le<=256&&T==0&&(W=le<=2?1:le<=4?2:le<=16?4:8,W=Math.max(W,O)),B=0;B>1)]|=$e[_e+ce]<<4-4*(1&ce);else if(W==2)for(ce=0;ce>2)]|=$e[_e+ce]<<6-2*(3&ce);else if(W==1)for(ce=0;ce>3)]|=$e[_e+ce]<<7-1*(7&ce)}me=ge,U=3,ke=1}else if(V==0&&X.length==1){ge=new Uint8Array(te*ye*3);const $e=te*ye;for(Y=0;Y<$e;Y++){const ie=3*Y,_e=4*Y;ge[ie]=me[_e],ge[ie+1]=me[_e+1],ge[ie+2]=me[_e+2]}me=ge,U=2,ke=3,pe=3*te}fe.img=me,fe.bpl=pe,fe.bpp=ke}return{ctype:U,depth:W,plte:q,frames:X}}function u(C,w,E,A,P,L,$){const D=Uint8Array,O=Uint32Array,T=new D(C[P-1]),z=new O(C[P-1]),U=P+1Y&&(Y=ne),eeV&&(V=ee))}Y==-1&&(B=j=Y=V=0),$&&((1&B)==1&&B--,(1&j)==1&&j--),L={x:B,y:j,width:Y-B+1,height:V-j+1};const X=A[P];X.rect=L,X.blend=1,X.img=new Uint8Array(L.width*L.height*4),A[P-1].dispose==0?(e(T,w,E,X.img,L.width,L.height,-L.x,-L.y,0),f(W,w,E,X.img,L)):e(W,w,E,X.img,L.width,L.height,-L.x,-L.y,0)}function f(C,w,E,A,P){e(C,w,E,A,P.width,P.height,-P.x,-P.y,2)}function g(C,w,E,A,P,L,$){const D=[];let O,T=[0,1,2,3,4];L!=-1?T=[L]:(w*A>5e5||E==1)&&(T=[0]),$&&(O={level:0});const z=CE;for(var U=0;U>1)+256&255;if(L==4)for(O=P;O>1)&255;for(O=P;O>1)&255}if(L==4){for(O=0;O>2);let z;if(E.length<2e7)for(var U=0;U>2]=z.ind,P[U>>2]=z.est.rgba;else for(U=0;U>2]=z.ind,P[U>>2]=z.est.rgba}return{abuf:A.buffer,inds:T,plte:D}}function y(C,w,E){E==null&&(E=1e-4);const A=new Uint32Array(C.buffer),P={i0:0,i1:C.length,bst:null,est:null,tdst:0,left:null,right:null};P.bst=S(C,P.i0,P.i1),P.est=k(P.bst);const L=[P];for(;L.lengthD&&(D=L[$].est.L,O=$);if(D=z||T.i1<=z){T.est.L=0;continue}const U={i0:T.i0,i1:z,bst:null,est:null,tdst:0,left:null,right:null};U.bst=S(C,U.i0,U.i1),U.est=k(U.bst);const W={i0:z,i1:T.i1,bst:null,est:null,tdst:0,left:null,right:null};for(W.bst={R:[],m:[],N:T.bst.N-U.bst.N},$=0;$<16;$++)W.bst.R[$]=T.bst.R[$]-U.bst.R[$];for($=0;$<4;$++)W.bst.m[$]=T.bst.m[$]-U.bst.m[$];W.est=k(W.bst),T.left=U,T.right=W,L[O]=U,L.push(W)}for(L.sort(((D,O)=>O.bst.N-D.bst.N)),$=0;$0&&($=C.right,D=C.left);const O=b($,w,E,A,P);if(O.tdst<=L*L)return O;const T=b(D,w,E,A,P);return T.tdstL;)A-=4;if(E>=A)break;const $=w[E>>2];w[E>>2]=w[A>>2],w[A>>2]=$,E+=4,A-=4}for(;M(C,E,P)>L;)E-=4;return E+4}function M(C,w,E){return C[w]*E[0]+C[w+1]*E[1]+C[w+2]*E[2]+C[w+3]*E[3]}function S(C,w,E){const A=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],P=[0,0,0,0],L=E-w>>2;for(let $=w;$>>0}}var N={multVec:(C,w)=>[C[0]*w[0]+C[1]*w[1]+C[2]*w[2]+C[3]*w[3],C[4]*w[0]+C[5]*w[1]+C[6]*w[2]+C[7]*w[3],C[8]*w[0]+C[9]*w[1]+C[10]*w[2]+C[11]*w[3],C[12]*w[0]+C[13]*w[1]+C[14]*w[2]+C[15]*w[3]],dot:(C,w)=>C[0]*w[0]+C[1]*w[1]+C[2]*w[2]+C[3]*w[3],sml:(C,w)=>[C*w[0],C*w[1],C*w[2],C*w[3]]};An.encode=function(w,E,A,P,L,$,D){P==null&&(P=0),D==null&&(D=!1);const O=d(w,E,A,P,[!1,!1,!1,0,D,!1]);return c(O,-1),l(O,E,A,L,$)},An.encodeLL=function(w,E,A,P,L,$,D,O){const T={ctype:0+(P==1?0:2)+(L==0?0:4),depth:$,frames:[]},z=(P+L)*$,U=z*E;for(let W=0;W>>0),S(1),S(32),k(3),k(l),k(2835),k(2835),N(8),k(16711680),k(65280),k(255),k(4278190080),k(1466527264),(function C(){for(;_0;){for(y=122+_*a,g=0;g>>24,u.setUint32(y+g,m<<8|p),g+=4;_++}M{t(new Blob([n],{type:"image/bmp"}))}))},_dly:9};var tn={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},SE={[tn.CHROME]:16384,[tn.FIREFOX]:11180,[tn.DESKTOP_SAFARI]:16384,[tn.IE]:8192,[tn.IOS]:4096,[tn.ETC]:8192};const zc=typeof window<"u",Mp=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,Ji=zc&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),_E=(zc||Mp)&&(Ji&&Ji.getOriginalSymbol(window,"File")||typeof File<"u"&&File),Np=(zc||Mp)&&(Ji&&Ji.getOriginalSymbol(window,"FileReader")||typeof FileReader<"u"&&FileReader);function Vc(e,t,n=Date.now()){return new Promise((r=>{const s=e.split(","),o=s[0].match(/:(.*?);/)[1],i=globalThis.atob(s[1]);let a=i.length;const l=new Uint8Array(a);for(;a--;)l[a]=i.charCodeAt(a);const c=new Blob([l],{type:o});c.name=t,c.lastModified=n,r(c)}))}function Tp(e){return new Promise(((t,n)=>{const r=new Np;r.onload=()=>t(r.result),r.onerror=s=>n(s),r.readAsDataURL(e)}))}function Op(e){return new Promise(((t,n)=>{const r=new Image;r.onload=()=>t(r),r.onerror=s=>n(s),r.src=e}))}function Ar(){if(Ar.cachedResult!==void 0)return Ar.cachedResult;let e=tn.ETC;const{userAgent:t}=navigator;return/Chrom(e|ium)/i.test(t)?e=tn.CHROME:/iP(ad|od|hone)/i.test(t)&&/WebKit/i.test(t)?e=tn.IOS:/Safari/i.test(t)?e=tn.DESKTOP_SAFARI:/Firefox/i.test(t)?e=tn.FIREFOX:(/MSIE/i.test(t)||document.documentMode)&&(e=tn.IE),Ar.cachedResult=e,Ar.cachedResult}function Ap(e,t){const n=Ar(),r=SE[n];let s=e,o=t,i=s*o;const a=s>o?o/s:s/o;for(;i>r*r;){const l=(r+s)/2,c=(r+o)/2;lEp.toBlob(e,d))).then(function(d){try{return a=d,a.name=n,a.lastModified=r,c.call(this)}catch(u){return i(u)}}.bind(this),i);{let d=function(){return c.call(this)};if(typeof OffscreenCanvas=="function"&&e instanceof OffscreenCanvas)return e.convertToBlob({type:t,quality:s}).then(function(u){try{return a=u,a.name=n,a.lastModified=r,d.call(this)}catch(f){return i(f)}}.bind(this),i);{let u;return u=e.toDataURL(t,s),Vc(u,n,r).then(function(f){try{return a=f,d.call(this)}catch(g){return i(g)}}.bind(this),i)}}}function l(){return o(a)}}))}function Bn(e){e.width=0,e.height=0}function ws(){return new Promise((function(e,t){let n,r,s,o;return ws.cachedResult!==void 0?e(ws.cachedResult):Vc("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(i){try{return n=i,ta(n).then((function(a){try{return r=a[1],na(r,n.type,n.name,n.lastModified).then((function(l){try{return s=l,Bn(r),ta(s).then((function(c){try{return o=c[0],ws.cachedResult=o.width===1&&o.height===2,e(ws.cachedResult)}catch(d){return t(d)}}),t)}catch(c){return t(c)}}),t)}catch(l){return t(l)}}),t)}catch(a){return t(a)}}),t)}))}function Ip(e){return new Promise(((t,n)=>{const r=new Np;r.onload=s=>{const o=new DataView(s.target.result);if(o.getUint16(0,!1)!=65496)return t(-2);const i=o.byteLength;let a=2;for(;an(s),r.readAsArrayBuffer(e)}))}function Lp(e,t){const{width:n}=e,{height:r}=e,{maxWidthOrHeight:s}=t;let o,i=e;return isFinite(s)&&(n>s||r>s)&&([i,o]=Ri(n,r),n>r?(i.width=s,i.height=r/n*s):(i.width=n/r*s,i.height=s),o.drawImage(e,0,0,i.width,i.height),Bn(e)),i}function Pp(e,t){const{width:n}=e,{height:r}=e,[s,o]=Ri(n,r);switch(t>4&&t<9?(s.width=r,s.height=n):(s.width=n,s.height=r),t){case 2:o.transform(-1,0,0,1,n,0);break;case 3:o.transform(-1,0,0,-1,n,r);break;case 4:o.transform(1,0,0,-1,0,r);break;case 5:o.transform(0,1,1,0,0,0);break;case 6:o.transform(0,1,-1,0,r,0);break;case 7:o.transform(0,-1,-1,0,r,n);break;case 8:o.transform(0,-1,1,0,0,n)}return o.drawImage(e,0,0,n,r),Bn(e),s}function $p(e,t,n=0){return new Promise((function(r,s){let o,i,a,l,c,d,u,f,g,m,p,y,b,_,x,M,S,k,N,C;function w(A=5){if(t.signal&&t.signal.aborted)throw t.signal.reason;o+=A,t.onProgress(Math.min(o,100))}function E(A){if(t.signal&&t.signal.aborted)throw t.signal.reason;o=Math.min(Math.max(A,o),100),t.onProgress(o)}return o=n,i=t.maxIteration||10,a=1024*t.maxSizeMB*1024,w(),ta(e,t).then(function(A){try{return[,l]=A,w(),c=Lp(l,t),w(),new Promise((function(P,L){var $;if(!($=t.exifOrientation))return Ip(e).then(function(O){try{return $=O,D.call(this)}catch(T){return L(T)}}.bind(this),L);function D(){return P($)}return D.call(this)})).then(function(P){try{return d=P,w(),ws().then(function(L){try{return u=L?c:Pp(c,d),w(),f=t.initialQuality||1,g=t.fileType||e.type,na(u,g,e.name,e.lastModified,f).then(function($){try{{let O=function(){if(i--&&(x>a||x>b)){let z,U;return z=C?.95*N.width:N.width,U=C?.95*N.height:N.height,[S,k]=Ri(z,U),k.drawImage(N,0,0,z,U),f*=g==="image/png"?.85:.95,na(S,g,e.name,e.lastModified,f).then((function(W){try{return M=W,Bn(N),N=S,x=M.size,E(Math.min(99,Math.floor((_-x)/(_-a)*100))),O}catch(G){return s(G)}}),s)}return[1]},T=function(){return Bn(N),Bn(S),Bn(c),Bn(u),Bn(l),E(100),r(M)};if(m=$,w(),p=m.size>a,y=m.size>e.size,!p&&!y)return E(100),r(m);var D;return b=e.size,_=m.size,x=_,N=u,C=!t.alwaysKeepResolution&&p,(D=function(z){for(;z;){if(z.then)return void z.then(D,s);try{if(z.pop){if(z.length)return z.pop()?T.call(this):z;z=O}else z=z.call(this)}catch(U){return s(U)}}}.bind(this))(O)}}catch(O){return s(O)}}.bind(this),s)}catch($){return s($)}}.bind(this),s)}catch(L){return s(L)}}.bind(this),s)}catch(P){return s(P)}}.bind(this),s)}))}const EE=` let scriptImported = false self.addEventListener('message', async (e) => { const { file, id, imageCompressionLibUrl, options } = e.data @@ -220,11 +221,11 @@ self.addEventListener('message', async (e) => { self.postMessage({ error: e.message + '\\n' + e.stack, id }) } }) -`;let Ic;function lE(t,e){return new Promise(((n,r)=>{Ic||(Ic=(function(i){const a=[];return a.push(i),URL.createObjectURL(new Blob(a))})(aE));const s=new Worker(Ic);s.addEventListener("message",(function(i){if(e.signal&&e.signal.aborted)s.terminate();else if(i.data.progress===void 0){if(i.data.error)return r(new Error(i.data.error)),void s.terminate();n(i.data.file),s.terminate()}else e.onProgress(i.data.progress)})),s.addEventListener("error",r),e.signal&&e.signal.addEventListener("abort",(()=>{r(e.signal.reason),s.terminate()})),s.postMessage({file:t,imageCompressionLibUrl:e.libURL,options:{...e,onProgress:void 0,signal:void 0}})}))}function Ht(t,e){return new Promise((function(n,r){let s,o,i,a,l,c;if(s={...e},i=0,{onProgress:a}=s,s.maxSizeMB=s.maxSizeMB||Number.POSITIVE_INFINITY,l=typeof s.useWebWorker!="boolean"||s.useWebWorker,delete s.useWebWorker,s.onProgress=g=>{i=g,typeof a=="function"&&a(i)},!(t instanceof Blob||t instanceof iE))return r(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(t.type))return r(new Error("The file given is not an image"));if(c=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,!l||typeof Worker!="function"||c)return Sp(t,s).then(function(g){try{return o=g,f.call(this)}catch(m){return r(m)}}.bind(this),r);var d=function(){try{return f.call(this)}catch(g){return r(g)}}.bind(this),u=function(g){try{return Sp(t,s).then((function(m){try{return o=m,d()}catch(p){return r(p)}}),r)}catch(m){return r(m)}};try{return s.libURL=s.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",lE(t,s).then((function(g){try{return o=g,d()}catch{return u()}}),u)}catch{u()}function f(){try{o.name=t.name,o.lastModified=t.lastModified}catch{}try{s.preserveExif&&t.type==="image/jpeg"&&(!s.fileType||s.fileType&&s.fileType===t.type)&&(o=hp(t,o))}catch{}return n(o)}}))}Ht.getDataUrlFromFile=bp,Ht.getFilefromDataUrl=Dc,Ht.loadImage=yp,Ht.drawImageInCanvas=xp,Ht.drawFileInCanvas=Xi,Ht.canvasToFile=qi,Ht.getExifOrientation=wp,Ht.handleMaxWidthOrHeight=kp,Ht.followExifOrientation=Cp,Ht.cleanupCanvasMemory=$n,Ht.isAutoOrientationInBrowser=as,Ht.approximateBelowMaximumCanvasSizeOfBrowser=vp,Ht.copyExifWithoutOrientation=hp,Ht.getBrowserName=kr,Ht.version="2.0.2";const cE=window.G7Core,Lc=t=>{const e=["B","KB","MB","GB"];let n=0,r=t;for(;r>=1024&&n{if(t&&typeof t=="object"){const e=t;if(e.response?.data?.message)return e.response.data.message;if(e.response?.data?.errors){const n=Object.values(e.response.data.errors)[0];if(Array.isArray(n)&&n[0])return n[0]}if(e.message)return e.message}return t instanceof Error?t.message:"업로드 실패"},uE=t=>t?t.startsWith("image/")?"fa-file-image":t.startsWith("video/")?"fa-file-video":t.startsWith("audio/")?"fa-file-audio":t==="application/pdf"?"fa-file-pdf":t.includes("word")||t.includes("document")?"fa-file-word":t.includes("excel")||t.includes("spreadsheet")?"fa-file-excel":t.includes("zip")||t.includes("compressed")?"fa-file-zipper":"fa-file":"fa-file",It=(t,e)=>cE?.t?.(t,e)??t,_p=window.G7Core?.createLogger?.("Comp:FileUploader")??{log:(...t)=>console.log("[Comp:FileUploader]",...t),warn:(...t)=>console.warn("[Comp:FileUploader]",...t),error:(...t)=>console.error("[Comp:FileUploader]",...t)},Cr=window.G7Core;function fE(t){const{attachmentableType:e,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:u,endpoints:f,onFilesChange:g,onUploadComplete:m,onUploadError:p,onRemove:y,onReorder:b,uploadTriggerEvent:_,confirmBeforeRemove:x,uploadParams:E}=t,[C,k]=v.useState(u),[N,S]=v.useState([]),[w,M]=v.useState(!1),T=v.useRef(null),P=v.useRef(0),L=v.useRef(new Set),$=v.useRef(new Map),A=v.useRef(null),[D,O]=v.useState(!1),[z,H]=v.useState(null),[j,W]=v.useState(!1),U=v.useRef(!1),[B,G]=v.useState([]),[V,X]=v.useState(!1),[K,Z]=v.useState(0),ee=v.useRef(0),[ne,he]=v.useState(new Map),J=v.useRef(new Map),le=v.useRef(g);le.current=g,v.useEffect(()=>{le.current?.(N)},[N]),v.useEffect(()=>{if(U.current)return;const te=u.filter(pe=>!L.current.has(pe.id)&&(!pe.hash||!L.current.has(pe.hash))),ge=new Set(te.map(pe=>pe.id)),me=Array.from($.current.values()).filter(pe=>!ge.has(pe.id)&&!L.current.has(pe.id));k([...te,...me])},[u]);const ue=v.useCallback(async te=>{const ge={maxSizeMB:l?.maxSizeMB??1,maxWidthOrHeight:l?.maxWidthOrHeight??1920,useWebWorker:!0};return await Ht(te,ge)},[l]),ae=v.useCallback(async te=>{const ge=C.length+N.length,me=Math.max(0,s-ge),pe=Array.from(te).slice(0,me);if(te.length>me){const be=Array.from(te).slice(me),Ue=It("attachment.upload_limit_exceeded",{max:s});p?.(Ue,be[0]??te[0])}for(const be of pe){if(be.size>o*1024*1024){p?.(`[${be.name}] 파일 크기(${Lc(be.size)})가 최대 허용 크기(${o}MB)를 초과합니다.`,be);continue}if(a){const Ce="."+be.name.split(".").pop()?.toLowerCase();if(!a.split(",").map(Se=>Se.trim().toLowerCase()).includes(Ce)){p?.(`[${be.name}] 허용되지 않는 파일 형식입니다. (허용: ${a})`,be);continue}}const Ue={id:`pending-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,file:be,original_filename:be.name,mime_type:be.type,size:be.size,size_formatted:Lc(be.size),status:"pending",progress:0};if(be.type.startsWith("image/")&&(Ue.preview=URL.createObjectURL(be)),S(Ce=>[...Ce,Ue]),be.type.startsWith("image/")&&l){S(Ce=>Ce.map(ze=>ze.id===Ue.id?{...ze,status:"compressing"}:ze));try{const Ce=await ue(be);S(ze=>ze.map(Se=>Se.id===Ue.id?{...Se,compressedBlob:Ce,size:Ce.size,size_formatted:Lc(Ce.size),status:"pending",progress:0}:Se))}catch{S(Ce=>Ce.map(ze=>ze.id===Ue.id?{...ze,status:"error",error:"이미지 압축 실패"}:ze))}}}},[C.length,N.length,s,o,a,l,ue,p]),ve=v.useCallback(async te=>{const ge=new FormData,me=te.compressedBlob?new File([te.compressedBlob],te.original_filename,{type:te.mime_type}):te.file;ge.append("file",me),e&&ge.append("attachmentable_type",e),n&&ge.append("attachmentable_id",String(n)),ge.append("collection",r),c.forEach(pe=>{ge.append("role_ids[]",String(pe))}),E&&Object.entries(E).forEach(([pe,be])=>{ge.append(pe,be)});try{const pe=await Cr.api.post(f.upload,ge,{headers:{"Content-Type":"multipart/form-data"},onUploadProgress:Ue=>{const Ce=Math.round(Ue.loaded*100/(Ue.total??1));S(ze=>ze.map(Se=>Se.id===te.id?{...Se,progress:Ce}:Se))}});return pe?.data?.data??pe?.data}catch(pe){const be=dE(pe);throw new Error(be)}},[e,n,r,c,f.upload]),fe=v.useCallback(async()=>{const te=N.filter(Ce=>Ce.status==="pending"||Ce.status==="error");if(te.length===0)return A.current=C,[];const ge=[],me=new Map,pe=[...te],be=async()=>{for(;pe.length>0&&P.currentze.map(Se=>Se.id===Ce.id?{...Se,status:"uploading",progress:0}:Se));try{const ze=await ve(Ce);if(ze)ge.push(ze),me.set(String(Ce.id),String(ze.hash)),$.current.set(ze.id,ze),S(Se=>Se.filter(Re=>Re.id!==Ce.id)),k(Se=>[...Se,ze]),G(Se=>{if(Se.length===0)return Se;const Re=String(Ce.id);return Se.some(Pe=>String(Pe)===Re)?Se.map(Pe=>String(Pe)===Re?String(ze.hash):Pe):Se}),Ce.preview&&URL.revokeObjectURL(Ce.preview);else{const Se=It("attachment.upload_response_invalid");S(Re=>Re.map(ot=>ot.id===Ce.id?{...ot,status:"error",error:Se}:ot)),p?.(`[${Ce.original_filename}] ${Se}`,Ce.file)}}catch(ze){const Se=ze instanceof Error?ze.message:"업로드 실패";S(Re=>Re.map(ot=>ot.id===Ce.id?{...ot,status:"error",error:Se}:ot)),p?.(`[${Ce.original_filename}] ${Se}`,Ce.file)}finally{P.current--}}},Ue=Array(Math.min(i,te.length)).fill(null).map(()=>be());if(await Promise.all(Ue),ge.length>0){m?.(ge);const Ce=[...C,...ge];if(B.length>0){const ze=B.map(ot=>me.get(String(ot))??ot),Se=new Map(ze.map((ot,Pe)=>[String(ot),Pe])),Re=[...Ce].sort((ot,Pe)=>{const Te=Se.get(String(ot.hash))??Ce.length,Ve=Se.get(String(Pe.hash))??Ce.length;return Te-Ve}).map((ot,Pe)=>({...ot,sort_order:Pe,order:Pe+1}));A.current=Re,f.reorder&&b&&b(Re)}else A.current=Ce.map((ze,Se)=>({...ze,sort_order:Se,order:Se+1}))}else A.current=C;return ge},[N,i,ve,m,p,B,C,b,f.reorder]);v.useEffect(()=>{if(!_)return;const te=Cr.componentEvent.on(_,async()=>{const ge=await fe(),me=A.current??[...C,...ge];return{uploadedAttachments:ge,existingFiles:C,allFiles:me}});return()=>{te()}},[_,fe,C]),v.useEffect(()=>{d&&N.some(te=>te.status==="pending")&&fe()},[d,N,fe]);const ye=v.useCallback(async te=>{W(!0);try{if(te.id&&f.delete){const me=f.delete.replace(":id",String(te.id));await Cr.api.delete(me)}const ge=te.hash||te.id;te.id&&(L.current.add(te.id),$.current.delete(te.id)),te.hash&&L.current.add(te.hash),k(me=>me.filter(pe=>(pe.hash||pe.id)!==ge)),y?.(te.hash||te.id),O(!1),H(null)}catch{_p.error("첨부파일 삭제 실패")}finally{W(!1)}},[y,f.delete]),we=v.useCallback(async te=>{"hash"in te?x?(H(te),O(!0)):await ye(te):(te.preview&&URL.revokeObjectURL(te.preview),S(ge=>ge.filter(me=>me.id!==te.id)))},[x,ye]),He=v.useCallback(te=>{S(ge=>ge.map(me=>me.id===te.id?{...me,status:"pending",error:void 0}:me))},[]),se=v.useCallback(async te=>{const{arrayMove:ge}=await Promise.resolve().then(()=>LS),{active:me,over:pe}=te;if(!pe||me.id===pe.id)return;const be=[...C,...N];let Ue;if(B.length===0)Ue=be;else{const Te=new Map(B.map((Ve,De)=>[String(Ve),De]));Ue=[...be].sort((Ve,De)=>{const qe="hash"in Ve?String(Ve.hash):String(Ve.id),it="hash"in De?String(De.hash):String(De.id);return(Te.get(qe)??be.length)-(Te.get(it)??be.length)})}const Ce=Ue.findIndex(Te=>"hash"in Te?Te.hash===me.id:Te.id===me.id),ze=Ue.findIndex(Te=>"hash"in Te?Te.hash===pe.id:Te.id===pe.id),Se=ge(Ue,Ce,ze);G(Se.map(Te=>"hash"in Te?Te.hash:Te.id));const Re=Se.filter(Te=>"hash"in Te),ot=Se.filter(Te=>!("hash"in Te));U.current=!0,k(Re),S(ot);const Pe=Re.map((Te,Ve)=>({...Te,sort_order:Ve,order:Ve+1}));if(Re.length>0&&f.reorder){const Te=Re.map((Ve,De)=>({id:Ve.id,order:De+1}));try{const Ve=await Cr.api.patch(f.reorder,{order:Te});Cr.toast.success(Ve.message)}catch(Ve){const De=Ve instanceof Error?Ve.message:It("attachment.reorder_failed");Cr.toast.error(De)}}b?.(Pe)},[C,N,B,f.reorder,b]),Oe=v.useMemo(()=>[...C,...N].filter(te=>"is_image"in te?te.is_image:te.mime_type?.startsWith("image/")??!1),[C,N]),Le=v.useCallback(te=>{const ge=Oe.findIndex(me=>"hash"in te&&"hash"in me?me.hash===te.hash:"id"in te&&"id"in me&&!("hash"in te)&&!("hash"in me)?me.id===te.id:!1);ge!==-1&&(ee.current+=1,Z(ge),X(!0))},[Oe]);v.useEffect(()=>{let te=!1;return(async()=>{const me=C.filter(be=>be.is_image),pe=new Set(me.map(be=>be.id));for(const[be,Ue]of J.current)pe.has(be)||(URL.revokeObjectURL(Ue),J.current.delete(be));for(const be of me){if(te)break;if(!J.current.has(be.id))try{const Ue=await Cr.api.get(be.download_url,{responseType:"blob"});if(!te&&Ue){const Ce=URL.createObjectURL(Ue);J.current.set(be.id,Ce),he(new Map(J.current))}}catch(Ue){_p.error("Failed to load authenticated image:",Ue)}}})(),()=>{te=!0}},[C]),v.useEffect(()=>()=>{J.current.forEach(te=>URL.revokeObjectURL(te)),J.current.clear()},[]);const Ie=v.useCallback(async te=>{const{executeImageDownload:ge}=await Promise.resolve().then(()=>J2);await ge({src:te.download_url,downloadUrl:te.download_url,filename:te.original_filename,downloadRequiresAuth:!0})},[]),Be=v.useMemo(()=>{const te=[...C,...N];if(B.length===0)return te;const ge=new Map(B.map((me,pe)=>[String(me),pe]));return[...te].sort((me,pe)=>{const be="hash"in me?String(me.hash):String(me.id),Ue="hash"in pe?String(pe.hash):String(pe.id),Ce=ge.get(be)??te.length,ze=ge.get(Ue)??te.length;return Ce-ze})},[C,N,B]),st=Be.length,ut=st>0,je=stS([]),[]),Ye=v.useCallback(()=>N,[N]);return{existingFiles:C,pendingFiles:N,isDragOver:w,setIsDragOver:M,isDeleting:j,totalCount:st,hasFiles:ut,canAddMore:je,allItems:Be,imageFiles:Oe,handleFiles:ae,handleRemove:we,handleRetry:He,handleUploadAll:fe,handleOpenGallery:Le,handleDownload:Ie,handleDragEnd:se,inputRef:T,galleryOpen:V,setGalleryOpen:X,galleryStartIndex:K,galleryKeyRef:ee,confirmDialogOpen:D,setConfirmDialogOpen:O,itemToDelete:z,executeRemoveAttachment:ye,authenticatedImageUrls:ne,clear:nt,getPendingFiles:Ye}}const hE=({isDragOver:t,canAddMore:e,onFiles:n,setIsDragOver:r,inputRef:s,accept:o,maxFiles:i,maxSize:a,disabled:l,children:c})=>{const d=e&&!l;return h.jsxs(I,{className:` +`;let Hc;function ME(e,t){return new Promise(((n,r)=>{Hc||(Hc=(function(i){const a=[];return a.push(i),URL.createObjectURL(new Blob(a))})(EE));const s=new Worker(Hc);s.addEventListener("message",(function(i){if(t.signal&&t.signal.aborted)s.terminate();else if(i.data.progress===void 0){if(i.data.error)return r(new Error(i.data.error)),void s.terminate();n(i.data.file),s.terminate()}else t.onProgress(i.data.progress)})),s.addEventListener("error",r),t.signal&&t.signal.addEventListener("abort",(()=>{r(t.signal.reason),s.terminate()})),s.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}})}))}function jt(e,t){return new Promise((function(n,r){let s,o,i,a,l,c;if(s={...t},i=0,{onProgress:a}=s,s.maxSizeMB=s.maxSizeMB||Number.POSITIVE_INFINITY,l=typeof s.useWebWorker!="boolean"||s.useWebWorker,delete s.useWebWorker,s.onProgress=g=>{i=g,typeof a=="function"&&a(i)},!(e instanceof Blob||e instanceof _E))return r(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return r(new Error("The file given is not an image"));if(c=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,!l||typeof Worker!="function"||c)return $p(e,s).then(function(g){try{return o=g,f.call(this)}catch(m){return r(m)}}.bind(this),r);var d=function(){try{return f.call(this)}catch(g){return r(g)}}.bind(this),u=function(g){try{return $p(e,s).then((function(m){try{return o=m,d()}catch(p){return r(p)}}),r)}catch(m){return r(m)}};try{return s.libURL=s.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",ME(e,s).then((function(g){try{return o=g,d()}catch{return u()}}),u)}catch{u()}function f(){try{o.name=e.name,o.lastModified=e.lastModified}catch{}try{s.preserveExif&&e.type==="image/jpeg"&&(!s.fileType||s.fileType&&s.fileType===e.type)&&(o=_p(e,o))}catch{}return n(o)}}))}jt.getDataUrlFromFile=Tp,jt.getFilefromDataUrl=Vc,jt.loadImage=Op,jt.drawImageInCanvas=Dp,jt.drawFileInCanvas=ta,jt.canvasToFile=na,jt.getExifOrientation=Ip,jt.handleMaxWidthOrHeight=Lp,jt.followExifOrientation=Pp,jt.cleanupCanvasMemory=Bn,jt.isAutoOrientationInBrowser=ws,jt.approximateBelowMaximumCanvasSizeOfBrowser=Ap,jt.copyExifWithoutOrientation=_p,jt.getBrowserName=Ar,jt.version="2.0.2";const NE=window.G7Core,Uc=e=>{const t=["B","KB","MB","GB"];let n=0,r=e;for(;r>=1024&&n{if(e&&typeof e=="object"){const t=e;if(t.response?.data?.message)return t.response.data.message;if(t.response?.data?.errors){const n=Object.values(t.response.data.errors)[0];if(Array.isArray(n)&&n[0])return n[0]}if(t.message)return t.message}return e instanceof Error?e.message:"업로드 실패"},OE=e=>e?e.startsWith("image/")?"fa-file-image":e.startsWith("video/")?"fa-file-video":e.startsWith("audio/")?"fa-file-audio":e==="application/pdf"?"fa-file-pdf":e.includes("word")||e.includes("document")?"fa-file-word":e.includes("excel")||e.includes("spreadsheet")?"fa-file-excel":e.includes("zip")||e.includes("compressed")?"fa-file-zipper":"fa-file":"fa-file",Lt=(e,t)=>NE?.t?.(e,t)??e,Fp=window.G7Core?.createLogger?.("Comp:FileUploader")??{log:(...e)=>console.log("[Comp:FileUploader]",...e),warn:(...e)=>console.warn("[Comp:FileUploader]",...e),error:(...e)=>console.error("[Comp:FileUploader]",...e)},Dr=window.G7Core;function AE(e){const{attachmentableType:t,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:u,endpoints:f,onFilesChange:g,onUploadComplete:m,onUploadError:p,onRemove:y,onReorder:b,uploadTriggerEvent:_,confirmBeforeRemove:x,uploadParams:M}=e,[S,k]=v.useState(u),[N,C]=v.useState([]),[w,E]=v.useState(!1),A=v.useRef(null),P=v.useRef(0),L=v.useRef(new Set),$=v.useRef(new Map),D=v.useRef(null),[O,T]=v.useState(!1),[z,U]=v.useState(null),[W,G]=v.useState(!1),B=v.useRef(!1),[j,Y]=v.useState([]),[V,X]=v.useState(!1),[K,q]=v.useState(0),ne=v.useRef(0),[ee,fe]=v.useState(new Map),te=v.useRef(new Map),le=v.useRef(g);le.current=g,v.useEffect(()=>{le.current?.(N)},[N]),v.useEffect(()=>{if(B.current)return;const re=u.filter(ve=>!L.current.has(ve.id)&&(!ve.hash||!L.current.has(ve.hash))),de=new Set(re.map(ve=>ve.id)),xe=Array.from($.current.values()).filter(ve=>!de.has(ve.id)&&!L.current.has(ve.id));k([...re,...xe])},[u]);const ge=v.useCallback(async re=>{const de={maxSizeMB:l?.maxSizeMB??1,maxWidthOrHeight:l?.maxWidthOrHeight??1920,useWebWorker:!0};return await jt(re,de)},[l]),ce=v.useCallback(async re=>{const de=S.length+N.length,xe=Math.max(0,s-de),ve=Array.from(re).slice(0,xe);if(re.length>xe){const we=Array.from(re).slice(xe),Ve=Lt("attachment.upload_limit_exceeded",{max:s});p?.(Ve,we[0]??re[0])}for(const we of ve){if(we.size>o*1024*1024){p?.(`[${we.name}] 파일 크기(${Uc(we.size)})가 최대 허용 크기(${o}MB)를 초과합니다.`,we);continue}if(a){const De="."+we.name.split(".").pop()?.toLowerCase();if(!a.split(",").map(Te=>Te.trim().toLowerCase()).includes(De)){p?.(`[${we.name}] 허용되지 않는 파일 형식입니다. (허용: ${a})`,we);continue}}const Ve={id:`pending-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,file:we,original_filename:we.name,mime_type:we.type,size:we.size,size_formatted:Uc(we.size),status:"pending",progress:0};if(we.type.startsWith("image/")&&(Ve.preview=URL.createObjectURL(we)),C(De=>[...De,Ve]),we.type.startsWith("image/")&&l){C(De=>De.map(Ne=>Ne.id===Ve.id?{...Ne,status:"compressing"}:Ne));try{const De=await ge(we);C(Ne=>Ne.map(Te=>Te.id===Ve.id?{...Te,compressedBlob:De,size:De.size,size_formatted:Uc(De.size),status:"pending",progress:0}:Te))}catch{C(De=>De.map(Ne=>Ne.id===Ve.id?{...Ne,status:"error",error:"이미지 압축 실패"}:Ne))}}}},[S.length,N.length,s,o,a,l,ge,p]),ye=v.useCallback(async re=>{const de=new FormData,xe=re.compressedBlob?new File([re.compressedBlob],re.original_filename,{type:re.mime_type}):re.file;de.append("file",xe),t&&de.append("attachmentable_type",t),n&&de.append("attachmentable_id",String(n)),de.append("collection",r),c.forEach(ve=>{de.append("role_ids[]",String(ve))}),M&&Object.entries(M).forEach(([ve,we])=>{de.append(ve,we)});try{const ve=await Dr.api.post(f.upload,de,{headers:{"Content-Type":"multipart/form-data"},onUploadProgress:Ve=>{const De=Math.round(Ve.loaded*100/(Ve.total??1));C(Ne=>Ne.map(Te=>Te.id===re.id?{...Te,progress:De}:Te))}});return ve?.data?.data??ve?.data}catch(ve){const we=TE(ve);throw new Error(we)}},[t,n,r,c,f.upload]),me=v.useCallback(async()=>{const re=N.filter(De=>De.status==="pending"||De.status==="error");if(re.length===0)return D.current=S,[];const de=[],xe=new Map,ve=[...re],we=async()=>{for(;ve.length>0&&P.currentNe.map(Te=>Te.id===De.id?{...Te,status:"uploading",progress:0}:Te));try{const Ne=await ye(De);if(Ne)de.push(Ne),xe.set(String(De.id),String(Ne.hash)),$.current.set(Ne.id,Ne),C(Te=>Te.filter(He=>He.id!==De.id)),k(Te=>[...Te,Ne]),Y(Te=>{if(Te.length===0)return Te;const He=String(De.id);return Te.some(Le=>String(Le)===He)?Te.map(Le=>String(Le)===He?String(Ne.hash):Le):Te}),De.preview&&URL.revokeObjectURL(De.preview);else{const Te=Lt("attachment.upload_response_invalid");C(He=>He.map(Ke=>Ke.id===De.id?{...Ke,status:"error",error:Te}:Ke)),p?.(`[${De.original_filename}] ${Te}`,De.file)}}catch(Ne){const Te=Ne instanceof Error?Ne.message:"업로드 실패";C(He=>He.map(Ke=>Ke.id===De.id?{...Ke,status:"error",error:Te}:Ke)),p?.(`[${De.original_filename}] ${Te}`,De.file)}finally{P.current--}}},Ve=Array(Math.min(i,re.length)).fill(null).map(()=>we());if(await Promise.all(Ve),de.length>0){m?.(de);const De=[...S,...de];if(j.length>0){const Ne=j.map(Ke=>xe.get(String(Ke))??Ke),Te=new Map(Ne.map((Ke,Le)=>[String(Ke),Le])),He=[...De].sort((Ke,Le)=>{const Ee=Te.get(String(Ke.hash))??De.length,ze=Te.get(String(Le.hash))??De.length;return Ee-ze}).map((Ke,Le)=>({...Ke,sort_order:Le,order:Le+1}));D.current=He,f.reorder&&b&&b(He)}else D.current=De.map((Ne,Te)=>({...Ne,sort_order:Te,order:Te+1}))}else D.current=S;return de},[N,i,ye,m,p,j,S,b,f.reorder]);v.useEffect(()=>{if(!_)return;const re=Dr.componentEvent.on(_,async()=>{const de=await me(),xe=D.current??[...S,...de];return{uploadedAttachments:de,existingFiles:S,allFiles:xe}});return()=>{re()}},[_,me,S]),v.useEffect(()=>{d&&N.some(re=>re.status==="pending")&&me()},[d,N,me]);const pe=v.useCallback(async re=>{G(!0);try{if(re.id&&f.delete){const xe=f.delete.replace(":id",String(re.id));await Dr.api.delete(xe)}const de=re.hash||re.id;re.id&&(L.current.add(re.id),$.current.delete(re.id)),re.hash&&L.current.add(re.hash),k(xe=>xe.filter(ve=>(ve.hash||ve.id)!==de)),y?.(re.hash||re.id),T(!1),U(null)}catch{Fp.error("첨부파일 삭제 실패")}finally{G(!1)}},[y,f.delete]),ke=v.useCallback(async re=>{"hash"in re?x?(U(re),T(!0)):await pe(re):(re.preview&&URL.revokeObjectURL(re.preview),C(de=>de.filter(xe=>xe.id!==re.id)))},[x,pe]),$e=v.useCallback(re=>{C(de=>de.map(xe=>xe.id===re.id?{...xe,status:"pending",error:void 0}:xe))},[]),ie=v.useCallback(async re=>{const{arrayMove:de}=await Promise.resolve().then(()=>RS),{active:xe,over:ve}=re;if(!ve||xe.id===ve.id)return;const we=[...S,...N];let Ve;if(j.length===0)Ve=we;else{const Ee=new Map(j.map((ze,rt)=>[String(ze),rt]));Ve=[...we].sort((ze,rt)=>{const it="hash"in ze?String(ze.hash):String(ze.id),Ye="hash"in rt?String(rt.hash):String(rt.id);return(Ee.get(it)??we.length)-(Ee.get(Ye)??we.length)})}const De=Ve.findIndex(Ee=>"hash"in Ee?Ee.hash===xe.id:Ee.id===xe.id),Ne=Ve.findIndex(Ee=>"hash"in Ee?Ee.hash===ve.id:Ee.id===ve.id),Te=de(Ve,De,Ne);Y(Te.map(Ee=>"hash"in Ee?Ee.hash:Ee.id));const He=Te.filter(Ee=>"hash"in Ee),Ke=Te.filter(Ee=>!("hash"in Ee));B.current=!0,k(He),C(Ke);const Le=He.map((Ee,ze)=>({...Ee,sort_order:ze,order:ze+1}));if(He.length>0&&f.reorder){const Ee=He.map((ze,rt)=>({id:ze.id,order:rt+1}));try{const ze=await Dr.api.patch(f.reorder,{order:Ee});Dr.toast.success(ze.message)}catch(ze){const rt=ze instanceof Error?ze.message:Lt("attachment.reorder_failed");Dr.toast.error(rt)}}b?.(Le)},[S,N,j,f.reorder,b]),_e=v.useMemo(()=>[...S,...N].filter(re=>"is_image"in re?re.is_image:re.mime_type?.startsWith("image/")??!1),[S,N]),Ae=v.useCallback(re=>{const de=_e.findIndex(xe=>"hash"in re&&"hash"in xe?xe.hash===re.hash:"id"in re&&"id"in xe&&!("hash"in re)&&!("hash"in xe)?xe.id===re.id:!1);de!==-1&&(ne.current+=1,q(de),X(!0))},[_e]);v.useEffect(()=>{let re=!1;return(async()=>{const xe=S.filter(we=>we.is_image),ve=new Set(xe.map(we=>we.id));for(const[we,Ve]of te.current)ve.has(we)||(URL.revokeObjectURL(Ve),te.current.delete(we));for(const we of xe){if(re)break;if(!te.current.has(we.id))try{const Ve=await Dr.api.get(we.download_url,{responseType:"blob"});if(!re&&Ve){const De=URL.createObjectURL(Ve);te.current.set(we.id,De),fe(new Map(te.current))}}catch(Ve){Fp.error("Failed to load authenticated image:",Ve)}}})(),()=>{re=!0}},[S]),v.useEffect(()=>()=>{te.current.forEach(re=>URL.revokeObjectURL(re)),te.current.clear()},[]);const Ie=v.useCallback(async re=>{const{executeImageDownload:de}=await Promise.resolve().then(()=>bE);await de({src:re.download_url,downloadUrl:re.download_url,filename:re.original_filename,downloadRequiresAuth:!0})},[]),Be=v.useMemo(()=>{const re=[...S,...N];if(j.length===0)return re;const de=new Map(j.map((xe,ve)=>[String(xe),ve]));return[...re].sort((xe,ve)=>{const we="hash"in xe?String(xe.hash):String(xe.id),Ve="hash"in ve?String(ve.hash):String(ve.id),De=de.get(we)??re.length,Ne=de.get(Ve)??re.length;return De-Ne})},[S,N,j]),gt=Be.length,mt=gt>0,Ge=gtC([]),[]),Fe=v.useCallback(()=>N,[N]);return{existingFiles:S,pendingFiles:N,isDragOver:w,setIsDragOver:E,isDeleting:W,totalCount:gt,hasFiles:mt,canAddMore:Ge,allItems:Be,imageFiles:_e,handleFiles:ce,handleRemove:ke,handleRetry:$e,handleUploadAll:me,handleOpenGallery:Ae,handleDownload:Ie,handleDragEnd:ie,inputRef:A,galleryOpen:V,setGalleryOpen:X,galleryStartIndex:K,galleryKeyRef:ne,confirmDialogOpen:O,setConfirmDialogOpen:T,itemToDelete:z,executeRemoveAttachment:pe,authenticatedImageUrls:ee,clear:ot,getPendingFiles:Fe}}const DE=({isDragOver:e,canAddMore:t,onFiles:n,setIsDragOver:r,inputRef:s,accept:o,maxFiles:i,maxSize:a,disabled:l,children:c})=>{const d=t&&!l;return h.jsxs(I,{className:` relative border-2 border-dashed rounded-lg transition-colors ${l?"opacity-60 cursor-not-allowed":""} - ${t&&!l?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600"} - `,onDragOver:u=>{u.preventDefault(),d&&r(!0)},onDragLeave:()=>r(!1),onDrop:u=>{u.preventDefault(),r(!1),d&&n(u.dataTransfer.files)},children:[h.jsx("input",{ref:s,type:"file",multiple:!0,accept:o,onChange:u=>u.target.files&&!l&&n(u.target.files),className:"hidden",disabled:l}),!c&&h.jsxs(I,{className:`p-8 text-center transition-colors ${l?"cursor-not-allowed":"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"}`,onClick:()=>!l&&s.current?.click(),children:[h.jsx(mt,{className:"fa-solid fa-cloud-arrow-up text-4xl text-gray-400 dark:text-gray-500 mb-4"}),h.jsx(yt,{className:"text-sm text-gray-600 dark:text-gray-400",children:It("attachment.drop_or_click")}),h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-500 mt-2",children:[It("attachment.upload_limit",{maxFiles:i,maxSize:a}),o&&` (${o})`]})]}),c]})},gE=window.G7Core,pE=window.G7Core?.createLogger?.("Comp:SortableThumbnail")??{log:(...t)=>console.log("[Comp:SortableThumbnail]",...t),warn:(...t)=>console.warn("[Comp:SortableThumbnail]",...t),error:(...t)=>console.error("[Comp:SortableThumbnail]",...t)},mE=({file:t,onRemove:e,onRetry:n,onImageClick:r,onDownload:s,isPrimary:o,onPrimaryClick:i,disabled:a})=>{const{attributes:l,listeners:c,setNodeRef:d,transform:u,transition:f,isDragging:g}=Ii({id:"hash"in t?t.hash:t.id}),m={transform:Pn.Transform.toString(u),transition:f,opacity:g?.5:1},p="status"in t&&t.status!==void 0,y=p?t.status:"done",b=y==="uploading",_=y==="compressing",x=y==="error",E=p?t.progress:100,C=p?t.preview:void 0,k=p?t.mime_type?.startsWith("image/")??!1:t.is_image,[N,S]=v.useState(void 0),w=!p&&k?t.download_url:void 0;v.useEffect(()=>{if(!w){S(void 0);return}let T=!0,P;return(async()=>{try{const $=await gE.api.get(w,{responseType:"blob"});T&&$&&(P=URL.createObjectURL($),S(P))}catch($){pE.error("Failed to load authenticated image:",$)}})(),()=>{T=!1,P&&URL.revokeObjectURL(P)}},[w]);const M=N;return h.jsxs(I,{ref:d,style:m,className:"relative group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden",children:[h.jsxs(I,{className:`aspect-square w-full flex items-center justify-center bg-gray-50 dark:bg-gray-900 relative ${k||!p?"cursor-pointer":""}`,onClick:()=>{b||_||(k&&r?r():!p&&s&&s())},children:[C||M?h.jsx(yn,{src:C||M,alt:t.original_filename,className:"w-full h-full object-cover"}):h.jsx(mt,{className:`fa-solid ${uE(t.mime_type)} text-4xl text-gray-400 dark:text-gray-500`}),(b||_)&&h.jsxs(I,{className:"absolute inset-0 bg-black/50 flex flex-col items-center justify-center",children:[h.jsx(I,{className:"w-3/4 h-2 bg-gray-700 rounded-full overflow-hidden",children:h.jsx(I,{className:"h-full bg-blue-500 transition-all",style:{width:`${E}%`}})}),h.jsxs(Q,{className:"text-white text-xs mt-1",children:[E,"%"]})]}),x&&h.jsx(I,{className:"absolute inset-0 bg-red-500/50 flex items-center justify-center",children:h.jsx(mt,{className:"fa-solid fa-exclamation-triangle text-2xl text-white"})}),i&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),i()},className:`absolute top-1.5 left-1.5 z-10 p-1 rounded-full transition-colors ${o?"bg-yellow-500 text-white":"bg-gray-800/50 text-gray-300 hover:bg-yellow-500 hover:text-white opacity-0 group-hover:opacity-100"}`,title:It(o?"attachment.primary_image":"attachment.set_as_primary"),children:h.jsx(mt,{className:"fa-solid fa-star text-xs"})}),h.jsxs(I,{className:`absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-start justify-end p-1 gap-1 opacity-0 group-hover:opacity-100 ${a?"hidden":""}`,children:[h.jsx(oe,{type:"button",...l,...c,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-gray-600 dark:text-gray-300 hover:bg-white cursor-grab active:cursor-grabbing",title:It("attachment.drag_to_reorder"),onClick:T=>T.stopPropagation(),children:h.jsx(mt,{className:"fa-solid fa-grip-vertical text-sm"})}),!p&&s&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),s()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-blue-500 hover:bg-white",title:It("common.download"),children:h.jsx(mt,{className:"fa-solid fa-download text-sm"})}),x&&n&&h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),n()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-orange-500 hover:bg-white",title:It("common.retry"),children:h.jsx(mt,{className:"fa-solid fa-arrow-rotate-right text-sm"})}),h.jsx(oe,{type:"button",onClick:T=>{T.stopPropagation(),e()},disabled:b||_,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-red-500 hover:bg-white disabled:opacity-50",title:It("common.delete"),children:h.jsx(mt,{className:"fa-solid fa-times text-sm"})})]})]}),h.jsxs(I,{className:`p-2 ${!p&&s?"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700":""}`,onClick:()=>{!p&&s&&s()},children:[h.jsx(yt,{className:"text-xs font-medium text-gray-900 dark:text-white truncate",title:t.original_filename,children:t.original_filename}),h.jsx(yt,{className:"text-xs text-gray-500 dark:text-gray-400",children:t.size_formatted})]})]})},bE=({allItems:t,canAddMore:e,totalCount:n,maxFiles:r,maxSize:s,accept:o,onRemove:i,onRetry:a,onImageClick:l,onDownload:c,onDragEnd:d,onAddClick:u,enablePrimarySelection:f,primaryFileId:g,onPrimaryChange:m,disabled:p})=>{const y=jl(Zr(Xs,{activationConstraint:{distance:8}}),Zr(Ks,{coordinateGetter:Li}));return h.jsxs(I,{className:"p-3",children:[h.jsx(ec,{sensors:y,collisionDetection:Wl,onDragEnd:d,children:h.jsx(Di,{items:t.map(b=>"hash"in b?b.hash:b.id),strategy:Ti,children:h.jsxs(I,{className:"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3",children:[t.map(b=>{const _="hash"in b,x=_?b.is_image:b.mime_type?.startsWith("image/")??!1;return h.jsx(mE,{file:b,onRemove:()=>i(b),onRetry:"status"in b&&b.status==="error"?()=>a(b):void 0,onImageClick:x?()=>l(b):void 0,onDownload:_?()=>c(b):void 0,isPrimary:f&&_&&(b.hash||b.id)===g,onPrimaryClick:f&&_?()=>m?.(b.hash||b.id):void 0,disabled:p},_?b.hash:b.id)}),e&&!p&&h.jsxs(I,{className:"aspect-square flex flex-col items-center justify-center border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer hover:border-gray-400 dark:hover:border-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors",onClick:u,children:[h.jsx(mt,{className:"fa-solid fa-plus text-2xl text-gray-400 dark:text-gray-500 mb-1"}),h.jsx(Q,{className:"text-xs text-gray-500 dark:text-gray-400",children:It("common.add")})]})]})})}),h.jsxs(I,{className:"mt-3 pt-3 border-t border-gray-200 dark:border-gray-700 flex justify-between items-center text-xs text-gray-500 dark:text-gray-400",children:[h.jsx(Q,{children:It("attachment.attached_count",{count:n,max:r})}),h.jsxs(Q,{children:[It("attachment.max_size",{size:s}),o&&` (${o})`]})]})]})},Ep=v.forwardRef(({uploadTriggerEvent:t,attachmentableType:e,attachmentableId:n,collection:r="default",maxFiles:s=10,maxSize:o=10,maxConcurrentUploads:i=3,accept:a,imageCompression:l,roleIds:c=[],autoUpload:d=!0,onFilesChange:u,onUploadComplete:f,onUploadError:g,onRemove:m,onReorder:p,initialFiles:y=[],disabled:b=!1,className:_="",confirmBeforeRemove:x=!1,confirmRemoveTitle:E,confirmRemoveMessage:C,apiEndpoints:k,uploadParams:N,enablePrimarySelection:S=!1,primaryFileId:w,onPrimaryChange:M,id:T,editorAttrs:P},L)=>{const $={upload:k?.upload??"/api/admin/attachments",delete:k?.delete??"/api/admin/attachments/:id",reorder:k?.reorder??"/api/admin/attachments/reorder"},A=fE({attachmentableType:e,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:y,endpoints:$,onFilesChange:u,onUploadComplete:f,onUploadError:g,onRemove:m,onReorder:p,uploadTriggerEvent:t,confirmBeforeRemove:x,uploadParams:N});v.useImperativeHandle(L,()=>({uploadAll:A.handleUploadAll,clear:A.clear,getPendingFiles:A.getPendingFiles}));const D=v.useMemo(()=>A.imageFiles.map(z=>{const H=!("hash"in z),j=z,W=z;return H?{src:j.preview||"",filename:j.original_filename,title:j.original_filename,downloadRequiresAuth:!1}:{src:A.authenticatedImageUrls.get(W.id)||W.download_url,downloadUrl:W.download_url,filename:W.original_filename,title:W.original_filename,downloadRequiresAuth:!0}}),[A.imageFiles,A.authenticatedImageUrls]),O=v.useMemo(()=>({existingFiles:A.existingFiles,pendingFiles:A.pendingFiles,isDragOver:A.isDragOver,isDeleting:A.isDeleting,maxFiles:s,maxSize:o,accept:a,endpoints:$,confirmBeforeRemove:x,totalCount:A.totalCount,hasFiles:A.hasFiles,canAddMore:A.canAddMore,allItems:A.allItems,imageFiles:A.imageFiles,handleFiles:A.handleFiles,handleRemove:A.handleRemove,handleRetry:A.handleRetry,handleUploadAll:A.handleUploadAll,handleOpenGallery:A.handleOpenGallery,handleDownload:A.handleDownload,handleDragEnd:A.handleDragEnd,setIsDragOver:A.setIsDragOver,inputRef:A.inputRef,galleryOpen:A.galleryOpen,setGalleryOpen:A.setGalleryOpen,galleryStartIndex:A.galleryStartIndex,galleryKeyRef:A.galleryKeyRef,confirmDialogOpen:A.confirmDialogOpen,setConfirmDialogOpen:A.setConfirmDialogOpen,itemToDelete:A.itemToDelete,executeRemoveAttachment:A.executeRemoveAttachment,authenticatedImageUrls:A.authenticatedImageUrls}),[A,s,o,a,$,x]);return h.jsx(eE,{value:O,children:h.jsxs(I,{id:T,className:_,...P,children:[h.jsx(hE,{isDragOver:A.isDragOver,canAddMore:A.canAddMore,onFiles:A.handleFiles,setIsDragOver:A.setIsDragOver,inputRef:A.inputRef,accept:a,maxFiles:s,maxSize:o,disabled:b,children:A.hasFiles&&h.jsx(bE,{allItems:A.allItems,canAddMore:A.canAddMore,totalCount:A.totalCount,maxFiles:s,maxSize:o,accept:a,onRemove:A.handleRemove,onRetry:A.handleRetry,onImageClick:A.handleOpenGallery,onDownload:A.handleDownload,onDragEnd:A.handleDragEnd,onAddClick:()=>A.inputRef.current?.click(),enablePrimarySelection:S,primaryFileId:w,onPrimaryChange:M,disabled:b})}),x&&h.jsx(ru,{isOpen:A.confirmDialogOpen,onClose:()=>{A.isDeleting||A.setConfirmDialogOpen(!1)},title:E??It("attachment.delete_confirm_title"),message:C??It("attachment.delete_confirm_message"),confirmText:It("common.delete"),confirmButtonVariant:"danger",onConfirm:()=>{A.itemToDelete&&A.executeRemoveAttachment(A.itemToDelete)},isLoading:A.isDeleting,loadingText:It("attachment.deleting")}),D.length>0&&A.galleryOpen&&h.jsx(ji,{images:D,isOpen:!0,onClose:()=>A.setGalleryOpen(!1),startIndex:A.galleryStartIndex,showDownload:!0},`gallery-${A.galleryKeyRef.current}`)]})})});Ep.displayName="FileUploader";const yE=({name:t,value:e,options:n,onChange:r,disabled:s=!1,inline:o=!1,className:i="",size:a="md",label:l,error:c,id:d,editorAttrs:u})=>{const[f,g]=v.useState(()=>e??"");v.useEffect(()=>{g(e??"")},[e]);const m=_=>{const x=_.target.value;g(x),r&&!s&&r(_)},y={sm:{radio:"w-3.5 h-3.5",label:"text-xs",gap:"gap-1.5"},md:{radio:"w-4 h-4",label:"text-sm",gap:"gap-2"},lg:{radio:"w-5 h-5",label:"text-base",gap:"gap-2.5"}}[a],b=o?"flex flex-wrap items-center gap-4":"flex flex-col gap-2";return h.jsxs(I,{className:i,id:d,...u,children:[l&&h.jsx(Q,{className:`block mb-2 font-medium text-gray-700 dark:text-gray-300 ${y.label}`,children:l}),h.jsx(I,{className:b,children:n.map(_=>{const x=String(_.value)===String(f),E=s||_.disabled;return h.jsxs(sn,{className:`inline-flex items-center ${y.gap} ${E?"cursor-not-allowed opacity-50":"cursor-pointer"}`,children:[h.jsx("input",{type:"radio",name:t,value:_.value,checked:x,onChange:m,disabled:E,className:` + ${e&&!l?"border-blue-500 bg-blue-50 dark:bg-blue-900/20":"border-gray-300 dark:border-gray-600"} + `,onDragOver:u=>{u.preventDefault(),d&&r(!0)},onDragLeave:()=>r(!1),onDrop:u=>{u.preventDefault(),r(!1),d&&n(u.dataTransfer.files)},children:[h.jsx("input",{ref:s,type:"file",multiple:!0,accept:o,onChange:u=>u.target.files&&!l&&n(u.target.files),className:"hidden",disabled:l}),!c&&h.jsxs(I,{className:`p-8 text-center transition-colors ${l?"cursor-not-allowed":"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50"}`,onClick:()=>!l&&s.current?.click(),children:[h.jsx(pt,{className:"fa-solid fa-cloud-arrow-up text-4xl text-gray-400 dark:text-gray-500 mb-4"}),h.jsx(yt,{className:"text-sm text-gray-600 dark:text-gray-400",children:Lt("attachment.drop_or_click")}),h.jsxs(yt,{className:"text-xs text-gray-500 dark:text-gray-500 mt-2",children:[Lt("attachment.upload_limit",{maxFiles:i,maxSize:a}),o&&` (${o})`]})]}),c]})},IE=window.G7Core,LE=window.G7Core?.createLogger?.("Comp:SortableThumbnail")??{log:(...e)=>console.log("[Comp:SortableThumbnail]",...e),warn:(...e)=>console.warn("[Comp:SortableThumbnail]",...e),error:(...e)=>console.error("[Comp:SortableThumbnail]",...e)},PE=({file:e,onRemove:t,onRetry:n,onImageClick:r,onDownload:s,isPrimary:o,onPrimaryClick:i,disabled:a})=>{const{attributes:l,listeners:c,setNodeRef:d,transform:u,transition:f,isDragging:g}=Hi({id:"hash"in e?e.hash:e.id}),m={transform:Un.Transform.toString(u),transition:f,opacity:g?.5:1},p="status"in e&&e.status!==void 0,y=p?e.status:"done",b=y==="uploading",_=y==="compressing",x=y==="error",M=p?e.progress:100,S=p?e.preview:void 0,k=p?e.mime_type?.startsWith("image/")??!1:e.is_image,[N,C]=v.useState(void 0),w=!p&&k?e.download_url:void 0;v.useEffect(()=>{if(!w){C(void 0);return}let A=!0,P;return(async()=>{try{const $=await IE.api.get(w,{responseType:"blob"});A&&$&&(P=URL.createObjectURL($),C(P))}catch($){LE.error("Failed to load authenticated image:",$)}})(),()=>{A=!1,P&&URL.revokeObjectURL(P)}},[w]);const E=N;return h.jsxs(I,{ref:d,style:m,className:"relative group bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden",children:[h.jsxs(I,{className:`aspect-square w-full flex items-center justify-center bg-gray-50 dark:bg-gray-900 relative ${k||!p?"cursor-pointer":""}`,onClick:()=>{b||_||(k&&r?r():!p&&s&&s())},children:[S||E?h.jsx(Sn,{src:S||E,alt:e.original_filename,className:"w-full h-full object-cover"}):h.jsx(pt,{className:`fa-solid ${OE(e.mime_type)} text-4xl text-gray-400 dark:text-gray-500`}),(b||_)&&h.jsxs(I,{className:"absolute inset-0 bg-black/50 flex flex-col items-center justify-center",children:[h.jsx(I,{className:"w-3/4 h-2 bg-gray-700 rounded-full overflow-hidden",children:h.jsx(I,{className:"h-full bg-blue-500 transition-all",style:{width:`${M}%`}})}),h.jsxs(J,{className:"text-white text-xs mt-1",children:[M,"%"]})]}),x&&h.jsx(I,{className:"absolute inset-0 bg-red-500/50 flex items-center justify-center",children:h.jsx(pt,{className:"fa-solid fa-exclamation-triangle text-2xl text-white"})}),i&&h.jsx(ae,{type:"button",onClick:A=>{A.stopPropagation(),i()},className:`absolute top-1.5 left-1.5 z-10 p-1 rounded-full transition-colors ${o?"bg-yellow-500 text-white":"bg-gray-800/50 text-gray-300 hover:bg-yellow-500 hover:text-white opacity-0 group-hover:opacity-100"}`,title:Lt(o?"attachment.primary_image":"attachment.set_as_primary"),children:h.jsx(pt,{className:"fa-solid fa-star text-xs"})}),h.jsxs(I,{className:`absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-start justify-end p-1 gap-1 opacity-0 group-hover:opacity-100 ${a?"hidden":""}`,children:[h.jsx(ae,{type:"button",...l,...c,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-gray-600 dark:text-gray-300 hover:bg-white cursor-grab active:cursor-grabbing",title:Lt("attachment.drag_to_reorder"),onClick:A=>A.stopPropagation(),children:h.jsx(pt,{className:"fa-solid fa-grip-vertical text-sm"})}),!p&&s&&h.jsx(ae,{type:"button",onClick:A=>{A.stopPropagation(),s()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-blue-500 hover:bg-white",title:Lt("common.download"),children:h.jsx(pt,{className:"fa-solid fa-download text-sm"})}),x&&n&&h.jsx(ae,{type:"button",onClick:A=>{A.stopPropagation(),n()},className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-orange-500 hover:bg-white",title:Lt("common.retry"),children:h.jsx(pt,{className:"fa-solid fa-arrow-rotate-right text-sm"})}),h.jsx(ae,{type:"button",onClick:A=>{A.stopPropagation(),t()},disabled:b||_,className:"p-1.5 bg-white/90 dark:bg-gray-800/90 rounded text-red-500 hover:bg-white disabled:opacity-50",title:Lt("common.delete"),children:h.jsx(pt,{className:"fa-solid fa-times text-sm"})})]})]}),h.jsxs(I,{className:`p-2 ${!p&&s?"cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700":""}`,onClick:()=>{!p&&s&&s()},children:[h.jsx(yt,{className:"text-xs font-medium text-gray-900 dark:text-white truncate",title:e.original_filename,children:e.original_filename}),h.jsx(yt,{className:"text-xs text-gray-500 dark:text-gray-400",children:e.size_formatted})]})]})},$E=({allItems:e,canAddMore:t,totalCount:n,maxFiles:r,maxSize:s,accept:o,onRemove:i,onRetry:a,onImageClick:l,onDownload:c,onDragEnd:d,onAddClick:u,enablePrimarySelection:f,primaryFileId:g,onPrimaryChange:m,disabled:p})=>{const y=Zl(ds(oo,{activationConstraint:{distance:8}}),ds(so,{coordinateGetter:Ui}));return h.jsxs(I,{className:"p-3",children:[h.jsx(ac,{sensors:y,collisionDetection:Ql,onDragEnd:d,children:h.jsx(Vi,{items:e.map(b=>"hash"in b?b.hash:b.id),strategy:Fi,children:h.jsxs(I,{className:"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3",children:[e.map(b=>{const _="hash"in b,x=_?b.is_image:b.mime_type?.startsWith("image/")??!1;return h.jsx(PE,{file:b,onRemove:()=>i(b),onRetry:"status"in b&&b.status==="error"?()=>a(b):void 0,onImageClick:x?()=>l(b):void 0,onDownload:_?()=>c(b):void 0,isPrimary:f&&_&&(b.hash||b.id)===g,onPrimaryClick:f&&_?()=>m?.(b.hash||b.id):void 0,disabled:p},_?b.hash:b.id)}),t&&!p&&h.jsxs(I,{className:"aspect-square flex flex-col items-center justify-center border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer hover:border-gray-400 dark:hover:border-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors",onClick:u,children:[h.jsx(pt,{className:"fa-solid fa-plus text-2xl text-gray-400 dark:text-gray-500 mb-1"}),h.jsx(J,{className:"text-xs text-gray-500 dark:text-gray-400",children:Lt("common.add")})]})]})})}),h.jsxs(I,{className:"mt-3 pt-3 border-t border-gray-200 dark:border-gray-700 flex justify-between items-center text-xs text-gray-500 dark:text-gray-400",children:[h.jsx(J,{children:Lt("attachment.attached_count",{count:n,max:r})}),h.jsxs(J,{children:[Lt("attachment.max_size",{size:s}),o&&` (${o})`]})]})]})},zp=v.forwardRef(({uploadTriggerEvent:e,attachmentableType:t,attachmentableId:n,collection:r="default",maxFiles:s=10,maxSize:o=10,maxConcurrentUploads:i=3,accept:a,imageCompression:l,roleIds:c=[],autoUpload:d=!0,onFilesChange:u,onUploadComplete:f,onUploadError:g,onRemove:m,onReorder:p,initialFiles:y=[],disabled:b=!1,className:_="",confirmBeforeRemove:x=!1,confirmRemoveTitle:M,confirmRemoveMessage:S,apiEndpoints:k,uploadParams:N,enablePrimarySelection:C=!1,primaryFileId:w,onPrimaryChange:E,id:A,editorAttrs:P},L)=>{const $={upload:k?.upload??"/api/admin/attachments",delete:k?.delete??"/api/admin/attachments/:id",reorder:k?.reorder??"/api/admin/attachments/reorder"},D=AE({attachmentableType:t,attachmentableId:n,collection:r,maxFiles:s,maxSize:o,maxConcurrentUploads:i,accept:a,imageCompression:l,roleIds:c,autoUpload:d,initialFiles:y,endpoints:$,onFilesChange:u,onUploadComplete:f,onUploadError:g,onRemove:m,onReorder:p,uploadTriggerEvent:e,confirmBeforeRemove:x,uploadParams:N});v.useImperativeHandle(L,()=>({uploadAll:D.handleUploadAll,clear:D.clear,getPendingFiles:D.getPendingFiles}));const O=v.useMemo(()=>D.imageFiles.map(z=>{const U=!("hash"in z),W=z,G=z;return U?{src:W.preview||"",filename:W.original_filename,title:W.original_filename,downloadRequiresAuth:!1}:{src:D.authenticatedImageUrls.get(G.id)||G.download_url,downloadUrl:G.download_url,filename:G.original_filename,title:G.original_filename,downloadRequiresAuth:!0}}),[D.imageFiles,D.authenticatedImageUrls]),T=v.useMemo(()=>({existingFiles:D.existingFiles,pendingFiles:D.pendingFiles,isDragOver:D.isDragOver,isDeleting:D.isDeleting,maxFiles:s,maxSize:o,accept:a,endpoints:$,confirmBeforeRemove:x,totalCount:D.totalCount,hasFiles:D.hasFiles,canAddMore:D.canAddMore,allItems:D.allItems,imageFiles:D.imageFiles,handleFiles:D.handleFiles,handleRemove:D.handleRemove,handleRetry:D.handleRetry,handleUploadAll:D.handleUploadAll,handleOpenGallery:D.handleOpenGallery,handleDownload:D.handleDownload,handleDragEnd:D.handleDragEnd,setIsDragOver:D.setIsDragOver,inputRef:D.inputRef,galleryOpen:D.galleryOpen,setGalleryOpen:D.setGalleryOpen,galleryStartIndex:D.galleryStartIndex,galleryKeyRef:D.galleryKeyRef,confirmDialogOpen:D.confirmDialogOpen,setConfirmDialogOpen:D.setConfirmDialogOpen,itemToDelete:D.itemToDelete,executeRemoveAttachment:D.executeRemoveAttachment,authenticatedImageUrls:D.authenticatedImageUrls}),[D,s,o,a,$,x]);return h.jsx(vE,{value:T,children:h.jsxs(I,{id:A,className:_,...P,children:[h.jsx(DE,{isDragOver:D.isDragOver,canAddMore:D.canAddMore,onFiles:D.handleFiles,setIsDragOver:D.setIsDragOver,inputRef:D.inputRef,accept:a,maxFiles:s,maxSize:o,disabled:b,children:D.hasFiles&&h.jsx($E,{allItems:D.allItems,canAddMore:D.canAddMore,totalCount:D.totalCount,maxFiles:s,maxSize:o,accept:a,onRemove:D.handleRemove,onRetry:D.handleRetry,onImageClick:D.handleOpenGallery,onDownload:D.handleDownload,onDragEnd:D.handleDragEnd,onAddClick:()=>D.inputRef.current?.click(),enablePrimarySelection:C,primaryFileId:w,onPrimaryChange:E,disabled:b})}),x&&h.jsx(pu,{isOpen:D.confirmDialogOpen,onClose:()=>{D.isDeleting||D.setConfirmDialogOpen(!1)},title:M??Lt("attachment.delete_confirm_title"),message:S??Lt("attachment.delete_confirm_message"),confirmText:Lt("common.delete"),confirmButtonVariant:"danger",onConfirm:()=>{D.itemToDelete&&D.executeRemoveAttachment(D.itemToDelete)},isLoading:D.isDeleting,loadingText:Lt("attachment.deleting")}),O.length>0&&D.galleryOpen&&h.jsx(Zi,{images:O,isOpen:!0,onClose:()=>D.setGalleryOpen(!1),startIndex:D.galleryStartIndex,showDownload:!0},`gallery-${D.galleryKeyRef.current}`)]})})});zp.displayName="FileUploader";const FE=({name:e,value:t,options:n,onChange:r,disabled:s=!1,inline:o=!1,className:i="",size:a="md",label:l,error:c,id:d,editorAttrs:u})=>{const[f,g]=v.useState(()=>t??"");v.useEffect(()=>{g(t??"")},[t]);const m=_=>{const x=_.target.value;g(x),r&&!s&&r(_)},y={sm:{radio:"w-3.5 h-3.5",label:"text-xs",gap:"gap-1.5"},md:{radio:"w-4 h-4",label:"text-sm",gap:"gap-2"},lg:{radio:"w-5 h-5",label:"text-base",gap:"gap-2.5"}}[a],b=o?"flex flex-wrap items-center gap-4":"flex flex-col gap-2";return h.jsxs(I,{className:i,id:d,...u,children:[l&&h.jsx(J,{className:`block mb-2 font-medium text-gray-700 dark:text-gray-300 ${y.label}`,children:l}),h.jsx(I,{className:b,children:n.map(_=>{const x=String(_.value)===String(f),M=s||_.disabled;return h.jsxs(ln,{className:`inline-flex items-center ${y.gap} ${M?"cursor-not-allowed opacity-50":"cursor-pointer"}`,children:[h.jsx("input",{type:"radio",name:e,value:_.value,checked:x,onChange:m,disabled:M,className:` ${y.radio} text-blue-600 bg-gray-100 dark:bg-gray-700 @@ -232,15 +233,15 @@ self.addEventListener('message', async (e) => { focus:ring-blue-500 dark:focus:ring-blue-600 focus:ring-2 dark:ring-offset-gray-800 - ${E?"cursor-not-allowed":"cursor-pointer"} - `}),h.jsx(Q,{className:` + ${M?"cursor-not-allowed":"cursor-pointer"} + `}),h.jsx(J,{className:` ${y.label} font-medium text-gray-900 dark:text-gray-300 - `,children:_.label})]},_.value)})}),c&&h.jsx(Q,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:c})]})},ls=()=>window.G7Core,Zi=(t,e)=>ls()?.t?.(t,e)??t,vE=()=>ls()?.locale?.supported?.()??["ko","en"],xE=()=>ls()?.locale?.current?.()??"ko",Mp=t=>{const e=Zi(`common.language_${t}`);return e===`common.language_${t}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[t]||t.toUpperCase():e},wE=({content:t="",value:e,onChange:n,multilingual:r=!1,isHtml:s=!1,onIsHtmlChange:o,rows:i=15,placeholder:a="",label:l,showHtmlModeToggle:c=!0,contentClassName:d="",purifyConfig:u,className:f="",name:g="content",htmlFieldName:m="content_mode",readOnly:p=!1,id:y,editorAttrs:b})=>{const _=v.useMemo(()=>vE(),[]),x=v.useMemo(()=>xE(),[]),[E,C]=v.useState(x),[k,N]=v.useState(s),[S,w]=v.useState(t),[M,T]=v.useState(e??{}),[P,L]=v.useState(!1);v.useEffect(()=>{N(s)},[s]),v.useEffect(()=>{r||w(t)},[t,r]),v.useEffect(()=>{r&&e&&T(e)},[e,r]);const $=v.useCallback(W=>{const U=W.target.value;if(w(U),n){const B=ls()?.createChangeEvent?.({value:U,name:g,type:"textarea"})??{target:{name:g,value:U}};n(B)}},[n,g]),A=v.useCallback((W,U)=>{const B={...M,[W]:U};if(T(B),n){const G=ls()?.createChangeEvent?.({value:B,name:g,type:"multilingual"})??{target:{name:g,value:B}};n(G)}},[n,g,M]),D=v.useCallback(W=>{const U=W.target.checked;if(N(U),U||L(!1),o){const B=ls()?.createChangeEvent?.({checked:U,name:m,type:"checkbox"})??{target:{name:m,checked:U}};o(B)}},[o,m]),O=v.useCallback(()=>{L(W=>!W)},[]),z=r?M[E]??"":S,H=v.useCallback(W=>!!M[W]?.trim(),[M]),j=()=>h.jsx(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:_.map(W=>{const U=W===x,B=E===W;return h.jsxs(oe,{type:"button",className:` + `,children:_.label})]},_.value)})}),c&&h.jsx(J,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:c})]})},ks=()=>window.G7Core,ra=(e,t)=>ks()?.t?.(e,t)??e,zE=()=>ks()?.locale?.supported?.()??["ko","en"],VE=()=>ks()?.locale?.current?.()??"ko",Vp=e=>{const t=ra(`common.language_${e}`);return t===`common.language_${e}`?{ko:"한국어",en:"English",ja:"日本語",zh:"中文"}[e]||e.toUpperCase():t},HE=({content:e="",value:t,onChange:n,multilingual:r=!1,isHtml:s=!1,onIsHtmlChange:o,rows:i=15,placeholder:a="",label:l,showHtmlModeToggle:c=!0,contentClassName:d="",purifyConfig:u,className:f="",name:g="content",htmlFieldName:m="content_mode",readOnly:p=!1,id:y,editorAttrs:b})=>{const _=v.useMemo(()=>zE(),[]),x=v.useMemo(()=>VE(),[]),[M,S]=v.useState(x),[k,N]=v.useState(s),[C,w]=v.useState(e),[E,A]=v.useState(t??{}),[P,L]=v.useState(!1);v.useEffect(()=>{N(s)},[s]),v.useEffect(()=>{r||w(e)},[e,r]),v.useEffect(()=>{r&&t&&A(t)},[t,r]);const $=v.useCallback(G=>{const B=G.target.value;if(w(B),n){const j=ks()?.createChangeEvent?.({value:B,name:g,type:"textarea"})??{target:{name:g,value:B}};n(j)}},[n,g]),D=v.useCallback((G,B)=>{const j={...E,[G]:B};if(A(j),n){const Y=ks()?.createChangeEvent?.({value:j,name:g,type:"multilingual"})??{target:{name:g,value:j}};n(Y)}},[n,g,E]),O=v.useCallback(G=>{const B=G.target.checked;if(N(B),B||L(!1),o){const j=ks()?.createChangeEvent?.({checked:B,name:m,type:"checkbox"})??{target:{name:m,checked:B}};o(j)}},[o,m]),T=v.useCallback(()=>{L(G=>!G)},[]),z=r?E[M]??"":C,U=v.useCallback(G=>!!E[G]?.trim(),[E]),W=()=>h.jsx(I,{className:"flex flex-wrap items-center gap-2 mb-3",children:_.map(G=>{const B=G===x,j=M===G;return h.jsxs(ae,{type:"button",className:` inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold transition-all - ${B?U?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":U?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"} - `,onClick:()=>C(W),children:[h.jsx(re,{name:"globe",className:"w-3 h-3"}),h.jsx(Q,{children:Mp(W)}),U&&h.jsx(Q,{className:"text-red-500",children:"*"}),H(W)&&!B&&h.jsx(re,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"})]},W)})});return h.jsxs(I,{className:`space-y-2 ${f}`,id:y,...b,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[l&&h.jsx(sn,{className:"block text-sm font-medium text-gray-500 dark:text-gray-400",children:l}),h.jsxs(I,{className:"flex items-center gap-3",children:[k&&!p&&h.jsx(oe,{type:"button",onClick:O,className:`px-3 py-1.5 text-xs font-bold rounded-lg focus:outline-none focus:ring-2 ${P?"text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-600 border border-gray-300 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-500 focus:ring-gray-400 dark:focus:ring-gray-500":"text-blue-600 dark:text-blue-400 bg-white dark:bg-gray-700 border border-blue-300 dark:border-blue-600 hover:bg-blue-50 dark:hover:bg-gray-600 focus:ring-blue-500 dark:focus:ring-blue-600"}`,children:Zi(P?"common.preview_off":"common.preview")}),c&&h.jsxs(sn,{className:"flex items-center gap-2 p-2 cursor-pointer",children:[h.jsx(kt,{type:"checkbox",name:m,checked:k,onChange:D,disabled:p,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(I,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:Zi("common.html_mode")})]})]})]}),r&&j(),!P&&(r?_.map(W=>h.jsx(I,{className:E===W?"block":"hidden",children:h.jsx(Qn,{name:`${g}[${W}]`,value:M[W]??"",onChange:U=>A(W,U.target.value),placeholder:`${a} (${Mp(W)})`,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})},W)):h.jsx(Qn,{name:g,value:S,onChange:$,placeholder:a,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})),P&&h.jsx(I,{className:"p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 min-h-[400px]",children:h.jsx(Za,{content:z,isHtml:!0,className:d||"prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100",purifyConfig:u})})]})},Np="g7_dismissed_warnings";function Op(){try{const t=sessionStorage.getItem(Np);return t?JSON.parse(t):[]}catch{return[]}}function kE(t){try{const e=Op();e.includes(t)||(e.push(t),sessionStorage.setItem(Np,JSON.stringify(e)))}catch{}}function CE(t){switch(t){case"error":return"error";case"warning":return"warning";default:return"info"}}const SE=({warnings:t,className:e=""})=>{const[n,r]=v.useState([]);v.useEffect(()=>{r(Op())},[]);const s=v.useCallback(i=>{kE(i),r(a=>[...a,i])},[]);if(!t||t.length===0)return null;const o=t.filter(i=>!n.includes(i.id));return o.length===0?null:h.jsx(I,{className:`space-y-2 ${e}`.trim(),children:o.map(i=>h.jsx(cu,{type:CE(i.level),message:i.message,dismissible:!0,onDismiss:()=>s(i.id)},i.id))})},Tp=window.G7Core?.createLogger?.("Comp:FilterVisibility")??{log:(...t)=>console.log("[Comp:FilterVisibility]",...t),warn:(...t)=>console.warn("[Comp:FilterVisibility]",...t),error:(...t)=>console.error("[Comp:FilterVisibility]",...t)},_E=({id:t,visibleFilters:e,defaultFilters:n=[],onFilterVisibilityChange:r})=>{const[s,o]=v.useState(!1),i=v.useRef(!0),a=v.useMemo(()=>{try{const f=window.G7Core?.AuthManager?.getInstance();return f?f.getUser()?.uuid:void 0}catch{return}},[]),l=v.useMemo(()=>`g7_filters_${t}${a!==void 0?`_${a}`:""}`,[t,a]),c=v.useCallback(()=>{try{const f=localStorage.getItem(l);if(f){const g=JSON.parse(f);if(Array.isArray(g))return g}}catch(f){Tp.warn("Failed to load from localStorage:",f)}return null},[l]),d=v.useCallback(f=>{try{localStorage.setItem(l,JSON.stringify(f))}catch(g){Tp.warn("Failed to save to localStorage:",g)}},[l]),u=v.useCallback(()=>{const f=c();return f||(e&&e.length>0?e:n)},[c,e,n]);return v.useEffect(()=>{if(!s){const f=u();r&&f.length>0&&r(f),o(!0)}},[s,r,u]),v.useEffect(()=>{if(s&&e&&e.length>0){if(i.current){i.current=!1;return}d(e)}},[s,e,d]),null},EE=window.G7Core?.createLogger?.("Comp:SlotContainer")??{log:(...t)=>console.log("[Comp:SlotContainer]",...t),warn:(...t)=>console.warn("[Comp:SlotContainer]",...t),error:(...t)=>console.error("[Comp:SlotContainer]",...t)};function ME(t,e){return e&&t&&typeof t.id=="string"&&t.id.length>0?{...t,id:`${t.id}__${e}`}:t}const NE=({slotId:t,className:e="",emptyContent:n,style:r,id:s,children:o})=>{const[i,a]=v.useState([]),[,l]=v.useState({}),c=v.useCallback(()=>{const m=window.__slotContextValue;if(m)return m;const p=window.G7Core;return p&&p.getSlotContext?.()||null},[]),d=v.useCallback(()=>{const m=c();m&&m.isEnabled&&a(m.getSlotComponents(t))},[t,c]);v.useEffect(()=>{const m=c();return!m||!m.isEnabled?void 0:(d(),m.subscribeToSlot(t,()=>{d(),l({})}))},[t,c,d]);const u=v.useCallback(()=>{const m=window.G7Core;return m?m.getDynamicRenderer?.():null},[]);if(i.length===0)return n?h.jsx(I,{className:e,style:r,id:s,children:n}):o?h.jsx(I,{className:e,style:r,id:s,children:o}):null;const f=u(),g=window.G7Core;return!f||!g?(EE.error("G7Core or DynamicRenderer not available"),null):h.jsxs(I,{className:e,style:r,id:s,children:[i.map(m=>{const{componentDef:p,dataContext:y,parentFormContext:b,getParentComponentContext:_,translationContext:x}=m,E=ME(p,s);return h.jsx(f,{componentDef:E,dataContext:y,translationContext:x,registry:g.getComponentRegistry(),bindingEngine:g.getDataBindingEngine(),translationEngine:g.getTranslationEngine(),actionDispatcher:g.getActionDispatcher(),parentComponentContext:_?.(),parentFormContextProp:b,isRootRenderer:!1},E.id||`slot-${t}-${m.order}`)}),o]})},Ap=(t,e)=>window.G7Core?.t?.(t,e)??t,Dp=(t,e,n)=>window.G7Core?.renderComponentLayout?.(t,e,n)??null,OE=({options:t=[],value:e,onChange:n,placeholder:r,disabled:s=!1,className:o="",maxHeight:i="320px",children:a,selectedChildren:l,__componentLayoutDefs:c,id:d,editorAttrs:u})=>{const[f,g]=v.useState(!1),m=v.useRef(null),p=v.useRef(null),y=v.useMemo(()=>e==null?null:t.find(k=>String(k.value)===String(e))||null,[t,e]);v.useEffect(()=>{const k=N=>{m.current&&!m.current.contains(N.target)&&g(!1)};return f&&document.addEventListener("mousedown",k),()=>{document.removeEventListener("mousedown",k)}},[f]),v.useEffect(()=>{const k=N=>{N.key==="Escape"&&f&&(g(!1),p.current?.focus())};return document.addEventListener("keydown",k),()=>document.removeEventListener("keydown",k)},[f]);const b=v.useCallback(()=>{s||g(k=>!k)},[s]),_=v.useCallback(k=>{n&&n({target:{value:k},preventDefault:()=>{},stopPropagation:()=>{},type:"change"}),g(!1),p.current?.focus()},[n]),x=r??Ap("common.select"),E=()=>{if(!y)return h.jsx(Q,{className:"text-gray-400 dark:text-gray-500",children:x});if(c?.selected){const k=Dp(c.selected,{item:y,isSelected:!0},"selected");if(k)return k}return l||h.jsx(Q,{className:"truncate",children:y.label})},C=(k,N)=>h.jsxs(I,{className:"flex items-center justify-between w-full",children:[h.jsx(Q,{className:N?"text-blue-600 dark:text-blue-400 font-medium":"text-gray-900 dark:text-white",children:k.label}),N&&h.jsx(Un,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})]});return h.jsxs(I,{ref:m,className:`relative ${o}`,id:d,...u,children:[h.jsxs(oe,{ref:p,type:"button",onClick:b,disabled:s,className:` + ${j?B?"bg-blue-500 text-white dark:bg-blue-600 dark:text-white shadow-md scale-105":"bg-gray-500 text-white dark:bg-gray-600 dark:text-white shadow-md scale-105":B?"bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/50":"bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"} + `,onClick:()=>S(G),children:[h.jsx(oe,{name:"globe",className:"w-3 h-3"}),h.jsx(J,{children:Vp(G)}),B&&h.jsx(J,{className:"text-red-500",children:"*"}),U(G)&&!j&&h.jsx(oe,{name:"check",className:"w-3 h-3 text-green-500 dark:text-green-400"})]},G)})});return h.jsxs(I,{className:`space-y-2 ${f}`,id:y,...b,children:[h.jsxs(I,{className:"flex items-center justify-between",children:[l&&h.jsx(ln,{className:"block text-sm font-medium text-gray-500 dark:text-gray-400",children:l}),h.jsxs(I,{className:"flex items-center gap-3",children:[k&&!p&&h.jsx(ae,{type:"button",onClick:T,className:`px-3 py-1.5 text-xs font-bold rounded-lg focus:outline-none focus:ring-2 ${P?"text-gray-700 dark:text-gray-200 bg-gray-200 dark:bg-gray-600 border border-gray-300 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-500 focus:ring-gray-400 dark:focus:ring-gray-500":"text-blue-600 dark:text-blue-400 bg-white dark:bg-gray-700 border border-blue-300 dark:border-blue-600 hover:bg-blue-50 dark:hover:bg-gray-600 focus:ring-blue-500 dark:focus:ring-blue-600"}`,children:ra(P?"common.preview_off":"common.preview")}),c&&h.jsxs(ln,{className:"flex items-center gap-2 p-2 cursor-pointer",children:[h.jsx(kt,{type:"checkbox",name:m,checked:k,onChange:O,disabled:p,className:"w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"}),h.jsx(I,{className:"text-sm font-medium text-gray-700 dark:text-gray-300",children:ra("common.html_mode")})]})]})]}),r&&W(),!P&&(r?_.map(G=>h.jsx(I,{className:M===G?"block":"hidden",children:h.jsx(sr,{name:`${g}[${G}]`,value:E[G]??"",onChange:B=>D(G,B.target.value),placeholder:`${a} (${Vp(G)})`,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})},G)):h.jsx(sr,{name:g,value:C,onChange:$,placeholder:a,rows:i,readOnly:p,className:`block w-full rounded-lg border px-3 py-2 text-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-1 disabled:opacity-50 disabled:cursor-not-allowed ${k?"font-mono bg-white dark:bg-gray-800 border-blue-300 dark:border-blue-600 text-gray-800 dark:text-gray-200 focus:border-blue-500 focus:ring-blue-500":"bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white focus:border-blue-500 focus:ring-blue-500"}`})),P&&h.jsx(I,{className:"p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 min-h-[400px]",children:h.jsx(ol,{content:z,isHtml:!0,className:d||"prose dark:prose-invert max-w-none text-gray-900 dark:text-gray-100",purifyConfig:u})})]})},Hp="g7_dismissed_warnings";function Up(){try{const e=sessionStorage.getItem(Hp);return e?JSON.parse(e):[]}catch{return[]}}function UE(e){try{const t=Up();t.includes(e)||(t.push(e),sessionStorage.setItem(Hp,JSON.stringify(t)))}catch{}}function BE(e){switch(e){case"error":return"error";case"warning":return"warning";default:return"info"}}const jE=({warnings:e,className:t=""})=>{const[n,r]=v.useState([]);v.useEffect(()=>{r(Up())},[]);const s=v.useCallback(i=>{UE(i),r(a=>[...a,i])},[]);if(!e||e.length===0)return null;const o=e.filter(i=>!n.includes(i.id));return o.length===0?null:h.jsx(I,{className:`space-y-2 ${t}`.trim(),children:o.map(i=>h.jsx(wu,{type:BE(i.level),message:i.message,dismissible:!0,onDismiss:()=>s(i.id)},i.id))})},Bp=window.G7Core?.createLogger?.("Comp:FilterVisibility")??{log:(...e)=>console.log("[Comp:FilterVisibility]",...e),warn:(...e)=>console.warn("[Comp:FilterVisibility]",...e),error:(...e)=>console.error("[Comp:FilterVisibility]",...e)},WE=({id:e,visibleFilters:t,defaultFilters:n=[],onFilterVisibilityChange:r})=>{const[s,o]=v.useState(!1),i=v.useRef(!0),a=v.useMemo(()=>{try{const f=window.G7Core?.AuthManager?.getInstance();return f?f.getUser()?.uuid:void 0}catch{return}},[]),l=v.useMemo(()=>`g7_filters_${e}${a!==void 0?`_${a}`:""}`,[e,a]),c=v.useCallback(()=>{try{const f=localStorage.getItem(l);if(f){const g=JSON.parse(f);if(Array.isArray(g))return g}}catch(f){Bp.warn("Failed to load from localStorage:",f)}return null},[l]),d=v.useCallback(f=>{try{localStorage.setItem(l,JSON.stringify(f))}catch(g){Bp.warn("Failed to save to localStorage:",g)}},[l]),u=v.useCallback(()=>{const f=c();return f||(t&&t.length>0?t:n)},[c,t,n]);return v.useEffect(()=>{if(!s){const f=u();r&&f.length>0&&r(f),o(!0)}},[s,r,u]),v.useEffect(()=>{if(s&&t&&t.length>0){if(i.current){i.current=!1;return}d(t)}},[s,t,d]),null},GE=window.G7Core?.createLogger?.("Comp:SlotContainer")??{log:(...e)=>console.log("[Comp:SlotContainer]",...e),warn:(...e)=>console.warn("[Comp:SlotContainer]",...e),error:(...e)=>console.error("[Comp:SlotContainer]",...e)};function YE(e,t){return t&&e&&typeof e.id=="string"&&e.id.length>0?{...e,id:`${e.id}__${t}`}:e}const XE=({slotId:e,className:t="",emptyContent:n,style:r,id:s,children:o})=>{const[i,a]=v.useState([]),[,l]=v.useState({}),c=v.useCallback(()=>{const m=window.__slotContextValue;if(m)return m;const p=window.G7Core;return p&&p.getSlotContext?.()||null},[]),d=v.useCallback(()=>{const m=c();m&&m.isEnabled&&a(m.getSlotComponents(e))},[e,c]);v.useEffect(()=>{const m=c();return!m||!m.isEnabled?void 0:(d(),m.subscribeToSlot(e,()=>{d(),l({})}))},[e,c,d]);const u=v.useCallback(()=>{const m=window.G7Core;return m?m.getDynamicRenderer?.():null},[]);if(i.length===0)return n?h.jsx(I,{className:t,style:r,id:s,children:n}):o?h.jsx(I,{className:t,style:r,id:s,children:o}):null;const f=u(),g=window.G7Core;return!f||!g?(GE.error("G7Core or DynamicRenderer not available"),null):h.jsxs(I,{className:t,style:r,id:s,children:[i.map(m=>{const{componentDef:p,dataContext:y,parentFormContext:b,getParentComponentContext:_,translationContext:x}=m,M=YE(p,s);return h.jsx(f,{componentDef:M,dataContext:y,translationContext:x,registry:g.getComponentRegistry(),bindingEngine:g.getDataBindingEngine(),translationEngine:g.getTranslationEngine(),actionDispatcher:g.getActionDispatcher(),parentComponentContext:_?.(),parentFormContextProp:b,isRootRenderer:!1},M.id||`slot-${e}-${m.order}`)}),o]})},jp=(e,t)=>window.G7Core?.t?.(e,t)??e,Wp=(e,t,n)=>window.G7Core?.renderComponentLayout?.(e,t,n)??null,KE=({options:e=[],value:t,onChange:n,placeholder:r,disabled:s=!1,className:o="",maxHeight:i="320px",children:a,selectedChildren:l,__componentLayoutDefs:c,id:d,editorAttrs:u})=>{const[f,g]=v.useState(!1),m=v.useRef(null),p=v.useRef(null),y=v.useMemo(()=>t==null?null:e.find(k=>String(k.value)===String(t))||null,[e,t]);v.useEffect(()=>{const k=N=>{m.current&&!m.current.contains(N.target)&&g(!1)};return f&&document.addEventListener("mousedown",k),()=>{document.removeEventListener("mousedown",k)}},[f]),v.useEffect(()=>{const k=N=>{N.key==="Escape"&&f&&(g(!1),p.current?.focus())};return document.addEventListener("keydown",k),()=>document.removeEventListener("keydown",k)},[f]);const b=v.useCallback(()=>{s||g(k=>!k)},[s]),_=v.useCallback(k=>{n&&n({target:{value:k},preventDefault:()=>{},stopPropagation:()=>{},type:"change"}),g(!1),p.current?.focus()},[n]),x=r??jp("common.select"),M=()=>{if(!y)return h.jsx(J,{className:"text-gray-400 dark:text-gray-500",children:x});if(c?.selected){const k=Wp(c.selected,{item:y,isSelected:!0},"selected");if(k)return k}return l||h.jsx(J,{className:"truncate",children:y.label})},S=(k,N)=>h.jsxs(I,{className:"flex items-center justify-between w-full",children:[h.jsx(J,{className:N?"text-blue-600 dark:text-blue-400 font-medium":"text-gray-900 dark:text-white",children:k.label}),N&&h.jsx(Xn,{className:"w-5 h-5 text-blue-600 dark:text-blue-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M5 13l4 4L19 7"})})]});return h.jsxs(I,{ref:m,className:`relative ${o}`,id:d,...u,children:[h.jsxs(ae,{ref:p,type:"button",onClick:b,disabled:s,className:` w-full px-4 py-2.5 bg-gray-100 dark:bg-gray-700 border-0 rounded-xl @@ -249,13 +250,13 @@ self.addEventListener('message', async (e) => { flex items-center justify-between gap-2 text-left cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed - `,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(I,{className:"flex-1 min-w-0",children:E()}),h.jsx(Un,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&h.jsx(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",role:"listbox",children:h.jsx(I,{className:"overflow-auto",style:{maxHeight:i},children:t.length===0?h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:Ap("common.no_options")}):t.map((k,N)=>{const S=String(k.value)===String(e);return h.jsx(oe,{type:"button",onClick:()=>!k.disabled&&_(k.value),disabled:k.disabled,className:` + `,"aria-haspopup":"listbox","aria-expanded":f,children:[h.jsx(I,{className:"flex-1 min-w-0",children:M()}),h.jsx(Xn,{className:`w-4 h-4 flex-shrink-0 transition-transform duration-200 ${f?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:h.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})]}),f&&h.jsx(I,{className:"absolute z-50 w-full mt-2 bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 overflow-hidden",role:"listbox",children:h.jsx(I,{className:"overflow-auto",style:{maxHeight:i},children:e.length===0?h.jsx(I,{className:"px-4 py-6 text-center text-gray-500 dark:text-gray-400",children:jp("common.no_options")}):e.map((k,N)=>{const C=String(k.value)===String(t);return h.jsx(ae,{type:"button",onClick:()=>!k.disabled&&_(k.value),disabled:k.disabled,className:` w-full px-4 py-3 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors - ${S?"bg-blue-50 dark:bg-blue-900/20":""} + ${C?"bg-blue-50 dark:bg-blue-900/20":""} ${k.disabled?"opacity-50 cursor-not-allowed":"cursor-pointer"} - `,role:"option","aria-selected":S,"data-item-value":k.value,"data-item-index":N,"data-item-selected":S,children:c?.item?Dp(c.item,{item:k,index:N,isSelected:S},`item-${N}`):a||C(k,S)},k.value)})})})]})};function TE(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:null}const AE=({value:t,checked:e=!1,icon:n,label:r,variant:s="blue",customColor:o,className:i="",style:a,onChange:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),g="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium border cursor-pointer transition-all";if(o){const b=TE(o),_=document.documentElement.classList.contains("dark");let x;return e?x={backgroundColor:o,color:"#ffffff",borderColor:o,opacity:u?.85:1}:x=b?{backgroundColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${u?_?.25:.12:_?.15:.06})`,color:o,borderColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${_?.3:.2})`}:{},h.jsxs(sn,{className:`${g} ${i}`,style:{...x,...a},onMouseEnter:()=>f(!0),onMouseLeave:()=>f(!1),id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:t,checked:e,onChange:E=>{l?.(E)},className:"hidden"}),n&&h.jsx(re,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})}const y=e?{green:"bg-green-600 dark:bg-green-600 text-white dark:text-white border-green-700 dark:border-green-500 hover:bg-green-700 dark:hover:bg-green-500",orange:"bg-orange-600 dark:bg-orange-600 text-white dark:text-white border-orange-700 dark:border-orange-500 hover:bg-orange-700 dark:hover:bg-orange-500",yellow:"bg-yellow-600 dark:bg-yellow-600 text-white dark:text-white border-yellow-700 dark:border-yellow-500 hover:bg-yellow-700 dark:hover:bg-yellow-500",gray:"bg-gray-600 dark:bg-gray-500 text-white dark:text-white border-gray-700 dark:border-gray-400 hover:bg-gray-700 dark:hover:bg-gray-400",blue:"bg-blue-600 dark:bg-blue-600 text-white dark:text-white border-blue-700 dark:border-blue-500 hover:bg-blue-700 dark:hover:bg-blue-500"}[s]:{green:"bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800 hover:bg-green-100 dark:hover:bg-green-900/30",orange:"bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800 hover:bg-orange-100 dark:hover:bg-orange-900/30",yellow:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800 hover:bg-yellow-100 dark:hover:bg-yellow-900/30",gray:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-600",blue:"bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800 hover:bg-blue-100 dark:hover:bg-blue-900/30"}[s];return h.jsxs(sn,{className:`${g} ${y} ${i}`,style:a,id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:t,checked:e,onChange:b=>{l?.(b)},className:"hidden"}),n&&h.jsx(re,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})},DE=({label:t,icon:e,iconPosition:n="left",position:r="right",className:s="",style:o,children:i})=>{const[a,l]=v.useState(!1),[c,d]=v.useState({top:0,left:0}),u=v.useRef(null),f=v.useRef(null),g=v.useCallback(()=>{if(!u.current)return;const p=u.current.getBoundingClientRect(),y=224;let b=r==="right"?p.right-y:p.left;const _=p.bottom+8;b<8&&(b=8),b+y>window.innerWidth-8&&(b=window.innerWidth-y-8),d({top:_,left:b})},[r]);v.useEffect(()=>{a&&g()},[a,g]),v.useEffect(()=>{if(!a)return;const p=()=>{l(!1)};return window.addEventListener("scroll",p,!0),window.addEventListener("resize",p),()=>{window.removeEventListener("scroll",p,!0),window.removeEventListener("resize",p)}},[a]),v.useEffect(()=>{const p=y=>{const b=y.target,_=u.current?.contains(b),x=f.current?.contains(b);!_&&!x&&l(!1)};return a&&document.addEventListener("mousedown",p),()=>{document.removeEventListener("mousedown",p)}},[a]);const m=a?Tn.createPortal(h.jsx("div",{ref:f,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:c.top,left:c.left},onClick:()=>l(!1),children:i}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,children:[h.jsxs(oe,{ref:u,onClick:()=>l(!a),className:"flex items-center gap-2",children:[n==="left"&&e&&h.jsx(re,{name:e,className:"w-4 h-4"}),t&&h.jsx(Q,{className:"text-sm font-medium",children:t}),n==="right"&&e&&h.jsx(re,{name:e,className:"w-4 h-4"})]}),m]})},IE=({label:t,icon:e,variant:n="default",disabled:r=!1,divider:s=!1,className:o="",style:i,onClick:a})=>{if(s)return h.jsx(I,{className:"h-px bg-gray-200 dark:bg-gray-700 my-1",style:i});const l=n==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",c=r?"opacity-50 cursor-not-allowed":"cursor-pointer",d=()=>{!r&&a&&a()};return h.jsxs(I,{onClick:d,className:`flex items-center gap-3 px-4 py-3 transition-colors ${l} ${c} ${o}`,style:i,children:[e&&h.jsx(re,{name:e,className:"w-4 h-4"}),t&&h.jsx(Q,{className:"text-sm font-medium",children:t})]})},LE=()=>window.G7Core,Pc=v.createContext(null),Ip=()=>{const t=v.useContext(Pc);if(!t){const e=LE(),n=e?.locale?.current?.()||"ko",r=e?.locale?.supported?.()||["ko","en"];return{activeLocale:n,getActiveLocale:()=>e?.locale?.current?.()||"ko",supportedLocales:r,isInsideMultilingualTabPanel:!1}}return{...t,isInsideMultilingualTabPanel:!0}},PE=()=>window.G7Core,$E=({value:t,onChange:e,placeholder:n,readOnly:r,disabled:s,hasError:o})=>{const{activeLocale:i,getActiveLocale:a}=Ip(),l=typeof t=="string"?{[i]:t}:t||{},c=v.useCallback(u=>{const f=a();e({...l,[f]:u.target.value})},[l,a,e]),d=o?"border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500":"border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500";return h.jsx(kt,{type:"text",value:l[i]||"",onChange:c,placeholder:n,readOnly:r,disabled:s,className:`w-full px-3 py-2 text-sm border ${d} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2`})},FE=(t,e)=>{if(!e)return!1;const n=e.startsWith("!"),r=n?e.slice(1):e,s=!!t[r];return n?!s:s},zE=({column:t,item:e,index:n,onValueChange:r,readOnly:s,error:o})=>{const i=e[t.key],a=s||t.readOnly||FE(e,t.readOnlyCondition)||t.disabled,l=!!o,c=l?"border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500":"border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500",d=v.useCallback(f=>{r(t.key,f)},[t.key,r]),u=f=>h.jsxs(I,{className:"w-full",children:[f,l&&h.jsx(Q,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:o})]});switch(t.type){case"input":return u(h.jsx(kt,{type:"text",value:i||"",onChange:f=>d(f.target.value),placeholder:t.placeholder,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${t.className||""}`}));case"number":return u(h.jsx(kt,{type:"number",value:i??"",onChange:f=>d(f.target.value?Number(f.target.value):null),placeholder:t.placeholder,min:t.min,max:t.max,step:t.step,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${t.className||""}`}));case"select":return u(h.jsxs(Pr,{value:i??"",onChange:f=>d(f.target.value),disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${t.className||""}`,children:[h.jsx("option",{value:"",children:"선택하세요"}),t.options?.map(f=>h.jsx("option",{value:f.value,disabled:f.disabled,children:f.label},f.value))]}));case"textarea":return u(h.jsx(Qn,{value:i||"",onChange:f=>d(f.target.value),placeholder:t.placeholder,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 resize-none ${t.className||""}`,rows:2}));case"multilingual":return u(h.jsx($E,{value:i,onChange:f=>d(f),placeholder:t.placeholder,readOnly:a,disabled:a,hasError:l}));case"custom":return t.render?h.jsx(h.Fragment,{children:t.render(e,n,d)}):h.jsx(Q,{className:"text-gray-500 dark:text-gray-400",children:"-"});default:return h.jsx(Q,{className:"text-gray-900 dark:text-white text-sm",children:String(i??"")})}},VE=(t,e,n)=>{if(!t)return;if(n==="multilingual"){const o=PE()?.locale?.current?.()||"ko",i=`${e}.${o}`;return t[i]?.[0]}return t[e]?.[0]},HE=({item:t,index:e,id:n,columns:r,enableDrag:s=!0,showIndex:o=!0,canRemove:i=!0,rowActions:a,readOnly:l=!1,onValueChange:c,onRemove:d,className:u="",level:f=0,errors:g})=>{const{attributes:m,listeners:p,setNodeRef:y,transform:b,transition:_,isDragging:x}=Ii({id:n,disabled:!s||l}),E={transform:Pn.Transform.toString(b),transition:_,opacity:x?.5:1},C=v.useCallback((S,w)=>{c?.(S,w)},[c]),k=v.useCallback(S=>{S.onClick?.(t,e)},[t,e]),N=f>0?{paddingLeft:`${f*24}px`}:{};return h.jsxs(I,{ref:y,style:E,className:`flex items-center gap-2 py-2 px-2 border-b border-gray-100 dark:border-gray-700 last:border-b-0 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors ${x?"bg-blue-50 dark:bg-blue-900/20 shadow-lg rounded-lg z-10":""} ${u}`,children:[s&&!l&&h.jsx(I,{...m,...p,className:"flex-shrink-0 w-6 h-6 flex items-center justify-center cursor-grab active:cursor-grabbing text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400",style:N,children:h.jsx(re,{name:"grip-vertical",className:"w-4 h-4"})}),o&&h.jsx(I,{className:"flex-shrink-0 w-8 text-center text-sm text-gray-500 dark:text-gray-400 font-medium",children:e+1}),r.map(S=>{const w=VE(g,S.key,S.type);return h.jsx(I,{className:`flex-1 min-w-0 ${S.width||""}`,style:S.width?{flex:"none",width:S.width}:{},children:h.jsx(zE,{column:S,item:t,index:e,onValueChange:C,readOnly:l,error:w})},S.key)}),a&&a.length>0&&!l&&h.jsx(I,{className:"flex-shrink-0 flex items-center gap-1",children:a.map(S=>{const w=typeof S.disabled=="function"?S.disabled(t,e):S.disabled;return h.jsx(oe,{type:"button",onClick:()=>k(S),disabled:w,className:`p-1.5 rounded transition-colors ${S.danger?"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-600":"text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300"} disabled:opacity-50 disabled:cursor-not-allowed`,title:S.label,children:S.icon?h.jsx(re,{name:S.icon,className:"w-4 h-4"}):h.jsx(Q,{className:"text-xs",children:S.label})},S.key)})}),i&&!a&&!l&&h.jsx(I,{className:"flex-shrink-0",children:h.jsx(oe,{type:"button",onClick:d,className:"p-1.5 text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300 rounded transition-colors",title:"삭제",children:h.jsx(re,{name:"minus",className:"w-4 h-4"})})})]})},Lp=()=>window.G7Core,$c=()=>`item_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,UE=t=>{const e={_id:$c()};return t.forEach(n=>{if(n.type==="multilingual"){const s=Lp()?.locale?.all?.()||["ko","en"],o={};s.forEach(i=>{o[i]=""}),e[n.key]=o}else n.type==="number"?e[n.key]=null:(n.type,e[n.key]="")}),e},BE=(t,e)=>{if(!t)return{};const n=`fields.${e}.`,r={};return Object.keys(t).forEach(s=>{if(s.startsWith(n)){const o=s.substring(n.length);r[o]=t[s]}}),r},jE=({items:t=[],columns:e,onChange:n,onAddItem:r,onRemoveItem:s,onReorder:o,addLabel:i="항목 추가",enableDrag:a=!0,showIndex:l=!0,minItems:c=0,maxItems:d,rowActions:u,emptyMessage:f="항목이 없습니다.",className:g="",headerClassName:m="",rowClassName:p="",name:y,readOnly:b=!1,createDefaultItem:_,childrenKey:x,itemIdKey:E="_id",errors:C,id:k,editorAttrs:N})=>{const S=v.useRef(null),w=v.useRef([]),M=v.useRef(null),T=v.useMemo(()=>{let W=!1;for(const U of t)if(U[E]==null){W=!0;break}return W?t.map(U=>U[E]!=null?U:{...U,[E]:$c()}):t},[t,E]);w.current=T,v.useEffect(()=>{M.current=null},[T]);const P=v.useCallback(W=>String(W[E]??""),[E]);v.useEffect(()=>{if(y){const W=Lp();W?.form&&(S.current=W.form)}},[y]);const L=jl(Zr(Xs,{activationConstraint:{distance:8}}),Zr(Ks,{coordinateGetter:Li})),$=v.useMemo(()=>T.map(W=>P(W)),[T,P]),A=v.useCallback(W=>{const{active:U,over:B}=W;if(B&&U.id!==B.id){const G=M.current??w.current,V=G.map(Z=>String(Z[E]??"")),X=V.indexOf(String(U.id)),K=V.indexOf(String(B.id));if(X!==-1&&K!==-1){const Z=Jr([...G],X,K);M.current=Z,n?.(Z),o?.(Z)}}},[n,o,E]),D=v.useCallback((W,U,B)=>{const V=[...M.current??w.current];V[W]={...V[W],[U]:B},M.current=V,n?.(V)},[n]),O=v.useCallback(()=>{const W=M.current??w.current;if(d&&W.length>=d)return;if(r){r();return}const U=_?_():UE(e);U[E]||(U[E]=$c());const B=[...W,U];M.current=B,n?.(B)},[d,r,_,e,E,n]),z=v.useCallback(W=>{const U=M.current??w.current;if(U.length<=c)return;const B=U[W];if(s){s(W,B);return}const G=U.filter((V,X)=>X!==W);M.current=G,n?.(G)},[c,s,n]),H=T.length>c,j=!d||T.lengthh.jsxs(I,{className:`flex-1 min-w-0 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase ${W.width||""}`,style:W.width?{flex:"none",width:W.width}:{},children:[W.label,W.required&&h.jsx(Q,{className:"text-red-500 dark:text-red-400 ml-0.5",children:"*"})]},W.key)),(u||!b&&H)&&h.jsx(I,{className:"flex-shrink-0 w-8"})]}),T.length>0?h.jsx(ec,{sensors:L,collisionDetection:Wl,onDragEnd:A,children:h.jsx(Di,{items:$,strategy:tc,children:h.jsx(I,{className:"border-x border-gray-200 dark:border-gray-700",children:T.map((W,U)=>{const B=P(W),G=BE(C,U);return h.jsx(HE,{id:B,item:W,index:U,columns:e,enableDrag:a,showIndex:l,canRemove:H,rowActions:u,readOnly:b,onValueChange:(V,X)=>D(U,V,X),onRemove:()=>z(U),className:p,errors:G},B)})})})}):h.jsx(I,{className:"border-x border-gray-200 dark:border-gray-700 py-8 text-center text-gray-500 dark:text-gray-400 text-sm",children:f}),!b&&j&&h.jsx(I,{className:"relative border border-gray-200 dark:border-gray-700 border-t-0 rounded-b-lg",children:h.jsx(I,{className:"flex items-center justify-center py-2",children:h.jsxs(oe,{type:"button",onClick:O,className:"flex items-center gap-1.5 px-4 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[h.jsx(re,{name:"plus",className:"w-4 h-4"}),h.jsx(Q,{children:i})]})})}),(b||!j)&&T.length>0&&h.jsx(I,{className:"border-x border-b border-gray-200 dark:border-gray-700 rounded-b-lg h-1"})]})},Pp=()=>window.G7Core,WE=()=>Pp()?.locale?.supported?.()??["ko","en"],GE=()=>Pp()?.locale?.current?.()??"ko",YE={ko:"한국어",en:"English",ja:"日本語",zh:"中文"},KE=({children:t,className:e="",style:n,variant:r="default",defaultLocale:s,onLocaleChange:o,locales:i})=>{const a=v.useMemo(()=>i&&i.length>0?i:WE(),[i]),[l,c]=v.useState(s||GE());v.useEffect(()=>{if(a.length>0&&!a.includes(l)){const p=a[0];c(p),o?.(p)}},[a,l,o]);const d=v.useRef(l);d.current=l;const u=v.useCallback(()=>d.current,[]),f=v.useCallback(p=>{c(p),o?.(p)},[o]),g=v.useMemo(()=>({activeLocale:l,getActiveLocale:u,supportedLocales:a}),[l,u,a]),m=p=>{const y=p===l,b="px-4 py-2 text-sm font-medium transition-all";return r==="pills"?y?`${b} bg-blue-600 text-white rounded-lg`:`${b} text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg`:r==="underline"?y?`${b} text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400`:`${b} text-gray-600 dark:text-gray-400 border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-500`:y?`${b} text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 border-b-2 border-blue-600`:`${b} text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 border-b-2 border-transparent`};return h.jsxs(I,{className:e,style:n,children:[h.jsx(Po,{className:"flex gap-1 border-b border-gray-200 dark:border-gray-700 mb-4",children:a.map(p=>h.jsx(oe,{type:"button",onClick:()=>f(p),className:m(p),children:h.jsx(Q,{children:YE[p]||p})},p))}),h.jsx(Pc.Provider,{value:g,children:t})]})},XE=({label:t,required:e=!1,error:n,helperText:r,className:s="",labelClassName:o="",children:i,horizontal:a=!1,labelWidth:l="w-1/3",id:c,editorAttrs:d})=>{const u=a?`flex items-start gap-4 ${s}`:`space-y-1 ${s}`,f=a?`${l} flex-shrink-0 pt-2`:"",g=a?"flex-1":"";return h.jsxs(I,{className:u,id:c,...d,children:[t&&h.jsx(I,{className:f,children:h.jsxs(sn,{className:`block text-sm font-medium text-gray-700 dark:text-gray-300 ${o}`,children:[t,e&&h.jsx("span",{className:"ml-1 text-red-500 dark:text-red-400",children:"*"})]})}),h.jsxs(I,{className:`${g} ${n?"form-field-error":""}`.trim(),children:[i,r&&!n&&h.jsx(I,{className:"mt-1 text-xs text-gray-500 dark:text-gray-400",children:r}),n&&h.jsx(I,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:n})]})]})};function lo(t){return t+.5|0}const ir=(t,e,n)=>Math.max(Math.min(t,n),e);function co(t){return ir(lo(t*2.55),0,255)}function ar(t){return ir(lo(t*255),0,255)}function Gn(t){return ir(lo(t/2.55)/100,0,1)}function $p(t){return ir(lo(t*100),0,100)}const hn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Fc=[..."0123456789ABCDEF"],qE=t=>Fc[t&15],ZE=t=>Fc[(t&240)>>4]+Fc[t&15],Qi=t=>(t&240)>>4===(t&15),QE=t=>Qi(t.r)&&Qi(t.g)&&Qi(t.b)&&Qi(t.a);function JE(t){var e=t.length,n;return t[0]==="#"&&(e===4||e===5?n={r:255&hn[t[1]]*17,g:255&hn[t[2]]*17,b:255&hn[t[3]]*17,a:e===5?hn[t[4]]*17:255}:(e===7||e===9)&&(n={r:hn[t[1]]<<4|hn[t[2]],g:hn[t[3]]<<4|hn[t[4]],b:hn[t[5]]<<4|hn[t[6]],a:e===9?hn[t[7]]<<4|hn[t[8]]:255})),n}const RE=(t,e)=>t<255?e(t):"";function eM(t){var e=QE(t)?qE:ZE;return t?"#"+e(t.r)+e(t.g)+e(t.b)+RE(t.a,e):void 0}const tM=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Fp(t,e,n){const r=e*Math.min(n,1-n),s=(o,i=(o+t/30)%12)=>n-r*Math.max(Math.min(i-3,9-i,1),-1);return[s(0),s(8),s(4)]}function nM(t,e,n){const r=(s,o=(s+t/60)%6)=>n-n*e*Math.max(Math.min(o,4-o,1),0);return[r(5),r(3),r(1)]}function rM(t,e,n){const r=Fp(t,1,.5);let s;for(e+n>1&&(s=1/(e+n),e*=s,n*=s),s=0;s<3;s++)r[s]*=1-e-n,r[s]+=e;return r}function sM(t,e,n,r,s){return t===s?(e-n)/r+(e.5?d/(2-o-i):d/(o+i),l=sM(n,r,s,d,o),l=l*60+.5),[l|0,c||0,a]}function Vc(t,e,n,r){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,n,r)).map(ar)}function Hc(t,e,n){return Vc(Fp,t,e,n)}function oM(t,e,n){return Vc(rM,t,e,n)}function iM(t,e,n){return Vc(nM,t,e,n)}function zp(t){return(t%360+360)%360}function aM(t){const e=tM.exec(t);let n=255,r;if(!e)return;e[5]!==r&&(n=e[6]?co(+e[5]):ar(+e[5]));const s=zp(+e[2]),o=+e[3]/100,i=+e[4]/100;return e[1]==="hwb"?r=oM(s,o,i):e[1]==="hsv"?r=iM(s,o,i):r=Hc(s,o,i),{r:r[0],g:r[1],b:r[2],a:n}}function lM(t,e){var n=zc(t);n[0]=zp(n[0]+e),n=Hc(n),t.r=n[0],t.g=n[1],t.b=n[2]}function cM(t){if(!t)return;const e=zc(t),n=e[0],r=$p(e[1]),s=$p(e[2]);return t.a<255?`hsla(${n}, ${r}%, ${s}%, ${Gn(t.a)})`:`hsl(${n}, ${r}%, ${s}%)`}const Vp={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Hp={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function dM(){const t={},e=Object.keys(Hp),n=Object.keys(Vp);let r,s,o,i,a;for(r=0;r>16&255,o>>8&255,o&255]}return t}let Ji;function uM(t){Ji||(Ji=dM(),Ji.transparent=[0,0,0,0]);const e=Ji[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:e.length===4?e[3]:255}}const fM=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function hM(t){const e=fM.exec(t);let n=255,r,s,o;if(e){if(e[7]!==r){const i=+e[7];n=e[8]?co(i):ir(i*255,0,255)}return r=+e[1],s=+e[3],o=+e[5],r=255&(e[2]?co(r):ir(r,0,255)),s=255&(e[4]?co(s):ir(s,0,255)),o=255&(e[6]?co(o):ir(o,0,255)),{r,g:s,b:o,a:n}}}function gM(t){return t&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${Gn(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`)}const Uc=t=>t<=.0031308?t*12.92:Math.pow(t,1/2.4)*1.055-.055,cs=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function pM(t,e,n){const r=cs(Gn(t.r)),s=cs(Gn(t.g)),o=cs(Gn(t.b));return{r:ar(Uc(r+n*(cs(Gn(e.r))-r))),g:ar(Uc(s+n*(cs(Gn(e.g))-s))),b:ar(Uc(o+n*(cs(Gn(e.b))-o))),a:t.a+n*(e.a-t.a)}}function Ri(t,e,n){if(t){let r=zc(t);r[e]=Math.max(0,Math.min(r[e]+r[e]*n,e===0?360:1)),r=Hc(r),t.r=r[0],t.g=r[1],t.b=r[2]}}function Up(t,e){return t&&Object.assign(e||{},t)}function Bp(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=ar(t[3]))):(e=Up(t,{r:0,g:0,b:0,a:1}),e.a=ar(e.a)),e}function mM(t){return t.charAt(0)==="r"?hM(t):aM(t)}class uo{constructor(e){if(e instanceof uo)return e;const n=typeof e;let r;n==="object"?r=Bp(e):n==="string"&&(r=JE(e)||uM(e)||mM(e)),this._rgb=r,this._valid=!!r}get valid(){return this._valid}get rgb(){var e=Up(this._rgb);return e&&(e.a=Gn(e.a)),e}set rgb(e){this._rgb=Bp(e)}rgbString(){return this._valid?gM(this._rgb):void 0}hexString(){return this._valid?eM(this._rgb):void 0}hslString(){return this._valid?cM(this._rgb):void 0}mix(e,n){if(e){const r=this.rgb,s=e.rgb;let o;const i=n===o?.5:n,a=2*i-1,l=r.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,r.r=255&c*r.r+o*s.r+.5,r.g=255&c*r.g+o*s.g+.5,r.b=255&c*r.b+o*s.b+.5,r.a=i*r.a+(1-i)*s.a,this.rgb=r}return this}interpolate(e,n){return e&&(this._rgb=pM(this._rgb,e._rgb,n)),this}clone(){return new uo(this.rgb)}alpha(e){return this._rgb.a=ar(e),this}clearer(e){const n=this._rgb;return n.a*=1-e,this}greyscale(){const e=this._rgb,n=lo(e.r*.3+e.g*.59+e.b*.11);return e.r=e.g=e.b=n,this}opaquer(e){const n=this._rgb;return n.a*=1+e,this}negate(){const e=this._rgb;return e.r=255-e.r,e.g=255-e.g,e.b=255-e.b,this}lighten(e){return Ri(this._rgb,2,e),this}darken(e){return Ri(this._rgb,2,-e),this}saturate(e){return Ri(this._rgb,1,e),this}desaturate(e){return Ri(this._rgb,1,-e),this}rotate(e){return lM(this._rgb,e),this}}function Yn(){}const bM=(()=>{let t=0;return()=>t++})();function lt(t){return t==null}function Et(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return e.slice(0,7)==="[object"&&e.slice(-6)==="Array]"}function Qe(t){return t!==null&&Object.prototype.toString.call(t)==="[object Object]"}function gn(t){return(typeof t=="number"||t instanceof Number)&&isFinite(+t)}function Fn(t,e){return gn(t)?t:e}function Je(t,e){return typeof t>"u"?e:t}const yM=(t,e)=>typeof t=="string"&&t.endsWith("%")?parseFloat(t)/100:+t/e,jp=(t,e)=>typeof t=="string"&&t.endsWith("%")?parseFloat(t)/100*e:+t;function gt(t,e,n){if(t&&typeof t.call=="function")return t.apply(n,e)}function at(t,e,n,r){let s,o,i;if(Et(t))for(o=t.length,s=0;st,x:t=>t.x,y:t=>t.y};function wM(t){const e=t.split("."),n=[];let r="";for(const s of e)r+=s,r.endsWith("\\")?r=r.slice(0,-1)+".":(n.push(r),r="");return n}function kM(t){const e=wM(t);return n=>{for(const r of e){if(r==="")break;n=n&&n[r]}return n}}function Sr(t,e){return(Gp[e]||(Gp[e]=kM(e)))(t)}function Bc(t){return t.charAt(0).toUpperCase()+t.slice(1)}const go=t=>typeof t<"u",lr=t=>typeof t=="function",Yp=(t,e)=>{if(t.size!==e.size)return!1;for(const n of t)if(!e.has(n))return!1;return!0};function CM(t){return t.type==="mouseup"||t.type==="click"||t.type==="contextmenu"}const ct=Math.PI,St=2*ct,na=Number.POSITIVE_INFINITY,SM=ct/180,Mt=ct/2,_r=ct/4,Kp=ct*2/3,Xp=Math.log10,cr=Math.sign;function ra(t,e,n){return Math.abs(t-e)s-o).pop(),e}function EM(t){return typeof t=="symbol"||typeof t=="object"&&t!==null&&!(Symbol.toPrimitive in t||"toString"in t||"valueOf"in t)}function sa(t){return!EM(t)&&!isNaN(parseFloat(t))&&isFinite(t)}function MM(t,e){const n=Math.round(t);return n-e<=t&&n+e>=t}function NM(t,e,n){let r,s,o;for(r=0,s=t.length;rl&&c=Math.min(e,n)-r&&t<=Math.max(e,n)+r}function jc(t,e,n){n=n||(i=>t[i]1;)o=s+r>>1,n(o)?s=o:r=o;return{lo:s,hi:r}}const Wc=(t,e,n,r)=>jc(t,n,r?s=>{const o=t[s][e];return ot[s][e]jc(t,n,r=>t[r][e]>=n);function IM(t,e,n){let r=0,s=t.length;for(;rr&&t[s-1]>n;)s--;return r>0||s{const r="_onData"+Bc(n),s=t[n];Object.defineProperty(t,n,{configurable:!0,enumerable:!1,value(...o){const i=s.apply(this,o);return t._chartjs.listeners.forEach(a=>{typeof a[r]=="function"&&a[r](...o)}),i}})})}function Rp(t,e){const n=t._chartjs;if(!n)return;const r=n.listeners,s=r.indexOf(e);s!==-1&&r.splice(s,1),!(r.length>0)&&(Jp.forEach(o=>{delete t[o]}),delete t._chartjs)}function e0(t){const e=new Set(t);return e.size===t.length?t:Array.from(e)}const t0=(function(){return typeof window>"u"?function(t){return t()}:window.requestAnimationFrame})();function n0(t,e){let n=[],r=!1;return function(...s){n=s,r||(r=!0,t0.call(window,()=>{r=!1,t.apply(e,n)}))}}function PM(t,e){let n;return function(...r){return e?(clearTimeout(n),n=setTimeout(t,e,r)):t.apply(this,r),e}}const Gc=t=>t==="start"?"left":t==="end"?"right":"center",Ut=(t,e,n)=>t==="start"?e:t==="end"?n:(e+n)/2,$M=(t,e,n,r)=>t===(r?"left":"right")?n:t==="center"?(e+n)/2:e,ia=t=>t===0||t===1,r0=(t,e,n)=>-(Math.pow(2,10*(t-=1))*Math.sin((t-e)*St/n)),s0=(t,e,n)=>Math.pow(2,-10*t)*Math.sin((t-e)*St/n)+1,po={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>-Math.cos(t*Mt)+1,easeOutSine:t=>Math.sin(t*Mt),easeInOutSine:t=>-.5*(Math.cos(ct*t)-1),easeInExpo:t=>t===0?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>t===1?1:-Math.pow(2,-10*t)+1,easeInOutExpo:t=>ia(t)?t:t<.5?.5*Math.pow(2,10*(t*2-1)):.5*(-Math.pow(2,-10*(t*2-1))+2),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ia(t)?t:r0(t,.075,.3),easeOutElastic:t=>ia(t)?t:s0(t,.075,.3),easeInOutElastic(t){return ia(t)?t:t<.5?.5*r0(t*2,.1125,.45):.5+.5*s0(t*2-1,.1125,.45)},easeInBack(t){return t*t*((1.70158+1)*t-1.70158)},easeOutBack(t){return(t-=1)*t*((1.70158+1)*t+1.70158)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?.5*(t*t*(((e*=1.525)+1)*t-e)):.5*((t-=2)*t*(((e*=1.525)+1)*t+e)+2)},easeInBounce:t=>1-po.easeOutBounce(1-t),easeOutBounce(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:t=>t<.5?po.easeInBounce(t*2)*.5:po.easeOutBounce(t*2-1)*.5+.5};function o0(t){if(t&&typeof t=="object"){const e=t.toString();return e==="[object CanvasPattern]"||e==="[object CanvasGradient]"}return!1}function i0(t){return o0(t)?t:new uo(t)}function Yc(t){return o0(t)?t:new uo(t).saturate(.5).darken(.1).hexString()}const FM=["x","y","borderWidth","radius","tension"],zM=["color","borderColor","backgroundColor"];function VM(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:e=>e!=="onProgress"&&e!=="onComplete"&&e!=="fn"}),t.set("animations",{colors:{type:"color",properties:zM},numbers:{type:"number",properties:FM}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:e=>e|0}}}})}function HM(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const a0=new Map;function UM(t,e){e=e||{};const n=t+JSON.stringify(e);let r=a0.get(n);return r||(r=new Intl.NumberFormat(t,e),a0.set(n,r)),r}function Kc(t,e,n){return UM(e,n).format(t)}const BM={values(t){return Et(t)?t:""+t},numeric(t,e,n){if(t===0)return"0";const r=this.chart.options.locale;let s,o=t;if(n.length>1){const c=Math.max(Math.abs(n[0].value),Math.abs(n[n.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=jM(t,n)}const i=Xp(Math.abs(o)),a=isNaN(i)?1:Math.max(Math.min(-1*Math.floor(i),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),Kc(t,r,l)}};function jM(t,e){let n=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;return Math.abs(n)>=1&&t!==Math.floor(t)&&(n=t-Math.floor(t)),n}var l0={formatters:BM};function WM(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(e,n)=>n.lineWidth,tickColor:(e,n)=>n.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:l0.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:e=>!e.startsWith("before")&&!e.startsWith("after")&&e!=="callback"&&e!=="parser",_indexable:e=>e!=="borderDash"&&e!=="tickBorderDash"&&e!=="dash"}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:e=>e!=="backdropPadding"&&e!=="callback",_indexable:e=>e!=="backdropPadding"})}const Mr=Object.create(null),Xc=Object.create(null);function mo(t,e){if(!e)return t;const n=e.split(".");for(let r=0,s=n.length;rr.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(r,s)=>Yc(s.backgroundColor),this.hoverBorderColor=(r,s)=>Yc(s.borderColor),this.hoverColor=(r,s)=>Yc(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(e),this.apply(n)}set(e,n){return qc(this,e,n)}get(e){return mo(this,e)}describe(e,n){return qc(Xc,e,n)}override(e,n){return qc(Mr,e,n)}route(e,n,r,s){const o=mo(this,e),i=mo(this,r),a="_"+n;Object.defineProperties(o,{[a]:{value:o[n],writable:!0},[n]:{enumerable:!0,get(){const l=this[a],c=i[s];return Qe(l)?Object.assign({},c,l):Je(l,c)},set(l){this[a]=l}}})}apply(e){e.forEach(n=>n(this))}}var xt=new GM({_scriptable:t=>!t.startsWith("on"),_indexable:t=>t!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[VM,HM,WM]);function YM(t){return!t||lt(t.size)||lt(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function c0(t,e,n,r,s){let o=e[s];return o||(o=e[s]=t.measureText(s).width,n.push(s)),o>r&&(r=o),r}function Nr(t,e,n){const r=t.currentDevicePixelRatio,s=n!==0?Math.max(n/2,.5):0;return Math.round((e-s)*r)/r+s}function d0(t,e){!e&&!t||(e=e||t.getContext("2d"),e.save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore())}function u0(t,e,n,r){f0(t,e,n,r,null)}function f0(t,e,n,r,s){let o,i,a,l,c,d,u,f;const g=e.pointStyle,m=e.rotation,p=e.radius;let y=(m||0)*SM;if(g&&typeof g=="object"&&(o=g.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){t.save(),t.translate(n,r),t.rotate(y),t.drawImage(g,-g.width/2,-g.height/2,g.width,g.height),t.restore();return}if(!(isNaN(p)||p<=0)){switch(t.beginPath(),g){default:s?t.ellipse(n,r,s/2,p,0,0,St):t.arc(n,r,p,0,St),t.closePath();break;case"triangle":d=s?s/2:p,t.moveTo(n+Math.sin(y)*d,r-Math.cos(y)*p),y+=Kp,t.lineTo(n+Math.sin(y)*d,r-Math.cos(y)*p),y+=Kp,t.lineTo(n+Math.sin(y)*d,r-Math.cos(y)*p),t.closePath();break;case"rectRounded":c=p*.516,l=p-c,i=Math.cos(y+_r)*l,u=Math.cos(y+_r)*(s?s/2-c:l),a=Math.sin(y+_r)*l,f=Math.sin(y+_r)*(s?s/2-c:l),t.arc(n-u,r-a,c,y-ct,y-Mt),t.arc(n+f,r-i,c,y-Mt,y),t.arc(n+u,r+a,c,y,y+Mt),t.arc(n-f,r+i,c,y+Mt,y+ct),t.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*p,d=s?s/2:l,t.rect(n-d,r-l,2*d,2*l);break}y+=_r;case"rectRot":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),t.moveTo(n-u,r-a),t.lineTo(n+f,r-i),t.lineTo(n+u,r+a),t.lineTo(n-f,r+i),t.closePath();break;case"crossRot":y+=_r;case"cross":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),t.moveTo(n-u,r-a),t.lineTo(n+u,r+a),t.moveTo(n+f,r-i),t.lineTo(n-f,r+i);break;case"star":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),t.moveTo(n-u,r-a),t.lineTo(n+u,r+a),t.moveTo(n+f,r-i),t.lineTo(n-f,r+i),y+=_r,u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),t.moveTo(n-u,r-a),t.lineTo(n+u,r+a),t.moveTo(n+f,r-i),t.lineTo(n-f,r+i);break;case"line":i=s?s/2:Math.cos(y)*p,a=Math.sin(y)*p,t.moveTo(n-i,r-a),t.lineTo(n+i,r+a);break;case"dash":t.moveTo(n,r),t.lineTo(n+Math.cos(y)*(s?s/2:p),r+Math.sin(y)*p);break;case!1:t.closePath();break}t.fill(),e.borderWidth>0&&t.stroke()}}function h0(t,e,n){return n=n||.5,!e||t&&t.x>e.left-n&&t.xe.top-n&&t.y0&&o.strokeColor!=="";let l,c;for(t.save(),t.font=s.string,KM(t,o),l=0;l+t||0;function Jc(t,e){const n={},r=Qe(e),s=r?Object.keys(e):e,o=Qe(t)?r?i=>Je(t[i],t[e[i]]):i=>t[i]:()=>t;for(const i of s)n[i]=RM(o(i));return n}function g0(t){return Jc(t,{top:"y",right:"x",bottom:"y",left:"x"})}function ds(t){return Jc(t,["topLeft","topRight","bottomLeft","bottomRight"])}function pn(t){const e=g0(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Bt(t,e){t=t||{},e=e||xt.font;let n=Je(t.size,e.size);typeof n=="string"&&(n=parseInt(n,10));let r=Je(t.style,e.style);r&&!(""+r).match(QM)&&(console.warn('Invalid font style specified: "'+r+'"'),r=void 0);const s={family:Je(t.family,e.family),lineHeight:JM(Je(t.lineHeight,e.lineHeight),n),size:n,style:r,weight:Je(t.weight,e.weight),string:""};return s.string=YM(s),s}function la(t,e,n,r){let s,o,i;for(s=0,o=t.length;sn&&a===0?0:a+l;return{min:i(r,-Math.abs(o)),max:i(s,o)}}function us(t,e){return Object.assign(Object.create(t),e)}function Rc(t,e=[""],n,r,s=()=>t[0]){const o=n||t;typeof r>"u"&&(r=v0("_fallback",t));const i={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:r,_getTarget:s,override:a=>Rc([a,...t],e,o,r)};return new Proxy(i,{deleteProperty(a,l){return delete a[l],delete a._keys,delete t[0][l],!0},get(a,l){return m0(a,l,()=>lN(l,e,t,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(t[0])},has(a,l){return x0(a).includes(l)},ownKeys(a){return x0(a)},set(a,l,c){const d=a._storage||(a._storage=s());return a[l]=d[l]=c,delete a._keys,!0}})}function fs(t,e,n,r){const s={_cacheable:!1,_proxy:t,_context:e,_subProxy:n,_stack:new Set,_descriptors:p0(t,r),setContext:o=>fs(t,o,n,r),override:o=>fs(t.override(o),e,n,r)};return new Proxy(s,{deleteProperty(o,i){return delete o[i],delete t[i],!0},get(o,i,a){return m0(o,i,()=>nN(o,i,a))},getOwnPropertyDescriptor(o,i){return o._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i)},getPrototypeOf(){return Reflect.getPrototypeOf(t)},has(o,i){return Reflect.has(t,i)},ownKeys(){return Reflect.ownKeys(t)},set(o,i,a){return t[i]=a,delete o[i],!0}})}function p0(t,e={scriptable:!0,indexable:!0}){const{_scriptable:n=e.scriptable,_indexable:r=e.indexable,_allKeys:s=e.allKeys}=t;return{allKeys:s,scriptable:n,indexable:r,isScriptable:lr(n)?n:()=>n,isIndexable:lr(r)?r:()=>r}}const tN=(t,e)=>t?t+Bc(e):e,ed=(t,e)=>Qe(e)&&t!=="adapters"&&(Object.getPrototypeOf(e)===null||e.constructor===Object);function m0(t,e,n){if(Object.prototype.hasOwnProperty.call(t,e)||e==="constructor")return t[e];const r=n();return t[e]=r,r}function nN(t,e,n){const{_proxy:r,_context:s,_subProxy:o,_descriptors:i}=t;let a=r[e];return lr(a)&&i.isScriptable(e)&&(a=rN(e,a,t,n)),Et(a)&&a.length&&(a=sN(e,a,t,i.isIndexable)),ed(e,a)&&(a=fs(a,s,o&&o[e],i)),a}function rN(t,e,n,r){const{_proxy:s,_context:o,_subProxy:i,_stack:a}=n;if(a.has(t))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+t);a.add(t);let l=e(o,i||r);return a.delete(t),ed(t,l)&&(l=td(s._scopes,s,t,l)),l}function sN(t,e,n,r){const{_proxy:s,_context:o,_subProxy:i,_descriptors:a}=n;if(typeof o.index<"u"&&r(t))return e[o.index%e.length];if(Qe(e[0])){const l=e,c=s._scopes.filter(d=>d!==l);e=[];for(const d of l){const u=td(c,s,t,d);e.push(fs(u,o,i&&i[t],a))}}return e}function b0(t,e,n){return lr(t)?t(e,n):t}const oN=(t,e)=>t===!0?e:typeof t=="string"?Sr(e,t):void 0;function iN(t,e,n,r,s){for(const o of e){const i=oN(n,o);if(i){t.add(i);const a=b0(i._fallback,n,s);if(typeof a<"u"&&a!==n&&a!==r)return a}else if(i===!1&&typeof r<"u"&&n!==r)return null}return!1}function td(t,e,n,r){const s=e._rootScopes,o=b0(e._fallback,n,r),i=[...t,...s],a=new Set;a.add(r);let l=y0(a,i,n,o||n,r);return l===null||typeof o<"u"&&o!==n&&(l=y0(a,i,o,l,r),l===null)?!1:Rc(Array.from(a),[""],s,o,()=>aN(e,n,r))}function y0(t,e,n,r,s){for(;n;)n=iN(t,e,n,r,s);return n}function aN(t,e,n){const r=t._getTarget();e in r||(r[e]={});const s=r[e];return Et(s)&&Qe(n)?n:s||{}}function lN(t,e,n,r){let s;for(const o of e)if(s=v0(tN(o,t),n),typeof s<"u")return ed(t,s)?td(n,r,t,s):s}function v0(t,e){for(const n of e){if(!n)continue;const r=n[t];if(typeof r<"u")return r}}function x0(t){let e=t._keys;return e||(e=t._keys=cN(t._scopes)),e}function cN(t){const e=new Set;for(const n of t)for(const r of Object.keys(n).filter(s=>!s.startsWith("_")))e.add(r);return Array.from(e)}function nd(){return typeof window<"u"&&typeof document<"u"}function rd(t){let e=t.parentNode;return e&&e.toString()==="[object ShadowRoot]"&&(e=e.host),e}function ca(t,e,n){let r;return typeof t=="string"?(r=parseInt(t,10),t.indexOf("%")!==-1&&(r=r/100*e.parentNode[n])):r=t,r}const da=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function dN(t,e){return da(t).getPropertyValue(e)}const uN=["top","right","bottom","left"];function Or(t,e,n){const r={};n=n?"-"+n:"";for(let s=0;s<4;s++){const o=uN[s];r[o]=parseFloat(t[e+"-"+o+n])||0}return r.width=r.left+r.right,r.height=r.top+r.bottom,r}const fN=(t,e,n)=>(t>0||e>0)&&(!n||!n.shadowRoot);function hN(t,e){const n=t.touches,r=n&&n.length?n[0]:t,{offsetX:s,offsetY:o}=r;let i=!1,a,l;if(fN(s,o,t.target))a=s,l=o;else{const c=e.getBoundingClientRect();a=r.clientX-c.left,l=r.clientY-c.top,i=!0}return{x:a,y:l,box:i}}function Tr(t,e){if("native"in t)return t;const{canvas:n,currentDevicePixelRatio:r}=e,s=da(n),o=s.boxSizing==="border-box",i=Or(s,"padding"),a=Or(s,"border","width"),{x:l,y:c,box:d}=hN(t,n),u=i.left+(d&&a.left),f=i.top+(d&&a.top);let{width:g,height:m}=e;return o&&(g-=i.width+a.width,m-=i.height+a.height),{x:Math.round((l-u)/g*n.width/r),y:Math.round((c-f)/m*n.height/r)}}function gN(t,e,n){let r,s;if(e===void 0||n===void 0){const o=t&&rd(t);if(!o)e=t.clientWidth,n=t.clientHeight;else{const i=o.getBoundingClientRect(),a=da(o),l=Or(a,"border","width"),c=Or(a,"padding");e=i.width-c.width-l.width,n=i.height-c.height-l.height,r=ca(a.maxWidth,o,"clientWidth"),s=ca(a.maxHeight,o,"clientHeight")}}return{width:e,height:n,maxWidth:r||na,maxHeight:s||na}}const dr=t=>Math.round(t*10)/10;function pN(t,e,n,r){const s=da(t),o=Or(s,"margin"),i=ca(s.maxWidth,t,"clientWidth")||na,a=ca(s.maxHeight,t,"clientHeight")||na,l=gN(t,e,n);let{width:c,height:d}=l;if(s.boxSizing==="content-box"){const f=Or(s,"border","width"),g=Or(s,"padding");c-=g.width+f.width,d-=g.height+f.height}return c=Math.max(0,c-o.width),d=Math.max(0,r?c/r:d-o.height),c=dr(Math.min(c,i,l.maxWidth)),d=dr(Math.min(d,a,l.maxHeight)),c&&!d&&(d=dr(c/2)),(e!==void 0||n!==void 0)&&r&&l.height&&d>l.height&&(d=l.height,c=dr(Math.floor(d*r))),{width:c,height:d}}function w0(t,e,n){const r=e||1,s=dr(t.height*r),o=dr(t.width*r);t.height=dr(t.height),t.width=dr(t.width);const i=t.canvas;return i.style&&(n||!i.style.height&&!i.style.width)&&(i.style.height=`${t.height}px`,i.style.width=`${t.width}px`),t.currentDevicePixelRatio!==r||i.height!==s||i.width!==o?(t.currentDevicePixelRatio=r,i.height=s,i.width=o,t.ctx.setTransform(r,0,0,r,0,0),!0):!1}const mN=(function(){let t=!1;try{const e={get passive(){return t=!0,!1}};nd()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch{}return t})();function k0(t,e){const n=dN(t,e),r=n&&n.match(/^(\d+)(\.\d+)?px$/);return r?+r[1]:void 0}const bN=function(t,e){return{x(n){return t+t+e-n},setWidth(n){e=n},textAlign(n){return n==="center"?n:n==="right"?"left":"right"},xPlus(n,r){return n-r},leftForLtr(n,r){return n-r}}},yN=function(){return{x(t){return t},setWidth(t){},textAlign(t){return t},xPlus(t,e){return t+e},leftForLtr(t,e){return t}}};function hs(t,e,n){return t?bN(e,n):yN()}function C0(t,e){let n,r;(e==="ltr"||e==="rtl")&&(n=t.canvas.style,r=[n.getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=r)}function S0(t,e){e!==void 0&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function ua(t,e,n){return t.options.clip?t[n]:e[n]}function vN(t,e){const{xScale:n,yScale:r}=t;return n&&r?{left:ua(n,e,"left"),right:ua(n,e,"right"),top:ua(r,e,"top"),bottom:ua(r,e,"bottom")}:e}function xN(t,e){const n=e._clip;if(n.disabled)return!1;const r=vN(e,t.chartArea);return{left:n.left===!1?0:r.left-(n.left===!0?0:n.left),right:n.right===!1?t.width:r.right+(n.right===!0?0:n.right),top:n.top===!1?0:r.top-(n.top===!0?0:n.top),bottom:n.bottom===!1?t.height:r.bottom+(n.bottom===!0?0:n.bottom)}}class wN{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(e,n,r,s){const o=n.listeners[s],i=n.duration;o.forEach(a=>a({chart:e,initial:n.initial,numSteps:i,currentStep:Math.min(r-n.start,i)}))}_refresh(){this._request||(this._running=!0,this._request=t0.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(e=Date.now()){let n=0;this._charts.forEach((r,s)=>{if(!r.running||!r.items.length)return;const o=r.items;let i=o.length-1,a=!1,l;for(;i>=0;--i)l=o[i],l._active?(l._total>r.duration&&(r.duration=l._total),l.tick(e),a=!0):(o[i]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,r,e,"progress")),o.length||(r.running=!1,this._notify(s,r,e,"complete"),r.initial=!1),n+=o.length}),this._lastDate=e,n===0&&(this._running=!1)}_getAnims(e){const n=this._charts;let r=n.get(e);return r||(r={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},n.set(e,r)),r}listen(e,n,r){this._getAnims(e).listeners[n].push(r)}add(e,n){!n||!n.length||this._getAnims(e).items.push(...n)}has(e){return this._getAnims(e).items.length>0}start(e){const n=this._charts.get(e);n&&(n.running=!0,n.start=Date.now(),n.duration=n.items.reduce((r,s)=>Math.max(r,s._duration),0),this._refresh())}running(e){if(!this._running)return!1;const n=this._charts.get(e);return!(!n||!n.running||!n.items.length)}stop(e){const n=this._charts.get(e);if(!n||!n.items.length)return;const r=n.items;let s=r.length-1;for(;s>=0;--s)r[s].cancel();n.items=[],this._notify(e,n,Date.now(),"complete")}remove(e){return this._charts.delete(e)}}var Xn=new wN;const _0="transparent",kN={boolean(t,e,n){return n>.5?e:t},color(t,e,n){const r=i0(t||_0),s=r.valid&&i0(e||_0);return s&&s.valid?s.mix(r,n).hexString():e},number(t,e,n){return t+(e-t)*n}};class CN{constructor(e,n,r,s){const o=n[r];s=la([e.to,s,o,e.from]);const i=la([e.from,o,s]);this._active=!0,this._fn=e.fn||kN[e.type||typeof i],this._easing=po[e.easing]||po.linear,this._start=Math.floor(Date.now()+(e.delay||0)),this._duration=this._total=Math.floor(e.duration),this._loop=!!e.loop,this._target=n,this._prop=r,this._from=i,this._to=s,this._promises=void 0}active(){return this._active}update(e,n,r){if(this._active){this._notify(!1);const s=this._target[this._prop],o=r-this._start,i=this._duration-o;this._start=r,this._duration=Math.floor(Math.max(i,e.duration)),this._total+=o,this._loop=!!e.loop,this._to=la([e.to,n,s,e.from]),this._from=la([e.from,s,n])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(e){const n=e-this._start,r=this._duration,s=this._prop,o=this._from,i=this._loop,a=this._to;let l;if(this._active=o!==a&&(i||n1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const e=this._promises||(this._promises=[]);return new Promise((n,r)=>{e.push({res:n,rej:r})})}_notify(e){const n=e?"res":"rej",r=this._promises||[];for(let s=0;s{const o=e[s];if(!Qe(o))return;const i={};for(const a of n)i[a]=o[a];(Et(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!r.has(a))&&r.set(a,i)})})}_animateOptions(e,n){const r=n.options,s=_N(e,r);if(!s)return[];const o=this._createAnimations(s,r);return r.$shared&&SN(e.options.$animations,r).then(()=>{e.options=r},()=>{}),o}_createAnimations(e,n){const r=this._properties,s=[],o=e.$animations||(e.$animations={}),i=Object.keys(n),a=Date.now();let l;for(l=i.length-1;l>=0;--l){const c=i[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(e,n));continue}const d=n[c];let u=o[c];const f=r.get(c);if(u)if(f&&u.active()){u.update(f,d,a);continue}else u.cancel();if(!f||!f.duration){e[c]=d;continue}o[c]=u=new CN(f,e,c,d),s.push(u)}return s}update(e,n){if(this._properties.size===0){Object.assign(e,n);return}const r=this._createAnimations(e,n);if(r.length)return Xn.add(this._chart,r),!0}}function SN(t,e){const n=[],r=Object.keys(e);for(let s=0;s0||!n&&o<0)return s.index}return null}function A0(t,e){const{chart:n,_cachedMeta:r}=t,s=n._stacks||(n._stacks={}),{iScale:o,vScale:i,index:a}=r,l=o.axis,c=i.axis,d=ON(o,i,r),u=e.length;let f;for(let g=0;gn[r].axis===e).shift()}function DN(t,e){return us(t,{active:!1,dataset:void 0,datasetIndex:e,index:e,mode:"default",type:"dataset"})}function IN(t,e,n){return us(t,{active:!1,dataIndex:e,parsed:void 0,raw:void 0,element:n,index:e,mode:"default",type:"data"})}function yo(t,e){const n=t.controller.index,r=t.vScale&&t.vScale.axis;if(r){e=e||t._parsed;for(const s of e){const o=s._stacks;if(!o||o[r]===void 0||o[r][n]===void 0)return;delete o[r][n],o[r]._visualValues!==void 0&&o[r]._visualValues[n]!==void 0&&delete o[r]._visualValues[n]}}}const id=t=>t==="reset"||t==="none",D0=(t,e)=>e?t:Object.assign({},t),LN=(t,e,n)=>t&&!e.hidden&&e._stacked&&{keys:N0(n,!0),values:null};class gs{constructor(e,n){this.chart=e,this._ctx=e.ctx,this.index=n,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const e=this._cachedMeta;this.configure(),this.linkScales(),e._stacked=sd(e.vScale,e),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(e){this.index!==e&&yo(this._cachedMeta),this.index=e}linkScales(){const e=this.chart,n=this._cachedMeta,r=this.getDataset(),s=(u,f,g,m)=>u==="x"?f:u==="r"?m:g,o=n.xAxisID=Je(r.xAxisID,od(e,"x")),i=n.yAxisID=Je(r.yAxisID,od(e,"y")),a=n.rAxisID=Je(r.rAxisID,od(e,"r")),l=n.indexAxis,c=n.iAxisID=s(l,o,i,a),d=n.vAxisID=s(l,i,o,a);n.xScale=this.getScaleForId(o),n.yScale=this.getScaleForId(i),n.rScale=this.getScaleForId(a),n.iScale=this.getScaleForId(c),n.vScale=this.getScaleForId(d)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(e){return this.chart.scales[e]}_getOtherScale(e){const n=this._cachedMeta;return e===n.iScale?n.vScale:n.iScale}reset(){this._update("reset")}_destroy(){const e=this._cachedMeta;this._data&&Rp(this._data,this),e._stacked&&yo(e)}_dataCheck(){const e=this.getDataset(),n=e.data||(e.data=[]),r=this._data;if(Qe(n)){const s=this._cachedMeta;this._data=NN(n,s)}else if(r!==n){if(r){Rp(r,this);const s=this._cachedMeta;yo(s),s._parsed=[]}n&&Object.isExtensible(n)&&LM(n,this),this._syncList=[],this._data=n}}addElements(){const e=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(e.dataset=new this.datasetElementType)}buildOrUpdateElements(e){const n=this._cachedMeta,r=this.getDataset();let s=!1;this._dataCheck();const o=n._stacked;n._stacked=sd(n.vScale,n),n.stack!==r.stack&&(s=!0,yo(n),n.stack=r.stack),this._resyncElements(e),(s||o!==n._stacked)&&(A0(this,n._parsed),n._stacked=sd(n.vScale,n))}configure(){const e=this.chart.config,n=e.datasetScopeKeys(this._type),r=e.getOptionScopes(this.getDataset(),n,!0);this.options=e.createResolver(r,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(e,n){const{_cachedMeta:r,_data:s}=this,{iScale:o,_stacked:i}=r,a=o.axis;let l=e===0&&n===s.length?!0:r._sorted,c=e>0&&r._parsed[e-1],d,u,f;if(this._parsing===!1)r._parsed=s,r._sorted=!0,f=s;else{Et(s[e])?f=this.parseArrayData(r,s,e,n):Qe(s[e])?f=this.parseObjectData(r,s,e,n):f=this.parsePrimitiveData(r,s,e,n);const g=()=>u[a]===null||c&&u[a]p||u=0;--f)if(!m()){this.updateRangeFromParsed(c,e,g,l);break}}return c}getAllParsedValues(e){const n=this._cachedMeta._parsed,r=[];let s,o,i;for(s=0,o=n.length;s=0&&ethis.getContext(r,s,n),p=c.resolveNamedOptions(f,g,m,u);return p.$shared&&(p.$shared=l,o[i]=Object.freeze(D0(p,l))),p}_resolveAnimations(e,n,r){const s=this.chart,o=this._cachedDataOpts,i=`animation-${n}`,a=o[i];if(a)return a;let l;if(s.options.animation!==!1){const d=this.chart.config,u=d.datasetAnimationScopeKeys(this._type,n),f=d.getOptionScopes(this.getDataset(),u);l=d.createResolver(f,this.getContext(e,r,n))}const c=new E0(s,l&&l.animations);return l&&l._cacheable&&(o[i]=Object.freeze(c)),c}getSharedOptions(e){if(e.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},e))}includeOptions(e,n){return!n||id(e)||this.chart._animationsDisabled}_getSharedOptions(e,n){const r=this.resolveDataElementOptions(e,n),s=this._sharedOptions,o=this.getSharedOptions(r),i=this.includeOptions(n,o)||o!==s;return this.updateSharedOptions(o,n,r),{sharedOptions:o,includeOptions:i}}updateElement(e,n,r,s){id(s)?Object.assign(e,r):this._resolveAnimations(n,s).update(e,r)}updateSharedOptions(e,n,r){e&&!id(n)&&this._resolveAnimations(void 0,n).update(e,r)}_setStyle(e,n,r,s){e.active=s;const o=this.getStyle(n,s);this._resolveAnimations(n,r,s).update(e,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(e,n,r){this._setStyle(e,r,"active",!1)}setHoverStyle(e,n,r){this._setStyle(e,r,"active",!0)}_removeDatasetHoverStyle(){const e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,"active",!1)}_setDatasetHoverStyle(){const e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,"active",!0)}_resyncElements(e){const n=this._data,r=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=r.length,o=n.length,i=Math.min(o,s);i&&this.parse(0,i),o>s?this._insertElements(s,o-s,e):o{for(c.length+=n,a=c.length-1;a>=i;a--)c[a]=c[a-n]};for(l(o),a=e;as-o))}return t._cache.$bar}function $N(t){const e=t.iScale,n=PN(e,t.type);let r=e._length,s,o,i,a;const l=()=>{i===32767||i===-32768||(go(a)&&(r=Math.min(r,Math.abs(i-a)||r)),a=i)};for(s=0,o=n.length;s0?s[t-1]:null,a=tMath.abs(a)&&(l=a,c=i),e[n.axis]=c,e._custom={barStart:l,barEnd:c,start:s,end:o,min:i,max:a}}function I0(t,e,n,r){return Et(t)?VN(t,e,n,r):e[n.axis]=n.parse(t,r),e}function L0(t,e,n,r){const s=t.iScale,o=t.vScale,i=s.getLabels(),a=s===o,l=[];let c,d,u,f;for(c=n,d=n+r;c=n?1:-1)}function UN(t){let e,n,r,s,o;return t.horizontal?(e=t.base>t.x,n="left",r="right"):(e=t.based.controller.options.grouped),o=r.options.stacked,i=[],a=this._cachedMeta.controller.getParsed(n),l=a&&a[r.axis],c=d=>{const u=d._parsed.find(g=>g[r.axis]===l),f=u&&u[d.vScale.axis];if(lt(f)||isNaN(f))return!0};for(const d of s)if(!(n!==void 0&&c(d))&&((o===!1||i.indexOf(d.stack)===-1||o===void 0&&d.stack===void 0)&&i.push(d.stack),d.index===e))break;return i.length||i.push(void 0),i}_getStackCount(e){return this._getStacks(void 0,e).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const e=this.chart.scales,n=this.chart.options.indexAxis;return Object.keys(e).filter(r=>e[r].axis===n).shift()}_getAxis(){const e={},n=this.getFirstScaleIdForIndexAxis();for(const r of this.chart.data.datasets)e[Je(this.chart.options.indexAxis==="x"?r.xAxisID:r.yAxisID,n)]=!0;return Object.keys(e)}_getStackIndex(e,n,r){const s=this._getStacks(e,r),o=n!==void 0?s.indexOf(n):-1;return o===-1?s.length-1:o}_getRuler(){const e=this.options,n=this._cachedMeta,r=n.iScale,s=[];let o,i;for(o=0,i=n.data.length;ooa(x,a,l,!0)?1:Math.max(E,E*n,C,C*n),m=(x,E,C)=>oa(x,a,l,!0)?-1:Math.min(E,E*n,C,C*n),p=g(0,c,u),y=g(Mt,d,f),b=m(ct,c,u),_=m(ct+Mt,d,f);r=(p-b)/2,s=(y-_)/2,o=-(p+b)/2,i=-(y+_)/2}return{ratioX:r,ratioY:s,offsetX:o,offsetY:i}}class vo extends gs{constructor(e,n){super(e,n),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(e,n){const r=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=r;else{let o=l=>+r[l];if(Qe(r[e])){const{key:l="value"}=this._parsing;o=c=>+Sr(r[c],l)}let i,a;for(i=e,a=e+n;i0&&!isNaN(e)?St*(Math.abs(e)/n):0}getLabelAndValue(e){const n=this._cachedMeta,r=this.chart,s=r.data.labels||[],o=Kc(n._parsed[e],r.options.locale);return{label:s[e]||"",value:o}}getMaxBorderWidth(e){let n=0;const r=this.chart;let s,o,i,a,l;if(!e){for(s=0,o=r.data.datasets.length;se!=="spacing",_indexable:e=>e!=="spacing"&&!e.startsWith("borderDash")&&!e.startsWith("hoverBorderDash")}),$e(vo,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){const n=e.data,{labels:{pointStyle:r,textAlign:s,color:o,useBorderRadius:i,borderRadius:a}}=e.legend.options;return n.labels.length&&n.datasets.length?n.labels.map((l,c)=>{const u=e.getDatasetMeta(0).controller.getStyle(c);return{text:l,fillStyle:u.backgroundColor,fontColor:o,hidden:!e.getDataVisibility(c),lineDash:u.borderDash,lineDashOffset:u.borderDashOffset,lineJoin:u.borderJoinStyle,lineWidth:u.borderWidth,strokeStyle:u.borderColor,textAlign:s,pointStyle:r,borderRadius:i&&(a||u.borderRadius),index:c}}):[]}},onClick(e,n,r){r.chart.toggleDataVisibility(n.index),r.chart.update()}}}});function Ar(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class ld{constructor(e){$e(this,"options");this.options=e||{}}static override(e){Object.assign(ld.prototype,e)}init(){}formats(){return Ar()}parse(){return Ar()}format(){return Ar()}add(){return Ar()}diff(){return Ar()}startOf(){return Ar()}endOf(){return Ar()}}var YN={_date:ld};function KN(t,e,n,r){const{controller:s,data:o,_sorted:i}=t,a=s._cachedMeta.iScale,l=t.dataset&&t.dataset.options?t.dataset.options.spanGaps:null;if(a&&e===a.axis&&e!=="r"&&i&&o.length){const c=a._reversePixels?DM:Wc;if(r){if(s._sharedOptions){const d=o[0],u=typeof d.getRange=="function"&&d.getRange(e);if(u){const f=c(o,e,n-u),g=c(o,e,n+u);return{lo:f.lo,hi:g.hi}}}}else{const d=c(o,e,n);if(l){const{vScale:u}=s._cachedMeta,{_parsed:f}=t,g=f.slice(0,d.lo+1).reverse().findIndex(p=>!lt(p[u.axis]));d.lo-=Math.max(0,g);const m=f.slice(d.hi).findIndex(p=>!lt(p[u.axis]));d.hi+=Math.max(0,m)}return d}}return{lo:0,hi:o.length-1}}function ha(t,e,n,r,s){const o=t.getSortedVisibleDatasetMetas(),i=n[e];for(let a=0,l=o.length;a{l[i]&&l[i](e[n],s)&&(o.push({element:l,datasetIndex:c,index:d}),a=a||l.inRange(e.x,e.y,s))}),r&&!a?[]:o}var QN={modes:{index(t,e,n,r){const s=Tr(e,t),o=n.axis||"x",i=n.includeInvisible||!1,a=n.intersect?cd(t,s,o,r,i):dd(t,s,o,!1,r,i),l=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach(c=>{const d=a[0].index,u=c.data[d];u&&!u.skip&&l.push({element:u,datasetIndex:c.index,index:d})}),l):[]},dataset(t,e,n,r){const s=Tr(e,t),o=n.axis||"xy",i=n.includeInvisible||!1;let a=n.intersect?cd(t,s,o,r,i):dd(t,s,o,!1,r,i);if(a.length>0){const l=a[0].datasetIndex,c=t.getDatasetMeta(l).data;a=[];for(let d=0;dn.pos===e)}function V0(t,e){return t.filter(n=>z0.indexOf(n.pos)===-1&&n.box.axis===e)}function wo(t,e){return t.sort((n,r)=>{const s=e?r:n,o=e?n:r;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function JN(t){const e=[];let n,r,s,o,i,a;for(n=0,r=(t||[]).length;nc.box.fullSize),!0),r=wo(xo(e,"left"),!0),s=wo(xo(e,"right")),o=wo(xo(e,"top"),!0),i=wo(xo(e,"bottom")),a=V0(e,"x"),l=V0(e,"y");return{fullSize:n,leftAndTop:r.concat(o),rightAndBottom:s.concat(l).concat(i).concat(a),chartArea:xo(e,"chartArea"),vertical:r.concat(s).concat(l),horizontal:o.concat(i).concat(a)}}function H0(t,e,n,r){return Math.max(t[n],e[n])+Math.max(t[r],e[r])}function U0(t,e){t.top=Math.max(t.top,e.top),t.left=Math.max(t.left,e.left),t.bottom=Math.max(t.bottom,e.bottom),t.right=Math.max(t.right,e.right)}function nO(t,e,n,r){const{pos:s,box:o}=n,i=t.maxPadding;if(!Qe(s)){n.size&&(t[s]-=n.size);const u=r[n.stack]||{size:0,count:1};u.size=Math.max(u.size,n.horizontal?o.height:o.width),n.size=u.size/u.count,t[s]+=n.size}o.getPadding&&U0(i,o.getPadding());const a=Math.max(0,e.outerWidth-H0(i,t,"left","right")),l=Math.max(0,e.outerHeight-H0(i,t,"top","bottom")),c=a!==t.w,d=l!==t.h;return t.w=a,t.h=l,n.horizontal?{same:c,other:d}:{same:d,other:c}}function rO(t){const e=t.maxPadding;function n(r){const s=Math.max(e[r]-t[r],0);return t[r]+=s,s}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}function sO(t,e){const n=e.maxPadding;function r(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(i=>{o[i]=Math.max(e[i],n[i])}),o}return r(t?["left","right"]:["top","bottom"])}function ko(t,e,n,r){const s=[];let o,i,a,l,c,d;for(o=0,i=t.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const d=l.reduce((p,y)=>y.box.options&&y.box.options.display===!1?p:p+1,0)||1,u=Object.freeze({outerWidth:e,outerHeight:n,padding:s,availableWidth:o,availableHeight:i,vBoxMaxWidth:o/2/d,hBoxMaxHeight:i/2}),f=Object.assign({},s);U0(f,pn(r));const g=Object.assign({maxPadding:f,w:o,h:i,x:s.left,y:s.top},s),m=eO(l.concat(c),u);ko(a.fullSize,g,u,m),ko(l,g,u,m),ko(c,g,u,m)&&ko(l,g,u,m),rO(g),B0(a.leftAndTop,g,u,m),g.x+=g.w,g.y+=g.h,B0(a.rightAndBottom,g,u,m),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},at(a.chartArea,p=>{const y=p.box;Object.assign(y,t.chartArea),y.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})})}};class j0{acquireContext(e,n){}releaseContext(e){return!1}addEventListener(e,n,r){}removeEventListener(e,n,r){}getDevicePixelRatio(){return 1}getMaximumSize(e,n,r,s){return n=Math.max(0,n||e.width),r=r||e.height,{width:n,height:Math.max(0,s?Math.floor(n/s):r)}}isAttached(e){return!0}updateConfig(e){}}class oO extends j0{acquireContext(e){return e&&e.getContext&&e.getContext("2d")||null}updateConfig(e){e.options.animation=!1}}const pa="$chartjs",iO={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},W0=t=>t===null||t==="";function aO(t,e){const n=t.style,r=t.getAttribute("height"),s=t.getAttribute("width");if(t[pa]={initial:{height:r,width:s,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",n.boxSizing=n.boxSizing||"border-box",W0(s)){const o=k0(t,"width");o!==void 0&&(t.width=o)}if(W0(r))if(t.style.height==="")t.height=t.width/(e||2);else{const o=k0(t,"height");o!==void 0&&(t.height=o)}return t}const G0=mN?{passive:!0}:!1;function lO(t,e,n){t&&t.addEventListener(e,n,G0)}function cO(t,e,n){t&&t.canvas&&t.canvas.removeEventListener(e,n,G0)}function dO(t,e){const n=iO[t.type]||t.type,{x:r,y:s}=Tr(t,e);return{type:n,chart:e,native:t,x:r!==void 0?r:null,y:s!==void 0?s:null}}function ma(t,e){for(const n of t)if(n===e||n.contains(e))return!0}function uO(t,e,n){const r=t.canvas,s=new MutationObserver(o=>{let i=!1;for(const a of o)i=i||ma(a.addedNodes,r),i=i&&!ma(a.removedNodes,r);i&&n()});return s.observe(document,{childList:!0,subtree:!0}),s}function fO(t,e,n){const r=t.canvas,s=new MutationObserver(o=>{let i=!1;for(const a of o)i=i||ma(a.removedNodes,r),i=i&&!ma(a.addedNodes,r);i&&n()});return s.observe(document,{childList:!0,subtree:!0}),s}const Co=new Map;let Y0=0;function K0(){const t=window.devicePixelRatio;t!==Y0&&(Y0=t,Co.forEach((e,n)=>{n.currentDevicePixelRatio!==t&&e()}))}function hO(t,e){Co.size||window.addEventListener("resize",K0),Co.set(t,e)}function gO(t){Co.delete(t),Co.size||window.removeEventListener("resize",K0)}function pO(t,e,n){const r=t.canvas,s=r&&rd(r);if(!s)return;const o=n0((a,l)=>{const c=s.clientWidth;n(a,l),c{const l=a[0],c=l.contentRect.width,d=l.contentRect.height;c===0&&d===0||o(c,d)});return i.observe(s),hO(t,o),i}function ud(t,e,n){n&&n.disconnect(),e==="resize"&&gO(t)}function mO(t,e,n){const r=t.canvas,s=n0(o=>{t.ctx!==null&&n(dO(o,t))},t);return lO(r,e,s),s}class bO extends j0{acquireContext(e,n){const r=e&&e.getContext&&e.getContext("2d");return r&&r.canvas===e?(aO(e,n),r):null}releaseContext(e){const n=e.canvas;if(!n[pa])return!1;const r=n[pa].initial;["height","width"].forEach(o=>{const i=r[o];lt(i)?n.removeAttribute(o):n.setAttribute(o,i)});const s=r.style||{};return Object.keys(s).forEach(o=>{n.style[o]=s[o]}),n.width=n.width,delete n[pa],!0}addEventListener(e,n,r){this.removeEventListener(e,n);const s=e.$proxies||(e.$proxies={}),i={attach:uO,detach:fO,resize:pO}[n]||mO;s[n]=i(e,n,r)}removeEventListener(e,n){const r=e.$proxies||(e.$proxies={}),s=r[n];if(!s)return;({attach:ud,detach:ud,resize:ud}[n]||cO)(e,n,s),r[n]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(e,n,r,s){return pN(e,n,r,s)}isAttached(e){const n=e&&rd(e);return!!(n&&n.isConnected)}}function yO(t){return!nd()||typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas?oO:bO}let Dr=(Aa=class{constructor(){$e(this,"x");$e(this,"y");$e(this,"active",!1);$e(this,"options");$e(this,"$animations")}tooltipPosition(e){const{x:n,y:r}=this.getProps(["x","y"],e);return{x:n,y:r}}hasValue(){return sa(this.x)&&sa(this.y)}getProps(e,n){const r=this.$animations;if(!n||!r)return this;const s={};return e.forEach(o=>{s[o]=r[o]&&r[o].active()?r[o]._to:this[o]}),s}},$e(Aa,"defaults",{}),$e(Aa,"defaultRoutes"),Aa);function vO(t,e){const n=t.options.ticks,r=xO(t),s=Math.min(n.maxTicksLimit||r,r),o=n.major.enabled?kO(e):[],i=o.length,a=o[0],l=o[i-1],c=[];if(i>s)return CO(e,c,o,i/s),c;const d=wO(o,e,s);if(i>0){let u,f;const g=i>1?Math.round((l-a)/(i-1)):null;for(ba(e,c,d,lt(g)?0:a-g,a),u=0,f=i-1;us)return l}return Math.max(s,1)}function kO(t){const e=[];let n,r;for(n=0,r=t.length;nt==="left"?"right":t==="right"?"left":t,X0=(t,e,n)=>e==="top"||e==="left"?t[e]+n:t[e]-n,q0=(t,e)=>Math.min(e||t,t);function Z0(t,e){const n=[],r=t.length/e,s=t.length;let o=0;for(;oi+a)))return l}function MO(t,e){at(t,n=>{const r=n.gc,s=r.length/2;let o;if(s>e){for(o=0;or?r:n,r=s&&n>r?n:r,{min:Fn(n,Fn(r,n)),max:Fn(r,Fn(n,r))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const e=this.chart.data;return this.options.labels||(this.isHorizontal()?e.xLabels:e.yLabels)||e.labels||[]}getLabelItems(e=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(e))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){gt(this.options.beforeUpdate,[this])}update(e,n,r){const{beginAtZero:s,grace:o,ticks:i}=this.options,a=i.sampleSize;this.beforeUpdate(),this.maxWidth=e,this.maxHeight=n,this._margins=r=Object.assign({left:0,right:0,top:0,bottom:0},r),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+r.left+r.right:this.height+r.top+r.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=eN(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||r<=1||!this.isHorizontal()){this.labelRotation=s;return}const d=this._getLabelSizes(),u=d.widest.width,f=d.highest.height,g=en(this.chart.width-u,0,this.maxWidth);a=e.offset?this.maxWidth/r:g/(r-1),u+6>a&&(a=g/(r-(e.offset?.5:1)),l=this.maxHeight-So(e.grid)-n.padding-Q0(e.title,this.chart.options.font),c=Math.sqrt(u*u+f*f),i=OM(Math.min(Math.asin(en((d.highest.height+6)/a,-1,1)),Math.asin(en(l/c,-1,1))-Math.asin(en(f/c,-1,1)))),i=Math.max(s,Math.min(o,i))),this.labelRotation=i}afterCalculateLabelRotation(){gt(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){gt(this.options.beforeFit,[this])}fit(){const e={width:0,height:0},{chart:n,options:{ticks:r,title:s,grid:o}}=this,i=this._isVisible(),a=this.isHorizontal();if(i){const l=Q0(s,n.options.font);if(a?(e.width=this.maxWidth,e.height=So(o)+l):(e.height=this.maxHeight,e.width=So(o)+l),r.display&&this.ticks.length){const{first:c,last:d,widest:u,highest:f}=this._getLabelSizes(),g=r.padding*2,m=Kn(this.labelRotation),p=Math.cos(m),y=Math.sin(m);if(a){const b=r.mirror?0:y*u.width+p*f.height;e.height=Math.min(this.maxHeight,e.height+b+g)}else{const b=r.mirror?0:p*u.width+y*f.height;e.width=Math.min(this.maxWidth,e.width+b+g)}this._calculatePadding(c,d,y,p)}}this._handleMargins(),a?(this.width=this._length=n.width-this._margins.left-this._margins.right,this.height=e.height):(this.width=e.width,this.height=this._length=n.height-this._margins.top-this._margins.bottom)}_calculatePadding(e,n,r,s){const{ticks:{align:o,padding:i},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const d=this.getPixelForTick(0)-this.left,u=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,g=0;l?c?(f=s*e.width,g=r*n.height):(f=r*e.height,g=s*n.width):o==="start"?g=n.width:o==="end"?f=e.width:o!=="inner"&&(f=e.width/2,g=n.width/2),this.paddingLeft=Math.max((f-d+i)*this.width/(this.width-d),0),this.paddingRight=Math.max((g-u+i)*this.width/(this.width-u),0)}else{let d=n.height/2,u=e.height/2;o==="start"?(d=0,u=e.height):o==="end"&&(d=n.height,u=0),this.paddingTop=d+i,this.paddingBottom=u+i}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){gt(this.options.afterFit,[this])}isHorizontal(){const{axis:e,position:n}=this.options;return n==="top"||n==="bottom"||e==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(e){this.beforeTickToLabelConversion(),this.generateTickLabels(e);let n,r;for(n=0,r=e.length;n({width:i[w]||0,height:a[w]||0});return{first:S(0),last:S(n-1),widest:S(k),highest:S(N),widths:i,heights:a}}getLabelForValue(e){return e}getPixelForValue(e,n){return NaN}getValueForPixel(e){}getPixelForTick(e){const n=this.ticks;return e<0||e>n.length-1?null:this.getPixelForValue(n[e].value)}getPixelForDecimal(e){this._reversePixels&&(e=1-e);const n=this._startPixel+e*this._length;return AM(this._alignToPixels?Nr(this.chart,n,0):n)}getDecimalForPixel(e){const n=(e-this._startPixel)/this._length;return this._reversePixels?1-n:n}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:e,max:n}=this;return e<0&&n<0?n:e>0&&n>0?e:0}getContext(e){const n=this.ticks||[];if(e>=0&&ea*s?a/r:l/s:l*s0}_computeGridLineItems(e){const n=this.axis,r=this.chart,s=this.options,{grid:o,position:i,border:a}=s,l=o.offset,c=this.isHorizontal(),u=this.ticks.length+(l?1:0),f=So(o),g=[],m=a.setContext(this.getContext()),p=m.display?m.width:0,y=p/2,b=function(D){return Nr(r,D,p)};let _,x,E,C,k,N,S,w,M,T,P,L;if(i==="top")_=b(this.bottom),N=this.bottom-f,w=_-y,T=b(e.top)+y,L=e.bottom;else if(i==="bottom")_=b(this.top),T=e.top,L=b(e.bottom)-y,N=_+y,w=this.top+f;else if(i==="left")_=b(this.right),k=this.right-f,S=_-y,M=b(e.left)+y,P=e.right;else if(i==="right")_=b(this.left),M=e.left,P=b(e.right)-y,k=_+y,S=this.left+f;else if(n==="x"){if(i==="center")_=b((e.top+e.bottom)/2+.5);else if(Qe(i)){const D=Object.keys(i)[0],O=i[D];_=b(this.chart.scales[D].getPixelForValue(O))}T=e.top,L=e.bottom,N=_+y,w=N+f}else if(n==="y"){if(i==="center")_=b((e.left+e.right)/2);else if(Qe(i)){const D=Object.keys(i)[0],O=i[D];_=b(this.chart.scales[D].getPixelForValue(O))}k=_-y,S=k-f,M=e.left,P=e.right}const $=Je(s.ticks.maxTicksLimit,u),A=Math.max(1,Math.ceil(u/$));for(x=0;x0&&(X-=G/2);break}W={left:X,top:V,width:G+U.width,height:B+U.height,color:A.backdropColor}}y.push({label:E,font:w,textOffset:P,options:{rotation:p,color:O,strokeColor:z,strokeWidth:H,textAlign:j,textBaseline:L,translation:[C,k],backdrop:W}})}return y}_getXAxisLabelAlignment(){const{position:e,ticks:n}=this.options;if(-Kn(this.labelRotation))return e==="top"?"left":"right";let s="center";return n.align==="start"?s="left":n.align==="end"?s="right":n.align==="inner"&&(s="inner"),s}_getYAxisLabelAlignment(e){const{position:n,ticks:{crossAlign:r,mirror:s,padding:o}}=this.options,i=this._getLabelSizes(),a=e+o,l=i.widest.width;let c,d;return n==="left"?s?(d=this.right+o,r==="near"?c="left":r==="center"?(c="center",d+=l/2):(c="right",d+=l)):(d=this.right-a,r==="near"?c="right":r==="center"?(c="center",d-=l/2):(c="left",d=this.left)):n==="right"?s?(d=this.left+o,r==="near"?c="right":r==="center"?(c="center",d-=l/2):(c="left",d-=l)):(d=this.left+a,r==="near"?c="left":r==="center"?(c="center",d+=l/2):(c="right",d=this.right)):c="right",{textAlign:c,x:d}}_computeLabelArea(){if(this.options.ticks.mirror)return;const e=this.chart,n=this.options.position;if(n==="left"||n==="right")return{top:0,left:this.left,bottom:e.height,right:this.right};if(n==="top"||n==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:e.width}}drawBackground(){const{ctx:e,options:{backgroundColor:n},left:r,top:s,width:o,height:i}=this;n&&(e.save(),e.fillStyle=n,e.fillRect(r,s,o,i),e.restore())}getLineWidthForValue(e){const n=this.options.grid;if(!this._isVisible()||!n.display)return 0;const s=this.ticks.findIndex(o=>o.value===e);return s>=0?n.setContext(this.getContext(s)).lineWidth:0}drawGrid(e){const n=this.options.grid,r=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(e));let o,i;const a=(l,c,d)=>{!d.width||!d.color||(r.save(),r.lineWidth=d.width,r.strokeStyle=d.color,r.setLineDash(d.borderDash||[]),r.lineDashOffset=d.borderDashOffset,r.beginPath(),r.moveTo(l.x,l.y),r.lineTo(c.x,c.y),r.stroke(),r.restore())};if(n.display)for(o=0,i=s.length;o{this.draw(o)}}]:[{z:r,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:n,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(e){const n=this.chart.getSortedVisibleDatasetMetas(),r=this.axis+"AxisID",s=[];let o,i;for(o=0,i=n.length;o{const r=n.split("."),s=r.pop(),o=[t].concat(r).join("."),i=e[n].split("."),a=i.pop(),l=i.join(".");xt.route(o,s,l,a)})}function LO(t){return"id"in t&&"defaults"in t}class PO{constructor(){this.controllers=new ya(gs,"datasets",!0),this.elements=new ya(Dr,"elements"),this.plugins=new ya(Object,"plugins"),this.scales=new ya(ps,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...e){this._each("register",e)}remove(...e){this._each("unregister",e)}addControllers(...e){this._each("register",e,this.controllers)}addElements(...e){this._each("register",e,this.elements)}addPlugins(...e){this._each("register",e,this.plugins)}addScales(...e){this._each("register",e,this.scales)}getController(e){return this._get(e,this.controllers,"controller")}getElement(e){return this._get(e,this.elements,"element")}getPlugin(e){return this._get(e,this.plugins,"plugin")}getScale(e){return this._get(e,this.scales,"scale")}removeControllers(...e){this._each("unregister",e,this.controllers)}removeElements(...e){this._each("unregister",e,this.elements)}removePlugins(...e){this._each("unregister",e,this.plugins)}removeScales(...e){this._each("unregister",e,this.scales)}_each(e,n,r){[...n].forEach(s=>{const o=r||this._getRegistryForType(s);r||o.isForType(s)||o===this.plugins&&s.id?this._exec(e,o,s):at(s,i=>{const a=r||this._getRegistryForType(i);this._exec(e,a,i)})})}_exec(e,n,r){const s=Bc(e);gt(r["before"+s],[],r),n[e](r),gt(r["after"+s],[],r)}_getRegistryForType(e){for(let n=0;no.filter(a=>!i.some(l=>a.plugin.id===l.plugin.id));this._notify(s(n,r),e,"stop"),this._notify(s(r,n),e,"start")}}function FO(t){const e={},n=[],r=Object.keys(Vn.plugins.items);for(let o=0;o1&&J0(t[0].toLowerCase());if(r)return r}throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function R0(t,e,n){if(n[e+"AxisID"]===t)return{axis:e}}function WO(t,e){if(e.data&&e.data.datasets){const n=e.data.datasets.filter(r=>r.xAxisID===t||r.yAxisID===t);if(n.length)return R0(t,"x",n[0])||R0(t,"y",n[0])}return{}}function GO(t,e){const n=Mr[t.type]||{scales:{}},r=e.scales||{},s=fd(t.type,e),o=Object.create(null);return Object.keys(r).forEach(i=>{const a=r[i];if(!Qe(a))return console.error(`Invalid scale configuration for scale: ${i}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${i}`);const l=hd(i,a,WO(i,t),xt.scales[a.type]),c=BO(l,s),d=n.scales||{};o[i]=ho(Object.create(null),[{axis:l},a,d[l],d[c]])}),t.data.datasets.forEach(i=>{const a=i.type||t.type,l=i.indexAxis||fd(a,e),d=(Mr[a]||{}).scales||{};Object.keys(d).forEach(u=>{const f=UO(u,l),g=i[f+"AxisID"]||f;o[g]=o[g]||Object.create(null),ho(o[g],[{axis:f},r[g],d[u]])})}),Object.keys(o).forEach(i=>{const a=o[i];ho(a,[xt.scales[a.type],xt.scale])}),o}function em(t){const e=t.options||(t.options={});e.plugins=Je(e.plugins,{}),e.scales=GO(t,e)}function tm(t){return t=t||{},t.datasets=t.datasets||[],t.labels=t.labels||[],t}function YO(t){return t=t||{},t.data=tm(t.data),em(t),t}const nm=new Map,rm=new Set;function va(t,e){let n=nm.get(t);return n||(n=e(),nm.set(t,n),rm.add(n)),n}const _o=(t,e,n)=>{const r=Sr(e,n);r!==void 0&&t.add(r)};class KO{constructor(e){this._config=YO(e),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(e){this._config.type=e}get data(){return this._config.data}set data(e){this._config.data=tm(e)}get options(){return this._config.options}set options(e){this._config.options=e}get plugins(){return this._config.plugins}update(){const e=this._config;this.clearCache(),em(e)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(e){return va(e,()=>[[`datasets.${e}`,""]])}datasetAnimationScopeKeys(e,n){return va(`${e}.transition.${n}`,()=>[[`datasets.${e}.transitions.${n}`,`transitions.${n}`],[`datasets.${e}`,""]])}datasetElementScopeKeys(e,n){return va(`${e}-${n}`,()=>[[`datasets.${e}.elements.${n}`,`datasets.${e}`,`elements.${n}`,""]])}pluginScopeKeys(e){const n=e.id,r=this.type;return va(`${r}-plugin-${n}`,()=>[[`plugins.${n}`,...e.additionalOptionScopes||[]]])}_cachedScopes(e,n){const r=this._scopeCache;let s=r.get(e);return(!s||n)&&(s=new Map,r.set(e,s)),s}getOptionScopes(e,n,r){const{options:s,type:o}=this,i=this._cachedScopes(e,r),a=i.get(n);if(a)return a;const l=new Set;n.forEach(d=>{e&&(l.add(e),d.forEach(u=>_o(l,e,u))),d.forEach(u=>_o(l,s,u)),d.forEach(u=>_o(l,Mr[o]||{},u)),d.forEach(u=>_o(l,xt,u)),d.forEach(u=>_o(l,Xc,u))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),rm.has(n)&&i.set(n,c),c}chartOptionScopes(){const{options:e,type:n}=this;return[e,Mr[n]||{},xt.datasets[n]||{},{type:n},xt,Xc]}resolveNamedOptions(e,n,r,s=[""]){const o={$shared:!0},{resolver:i,subPrefixes:a}=sm(this._resolverCache,e,s);let l=i;if(qO(i,n)){o.$shared=!1,r=lr(r)?r():r;const c=this.createResolver(e,r,a);l=fs(i,r,c)}for(const c of n)o[c]=l[c];return o}createResolver(e,n,r=[""],s){const{resolver:o}=sm(this._resolverCache,e,r);return Qe(n)?fs(o,n,void 0,s):o}}function sm(t,e,n){let r=t.get(e);r||(r=new Map,t.set(e,r));const s=n.join();let o=r.get(s);return o||(o={resolver:Rc(e,n),subPrefixes:n.filter(a=>!a.toLowerCase().includes("hover"))},r.set(s,o)),o}const XO=t=>Qe(t)&&Object.getOwnPropertyNames(t).some(e=>lr(t[e]));function qO(t,e){const{isScriptable:n,isIndexable:r}=p0(t);for(const s of e){const o=n(s),i=r(s),a=(i||o)&&t[s];if(o&&(lr(a)||XO(a))||i&&Et(a))return!0}return!1}var ZO="4.5.1";const QO=["top","bottom","left","right","chartArea"];function om(t,e){return t==="top"||t==="bottom"||QO.indexOf(t)===-1&&e==="x"}function im(t,e){return function(n,r){return n[t]===r[t]?n[e]-r[e]:n[t]-r[t]}}function am(t){const e=t.chart,n=e.options.animation;e.notifyPlugins("afterRender"),gt(n&&n.onComplete,[t],e)}function JO(t){const e=t.chart,n=e.options.animation;gt(n&&n.onProgress,[t],e)}function lm(t){return nd()&&typeof t=="string"?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const xa={},cm=t=>{const e=lm(t);return Object.values(xa).filter(n=>n.canvas===e).pop()};function RO(t,e,n){const r=Object.keys(t);for(const s of r){const o=+s;if(o>=e){const i=t[s];delete t[s],(n>0||o>e)&&(t[o+n]=i)}}}function eT(t,e,n,r){return!n||t.type==="mouseout"?null:r?e:t}let Eo=(fr=class{static register(...e){Vn.add(...e),dm()}static unregister(...e){Vn.remove(...e),dm()}constructor(e,n){const r=this.config=new KO(n),s=lm(e),o=cm(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const i=r.createResolver(r.chartOptionScopes(),this.getContext());this.platform=new(r.platform||yO(s)),this.platform.updateConfig(r);const a=this.platform.acquireContext(s,i.aspectRatio),l=a&&a.canvas,c=l&&l.height,d=l&&l.width;if(this.id=bM(),this.ctx=a,this.canvas=l,this.width=d,this.height=c,this._options=i,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new $O,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=PM(u=>this.update(u),i.resizeDelay||0),this._dataChanges=[],xa[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}Xn.listen(this,"complete",am),Xn.listen(this,"progress",JO),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:e,maintainAspectRatio:n},width:r,height:s,_aspectRatio:o}=this;return lt(e)?n&&o?o:s?r/s:null:e}get data(){return this.config.data}set data(e){this.config.data=e}get options(){return this._options}set options(e){this.config.options=e}get registry(){return Vn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():w0(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return d0(this.canvas,this.ctx),this}stop(){return Xn.stop(this),this}resize(e,n){Xn.running(this)?this._resizeBeforeDraw={width:e,height:n}:this._resize(e,n)}_resize(e,n){const r=this.options,s=this.canvas,o=r.maintainAspectRatio&&this.aspectRatio,i=this.platform.getMaximumSize(s,e,n,o),a=r.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=i.width,this.height=i.height,this._aspectRatio=this.aspectRatio,w0(this,a,!0)&&(this.notifyPlugins("resize",{size:i}),gt(r.onResize,[this,i],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const n=this.options.scales||{};at(n,(r,s)=>{r.id=s})}buildOrUpdateScales(){const e=this.options,n=e.scales,r=this.scales,s=Object.keys(r).reduce((i,a)=>(i[a]=!1,i),{});let o=[];n&&(o=o.concat(Object.keys(n).map(i=>{const a=n[i],l=hd(i,a),c=l==="r",d=l==="x";return{options:a,dposition:c?"chartArea":d?"bottom":"left",dtype:c?"radialLinear":d?"category":"linear"}}))),at(o,i=>{const a=i.options,l=a.id,c=hd(l,a),d=Je(a.type,i.dtype);(a.position===void 0||om(a.position,c)!==om(i.dposition))&&(a.position=i.dposition),s[l]=!0;let u=null;if(l in r&&r[l].type===d)u=r[l];else{const f=Vn.getScale(d);u=new f({id:l,type:d,ctx:this.ctx,chart:this}),r[u.id]=u}u.init(a,e)}),at(s,(i,a)=>{i||delete r[a]}),at(r,i=>{mn.configure(this,i,i.options),mn.addBox(this,i)})}_updateMetasets(){const e=this._metasets,n=this.data.datasets.length,r=e.length;if(e.sort((s,o)=>s.index-o.index),r>n){for(let s=n;sn.length&&delete this._stacks,e.forEach((r,s)=>{n.filter(o=>o===r._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const e=[],n=this.data.datasets;let r,s;for(this._removeUnreferencedMetasets(),r=0,s=n.length;r{this.getDatasetMeta(n).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(e){const n=this.config;n.update();const r=this._options=n.createResolver(n.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!r.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:e,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let i=0;for(let c=0,d=this.data.datasets.length;c{c.reset()}),this._updateDatasets(e),this.notifyPlugins("afterUpdate",{mode:e}),this._layers.sort(im("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){at(this.scales,e=>{mn.removeBox(this,e)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const e=this.options,n=new Set(Object.keys(this._listeners)),r=new Set(e.events);(!Yp(n,r)||!!this._responsiveListeners!==e.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:e}=this,n=this._getUniformDataChanges()||[];for(const{method:r,start:s,count:o}of n){const i=r==="_removeElements"?-o:o;RO(e,s,i)}}_getUniformDataChanges(){const e=this._dataChanges;if(!e||!e.length)return;this._dataChanges=[];const n=this.data.datasets.length,r=o=>new Set(e.filter(i=>i[0]===o).map((i,a)=>a+","+i.splice(1).join(","))),s=r(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(e){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;mn.update(this,this.width,this.height,e);const n=this.chartArea,r=n.width<=0||n.height<=0;this._layers=[],at(this.boxes,s=>{r&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(e){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:e,cancelable:!0})!==!1){for(let n=0,r=this.data.datasets.length;n=0;--n)this._drawDataset(e[n]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(e){const n=this.ctx,r={meta:e,index:e.index,cancelable:!0},s=xN(this,e);this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&Zc(n,s),e.controller.draw(),s&&Qc(n),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(e){return h0(e,this.chartArea,this._minPadding)}getElementsAtEventForMode(e,n,r,s){const o=QN.modes[n];return typeof o=="function"?o(this,e,r,s):[]}getDatasetMeta(e){const n=this.data.datasets[e],r=this._metasets;let s=r.filter(o=>o&&o._dataset===n).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:n&&n.order||0,index:e,_dataset:n,_parsed:[],_sorted:!1},r.push(s)),s}getContext(){return this.$context||(this.$context=us(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(e){const n=this.data.datasets[e];if(!n)return!1;const r=this.getDatasetMeta(e);return typeof r.hidden=="boolean"?!r.hidden:!n.hidden}setDatasetVisibility(e,n){const r=this.getDatasetMeta(e);r.hidden=!n}toggleDataVisibility(e){this._hiddenIndices[e]=!this._hiddenIndices[e]}getDataVisibility(e){return!this._hiddenIndices[e]}_updateVisibility(e,n,r){const s=r?"show":"hide",o=this.getDatasetMeta(e),i=o.controller._resolveAnimations(void 0,s);go(n)?(o.data[n].hidden=!r,this.update()):(this.setDatasetVisibility(e,r),i.update(o,{visible:r}),this.update(a=>a.datasetIndex===e?s:void 0))}hide(e,n){this._updateVisibility(e,n,!1)}show(e,n){this._updateVisibility(e,n,!0)}_destroyDatasetMeta(e){const n=this._metasets[e];n&&n.controller&&n.controller._destroy(),delete this._metasets[e]}_stop(){let e,n;for(this.stop(),Xn.remove(this),e=0,n=this.data.datasets.length;e{n.addEventListener(this,o,i),e[o]=i},s=(o,i,a)=>{o.offsetX=i,o.offsetY=a,this._eventHandler(o)};at(this.options.events,o=>r(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const e=this._responsiveListeners,n=this.platform,r=(l,c)=>{n.addEventListener(this,l,c),e[l]=c},s=(l,c)=>{e[l]&&(n.removeEventListener(this,l,c),delete e[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let i;const a=()=>{s("attach",a),this.attached=!0,this.resize(),r("resize",o),r("detach",i)};i=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),r("attach",a)},n.isAttached(this.canvas)?a():i()}unbindEvents(){at(this._listeners,(e,n)=>{this.platform.removeEventListener(this,n,e)}),this._listeners={},at(this._responsiveListeners,(e,n)=>{this.platform.removeEventListener(this,n,e)}),this._responsiveListeners=void 0}updateHoverStyle(e,n,r){const s=r?"set":"remove";let o,i,a,l;for(n==="dataset"&&(o=this.getDatasetMeta(e[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=e.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[i],index:i}});!ea(r,n)&&(this._active=r,this._lastEvent=null,this._updateHoverStyles(r,n))}notifyPlugins(e,n,r){return this._plugins.notify(this,e,n,r)}isPluginEnabled(e){return this._plugins._cache.filter(n=>n.plugin.id===e).length===1}_updateHoverStyles(e,n,r){const s=this.options.hover,o=(l,c)=>l.filter(d=>!c.some(u=>d.datasetIndex===u.datasetIndex&&d.index===u.index)),i=o(n,e),a=r?e:o(e,n);i.length&&this.updateHoverStyle(i,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(e,n){const r={event:e,replay:n,cancelable:!0,inChartArea:this.isPointInArea(e)},s=i=>(i.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins("beforeEvent",r,s)===!1)return;const o=this._handleEvent(e,n,r.inChartArea);return r.cancelable=!1,this.notifyPlugins("afterEvent",r,s),(o||r.changed)&&this.render(),this}_handleEvent(e,n,r){const{_active:s=[],options:o}=this,i=n,a=this._getActiveElements(e,s,r,i),l=CM(e),c=eT(e,this._lastEvent,r,l);r&&(this._lastEvent=null,gt(o.onHover,[e,a,this],this),l&>(o.onClick,[e,a,this],this));const d=!ea(a,s);return(d||n)&&(this._active=a,this._updateHoverStyles(a,s,n)),this._lastEvent=c,d}_getActiveElements(e,n,r,s){if(e.type==="mouseout")return[];if(!r)return n;const o=this.options.hover;return this.getElementsAtEventForMode(e,o.mode,o,s)}},$e(fr,"defaults",xt),$e(fr,"instances",xa),$e(fr,"overrides",Mr),$e(fr,"registry",Vn),$e(fr,"version",ZO),$e(fr,"getChart",cm),fr);function dm(){return at(Eo.instances,t=>t._plugins.invalidate())}function tT(t,e,n){const{startAngle:r,x:s,y:o,outerRadius:i,innerRadius:a,options:l}=e,{borderWidth:c,borderJoinStyle:d}=l,u=Math.min(c/i,zn(r-n));if(t.beginPath(),t.arc(s,o,i-c/2,r+u/2,n-u/2),a>0){const f=Math.min(c/a,zn(r-n));t.arc(s,o,a+c/2,n-f/2,r+f/2,!0)}else{const f=Math.min(c/2,i*zn(r-n));if(d==="round")t.arc(s,o,f,n-ct/2,r+ct/2,!0);else if(d==="bevel"){const g=2*f*f,m=-g*Math.cos(n+ct/2)+s,p=-g*Math.sin(n+ct/2)+o,y=g*Math.cos(r+ct/2)+s,b=g*Math.sin(r+ct/2)+o;t.lineTo(m,p),t.lineTo(y,b)}}t.closePath(),t.moveTo(0,0),t.rect(0,0,t.canvas.width,t.canvas.height),t.clip("evenodd")}function nT(t,e,n){const{startAngle:r,pixelMargin:s,x:o,y:i,outerRadius:a,innerRadius:l}=e;let c=s/a;t.beginPath(),t.arc(o,i,a,r-c,n+c),l>s?(c=s/l,t.arc(o,i,l,n+c,r-c,!0)):t.arc(o,i,s,n+Mt,r-Mt),t.closePath(),t.clip()}function rT(t){return Jc(t,["outerStart","outerEnd","innerStart","innerEnd"])}function sT(t,e,n,r){const s=rT(t.options.borderRadius),o=(n-e)/2,i=Math.min(o,r*e/2),a=l=>{const c=(n-Math.min(o,l))*r/2;return en(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:en(s.innerStart,0,i),innerEnd:en(s.innerEnd,0,i)}}function ms(t,e,n,r){return{x:n+t*Math.cos(e),y:r+t*Math.sin(e)}}function wa(t,e,n,r,s,o){const{x:i,y:a,startAngle:l,pixelMargin:c,innerRadius:d}=e,u=Math.max(e.outerRadius+r+n-c,0),f=d>0?d+r+n+c:0;let g=0;const m=s-l;if(r){const A=d>0?d-r:0,D=u>0?u-r:0,O=(A+D)/2,z=O!==0?m*O/(O+r):m;g=(m-z)/2}const p=Math.max(.001,m*u-n/ct)/u,y=(m-p)/2,b=l+y+g,_=s-y-g,{outerStart:x,outerEnd:E,innerStart:C,innerEnd:k}=sT(e,f,u,_-b),N=u-x,S=u-E,w=b+x/N,M=_-E/S,T=f+C,P=f+k,L=b+C/T,$=_-k/P;if(t.beginPath(),o){const A=(w+M)/2;if(t.arc(i,a,u,w,A),t.arc(i,a,u,A,M),E>0){const H=ms(S,M,i,a);t.arc(H.x,H.y,E,M,_+Mt)}const D=ms(P,_,i,a);if(t.lineTo(D.x,D.y),k>0){const H=ms(P,$,i,a);t.arc(H.x,H.y,k,_+Mt,$+Math.PI)}const O=(_-k/f+(b+C/f))/2;if(t.arc(i,a,f,_-k/f,O,!0),t.arc(i,a,f,O,b+C/f,!0),C>0){const H=ms(T,L,i,a);t.arc(H.x,H.y,C,L+Math.PI,b-Mt)}const z=ms(N,b,i,a);if(t.lineTo(z.x,z.y),x>0){const H=ms(N,w,i,a);t.arc(H.x,H.y,x,b-Mt,w)}}else{t.moveTo(i,a);const A=Math.cos(w)*u+i,D=Math.sin(w)*u+a;t.lineTo(A,D);const O=Math.cos(M)*u+i,z=Math.sin(M)*u+a;t.lineTo(O,z)}t.closePath()}function oT(t,e,n,r,s){const{fullCircles:o,startAngle:i,circumference:a}=e;let l=e.endAngle;if(o){wa(t,e,n,r,l,s);for(let c=0;c=ct&&g===0&&d!=="miter"&&tT(t,e,p),o||(wa(t,e,n,r,p,s),t.stroke())}class Mo extends Dr{constructor(n){super();$e(this,"circumference");$e(this,"endAngle");$e(this,"fullCircles");$e(this,"innerRadius");$e(this,"outerRadius");$e(this,"pixelMargin");$e(this,"startAngle");this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,n&&Object.assign(this,n)}inRange(n,r,s){const o=this.getProps(["x","y"],s),{angle:i,distance:a}=Qp(o,{x:n,y:r}),{startAngle:l,endAngle:c,innerRadius:d,outerRadius:u,circumference:f}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],s),g=(this.options.spacing+this.options.borderWidth)/2,m=Je(f,c-l),p=oa(i,l,c)&&l!==c,y=m>=St||p,b=Er(a,d+g,u+g);return y&&b}getCenterPoint(n){const{x:r,y:s,startAngle:o,endAngle:i,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],n),{offset:c,spacing:d}=this.options,u=(o+i)/2,f=(a+l+d+c)/2;return{x:r+Math.cos(u)*f,y:s+Math.sin(u)*f}}tooltipPosition(n){return this.getCenterPoint(n)}draw(n){const{options:r,circumference:s}=this,o=(r.offset||0)/4,i=(r.spacing||0)/2,a=r.circular;if(this.pixelMargin=r.borderAlign==="inner"?.33:0,this.fullCircles=s>St?Math.floor(s/St):0,s===0||this.innerRadius<0||this.outerRadius<0)return;n.save();const l=(this.startAngle+this.endAngle)/2;n.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min(ct,s||0)),d=o*c;n.fillStyle=r.backgroundColor,n.strokeStyle=r.borderColor,oT(n,this,d,i,a),iT(n,this,d,i,a),n.restore()}}$e(Mo,"id","arc"),$e(Mo,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1}),$e(Mo,"defaultRoutes",{backgroundColor:"backgroundColor"}),$e(Mo,"descriptors",{_scriptable:!0,_indexable:n=>n!=="borderDash"});function um(t,e){const{x:n,y:r,base:s,width:o,height:i}=t.getProps(["x","y","base","width","height"],e);let a,l,c,d,u;return t.horizontal?(u=i/2,a=Math.min(n,s),l=Math.max(n,s),c=r-u,d=r+u):(u=o/2,a=n-u,l=n+u,c=Math.min(r,s),d=Math.max(r,s)),{left:a,top:c,right:l,bottom:d}}function ur(t,e,n,r){return t?0:en(e,n,r)}function aT(t,e,n){const r=t.options.borderWidth,s=t.borderSkipped,o=g0(r);return{t:ur(s.top,o.top,0,n),r:ur(s.right,o.right,0,e),b:ur(s.bottom,o.bottom,0,n),l:ur(s.left,o.left,0,e)}}function lT(t,e,n){const{enableBorderRadius:r}=t.getProps(["enableBorderRadius"]),s=t.options.borderRadius,o=ds(s),i=Math.min(e,n),a=t.borderSkipped,l=r||Qe(s);return{topLeft:ur(!l||a.top||a.left,o.topLeft,0,i),topRight:ur(!l||a.top||a.right,o.topRight,0,i),bottomLeft:ur(!l||a.bottom||a.left,o.bottomLeft,0,i),bottomRight:ur(!l||a.bottom||a.right,o.bottomRight,0,i)}}function cT(t){const e=um(t),n=e.right-e.left,r=e.bottom-e.top,s=aT(t,n/2,r/2),o=lT(t,n/2,r/2);return{outer:{x:e.left,y:e.top,w:n,h:r,radius:o},inner:{x:e.left+s.l,y:e.top+s.t,w:n-s.l-s.r,h:r-s.t-s.b,radius:{topLeft:Math.max(0,o.topLeft-Math.max(s.t,s.l)),topRight:Math.max(0,o.topRight-Math.max(s.t,s.r)),bottomLeft:Math.max(0,o.bottomLeft-Math.max(s.b,s.l)),bottomRight:Math.max(0,o.bottomRight-Math.max(s.b,s.r))}}}}function gd(t,e,n,r){const s=e===null,o=n===null,a=t&&!(s&&o)&&um(t,r);return a&&(s||Er(e,a.left,a.right))&&(o||Er(n,a.top,a.bottom))}function dT(t){return t.topLeft||t.topRight||t.bottomLeft||t.bottomRight}function uT(t,e){t.rect(e.x,e.y,e.w,e.h)}function pd(t,e,n={}){const r=t.x!==n.x?-e:0,s=t.y!==n.y?-e:0,o=(t.x+t.w!==n.x+n.w?e:0)-r,i=(t.y+t.h!==n.y+n.h?e:0)-s;return{x:t.x+r,y:t.y+s,w:t.w+o,h:t.h+i,radius:t.radius}}class ka extends Dr{constructor(e){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,e&&Object.assign(this,e)}draw(e){const{inflateAmount:n,options:{borderColor:r,backgroundColor:s}}=this,{inner:o,outer:i}=cT(this),a=dT(i.radius)?aa:uT;e.save(),(i.w!==o.w||i.h!==o.h)&&(e.beginPath(),a(e,pd(i,n,o)),e.clip(),a(e,pd(o,-n,i)),e.fillStyle=r,e.fill("evenodd")),e.beginPath(),a(e,pd(o,n)),e.fillStyle=s,e.fill(),e.restore()}inRange(e,n,r){return gd(this,e,n,r)}inXRange(e,n){return gd(this,e,null,n)}inYRange(e,n){return gd(this,null,e,n)}getCenterPoint(e){const{x:n,y:r,base:s,horizontal:o}=this.getProps(["x","y","base","horizontal"],e);return{x:o?(n+s)/2:n,y:o?r:(r+s)/2}}getRange(e){return e==="x"?this.width/2:this.height/2}}$e(ka,"id","bar"),$e(ka,"defaults",{borderSkipped:"start",borderWidth:0,borderRadius:0,inflateAmount:"auto",pointStyle:void 0}),$e(ka,"defaultRoutes",{backgroundColor:"backgroundColor",borderColor:"borderColor"});const fm=(t,e)=>{let{boxHeight:n=e,boxWidth:r=e}=t;return t.usePointStyle&&(n=Math.min(n,e),r=t.pointStyleWidth||Math.min(r,e)),{boxWidth:r,boxHeight:n,itemHeight:Math.max(e,n)}},fT=(t,e)=>t!==null&&e!==null&&t.datasetIndex===e.datasetIndex&&t.index===e.index;class hm extends Dr{constructor(e){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=e.chart,this.options=e.options,this.ctx=e.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(e,n,r){this.maxWidth=e,this.maxHeight=n,this._margins=r,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const e=this.options.labels||{};let n=gt(e.generateLabels,[this.chart],this)||[];e.filter&&(n=n.filter(r=>e.filter(r,this.chart.data))),e.sort&&(n=n.sort((r,s)=>e.sort(r,s,this.chart.data))),this.options.reverse&&n.reverse(),this.legendItems=n}fit(){const{options:e,ctx:n}=this;if(!e.display){this.width=this.height=0;return}const r=e.labels,s=Bt(r.font),o=s.size,i=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=fm(r,o);let c,d;n.font=s.string,this.isHorizontal()?(c=this.maxWidth,d=this._fitRows(i,o,a,l)+10):(d=this.maxHeight,c=this._fitCols(i,s,a,l)+10),this.width=Math.min(c,e.maxWidth||this.maxWidth),this.height=Math.min(d,e.maxHeight||this.maxHeight)}_fitRows(e,n,r,s){const{ctx:o,maxWidth:i,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],d=s+a;let u=e;o.textAlign="left",o.textBaseline="middle";let f=-1,g=-d;return this.legendItems.forEach((m,p)=>{const y=r+n/2+o.measureText(m.text).width;(p===0||c[c.length-1]+y+2*a>i)&&(u+=d,c[c.length-(p>0?0:1)]=0,g+=d,f++),l[p]={left:0,top:g,row:f,width:y,height:s},c[c.length-1]+=y+a}),u}_fitCols(e,n,r,s){const{ctx:o,maxHeight:i,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],d=i-e;let u=a,f=0,g=0,m=0,p=0;return this.legendItems.forEach((y,b)=>{const{itemWidth:_,itemHeight:x}=hT(r,n,o,y,s);b>0&&g+x+2*a>d&&(u+=f+a,c.push({width:f,height:g}),m+=f+a,p++,f=g=0),l[b]={left:m,top:g,col:p,width:_,height:x},f=Math.max(f,_),g+=x+a}),u+=f,c.push({width:f,height:g}),u}adjustHitBoxes(){if(!this.options.display)return;const e=this._computeTitleHeight(),{legendHitBoxes:n,options:{align:r,labels:{padding:s},rtl:o}}=this,i=hs(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=Ut(r,this.left+s,this.right-this.lineWidths[a]);for(const c of n)a!==c.row&&(a=c.row,l=Ut(r,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+e+s,c.left=i.leftForLtr(i.x(l),c.width),l+=c.width+s}else{let a=0,l=Ut(r,this.top+e+s,this.bottom-this.columnSizes[a].height);for(const c of n)c.col!==a&&(a=c.col,l=Ut(r,this.top+e+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=i.leftForLtr(i.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const e=this.ctx;Zc(e,this),this._draw(),Qc(e)}}_draw(){const{options:e,columnSizes:n,lineWidths:r,ctx:s}=this,{align:o,labels:i}=e,a=xt.color,l=hs(e.rtl,this.left,this.width),c=Bt(i.font),{padding:d}=i,u=c.size,f=u/2;let g;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:m,boxHeight:p,itemHeight:y}=fm(i,u),b=function(k,N,S){if(isNaN(m)||m<=0||isNaN(p)||p<0)return;s.save();const w=Je(S.lineWidth,1);if(s.fillStyle=Je(S.fillStyle,a),s.lineCap=Je(S.lineCap,"butt"),s.lineDashOffset=Je(S.lineDashOffset,0),s.lineJoin=Je(S.lineJoin,"miter"),s.lineWidth=w,s.strokeStyle=Je(S.strokeStyle,a),s.setLineDash(Je(S.lineDash,[])),i.usePointStyle){const M={radius:p*Math.SQRT2/2,pointStyle:S.pointStyle,rotation:S.rotation,borderWidth:w},T=l.xPlus(k,m/2),P=N+f;f0(s,M,T,P,i.pointStyleWidth&&m)}else{const M=N+Math.max((u-p)/2,0),T=l.leftForLtr(k,m),P=ds(S.borderRadius);s.beginPath(),Object.values(P).some(L=>L!==0)?aa(s,{x:T,y:M,w:m,h:p,radius:P}):s.rect(T,M,m,p),s.fill(),w!==0&&s.stroke()}s.restore()},_=function(k,N,S){bo(s,S.text,k,N+y/2,c,{strikethrough:S.hidden,textAlign:l.textAlign(S.textAlign)})},x=this.isHorizontal(),E=this._computeTitleHeight();x?g={x:Ut(o,this.left+d,this.right-r[0]),y:this.top+d+E,line:0}:g={x:this.left+d,y:Ut(o,this.top+E+d,this.bottom-n[0].height),line:0},C0(this.ctx,e.textDirection);const C=y+d;this.legendItems.forEach((k,N)=>{s.strokeStyle=k.fontColor,s.fillStyle=k.fontColor;const S=s.measureText(k.text).width,w=l.textAlign(k.textAlign||(k.textAlign=i.textAlign)),M=m+f+S;let T=g.x,P=g.y;l.setWidth(this.width),x?N>0&&T+M+d>this.right&&(P=g.y+=C,g.line++,T=g.x=Ut(o,this.left+d,this.right-r[g.line])):N>0&&P+C>this.bottom&&(T=g.x=T+n[g.line].width+d,g.line++,P=g.y=Ut(o,this.top+E+d,this.bottom-n[g.line].height));const L=l.x(T);if(b(L,P,k),T=$M(w,T+m+f,x?T+M:this.right,e.rtl),_(l.x(T),P,k),x)g.x+=M+d;else if(typeof k.text!="string"){const $=c.lineHeight;g.y+=gm(k,$)+d}else g.y+=C}),S0(this.ctx,e.textDirection)}drawTitle(){const e=this.options,n=e.title,r=Bt(n.font),s=pn(n.padding);if(!n.display)return;const o=hs(e.rtl,this.left,this.width),i=this.ctx,a=n.position,l=r.size/2,c=s.top+l;let d,u=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),d=this.top+c,u=Ut(e.align,u,this.right-f);else{const m=this.columnSizes.reduce((p,y)=>Math.max(p,y.height),0);d=c+Ut(e.align,this.top,this.bottom-m-e.labels.padding-this._computeTitleHeight())}const g=Ut(a,u,u+f);i.textAlign=o.textAlign(Gc(a)),i.textBaseline="middle",i.strokeStyle=n.color,i.fillStyle=n.color,i.font=r.string,bo(i,n.text,g,d,r)}_computeTitleHeight(){const e=this.options.title,n=Bt(e.font),r=pn(e.padding);return e.display?n.lineHeight+r.height:0}_getLegendItemAt(e,n){let r,s,o;if(Er(e,this.left,this.right)&&Er(n,this.top,this.bottom)){for(o=this.legendHitBoxes,r=0;ro.length>i.length?o:i)),e+n.size/2+r.measureText(s).width}function pT(t,e,n){let r=t;return typeof e.text!="string"&&(r=gm(e,n)),r}function gm(t,e){const n=t.text?t.text.length:0;return e*n}function mT(t,e){return!!((t==="mousemove"||t==="mouseout")&&(e.onHover||e.onLeave)||e.onClick&&(t==="click"||t==="mouseup"))}var pm={id:"legend",_element:hm,start(t,e,n){const r=t.legend=new hm({ctx:t.ctx,options:n,chart:t});mn.configure(t,r,n),mn.addBox(t,r)},stop(t){mn.removeBox(t,t.legend),delete t.legend},beforeUpdate(t,e,n){const r=t.legend;mn.configure(t,r,n),r.options=n},afterUpdate(t){const e=t.legend;e.buildLabels(),e.adjustHitBoxes()},afterEvent(t,e){e.replay||t.legend.handleEvent(e.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(t,e,n){const r=e.datasetIndex,s=n.chart;s.isDatasetVisible(r)?(s.hide(r),e.hidden=!0):(s.show(r),e.hidden=!1)},onHover:null,onLeave:null,labels:{color:t=>t.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:n,pointStyle:r,textAlign:s,color:o,useBorderRadius:i,borderRadius:a}}=t.legend.options;return t._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(n?0:void 0),d=pn(c.borderWidth);return{text:e[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(d.width+d.height)/4,strokeStyle:c.borderColor,pointStyle:r||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:i&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class mm extends Dr{constructor(e){super(),this.chart=e.chart,this.options=e.options,this.ctx=e.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(e,n){const r=this.options;if(this.left=0,this.top=0,!r.display){this.width=this.height=this.right=this.bottom=0;return}this.width=this.right=e,this.height=this.bottom=n;const s=Et(r.text)?r.text.length:1;this._padding=pn(r.padding);const o=s*Bt(r.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const e=this.options.position;return e==="top"||e==="bottom"}_drawArgs(e){const{top:n,left:r,bottom:s,right:o,options:i}=this,a=i.align;let l=0,c,d,u;return this.isHorizontal()?(d=Ut(a,r,o),u=n+e,c=o-r):(i.position==="left"?(d=r+e,u=Ut(a,s,n),l=ct*-.5):(d=o-e,u=Ut(a,n,s),l=ct*.5),c=s-n),{titleX:d,titleY:u,maxWidth:c,rotation:l}}draw(){const e=this.ctx,n=this.options;if(!n.display)return;const r=Bt(n.font),o=r.lineHeight/2+this._padding.top,{titleX:i,titleY:a,maxWidth:l,rotation:c}=this._drawArgs(o);bo(e,n.text,0,0,r,{color:n.color,maxWidth:l,rotation:c,textAlign:Gc(n.align),textBaseline:"middle",translation:[i,a]})}}function bT(t,e){const n=new mm({ctx:t.ctx,options:e,chart:t});mn.configure(t,n,e),mn.addBox(t,n),t.titleBlock=n}var yT={id:"title",_element:mm,start(t,e,n){bT(t,n)},stop(t){const e=t.titleBlock;mn.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,n){const r=t.titleBlock;mn.configure(t,r,n),r.options=n},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const No={average(t){if(!t.length)return!1;let e,n,r=new Set,s=0,o=0;for(e=0,n=t.length;ea+l)/r.size,y:s/o}},nearest(t,e){if(!t.length)return!1;let n=e.x,r=e.y,s=Number.POSITIVE_INFINITY,o,i,a;for(o=0,i=t.length;o-1?t.split(` -`):t}function vT(t,e){const{element:n,datasetIndex:r,index:s}=e,o=t.getDatasetMeta(r).controller,{label:i,value:a}=o.getLabelAndValue(s);return{chart:t,label:i,parsed:o.getParsed(s),raw:t.data.datasets[r].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:r,element:n}}function bm(t,e){const n=t.chart.ctx,{body:r,footer:s,title:o}=t,{boxWidth:i,boxHeight:a}=e,l=Bt(e.bodyFont),c=Bt(e.titleFont),d=Bt(e.footerFont),u=o.length,f=s.length,g=r.length,m=pn(e.padding);let p=m.height,y=0,b=r.reduce((E,C)=>E+C.before.length+C.lines.length+C.after.length,0);if(b+=t.beforeBody.length+t.afterBody.length,u&&(p+=u*c.lineHeight+(u-1)*e.titleSpacing+e.titleMarginBottom),b){const E=e.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=g*E+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(p+=e.footerMarginTop+f*d.lineHeight+(f-1)*e.footerSpacing);let _=0;const x=function(E){y=Math.max(y,n.measureText(E).width+_)};return n.save(),n.font=c.string,at(t.title,x),n.font=l.string,at(t.beforeBody.concat(t.afterBody),x),_=e.displayColors?i+2+e.boxPadding:0,at(r,E=>{at(E.before,x),at(E.lines,x),at(E.after,x)}),_=0,n.font=d.string,at(t.footer,x),n.restore(),y+=m.width,{width:y,height:p}}function xT(t,e){const{y:n,height:r}=e;return nt.height-r/2?"bottom":"center"}function wT(t,e,n,r){const{x:s,width:o}=r,i=n.caretSize+n.caretPadding;if(t==="left"&&s+o+i>e.width||t==="right"&&s-o-i<0)return!0}function kT(t,e,n,r){const{x:s,width:o}=n,{width:i,chartArea:{left:a,right:l}}=t;let c="center";return r==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=i-o/2&&(c="right"),wT(c,t,e,n)&&(c="center"),c}function ym(t,e,n){const r=n.yAlign||e.yAlign||xT(t,n);return{xAlign:n.xAlign||e.xAlign||kT(t,e,n,r),yAlign:r}}function CT(t,e){let{x:n,width:r}=t;return e==="right"?n-=r:e==="center"&&(n-=r/2),n}function ST(t,e,n){let{y:r,height:s}=t;return e==="top"?r+=n:e==="bottom"?r-=s+n:r-=s/2,r}function vm(t,e,n,r){const{caretSize:s,caretPadding:o,cornerRadius:i}=t,{xAlign:a,yAlign:l}=n,c=s+o,{topLeft:d,topRight:u,bottomLeft:f,bottomRight:g}=ds(i);let m=CT(e,a);const p=ST(e,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(d,f)+s:a==="right"&&(m+=Math.max(u,g)+s),{x:en(m,0,r.width-e.width),y:en(p,0,r.height-e.height)}}function Ca(t,e,n){const r=pn(n.padding);return e==="center"?t.x+t.width/2:e==="right"?t.x+t.width-r.right:t.x+r.left}function xm(t){return Hn([],qn(t))}function _T(t,e,n){return us(t,{tooltip:e,tooltipItems:n,type:"tooltip"})}function wm(t,e){const n=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return n?t.override(n):t}const km={beforeTitle:Yn,title(t){if(t.length>0){const e=t[0],n=e.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode==="dataset")return e.dataset.label||"";if(e.label)return e.label;if(r>0&&e.dataIndex"u"?km[e].call(n,r):s}class md extends Dr{constructor(e){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=e.chart,this.options=e.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(e){this.options=e,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const e=this._cachedAnimations;if(e)return e;const n=this.chart,r=this.options.setContext(this.getContext()),s=r.enabled&&n.options.animation&&r.animations,o=new E0(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=_T(this.chart.getContext(),this,this._tooltipItems))}getTitle(e,n){const{callbacks:r}=n,s=tn(r,"beforeTitle",this,e),o=tn(r,"title",this,e),i=tn(r,"afterTitle",this,e);let a=[];return a=Hn(a,qn(s)),a=Hn(a,qn(o)),a=Hn(a,qn(i)),a}getBeforeBody(e,n){return xm(tn(n.callbacks,"beforeBody",this,e))}getBody(e,n){const{callbacks:r}=n,s=[];return at(e,o=>{const i={before:[],lines:[],after:[]},a=wm(r,o);Hn(i.before,qn(tn(a,"beforeLabel",this,o))),Hn(i.lines,tn(a,"label",this,o)),Hn(i.after,qn(tn(a,"afterLabel",this,o))),s.push(i)}),s}getAfterBody(e,n){return xm(tn(n.callbacks,"afterBody",this,e))}getFooter(e,n){const{callbacks:r}=n,s=tn(r,"beforeFooter",this,e),o=tn(r,"footer",this,e),i=tn(r,"afterFooter",this,e);let a=[];return a=Hn(a,qn(s)),a=Hn(a,qn(o)),a=Hn(a,qn(i)),a}_createItems(e){const n=this._active,r=this.chart.data,s=[],o=[],i=[];let a=[],l,c;for(l=0,c=n.length;le.filter(d,u,f,r))),e.itemSort&&(a=a.sort((d,u)=>e.itemSort(d,u,r))),at(a,d=>{const u=wm(e.callbacks,d);s.push(tn(u,"labelColor",this,d)),o.push(tn(u,"labelPointStyle",this,d)),i.push(tn(u,"labelTextColor",this,d))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=i,this.dataPoints=a,a}update(e,n){const r=this.options.setContext(this.getContext()),s=this._active;let o,i=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=No[r.position].call(this,s,this._eventPosition);i=this._createItems(r),this.title=this.getTitle(i,r),this.beforeBody=this.getBeforeBody(i,r),this.body=this.getBody(i,r),this.afterBody=this.getAfterBody(i,r),this.footer=this.getFooter(i,r);const l=this._size=bm(this,r),c=Object.assign({},a,l),d=ym(this.chart,r,c),u=vm(r,c,d,this.chart);this.xAlign=d.xAlign,this.yAlign=d.yAlign,o={opacity:1,x:u.x,y:u.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=i,this.$context=void 0,o&&this._resolveAnimations().update(this,o),e&&r.external&&r.external.call(this,{chart:this.chart,tooltip:this,replay:n})}drawCaret(e,n,r,s){const o=this.getCaretPosition(e,r,s);n.lineTo(o.x1,o.y1),n.lineTo(o.x2,o.y2),n.lineTo(o.x3,o.y3)}getCaretPosition(e,n,r){const{xAlign:s,yAlign:o}=this,{caretSize:i,cornerRadius:a}=r,{topLeft:l,topRight:c,bottomLeft:d,bottomRight:u}=ds(a),{x:f,y:g}=e,{width:m,height:p}=n;let y,b,_,x,E,C;return o==="center"?(E=g+p/2,s==="left"?(y=f,b=y-i,x=E+i,C=E-i):(y=f+m,b=y+i,x=E-i,C=E+i),_=y):(s==="left"?b=f+Math.max(l,d)+i:s==="right"?b=f+m-Math.max(c,u)-i:b=this.caretX,o==="top"?(x=g,E=x-i,y=b-i,_=b+i):(x=g+p,E=x+i,y=b+i,_=b-i),C=x),{x1:y,x2:b,x3:_,y1:x,y2:E,y3:C}}drawTitle(e,n,r){const s=this.title,o=s.length;let i,a,l;if(o){const c=hs(r.rtl,this.x,this.width);for(e.x=Ca(this,r.titleAlign,r),n.textAlign=c.textAlign(r.titleAlign),n.textBaseline="middle",i=Bt(r.titleFont),a=r.titleSpacing,n.fillStyle=r.titleColor,n.font=i.string,l=0;l_!==0)?(e.beginPath(),e.fillStyle=o.multiKeyBackground,aa(e,{x:p,y:m,w:c,h:l,radius:b}),e.fill(),e.stroke(),e.fillStyle=i.backgroundColor,e.beginPath(),aa(e,{x:y,y:m+1,w:c-2,h:l-2,radius:b}),e.fill()):(e.fillStyle=o.multiKeyBackground,e.fillRect(p,m,c,l),e.strokeRect(p,m,c,l),e.fillStyle=i.backgroundColor,e.fillRect(y,m+1,c-2,l-2))}e.fillStyle=this.labelTextColors[r]}drawBody(e,n,r){const{body:s}=this,{bodySpacing:o,bodyAlign:i,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:d}=r,u=Bt(r.bodyFont);let f=u.lineHeight,g=0;const m=hs(r.rtl,this.x,this.width),p=function(S){n.fillText(S,m.x(e.x+g),e.y+f/2),e.y+=f+o},y=m.textAlign(i);let b,_,x,E,C,k,N;for(n.textAlign=i,n.textBaseline="middle",n.font=u.string,e.x=Ca(this,y,r),n.fillStyle=r.bodyColor,at(this.beforeBody,p),g=a&&y!=="right"?i==="center"?c/2+d:c+2+d:0,E=0,k=s.length;E0&&n.stroke()}_updateAnimationTarget(e){const n=this.chart,r=this.$animations,s=r&&r.x,o=r&&r.y;if(s||o){const i=No[e.position].call(this,this._active,this._eventPosition);if(!i)return;const a=this._size=bm(this,e),l=Object.assign({},i,this._size),c=ym(n,e,l),d=vm(e,l,c,n);(s._to!==d.x||o._to!==d.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,d))}}_willRender(){return!!this.opacity}draw(e){const n=this.options.setContext(this.getContext());let r=this.opacity;if(!r)return;this._updateAnimationTarget(n);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};r=Math.abs(r)<.001?0:r;const i=pn(n.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;n.enabled&&a&&(e.save(),e.globalAlpha=r,this.drawBackground(o,e,s,n),C0(e,n.textDirection),o.y+=i.top,this.drawTitle(o,e,n),this.drawBody(o,e,n),this.drawFooter(o,e,n),S0(e,n.textDirection),e.restore())}getActiveElements(){return this._active||[]}setActiveElements(e,n){const r=this._active,s=e.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!ea(r,s),i=this._positionChanged(s,n);(o||i)&&(this._active=s,this._eventPosition=n,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(e,n,r=!0){if(n&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],i=this._getActiveElements(e,o,n,r),a=this._positionChanged(i,e),l=n||!ea(i,o)||a;return l&&(this._active=i,(s.enabled||s.external)&&(this._eventPosition={x:e.x,y:e.y},this.update(!0,n))),l}_getActiveElements(e,n,r,s){const o=this.options;if(e.type==="mouseout")return[];if(!s)return n.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const i=this.chart.getElementsAtEventForMode(e,o.mode,o,r);return o.reverse&&i.reverse(),i}_positionChanged(e,n){const{caretX:r,caretY:s,options:o}=this,i=No[o.position].call(this,e,n);return i!==!1&&(r!==i.x||s!==i.y)}}$e(md,"positioners",No);var Cm={id:"tooltip",_element:md,positioners:No,afterInit(t,e,n){n&&(t.tooltip=new md({chart:t,options:n}))},beforeUpdate(t,e,n){t.tooltip&&t.tooltip.initialize(n)},reset(t,e,n){t.tooltip&&t.tooltip.initialize(n)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const n={tooltip:e};if(t.notifyPlugins("beforeTooltipDraw",{...n,cancelable:!0})===!1)return;e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",n)}},afterEvent(t,e){if(t.tooltip){const n=e.replay;t.tooltip.handleEvent(e.event,n,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:km},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:t=>t!=="filter"&&t!=="itemSort"&&t!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const ET=(t,e,n,r)=>(typeof e=="string"?(n=t.push(e)-1,r.unshift({index:n,label:e})):isNaN(e)&&(n=null),n);function MT(t,e,n,r){const s=t.indexOf(e);if(s===-1)return ET(t,e,n,r);const o=t.lastIndexOf(e);return s!==o?n:s}const NT=(t,e)=>t===null?null:en(Math.round(t),0,e);function Sm(t){const e=this.getLabels();return t>=0&&tn.length-1?null:this.getPixelForValue(n[e].value)}getValueForPixel(e){return Math.round(this._startValue+this.getDecimalForPixel(e)*this._valueRange)}getBasePixel(){return this.bottom}}$e(bd,"id","category"),$e(bd,"defaults",{ticks:{callback:Sm}});function OT(t,e){const n=[],{bounds:s,step:o,min:i,max:a,precision:l,count:c,maxTicks:d,maxDigits:u,includeBounds:f}=t,g=o||1,m=d-1,{min:p,max:y}=e,b=!lt(i),_=!lt(a),x=!lt(c),E=(y-p)/(u+1);let C=qp((y-p)/m/g)*g,k,N,S,w;if(C<1e-14&&!b&&!_)return[{value:p},{value:y}];w=Math.ceil(y/C)-Math.floor(p/C),w>m&&(C=qp(w*C/m/g)*g),lt(l)||(k=Math.pow(10,l),C=Math.ceil(C*k)/k),s==="ticks"?(N=Math.floor(p/C)*C,S=Math.ceil(y/C)*C):(N=p,S=y),b&&_&&o&&MM((a-i)/o,C/1e3)?(w=Math.round(Math.min((a-i)/C,d)),C=(a-i)/w,N=i,S=a):x?(N=b?i:N,S=_?a:S,w=c-1,C=(S-N)/w):(w=(S-N)/C,ra(w,Math.round(w),C/1e3)?w=Math.round(w):w=Math.ceil(w));const M=Math.max(Zp(C),Zp(N));k=Math.pow(10,lt(l)?M:l),N=Math.round(N*k)/k,S=Math.round(S*k)/k;let T=0;for(b&&(f&&N!==i?(n.push({value:i}),Na)break;n.push({value:P})}return _&&f&&S!==a?n.length&&ra(n[n.length-1].value,a,_m(a,E,t))?n[n.length-1].value=a:n.push({value:a}):(!_||S===a)&&n.push({value:S}),n}function _m(t,e,{horizontal:n,minRotation:r}){const s=Kn(r),o=(n?Math.sin(s):Math.cos(s))||.001,i=.75*e*(""+t).length;return Math.min(e/o,i)}class TT extends ps{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(e,n){return lt(e)||(typeof e=="number"||e instanceof Number)&&!isFinite(+e)?null:+e}handleTickRangeOptions(){const{beginAtZero:e}=this.options,{minDefined:n,maxDefined:r}=this.getUserBounds();let{min:s,max:o}=this;const i=l=>s=n?s:l,a=l=>o=r?o:l;if(e){const l=cr(s),c=cr(o);l<0&&c<0?a(0):l>0&&c>0&&i(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),e||i(s-l)}this.min=s,this.max=o}getTickLimit(){const e=this.options.ticks;let{maxTicksLimit:n,stepSize:r}=e,s;return r?(s=Math.ceil(this.max/r)-Math.floor(this.min/r)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${r} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),n=n||11),n&&(s=Math.min(n,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const e=this.options,n=e.ticks;let r=this.getTickLimit();r=Math.max(2,r);const s={maxTicks:r,bounds:e.bounds,min:e.min,max:e.max,precision:n.precision,step:n.stepSize,count:n.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:n.minRotation||0,includeBounds:n.includeBounds!==!1},o=this._range||this,i=OT(s,o);return e.bounds==="ticks"&&NM(i,this,"value"),e.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}configure(){const e=this.ticks;let n=this.min,r=this.max;if(super.configure(),this.options.offset&&e.length){const s=(r-n)/Math.max(e.length-1,1)/2;n-=s,r+=s}this._startValue=n,this._endValue=r,this._valueRange=r-n}getLabelForValue(e){return Kc(e,this.chart.options.locale,this.options.ticks.format)}}class yd extends TT{determineDataLimits(){const{min:e,max:n}=this.getMinMax(!0);this.min=gn(e)?e:0,this.max=gn(n)?n:1,this.handleTickRangeOptions()}computeTickLimit(){const e=this.isHorizontal(),n=e?this.width:this.height,r=Kn(this.options.ticks.minRotation),s=(e?Math.sin(r):Math.cos(r))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(n/Math.min(40,o.lineHeight/s))}getPixelForValue(e){return e===null?NaN:this.getPixelForDecimal((e-this._startValue)/this._valueRange)}getValueForPixel(e){return this._startValue+this.getDecimalForPixel(e)*this._valueRange}}$e(yd,"id","linear"),$e(yd,"defaults",{ticks:{callback:l0.formatters.numeric}});const Sa={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},nn=Object.keys(Sa);function Em(t,e){return t-e}function Mm(t,e){if(lt(e))return null;const n=t._adapter,{parser:r,round:s,isoWeekday:o}=t._parseOpts;let i=e;return typeof r=="function"&&(i=r(i)),gn(i)||(i=typeof r=="string"?n.parse(i,r):n.parse(i)),i===null?null:(s&&(i=s==="week"&&(sa(o)||o===!0)?n.startOf(i,"isoWeek",o):n.startOf(i,s)),+i)}function Nm(t,e,n,r){const s=nn.length;for(let o=nn.indexOf(t);o=nn.indexOf(n);o--){const i=nn[o];if(Sa[i].common&&t._adapter.diff(s,r,i)>=e-1)return i}return nn[n?nn.indexOf(n):0]}function DT(t){for(let e=nn.indexOf(t)+1,n=nn.length;e=e?n[r]:n[s];t[o]=!0}}function IT(t,e,n,r){const s=t._adapter,o=+s.startOf(e[0].value,r),i=e[e.length-1].value;let a,l;for(a=o;a<=i;a=+s.add(a,1,r))l=n[a],l>=0&&(e[l].major=!0);return e}function Tm(t,e,n){const r=[],s={},o=e.length;let i,a;for(i=0;i+e.value))}initOffsets(e=[]){let n=0,r=0,s,o;this.options.offset&&e.length&&(s=this.getDecimalForValue(e[0]),e.length===1?n=1-s:n=(this.getDecimalForValue(e[1])-s)/2,o=this.getDecimalForValue(e[e.length-1]),e.length===1?r=o:r=(o-this.getDecimalForValue(e[e.length-2]))/2);const i=e.length<3?.5:.25;n=en(n,0,i),r=en(r,0,i),this._offsets={start:n,end:r,factor:1/(n+1+r)}}_generate(){const e=this._adapter,n=this.min,r=this.max,s=this.options,o=s.time,i=o.unit||Nm(o.minUnit,n,r,this._getLabelCapacity(n)),a=Je(s.ticks.stepSize,1),l=i==="week"?o.isoWeekday:!1,c=sa(l)||l===!0,d={};let u=n,f,g;if(c&&(u=+e.startOf(u,"isoWeek",l)),u=+e.startOf(u,c?"day":i),e.diff(r,n,i)>1e5*a)throw new Error(n+" and "+r+" are too far apart with stepSize of "+a+" "+i);const m=s.ticks.source==="data"&&this.getDataTimestamps();for(f=u,g=0;f+p)}getLabelForValue(e){const n=this._adapter,r=this.options.time;return r.tooltipFormat?n.format(e,r.tooltipFormat):n.format(e,r.displayFormats.datetime)}format(e,n){const s=this.options.time.displayFormats,o=this._unit,i=n||s[o];return this._adapter.format(e,i)}_tickFormatFunction(e,n,r,s){const o=this.options,i=o.ticks.callback;if(i)return gt(i,[e,n,r],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,d=l&&a[l],u=c&&a[c],f=r[n],g=c&&u&&f&&f.major;return this._adapter.format(e,s||(g?u:d))}generateTickLabels(e){let n,r,s;for(n=0,r=e.length;n0?a:1}getDataTimestamps(){let e=this._cache.data||[],n,r;if(e.length)return e;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(n=0,r=s.length;n=t[r].pos&&e<=t[s].pos&&({lo:r,hi:s}=Wc(t,"pos",e)),{pos:o,time:a}=t[r],{pos:i,time:l}=t[s]):(e>=t[r].time&&e<=t[s].time&&({lo:r,hi:s}=Wc(t,"time",e)),{time:o,pos:a}=t[r],{time:i,pos:l}=t[s]);const c=i-o;return c?a+(l-a)*(e-o)/c:a}class Am extends _a{constructor(e){super(e),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const e=this._getTimestampsForTable(),n=this._table=this.buildLookupTable(e);this._minPos=Ea(n,this.min),this._tableRange=Ea(n,this.max)-this._minPos,super.initOffsets(e)}buildLookupTable(e){const{min:n,max:r}=this,s=[],o=[];let i,a,l,c,d;for(i=0,a=e.length;i=n&&c<=r&&s.push(c);if(s.length<2)return[{time:n,pos:0},{time:r,pos:1}];for(i=0,a=s.length;is-o)}_getTimestampsForTable(){let e=this._cache.all||[];if(e.length)return e;const n=this.getDataTimestamps(),r=this.getLabelTimestamps();return n.length&&r.length?e=this.normalize(n.concat(r)):e=n.length?n:r,e=this._cache.all=e,e}getDecimalForValue(e){return(Ea(this._table,e)-this._minPos)/this._tableRange}getValueForPixel(e){const n=this._offsets,r=this.getDecimalForPixel(e)/n.factor-n.end;return Ea(this._table,r*this._tableRange+this._minPos,!0)}}$e(Am,"id","timeseries"),$e(Am,"defaults",_a.defaults);const Dm="label";function Im(t,e){typeof t=="function"?t(e):t&&(t.current=e)}function LT(t,e){const n=t.options;n&&e&&Object.assign(n,e)}function Lm(t,e){t.labels=e}function Pm(t,e,n=Dm){const r=[];t.datasets=e.map(s=>{const o=t.datasets.find(i=>i[n]===s[n]);return!o||!s.data||r.includes(o)?{...s}:(r.push(o),Object.assign(o,s),o)})}function PT(t,e=Dm){const n={labels:[],datasets:[]};return Lm(n,t.labels),Pm(n,t.datasets,e),n}function $T(t,e){const{height:n=150,width:r=300,redraw:s=!1,datasetIdKey:o,type:i,data:a,options:l,plugins:c=[],fallbackContent:d,updateMode:u,...f}=t,g=v.useRef(null),m=v.useRef(null),p=()=>{g.current&&(m.current=new Eo(g.current,{type:i,data:PT(a,o),options:l&&{...l},plugins:c}),Im(e,m.current))},y=()=>{Im(e,null),m.current&&(m.current.destroy(),m.current=null)};return v.useEffect(()=>{!s&&m.current&&l&<(m.current,l)},[s,l]),v.useEffect(()=>{!s&&m.current&&Lm(m.current.config.data,a.labels)},[s,a.labels]),v.useEffect(()=>{!s&&m.current&&a.datasets&&Pm(m.current.config.data,a.datasets,o)},[s,a.datasets]),v.useEffect(()=>{m.current&&(s?(y(),setTimeout(p)):m.current.update(u))},[s,l,a.labels,a.datasets,u]),v.useEffect(()=>{m.current&&(y(),setTimeout(p))},[i]),v.useEffect(()=>(p(),()=>y()),[]),h.jsx("canvas",{ref:g,role:"img",height:n,width:r,...f,children:d})}const FT=v.forwardRef($T);function $m(t,e){return Eo.register(e),v.forwardRef((n,r)=>h.jsx(FT,{...n,ref:r,type:t}))}const zT=$m("bar",fa),VT=$m("doughnut",vo);Eo.register(bd,yd,ka,yT,Cm,pm);const HT=({labels:t,datasets:e,height:n=200,showLegend:r=!1,showGrid:s=!1,showYGrid:o=!1,showYAxis:i=!1,className:a="",id:l,editorAttrs:c})=>{const d=v.useMemo(()=>({labels:t??[],datasets:(e??[]).map((m,p)=>({label:m.label,data:m.data,backgroundColor:m.backgroundColor||(p===0?"#7C3AED":"#DDD6FE"),borderRadius:m.borderRadius??4,barThickness:16,maxBarThickness:20,yAxisID:m.yAxisID??"y"}))}),[t,e]),u=v.useMemo(()=>(e??[]).some(m=>m.yAxisID==="y1"),[e]),f=v.useMemo(()=>({responsive:!0,maintainAspectRatio:!1,animation:{duration:750,easing:"easeOutQuart"},animations:{x:{duration:0},y:{from:m=>{if(m.type==="data")return m.chart.scales.y.getPixelForValue(0)}}},transitions:{active:{animation:{duration:200}},resize:{animation:{duration:0}}},plugins:{legend:{display:r,position:"top"},tooltip:{backgroundColor:"#1e293b",titleColor:"#fff",bodyColor:"#fff",padding:12,cornerRadius:8}},scales:{x:{grid:{display:s},border:{display:!1},ticks:{color:"#94a3b8",font:{size:11}}},y:{type:"linear",position:"left",grid:{display:o,color:"#e2e8f0"},border:{display:!1},ticks:{display:i,color:"#94a3b8",font:{size:11},padding:8},beginAtZero:!0,grace:"5%"},...u?{y1:{type:"linear",position:"right",grid:{display:!1},border:{display:!1},ticks:{display:i,color:"#94a3b8",font:{size:11},padding:8},beginAtZero:!0,grace:"5%"}}:{}}}),[r,s,o,i,u]),g=typeof n=="number"?n:/^\d+$/.test(String(n))?`${n}px`:n;return h.jsx("div",{className:`bar-chart-container ${a}`,style:{height:g},id:l,...c,children:h.jsx(zT,{data:d,options:f})})},UT=v.memo(HT,(t,e)=>JSON.stringify(t.labels)===JSON.stringify(e.labels)&&JSON.stringify(t.datasets)===JSON.stringify(e.datasets)&&t.height===e.height&&t.showLegend===e.showLegend&&t.showGrid===e.showGrid&&t.showYGrid===e.showYGrid&&t.showYAxis===e.showYAxis&&t.className===e.className&&t.id===e.id&&t.editorAttrs===e.editorAttrs);Eo.register(Mo,Cm,pm);const BT=({data:t,centerLabel:e,centerValue:n,size:r=200,cutout:s="70%",showLegend:o=!1,className:i="",id:a,editorAttrs:l})=>{const c=v.useMemo(()=>({labels:(t??[]).map(f=>f.name),datasets:[{data:(t??[]).map(f=>f.value),backgroundColor:(t??[]).map(f=>f.color),borderWidth:0,hoverOffset:4}]}),[t]),d=v.useMemo(()=>({responsive:!0,maintainAspectRatio:!1,cutout:s,plugins:{legend:{display:o},tooltip:{backgroundColor:"#1e293b",titleColor:"#fff",bodyColor:"#fff",padding:12,cornerRadius:8,callbacks:{label:f=>` $${f.parsed.toLocaleString()}`}}}}),[s,o]),u=typeof r=="number"?r:/^\d+$/.test(String(r))?`${r}px`:r;return h.jsx("div",{className:`donut-chart-wrapper ${i}`,id:a,...l,children:h.jsxs("div",{className:"relative",style:{width:u,height:u},children:[h.jsx(VT,{data:c,options:d}),(e||n)&&h.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center pointer-events-none",children:[e&&h.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:e}),n&&h.jsx("span",{className:"text-lg font-bold text-slate-900 dark:text-white",children:n})]})]})})},jT=({id:t,className:e,style:n,children:r,editorAttrs:s})=>h.jsx("div",{id:t,className:e,style:n,...s,children:r}),WT=({children:t,cols:e=1,responsive:n,gap:r,rowGap:s,colGap:o,autoRows:i,autoCols:a,flow:l="row",className:c="",style:d,onClick:u,id:f,editorAttrs:g})=>{const m=["grid"];if(e&&m.push(`grid-cols-${e}`),n&&(n.sm&&m.push(`sm:grid-cols-${n.sm}`),n.md&&m.push(`md:grid-cols-${n.md}`),n.lg&&m.push(`lg:grid-cols-${n.lg}`),n.xl&&m.push(`xl:grid-cols-${n.xl}`),n["2xl"]&&m.push(`2xl:grid-cols-${n["2xl"]}`)),r!==void 0&&r>0&&m.push(`gap-${r}`),s!==void 0&&s>0&&m.push(`gap-y-${s}`),o!==void 0&&o>0&&m.push(`gap-x-${o}`),i){const y={auto:"auto-rows-auto",min:"auto-rows-min",max:"auto-rows-max",fr:"auto-rows-fr"};m.push(y[i])}if(a){const y={auto:"auto-cols-auto",min:"auto-cols-min",max:"auto-cols-max",fr:"auto-cols-fr"};m.push(y[a])}const p={row:"grid-flow-row",col:"grid-flow-col",dense:"grid-flow-dense","row-dense":"grid-flow-row-dense","col-dense":"grid-flow-col-dense"};return m.push(p[l]),c&&m.push(c),h.jsx(I,{className:m.join(" "),style:d,onClick:u,id:f,...g,children:t})},GT=({children:t,direction:e="row",justify:n="start",align:r="stretch",wrap:s="nowrap",gap:o=0,grow:i=!1,shrink:a=!0,className:l="",style:c,onClick:d,id:u,editorAttrs:f})=>{const g=["flex"],m={row:"flex-row","row-reverse":"flex-row-reverse",col:"flex-col","col-reverse":"flex-col-reverse"};g.push(m[e]);const p={wrap:"flex-wrap",nowrap:"flex-nowrap","wrap-reverse":"flex-wrap-reverse"};g.push(p[s]);const y={start:"justify-start",end:"justify-end",center:"justify-center",between:"justify-between",around:"justify-around",evenly:"justify-evenly"};g.push(y[n]);const b={start:"items-start",end:"items-end",center:"items-center",baseline:"items-baseline",stretch:"items-stretch"};return g.push(b[r]),o>0&&g.push(`gap-${o}`),i===!0?g.push("flex-grow"):typeof i=="number"&&i>0&&g.push(`flex-grow-${i}`),a===!1?g.push("shrink-0"):a===!0?g.push("flex-shrink"):typeof a=="number"&&a>0&&g.push(`flex-shrink-${a}`),l&&g.push(l),h.jsx(I,{className:g.join(" "),style:c,onClick:d,id:u,...f,children:t})},YT=({title:t,subtitle:e,children:n,padding:r="none",background:s="none",maxWidth:o,centered:i=!1,border:a=!1,shadow:l="none",rounded:c=!1,className:d="",style:u,onClick:f,id:g,editorAttrs:m})=>{const p=[],y={none:"",sm:"p-2",md:"p-4",lg:"p-6",xl:"p-8"};y[r]&&p.push(y[r]);const b={none:"",white:"bg-white dark:bg-gray-700",gray:"bg-gray-50 dark:bg-gray-800",primary:"bg-blue-50 dark:bg-blue-900/20",secondary:"bg-gray-50 dark:bg-gray-800"};if(s!=="none"&&p.push(b[s]),o){const x={sm:"max-w-sm",md:"max-w-md",lg:"max-w-lg",xl:"max-w-xl","2xl":"max-w-2xl","4xl":"max-w-4xl","6xl":"max-w-6xl","7xl":"max-w-7xl",full:"max-w-full"};p.push(x[o])}i&&p.push("mx-auto"),a&&p.push("border","border-gray-200","dark:border-gray-700");const _={none:"",sm:"shadow-sm",md:"shadow-md",lg:"shadow-lg",xl:"shadow-xl"};return l!=="none"&&p.push(_[l]),c&&p.push("rounded-lg"),d&&p.push(d),h.jsxs(Nd,{className:p.join(" "),style:u,onClick:f,id:g,...m,children:[(t||e)&&h.jsxs("div",{className:"mb-4",children:[t&&h.jsx("h2",{className:"text-2xl font-bold text-gray-900 dark:text-white",children:t}),e&&h.jsx("p",{className:"mt-1 text-sm text-gray-600 dark:text-gray-400",children:e})]}),n]})},KT=({leftWidth:t="250px",rightWidth:e="300px",leftSlot:n,centerSlot:r,rightSlot:s,className:o="",style:i,id:a,editorAttrs:l})=>{const c=`flex flex-row w-full h-full ${o}`.trim(),d={width:t,flexShrink:0},u={flex:1,minWidth:0},f={width:e,flexShrink:0};return h.jsxs(I,{className:c,style:i,id:a,...l,children:[h.jsx(I,{className:"flex flex-col",style:d,children:n}),h.jsx(I,{className:"flex flex-col",style:u,children:r}),h.jsx(I,{className:"flex flex-col",style:f,children:s})]})},XT={language:"json",theme:"vs-dark",options:{automaticLayout:!0,minimap:{enabled:!0},scrollBeyondLastLine:!1,fontSize:14,tabSize:2,formatOnPaste:!0,formatOnType:!0,wordWrap:"on",folding:!0,lineNumbers:"on",renderWhitespace:"selection"}},Fm=[{uri:`${window.location.origin}/schemas/layout.json`,fileMatch:["*layout*.json"],schema:{type:"object",properties:{version:{type:"string"},endpoint:{type:"string"},components:{type:"array"}},required:["version","endpoint","components"]}}],qT={"vs/nls":{availableLanguages:{"*":"ko"}}};function ZT(t){t.languages.json.jsonDefaults.setDiagnosticsOptions({validate:!0,schemas:Fm,enableSchemaRequest:!0,schemaValidation:"error",schemaRequest:"error"})}const QT={identifier:"sirsoft-admin_basic",vendor:"sirsoft",name:{ko:"Admin Basic",en:"Admin Basic"},version:"1.0.4",license:"MIT",description:{ko:"그누보드7 기본 관리자 템플릿",en:"Gnuboard7 default admin template"},type:"admin",locales:["ko","en"],author:{name:"sirsoft",email:"contact@sirsoft.com",url:"https://sirsoft.com"},release_date:"2026-05-15",g7_version:">=7.0.6",dependencies:{modules:{},plugins:{}},assets:{css:["assets/css/main.css","assets/css/ui-system.css"],js:["dist/components.js"],fonts:["assets/fonts/"],images:["assets/images/"]},components:{basic:["a","button","checkbox","div","form","h1","h2","h3","hr","icon","img","input","label","li","nav","p","section","select","span","svg","table","tbody","td","textarea","th","thead","tr","ul"],composite:["action-menu","admin-footer","admin-header","admin-sidebar","alert-dialog","breadcrumb","card","confirm-dialog","data-grid","dialog","dropdown","empty-state","filter-group","icon-button","loading-spinner","modal","page-header","pagination","product-card","search-bar","stat-card","status-badge","tab-navigation","template-card","toast"],layout:["container","flex","grid","section"]},preview:{thumbnail:"preview/thumbnail.png",screenshots:["preview/screenshot1.png","preview/screenshot2.png"]},error_config:{layouts:{401:"errors/401",403:"errors/403",404:"errors/404",500:"errors/500",503:"errors/503",maintenance:"errors/maintenance"}},github_url:"https://github.com/gnuboard/g7-template-sirsoft-admin_basic",github_changelog_url:"https://github.com/gnuboard/g7-template-sirsoft-admin_basic/blob/main/CHANGELOG.md",externals:[{id:"fontawesome",type:"style",url:"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",preconnect:"https://cdnjs.cloudflare.com"},{id:"pretendard",type:"webfont",url:"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css",preconnect:"https://cdn.jsdelivr.net",crossorigin:"anonymous"},{id:"flag-icons",type:"style",url:"https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css"}]},Zn=window.G7Core?.createLogger?.("Handler:SetTheme")??{log:(...t)=>console.log("[Handler:SetTheme]",...t),warn:(...t)=>console.warn("[Handler:SetTheme]",...t),error:(...t)=>console.error("[Handler:SetTheme]",...t)},vd=["auto","light","dark"],zm="g7_color_scheme",JT=t=>t==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":t,Ma=t=>{const e=JT(t);document.documentElement.setAttribute("data-theme",e),e==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")};function RT(){try{const t=localStorage.getItem(zm),e=t&&vd.includes(t)?t:"auto";Ma(e);try{window.G7Core?.state?.setGlobal?.({theme:e})}catch(n){Zn.warn("Failed to set global theme state:",n)}Zn.log("Initial theme applied:",e)}catch(t){Zn.warn("Failed to load initial theme:",t),Ma("auto")}}async function e5(t,e){const n=t?.target;if(n&&typeof n=="string"&&vd.includes(n)){Ma(n);try{window.G7Core?.state?.setGlobal?.({theme:n})}catch(r){Zn.warn("Failed to set global theme state:",r)}return}RT()}async function t5(t,e){let n;if(t?.target&&typeof t.target=="string"&&!t.target.includes("{{")&&(n=t.target),!n||typeof n!="string"){Zn.warn("Invalid theme:",n);return}if(!vd.includes(n)){Zn.warn("Unsupported theme:",n);return}const r=n;try{localStorage.setItem(zm,r)}catch(s){Zn.error("Failed to save theme to localStorage:",s);return}Ma(r);try{window.G7Core?.state?.setGlobal?.({theme:r})}catch(s){Zn.warn("Failed to set global theme state:",s)}Zn.log("Theme changed to:",r)}const bs=window.G7Core?.createLogger?.("Handler:InitMenu")??{log:(...t)=>console.log("[Handler:InitMenu]",...t),warn:(...t)=>console.warn("[Handler:InitMenu]",...t),error:(...t)=>console.error("[Handler:InitMenu]",...t)};function Vm(t,e){for(const n of t){if(n.slug===e)return n;if(n.children&&n.children.length>0){const r=Vm(n.children,e);if(r)return r}}return null}function Hm(t){return new URLSearchParams(window.location.search).get(t)}function n5(t){return window.G7Core?.state?.getDataSource?.(t)}function r5(t){const e=[t,t?.data,t?.data?.data];for(const n of e)if(Array.isArray(n)&&n.length>0)return n;return null}async function s5(t,e=30,n=100){for(let r=0;rsetTimeout(o,n))}return null}async function o5(t){const e=window.G7Core;try{const r=(await e?.api?.get?.(`/api/admin/menus/${t.id}`))?.data;if(r&&typeof r=="object"&&r.id===t.id)return r}catch(n){bs.warn("[initMenuFromUrl] Failed to hydrate menu detail:",n)}return t}async function i5(t,e){const n=window.G7Core,r=Hm("menu"),s=Hm("mode");if(!r){bs.log("[initMenuFromUrl] No menu parameter in URL");return}if(!n?.state?.set){bs.warn("[initMenuFromUrl] G7Core.state.set not available");return}const o=await s5("menus");if(!o){bs.warn("[initMenuFromUrl] menus data not available after waiting");return}const i=Vm(o,r);if(!i){bs.warn("[initMenuFromUrl] Menu not found with slug:",r);return}const a=await o5(i),l=s==="edit"?"edit":"view";n.state.set({selectedMenuId:a.id,selectedMenu:a,panelMode:l}),l==="edit"&&n.state.set({formData:{name:a.name,slug:a.slug,url:a.url,icon:a.icon,parent_id:a.parent_id,extension_type:a.extension_type,extension_identifier:a.extension_identifier,is_active:a.is_active,roles:(a.roles??[]).map(c=>c.id)}}),bs.log("[initMenuFromUrl] Menu initialized from URL:",{slug:r,mode:l,menuId:i.id})}const qt=window.G7Core?.createLogger?.("Handler:FilterVisibility")??{log:(...t)=>console.log("[Handler:FilterVisibility]",...t),warn:(...t)=>console.warn("[Handler:FilterVisibility]",...t),error:(...t)=>console.error("[Handler:FilterVisibility]",...t)},Na="g7_filter_visibility_";async function a5(t,e){const{storageKey:n,defaultFilters:r=[],stateKey:s="visibleFilters"}=t?.params||{};if(!n){qt.warn("[initFilterVisibility] storageKey is required");return}const o=`${Na}${n}`;try{const i=localStorage.getItem(o);let a;if(i){const l=JSON.parse(i);Array.isArray(l)?(a=l,qt.log(`[initFilterVisibility] Loaded from localStorage: ${a.join(", ")}`)):(a=r,qt.warn("[initFilterVisibility] Invalid saved data format, using defaults"))}else a=r,qt.log(`[initFilterVisibility] No saved data, using defaults: ${r.join(", ")}`);e?.setState&&e.setState({[s]:a})}catch(i){qt.error("[initFilterVisibility] Failed to load filter visibility:",i),e?.setState&&e.setState({[s]:r})}}async function l5(t,e){const{storageKey:n,filters:r}=t?.params||{};if(!n){qt.warn("[saveFilterVisibility] storageKey is required");return}if(!Array.isArray(r)){qt.warn("[saveFilterVisibility] filters must be an array");return}const s=`${Na}${n}`;try{localStorage.setItem(s,JSON.stringify(r)),qt.log(`[saveFilterVisibility] Saved to localStorage: ${r.join(", ")}`)}catch(o){qt.error("[saveFilterVisibility] Failed to save filter visibility:",o)}}async function c5(t,e){const{storageKey:n,filterId:r,stateKey:s="visibleFilters"}=t?.params||{};if(!n||!r){qt.warn("[toggleFilterVisibility] storageKey and filterId are required");return}const o=e?.state?.[s]||[];let i;o.includes(r)?i=o.filter(l=>l!==r):i=[...o,r],e?.setState&&e.setState({[s]:i});const a=`${Na}${n}`;try{localStorage.setItem(a,JSON.stringify(i)),qt.log(`[toggleFilterVisibility] Toggled ${r}: ${i.join(", ")}`)}catch(l){qt.error("[toggleFilterVisibility] Failed to save:",l)}}async function d5(t,e){const{storageKey:n,defaultFilters:r=[],stateKey:s="visibleFilters"}=t?.params||{};if(!n){qt.warn("[resetFilterVisibility] storageKey is required");return}const o=`${Na}${n}`;try{localStorage.setItem(o,JSON.stringify(r)),e?.setState&&e.setState({[s]:r}),qt.log(`[resetFilterVisibility] Reset to defaults: ${r.join(", ")}`)}catch(i){qt.error("[resetFilterVisibility] Failed to reset:",i)}}const ys=window.G7Core?.createLogger?.("Handler:MultilingualTag")??{log:(...t)=>console.log("[Handler:MultilingualTag]",...t),warn:(...t)=>console.warn("[Handler:MultilingualTag]",...t),error:(...t)=>console.error("[Handler:MultilingualTag]",...t)};async function u5(t,e){const n=window.G7Core;if(!n){ys.error("G7Core not available");return}const r=n.state?.getGlobal?.()?.multilingualTagEdit;if(!r){ys.warn("No multilingualTagEdit state found"),n.modal?.close?.();return}const{fieldName:s,editingIndex:o,values:i,statePath:a,currentTags:l}=r;if(!Object.values(i||{}).some(g=>typeof g=="string"&&g.trim()!=="")){ys.warn("No values to save"),n.modal?.close?.(),xd(n);return}const d=Array.isArray(l)?l:[];let u;o==null?u=[...d,i]:u=d.map((g,m)=>m===o?i:g);const f=a||(s?.startsWith("form.")?s:`form.${s}`);n.state?.setParentLocal?.({[f]:u}),n.modal?.close?.(),xd(n)}async function f5(t,e){const n=window.G7Core;if(!n){ys.error("G7Core not available");return}n.modal?.close?.(),xd(n)}async function h5(t,e){const n=window.G7Core;if(!n){ys.error("G7Core not available");return}const r=t?.params?.locale;if(!r){ys.warn("No locale specified");return}const s=e?.event;let o="";s?.target?.value!==void 0?o=s.target.value:s?.value!==void 0&&(o=s.value);const a=(n.state?.getGlobal?.()||{}).multilingualTagEdit||{},l={...a.values||{},[r]:o};n.dispatch?.({handler:"setState",params:{target:"global",multilingualTagEdit:{...a,values:l}}})}function xd(t){t.dispatch?.({handler:"setState",params:{target:"global",multilingualTagEdit:null}})}function Um(t){const e=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0"),s=String(t.getHours()).padStart(2,"0"),o=String(t.getMinutes()).padStart(2,"0"),i=String(t.getSeconds()).padStart(2,"0");return`${e}-${n}-${r}T${s}:${o}:${i}`}function g5(t){const e=new Date,n=new Date(e.getFullYear(),e.getMonth(),e.getDate(),23,59,59),r=Um(n),s=new Date(e.getFullYear(),e.getMonth(),e.getDate(),0,0,0);switch(t){case"today":break;case"week":s.setDate(s.getDate()-6);break;case"month":s.setMonth(s.getMonth()-1);break;case"3months":s.setMonth(s.getMonth()-3);break;case"6months":s.setMonth(s.getMonth()-6);break;case"1year":s.setFullYear(s.getFullYear()-1);break}return{startDate:Um(s),endDate:r}}function p5(t){const{preset:e}=t.params||{preset:"today"},{startDate:n,endDate:r}=g5(e);return{startDate:n,endDate:r,preset:e}}const Oo=window.G7Core?.createLogger?.("Handler:Sidebar")??{log:(...t)=>console.log("[Handler:Sidebar]",...t),warn:(...t)=>console.warn("[Handler:Sidebar]",...t),error:(...t)=>console.error("[Handler:Sidebar]",...t)},Bm="g7_admin_sidebar_collapsed";function jm(){try{return localStorage.getItem(Bm)==="1"}catch{return!1}}function m5(t){try{localStorage.setItem(Bm,t?"1":"0")}catch(e){Oo.error("Failed to save sidebar state to localStorage:",e)}try{window.G7Core?.state?.setGlobal?.({sidebarCollapsed:t})}catch(e){Oo.warn("Failed to set global sidebar state:",e)}}function Wm(){const t=jm();try{window.G7Core?.state?.setGlobal?.({sidebarCollapsed:t})}catch(e){Oo.warn("Failed to restore sidebar state:",e)}Oo.log("Initial sidebar collapsed state restored:",t)}async function b5(t,e){Wm()}async function y5(t,e){const r=!(()=>{try{return!!window.G7Core?.state?.getGlobal?.()?.sidebarCollapsed}catch{return jm()}})();m5(r),Oo.log("Sidebar collapsed toggled to:",r)}const wd=window.G7Core?.createLogger?.("Handler:DownloadAttachment")??{log:(...t)=>console.log("[Handler:DownloadAttachment]",...t),warn:(...t)=>console.warn("[Handler:DownloadAttachment]",...t),error:(...t)=>console.error("[Handler:DownloadAttachment]",...t)};async function v5(t,e){const n=window.G7Core,{url:r,filename:s}=t?.params||{};if(!r){wd.warn("다운로드 URL 이 없어 요청을 건너뜁니다.");return}if(!n?.api?.get){wd.error("G7Core.api.get 이 초기화되지 않아 다운로드를 실행할 수 없습니다.");return}try{const o=await n.api.get(r,{responseType:"blob"});if(o){const i=URL.createObjectURL(o),a=document.createElement("a");a.href=i,a.download=s||"",document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(i)}}catch(o){wd.error("첨부파일 다운로드 실패",o),n?.toast?.error?.(n?.t?.("common.download_failed")??"Download failed")}}const Oa=window.G7Core?.createLogger?.("Handler:DetectAssetUrlMode")??{log:(...t)=>console.log("[Handler:DetectAssetUrlMode]",...t),warn:(...t)=>console.warn("[Handler:DetectAssetUrlMode]",...t),error:(...t)=>console.error("[Handler:DetectAssetUrlMode]",...t)},x5="G7_ASSET_PROBE_OK";async function Gm(t){try{const e=await fetch(t,{cache:"no-store",credentials:"omit"});if(!e.ok)return!1;const n=e.headers.get("content-type")??"";return/javascript|ecmascript/i.test(n)?(await e.text()).includes(x5):!1}catch{return!1}}async function Ym(){const[t,e]=await Promise.all([Gm("/api/system/asset-probe.js"),Gm("/api/system/asset-probe")]);return t&&e?"extension":!t&&e?"extensionless":"unavailable"}function w5(){return window.G7Config?.settings?.general?.asset_url_mode??"extension"}async function k5(t){try{const e=await Ym();if(e==="unavailable")return;const n=w5();if(e===n)return;window.G7Core?.state?.setGlobal?.({assetUrlModeDrift:{detected:e,stored:n}})}catch(e){Oa.warn("자산 URL 방식 대조 실패 (대시보드 동작에는 영향 없음):",e)}}async function C5(t,e){const n=window.G7Core,r=e?.setState??n?.state?.setLocal,s=o=>{typeof r=="function"?r({asset_url_mode_detect_status:o}):Oa.warn("setState 를 찾지 못해 감지 상태를 폼에 반영하지 못했습니다")};try{s("detecting");const o=await Ym();if(o==="unavailable"){s("unavailable");return}typeof r=="function"?r({"general.asset_url_mode":o,asset_url_mode_detect_status:o}):Oa.warn("setState 를 찾지 못해 감지 결과를 폼에 반영하지 못했습니다")}catch(o){Oa.error("자산 URL 방식 감지 실패:",o),s("unavailable")}}const Ta={detectAssetUrlMode:C5,checkAssetUrlModeDrift:k5,setTheme:t5,initTheme:e5,scrollToSection:eu,initMenuFromUrl:i5,initFilterVisibility:a5,saveFilterVisibility:l5,toggleFilterVisibility:c5,resetFilterVisibility:d5,saveMultilingualTag:u5,cancelMultilingualTag:f5,updateMultilingualTagValue:h5,setDateRange:p5,toggleSidebar:y5,initSidebar:b5,downloadAttachment:v5},To=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic:IdentityLauncher")??{log:(...t)=>console.log("[Template:sirsoft-admin_basic:IdentityLauncher]",...t),warn:(...t)=>console.warn("[Template:sirsoft-admin_basic:IdentityLauncher]",...t),error:(...t)=>console.error("[Template:sirsoft-admin_basic:IdentityLauncher]",...t)};function S5(){const e=window.G7Core?.api?.getToken?.()??null;return e?{Authorization:`Bearer ${e}`}:{}}function _5(){try{const t=typeof window<"u"?window.localStorage?.getItem("g7_locale"):null;return t?{"Accept-Language":t}:{}}catch{return{}}}async function E5(t,e=null){const n={purpose:t.purpose};t.provider_id&&(n.provider_id=t.provider_id),e&&(n.target=e);const r=t.challenge_start_url||"/api/identity/challenges",s=await fetch(r,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json",..._5(),...S5()},body:JSON.stringify(n)}),o=await s.json().catch(()=>null);if(!s.ok||!o?.success){const a=o?.message??`Challenge 시작 실패 (HTTP ${s.status})`;throw new Error(a)}const i=o.data??o;return{id:i.id,expires_at:i.expires_at,render_hint:i.render_hint??t.render_hint??"text_code",public_payload:i.public_payload,redirect_url:i.redirect_url,max_attempts:typeof i.max_attempts=="number"?i.max_attempts:void 0}}async function M5(t){const e=window.G7Core,n=e?.identity;if(!e?.dispatch||!e?.state||!n)return To.error("G7Core 또는 G7Core.identity 가 초기화되지 않아 launcher 를 실행할 수 없습니다."),{status:"failed",failureCode:"G7_NOT_READY"};if(t.render_hint==="external_redirect"||t.redirect_url)return n.redirectExternally(t);const r=t.target&&(t.target.email||t.target.phone)?t.target:null;let s;try{s=await E5(t,r)}catch(m){To.error("Challenge 시작 실패:",m);try{await e.dispatch({handler:"toast",params:{type:"error",message:m instanceof Error?m.message:"본인인증 시작에 실패했습니다."}})}catch{}return{status:"failed",failureCode:"CHALLENGE_START_FAILED",reason:m instanceof Error?m.message:String(m)}}if(s.redirect_url)return n.redirectExternally({...t,render_hint:"external_redirect",redirect_url:s.redirect_url});const o=s.expires_at?new Date(s.expires_at).getTime():0,i=o>0?Math.max(0,Math.floor((o-Date.now())/1e3)):0,a=typeof s.max_attempts=="number"?s.max_attempts:0;e.state.set({identityChallenge:{policy_key:t.policy_key,purpose:t.purpose,provider_id:t.provider_id??null,render_hint:s.render_hint,challenge_id:s.id,expires_at:s.expires_at,public_payload:s.public_payload??{},target:r,code:"",error:null,attempts:0,maxAttempts:a,remainingSeconds:i,resendCooldown:0}});let l=o,c=0;const d=window.setInterval(()=>{const m=l>0?Math.max(0,Math.floor((l-Date.now())/1e3)):0;c=Math.max(0,c-1),e.state.set({identityChallenge:{remainingSeconds:m,resendCooldown:c}})},1e3);let u=null;try{u=e.state.subscribe?.(m=>{const p=m?.identityChallenge?.expires_at;if(typeof p=="string"){const b=new Date(p).getTime();Number.isFinite(b)&&b!==l&&(l=b)}const y=m?.identityChallenge?.resendCooldown;typeof y=="number"&&y>c&&(c=y)})??null}catch{}const f=n.createDeferred();try{await e.dispatch({handler:"openModal",target:"identity-challenge-modal"})}catch(m){return To.error("openModal 실패 — 풀페이지로 폴백:",m),window.clearInterval(d),u?.(),n.redirectExternally({...t,render_hint:"external_redirect",redirect_url:`/admin/identity/challenge?challenge_id=${encodeURIComponent(s.id)}&return=${encodeURIComponent(window.location.href)}`})}const g=await f;return window.clearInterval(d),u?.(),g}function N5(){const t=window.G7Core?.identity;if(!t?.setLauncher){To.warn("G7Core.identity 가 아직 초기화되지 않아 launcher 등록을 건너뜁니다. 코어 부트스트랩 순서를 확인하세요.");return}t.setLauncher(M5),To.log("IDV launcher registered (sirsoft-admin_basic)")}function O5(t){const e=t.icons;return Array.isArray(e)?e.filter(n=>!!n&&typeof n=="object"&&typeof n.value=="string"):[]}function T5(t,e){const n=t.label;return typeof n=="string"?n.startsWith("$t:")?e(n.slice(3)):n:t.value}function A5(t,e,n){if(n==="")return!0;const r=n.toLowerCase();return t.value.toLowerCase().includes(r)||e.toLowerCase().includes(r)?!0:(t.keywords??[]).some(s=>s.toLowerCase().includes(r))}function D5({entry:t}){const e=t.preview;return e?.html?h.jsx("span",{className:"g7le-icon-preview",dangerouslySetInnerHTML:{__html:e.html},style:Cd}):e?.className?h.jsx("span",{className:`g7le-icon-preview ${e.className}`,style:Cd}):h.jsx("i",{className:`fas fa-${t.value}`,style:Cd,"aria-hidden":"true"})}function I5({control:t,value:e,onChange:n,t:r}){const s=O5(t),o=e==null?"":String(e),i=typeof t.iconColumns=="number"?t.iconColumns:6,a=t.iconSearchPlaceholder,l=typeof a=="string"&&a.startsWith("$t:")?r(a.slice(3)):r("layout_editor.icon_picker.search_placeholder"),[c,d]=v.useState("");if(s.length===0)return h.jsx("div",{className:"g7le-widget g7le-widget--icon-picker","data-testid":"g7le-widget-icon-picker",style:Km,children:h.jsx("input",{type:"text",className:"g7le-widget g7le-widget--icon-free","data-testid":"g7le-widget-icon-picker-free",value:o,placeholder:r("layout_editor.icon_picker.free_input_hint"),onChange:g=>n(g.target.value===""?void 0:g.target.value),style:Xm})});const u=g=>T5(g,r),f=s.filter(g=>A5(g,u(g),c));return h.jsxs("div",{className:"g7le-widget g7le-widget--icon-picker","data-testid":"g7le-widget-icon-picker",style:Km,children:[h.jsx("input",{type:"text",className:"g7le-widget g7le-widget--icon-search","data-testid":"g7le-widget-icon-picker-search",value:c,placeholder:l,onChange:g=>d(g.target.value),style:Xm}),h.jsxs("div",{className:"g7le-icon-grid","data-testid":"g7le-widget-icon-picker-grid",style:{...L5,gridTemplateColumns:`repeat(${i}, minmax(0, 1fr))`},children:[h.jsx("button",{type:"button","data-testid":"g7le-icon-cell-none",title:r("layout_editor.icon_picker.none"),onClick:()=>n(void 0),"aria-pressed":o==="",style:o===""?qm:kd,children:h.jsx("span",{style:P5,children:"—"})}),f.map(g=>{const m=g.value===o;return h.jsx("button",{type:"button","data-testid":`g7le-icon-cell-${g.value}`,title:u(g),"aria-pressed":m,onClick:()=>n(g.value),style:m?qm:kd,children:h.jsx(D5,{entry:g})},g.value)})]}),f.length===0&&h.jsx("div",{"data-testid":"g7le-widget-icon-picker-empty",style:$5,children:r("layout_editor.icon_picker.no_results")})]})}const Km={display:"flex",flexDirection:"column",gap:6,width:"100%"},Xm={padding:"5px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6,width:"100%"},L5={display:"grid",gap:4,maxHeight:220,overflowY:"auto",padding:2},kd={display:"flex",alignItems:"center",justifyContent:"center",minHeight:34,padding:4,border:"1px solid #e2e8f0",borderRadius:6,background:"#fff",cursor:"pointer"},qm={...kd,border:"2px solid #2563eb",background:"#eff6ff"},Cd={display:"inline-flex",alignItems:"center",justifyContent:"center",fontSize:16,lineHeight:1},P5={fontSize:10,color:"#475569",fontFamily:"monospace",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",maxWidth:"100%"},$5={fontSize:11,color:"#94a3b8",fontStyle:"italic",padding:"4px 2px"},Zm=130,Mn=20;function F5(t){const e=t?.arrayProp;return typeof e=="string"&&e?e:"tabs"}function z5(t){let e=0;for(const n of t){const r=typeof n.id=="number"?n.id:Number(n.id);Number.isFinite(r)&&r>e&&(e=r)}return e+1}function V5({node:t,params:e,cellBoxes:n,t:r,onPatchNode:s}){const o=F5(e),i=v.useMemo(()=>{const g=(t.props??{})[o];return typeof g=="string"||!Array.isArray(g)?null:g},[t,o]),a=v.useMemo(()=>{const g=new Map,m=`props.${o}.`;for(const p of n??[]){if(!p.path.startsWith(m))continue;const y=Number(p.path.slice(m.length));Number.isInteger(y)&&g.set(y,{top:p.top,left:p.left,width:p.width,height:p.height})}return g},[n,o]),l=v.useCallback(g=>{const m=t.props??{};s({...t,props:{...m,[o]:g}})},[t,o,s]),c=v.useCallback(g=>{if(!i)return;const p={id:z5(i),label:r("editor.tabnav_inplace.new_tab")},y=i.slice();y.splice(g,0,p),l(y)},[i,l,r]),d=v.useCallback(g=>{if(!i)return;const m=i.slice();m.splice(g,1),l(m)},[i,l]),u=v.useCallback((g,m)=>{if(!i)return;const p=g+m;if(p<0||p>=i.length)return;const y=i.slice(),[b]=y.splice(g,1);y.splice(p,0,b),l(y)},[i,l]);if(!i||a.size===0)return null;const f={position:"absolute",width:Mn,height:Mn,display:"flex",alignItems:"center",justifyContent:"center",fontSize:12,lineHeight:1,border:"1px solid rgba(0,0,0,0.15)",borderRadius:4,background:"#ffffff",color:"#1f2937",cursor:"pointer",boxShadow:"0 1px 2px rgba(0,0,0,0.18)",pointerEvents:"auto",padding:0,zIndex:Zm};return h.jsx("div",{className:"g7le-tabnav-inplace","data-testid":"g7le-tabnav-inplace",style:{position:"absolute",inset:0,pointerEvents:"none",zIndex:Zm},children:i.map((g,m)=>{const p=a.get(m);if(!p)return null;const y=p.left+p.width;return h.jsxs("div",{"data-testid":`g7le-tabnav-aff-${m}`,children:[h.jsx("button",{type:"button","data-testid":`g7le-tabnav-remove-${m}`,title:r("editor.tabnav_inplace.remove"),"aria-label":r("editor.tabnav_inplace.remove"),style:{...f,left:y-Mn/2,top:p.top-Mn/2,color:"#b91c1c"},onClick:b=>{b.preventDefault(),b.stopPropagation(),d(m)},children:"✕"}),m>0&&h.jsx("button",{type:"button","data-testid":`g7le-tabnav-move-left-${m}`,title:r("editor.tabnav_inplace.move_left"),"aria-label":r("editor.tabnav_inplace.move_left"),style:{...f,left:p.left,top:p.top+p.height-Mn/2},onClick:b=>{b.preventDefault(),b.stopPropagation(),u(m,-1)},children:"◀"}),m{b.preventDefault(),b.stopPropagation(),u(m,1)},children:"▶"}),m===0&&h.jsx("button",{type:"button","data-testid":"g7le-tabnav-add-0",title:r("editor.tabnav_inplace.add"),"aria-label":r("editor.tabnav_inplace.add"),style:{...f,left:p.left-Mn,top:p.top+p.height/2-Mn/2,color:"#15803d"},onClick:b=>{b.preventDefault(),b.stopPropagation(),c(0)},children:"+"}),h.jsx("button",{type:"button","data-testid":`g7le-tabnav-add-${m+1}`,title:r("editor.tabnav_inplace.add"),"aria-label":r("editor.tabnav_inplace.add"),style:{...f,left:y-Mn/2,top:p.top+p.height/2-Mn/2,color:"#15803d"},onClick:b=>{b.preventDefault(),b.stopPropagation(),c(m+1)},children:"+"})]},`tab-aff-${g.id}-${m}`)})})}const Sd=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic:EditorWidgets")??{log:(...t)=>console.log("[Template:sirsoft-admin_basic:EditorWidgets]",...t),warn:(...t)=>console.warn("[Template:sirsoft-admin_basic:EditorWidgets]",...t)};function H5(){if(typeof window>"u")return;const t=window.G7Core?.layoutEditor;if(!t?.registerWidget){Sd.warn("G7Core.layoutEditor 미노출 — icon-picker 위젯 등록 보류");return}t.registerWidget("icon-picker",I5),Sd.log("편집기 위젯 'icon-picker' 등록됨(Font Awesome 검색 그리드)"),t.registerCanvasOverlay&&(t.registerCanvasOverlay("tabnav",V5),Sd.log("편집기 캔버스 오버레이 'tabnav' 등록됨(TabNavigation 탭 인플레이스)"))}const _d=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic")??{log:(...t)=>console.log("[Template:sirsoft-admin_basic]",...t),warn:(...t)=>console.warn("[Template:sirsoft-admin_basic]",...t),error:(...t)=>console.error("[Template:sirsoft-admin_basic]",...t)};typeof window<"u"&&(window.G7TemplateHandlers=Ta);function Qm(){if(typeof window<"u"){let t=0;const e=50,n=()=>{const r=window.G7Core?.getActionDispatcher?.();r?(Object.entries(Ta).forEach(([s,o])=>{r.registerHandler(s,o)}),_d.log(`${Object.keys(Ta).length} custom handler(s) registered:`,Object.keys(Ta)),N5(),Wm()):(t++,t<=e?(_d.warn(`ActionDispatcher not found, retrying... (${t}/${e})`),setTimeout(n,100)):_d.error("Failed to register handlers: ActionDispatcher not available after maximum retries"))};document.readyState==="complete"?n():window.addEventListener("load",n)}}return H5(),Qm(),R.A=Lr,R.Accordion=rc,R.ActionMenu=xs,R.AdminFooter=My,R.AdminHeader=Cy,R.AdminSidebar=ky,R.Alert=cu,R.AlertDialog=hy,R.Badge=Ld,R.BarChart=UT,R.Breadcrumb=ou,R.Button=oe,R.Card=pb,R.CardGrid=Zb,R.CategoryTree=GS,R.Checkbox=vn,R.ChipCheckbox=AE,R.Code=ub,R.CodeEditor=jv,R.ColumnSelector=Zv,R.ConfirmDialog=ru,R.Container=jT,R.DataGrid=Wb,R.Dialog=nu,R.Div=I,R.DonutChart=BT,R.Dropdown=Jb,R.DropdownButton=DE,R.DropdownMenuItem=IE,R.DynamicFieldList=jE,R.EmptyState=vy,R.ExtensionBadge=nh,R.FileInput=Ed,R.FileUploader=Ep,R.FilterGroup=ny,R.FilterVisibilitySelector=_E,R.Flex=GT,R.Form=Pa,R.FormField=XE,R.Fragment=hb,R.Grid=WT,R.H1=Da,R.H2=Jn,R.H3=Ir,R.H4=Md,R.Hr=gb,R.HtmlContent=Za,R.HtmlEditor=wE,R.I=mt,R.Icon=re,R.IconButton=dy,R.IconName=de,R.IconSelect=VS,R.ImageGallery=ji,R.Img=yn,R.Input=kt,R.Label=sn,R.LanguageSelector=Ey,R.LayoutEditorHeader=Dy,R.LayoutFileList=Iy,R.LayoutHistoryPanel=Gv,R.LayoutWarnings=SE,R.Li=La,R.LoadingSpinner=wy,R.LoginForm=Yv,R.Modal=Bo,R.MultilingualInput=Ha,R.MultilingualLocaleContext=Pc,R.MultilingualTabPanel=KE,R.MultilingualTagInput=Kk,R.Nav=Po,R.NotificationCenter=Sy,R.Ol=sb,R.Optgroup=lb,R.Option=ab,R.P=yt,R.PageHeader=Ny,R.PageLoading=ex,R.PageTransitionIndicator=Jv,R.Pagination=$o,R.PermissionTree=jS,R.Pre=fb,R.ProductCard=Rb,R.RadioGroup=yE,R.RichSelect=OE,R.SearchBar=ty,R.SearchableDropdown=Xk,R.Section=Nd,R.SectionHeader=Ty,R.SectionLayout=YT,R.Select=Pr,R.SlotContainer=NE,R.SortableMenuItem=Gh,R.SortableMenuList=FS,R.Span=Q,R.StatCard=ey,R.StatusBadge=Fo,R.Svg=Un,R.TabNavigation=ry,R.TabNavigationScroll=ly,R.Table=$a,R.TagInput=Wk,R.TagSelect=qk,R.Tbody=za,R.Td=Zt,R.TemplateCard=Ay,R.Textarea=Qn,R.Tfoot=Va,R.Th=$r,R.Thead=Fa,R.ThemeToggle=Xv,R.ThreeColumnLayout=KT,R.Toast=by,R.Toggle=Ua,R.Tr=Rn,R.Ul=Ia,R.UnifiedIcon=db,R.UserProfile=_y,R.VersionList=_u,R.configureJSONLanguageService=ZT,R.defaultJSONSchemas=Fm,R.executeImageDownload=Tc,R.initTemplate=Qm,R.jsonEditorDefaults=XT,R.monacoInitOptions=qT,R.templateMetadata=QT,R.useCurrentMultilingualLocale=Ip,R.useImageGallery=fp,Object.defineProperty(R,Symbol.toStringTag,{value:"Module"}),R})({},ReactJSXRuntime,React,ReactDOM); + `,role:"option","aria-selected":C,"data-item-value":k.value,"data-item-index":N,"data-item-selected":C,children:c?.item?Wp(c.item,{item:k,index:N,isSelected:C},`item-${N}`):a||S(k,C)},k.value)})})})]})};function qE(e){const t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?{r:parseInt(t[1],16),g:parseInt(t[2],16),b:parseInt(t[3],16)}:null}const ZE=({value:e,checked:t=!1,icon:n,label:r,variant:s="blue",customColor:o,className:i="",style:a,onChange:l,id:c,editorAttrs:d})=>{const[u,f]=v.useState(!1),g="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium border cursor-pointer transition-all";if(o){const b=qE(o),_=document.documentElement.classList.contains("dark");let x;return t?x={backgroundColor:o,color:"#ffffff",borderColor:o,opacity:u?.85:1}:x=b?{backgroundColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${u?_?.25:.12:_?.15:.06})`,color:o,borderColor:`rgba(${b.r}, ${b.g}, ${b.b}, ${_?.3:.2})`}:{},h.jsxs(ln,{className:`${g} ${i}`,style:{...x,...a},onMouseEnter:()=>f(!0),onMouseLeave:()=>f(!1),id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:e,checked:t,onChange:M=>{l?.(M)},className:"hidden"}),n&&h.jsx(oe,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})}const y=t?{green:"bg-green-600 dark:bg-green-600 text-white dark:text-white border-green-700 dark:border-green-500 hover:bg-green-700 dark:hover:bg-green-500",orange:"bg-orange-600 dark:bg-orange-600 text-white dark:text-white border-orange-700 dark:border-orange-500 hover:bg-orange-700 dark:hover:bg-orange-500",yellow:"bg-yellow-600 dark:bg-yellow-600 text-white dark:text-white border-yellow-700 dark:border-yellow-500 hover:bg-yellow-700 dark:hover:bg-yellow-500",gray:"bg-gray-600 dark:bg-gray-500 text-white dark:text-white border-gray-700 dark:border-gray-400 hover:bg-gray-700 dark:hover:bg-gray-400",blue:"bg-blue-600 dark:bg-blue-600 text-white dark:text-white border-blue-700 dark:border-blue-500 hover:bg-blue-700 dark:hover:bg-blue-500"}[s]:{green:"bg-green-50 dark:bg-green-900/20 text-green-600 dark:text-green-400 border-green-200 dark:border-green-800 hover:bg-green-100 dark:hover:bg-green-900/30",orange:"bg-orange-50 dark:bg-orange-900/20 text-orange-600 dark:text-orange-400 border-orange-200 dark:border-orange-800 hover:bg-orange-100 dark:hover:bg-orange-900/30",yellow:"bg-yellow-50 dark:bg-yellow-900/20 text-yellow-600 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800 hover:bg-yellow-100 dark:hover:bg-yellow-900/30",gray:"bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border-gray-200 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-600",blue:"bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800 hover:bg-blue-100 dark:hover:bg-blue-900/30"}[s];return h.jsxs(ln,{className:`${g} ${y} ${i}`,style:a,id:c,...d,children:[h.jsx(kt,{type:"checkbox",value:e,checked:t,onChange:b=>{l?.(b)},className:"hidden"}),n&&h.jsx(oe,{name:n,className:"w-4 h-4"}),h.jsx(I,{children:r})]})},QE=({label:e,icon:t,iconPosition:n="left",position:r="right",className:s="",style:o,children:i})=>{const[a,l]=v.useState(!1),[c,d]=v.useState({top:0,left:0}),u=v.useRef(null),f=v.useRef(null),g=v.useCallback(()=>{if(!u.current)return;const p=u.current.getBoundingClientRect(),y=224;let b=r==="right"?p.right-y:p.left;const _=p.bottom+8;b<8&&(b=8),b+y>window.innerWidth-8&&(b=window.innerWidth-y-8),d({top:_,left:b})},[r]);v.useEffect(()=>{a&&g()},[a,g]),v.useEffect(()=>{if(!a)return;const p=()=>{l(!1)};return window.addEventListener("scroll",p,!0),window.addEventListener("resize",p),()=>{window.removeEventListener("scroll",p,!0),window.removeEventListener("resize",p)}},[a]),v.useEffect(()=>{const p=y=>{const b=y.target,_=u.current?.contains(b),x=f.current?.contains(b);!_&&!x&&l(!1)};return a&&document.addEventListener("mousedown",p),()=>{document.removeEventListener("mousedown",p)}},[a]);const m=a?Ln.createPortal(h.jsx("div",{ref:f,className:"fixed z-[9999] w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",style:{top:c.top,left:c.left},onClick:()=>l(!1),children:i}),document.body):null;return h.jsxs(I,{className:`relative inline-block ${s}`,style:o,children:[h.jsxs(ae,{ref:u,onClick:()=>l(!a),className:"flex items-center gap-2",children:[n==="left"&&t&&h.jsx(oe,{name:t,className:"w-4 h-4"}),e&&h.jsx(J,{className:"text-sm font-medium",children:e}),n==="right"&&t&&h.jsx(oe,{name:t,className:"w-4 h-4"})]}),m]})},JE=({label:e,icon:t,variant:n="default",disabled:r=!1,divider:s=!1,className:o="",style:i,onClick:a})=>{if(s)return h.jsx(I,{className:"h-px bg-gray-200 dark:bg-gray-700 my-1",style:i});const l=n==="danger"?"text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20":"text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700",c=r?"opacity-50 cursor-not-allowed":"cursor-pointer",d=()=>{!r&&a&&a()};return h.jsxs(I,{onClick:d,className:`flex items-center gap-3 px-4 py-3 transition-colors ${l} ${c} ${o}`,style:i,children:[t&&h.jsx(oe,{name:t,className:"w-4 h-4"}),e&&h.jsx(J,{className:"text-sm font-medium",children:e})]})},RE=()=>window.G7Core,Bc=v.createContext(null),Gp=()=>{const e=v.useContext(Bc);if(!e){const t=RE(),n=t?.locale?.current?.()||"ko",r=t?.locale?.supported?.()||["ko","en"];return{activeLocale:n,getActiveLocale:()=>t?.locale?.current?.()||"ko",supportedLocales:r,isInsideMultilingualTabPanel:!1}}return{...e,isInsideMultilingualTabPanel:!0}},eM=()=>window.G7Core,tM=({value:e,onChange:t,placeholder:n,readOnly:r,disabled:s,hasError:o})=>{const{activeLocale:i,getActiveLocale:a}=Gp(),l=typeof e=="string"?{[i]:e}:e||{},c=v.useCallback(u=>{const f=a();t({...l,[f]:u.target.value})},[l,a,t]),d=o?"border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500":"border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500";return h.jsx(kt,{type:"text",value:l[i]||"",onChange:c,placeholder:n,readOnly:r,disabled:s,className:`w-full px-3 py-2 text-sm border ${d} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2`})},nM=(e,t)=>{if(!t)return!1;const n=t.startsWith("!"),r=n?t.slice(1):t,s=!!e[r];return n?!s:s},rM=({column:e,item:t,index:n,onValueChange:r,readOnly:s,error:o})=>{const i=t[e.key],a=s||e.readOnly||nM(t,e.readOnlyCondition)||e.disabled,l=!!o,c=l?"border-red-500 dark:border-red-500 focus:ring-red-500 focus:border-red-500":"border-gray-300 dark:border-gray-600 focus:ring-blue-500 focus:border-blue-500",d=v.useCallback(f=>{r(e.key,f)},[e.key,r]),u=f=>h.jsxs(I,{className:"w-full",children:[f,l&&h.jsx(J,{className:"text-xs text-red-500 dark:text-red-400 mt-1 block",children:o})]});switch(e.type){case"input":return u(h.jsx(kt,{type:"text",value:i||"",onChange:f=>d(f.target.value),placeholder:e.placeholder,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${e.className||""}`}));case"number":return u(h.jsx(kt,{type:"number",value:i??"",onChange:f=>d(f.target.value?Number(f.target.value):null),placeholder:e.placeholder,min:e.min,max:e.max,step:e.step,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${e.className||""}`}));case"select":return u(h.jsxs(Gr,{value:i??"",onChange:f=>d(f.target.value),disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 ${e.className||""}`,children:[h.jsx("option",{value:"",children:"선택하세요"}),e.options?.map(f=>h.jsx("option",{value:f.value,disabled:f.disabled,children:f.label},f.value))]}));case"textarea":return u(h.jsx(sr,{value:i||"",onChange:f=>d(f.target.value),placeholder:e.placeholder,readOnly:a,disabled:a,className:`w-full px-3 py-2 text-sm border ${c} rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 resize-none ${e.className||""}`,rows:2}));case"multilingual":return u(h.jsx(tM,{value:i,onChange:f=>d(f),placeholder:e.placeholder,readOnly:a,disabled:a,hasError:l}));case"custom":return e.render?h.jsx(h.Fragment,{children:e.render(t,n,d)}):h.jsx(J,{className:"text-gray-500 dark:text-gray-400",children:"-"});default:return h.jsx(J,{className:"text-gray-900 dark:text-white text-sm",children:String(i??"")})}},sM=(e,t,n)=>{if(!e)return;if(n==="multilingual"){const o=eM()?.locale?.current?.()||"ko",i=`${t}.${o}`;return e[i]?.[0]}return e[t]?.[0]},oM=({item:e,index:t,id:n,columns:r,enableDrag:s=!0,showIndex:o=!0,canRemove:i=!0,rowActions:a,readOnly:l=!1,onValueChange:c,onRemove:d,className:u="",level:f=0,errors:g})=>{const{attributes:m,listeners:p,setNodeRef:y,transform:b,transition:_,isDragging:x}=Hi({id:n,disabled:!s||l}),M={transform:Un.Transform.toString(b),transition:_,opacity:x?.5:1},S=v.useCallback((C,w)=>{c?.(C,w)},[c]),k=v.useCallback(C=>{C.onClick?.(e,t)},[e,t]),N=f>0?{paddingLeft:`${f*24}px`}:{};return h.jsxs(I,{ref:y,style:M,className:`flex items-center gap-2 py-2 px-2 border-b border-gray-100 dark:border-gray-700 last:border-b-0 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors ${x?"bg-blue-50 dark:bg-blue-900/20 shadow-lg rounded-lg z-10":""} ${u}`,children:[s&&!l&&h.jsx(I,{...m,...p,className:"flex-shrink-0 w-6 h-6 flex items-center justify-center cursor-grab active:cursor-grabbing text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400",style:N,children:h.jsx(oe,{name:"grip-vertical",className:"w-4 h-4"})}),o&&h.jsx(I,{className:"flex-shrink-0 w-8 text-center text-sm text-gray-500 dark:text-gray-400 font-medium",children:t+1}),r.map(C=>{const w=sM(g,C.key,C.type);return h.jsx(I,{className:`flex-1 min-w-0 ${C.width||""}`,style:C.width?{flex:"none",width:C.width}:{},children:h.jsx(rM,{column:C,item:e,index:t,onValueChange:S,readOnly:l,error:w})},C.key)}),a&&a.length>0&&!l&&h.jsx(I,{className:"flex-shrink-0 flex items-center gap-1",children:a.map(C=>{const w=typeof C.disabled=="function"?C.disabled(e,t):C.disabled;return h.jsx(ae,{type:"button",onClick:()=>k(C),disabled:w,className:`p-1.5 rounded transition-colors ${C.danger?"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-600":"text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300"} disabled:opacity-50 disabled:cursor-not-allowed`,title:C.label,children:C.icon?h.jsx(oe,{name:C.icon,className:"w-4 h-4"}):h.jsx(J,{className:"text-xs",children:C.label})},C.key)})}),i&&!a&&!l&&h.jsx(I,{className:"flex-shrink-0",children:h.jsx(ae,{type:"button",onClick:d,className:"p-1.5 text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300 rounded transition-colors",title:"삭제",children:h.jsx(oe,{name:"minus",className:"w-4 h-4"})})})]})},Yp=()=>window.G7Core,jc=()=>`item_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,iM=e=>{const t={_id:jc()};return e.forEach(n=>{if(n.type==="multilingual"){const s=Yp()?.locale?.all?.()||["ko","en"],o={};s.forEach(i=>{o[i]=""}),t[n.key]=o}else n.type==="number"?t[n.key]=null:(n.type,t[n.key]="")}),t},aM=(e,t)=>{if(!e)return{};const n=`fields.${t}.`,r={};return Object.keys(e).forEach(s=>{if(s.startsWith(n)){const o=s.substring(n.length);r[o]=e[s]}}),r},lM=({items:e=[],columns:t,onChange:n,onAddItem:r,onRemoveItem:s,onReorder:o,addLabel:i="항목 추가",enableDrag:a=!0,showIndex:l=!0,minItems:c=0,maxItems:d,rowActions:u,emptyMessage:f="항목이 없습니다.",className:g="",headerClassName:m="",rowClassName:p="",name:y,readOnly:b=!1,createDefaultItem:_,childrenKey:x,itemIdKey:M="_id",errors:S,id:k,editorAttrs:N})=>{const C=v.useRef(null),w=v.useRef([]),E=v.useRef(null),A=v.useMemo(()=>{let G=!1;for(const B of e)if(B[M]==null){G=!0;break}return G?e.map(B=>B[M]!=null?B:{...B,[M]:jc()}):e},[e,M]);w.current=A,v.useEffect(()=>{E.current=null},[A]);const P=v.useCallback(G=>String(G[M]??""),[M]);v.useEffect(()=>{if(y){const G=Yp();G?.form&&(C.current=G.form)}},[y]);const L=Zl(ds(oo,{activationConstraint:{distance:8}}),ds(so,{coordinateGetter:Ui})),$=v.useMemo(()=>A.map(G=>P(G)),[A,P]),D=v.useCallback(G=>{const{active:B,over:j}=G;if(j&&B.id!==j.id){const Y=E.current??w.current,V=Y.map(q=>String(q[M]??"")),X=V.indexOf(String(B.id)),K=V.indexOf(String(j.id));if(X!==-1&&K!==-1){const q=fs([...Y],X,K);E.current=q,n?.(q),o?.(q)}}},[n,o,M]),O=v.useCallback((G,B,j)=>{const V=[...E.current??w.current];V[G]={...V[G],[B]:j},E.current=V,n?.(V)},[n]),T=v.useCallback(()=>{const G=E.current??w.current;if(d&&G.length>=d)return;if(r){r();return}const B=_?_():iM(t);B[M]||(B[M]=jc());const j=[...G,B];E.current=j,n?.(j)},[d,r,_,t,M,n]),z=v.useCallback(G=>{const B=E.current??w.current;if(B.length<=c)return;const j=B[G];if(s){s(G,j);return}const Y=B.filter((V,X)=>X!==G);E.current=Y,n?.(Y)},[c,s,n]),U=A.length>c,W=!d||A.lengthh.jsxs(I,{className:`flex-1 min-w-0 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase ${G.width||""}`,style:G.width?{flex:"none",width:G.width}:{},children:[G.label,G.required&&h.jsx(J,{className:"text-red-500 dark:text-red-400 ml-0.5",children:"*"})]},G.key)),(u||!b&&U)&&h.jsx(I,{className:"flex-shrink-0 w-8"})]}),A.length>0?h.jsx(ac,{sensors:L,collisionDetection:Ql,onDragEnd:D,children:h.jsx(Vi,{items:$,strategy:lc,children:h.jsx(I,{className:"border-x border-gray-200 dark:border-gray-700",children:A.map((G,B)=>{const j=P(G),Y=aM(S,B);return h.jsx(oM,{id:j,item:G,index:B,columns:t,enableDrag:a,showIndex:l,canRemove:U,rowActions:u,readOnly:b,onValueChange:(V,X)=>O(B,V,X),onRemove:()=>z(B),className:p,errors:Y},j)})})})}):h.jsx(I,{className:"border-x border-gray-200 dark:border-gray-700 py-8 text-center text-gray-500 dark:text-gray-400 text-sm",children:f}),!b&&W&&h.jsx(I,{className:"relative border border-gray-200 dark:border-gray-700 border-t-0 rounded-b-lg",children:h.jsx(I,{className:"flex items-center justify-center py-2",children:h.jsxs(ae,{type:"button",onClick:T,className:"flex items-center gap-1.5 px-4 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",children:[h.jsx(oe,{name:"plus",className:"w-4 h-4"}),h.jsx(J,{children:i})]})})}),(b||!W)&&A.length>0&&h.jsx(I,{className:"border-x border-b border-gray-200 dark:border-gray-700 rounded-b-lg h-1"})]})},Xp=()=>window.G7Core,cM=()=>Xp()?.locale?.supported?.()??["ko","en"],dM=()=>Xp()?.locale?.current?.()??"ko",uM={ko:"한국어",en:"English",ja:"日本語",zh:"中文"},fM=({children:e,className:t="",style:n,variant:r="default",defaultLocale:s,onLocaleChange:o,locales:i})=>{const a=v.useMemo(()=>i&&i.length>0?i:cM(),[i]),[l,c]=v.useState(s||dM());v.useEffect(()=>{if(a.length>0&&!a.includes(l)){const p=a[0];c(p),o?.(p)}},[a,l,o]);const d=v.useRef(l);d.current=l;const u=v.useCallback(()=>d.current,[]),f=v.useCallback(p=>{c(p),o?.(p)},[o]),g=v.useMemo(()=>({activeLocale:l,getActiveLocale:u,supportedLocales:a}),[l,u,a]),m=p=>{const y=p===l,b="px-4 py-2 text-sm font-medium transition-all";return r==="pills"?y?`${b} bg-blue-600 text-white rounded-lg`:`${b} text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg`:r==="underline"?y?`${b} text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400`:`${b} text-gray-600 dark:text-gray-400 border-b-2 border-transparent hover:border-gray-300 dark:hover:border-gray-500`:y?`${b} text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 border-b-2 border-blue-600`:`${b} text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 border-b-2 border-transparent`};return h.jsxs(I,{className:t,style:n,children:[h.jsx(Go,{className:"flex gap-1 border-b border-gray-200 dark:border-gray-700 mb-4",children:a.map(p=>h.jsx(ae,{type:"button",onClick:()=>f(p),className:m(p),children:h.jsx(J,{children:uM[p]||p})},p))}),h.jsx(Bc.Provider,{value:g,children:e})]})},hM=({label:e,required:t=!1,error:n,helperText:r,className:s="",labelClassName:o="",children:i,horizontal:a=!1,labelWidth:l="w-1/3",id:c,editorAttrs:d})=>{const u=a?`flex items-start gap-4 ${s}`:`space-y-1 ${s}`,f=a?`${l} flex-shrink-0 pt-2`:"",g=a?"flex-1":"";return h.jsxs(I,{className:u,id:c,...d,children:[e&&h.jsx(I,{className:f,children:h.jsxs(ln,{className:`block text-sm font-medium text-gray-700 dark:text-gray-300 ${o}`,children:[e,t&&h.jsx("span",{className:"ml-1 text-red-500 dark:text-red-400",children:"*"})]})}),h.jsxs(I,{className:`${g} ${n?"form-field-error":""}`.trim(),children:[i,r&&!n&&h.jsx(I,{className:"mt-1 text-xs text-gray-500 dark:text-gray-400",children:r}),n&&h.jsx(I,{className:"mt-1 text-xs text-red-600 dark:text-red-400",children:n})]})]})};function xo(e){return e+.5|0}const hr=(e,t,n)=>Math.max(Math.min(e,n),t);function wo(e){return hr(xo(e*2.55),0,255)}function gr(e){return hr(xo(e*255),0,255)}function qn(e){return hr(xo(e/2.55)/100,0,1)}function Kp(e){return hr(xo(e*100),0,100)}const mn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Wc=[..."0123456789ABCDEF"],gM=e=>Wc[e&15],pM=e=>Wc[(e&240)>>4]+Wc[e&15],sa=e=>(e&240)>>4===(e&15),mM=e=>sa(e.r)&&sa(e.g)&&sa(e.b)&&sa(e.a);function bM(e){var t=e.length,n;return e[0]==="#"&&(t===4||t===5?n={r:255&mn[e[1]]*17,g:255&mn[e[2]]*17,b:255&mn[e[3]]*17,a:t===5?mn[e[4]]*17:255}:(t===7||t===9)&&(n={r:mn[e[1]]<<4|mn[e[2]],g:mn[e[3]]<<4|mn[e[4]],b:mn[e[5]]<<4|mn[e[6]],a:t===9?mn[e[7]]<<4|mn[e[8]]:255})),n}const yM=(e,t)=>e<255?t(e):"";function vM(e){var t=mM(e)?gM:pM;return e?"#"+t(e.r)+t(e.g)+t(e.b)+yM(e.a,t):void 0}const xM=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function qp(e,t,n){const r=t*Math.min(n,1-n),s=(o,i=(o+e/30)%12)=>n-r*Math.max(Math.min(i-3,9-i,1),-1);return[s(0),s(8),s(4)]}function wM(e,t,n){const r=(s,o=(s+e/60)%6)=>n-n*t*Math.max(Math.min(o,4-o,1),0);return[r(5),r(3),r(1)]}function kM(e,t,n){const r=qp(e,1,.5);let s;for(t+n>1&&(s=1/(t+n),t*=s,n*=s),s=0;s<3;s++)r[s]*=1-t-n,r[s]+=t;return r}function CM(e,t,n,r,s){return e===s?(t-n)/r+(t.5?d/(2-o-i):d/(o+i),l=CM(n,r,s,d,o),l=l*60+.5),[l|0,c||0,a]}function Yc(e,t,n,r){return(Array.isArray(t)?e(t[0],t[1],t[2]):e(t,n,r)).map(gr)}function Xc(e,t,n){return Yc(qp,e,t,n)}function SM(e,t,n){return Yc(kM,e,t,n)}function _M(e,t,n){return Yc(wM,e,t,n)}function Zp(e){return(e%360+360)%360}function EM(e){const t=xM.exec(e);let n=255,r;if(!t)return;t[5]!==r&&(n=t[6]?wo(+t[5]):gr(+t[5]));const s=Zp(+t[2]),o=+t[3]/100,i=+t[4]/100;return t[1]==="hwb"?r=SM(s,o,i):t[1]==="hsv"?r=_M(s,o,i):r=Xc(s,o,i),{r:r[0],g:r[1],b:r[2],a:n}}function MM(e,t){var n=Gc(e);n[0]=Zp(n[0]+t),n=Xc(n),e.r=n[0],e.g=n[1],e.b=n[2]}function NM(e){if(!e)return;const t=Gc(e),n=t[0],r=Kp(t[1]),s=Kp(t[2]);return e.a<255?`hsla(${n}, ${r}%, ${s}%, ${qn(e.a)})`:`hsl(${n}, ${r}%, ${s}%)`}const Qp={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Jp={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function TM(){const e={},t=Object.keys(Jp),n=Object.keys(Qp);let r,s,o,i,a;for(r=0;r>16&255,o>>8&255,o&255]}return e}let oa;function OM(e){oa||(oa=TM(),oa.transparent=[0,0,0,0]);const t=oa[e.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const AM=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function DM(e){const t=AM.exec(e);let n=255,r,s,o;if(t){if(t[7]!==r){const i=+t[7];n=t[8]?wo(i):hr(i*255,0,255)}return r=+t[1],s=+t[3],o=+t[5],r=255&(t[2]?wo(r):hr(r,0,255)),s=255&(t[4]?wo(s):hr(s,0,255)),o=255&(t[6]?wo(o):hr(o,0,255)),{r,g:s,b:o,a:n}}}function IM(e){return e&&(e.a<255?`rgba(${e.r}, ${e.g}, ${e.b}, ${qn(e.a)})`:`rgb(${e.r}, ${e.g}, ${e.b})`)}const Kc=e=>e<=.0031308?e*12.92:Math.pow(e,1/2.4)*1.055-.055,Cs=e=>e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4);function LM(e,t,n){const r=Cs(qn(e.r)),s=Cs(qn(e.g)),o=Cs(qn(e.b));return{r:gr(Kc(r+n*(Cs(qn(t.r))-r))),g:gr(Kc(s+n*(Cs(qn(t.g))-s))),b:gr(Kc(o+n*(Cs(qn(t.b))-o))),a:e.a+n*(t.a-e.a)}}function ia(e,t,n){if(e){let r=Gc(e);r[t]=Math.max(0,Math.min(r[t]+r[t]*n,t===0?360:1)),r=Xc(r),e.r=r[0],e.g=r[1],e.b=r[2]}}function Rp(e,t){return e&&Object.assign(t||{},e)}function e0(e){var t={r:0,g:0,b:0,a:255};return Array.isArray(e)?e.length>=3&&(t={r:e[0],g:e[1],b:e[2],a:255},e.length>3&&(t.a=gr(e[3]))):(t=Rp(e,{r:0,g:0,b:0,a:1}),t.a=gr(t.a)),t}function PM(e){return e.charAt(0)==="r"?DM(e):EM(e)}class ko{constructor(t){if(t instanceof ko)return t;const n=typeof t;let r;n==="object"?r=e0(t):n==="string"&&(r=bM(t)||OM(t)||PM(t)),this._rgb=r,this._valid=!!r}get valid(){return this._valid}get rgb(){var t=Rp(this._rgb);return t&&(t.a=qn(t.a)),t}set rgb(t){this._rgb=e0(t)}rgbString(){return this._valid?IM(this._rgb):void 0}hexString(){return this._valid?vM(this._rgb):void 0}hslString(){return this._valid?NM(this._rgb):void 0}mix(t,n){if(t){const r=this.rgb,s=t.rgb;let o;const i=n===o?.5:n,a=2*i-1,l=r.a-s.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,r.r=255&c*r.r+o*s.r+.5,r.g=255&c*r.g+o*s.g+.5,r.b=255&c*r.b+o*s.b+.5,r.a=i*r.a+(1-i)*s.a,this.rgb=r}return this}interpolate(t,n){return t&&(this._rgb=LM(this._rgb,t._rgb,n)),this}clone(){return new ko(this.rgb)}alpha(t){return this._rgb.a=gr(t),this}clearer(t){const n=this._rgb;return n.a*=1-t,this}greyscale(){const t=this._rgb,n=xo(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=n,this}opaquer(t){const n=this._rgb;return n.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return ia(this._rgb,2,t),this}darken(t){return ia(this._rgb,2,-t),this}saturate(t){return ia(this._rgb,1,t),this}desaturate(t){return ia(this._rgb,1,-t),this}rotate(t){return MM(this._rgb,t),this}}function Zn(){}const $M=(()=>{let e=0;return()=>e++})();function lt(e){return e==null}function Mt(e){if(Array.isArray&&Array.isArray(e))return!0;const t=Object.prototype.toString.call(e);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function Ze(e){return e!==null&&Object.prototype.toString.call(e)==="[object Object]"}function bn(e){return(typeof e=="number"||e instanceof Number)&&isFinite(+e)}function jn(e,t){return bn(e)?e:t}function Qe(e,t){return typeof e>"u"?t:e}const FM=(e,t)=>typeof e=="string"&&e.endsWith("%")?parseFloat(e)/100:+e/t,t0=(e,t)=>typeof e=="string"&&e.endsWith("%")?parseFloat(e)/100*t:+e;function ht(e,t,n){if(e&&typeof e.call=="function")return e.apply(n,t)}function st(e,t,n,r){let s,o,i;if(Mt(e))for(o=e.length,s=0;se,x:e=>e.x,y:e=>e.y};function HM(e){const t=e.split("."),n=[];let r="";for(const s of t)r+=s,r.endsWith("\\")?r=r.slice(0,-1)+".":(n.push(r),r="");return n}function UM(e){const t=HM(e);return n=>{for(const r of t){if(r==="")break;n=n&&n[r]}return n}}function Ir(e,t){return(r0[t]||(r0[t]=UM(t)))(e)}function qc(e){return e.charAt(0).toUpperCase()+e.slice(1)}const _o=e=>typeof e<"u",pr=e=>typeof e=="function",s0=(e,t)=>{if(e.size!==t.size)return!1;for(const n of e)if(!t.has(n))return!1;return!0};function BM(e){return e.type==="mouseup"||e.type==="click"||e.type==="contextmenu"}const ct=Math.PI,_t=2*ct,ca=Number.POSITIVE_INFINITY,jM=ct/180,Nt=ct/2,Lr=ct/4,o0=ct*2/3,i0=Math.log10,mr=Math.sign;function da(e,t,n){return Math.abs(e-t)s-o).pop(),t}function GM(e){return typeof e=="symbol"||typeof e=="object"&&e!==null&&!(Symbol.toPrimitive in e||"toString"in e||"valueOf"in e)}function ua(e){return!GM(e)&&!isNaN(parseFloat(e))&&isFinite(e)}function YM(e,t){const n=Math.round(e);return n-t<=e&&n+t>=e}function XM(e,t,n){let r,s,o;for(r=0,s=e.length;rl&&c=Math.min(t,n)-r&&e<=Math.max(t,n)+r}function Zc(e,t,n){n=n||(i=>e[i]1;)o=s+r>>1,n(o)?s=o:r=o;return{lo:s,hi:r}}const Qc=(e,t,n,r)=>Zc(e,n,r?s=>{const o=e[s][t];return oe[s][t]Zc(e,n,r=>e[r][t]>=n);function JM(e,t,n){let r=0,s=e.length;for(;rr&&e[s-1]>n;)s--;return r>0||s{const r="_onData"+qc(n),s=e[n];Object.defineProperty(e,n,{configurable:!0,enumerable:!1,value(...o){const i=s.apply(this,o);return e._chartjs.listeners.forEach(a=>{typeof a[r]=="function"&&a[r](...o)}),i}})})}function u0(e,t){const n=e._chartjs;if(!n)return;const r=n.listeners,s=r.indexOf(t);s!==-1&&r.splice(s,1),!(r.length>0)&&(d0.forEach(o=>{delete e[o]}),delete e._chartjs)}function f0(e){const t=new Set(e);return t.size===e.length?e:Array.from(t)}const h0=(function(){return typeof window>"u"?function(e){return e()}:window.requestAnimationFrame})();function g0(e,t){let n=[],r=!1;return function(...s){n=s,r||(r=!0,h0.call(window,()=>{r=!1,e.apply(t,n)}))}}function eN(e,t){let n;return function(...r){return t?(clearTimeout(n),n=setTimeout(e,t,r)):e.apply(this,r),t}}const Jc=e=>e==="start"?"left":e==="end"?"right":"center",Wt=(e,t,n)=>e==="start"?t:e==="end"?n:(t+n)/2,tN=(e,t,n,r)=>e===(r?"left":"right")?n:e==="center"?(t+n)/2:t,ha=e=>e===0||e===1,p0=(e,t,n)=>-(Math.pow(2,10*(e-=1))*Math.sin((e-t)*_t/n)),m0=(e,t,n)=>Math.pow(2,-10*e)*Math.sin((e-t)*_t/n)+1,Eo={linear:e=>e,easeInQuad:e=>e*e,easeOutQuad:e=>-e*(e-2),easeInOutQuad:e=>(e/=.5)<1?.5*e*e:-.5*(--e*(e-2)-1),easeInCubic:e=>e*e*e,easeOutCubic:e=>(e-=1)*e*e+1,easeInOutCubic:e=>(e/=.5)<1?.5*e*e*e:.5*((e-=2)*e*e+2),easeInQuart:e=>e*e*e*e,easeOutQuart:e=>-((e-=1)*e*e*e-1),easeInOutQuart:e=>(e/=.5)<1?.5*e*e*e*e:-.5*((e-=2)*e*e*e-2),easeInQuint:e=>e*e*e*e*e,easeOutQuint:e=>(e-=1)*e*e*e*e+1,easeInOutQuint:e=>(e/=.5)<1?.5*e*e*e*e*e:.5*((e-=2)*e*e*e*e+2),easeInSine:e=>-Math.cos(e*Nt)+1,easeOutSine:e=>Math.sin(e*Nt),easeInOutSine:e=>-.5*(Math.cos(ct*e)-1),easeInExpo:e=>e===0?0:Math.pow(2,10*(e-1)),easeOutExpo:e=>e===1?1:-Math.pow(2,-10*e)+1,easeInOutExpo:e=>ha(e)?e:e<.5?.5*Math.pow(2,10*(e*2-1)):.5*(-Math.pow(2,-10*(e*2-1))+2),easeInCirc:e=>e>=1?e:-(Math.sqrt(1-e*e)-1),easeOutCirc:e=>Math.sqrt(1-(e-=1)*e),easeInOutCirc:e=>(e/=.5)<1?-.5*(Math.sqrt(1-e*e)-1):.5*(Math.sqrt(1-(e-=2)*e)+1),easeInElastic:e=>ha(e)?e:p0(e,.075,.3),easeOutElastic:e=>ha(e)?e:m0(e,.075,.3),easeInOutElastic(e){return ha(e)?e:e<.5?.5*p0(e*2,.1125,.45):.5+.5*m0(e*2-1,.1125,.45)},easeInBack(e){return e*e*((1.70158+1)*e-1.70158)},easeOutBack(e){return(e-=1)*e*((1.70158+1)*e+1.70158)+1},easeInOutBack(e){let t=1.70158;return(e/=.5)<1?.5*(e*e*(((t*=1.525)+1)*e-t)):.5*((e-=2)*e*(((t*=1.525)+1)*e+t)+2)},easeInBounce:e=>1-Eo.easeOutBounce(1-e),easeOutBounce(e){return e<1/2.75?7.5625*e*e:e<2/2.75?7.5625*(e-=1.5/2.75)*e+.75:e<2.5/2.75?7.5625*(e-=2.25/2.75)*e+.9375:7.5625*(e-=2.625/2.75)*e+.984375},easeInOutBounce:e=>e<.5?Eo.easeInBounce(e*2)*.5:Eo.easeOutBounce(e*2-1)*.5+.5};function b0(e){if(e&&typeof e=="object"){const t=e.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function y0(e){return b0(e)?e:new ko(e)}function Rc(e){return b0(e)?e:new ko(e).saturate(.5).darken(.1).hexString()}const nN=["x","y","borderWidth","radius","tension"],rN=["color","borderColor","backgroundColor"];function sN(e){e.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),e.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),e.set("animations",{colors:{type:"color",properties:rN},numbers:{type:"number",properties:nN}}),e.describe("animations",{_fallback:"animation"}),e.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function oN(e){e.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const v0=new Map;function iN(e,t){t=t||{};const n=e+JSON.stringify(t);let r=v0.get(n);return r||(r=new Intl.NumberFormat(e,t),v0.set(n,r)),r}function ed(e,t,n){return iN(t,n).format(e)}const aN={values(e){return Mt(e)?e:""+e},numeric(e,t,n){if(e===0)return"0";const r=this.chart.options.locale;let s,o=e;if(n.length>1){const c=Math.max(Math.abs(n[0].value),Math.abs(n[n.length-1].value));(c<1e-4||c>1e15)&&(s="scientific"),o=lN(e,n)}const i=i0(Math.abs(o)),a=isNaN(i)?1:Math.max(Math.min(-1*Math.floor(i),20),0),l={notation:s,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),ed(e,r,l)}};function lN(e,t){let n=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(n)>=1&&e!==Math.floor(e)&&(n=e-Math.floor(e)),n}var x0={formatters:aN};function cN(e){e.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,n)=>n.lineWidth,tickColor:(t,n)=>n.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:x0.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),e.route("scale.ticks","color","","color"),e.route("scale.grid","color","","borderColor"),e.route("scale.border","color","","borderColor"),e.route("scale.title","color","","color"),e.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),e.describe("scales",{_fallback:"scale"}),e.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const $r=Object.create(null),td=Object.create(null);function Mo(e,t){if(!t)return e;const n=t.split(".");for(let r=0,s=n.length;rr.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(r,s)=>Rc(s.backgroundColor),this.hoverBorderColor=(r,s)=>Rc(s.borderColor),this.hoverColor=(r,s)=>Rc(s.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(n)}set(t,n){return nd(this,t,n)}get(t){return Mo(this,t)}describe(t,n){return nd(td,t,n)}override(t,n){return nd($r,t,n)}route(t,n,r,s){const o=Mo(this,t),i=Mo(this,r),a="_"+n;Object.defineProperties(o,{[a]:{value:o[n],writable:!0},[n]:{enumerable:!0,get(){const l=this[a],c=i[s];return Ze(l)?Object.assign({},c,l):Qe(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(n=>n(this))}}var wt=new dN({_scriptable:e=>!e.startsWith("on"),_indexable:e=>e!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[sN,oN,cN]);function uN(e){return!e||lt(e.size)||lt(e.family)?null:(e.style?e.style+" ":"")+(e.weight?e.weight+" ":"")+e.size+"px "+e.family}function w0(e,t,n,r,s){let o=t[s];return o||(o=t[s]=e.measureText(s).width,n.push(s)),o>r&&(r=o),r}function Fr(e,t,n){const r=e.currentDevicePixelRatio,s=n!==0?Math.max(n/2,.5):0;return Math.round((t-s)*r)/r+s}function k0(e,t){!t&&!e||(t=t||e.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,e.width,e.height),t.restore())}function C0(e,t,n,r){S0(e,t,n,r,null)}function S0(e,t,n,r,s){let o,i,a,l,c,d,u,f;const g=t.pointStyle,m=t.rotation,p=t.radius;let y=(m||0)*jM;if(g&&typeof g=="object"&&(o=g.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){e.save(),e.translate(n,r),e.rotate(y),e.drawImage(g,-g.width/2,-g.height/2,g.width,g.height),e.restore();return}if(!(isNaN(p)||p<=0)){switch(e.beginPath(),g){default:s?e.ellipse(n,r,s/2,p,0,0,_t):e.arc(n,r,p,0,_t),e.closePath();break;case"triangle":d=s?s/2:p,e.moveTo(n+Math.sin(y)*d,r-Math.cos(y)*p),y+=o0,e.lineTo(n+Math.sin(y)*d,r-Math.cos(y)*p),y+=o0,e.lineTo(n+Math.sin(y)*d,r-Math.cos(y)*p),e.closePath();break;case"rectRounded":c=p*.516,l=p-c,i=Math.cos(y+Lr)*l,u=Math.cos(y+Lr)*(s?s/2-c:l),a=Math.sin(y+Lr)*l,f=Math.sin(y+Lr)*(s?s/2-c:l),e.arc(n-u,r-a,c,y-ct,y-Nt),e.arc(n+f,r-i,c,y-Nt,y),e.arc(n+u,r+a,c,y,y+Nt),e.arc(n-f,r+i,c,y+Nt,y+ct),e.closePath();break;case"rect":if(!m){l=Math.SQRT1_2*p,d=s?s/2:l,e.rect(n-d,r-l,2*d,2*l);break}y+=Lr;case"rectRot":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),e.moveTo(n-u,r-a),e.lineTo(n+f,r-i),e.lineTo(n+u,r+a),e.lineTo(n-f,r+i),e.closePath();break;case"crossRot":y+=Lr;case"cross":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),e.moveTo(n-u,r-a),e.lineTo(n+u,r+a),e.moveTo(n+f,r-i),e.lineTo(n-f,r+i);break;case"star":u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),e.moveTo(n-u,r-a),e.lineTo(n+u,r+a),e.moveTo(n+f,r-i),e.lineTo(n-f,r+i),y+=Lr,u=Math.cos(y)*(s?s/2:p),i=Math.cos(y)*p,a=Math.sin(y)*p,f=Math.sin(y)*(s?s/2:p),e.moveTo(n-u,r-a),e.lineTo(n+u,r+a),e.moveTo(n+f,r-i),e.lineTo(n-f,r+i);break;case"line":i=s?s/2:Math.cos(y)*p,a=Math.sin(y)*p,e.moveTo(n-i,r-a),e.lineTo(n+i,r+a);break;case"dash":e.moveTo(n,r),e.lineTo(n+Math.cos(y)*(s?s/2:p),r+Math.sin(y)*p);break;case!1:e.closePath();break}e.fill(),t.borderWidth>0&&e.stroke()}}function _0(e,t,n){return n=n||.5,!t||e&&e.x>t.left-n&&e.xt.top-n&&e.y0&&o.strokeColor!=="";let l,c;for(e.save(),e.font=s.string,fN(e,o),l=0;l+e||0;function od(e,t){const n={},r=Ze(t),s=r?Object.keys(t):t,o=Ze(e)?r?i=>Qe(e[i],e[t[i]]):i=>e[i]:()=>e;for(const i of s)n[i]=yN(o(i));return n}function E0(e){return od(e,{top:"y",right:"x",bottom:"y",left:"x"})}function Ss(e){return od(e,["topLeft","topRight","bottomLeft","bottomRight"])}function yn(e){const t=E0(e);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function Gt(e,t){e=e||{},t=t||wt.font;let n=Qe(e.size,t.size);typeof n=="string"&&(n=parseInt(n,10));let r=Qe(e.style,t.style);r&&!(""+r).match(mN)&&(console.warn('Invalid font style specified: "'+r+'"'),r=void 0);const s={family:Qe(e.family,t.family),lineHeight:bN(Qe(e.lineHeight,t.lineHeight),n),size:n,style:r,weight:Qe(e.weight,t.weight),string:""};return s.string=uN(s),s}function pa(e,t,n,r){let s,o,i;for(s=0,o=e.length;sn&&a===0?0:a+l;return{min:i(r,-Math.abs(o)),max:i(s,o)}}function _s(e,t){return Object.assign(Object.create(e),t)}function id(e,t=[""],n,r,s=()=>e[0]){const o=n||e;typeof r>"u"&&(r=A0("_fallback",e));const i={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:e,_rootScopes:o,_fallback:r,_getTarget:s,override:a=>id([a,...e],t,o,r)};return new Proxy(i,{deleteProperty(a,l){return delete a[l],delete a._keys,delete e[0][l],!0},get(a,l){return N0(a,l,()=>MN(l,t,e,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(e[0])},has(a,l){return D0(a).includes(l)},ownKeys(a){return D0(a)},set(a,l,c){const d=a._storage||(a._storage=s());return a[l]=d[l]=c,delete a._keys,!0}})}function Es(e,t,n,r){const s={_cacheable:!1,_proxy:e,_context:t,_subProxy:n,_stack:new Set,_descriptors:M0(e,r),setContext:o=>Es(e,o,n,r),override:o=>Es(e.override(o),t,n,r)};return new Proxy(s,{deleteProperty(o,i){return delete o[i],delete e[i],!0},get(o,i,a){return N0(o,i,()=>wN(o,i,a))},getOwnPropertyDescriptor(o,i){return o._descriptors.allKeys?Reflect.has(e,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(e,i)},getPrototypeOf(){return Reflect.getPrototypeOf(e)},has(o,i){return Reflect.has(e,i)},ownKeys(){return Reflect.ownKeys(e)},set(o,i,a){return e[i]=a,delete o[i],!0}})}function M0(e,t={scriptable:!0,indexable:!0}){const{_scriptable:n=t.scriptable,_indexable:r=t.indexable,_allKeys:s=t.allKeys}=e;return{allKeys:s,scriptable:n,indexable:r,isScriptable:pr(n)?n:()=>n,isIndexable:pr(r)?r:()=>r}}const xN=(e,t)=>e?e+qc(t):t,ad=(e,t)=>Ze(t)&&e!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function N0(e,t,n){if(Object.prototype.hasOwnProperty.call(e,t)||t==="constructor")return e[t];const r=n();return e[t]=r,r}function wN(e,t,n){const{_proxy:r,_context:s,_subProxy:o,_descriptors:i}=e;let a=r[t];return pr(a)&&i.isScriptable(t)&&(a=kN(t,a,e,n)),Mt(a)&&a.length&&(a=CN(t,a,e,i.isIndexable)),ad(t,a)&&(a=Es(a,s,o&&o[t],i)),a}function kN(e,t,n,r){const{_proxy:s,_context:o,_subProxy:i,_stack:a}=n;if(a.has(e))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+e);a.add(e);let l=t(o,i||r);return a.delete(e),ad(e,l)&&(l=ld(s._scopes,s,e,l)),l}function CN(e,t,n,r){const{_proxy:s,_context:o,_subProxy:i,_descriptors:a}=n;if(typeof o.index<"u"&&r(e))return t[o.index%t.length];if(Ze(t[0])){const l=t,c=s._scopes.filter(d=>d!==l);t=[];for(const d of l){const u=ld(c,s,e,d);t.push(Es(u,o,i&&i[e],a))}}return t}function T0(e,t,n){return pr(e)?e(t,n):e}const SN=(e,t)=>e===!0?t:typeof e=="string"?Ir(t,e):void 0;function _N(e,t,n,r,s){for(const o of t){const i=SN(n,o);if(i){e.add(i);const a=T0(i._fallback,n,s);if(typeof a<"u"&&a!==n&&a!==r)return a}else if(i===!1&&typeof r<"u"&&n!==r)return null}return!1}function ld(e,t,n,r){const s=t._rootScopes,o=T0(t._fallback,n,r),i=[...e,...s],a=new Set;a.add(r);let l=O0(a,i,n,o||n,r);return l===null||typeof o<"u"&&o!==n&&(l=O0(a,i,o,l,r),l===null)?!1:id(Array.from(a),[""],s,o,()=>EN(t,n,r))}function O0(e,t,n,r,s){for(;n;)n=_N(e,t,n,r,s);return n}function EN(e,t,n){const r=e._getTarget();t in r||(r[t]={});const s=r[t];return Mt(s)&&Ze(n)?n:s||{}}function MN(e,t,n,r){let s;for(const o of t)if(s=A0(xN(o,e),n),typeof s<"u")return ad(e,s)?ld(n,r,e,s):s}function A0(e,t){for(const n of t){if(!n)continue;const r=n[e];if(typeof r<"u")return r}}function D0(e){let t=e._keys;return t||(t=e._keys=NN(e._scopes)),t}function NN(e){const t=new Set;for(const n of e)for(const r of Object.keys(n).filter(s=>!s.startsWith("_")))t.add(r);return Array.from(t)}function cd(){return typeof window<"u"&&typeof document<"u"}function dd(e){let t=e.parentNode;return t&&t.toString()==="[object ShadowRoot]"&&(t=t.host),t}function ma(e,t,n){let r;return typeof e=="string"?(r=parseInt(e,10),e.indexOf("%")!==-1&&(r=r/100*t.parentNode[n])):r=e,r}const ba=e=>e.ownerDocument.defaultView.getComputedStyle(e,null);function TN(e,t){return ba(e).getPropertyValue(t)}const ON=["top","right","bottom","left"];function zr(e,t,n){const r={};n=n?"-"+n:"";for(let s=0;s<4;s++){const o=ON[s];r[o]=parseFloat(e[t+"-"+o+n])||0}return r.width=r.left+r.right,r.height=r.top+r.bottom,r}const AN=(e,t,n)=>(e>0||t>0)&&(!n||!n.shadowRoot);function DN(e,t){const n=e.touches,r=n&&n.length?n[0]:e,{offsetX:s,offsetY:o}=r;let i=!1,a,l;if(AN(s,o,e.target))a=s,l=o;else{const c=t.getBoundingClientRect();a=r.clientX-c.left,l=r.clientY-c.top,i=!0}return{x:a,y:l,box:i}}function Vr(e,t){if("native"in e)return e;const{canvas:n,currentDevicePixelRatio:r}=t,s=ba(n),o=s.boxSizing==="border-box",i=zr(s,"padding"),a=zr(s,"border","width"),{x:l,y:c,box:d}=DN(e,n),u=i.left+(d&&a.left),f=i.top+(d&&a.top);let{width:g,height:m}=t;return o&&(g-=i.width+a.width,m-=i.height+a.height),{x:Math.round((l-u)/g*n.width/r),y:Math.round((c-f)/m*n.height/r)}}function IN(e,t,n){let r,s;if(t===void 0||n===void 0){const o=e&&dd(e);if(!o)t=e.clientWidth,n=e.clientHeight;else{const i=o.getBoundingClientRect(),a=ba(o),l=zr(a,"border","width"),c=zr(a,"padding");t=i.width-c.width-l.width,n=i.height-c.height-l.height,r=ma(a.maxWidth,o,"clientWidth"),s=ma(a.maxHeight,o,"clientHeight")}}return{width:t,height:n,maxWidth:r||ca,maxHeight:s||ca}}const br=e=>Math.round(e*10)/10;function LN(e,t,n,r){const s=ba(e),o=zr(s,"margin"),i=ma(s.maxWidth,e,"clientWidth")||ca,a=ma(s.maxHeight,e,"clientHeight")||ca,l=IN(e,t,n);let{width:c,height:d}=l;if(s.boxSizing==="content-box"){const f=zr(s,"border","width"),g=zr(s,"padding");c-=g.width+f.width,d-=g.height+f.height}return c=Math.max(0,c-o.width),d=Math.max(0,r?c/r:d-o.height),c=br(Math.min(c,i,l.maxWidth)),d=br(Math.min(d,a,l.maxHeight)),c&&!d&&(d=br(c/2)),(t!==void 0||n!==void 0)&&r&&l.height&&d>l.height&&(d=l.height,c=br(Math.floor(d*r))),{width:c,height:d}}function I0(e,t,n){const r=t||1,s=br(e.height*r),o=br(e.width*r);e.height=br(e.height),e.width=br(e.width);const i=e.canvas;return i.style&&(n||!i.style.height&&!i.style.width)&&(i.style.height=`${e.height}px`,i.style.width=`${e.width}px`),e.currentDevicePixelRatio!==r||i.height!==s||i.width!==o?(e.currentDevicePixelRatio=r,i.height=s,i.width=o,e.ctx.setTransform(r,0,0,r,0,0),!0):!1}const PN=(function(){let e=!1;try{const t={get passive(){return e=!0,!1}};cd()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return e})();function L0(e,t){const n=TN(e,t),r=n&&n.match(/^(\d+)(\.\d+)?px$/);return r?+r[1]:void 0}const $N=function(e,t){return{x(n){return e+e+t-n},setWidth(n){t=n},textAlign(n){return n==="center"?n:n==="right"?"left":"right"},xPlus(n,r){return n-r},leftForLtr(n,r){return n-r}}},FN=function(){return{x(e){return e},setWidth(e){},textAlign(e){return e},xPlus(e,t){return e+t},leftForLtr(e,t){return e}}};function Ms(e,t,n){return e?$N(t,n):FN()}function P0(e,t){let n,r;(t==="ltr"||t==="rtl")&&(n=e.canvas.style,r=[n.getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",t,"important"),e.prevTextDirection=r)}function $0(e,t){t!==void 0&&(delete e.prevTextDirection,e.canvas.style.setProperty("direction",t[0],t[1]))}function ya(e,t,n){return e.options.clip?e[n]:t[n]}function zN(e,t){const{xScale:n,yScale:r}=e;return n&&r?{left:ya(n,t,"left"),right:ya(n,t,"right"),top:ya(r,t,"top"),bottom:ya(r,t,"bottom")}:t}function VN(e,t){const n=t._clip;if(n.disabled)return!1;const r=zN(t,e.chartArea);return{left:n.left===!1?0:r.left-(n.left===!0?0:n.left),right:n.right===!1?e.width:r.right+(n.right===!0?0:n.right),top:n.top===!1?0:r.top-(n.top===!0?0:n.top),bottom:n.bottom===!1?e.height:r.bottom+(n.bottom===!0?0:n.bottom)}}class HN{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,n,r,s){const o=n.listeners[s],i=n.duration;o.forEach(a=>a({chart:t,initial:n.initial,numSteps:i,currentStep:Math.min(r-n.start,i)}))}_refresh(){this._request||(this._running=!0,this._request=h0.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let n=0;this._charts.forEach((r,s)=>{if(!r.running||!r.items.length)return;const o=r.items;let i=o.length-1,a=!1,l;for(;i>=0;--i)l=o[i],l._active?(l._total>r.duration&&(r.duration=l._total),l.tick(t),a=!0):(o[i]=o[o.length-1],o.pop());a&&(s.draw(),this._notify(s,r,t,"progress")),o.length||(r.running=!1,this._notify(s,r,t,"complete"),r.initial=!1),n+=o.length}),this._lastDate=t,n===0&&(this._running=!1)}_getAnims(t){const n=this._charts;let r=n.get(t);return r||(r={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},n.set(t,r)),r}listen(t,n,r){this._getAnims(t).listeners[n].push(r)}add(t,n){!n||!n.length||this._getAnims(t).items.push(...n)}has(t){return this._getAnims(t).items.length>0}start(t){const n=this._charts.get(t);n&&(n.running=!0,n.start=Date.now(),n.duration=n.items.reduce((r,s)=>Math.max(r,s._duration),0),this._refresh())}running(t){if(!this._running)return!1;const n=this._charts.get(t);return!(!n||!n.running||!n.items.length)}stop(t){const n=this._charts.get(t);if(!n||!n.items.length)return;const r=n.items;let s=r.length-1;for(;s>=0;--s)r[s].cancel();n.items=[],this._notify(t,n,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var Jn=new HN;const F0="transparent",UN={boolean(e,t,n){return n>.5?t:e},color(e,t,n){const r=y0(e||F0),s=r.valid&&y0(t||F0);return s&&s.valid?s.mix(r,n).hexString():t},number(e,t,n){return e+(t-e)*n}};class BN{constructor(t,n,r,s){const o=n[r];s=pa([t.to,s,o,t.from]);const i=pa([t.from,o,s]);this._active=!0,this._fn=t.fn||UN[t.type||typeof i],this._easing=Eo[t.easing]||Eo.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=n,this._prop=r,this._from=i,this._to=s,this._promises=void 0}active(){return this._active}update(t,n,r){if(this._active){this._notify(!1);const s=this._target[this._prop],o=r-this._start,i=this._duration-o;this._start=r,this._duration=Math.floor(Math.max(i,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=pa([t.to,n,s,t.from]),this._from=pa([t.from,s,n])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const n=t-this._start,r=this._duration,s=this._prop,o=this._from,i=this._loop,a=this._to;let l;if(this._active=o!==a&&(i||n1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[s]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((n,r)=>{t.push({res:n,rej:r})})}_notify(t){const n=t?"res":"rej",r=this._promises||[];for(let s=0;s{const o=t[s];if(!Ze(o))return;const i={};for(const a of n)i[a]=o[a];(Mt(o.properties)&&o.properties||[s]).forEach(a=>{(a===s||!r.has(a))&&r.set(a,i)})})}_animateOptions(t,n){const r=n.options,s=WN(t,r);if(!s)return[];const o=this._createAnimations(s,r);return r.$shared&&jN(t.options.$animations,r).then(()=>{t.options=r},()=>{}),o}_createAnimations(t,n){const r=this._properties,s=[],o=t.$animations||(t.$animations={}),i=Object.keys(n),a=Date.now();let l;for(l=i.length-1;l>=0;--l){const c=i[l];if(c.charAt(0)==="$")continue;if(c==="options"){s.push(...this._animateOptions(t,n));continue}const d=n[c];let u=o[c];const f=r.get(c);if(u)if(f&&u.active()){u.update(f,d,a);continue}else u.cancel();if(!f||!f.duration){t[c]=d;continue}o[c]=u=new BN(f,t,c,d),s.push(u)}return s}update(t,n){if(this._properties.size===0){Object.assign(t,n);return}const r=this._createAnimations(t,n);if(r.length)return Jn.add(this._chart,r),!0}}function jN(e,t){const n=[],r=Object.keys(t);for(let s=0;s0||!n&&o<0)return s.index}return null}function j0(e,t){const{chart:n,_cachedMeta:r}=e,s=n._stacks||(n._stacks={}),{iScale:o,vScale:i,index:a}=r,l=o.axis,c=i.axis,d=KN(o,i,r),u=t.length;let f;for(let g=0;gn[r].axis===t).shift()}function QN(e,t){return _s(e,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function JN(e,t,n){return _s(e,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:n,index:t,mode:"default",type:"data"})}function To(e,t){const n=e.controller.index,r=e.vScale&&e.vScale.axis;if(r){t=t||e._parsed;for(const s of t){const o=s._stacks;if(!o||o[r]===void 0||o[r][n]===void 0)return;delete o[r][n],o[r]._visualValues!==void 0&&o[r]._visualValues[n]!==void 0&&delete o[r]._visualValues[n]}}}const hd=e=>e==="reset"||e==="none",W0=(e,t)=>t?e:Object.assign({},e),RN=(e,t,n)=>e&&!t.hidden&&t._stacked&&{keys:H0(n,!0),values:null};class Ns{constructor(t,n){this.chart=t,this._ctx=t.ctx,this.index=n,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=ud(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&To(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,n=this._cachedMeta,r=this.getDataset(),s=(u,f,g,m)=>u==="x"?f:u==="r"?m:g,o=n.xAxisID=Qe(r.xAxisID,fd(t,"x")),i=n.yAxisID=Qe(r.yAxisID,fd(t,"y")),a=n.rAxisID=Qe(r.rAxisID,fd(t,"r")),l=n.indexAxis,c=n.iAxisID=s(l,o,i,a),d=n.vAxisID=s(l,i,o,a);n.xScale=this.getScaleForId(o),n.yScale=this.getScaleForId(i),n.rScale=this.getScaleForId(a),n.iScale=this.getScaleForId(c),n.vScale=this.getScaleForId(d)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const n=this._cachedMeta;return t===n.iScale?n.vScale:n.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&u0(this._data,this),t._stacked&&To(t)}_dataCheck(){const t=this.getDataset(),n=t.data||(t.data=[]),r=this._data;if(Ze(n)){const s=this._cachedMeta;this._data=XN(n,s)}else if(r!==n){if(r){u0(r,this);const s=this._cachedMeta;To(s),s._parsed=[]}n&&Object.isExtensible(n)&&RM(n,this),this._syncList=[],this._data=n}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const n=this._cachedMeta,r=this.getDataset();let s=!1;this._dataCheck();const o=n._stacked;n._stacked=ud(n.vScale,n),n.stack!==r.stack&&(s=!0,To(n),n.stack=r.stack),this._resyncElements(t),(s||o!==n._stacked)&&(j0(this,n._parsed),n._stacked=ud(n.vScale,n))}configure(){const t=this.chart.config,n=t.datasetScopeKeys(this._type),r=t.getOptionScopes(this.getDataset(),n,!0);this.options=t.createResolver(r,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,n){const{_cachedMeta:r,_data:s}=this,{iScale:o,_stacked:i}=r,a=o.axis;let l=t===0&&n===s.length?!0:r._sorted,c=t>0&&r._parsed[t-1],d,u,f;if(this._parsing===!1)r._parsed=s,r._sorted=!0,f=s;else{Mt(s[t])?f=this.parseArrayData(r,s,t,n):Ze(s[t])?f=this.parseObjectData(r,s,t,n):f=this.parsePrimitiveData(r,s,t,n);const g=()=>u[a]===null||c&&u[a]p||u=0;--f)if(!m()){this.updateRangeFromParsed(c,t,g,l);break}}return c}getAllParsedValues(t){const n=this._cachedMeta._parsed,r=[];let s,o,i;for(s=0,o=n.length;s=0&&tthis.getContext(r,s,n),p=c.resolveNamedOptions(f,g,m,u);return p.$shared&&(p.$shared=l,o[i]=Object.freeze(W0(p,l))),p}_resolveAnimations(t,n,r){const s=this.chart,o=this._cachedDataOpts,i=`animation-${n}`,a=o[i];if(a)return a;let l;if(s.options.animation!==!1){const d=this.chart.config,u=d.datasetAnimationScopeKeys(this._type,n),f=d.getOptionScopes(this.getDataset(),u);l=d.createResolver(f,this.getContext(t,r,n))}const c=new z0(s,l&&l.animations);return l&&l._cacheable&&(o[i]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,n){return!n||hd(t)||this.chart._animationsDisabled}_getSharedOptions(t,n){const r=this.resolveDataElementOptions(t,n),s=this._sharedOptions,o=this.getSharedOptions(r),i=this.includeOptions(n,o)||o!==s;return this.updateSharedOptions(o,n,r),{sharedOptions:o,includeOptions:i}}updateElement(t,n,r,s){hd(s)?Object.assign(t,r):this._resolveAnimations(n,s).update(t,r)}updateSharedOptions(t,n,r){t&&!hd(n)&&this._resolveAnimations(void 0,n).update(t,r)}_setStyle(t,n,r,s){t.active=s;const o=this.getStyle(n,s);this._resolveAnimations(n,r,s).update(t,{options:!s&&this.getSharedOptions(o)||o})}removeHoverStyle(t,n,r){this._setStyle(t,r,"active",!1)}setHoverStyle(t,n,r){this._setStyle(t,r,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const n=this._data,r=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const s=r.length,o=n.length,i=Math.min(o,s);i&&this.parse(0,i),o>s?this._insertElements(s,o-s,t):o{for(c.length+=n,a=c.length-1;a>=i;a--)c[a]=c[a-n]};for(l(o),a=t;as-o))}return e._cache.$bar}function tT(e){const t=e.iScale,n=eT(t,e.type);let r=t._length,s,o,i,a;const l=()=>{i===32767||i===-32768||(_o(a)&&(r=Math.min(r,Math.abs(i-a)||r)),a=i)};for(s=0,o=n.length;s0?s[e-1]:null,a=eMath.abs(a)&&(l=a,c=i),t[n.axis]=c,t._custom={barStart:l,barEnd:c,start:s,end:o,min:i,max:a}}function G0(e,t,n,r){return Mt(e)?sT(e,t,n,r):t[n.axis]=n.parse(e,r),t}function Y0(e,t,n,r){const s=e.iScale,o=e.vScale,i=s.getLabels(),a=s===o,l=[];let c,d,u,f;for(c=n,d=n+r;c=n?1:-1)}function iT(e){let t,n,r,s,o;return e.horizontal?(t=e.base>e.x,n="left",r="right"):(t=e.based.controller.options.grouped),o=r.options.stacked,i=[],a=this._cachedMeta.controller.getParsed(n),l=a&&a[r.axis],c=d=>{const u=d._parsed.find(g=>g[r.axis]===l),f=u&&u[d.vScale.axis];if(lt(f)||isNaN(f))return!0};for(const d of s)if(!(n!==void 0&&c(d))&&((o===!1||i.indexOf(d.stack)===-1||o===void 0&&d.stack===void 0)&&i.push(d.stack),d.index===t))break;return i.length||i.push(void 0),i}_getStackCount(t){return this._getStacks(void 0,t).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const t=this.chart.scales,n=this.chart.options.indexAxis;return Object.keys(t).filter(r=>t[r].axis===n).shift()}_getAxis(){const t={},n=this.getFirstScaleIdForIndexAxis();for(const r of this.chart.data.datasets)t[Qe(this.chart.options.indexAxis==="x"?r.xAxisID:r.yAxisID,n)]=!0;return Object.keys(t)}_getStackIndex(t,n,r){const s=this._getStacks(t,r),o=n!==void 0?s.indexOf(n):-1;return o===-1?s.length-1:o}_getRuler(){const t=this.options,n=this._cachedMeta,r=n.iScale,s=[];let o,i;for(o=0,i=n.data.length;ofa(x,a,l,!0)?1:Math.max(M,M*n,S,S*n),m=(x,M,S)=>fa(x,a,l,!0)?-1:Math.min(M,M*n,S,S*n),p=g(0,c,u),y=g(Nt,d,f),b=m(ct,c,u),_=m(ct+Nt,d,f);r=(p-b)/2,s=(y-_)/2,o=-(p+b)/2,i=-(y+_)/2}return{ratioX:r,ratioY:s,offsetX:o,offsetY:i}}class Oo extends Ns{constructor(t,n){super(t,n),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,n){const r=this.getDataset().data,s=this._cachedMeta;if(this._parsing===!1)s._parsed=r;else{let o=l=>+r[l];if(Ze(r[t])){const{key:l="value"}=this._parsing;o=c=>+Ir(r[c],l)}let i,a;for(i=t,a=t+n;i0&&!isNaN(t)?_t*(Math.abs(t)/n):0}getLabelAndValue(t){const n=this._cachedMeta,r=this.chart,s=r.data.labels||[],o=ed(n._parsed[t],r.options.locale);return{label:s[t]||"",value:o}}getMaxBorderWidth(t){let n=0;const r=this.chart;let s,o,i,a,l;if(!t){for(s=0,o=r.data.datasets.length;st!=="spacing",_indexable:t=>t!=="spacing"&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")}),Pe(Oo,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const n=t.data,{labels:{pointStyle:r,textAlign:s,color:o,useBorderRadius:i,borderRadius:a}}=t.legend.options;return n.labels.length&&n.datasets.length?n.labels.map((l,c)=>{const u=t.getDatasetMeta(0).controller.getStyle(c);return{text:l,fillStyle:u.backgroundColor,fontColor:o,hidden:!t.getDataVisibility(c),lineDash:u.borderDash,lineDashOffset:u.borderDashOffset,lineJoin:u.borderJoinStyle,lineWidth:u.borderWidth,strokeStyle:u.borderColor,textAlign:s,pointStyle:r,borderRadius:i&&(a||u.borderRadius),index:c}}):[]}},onClick(t,n,r){r.chart.toggleDataVisibility(n.index),r.chart.update()}}}});function Hr(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class pd{constructor(t){Pe(this,"options");this.options=t||{}}static override(t){Object.assign(pd.prototype,t)}init(){}formats(){return Hr()}parse(){return Hr()}format(){return Hr()}add(){return Hr()}diff(){return Hr()}startOf(){return Hr()}endOf(){return Hr()}}var uT={_date:pd};function fT(e,t,n,r){const{controller:s,data:o,_sorted:i}=e,a=s._cachedMeta.iScale,l=e.dataset&&e.dataset.options?e.dataset.options.spanGaps:null;if(a&&t===a.axis&&t!=="r"&&i&&o.length){const c=a._reversePixels?QM:Qc;if(r){if(s._sharedOptions){const d=o[0],u=typeof d.getRange=="function"&&d.getRange(t);if(u){const f=c(o,t,n-u),g=c(o,t,n+u);return{lo:f.lo,hi:g.hi}}}}else{const d=c(o,t,n);if(l){const{vScale:u}=s._cachedMeta,{_parsed:f}=e,g=f.slice(0,d.lo+1).reverse().findIndex(p=>!lt(p[u.axis]));d.lo-=Math.max(0,g);const m=f.slice(d.hi).findIndex(p=>!lt(p[u.axis]));d.hi+=Math.max(0,m)}return d}}return{lo:0,hi:o.length-1}}function xa(e,t,n,r,s){const o=e.getSortedVisibleDatasetMetas(),i=n[t];for(let a=0,l=o.length;a{l[i]&&l[i](t[n],s)&&(o.push({element:l,datasetIndex:c,index:d}),a=a||l.inRange(t.x,t.y,s))}),r&&!a?[]:o}var mT={modes:{index(e,t,n,r){const s=Vr(t,e),o=n.axis||"x",i=n.includeInvisible||!1,a=n.intersect?md(e,s,o,r,i):bd(e,s,o,!1,r,i),l=[];return a.length?(e.getSortedVisibleDatasetMetas().forEach(c=>{const d=a[0].index,u=c.data[d];u&&!u.skip&&l.push({element:u,datasetIndex:c.index,index:d})}),l):[]},dataset(e,t,n,r){const s=Vr(t,e),o=n.axis||"xy",i=n.includeInvisible||!1;let a=n.intersect?md(e,s,o,r,i):bd(e,s,o,!1,r,i);if(a.length>0){const l=a[0].datasetIndex,c=e.getDatasetMeta(l).data;a=[];for(let d=0;dn.pos===t)}function Q0(e,t){return e.filter(n=>Z0.indexOf(n.pos)===-1&&n.box.axis===t)}function Do(e,t){return e.sort((n,r)=>{const s=t?r:n,o=t?n:r;return s.weight===o.weight?s.index-o.index:s.weight-o.weight})}function bT(e){const t=[];let n,r,s,o,i,a;for(n=0,r=(e||[]).length;nc.box.fullSize),!0),r=Do(Ao(t,"left"),!0),s=Do(Ao(t,"right")),o=Do(Ao(t,"top"),!0),i=Do(Ao(t,"bottom")),a=Q0(t,"x"),l=Q0(t,"y");return{fullSize:n,leftAndTop:r.concat(o),rightAndBottom:s.concat(l).concat(i).concat(a),chartArea:Ao(t,"chartArea"),vertical:r.concat(s).concat(l),horizontal:o.concat(i).concat(a)}}function J0(e,t,n,r){return Math.max(e[n],t[n])+Math.max(e[r],t[r])}function R0(e,t){e.top=Math.max(e.top,t.top),e.left=Math.max(e.left,t.left),e.bottom=Math.max(e.bottom,t.bottom),e.right=Math.max(e.right,t.right)}function wT(e,t,n,r){const{pos:s,box:o}=n,i=e.maxPadding;if(!Ze(s)){n.size&&(e[s]-=n.size);const u=r[n.stack]||{size:0,count:1};u.size=Math.max(u.size,n.horizontal?o.height:o.width),n.size=u.size/u.count,e[s]+=n.size}o.getPadding&&R0(i,o.getPadding());const a=Math.max(0,t.outerWidth-J0(i,e,"left","right")),l=Math.max(0,t.outerHeight-J0(i,e,"top","bottom")),c=a!==e.w,d=l!==e.h;return e.w=a,e.h=l,n.horizontal?{same:c,other:d}:{same:d,other:c}}function kT(e){const t=e.maxPadding;function n(r){const s=Math.max(t[r]-e[r],0);return e[r]+=s,s}e.y+=n("top"),e.x+=n("left"),n("right"),n("bottom")}function CT(e,t){const n=t.maxPadding;function r(s){const o={left:0,top:0,right:0,bottom:0};return s.forEach(i=>{o[i]=Math.max(t[i],n[i])}),o}return r(e?["left","right"]:["top","bottom"])}function Io(e,t,n,r){const s=[];let o,i,a,l,c,d;for(o=0,i=e.length,c=0;o{typeof p.beforeLayout=="function"&&p.beforeLayout()});const d=l.reduce((p,y)=>y.box.options&&y.box.options.display===!1?p:p+1,0)||1,u=Object.freeze({outerWidth:t,outerHeight:n,padding:s,availableWidth:o,availableHeight:i,vBoxMaxWidth:o/2/d,hBoxMaxHeight:i/2}),f=Object.assign({},s);R0(f,yn(r));const g=Object.assign({maxPadding:f,w:o,h:i,x:s.left,y:s.top},s),m=vT(l.concat(c),u);Io(a.fullSize,g,u,m),Io(l,g,u,m),Io(c,g,u,m)&&Io(l,g,u,m),kT(g),em(a.leftAndTop,g,u,m),g.x+=g.w,g.y+=g.h,em(a.rightAndBottom,g,u,m),e.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},st(a.chartArea,p=>{const y=p.box;Object.assign(y,e.chartArea),y.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})})}};class tm{acquireContext(t,n){}releaseContext(t){return!1}addEventListener(t,n,r){}removeEventListener(t,n,r){}getDevicePixelRatio(){return 1}getMaximumSize(t,n,r,s){return n=Math.max(0,n||t.width),r=r||t.height,{width:n,height:Math.max(0,s?Math.floor(n/s):r)}}isAttached(t){return!0}updateConfig(t){}}class ST extends tm{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ka="$chartjs",_T={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},nm=e=>e===null||e==="";function ET(e,t){const n=e.style,r=e.getAttribute("height"),s=e.getAttribute("width");if(e[ka]={initial:{height:r,width:s,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",n.boxSizing=n.boxSizing||"border-box",nm(s)){const o=L0(e,"width");o!==void 0&&(e.width=o)}if(nm(r))if(e.style.height==="")e.height=e.width/(t||2);else{const o=L0(e,"height");o!==void 0&&(e.height=o)}return e}const rm=PN?{passive:!0}:!1;function MT(e,t,n){e&&e.addEventListener(t,n,rm)}function NT(e,t,n){e&&e.canvas&&e.canvas.removeEventListener(t,n,rm)}function TT(e,t){const n=_T[e.type]||e.type,{x:r,y:s}=Vr(e,t);return{type:n,chart:t,native:e,x:r!==void 0?r:null,y:s!==void 0?s:null}}function Ca(e,t){for(const n of e)if(n===t||n.contains(t))return!0}function OT(e,t,n){const r=e.canvas,s=new MutationObserver(o=>{let i=!1;for(const a of o)i=i||Ca(a.addedNodes,r),i=i&&!Ca(a.removedNodes,r);i&&n()});return s.observe(document,{childList:!0,subtree:!0}),s}function AT(e,t,n){const r=e.canvas,s=new MutationObserver(o=>{let i=!1;for(const a of o)i=i||Ca(a.removedNodes,r),i=i&&!Ca(a.addedNodes,r);i&&n()});return s.observe(document,{childList:!0,subtree:!0}),s}const Lo=new Map;let sm=0;function om(){const e=window.devicePixelRatio;e!==sm&&(sm=e,Lo.forEach((t,n)=>{n.currentDevicePixelRatio!==e&&t()}))}function DT(e,t){Lo.size||window.addEventListener("resize",om),Lo.set(e,t)}function IT(e){Lo.delete(e),Lo.size||window.removeEventListener("resize",om)}function LT(e,t,n){const r=e.canvas,s=r&&dd(r);if(!s)return;const o=g0((a,l)=>{const c=s.clientWidth;n(a,l),c{const l=a[0],c=l.contentRect.width,d=l.contentRect.height;c===0&&d===0||o(c,d)});return i.observe(s),DT(e,o),i}function yd(e,t,n){n&&n.disconnect(),t==="resize"&&IT(e)}function PT(e,t,n){const r=e.canvas,s=g0(o=>{e.ctx!==null&&n(TT(o,e))},e);return MT(r,t,s),s}class $T extends tm{acquireContext(t,n){const r=t&&t.getContext&&t.getContext("2d");return r&&r.canvas===t?(ET(t,n),r):null}releaseContext(t){const n=t.canvas;if(!n[ka])return!1;const r=n[ka].initial;["height","width"].forEach(o=>{const i=r[o];lt(i)?n.removeAttribute(o):n.setAttribute(o,i)});const s=r.style||{};return Object.keys(s).forEach(o=>{n.style[o]=s[o]}),n.width=n.width,delete n[ka],!0}addEventListener(t,n,r){this.removeEventListener(t,n);const s=t.$proxies||(t.$proxies={}),i={attach:OT,detach:AT,resize:LT}[n]||PT;s[n]=i(t,n,r)}removeEventListener(t,n){const r=t.$proxies||(t.$proxies={}),s=r[n];if(!s)return;({attach:yd,detach:yd,resize:yd}[n]||NT)(t,n,s),r[n]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,n,r,s){return LN(t,n,r,s)}isAttached(t){const n=t&&dd(t);return!!(n&&n.isConnected)}}function FT(e){return!cd()||typeof OffscreenCanvas<"u"&&e instanceof OffscreenCanvas?ST:$T}let Ur=(za=class{constructor(){Pe(this,"x");Pe(this,"y");Pe(this,"active",!1);Pe(this,"options");Pe(this,"$animations")}tooltipPosition(t){const{x:n,y:r}=this.getProps(["x","y"],t);return{x:n,y:r}}hasValue(){return ua(this.x)&&ua(this.y)}getProps(t,n){const r=this.$animations;if(!n||!r)return this;const s={};return t.forEach(o=>{s[o]=r[o]&&r[o].active()?r[o]._to:this[o]}),s}},Pe(za,"defaults",{}),Pe(za,"defaultRoutes"),za);function zT(e,t){const n=e.options.ticks,r=VT(e),s=Math.min(n.maxTicksLimit||r,r),o=n.major.enabled?UT(t):[],i=o.length,a=o[0],l=o[i-1],c=[];if(i>s)return BT(t,c,o,i/s),c;const d=HT(o,t,s);if(i>0){let u,f;const g=i>1?Math.round((l-a)/(i-1)):null;for(Sa(t,c,d,lt(g)?0:a-g,a),u=0,f=i-1;us)return l}return Math.max(s,1)}function UT(e){const t=[];let n,r;for(n=0,r=e.length;ne==="left"?"right":e==="right"?"left":e,im=(e,t,n)=>t==="top"||t==="left"?e[t]+n:e[t]-n,am=(e,t)=>Math.min(t||e,e);function lm(e,t){const n=[],r=e.length/t,s=e.length;let o=0;for(;oi+a)))return l}function YT(e,t){st(e,n=>{const r=n.gc,s=r.length/2;let o;if(s>t){for(o=0;or?r:n,r=s&&n>r?n:r,{min:jn(n,jn(r,n)),max:jn(r,jn(n,r))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){ht(this.options.beforeUpdate,[this])}update(t,n,r){const{beginAtZero:s,grace:o,ticks:i}=this.options,a=i.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=n,this._margins=r=Object.assign({left:0,right:0,top:0,bottom:0},r),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+r.left+r.right:this.height+r.top+r.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=vN(this,o,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||r<=1||!this.isHorizontal()){this.labelRotation=s;return}const d=this._getLabelSizes(),u=d.widest.width,f=d.highest.height,g=nn(this.chart.width-u,0,this.maxWidth);a=t.offset?this.maxWidth/r:g/(r-1),u+6>a&&(a=g/(r-(t.offset?.5:1)),l=this.maxHeight-Po(t.grid)-n.padding-cm(t.title,this.chart.options.font),c=Math.sqrt(u*u+f*f),i=KM(Math.min(Math.asin(nn((d.highest.height+6)/a,-1,1)),Math.asin(nn(l/c,-1,1))-Math.asin(nn(f/c,-1,1)))),i=Math.max(s,Math.min(o,i))),this.labelRotation=i}afterCalculateLabelRotation(){ht(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){ht(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:n,options:{ticks:r,title:s,grid:o}}=this,i=this._isVisible(),a=this.isHorizontal();if(i){const l=cm(s,n.options.font);if(a?(t.width=this.maxWidth,t.height=Po(o)+l):(t.height=this.maxHeight,t.width=Po(o)+l),r.display&&this.ticks.length){const{first:c,last:d,widest:u,highest:f}=this._getLabelSizes(),g=r.padding*2,m=Qn(this.labelRotation),p=Math.cos(m),y=Math.sin(m);if(a){const b=r.mirror?0:y*u.width+p*f.height;t.height=Math.min(this.maxHeight,t.height+b+g)}else{const b=r.mirror?0:p*u.width+y*f.height;t.width=Math.min(this.maxWidth,t.width+b+g)}this._calculatePadding(c,d,y,p)}}this._handleMargins(),a?(this.width=this._length=n.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=n.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,n,r,s){const{ticks:{align:o,padding:i},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const d=this.getPixelForTick(0)-this.left,u=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,g=0;l?c?(f=s*t.width,g=r*n.height):(f=r*t.height,g=s*n.width):o==="start"?g=n.width:o==="end"?f=t.width:o!=="inner"&&(f=t.width/2,g=n.width/2),this.paddingLeft=Math.max((f-d+i)*this.width/(this.width-d),0),this.paddingRight=Math.max((g-u+i)*this.width/(this.width-u),0)}else{let d=n.height/2,u=t.height/2;o==="start"?(d=0,u=t.height):o==="end"&&(d=n.height,u=0),this.paddingTop=d+i,this.paddingBottom=u+i}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){ht(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:n}=this.options;return n==="top"||n==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let n,r;for(n=0,r=t.length;n({width:i[w]||0,height:a[w]||0});return{first:C(0),last:C(n-1),widest:C(k),highest:C(N),widths:i,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,n){return NaN}getValueForPixel(t){}getPixelForTick(t){const n=this.ticks;return t<0||t>n.length-1?null:this.getPixelForValue(n[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const n=this._startPixel+t*this._length;return ZM(this._alignToPixels?Fr(this.chart,n,0):n)}getDecimalForPixel(t){const n=(t-this._startPixel)/this._length;return this._reversePixels?1-n:n}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:n}=this;return t<0&&n<0?n:t>0&&n>0?t:0}getContext(t){const n=this.ticks||[];if(t>=0&&ta*s?a/r:l/s:l*s0}_computeGridLineItems(t){const n=this.axis,r=this.chart,s=this.options,{grid:o,position:i,border:a}=s,l=o.offset,c=this.isHorizontal(),u=this.ticks.length+(l?1:0),f=Po(o),g=[],m=a.setContext(this.getContext()),p=m.display?m.width:0,y=p/2,b=function(O){return Fr(r,O,p)};let _,x,M,S,k,N,C,w,E,A,P,L;if(i==="top")_=b(this.bottom),N=this.bottom-f,w=_-y,A=b(t.top)+y,L=t.bottom;else if(i==="bottom")_=b(this.top),A=t.top,L=b(t.bottom)-y,N=_+y,w=this.top+f;else if(i==="left")_=b(this.right),k=this.right-f,C=_-y,E=b(t.left)+y,P=t.right;else if(i==="right")_=b(this.left),E=t.left,P=b(t.right)-y,k=_+y,C=this.left+f;else if(n==="x"){if(i==="center")_=b((t.top+t.bottom)/2+.5);else if(Ze(i)){const O=Object.keys(i)[0],T=i[O];_=b(this.chart.scales[O].getPixelForValue(T))}A=t.top,L=t.bottom,N=_+y,w=N+f}else if(n==="y"){if(i==="center")_=b((t.left+t.right)/2);else if(Ze(i)){const O=Object.keys(i)[0],T=i[O];_=b(this.chart.scales[O].getPixelForValue(T))}k=_-y,C=k-f,E=t.left,P=t.right}const $=Qe(s.ticks.maxTicksLimit,u),D=Math.max(1,Math.ceil(u/$));for(x=0;x0&&(X-=Y/2);break}G={left:X,top:V,width:Y+B.width,height:j+B.height,color:D.backdropColor}}y.push({label:M,font:w,textOffset:P,options:{rotation:p,color:T,strokeColor:z,strokeWidth:U,textAlign:W,textBaseline:L,translation:[S,k],backdrop:G}})}return y}_getXAxisLabelAlignment(){const{position:t,ticks:n}=this.options;if(-Qn(this.labelRotation))return t==="top"?"left":"right";let s="center";return n.align==="start"?s="left":n.align==="end"?s="right":n.align==="inner"&&(s="inner"),s}_getYAxisLabelAlignment(t){const{position:n,ticks:{crossAlign:r,mirror:s,padding:o}}=this.options,i=this._getLabelSizes(),a=t+o,l=i.widest.width;let c,d;return n==="left"?s?(d=this.right+o,r==="near"?c="left":r==="center"?(c="center",d+=l/2):(c="right",d+=l)):(d=this.right-a,r==="near"?c="right":r==="center"?(c="center",d-=l/2):(c="left",d=this.left)):n==="right"?s?(d=this.left+o,r==="near"?c="right":r==="center"?(c="center",d-=l/2):(c="left",d-=l)):(d=this.left+a,r==="near"?c="left":r==="center"?(c="center",d+=l/2):(c="right",d=this.right)):c="right",{textAlign:c,x:d}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,n=this.options.position;if(n==="left"||n==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(n==="top"||n==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:n},left:r,top:s,width:o,height:i}=this;n&&(t.save(),t.fillStyle=n,t.fillRect(r,s,o,i),t.restore())}getLineWidthForValue(t){const n=this.options.grid;if(!this._isVisible()||!n.display)return 0;const s=this.ticks.findIndex(o=>o.value===t);return s>=0?n.setContext(this.getContext(s)).lineWidth:0}drawGrid(t){const n=this.options.grid,r=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,i;const a=(l,c,d)=>{!d.width||!d.color||(r.save(),r.lineWidth=d.width,r.strokeStyle=d.color,r.setLineDash(d.borderDash||[]),r.lineDashOffset=d.borderDashOffset,r.beginPath(),r.moveTo(l.x,l.y),r.lineTo(c.x,c.y),r.stroke(),r.restore())};if(n.display)for(o=0,i=s.length;o{this.draw(o)}}]:[{z:r,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:n,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const n=this.chart.getSortedVisibleDatasetMetas(),r=this.axis+"AxisID",s=[];let o,i;for(o=0,i=n.length;o{const r=n.split("."),s=r.pop(),o=[e].concat(r).join("."),i=t[n].split("."),a=i.pop(),l=i.join(".");wt.route(o,s,l,a)})}function RT(e){return"id"in e&&"defaults"in e}class eO{constructor(){this.controllers=new _a(Ns,"datasets",!0),this.elements=new _a(Ur,"elements"),this.plugins=new _a(Object,"plugins"),this.scales=new _a(Ts,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,n,r){[...n].forEach(s=>{const o=r||this._getRegistryForType(s);r||o.isForType(s)||o===this.plugins&&s.id?this._exec(t,o,s):st(s,i=>{const a=r||this._getRegistryForType(i);this._exec(t,a,i)})})}_exec(t,n,r){const s=qc(t);ht(r["before"+s],[],r),n[t](r),ht(r["after"+s],[],r)}_getRegistryForType(t){for(let n=0;no.filter(a=>!i.some(l=>a.plugin.id===l.plugin.id));this._notify(s(n,r),t,"stop"),this._notify(s(r,n),t,"start")}}function nO(e){const t={},n=[],r=Object.keys(Gn.plugins.items);for(let o=0;o1&&dm(e[0].toLowerCase());if(r)return r}throw new Error(`Cannot determine type of '${e}' axis. Please provide 'axis' or 'position' option.`)}function um(e,t,n){if(n[t+"AxisID"]===e)return{axis:t}}function cO(e,t){if(t.data&&t.data.datasets){const n=t.data.datasets.filter(r=>r.xAxisID===e||r.yAxisID===e);if(n.length)return um(e,"x",n[0])||um(e,"y",n[0])}return{}}function dO(e,t){const n=$r[e.type]||{scales:{}},r=t.scales||{},s=vd(e.type,t),o=Object.create(null);return Object.keys(r).forEach(i=>{const a=r[i];if(!Ze(a))return console.error(`Invalid scale configuration for scale: ${i}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${i}`);const l=xd(i,a,cO(i,e),wt.scales[a.type]),c=aO(l,s),d=n.scales||{};o[i]=So(Object.create(null),[{axis:l},a,d[l],d[c]])}),e.data.datasets.forEach(i=>{const a=i.type||e.type,l=i.indexAxis||vd(a,t),d=($r[a]||{}).scales||{};Object.keys(d).forEach(u=>{const f=iO(u,l),g=i[f+"AxisID"]||f;o[g]=o[g]||Object.create(null),So(o[g],[{axis:f},r[g],d[u]])})}),Object.keys(o).forEach(i=>{const a=o[i];So(a,[wt.scales[a.type],wt.scale])}),o}function fm(e){const t=e.options||(e.options={});t.plugins=Qe(t.plugins,{}),t.scales=dO(e,t)}function hm(e){return e=e||{},e.datasets=e.datasets||[],e.labels=e.labels||[],e}function uO(e){return e=e||{},e.data=hm(e.data),fm(e),e}const gm=new Map,pm=new Set;function Ea(e,t){let n=gm.get(e);return n||(n=t(),gm.set(e,n),pm.add(n)),n}const $o=(e,t,n)=>{const r=Ir(t,n);r!==void 0&&e.add(r)};class fO{constructor(t){this._config=uO(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=hm(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),fm(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Ea(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,n){return Ea(`${t}.transition.${n}`,()=>[[`datasets.${t}.transitions.${n}`,`transitions.${n}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,n){return Ea(`${t}-${n}`,()=>[[`datasets.${t}.elements.${n}`,`datasets.${t}`,`elements.${n}`,""]])}pluginScopeKeys(t){const n=t.id,r=this.type;return Ea(`${r}-plugin-${n}`,()=>[[`plugins.${n}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,n){const r=this._scopeCache;let s=r.get(t);return(!s||n)&&(s=new Map,r.set(t,s)),s}getOptionScopes(t,n,r){const{options:s,type:o}=this,i=this._cachedScopes(t,r),a=i.get(n);if(a)return a;const l=new Set;n.forEach(d=>{t&&(l.add(t),d.forEach(u=>$o(l,t,u))),d.forEach(u=>$o(l,s,u)),d.forEach(u=>$o(l,$r[o]||{},u)),d.forEach(u=>$o(l,wt,u)),d.forEach(u=>$o(l,td,u))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),pm.has(n)&&i.set(n,c),c}chartOptionScopes(){const{options:t,type:n}=this;return[t,$r[n]||{},wt.datasets[n]||{},{type:n},wt,td]}resolveNamedOptions(t,n,r,s=[""]){const o={$shared:!0},{resolver:i,subPrefixes:a}=mm(this._resolverCache,t,s);let l=i;if(gO(i,n)){o.$shared=!1,r=pr(r)?r():r;const c=this.createResolver(t,r,a);l=Es(i,r,c)}for(const c of n)o[c]=l[c];return o}createResolver(t,n,r=[""],s){const{resolver:o}=mm(this._resolverCache,t,r);return Ze(n)?Es(o,n,void 0,s):o}}function mm(e,t,n){let r=e.get(t);r||(r=new Map,e.set(t,r));const s=n.join();let o=r.get(s);return o||(o={resolver:id(t,n),subPrefixes:n.filter(a=>!a.toLowerCase().includes("hover"))},r.set(s,o)),o}const hO=e=>Ze(e)&&Object.getOwnPropertyNames(e).some(t=>pr(e[t]));function gO(e,t){const{isScriptable:n,isIndexable:r}=M0(e);for(const s of t){const o=n(s),i=r(s),a=(i||o)&&e[s];if(o&&(pr(a)||hO(a))||i&&Mt(a))return!0}return!1}var pO="4.5.1";const mO=["top","bottom","left","right","chartArea"];function bm(e,t){return e==="top"||e==="bottom"||mO.indexOf(e)===-1&&t==="x"}function ym(e,t){return function(n,r){return n[e]===r[e]?n[t]-r[t]:n[e]-r[e]}}function vm(e){const t=e.chart,n=t.options.animation;t.notifyPlugins("afterRender"),ht(n&&n.onComplete,[e],t)}function bO(e){const t=e.chart,n=t.options.animation;ht(n&&n.onProgress,[e],t)}function xm(e){return cd()&&typeof e=="string"?e=document.getElementById(e):e&&e.length&&(e=e[0]),e&&e.canvas&&(e=e.canvas),e}const Ma={},wm=e=>{const t=xm(e);return Object.values(Ma).filter(n=>n.canvas===t).pop()};function yO(e,t,n){const r=Object.keys(e);for(const s of r){const o=+s;if(o>=t){const i=e[s];delete e[s],(n>0||o>t)&&(e[o+n]=i)}}}function vO(e,t,n,r){return!n||e.type==="mouseout"?null:r?t:e}let Fo=(vr=class{static register(...t){Gn.add(...t),km()}static unregister(...t){Gn.remove(...t),km()}constructor(t,n){const r=this.config=new fO(n),s=xm(t),o=wm(s);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const i=r.createResolver(r.chartOptionScopes(),this.getContext());this.platform=new(r.platform||FT(s)),this.platform.updateConfig(r);const a=this.platform.acquireContext(s,i.aspectRatio),l=a&&a.canvas,c=l&&l.height,d=l&&l.width;if(this.id=$M(),this.ctx=a,this.canvas=l,this.width=d,this.height=c,this._options=i,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new tO,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=eN(u=>this.update(u),i.resizeDelay||0),this._dataChanges=[],Ma[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}Jn.listen(this,"complete",vm),Jn.listen(this,"progress",bO),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:n},width:r,height:s,_aspectRatio:o}=this;return lt(t)?n&&o?o:s?r/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return Gn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():I0(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return k0(this.canvas,this.ctx),this}stop(){return Jn.stop(this),this}resize(t,n){Jn.running(this)?this._resizeBeforeDraw={width:t,height:n}:this._resize(t,n)}_resize(t,n){const r=this.options,s=this.canvas,o=r.maintainAspectRatio&&this.aspectRatio,i=this.platform.getMaximumSize(s,t,n,o),a=r.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=i.width,this.height=i.height,this._aspectRatio=this.aspectRatio,I0(this,a,!0)&&(this.notifyPlugins("resize",{size:i}),ht(r.onResize,[this,i],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const n=this.options.scales||{};st(n,(r,s)=>{r.id=s})}buildOrUpdateScales(){const t=this.options,n=t.scales,r=this.scales,s=Object.keys(r).reduce((i,a)=>(i[a]=!1,i),{});let o=[];n&&(o=o.concat(Object.keys(n).map(i=>{const a=n[i],l=xd(i,a),c=l==="r",d=l==="x";return{options:a,dposition:c?"chartArea":d?"bottom":"left",dtype:c?"radialLinear":d?"category":"linear"}}))),st(o,i=>{const a=i.options,l=a.id,c=xd(l,a),d=Qe(a.type,i.dtype);(a.position===void 0||bm(a.position,c)!==bm(i.dposition))&&(a.position=i.dposition),s[l]=!0;let u=null;if(l in r&&r[l].type===d)u=r[l];else{const f=Gn.getScale(d);u=new f({id:l,type:d,ctx:this.ctx,chart:this}),r[u.id]=u}u.init(a,t)}),st(s,(i,a)=>{i||delete r[a]}),st(r,i=>{vn.configure(this,i,i.options),vn.addBox(this,i)})}_updateMetasets(){const t=this._metasets,n=this.data.datasets.length,r=t.length;if(t.sort((s,o)=>s.index-o.index),r>n){for(let s=n;sn.length&&delete this._stacks,t.forEach((r,s)=>{n.filter(o=>o===r._dataset).length===0&&this._destroyDatasetMeta(s)})}buildOrUpdateControllers(){const t=[],n=this.data.datasets;let r,s;for(this._removeUnreferencedMetasets(),r=0,s=n.length;r{this.getDatasetMeta(n).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const n=this.config;n.update();const r=this._options=n.createResolver(n.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!r.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let i=0;for(let c=0,d=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(ym("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){st(this.scales,t=>{vn.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,n=new Set(Object.keys(this._listeners)),r=new Set(t.events);(!s0(n,r)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,n=this._getUniformDataChanges()||[];for(const{method:r,start:s,count:o}of n){const i=r==="_removeElements"?-o:o;yO(t,s,i)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const n=this.data.datasets.length,r=o=>new Set(t.filter(i=>i[0]===o).map((i,a)=>a+","+i.splice(1).join(","))),s=r(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;vn.update(this,this.width,this.height,t);const n=this.chartArea,r=n.width<=0||n.height<=0;this._layers=[],st(this.boxes,s=>{r&&s.position==="chartArea"||(s.configure&&s.configure(),this._layers.push(...s._layers()))},this),this._layers.forEach((s,o)=>{s._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let n=0,r=this.data.datasets.length;n=0;--n)this._drawDataset(t[n]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const n=this.ctx,r={meta:t,index:t.index,cancelable:!0},s=VN(this,t);this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(s&&rd(n,s),t.controller.draw(),s&&sd(n),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return _0(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,n,r,s){const o=mT.modes[n];return typeof o=="function"?o(this,t,r,s):[]}getDatasetMeta(t){const n=this.data.datasets[t],r=this._metasets;let s=r.filter(o=>o&&o._dataset===n).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:n&&n.order||0,index:t,_dataset:n,_parsed:[],_sorted:!1},r.push(s)),s}getContext(){return this.$context||(this.$context=_s(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const n=this.data.datasets[t];if(!n)return!1;const r=this.getDatasetMeta(t);return typeof r.hidden=="boolean"?!r.hidden:!n.hidden}setDatasetVisibility(t,n){const r=this.getDatasetMeta(t);r.hidden=!n}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,n,r){const s=r?"show":"hide",o=this.getDatasetMeta(t),i=o.controller._resolveAnimations(void 0,s);_o(n)?(o.data[n].hidden=!r,this.update()):(this.setDatasetVisibility(t,r),i.update(o,{visible:r}),this.update(a=>a.datasetIndex===t?s:void 0))}hide(t,n){this._updateVisibility(t,n,!1)}show(t,n){this._updateVisibility(t,n,!0)}_destroyDatasetMeta(t){const n=this._metasets[t];n&&n.controller&&n.controller._destroy(),delete this._metasets[t]}_stop(){let t,n;for(this.stop(),Jn.remove(this),t=0,n=this.data.datasets.length;t{n.addEventListener(this,o,i),t[o]=i},s=(o,i,a)=>{o.offsetX=i,o.offsetY=a,this._eventHandler(o)};st(this.options.events,o=>r(o,s))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,n=this.platform,r=(l,c)=>{n.addEventListener(this,l,c),t[l]=c},s=(l,c)=>{t[l]&&(n.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let i;const a=()=>{s("attach",a),this.attached=!0,this.resize(),r("resize",o),r("detach",i)};i=()=>{this.attached=!1,s("resize",o),this._stop(),this._resize(0,0),r("attach",a)},n.isAttached(this.canvas)?a():i()}unbindEvents(){st(this._listeners,(t,n)=>{this.platform.removeEventListener(this,n,t)}),this._listeners={},st(this._responsiveListeners,(t,n)=>{this.platform.removeEventListener(this,n,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,n,r){const s=r?"set":"remove";let o,i,a,l;for(n==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+s+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[i],index:i}});!aa(r,n)&&(this._active=r,this._lastEvent=null,this._updateHoverStyles(r,n))}notifyPlugins(t,n,r){return this._plugins.notify(this,t,n,r)}isPluginEnabled(t){return this._plugins._cache.filter(n=>n.plugin.id===t).length===1}_updateHoverStyles(t,n,r){const s=this.options.hover,o=(l,c)=>l.filter(d=>!c.some(u=>d.datasetIndex===u.datasetIndex&&d.index===u.index)),i=o(n,t),a=r?t:o(t,n);i.length&&this.updateHoverStyle(i,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,n){const r={event:t,replay:n,cancelable:!0,inChartArea:this.isPointInArea(t)},s=i=>(i.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",r,s)===!1)return;const o=this._handleEvent(t,n,r.inChartArea);return r.cancelable=!1,this.notifyPlugins("afterEvent",r,s),(o||r.changed)&&this.render(),this}_handleEvent(t,n,r){const{_active:s=[],options:o}=this,i=n,a=this._getActiveElements(t,s,r,i),l=BM(t),c=vO(t,this._lastEvent,r,l);r&&(this._lastEvent=null,ht(o.onHover,[t,a,this],this),l&&ht(o.onClick,[t,a,this],this));const d=!aa(a,s);return(d||n)&&(this._active=a,this._updateHoverStyles(a,s,n)),this._lastEvent=c,d}_getActiveElements(t,n,r,s){if(t.type==="mouseout")return[];if(!r)return n;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,s)}},Pe(vr,"defaults",wt),Pe(vr,"instances",Ma),Pe(vr,"overrides",$r),Pe(vr,"registry",Gn),Pe(vr,"version",pO),Pe(vr,"getChart",wm),vr);function km(){return st(Fo.instances,e=>e._plugins.invalidate())}function xO(e,t,n){const{startAngle:r,x:s,y:o,outerRadius:i,innerRadius:a,options:l}=t,{borderWidth:c,borderJoinStyle:d}=l,u=Math.min(c/i,Wn(r-n));if(e.beginPath(),e.arc(s,o,i-c/2,r+u/2,n-u/2),a>0){const f=Math.min(c/a,Wn(r-n));e.arc(s,o,a+c/2,n-f/2,r+f/2,!0)}else{const f=Math.min(c/2,i*Wn(r-n));if(d==="round")e.arc(s,o,f,n-ct/2,r+ct/2,!0);else if(d==="bevel"){const g=2*f*f,m=-g*Math.cos(n+ct/2)+s,p=-g*Math.sin(n+ct/2)+o,y=g*Math.cos(r+ct/2)+s,b=g*Math.sin(r+ct/2)+o;e.lineTo(m,p),e.lineTo(y,b)}}e.closePath(),e.moveTo(0,0),e.rect(0,0,e.canvas.width,e.canvas.height),e.clip("evenodd")}function wO(e,t,n){const{startAngle:r,pixelMargin:s,x:o,y:i,outerRadius:a,innerRadius:l}=t;let c=s/a;e.beginPath(),e.arc(o,i,a,r-c,n+c),l>s?(c=s/l,e.arc(o,i,l,n+c,r-c,!0)):e.arc(o,i,s,n+Nt,r-Nt),e.closePath(),e.clip()}function kO(e){return od(e,["outerStart","outerEnd","innerStart","innerEnd"])}function CO(e,t,n,r){const s=kO(e.options.borderRadius),o=(n-t)/2,i=Math.min(o,r*t/2),a=l=>{const c=(n-Math.min(o,l))*r/2;return nn(l,0,Math.min(o,c))};return{outerStart:a(s.outerStart),outerEnd:a(s.outerEnd),innerStart:nn(s.innerStart,0,i),innerEnd:nn(s.innerEnd,0,i)}}function Os(e,t,n,r){return{x:n+e*Math.cos(t),y:r+e*Math.sin(t)}}function Na(e,t,n,r,s,o){const{x:i,y:a,startAngle:l,pixelMargin:c,innerRadius:d}=t,u=Math.max(t.outerRadius+r+n-c,0),f=d>0?d+r+n+c:0;let g=0;const m=s-l;if(r){const D=d>0?d-r:0,O=u>0?u-r:0,T=(D+O)/2,z=T!==0?m*T/(T+r):m;g=(m-z)/2}const p=Math.max(.001,m*u-n/ct)/u,y=(m-p)/2,b=l+y+g,_=s-y-g,{outerStart:x,outerEnd:M,innerStart:S,innerEnd:k}=CO(t,f,u,_-b),N=u-x,C=u-M,w=b+x/N,E=_-M/C,A=f+S,P=f+k,L=b+S/A,$=_-k/P;if(e.beginPath(),o){const D=(w+E)/2;if(e.arc(i,a,u,w,D),e.arc(i,a,u,D,E),M>0){const U=Os(C,E,i,a);e.arc(U.x,U.y,M,E,_+Nt)}const O=Os(P,_,i,a);if(e.lineTo(O.x,O.y),k>0){const U=Os(P,$,i,a);e.arc(U.x,U.y,k,_+Nt,$+Math.PI)}const T=(_-k/f+(b+S/f))/2;if(e.arc(i,a,f,_-k/f,T,!0),e.arc(i,a,f,T,b+S/f,!0),S>0){const U=Os(A,L,i,a);e.arc(U.x,U.y,S,L+Math.PI,b-Nt)}const z=Os(N,b,i,a);if(e.lineTo(z.x,z.y),x>0){const U=Os(N,w,i,a);e.arc(U.x,U.y,x,b-Nt,w)}}else{e.moveTo(i,a);const D=Math.cos(w)*u+i,O=Math.sin(w)*u+a;e.lineTo(D,O);const T=Math.cos(E)*u+i,z=Math.sin(E)*u+a;e.lineTo(T,z)}e.closePath()}function SO(e,t,n,r,s){const{fullCircles:o,startAngle:i,circumference:a}=t;let l=t.endAngle;if(o){Na(e,t,n,r,l,s);for(let c=0;c=ct&&g===0&&d!=="miter"&&xO(e,t,p),o||(Na(e,t,n,r,p,s),e.stroke())}class zo extends Ur{constructor(n){super();Pe(this,"circumference");Pe(this,"endAngle");Pe(this,"fullCircles");Pe(this,"innerRadius");Pe(this,"outerRadius");Pe(this,"pixelMargin");Pe(this,"startAngle");this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,n&&Object.assign(this,n)}inRange(n,r,s){const o=this.getProps(["x","y"],s),{angle:i,distance:a}=c0(o,{x:n,y:r}),{startAngle:l,endAngle:c,innerRadius:d,outerRadius:u,circumference:f}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],s),g=(this.options.spacing+this.options.borderWidth)/2,m=Qe(f,c-l),p=fa(i,l,c)&&l!==c,y=m>=_t||p,b=Pr(a,d+g,u+g);return y&&b}getCenterPoint(n){const{x:r,y:s,startAngle:o,endAngle:i,innerRadius:a,outerRadius:l}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],n),{offset:c,spacing:d}=this.options,u=(o+i)/2,f=(a+l+d+c)/2;return{x:r+Math.cos(u)*f,y:s+Math.sin(u)*f}}tooltipPosition(n){return this.getCenterPoint(n)}draw(n){const{options:r,circumference:s}=this,o=(r.offset||0)/4,i=(r.spacing||0)/2,a=r.circular;if(this.pixelMargin=r.borderAlign==="inner"?.33:0,this.fullCircles=s>_t?Math.floor(s/_t):0,s===0||this.innerRadius<0||this.outerRadius<0)return;n.save();const l=(this.startAngle+this.endAngle)/2;n.translate(Math.cos(l)*o,Math.sin(l)*o);const c=1-Math.sin(Math.min(ct,s||0)),d=o*c;n.fillStyle=r.backgroundColor,n.strokeStyle=r.borderColor,SO(n,this,d,i,a),_O(n,this,d,i,a),n.restore()}}Pe(zo,"id","arc"),Pe(zo,"defaults",{borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1}),Pe(zo,"defaultRoutes",{backgroundColor:"backgroundColor"}),Pe(zo,"descriptors",{_scriptable:!0,_indexable:n=>n!=="borderDash"});function Cm(e,t){const{x:n,y:r,base:s,width:o,height:i}=e.getProps(["x","y","base","width","height"],t);let a,l,c,d,u;return e.horizontal?(u=i/2,a=Math.min(n,s),l=Math.max(n,s),c=r-u,d=r+u):(u=o/2,a=n-u,l=n+u,c=Math.min(r,s),d=Math.max(r,s)),{left:a,top:c,right:l,bottom:d}}function yr(e,t,n,r){return e?0:nn(t,n,r)}function EO(e,t,n){const r=e.options.borderWidth,s=e.borderSkipped,o=E0(r);return{t:yr(s.top,o.top,0,n),r:yr(s.right,o.right,0,t),b:yr(s.bottom,o.bottom,0,n),l:yr(s.left,o.left,0,t)}}function MO(e,t,n){const{enableBorderRadius:r}=e.getProps(["enableBorderRadius"]),s=e.options.borderRadius,o=Ss(s),i=Math.min(t,n),a=e.borderSkipped,l=r||Ze(s);return{topLeft:yr(!l||a.top||a.left,o.topLeft,0,i),topRight:yr(!l||a.top||a.right,o.topRight,0,i),bottomLeft:yr(!l||a.bottom||a.left,o.bottomLeft,0,i),bottomRight:yr(!l||a.bottom||a.right,o.bottomRight,0,i)}}function NO(e){const t=Cm(e),n=t.right-t.left,r=t.bottom-t.top,s=EO(e,n/2,r/2),o=MO(e,n/2,r/2);return{outer:{x:t.left,y:t.top,w:n,h:r,radius:o},inner:{x:t.left+s.l,y:t.top+s.t,w:n-s.l-s.r,h:r-s.t-s.b,radius:{topLeft:Math.max(0,o.topLeft-Math.max(s.t,s.l)),topRight:Math.max(0,o.topRight-Math.max(s.t,s.r)),bottomLeft:Math.max(0,o.bottomLeft-Math.max(s.b,s.l)),bottomRight:Math.max(0,o.bottomRight-Math.max(s.b,s.r))}}}}function wd(e,t,n,r){const s=t===null,o=n===null,a=e&&!(s&&o)&&Cm(e,r);return a&&(s||Pr(t,a.left,a.right))&&(o||Pr(n,a.top,a.bottom))}function TO(e){return e.topLeft||e.topRight||e.bottomLeft||e.bottomRight}function OO(e,t){e.rect(t.x,t.y,t.w,t.h)}function kd(e,t,n={}){const r=e.x!==n.x?-t:0,s=e.y!==n.y?-t:0,o=(e.x+e.w!==n.x+n.w?t:0)-r,i=(e.y+e.h!==n.y+n.h?t:0)-s;return{x:e.x+r,y:e.y+s,w:e.w+o,h:e.h+i,radius:e.radius}}class Ta extends Ur{constructor(t){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,t&&Object.assign(this,t)}draw(t){const{inflateAmount:n,options:{borderColor:r,backgroundColor:s}}=this,{inner:o,outer:i}=NO(this),a=TO(i.radius)?ga:OO;t.save(),(i.w!==o.w||i.h!==o.h)&&(t.beginPath(),a(t,kd(i,n,o)),t.clip(),a(t,kd(o,-n,i)),t.fillStyle=r,t.fill("evenodd")),t.beginPath(),a(t,kd(o,n)),t.fillStyle=s,t.fill(),t.restore()}inRange(t,n,r){return wd(this,t,n,r)}inXRange(t,n){return wd(this,t,null,n)}inYRange(t,n){return wd(this,null,t,n)}getCenterPoint(t){const{x:n,y:r,base:s,horizontal:o}=this.getProps(["x","y","base","horizontal"],t);return{x:o?(n+s)/2:n,y:o?r:(r+s)/2}}getRange(t){return t==="x"?this.width/2:this.height/2}}Pe(Ta,"id","bar"),Pe(Ta,"defaults",{borderSkipped:"start",borderWidth:0,borderRadius:0,inflateAmount:"auto",pointStyle:void 0}),Pe(Ta,"defaultRoutes",{backgroundColor:"backgroundColor",borderColor:"borderColor"});const Sm=(e,t)=>{let{boxHeight:n=t,boxWidth:r=t}=e;return e.usePointStyle&&(n=Math.min(n,t),r=e.pointStyleWidth||Math.min(r,t)),{boxWidth:r,boxHeight:n,itemHeight:Math.max(t,n)}},AO=(e,t)=>e!==null&&t!==null&&e.datasetIndex===t.datasetIndex&&e.index===t.index;class _m extends Ur{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,n,r){this.maxWidth=t,this.maxHeight=n,this._margins=r,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let n=ht(t.generateLabels,[this.chart],this)||[];t.filter&&(n=n.filter(r=>t.filter(r,this.chart.data))),t.sort&&(n=n.sort((r,s)=>t.sort(r,s,this.chart.data))),this.options.reverse&&n.reverse(),this.legendItems=n}fit(){const{options:t,ctx:n}=this;if(!t.display){this.width=this.height=0;return}const r=t.labels,s=Gt(r.font),o=s.size,i=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=Sm(r,o);let c,d;n.font=s.string,this.isHorizontal()?(c=this.maxWidth,d=this._fitRows(i,o,a,l)+10):(d=this.maxHeight,c=this._fitCols(i,s,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(d,t.maxHeight||this.maxHeight)}_fitRows(t,n,r,s){const{ctx:o,maxWidth:i,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],d=s+a;let u=t;o.textAlign="left",o.textBaseline="middle";let f=-1,g=-d;return this.legendItems.forEach((m,p)=>{const y=r+n/2+o.measureText(m.text).width;(p===0||c[c.length-1]+y+2*a>i)&&(u+=d,c[c.length-(p>0?0:1)]=0,g+=d,f++),l[p]={left:0,top:g,row:f,width:y,height:s},c[c.length-1]+=y+a}),u}_fitCols(t,n,r,s){const{ctx:o,maxHeight:i,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],d=i-t;let u=a,f=0,g=0,m=0,p=0;return this.legendItems.forEach((y,b)=>{const{itemWidth:_,itemHeight:x}=DO(r,n,o,y,s);b>0&&g+x+2*a>d&&(u+=f+a,c.push({width:f,height:g}),m+=f+a,p++,f=g=0),l[b]={left:m,top:g,col:p,width:_,height:x},f=Math.max(f,_),g+=x+a}),u+=f,c.push({width:f,height:g}),u}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:n,options:{align:r,labels:{padding:s},rtl:o}}=this,i=Ms(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=Wt(r,this.left+s,this.right-this.lineWidths[a]);for(const c of n)a!==c.row&&(a=c.row,l=Wt(r,this.left+s,this.right-this.lineWidths[a])),c.top+=this.top+t+s,c.left=i.leftForLtr(i.x(l),c.width),l+=c.width+s}else{let a=0,l=Wt(r,this.top+t+s,this.bottom-this.columnSizes[a].height);for(const c of n)c.col!==a&&(a=c.col,l=Wt(r,this.top+t+s,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+s,c.left=i.leftForLtr(i.x(c.left),c.width),l+=c.height+s}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;rd(t,this),this._draw(),sd(t)}}_draw(){const{options:t,columnSizes:n,lineWidths:r,ctx:s}=this,{align:o,labels:i}=t,a=wt.color,l=Ms(t.rtl,this.left,this.width),c=Gt(i.font),{padding:d}=i,u=c.size,f=u/2;let g;this.drawTitle(),s.textAlign=l.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:m,boxHeight:p,itemHeight:y}=Sm(i,u),b=function(k,N,C){if(isNaN(m)||m<=0||isNaN(p)||p<0)return;s.save();const w=Qe(C.lineWidth,1);if(s.fillStyle=Qe(C.fillStyle,a),s.lineCap=Qe(C.lineCap,"butt"),s.lineDashOffset=Qe(C.lineDashOffset,0),s.lineJoin=Qe(C.lineJoin,"miter"),s.lineWidth=w,s.strokeStyle=Qe(C.strokeStyle,a),s.setLineDash(Qe(C.lineDash,[])),i.usePointStyle){const E={radius:p*Math.SQRT2/2,pointStyle:C.pointStyle,rotation:C.rotation,borderWidth:w},A=l.xPlus(k,m/2),P=N+f;S0(s,E,A,P,i.pointStyleWidth&&m)}else{const E=N+Math.max((u-p)/2,0),A=l.leftForLtr(k,m),P=Ss(C.borderRadius);s.beginPath(),Object.values(P).some(L=>L!==0)?ga(s,{x:A,y:E,w:m,h:p,radius:P}):s.rect(A,E,m,p),s.fill(),w!==0&&s.stroke()}s.restore()},_=function(k,N,C){No(s,C.text,k,N+y/2,c,{strikethrough:C.hidden,textAlign:l.textAlign(C.textAlign)})},x=this.isHorizontal(),M=this._computeTitleHeight();x?g={x:Wt(o,this.left+d,this.right-r[0]),y:this.top+d+M,line:0}:g={x:this.left+d,y:Wt(o,this.top+M+d,this.bottom-n[0].height),line:0},P0(this.ctx,t.textDirection);const S=y+d;this.legendItems.forEach((k,N)=>{s.strokeStyle=k.fontColor,s.fillStyle=k.fontColor;const C=s.measureText(k.text).width,w=l.textAlign(k.textAlign||(k.textAlign=i.textAlign)),E=m+f+C;let A=g.x,P=g.y;l.setWidth(this.width),x?N>0&&A+E+d>this.right&&(P=g.y+=S,g.line++,A=g.x=Wt(o,this.left+d,this.right-r[g.line])):N>0&&P+S>this.bottom&&(A=g.x=A+n[g.line].width+d,g.line++,P=g.y=Wt(o,this.top+M+d,this.bottom-n[g.line].height));const L=l.x(A);if(b(L,P,k),A=tN(w,A+m+f,x?A+E:this.right,t.rtl),_(l.x(A),P,k),x)g.x+=E+d;else if(typeof k.text!="string"){const $=c.lineHeight;g.y+=Em(k,$)+d}else g.y+=S}),$0(this.ctx,t.textDirection)}drawTitle(){const t=this.options,n=t.title,r=Gt(n.font),s=yn(n.padding);if(!n.display)return;const o=Ms(t.rtl,this.left,this.width),i=this.ctx,a=n.position,l=r.size/2,c=s.top+l;let d,u=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),d=this.top+c,u=Wt(t.align,u,this.right-f);else{const m=this.columnSizes.reduce((p,y)=>Math.max(p,y.height),0);d=c+Wt(t.align,this.top,this.bottom-m-t.labels.padding-this._computeTitleHeight())}const g=Wt(a,u,u+f);i.textAlign=o.textAlign(Jc(a)),i.textBaseline="middle",i.strokeStyle=n.color,i.fillStyle=n.color,i.font=r.string,No(i,n.text,g,d,r)}_computeTitleHeight(){const t=this.options.title,n=Gt(t.font),r=yn(t.padding);return t.display?n.lineHeight+r.height:0}_getLegendItemAt(t,n){let r,s,o;if(Pr(t,this.left,this.right)&&Pr(n,this.top,this.bottom)){for(o=this.legendHitBoxes,r=0;ro.length>i.length?o:i)),t+n.size/2+r.measureText(s).width}function LO(e,t,n){let r=e;return typeof t.text!="string"&&(r=Em(t,n)),r}function Em(e,t){const n=e.text?e.text.length:0;return t*n}function PO(e,t){return!!((e==="mousemove"||e==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(e==="click"||e==="mouseup"))}var Mm={id:"legend",_element:_m,start(e,t,n){const r=e.legend=new _m({ctx:e.ctx,options:n,chart:e});vn.configure(e,r,n),vn.addBox(e,r)},stop(e){vn.removeBox(e,e.legend),delete e.legend},beforeUpdate(e,t,n){const r=e.legend;vn.configure(e,r,n),r.options=n},afterUpdate(e){const t=e.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(e,t){t.replay||e.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(e,t,n){const r=t.datasetIndex,s=n.chart;s.isDatasetVisible(r)?(s.hide(r),t.hidden=!0):(s.show(r),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:e=>e.chart.options.color,boxWidth:40,padding:10,generateLabels(e){const t=e.data.datasets,{labels:{usePointStyle:n,pointStyle:r,textAlign:s,color:o,useBorderRadius:i,borderRadius:a}}=e.legend.options;return e._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(n?0:void 0),d=yn(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(d.width+d.height)/4,strokeStyle:c.borderColor,pointStyle:r||c.pointStyle,rotation:c.rotation,textAlign:s||c.textAlign,borderRadius:i&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:e=>e.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:e=>!e.startsWith("on"),labels:{_scriptable:e=>!["generateLabels","filter","sort"].includes(e)}}};class Nm extends Ur{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,n){const r=this.options;if(this.left=0,this.top=0,!r.display){this.width=this.height=this.right=this.bottom=0;return}this.width=this.right=t,this.height=this.bottom=n;const s=Mt(r.text)?r.text.length:1;this._padding=yn(r.padding);const o=s*Gt(r.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return t==="top"||t==="bottom"}_drawArgs(t){const{top:n,left:r,bottom:s,right:o,options:i}=this,a=i.align;let l=0,c,d,u;return this.isHorizontal()?(d=Wt(a,r,o),u=n+t,c=o-r):(i.position==="left"?(d=r+t,u=Wt(a,s,n),l=ct*-.5):(d=o-t,u=Wt(a,n,s),l=ct*.5),c=s-n),{titleX:d,titleY:u,maxWidth:c,rotation:l}}draw(){const t=this.ctx,n=this.options;if(!n.display)return;const r=Gt(n.font),o=r.lineHeight/2+this._padding.top,{titleX:i,titleY:a,maxWidth:l,rotation:c}=this._drawArgs(o);No(t,n.text,0,0,r,{color:n.color,maxWidth:l,rotation:c,textAlign:Jc(n.align),textBaseline:"middle",translation:[i,a]})}}function $O(e,t){const n=new Nm({ctx:e.ctx,options:t,chart:e});vn.configure(e,n,t),vn.addBox(e,n),e.titleBlock=n}var FO={id:"title",_element:Nm,start(e,t,n){$O(e,n)},stop(e){const t=e.titleBlock;vn.removeBox(e,t),delete e.titleBlock},beforeUpdate(e,t,n){const r=e.titleBlock;vn.configure(e,r,n),r.options=n},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Vo={average(e){if(!e.length)return!1;let t,n,r=new Set,s=0,o=0;for(t=0,n=e.length;ta+l)/r.size,y:s/o}},nearest(e,t){if(!e.length)return!1;let n=t.x,r=t.y,s=Number.POSITIVE_INFINITY,o,i,a;for(o=0,i=e.length;o-1?e.split(` +`):e}function zO(e,t){const{element:n,datasetIndex:r,index:s}=t,o=e.getDatasetMeta(r).controller,{label:i,value:a}=o.getLabelAndValue(s);return{chart:e,label:i,parsed:o.getParsed(s),raw:e.data.datasets[r].data[s],formattedValue:a,dataset:o.getDataset(),dataIndex:s,datasetIndex:r,element:n}}function Tm(e,t){const n=e.chart.ctx,{body:r,footer:s,title:o}=e,{boxWidth:i,boxHeight:a}=t,l=Gt(t.bodyFont),c=Gt(t.titleFont),d=Gt(t.footerFont),u=o.length,f=s.length,g=r.length,m=yn(t.padding);let p=m.height,y=0,b=r.reduce((M,S)=>M+S.before.length+S.lines.length+S.after.length,0);if(b+=e.beforeBody.length+e.afterBody.length,u&&(p+=u*c.lineHeight+(u-1)*t.titleSpacing+t.titleMarginBottom),b){const M=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;p+=g*M+(b-g)*l.lineHeight+(b-1)*t.bodySpacing}f&&(p+=t.footerMarginTop+f*d.lineHeight+(f-1)*t.footerSpacing);let _=0;const x=function(M){y=Math.max(y,n.measureText(M).width+_)};return n.save(),n.font=c.string,st(e.title,x),n.font=l.string,st(e.beforeBody.concat(e.afterBody),x),_=t.displayColors?i+2+t.boxPadding:0,st(r,M=>{st(M.before,x),st(M.lines,x),st(M.after,x)}),_=0,n.font=d.string,st(e.footer,x),n.restore(),y+=m.width,{width:y,height:p}}function VO(e,t){const{y:n,height:r}=t;return ne.height-r/2?"bottom":"center"}function HO(e,t,n,r){const{x:s,width:o}=r,i=n.caretSize+n.caretPadding;if(e==="left"&&s+o+i>t.width||e==="right"&&s-o-i<0)return!0}function UO(e,t,n,r){const{x:s,width:o}=n,{width:i,chartArea:{left:a,right:l}}=e;let c="center";return r==="center"?c=s<=(a+l)/2?"left":"right":s<=o/2?c="left":s>=i-o/2&&(c="right"),HO(c,e,t,n)&&(c="center"),c}function Om(e,t,n){const r=n.yAlign||t.yAlign||VO(e,n);return{xAlign:n.xAlign||t.xAlign||UO(e,t,n,r),yAlign:r}}function BO(e,t){let{x:n,width:r}=e;return t==="right"?n-=r:t==="center"&&(n-=r/2),n}function jO(e,t,n){let{y:r,height:s}=e;return t==="top"?r+=n:t==="bottom"?r-=s+n:r-=s/2,r}function Am(e,t,n,r){const{caretSize:s,caretPadding:o,cornerRadius:i}=e,{xAlign:a,yAlign:l}=n,c=s+o,{topLeft:d,topRight:u,bottomLeft:f,bottomRight:g}=Ss(i);let m=BO(t,a);const p=jO(t,l,c);return l==="center"?a==="left"?m+=c:a==="right"&&(m-=c):a==="left"?m-=Math.max(d,f)+s:a==="right"&&(m+=Math.max(u,g)+s),{x:nn(m,0,r.width-t.width),y:nn(p,0,r.height-t.height)}}function Oa(e,t,n){const r=yn(n.padding);return t==="center"?e.x+e.width/2:t==="right"?e.x+e.width-r.right:e.x+r.left}function Dm(e){return Yn([],Rn(e))}function WO(e,t,n){return _s(e,{tooltip:t,tooltipItems:n,type:"tooltip"})}function Im(e,t){const n=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return n?e.override(n):e}const Lm={beforeTitle:Zn,title(e){if(e.length>0){const t=e[0],n=t.chart.data.labels,r=n?n.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(r>0&&t.dataIndex"u"?Lm[t].call(n,r):s}class Cd extends Ur{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const n=this.chart,r=this.options.setContext(this.getContext()),s=r.enabled&&n.options.animation&&r.animations,o=new z0(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=WO(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,n){const{callbacks:r}=n,s=rn(r,"beforeTitle",this,t),o=rn(r,"title",this,t),i=rn(r,"afterTitle",this,t);let a=[];return a=Yn(a,Rn(s)),a=Yn(a,Rn(o)),a=Yn(a,Rn(i)),a}getBeforeBody(t,n){return Dm(rn(n.callbacks,"beforeBody",this,t))}getBody(t,n){const{callbacks:r}=n,s=[];return st(t,o=>{const i={before:[],lines:[],after:[]},a=Im(r,o);Yn(i.before,Rn(rn(a,"beforeLabel",this,o))),Yn(i.lines,rn(a,"label",this,o)),Yn(i.after,Rn(rn(a,"afterLabel",this,o))),s.push(i)}),s}getAfterBody(t,n){return Dm(rn(n.callbacks,"afterBody",this,t))}getFooter(t,n){const{callbacks:r}=n,s=rn(r,"beforeFooter",this,t),o=rn(r,"footer",this,t),i=rn(r,"afterFooter",this,t);let a=[];return a=Yn(a,Rn(s)),a=Yn(a,Rn(o)),a=Yn(a,Rn(i)),a}_createItems(t){const n=this._active,r=this.chart.data,s=[],o=[],i=[];let a=[],l,c;for(l=0,c=n.length;lt.filter(d,u,f,r))),t.itemSort&&(a=a.sort((d,u)=>t.itemSort(d,u,r))),st(a,d=>{const u=Im(t.callbacks,d);s.push(rn(u,"labelColor",this,d)),o.push(rn(u,"labelPointStyle",this,d)),i.push(rn(u,"labelTextColor",this,d))}),this.labelColors=s,this.labelPointStyles=o,this.labelTextColors=i,this.dataPoints=a,a}update(t,n){const r=this.options.setContext(this.getContext()),s=this._active;let o,i=[];if(!s.length)this.opacity!==0&&(o={opacity:0});else{const a=Vo[r.position].call(this,s,this._eventPosition);i=this._createItems(r),this.title=this.getTitle(i,r),this.beforeBody=this.getBeforeBody(i,r),this.body=this.getBody(i,r),this.afterBody=this.getAfterBody(i,r),this.footer=this.getFooter(i,r);const l=this._size=Tm(this,r),c=Object.assign({},a,l),d=Om(this.chart,r,c),u=Am(r,c,d,this.chart);this.xAlign=d.xAlign,this.yAlign=d.yAlign,o={opacity:1,x:u.x,y:u.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=i,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&r.external&&r.external.call(this,{chart:this.chart,tooltip:this,replay:n})}drawCaret(t,n,r,s){const o=this.getCaretPosition(t,r,s);n.lineTo(o.x1,o.y1),n.lineTo(o.x2,o.y2),n.lineTo(o.x3,o.y3)}getCaretPosition(t,n,r){const{xAlign:s,yAlign:o}=this,{caretSize:i,cornerRadius:a}=r,{topLeft:l,topRight:c,bottomLeft:d,bottomRight:u}=Ss(a),{x:f,y:g}=t,{width:m,height:p}=n;let y,b,_,x,M,S;return o==="center"?(M=g+p/2,s==="left"?(y=f,b=y-i,x=M+i,S=M-i):(y=f+m,b=y+i,x=M-i,S=M+i),_=y):(s==="left"?b=f+Math.max(l,d)+i:s==="right"?b=f+m-Math.max(c,u)-i:b=this.caretX,o==="top"?(x=g,M=x-i,y=b-i,_=b+i):(x=g+p,M=x+i,y=b+i,_=b-i),S=x),{x1:y,x2:b,x3:_,y1:x,y2:M,y3:S}}drawTitle(t,n,r){const s=this.title,o=s.length;let i,a,l;if(o){const c=Ms(r.rtl,this.x,this.width);for(t.x=Oa(this,r.titleAlign,r),n.textAlign=c.textAlign(r.titleAlign),n.textBaseline="middle",i=Gt(r.titleFont),a=r.titleSpacing,n.fillStyle=r.titleColor,n.font=i.string,l=0;l_!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,ga(t,{x:p,y:m,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=i.backgroundColor,t.beginPath(),ga(t,{x:y,y:m+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(p,m,c,l),t.strokeRect(p,m,c,l),t.fillStyle=i.backgroundColor,t.fillRect(y,m+1,c-2,l-2))}t.fillStyle=this.labelTextColors[r]}drawBody(t,n,r){const{body:s}=this,{bodySpacing:o,bodyAlign:i,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:d}=r,u=Gt(r.bodyFont);let f=u.lineHeight,g=0;const m=Ms(r.rtl,this.x,this.width),p=function(C){n.fillText(C,m.x(t.x+g),t.y+f/2),t.y+=f+o},y=m.textAlign(i);let b,_,x,M,S,k,N;for(n.textAlign=i,n.textBaseline="middle",n.font=u.string,t.x=Oa(this,y,r),n.fillStyle=r.bodyColor,st(this.beforeBody,p),g=a&&y!=="right"?i==="center"?c/2+d:c+2+d:0,M=0,k=s.length;M0&&n.stroke()}_updateAnimationTarget(t){const n=this.chart,r=this.$animations,s=r&&r.x,o=r&&r.y;if(s||o){const i=Vo[t.position].call(this,this._active,this._eventPosition);if(!i)return;const a=this._size=Tm(this,t),l=Object.assign({},i,this._size),c=Om(n,t,l),d=Am(t,l,c,n);(s._to!==d.x||o._to!==d.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,d))}}_willRender(){return!!this.opacity}draw(t){const n=this.options.setContext(this.getContext());let r=this.opacity;if(!r)return;this._updateAnimationTarget(n);const s={width:this.width,height:this.height},o={x:this.x,y:this.y};r=Math.abs(r)<.001?0:r;const i=yn(n.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;n.enabled&&a&&(t.save(),t.globalAlpha=r,this.drawBackground(o,t,s,n),P0(t,n.textDirection),o.y+=i.top,this.drawTitle(o,t,n),this.drawBody(o,t,n),this.drawFooter(o,t,n),$0(t,n.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,n){const r=this._active,s=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!aa(r,s),i=this._positionChanged(s,n);(o||i)&&(this._active=s,this._eventPosition=n,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,n,r=!0){if(n&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,o=this._active||[],i=this._getActiveElements(t,o,n,r),a=this._positionChanged(i,t),l=n||!aa(i,o)||a;return l&&(this._active=i,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,n))),l}_getActiveElements(t,n,r,s){const o=this.options;if(t.type==="mouseout")return[];if(!s)return n.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const i=this.chart.getElementsAtEventForMode(t,o.mode,o,r);return o.reverse&&i.reverse(),i}_positionChanged(t,n){const{caretX:r,caretY:s,options:o}=this,i=Vo[o.position].call(this,t,n);return i!==!1&&(r!==i.x||s!==i.y)}}Pe(Cd,"positioners",Vo);var Pm={id:"tooltip",_element:Cd,positioners:Vo,afterInit(e,t,n){n&&(e.tooltip=new Cd({chart:e,options:n}))},beforeUpdate(e,t,n){e.tooltip&&e.tooltip.initialize(n)},reset(e,t,n){e.tooltip&&e.tooltip.initialize(n)},afterDraw(e){const t=e.tooltip;if(t&&t._willRender()){const n={tooltip:t};if(e.notifyPlugins("beforeTooltipDraw",{...n,cancelable:!0})===!1)return;t.draw(e.ctx),e.notifyPlugins("afterTooltipDraw",n)}},afterEvent(e,t){if(e.tooltip){const n=t.replay;e.tooltip.handleEvent(t.event,n,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(e,t)=>t.bodyFont.size,boxWidth:(e,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Lm},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:e=>e!=="filter"&&e!=="itemSort"&&e!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const GO=(e,t,n,r)=>(typeof t=="string"?(n=e.push(t)-1,r.unshift({index:n,label:t})):isNaN(t)&&(n=null),n);function YO(e,t,n,r){const s=e.indexOf(t);if(s===-1)return GO(e,t,n,r);const o=e.lastIndexOf(t);return s!==o?n:s}const XO=(e,t)=>e===null?null:nn(Math.round(e),0,t);function $m(e){const t=this.getLabels();return e>=0&&en.length-1?null:this.getPixelForValue(n[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}Pe(Sd,"id","category"),Pe(Sd,"defaults",{ticks:{callback:$m}});function KO(e,t){const n=[],{bounds:s,step:o,min:i,max:a,precision:l,count:c,maxTicks:d,maxDigits:u,includeBounds:f}=e,g=o||1,m=d-1,{min:p,max:y}=t,b=!lt(i),_=!lt(a),x=!lt(c),M=(y-p)/(u+1);let S=a0((y-p)/m/g)*g,k,N,C,w;if(S<1e-14&&!b&&!_)return[{value:p},{value:y}];w=Math.ceil(y/S)-Math.floor(p/S),w>m&&(S=a0(w*S/m/g)*g),lt(l)||(k=Math.pow(10,l),S=Math.ceil(S*k)/k),s==="ticks"?(N=Math.floor(p/S)*S,C=Math.ceil(y/S)*S):(N=p,C=y),b&&_&&o&&YM((a-i)/o,S/1e3)?(w=Math.round(Math.min((a-i)/S,d)),S=(a-i)/w,N=i,C=a):x?(N=b?i:N,C=_?a:C,w=c-1,S=(C-N)/w):(w=(C-N)/S,da(w,Math.round(w),S/1e3)?w=Math.round(w):w=Math.ceil(w));const E=Math.max(l0(S),l0(N));k=Math.pow(10,lt(l)?E:l),N=Math.round(N*k)/k,C=Math.round(C*k)/k;let A=0;for(b&&(f&&N!==i?(n.push({value:i}),Na)break;n.push({value:P})}return _&&f&&C!==a?n.length&&da(n[n.length-1].value,a,Fm(a,M,e))?n[n.length-1].value=a:n.push({value:a}):(!_||C===a)&&n.push({value:C}),n}function Fm(e,t,{horizontal:n,minRotation:r}){const s=Qn(r),o=(n?Math.sin(s):Math.cos(s))||.001,i=.75*t*(""+e).length;return Math.min(t/o,i)}class qO extends Ts{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,n){return lt(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:n,maxDefined:r}=this.getUserBounds();let{min:s,max:o}=this;const i=l=>s=n?s:l,a=l=>o=r?o:l;if(t){const l=mr(s),c=mr(o);l<0&&c<0?a(0):l>0&&c>0&&i(0)}if(s===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||i(s-l)}this.min=s,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:n,stepSize:r}=t,s;return r?(s=Math.ceil(this.max/r)-Math.floor(this.min/r)+1,s>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${r} would result generating up to ${s} ticks. Limiting to 1000.`),s=1e3)):(s=this.computeTickLimit(),n=n||11),n&&(s=Math.min(n,s)),s}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,n=t.ticks;let r=this.getTickLimit();r=Math.max(2,r);const s={maxTicks:r,bounds:t.bounds,min:t.min,max:t.max,precision:n.precision,step:n.stepSize,count:n.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:n.minRotation||0,includeBounds:n.includeBounds!==!1},o=this._range||this,i=KO(s,o);return t.bounds==="ticks"&&XM(i,this,"value"),t.reverse?(i.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),i}configure(){const t=this.ticks;let n=this.min,r=this.max;if(super.configure(),this.options.offset&&t.length){const s=(r-n)/Math.max(t.length-1,1)/2;n-=s,r+=s}this._startValue=n,this._endValue=r,this._valueRange=r-n}getLabelForValue(t){return ed(t,this.chart.options.locale,this.options.ticks.format)}}class _d extends qO{determineDataLimits(){const{min:t,max:n}=this.getMinMax(!0);this.min=bn(t)?t:0,this.max=bn(n)?n:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),n=t?this.width:this.height,r=Qn(this.options.ticks.minRotation),s=(t?Math.sin(r):Math.cos(r))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(n/Math.min(40,o.lineHeight/s))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}Pe(_d,"id","linear"),Pe(_d,"defaults",{ticks:{callback:x0.formatters.numeric}});const Aa={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},sn=Object.keys(Aa);function zm(e,t){return e-t}function Vm(e,t){if(lt(t))return null;const n=e._adapter,{parser:r,round:s,isoWeekday:o}=e._parseOpts;let i=t;return typeof r=="function"&&(i=r(i)),bn(i)||(i=typeof r=="string"?n.parse(i,r):n.parse(i)),i===null?null:(s&&(i=s==="week"&&(ua(o)||o===!0)?n.startOf(i,"isoWeek",o):n.startOf(i,s)),+i)}function Hm(e,t,n,r){const s=sn.length;for(let o=sn.indexOf(e);o=sn.indexOf(n);o--){const i=sn[o];if(Aa[i].common&&e._adapter.diff(s,r,i)>=t-1)return i}return sn[n?sn.indexOf(n):0]}function QO(e){for(let t=sn.indexOf(e)+1,n=sn.length;t=t?n[r]:n[s];e[o]=!0}}function JO(e,t,n,r){const s=e._adapter,o=+s.startOf(t[0].value,r),i=t[t.length-1].value;let a,l;for(a=o;a<=i;a=+s.add(a,1,r))l=n[a],l>=0&&(t[l].major=!0);return t}function Bm(e,t,n){const r=[],s={},o=t.length;let i,a;for(i=0;i+t.value))}initOffsets(t=[]){let n=0,r=0,s,o;this.options.offset&&t.length&&(s=this.getDecimalForValue(t[0]),t.length===1?n=1-s:n=(this.getDecimalForValue(t[1])-s)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?r=o:r=(o-this.getDecimalForValue(t[t.length-2]))/2);const i=t.length<3?.5:.25;n=nn(n,0,i),r=nn(r,0,i),this._offsets={start:n,end:r,factor:1/(n+1+r)}}_generate(){const t=this._adapter,n=this.min,r=this.max,s=this.options,o=s.time,i=o.unit||Hm(o.minUnit,n,r,this._getLabelCapacity(n)),a=Qe(s.ticks.stepSize,1),l=i==="week"?o.isoWeekday:!1,c=ua(l)||l===!0,d={};let u=n,f,g;if(c&&(u=+t.startOf(u,"isoWeek",l)),u=+t.startOf(u,c?"day":i),t.diff(r,n,i)>1e5*a)throw new Error(n+" and "+r+" are too far apart with stepSize of "+a+" "+i);const m=s.ticks.source==="data"&&this.getDataTimestamps();for(f=u,g=0;f+p)}getLabelForValue(t){const n=this._adapter,r=this.options.time;return r.tooltipFormat?n.format(t,r.tooltipFormat):n.format(t,r.displayFormats.datetime)}format(t,n){const s=this.options.time.displayFormats,o=this._unit,i=n||s[o];return this._adapter.format(t,i)}_tickFormatFunction(t,n,r,s){const o=this.options,i=o.ticks.callback;if(i)return ht(i,[t,n,r],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,d=l&&a[l],u=c&&a[c],f=r[n],g=c&&u&&f&&f.major;return this._adapter.format(t,s||(g?u:d))}generateTickLabels(t){let n,r,s;for(n=0,r=t.length;n0?a:1}getDataTimestamps(){let t=this._cache.data||[],n,r;if(t.length)return t;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(n=0,r=s.length;n=e[r].pos&&t<=e[s].pos&&({lo:r,hi:s}=Qc(e,"pos",t)),{pos:o,time:a}=e[r],{pos:i,time:l}=e[s]):(t>=e[r].time&&t<=e[s].time&&({lo:r,hi:s}=Qc(e,"time",t)),{time:o,pos:a}=e[r],{time:i,pos:l}=e[s]);const c=i-o;return c?a+(l-a)*(t-o)/c:a}class jm extends Da{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),n=this._table=this.buildLookupTable(t);this._minPos=Ia(n,this.min),this._tableRange=Ia(n,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:n,max:r}=this,s=[],o=[];let i,a,l,c,d;for(i=0,a=t.length;i=n&&c<=r&&s.push(c);if(s.length<2)return[{time:n,pos:0},{time:r,pos:1}];for(i=0,a=s.length;is-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const n=this.getDataTimestamps(),r=this.getLabelTimestamps();return n.length&&r.length?t=this.normalize(n.concat(r)):t=n.length?n:r,t=this._cache.all=t,t}getDecimalForValue(t){return(Ia(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const n=this._offsets,r=this.getDecimalForPixel(t)/n.factor-n.end;return Ia(this._table,r*this._tableRange+this._minPos,!0)}}Pe(jm,"id","timeseries"),Pe(jm,"defaults",Da.defaults);const Wm="label";function Gm(e,t){typeof e=="function"?e(t):e&&(e.current=t)}function RO(e,t){const n=e.options;n&&t&&Object.assign(n,t)}function Ym(e,t){e.labels=t}function Xm(e,t,n=Wm){const r=[];e.datasets=t.map(s=>{const o=e.datasets.find(i=>i[n]===s[n]);return!o||!s.data||r.includes(o)?{...s}:(r.push(o),Object.assign(o,s),o)})}function e5(e,t=Wm){const n={labels:[],datasets:[]};return Ym(n,e.labels),Xm(n,e.datasets,t),n}function t5(e,t){const{height:n=150,width:r=300,redraw:s=!1,datasetIdKey:o,type:i,data:a,options:l,plugins:c=[],fallbackContent:d,updateMode:u,...f}=e,g=v.useRef(null),m=v.useRef(null),p=()=>{g.current&&(m.current=new Fo(g.current,{type:i,data:e5(a,o),options:l&&{...l},plugins:c}),Gm(t,m.current))},y=()=>{Gm(t,null),m.current&&(m.current.destroy(),m.current=null)};return v.useEffect(()=>{!s&&m.current&&l&&RO(m.current,l)},[s,l]),v.useEffect(()=>{!s&&m.current&&Ym(m.current.config.data,a.labels)},[s,a.labels]),v.useEffect(()=>{!s&&m.current&&a.datasets&&Xm(m.current.config.data,a.datasets,o)},[s,a.datasets]),v.useEffect(()=>{m.current&&(s?(y(),setTimeout(p)):m.current.update(u))},[s,l,a.labels,a.datasets,u]),v.useEffect(()=>{m.current&&(y(),setTimeout(p))},[i]),v.useEffect(()=>(p(),()=>y()),[]),h.jsx("canvas",{ref:g,role:"img",height:n,width:r,...f,children:d})}const n5=v.forwardRef(t5);function Km(e,t){return Fo.register(t),v.forwardRef((n,r)=>h.jsx(n5,{...n,ref:r,type:e}))}const r5=Km("bar",va),s5=Km("doughnut",Oo);Fo.register(Sd,_d,Ta,FO,Pm,Mm);const o5=({labels:e,datasets:t,height:n=200,showLegend:r=!1,showGrid:s=!1,showYGrid:o=!1,showYAxis:i=!1,className:a="",id:l,editorAttrs:c})=>{const d=v.useMemo(()=>({labels:e??[],datasets:(t??[]).map((m,p)=>({label:m.label,data:m.data,backgroundColor:m.backgroundColor||(p===0?"#7C3AED":"#DDD6FE"),borderRadius:m.borderRadius??4,barThickness:16,maxBarThickness:20,yAxisID:m.yAxisID??"y"}))}),[e,t]),u=v.useMemo(()=>(t??[]).some(m=>m.yAxisID==="y1"),[t]),f=v.useMemo(()=>({responsive:!0,maintainAspectRatio:!1,animation:{duration:750,easing:"easeOutQuart"},animations:{x:{duration:0},y:{from:m=>{if(m.type==="data")return m.chart.scales.y.getPixelForValue(0)}}},transitions:{active:{animation:{duration:200}},resize:{animation:{duration:0}}},plugins:{legend:{display:r,position:"top"},tooltip:{backgroundColor:"#1e293b",titleColor:"#fff",bodyColor:"#fff",padding:12,cornerRadius:8}},scales:{x:{grid:{display:s},border:{display:!1},ticks:{color:"#94a3b8",font:{size:11}}},y:{type:"linear",position:"left",grid:{display:o,color:"#e2e8f0"},border:{display:!1},ticks:{display:i,color:"#94a3b8",font:{size:11},padding:8},beginAtZero:!0,grace:"5%"},...u?{y1:{type:"linear",position:"right",grid:{display:!1},border:{display:!1},ticks:{display:i,color:"#94a3b8",font:{size:11},padding:8},beginAtZero:!0,grace:"5%"}}:{}}}),[r,s,o,i,u]),g=typeof n=="number"?n:/^\d+$/.test(String(n))?`${n}px`:n;return h.jsx("div",{className:`bar-chart-container ${a}`,style:{height:g},id:l,...c,children:h.jsx(r5,{data:d,options:f})})},i5=v.memo(o5,(e,t)=>JSON.stringify(e.labels)===JSON.stringify(t.labels)&&JSON.stringify(e.datasets)===JSON.stringify(t.datasets)&&e.height===t.height&&e.showLegend===t.showLegend&&e.showGrid===t.showGrid&&e.showYGrid===t.showYGrid&&e.showYAxis===t.showYAxis&&e.className===t.className&&e.id===t.id&&e.editorAttrs===t.editorAttrs);Fo.register(zo,Pm,Mm);const a5=({data:e,centerLabel:t,centerValue:n,size:r=200,cutout:s="70%",showLegend:o=!1,className:i="",id:a,editorAttrs:l})=>{const c=v.useMemo(()=>({labels:(e??[]).map(f=>f.name),datasets:[{data:(e??[]).map(f=>f.value),backgroundColor:(e??[]).map(f=>f.color),borderWidth:0,hoverOffset:4}]}),[e]),d=v.useMemo(()=>({responsive:!0,maintainAspectRatio:!1,cutout:s,plugins:{legend:{display:o},tooltip:{backgroundColor:"#1e293b",titleColor:"#fff",bodyColor:"#fff",padding:12,cornerRadius:8,callbacks:{label:f=>` $${f.parsed.toLocaleString()}`}}}}),[s,o]),u=typeof r=="number"?r:/^\d+$/.test(String(r))?`${r}px`:r;return h.jsx("div",{className:`donut-chart-wrapper ${i}`,id:a,...l,children:h.jsxs("div",{className:"relative",style:{width:u,height:u},children:[h.jsx(s5,{data:c,options:d}),(t||n)&&h.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center pointer-events-none",children:[t&&h.jsx("span",{className:"text-xs text-slate-500 dark:text-slate-400",children:t}),n&&h.jsx("span",{className:"text-lg font-bold text-slate-900 dark:text-white",children:n})]})]})})},l5=({id:e,className:t,style:n,children:r,editorAttrs:s})=>h.jsx("div",{id:e,className:t,style:n,...s,children:r}),c5=({children:e,cols:t=1,responsive:n,gap:r,rowGap:s,colGap:o,autoRows:i,autoCols:a,flow:l="row",className:c="",style:d,onClick:u,id:f,editorAttrs:g})=>{const m=["grid"];if(t&&m.push(`grid-cols-${t}`),n&&(n.sm&&m.push(`sm:grid-cols-${n.sm}`),n.md&&m.push(`md:grid-cols-${n.md}`),n.lg&&m.push(`lg:grid-cols-${n.lg}`),n.xl&&m.push(`xl:grid-cols-${n.xl}`),n["2xl"]&&m.push(`2xl:grid-cols-${n["2xl"]}`)),r!==void 0&&r>0&&m.push(`gap-${r}`),s!==void 0&&s>0&&m.push(`gap-y-${s}`),o!==void 0&&o>0&&m.push(`gap-x-${o}`),i){const y={auto:"auto-rows-auto",min:"auto-rows-min",max:"auto-rows-max",fr:"auto-rows-fr"};m.push(y[i])}if(a){const y={auto:"auto-cols-auto",min:"auto-cols-min",max:"auto-cols-max",fr:"auto-cols-fr"};m.push(y[a])}const p={row:"grid-flow-row",col:"grid-flow-col",dense:"grid-flow-dense","row-dense":"grid-flow-row-dense","col-dense":"grid-flow-col-dense"};return m.push(p[l]),c&&m.push(c),h.jsx(I,{className:m.join(" "),style:d,onClick:u,id:f,...g,children:e})},d5=({children:e,direction:t="row",justify:n="start",align:r="stretch",wrap:s="nowrap",gap:o=0,grow:i=!1,shrink:a=!0,className:l="",style:c,onClick:d,id:u,editorAttrs:f})=>{const g=["flex"],m={row:"flex-row","row-reverse":"flex-row-reverse",col:"flex-col","col-reverse":"flex-col-reverse"};g.push(m[t]);const p={wrap:"flex-wrap",nowrap:"flex-nowrap","wrap-reverse":"flex-wrap-reverse"};g.push(p[s]);const y={start:"justify-start",end:"justify-end",center:"justify-center",between:"justify-between",around:"justify-around",evenly:"justify-evenly"};g.push(y[n]);const b={start:"items-start",end:"items-end",center:"items-center",baseline:"items-baseline",stretch:"items-stretch"};return g.push(b[r]),o>0&&g.push(`gap-${o}`),i===!0?g.push("flex-grow"):typeof i=="number"&&i>0&&g.push(`flex-grow-${i}`),a===!1?g.push("shrink-0"):a===!0?g.push("flex-shrink"):typeof a=="number"&&a>0&&g.push(`flex-shrink-${a}`),l&&g.push(l),h.jsx(I,{className:g.join(" "),style:c,onClick:d,id:u,...f,children:e})},u5=({title:e,subtitle:t,children:n,padding:r="none",background:s="none",maxWidth:o,centered:i=!1,border:a=!1,shadow:l="none",rounded:c=!1,className:d="",style:u,onClick:f,id:g,editorAttrs:m})=>{const p=[],y={none:"",sm:"p-2",md:"p-4",lg:"p-6",xl:"p-8"};y[r]&&p.push(y[r]);const b={none:"",white:"bg-white dark:bg-gray-700",gray:"bg-gray-50 dark:bg-gray-800",primary:"bg-blue-50 dark:bg-blue-900/20",secondary:"bg-gray-50 dark:bg-gray-800"};if(s!=="none"&&p.push(b[s]),o){const x={sm:"max-w-sm",md:"max-w-md",lg:"max-w-lg",xl:"max-w-xl","2xl":"max-w-2xl","4xl":"max-w-4xl","6xl":"max-w-6xl","7xl":"max-w-7xl",full:"max-w-full"};p.push(x[o])}i&&p.push("mx-auto"),a&&p.push("border","border-gray-200","dark:border-gray-700");const _={none:"",sm:"shadow-sm",md:"shadow-md",lg:"shadow-lg",xl:"shadow-xl"};return l!=="none"&&p.push(_[l]),c&&p.push("rounded-lg"),d&&p.push(d),h.jsxs(Fd,{className:p.join(" "),style:u,onClick:f,id:g,...m,children:[(e||t)&&h.jsxs("div",{className:"mb-4",children:[e&&h.jsx("h2",{className:"text-2xl font-bold text-gray-900 dark:text-white",children:e}),t&&h.jsx("p",{className:"mt-1 text-sm text-gray-600 dark:text-gray-400",children:t})]}),n]})},f5=({leftWidth:e="250px",rightWidth:t="300px",leftSlot:n,centerSlot:r,rightSlot:s,className:o="",style:i,id:a,editorAttrs:l})=>{const c=`flex flex-row w-full h-full ${o}`.trim(),d={width:e,flexShrink:0},u={flex:1,minWidth:0},f={width:t,flexShrink:0};return h.jsxs(I,{className:c,style:i,id:a,...l,children:[h.jsx(I,{className:"flex flex-col",style:d,children:n}),h.jsx(I,{className:"flex flex-col",style:u,children:r}),h.jsx(I,{className:"flex flex-col",style:f,children:s})]})},h5={language:"json",theme:"vs-dark",options:{automaticLayout:!0,minimap:{enabled:!0},scrollBeyondLastLine:!1,fontSize:14,tabSize:2,formatOnPaste:!0,formatOnType:!0,wordWrap:"on",folding:!0,lineNumbers:"on",renderWhitespace:"selection"}},qm=[{uri:`${window.location.origin}/schemas/layout.json`,fileMatch:["*layout*.json"],schema:{type:"object",properties:{version:{type:"string"},endpoint:{type:"string"},components:{type:"array"}},required:["version","endpoint","components"]}}],g5={"vs/nls":{availableLanguages:{"*":"ko"}}};function p5(e){e.languages.json.jsonDefaults.setDiagnosticsOptions({validate:!0,schemas:qm,enableSchemaRequest:!0,schemaValidation:"error",schemaRequest:"error"})}const m5={identifier:"sirsoft-admin_basic",vendor:"sirsoft",name:{ko:"Admin Basic",en:"Admin Basic"},version:"1.0.4",license:"MIT",description:{ko:"그누보드7 기본 관리자 템플릿",en:"Gnuboard7 default admin template"},type:"admin",locales:["ko","en"],author:{name:"sirsoft",email:"contact@sirsoft.com",url:"https://sirsoft.com"},release_date:"2026-05-15",g7_version:">=7.0.6",dependencies:{modules:{},plugins:{}},assets:{css:["assets/css/main.css","assets/css/ui-system.css"],js:["dist/components.js"],fonts:["assets/fonts/"],images:["assets/images/"]},components:{basic:["a","button","checkbox","div","form","h1","h2","h3","hr","icon","img","input","label","li","nav","p","section","select","span","svg","table","tbody","td","textarea","th","thead","tr","ul"],composite:["action-menu","admin-footer","admin-header","admin-sidebar","alert-dialog","breadcrumb","card","confirm-dialog","data-grid","dialog","dropdown","empty-state","filter-group","icon-button","loading-spinner","modal","page-header","pagination","product-card","search-bar","stat-card","status-badge","tab-navigation","template-card","toast"],layout:["container","flex","grid","section"]},preview:{thumbnail:"preview/thumbnail.png",screenshots:["preview/screenshot1.png","preview/screenshot2.png"]},error_config:{layouts:{401:"errors/401",403:"errors/403",404:"errors/404",500:"errors/500",503:"errors/503",maintenance:"errors/maintenance"}},github_url:"https://github.com/gnuboard/g7-template-sirsoft-admin_basic",github_changelog_url:"https://github.com/gnuboard/g7-template-sirsoft-admin_basic/blob/main/CHANGELOG.md",externals:[{id:"fontawesome",type:"style",url:"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",preconnect:"https://cdnjs.cloudflare.com"},{id:"pretendard",type:"webfont",url:"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css",preconnect:"https://cdn.jsdelivr.net",crossorigin:"anonymous"},{id:"flag-icons",type:"style",url:"https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css"}]},er=window.G7Core?.createLogger?.("Handler:SetTheme")??{log:(...e)=>console.log("[Handler:SetTheme]",...e),warn:(...e)=>console.warn("[Handler:SetTheme]",...e),error:(...e)=>console.error("[Handler:SetTheme]",...e)},Ed=["auto","light","dark"],Zm="g7_color_scheme",b5=e=>e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e,La=e=>{const t=b5(e);document.documentElement.setAttribute("data-theme",t),t==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")};function y5(){try{const e=localStorage.getItem(Zm),t=e&&Ed.includes(e)?e:"auto";La(t);try{window.G7Core?.state?.setGlobal?.({theme:t})}catch(n){er.warn("Failed to set global theme state:",n)}er.log("Initial theme applied:",t)}catch(e){er.warn("Failed to load initial theme:",e),La("auto")}}async function v5(e,t){const n=e?.target;if(n&&typeof n=="string"&&Ed.includes(n)){La(n);try{window.G7Core?.state?.setGlobal?.({theme:n})}catch(r){er.warn("Failed to set global theme state:",r)}return}y5()}async function x5(e,t){let n;if(e?.target&&typeof e.target=="string"&&!e.target.includes("{{")&&(n=e.target),!n||typeof n!="string"){er.warn("Invalid theme:",n);return}if(!Ed.includes(n)){er.warn("Unsupported theme:",n);return}const r=n;try{localStorage.setItem(Zm,r)}catch(s){er.error("Failed to save theme to localStorage:",s);return}La(r);try{window.G7Core?.state?.setGlobal?.({theme:r})}catch(s){er.warn("Failed to set global theme state:",s)}er.log("Theme changed to:",r)}const As=window.G7Core?.createLogger?.("Handler:InitMenu")??{log:(...e)=>console.log("[Handler:InitMenu]",...e),warn:(...e)=>console.warn("[Handler:InitMenu]",...e),error:(...e)=>console.error("[Handler:InitMenu]",...e)};function Qm(e,t){for(const n of e){if(n.slug===t)return n;if(n.children&&n.children.length>0){const r=Qm(n.children,t);if(r)return r}}return null}function Jm(e){return new URLSearchParams(window.location.search).get(e)}function w5(e){return window.G7Core?.state?.getDataSource?.(e)}function k5(e){const t=[e,e?.data,e?.data?.data];for(const n of t)if(Array.isArray(n)&&n.length>0)return n;return null}async function C5(e,t=30,n=100){for(let r=0;rsetTimeout(o,n))}return null}async function S5(e){const t=window.G7Core;try{const r=(await t?.api?.get?.(`/api/admin/menus/${e.id}`))?.data;if(r&&typeof r=="object"&&r.id===e.id)return r}catch(n){As.warn("[initMenuFromUrl] Failed to hydrate menu detail:",n)}return e}async function _5(e,t){const n=window.G7Core,r=Jm("menu"),s=Jm("mode");if(!r){As.log("[initMenuFromUrl] No menu parameter in URL");return}if(!n?.state?.set){As.warn("[initMenuFromUrl] G7Core.state.set not available");return}const o=await C5("menus");if(!o){As.warn("[initMenuFromUrl] menus data not available after waiting");return}const i=Qm(o,r);if(!i){As.warn("[initMenuFromUrl] Menu not found with slug:",r);return}const a=await S5(i),l=s==="edit"?"edit":"view";n.state.set({selectedMenuId:a.id,selectedMenu:a,panelMode:l}),l==="edit"&&n.state.set({formData:{name:a.name,slug:a.slug,url:a.url,icon:a.icon,parent_id:a.parent_id,extension_type:a.extension_type,extension_identifier:a.extension_identifier,is_active:a.is_active,roles:(a.roles??[]).map(c=>c.id)}}),As.log("[initMenuFromUrl] Menu initialized from URL:",{slug:r,mode:l,menuId:i.id})}const qt=window.G7Core?.createLogger?.("Handler:FilterVisibility")??{log:(...e)=>console.log("[Handler:FilterVisibility]",...e),warn:(...e)=>console.warn("[Handler:FilterVisibility]",...e),error:(...e)=>console.error("[Handler:FilterVisibility]",...e)},Pa="g7_filter_visibility_";async function E5(e,t){const{storageKey:n,defaultFilters:r=[],stateKey:s="visibleFilters"}=e?.params||{};if(!n){qt.warn("[initFilterVisibility] storageKey is required");return}const o=`${Pa}${n}`;try{const i=localStorage.getItem(o);let a;if(i){const l=JSON.parse(i);Array.isArray(l)?(a=l,qt.log(`[initFilterVisibility] Loaded from localStorage: ${a.join(", ")}`)):(a=r,qt.warn("[initFilterVisibility] Invalid saved data format, using defaults"))}else a=r,qt.log(`[initFilterVisibility] No saved data, using defaults: ${r.join(", ")}`);t?.setState&&t.setState({[s]:a})}catch(i){qt.error("[initFilterVisibility] Failed to load filter visibility:",i),t?.setState&&t.setState({[s]:r})}}async function M5(e,t){const{storageKey:n,filters:r}=e?.params||{};if(!n){qt.warn("[saveFilterVisibility] storageKey is required");return}if(!Array.isArray(r)){qt.warn("[saveFilterVisibility] filters must be an array");return}const s=`${Pa}${n}`;try{localStorage.setItem(s,JSON.stringify(r)),qt.log(`[saveFilterVisibility] Saved to localStorage: ${r.join(", ")}`)}catch(o){qt.error("[saveFilterVisibility] Failed to save filter visibility:",o)}}async function N5(e,t){const{storageKey:n,filterId:r,stateKey:s="visibleFilters"}=e?.params||{};if(!n||!r){qt.warn("[toggleFilterVisibility] storageKey and filterId are required");return}const o=t?.state?.[s]||[];let i;o.includes(r)?i=o.filter(l=>l!==r):i=[...o,r],t?.setState&&t.setState({[s]:i});const a=`${Pa}${n}`;try{localStorage.setItem(a,JSON.stringify(i)),qt.log(`[toggleFilterVisibility] Toggled ${r}: ${i.join(", ")}`)}catch(l){qt.error("[toggleFilterVisibility] Failed to save:",l)}}async function T5(e,t){const{storageKey:n,defaultFilters:r=[],stateKey:s="visibleFilters"}=e?.params||{};if(!n){qt.warn("[resetFilterVisibility] storageKey is required");return}const o=`${Pa}${n}`;try{localStorage.setItem(o,JSON.stringify(r)),t?.setState&&t.setState({[s]:r}),qt.log(`[resetFilterVisibility] Reset to defaults: ${r.join(", ")}`)}catch(i){qt.error("[resetFilterVisibility] Failed to reset:",i)}}const Ds=window.G7Core?.createLogger?.("Handler:MultilingualTag")??{log:(...e)=>console.log("[Handler:MultilingualTag]",...e),warn:(...e)=>console.warn("[Handler:MultilingualTag]",...e),error:(...e)=>console.error("[Handler:MultilingualTag]",...e)};async function O5(e,t){const n=window.G7Core;if(!n){Ds.error("G7Core not available");return}const r=n.state?.getGlobal?.()?.multilingualTagEdit;if(!r){Ds.warn("No multilingualTagEdit state found"),n.modal?.close?.();return}const{fieldName:s,editingIndex:o,values:i,statePath:a,currentTags:l}=r;if(!Object.values(i||{}).some(g=>typeof g=="string"&&g.trim()!=="")){Ds.warn("No values to save"),n.modal?.close?.(),Md(n);return}const d=Array.isArray(l)?l:[];let u;o==null?u=[...d,i]:u=d.map((g,m)=>m===o?i:g);const f=a||(s?.startsWith("form.")?s:`form.${s}`);n.state?.setParentLocal?.({[f]:u}),n.modal?.close?.(),Md(n)}async function A5(e,t){const n=window.G7Core;if(!n){Ds.error("G7Core not available");return}n.modal?.close?.(),Md(n)}async function D5(e,t){const n=window.G7Core;if(!n){Ds.error("G7Core not available");return}const r=e?.params?.locale;if(!r){Ds.warn("No locale specified");return}const s=t?.event;let o="";s?.target?.value!==void 0?o=s.target.value:s?.value!==void 0&&(o=s.value);const a=(n.state?.getGlobal?.()||{}).multilingualTagEdit||{},l={...a.values||{},[r]:o};n.dispatch?.({handler:"setState",params:{target:"global",multilingualTagEdit:{...a,values:l}}})}function Md(e){e.dispatch?.({handler:"setState",params:{target:"global",multilingualTagEdit:null}})}function Rm(e){const t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0"),s=String(e.getHours()).padStart(2,"0"),o=String(e.getMinutes()).padStart(2,"0"),i=String(e.getSeconds()).padStart(2,"0");return`${t}-${n}-${r}T${s}:${o}:${i}`}function I5(e){const t=new Date,n=new Date(t.getFullYear(),t.getMonth(),t.getDate(),23,59,59),r=Rm(n),s=new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0);switch(e){case"today":break;case"week":s.setDate(s.getDate()-6);break;case"month":s.setMonth(s.getMonth()-1);break;case"3months":s.setMonth(s.getMonth()-3);break;case"6months":s.setMonth(s.getMonth()-6);break;case"1year":s.setFullYear(s.getFullYear()-1);break}return{startDate:Rm(s),endDate:r}}function L5(e){const{preset:t}=e.params||{preset:"today"},{startDate:n,endDate:r}=I5(t);return{startDate:n,endDate:r,preset:t}}const Ho=window.G7Core?.createLogger?.("Handler:Sidebar")??{log:(...e)=>console.log("[Handler:Sidebar]",...e),warn:(...e)=>console.warn("[Handler:Sidebar]",...e),error:(...e)=>console.error("[Handler:Sidebar]",...e)},eb="g7_admin_sidebar_collapsed";function tb(){try{return localStorage.getItem(eb)==="1"}catch{return!1}}function P5(e){try{localStorage.setItem(eb,e?"1":"0")}catch(t){Ho.error("Failed to save sidebar state to localStorage:",t)}try{window.G7Core?.state?.setGlobal?.({sidebarCollapsed:e})}catch(t){Ho.warn("Failed to set global sidebar state:",t)}}function nb(){const e=tb();try{window.G7Core?.state?.setGlobal?.({sidebarCollapsed:e})}catch(t){Ho.warn("Failed to restore sidebar state:",t)}Ho.log("Initial sidebar collapsed state restored:",e)}async function $5(e,t){nb()}async function F5(e,t){const r=!(()=>{try{return!!window.G7Core?.state?.getGlobal?.()?.sidebarCollapsed}catch{return tb()}})();P5(r),Ho.log("Sidebar collapsed toggled to:",r)}const Nd=window.G7Core?.createLogger?.("Handler:DownloadAttachment")??{log:(...e)=>console.log("[Handler:DownloadAttachment]",...e),warn:(...e)=>console.warn("[Handler:DownloadAttachment]",...e),error:(...e)=>console.error("[Handler:DownloadAttachment]",...e)};async function z5(e,t){const n=window.G7Core,{url:r,filename:s}=e?.params||{};if(!r){Nd.warn("다운로드 URL 이 없어 요청을 건너뜁니다.");return}if(!n?.api?.get){Nd.error("G7Core.api.get 이 초기화되지 않아 다운로드를 실행할 수 없습니다.");return}try{const o=await n.api.get(r,{responseType:"blob"});if(o){const i=URL.createObjectURL(o),a=document.createElement("a");a.href=i,a.download=s||"",document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(i)}}catch(o){Nd.error("첨부파일 다운로드 실패",o),n?.toast?.error?.(n?.t?.("common.download_failed")??"Download failed")}}const $a=window.G7Core?.createLogger?.("Handler:DetectAssetUrlMode")??{log:(...e)=>console.log("[Handler:DetectAssetUrlMode]",...e),warn:(...e)=>console.warn("[Handler:DetectAssetUrlMode]",...e),error:(...e)=>console.error("[Handler:DetectAssetUrlMode]",...e)},V5="G7_ASSET_PROBE_OK";async function rb(e){try{const t=await fetch(e,{cache:"no-store",credentials:"omit"});if(!t.ok)return!1;const n=t.headers.get("content-type")??"";return/javascript|ecmascript/i.test(n)?(await t.text()).includes(V5):!1}catch{return!1}}async function sb(){const[e,t]=await Promise.all([rb("/api/system/asset-probe.js"),rb("/api/system/asset-probe")]);return e&&t?"extension":!e&&t?"extensionless":"unavailable"}function H5(){return window.G7Config?.settings?.general?.asset_url_mode??"extension"}async function U5(e){try{const t=await sb();if(t==="unavailable")return;const n=H5();if(t===n)return;window.G7Core?.state?.setGlobal?.({assetUrlModeDrift:{detected:t,stored:n}})}catch(t){$a.warn("자산 URL 방식 대조 실패 (대시보드 동작에는 영향 없음):",t)}}async function B5(e,t){const n=window.G7Core,r=t?.setState??n?.state?.setLocal,s=o=>{typeof r=="function"?r({asset_url_mode_detect_status:o}):$a.warn("setState 를 찾지 못해 감지 상태를 폼에 반영하지 못했습니다")};try{s("detecting");const o=await sb();if(o==="unavailable"){s("unavailable");return}typeof r=="function"?r({"general.asset_url_mode":o,asset_url_mode_detect_status:o}):$a.warn("setState 를 찾지 못해 감지 결과를 폼에 반영하지 못했습니다")}catch(o){$a.error("자산 URL 방식 감지 실패:",o),s("unavailable")}}const Fa={detectAssetUrlMode:B5,checkAssetUrlModeDrift:U5,setTheme:x5,initTheme:v5,scrollToSection:fu,initMenuFromUrl:_5,initFilterVisibility:E5,saveFilterVisibility:M5,toggleFilterVisibility:N5,resetFilterVisibility:T5,saveMultilingualTag:O5,cancelMultilingualTag:A5,updateMultilingualTagValue:D5,setDateRange:L5,toggleSidebar:F5,initSidebar:$5,downloadAttachment:z5},Uo=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic:IdentityLauncher")??{log:(...e)=>console.log("[Template:sirsoft-admin_basic:IdentityLauncher]",...e),warn:(...e)=>console.warn("[Template:sirsoft-admin_basic:IdentityLauncher]",...e),error:(...e)=>console.error("[Template:sirsoft-admin_basic:IdentityLauncher]",...e)};function j5(){const t=window.G7Core?.api?.getToken?.()??null;return t?{Authorization:`Bearer ${t}`}:{}}function W5(){try{const e=typeof window<"u"?window.localStorage?.getItem("g7_locale"):null;return e?{"Accept-Language":e}:{}}catch{return{}}}async function G5(e,t=null){const n={purpose:e.purpose};e.provider_id&&(n.provider_id=e.provider_id),t&&(n.target=t);const r=e.challenge_start_url||"/api/identity/challenges",s=await fetch(r,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json",...W5(),...j5()},body:JSON.stringify(n)}),o=await s.json().catch(()=>null);if(!s.ok||!o?.success){const a=o?.message??`Challenge 시작 실패 (HTTP ${s.status})`;throw new Error(a)}const i=o.data??o;return{id:i.id,expires_at:i.expires_at,render_hint:i.render_hint??e.render_hint??"text_code",public_payload:i.public_payload,redirect_url:i.redirect_url,max_attempts:typeof i.max_attempts=="number"?i.max_attempts:void 0}}async function Y5(e){const t=window.G7Core,n=t?.identity;if(!t?.dispatch||!t?.state||!n)return Uo.error("G7Core 또는 G7Core.identity 가 초기화되지 않아 launcher 를 실행할 수 없습니다."),{status:"failed",failureCode:"G7_NOT_READY"};if(e.render_hint==="external_redirect"||e.redirect_url)return n.redirectExternally(e);const r=e.target&&(e.target.email||e.target.phone)?e.target:null;let s;try{s=await G5(e,r)}catch(m){Uo.error("Challenge 시작 실패:",m);try{await t.dispatch({handler:"toast",params:{type:"error",message:m instanceof Error?m.message:"본인인증 시작에 실패했습니다."}})}catch{}return{status:"failed",failureCode:"CHALLENGE_START_FAILED",reason:m instanceof Error?m.message:String(m)}}if(s.redirect_url)return n.redirectExternally({...e,render_hint:"external_redirect",redirect_url:s.redirect_url});const o=s.expires_at?new Date(s.expires_at).getTime():0,i=o>0?Math.max(0,Math.floor((o-Date.now())/1e3)):0,a=typeof s.max_attempts=="number"?s.max_attempts:0;t.state.set({identityChallenge:{policy_key:e.policy_key,purpose:e.purpose,provider_id:e.provider_id??null,render_hint:s.render_hint,challenge_id:s.id,expires_at:s.expires_at,public_payload:s.public_payload??{},target:r,code:"",error:null,attempts:0,maxAttempts:a,remainingSeconds:i,resendCooldown:0}});let l=o,c=0;const d=window.setInterval(()=>{const m=l>0?Math.max(0,Math.floor((l-Date.now())/1e3)):0;c=Math.max(0,c-1),t.state.set({identityChallenge:{remainingSeconds:m,resendCooldown:c}})},1e3);let u=null;try{u=t.state.subscribe?.(m=>{const p=m?.identityChallenge?.expires_at;if(typeof p=="string"){const b=new Date(p).getTime();Number.isFinite(b)&&b!==l&&(l=b)}const y=m?.identityChallenge?.resendCooldown;typeof y=="number"&&y>c&&(c=y)})??null}catch{}const f=n.createDeferred();try{await t.dispatch({handler:"openModal",target:"identity-challenge-modal"})}catch(m){return Uo.error("openModal 실패 — 풀페이지로 폴백:",m),window.clearInterval(d),u?.(),n.redirectExternally({...e,render_hint:"external_redirect",redirect_url:`/admin/identity/challenge?challenge_id=${encodeURIComponent(s.id)}&return=${encodeURIComponent(window.location.href)}`})}const g=await f;return window.clearInterval(d),u?.(),g}function X5(){const e=window.G7Core?.identity;if(!e?.setLauncher){Uo.warn("G7Core.identity 가 아직 초기화되지 않아 launcher 등록을 건너뜁니다. 코어 부트스트랩 순서를 확인하세요.");return}e.setLauncher(Y5),Uo.log("IDV launcher registered (sirsoft-admin_basic)")}function K5(e){const t=e.icons;return Array.isArray(t)?t.filter(n=>!!n&&typeof n=="object"&&typeof n.value=="string"):[]}function q5(e,t){const n=e.label;return typeof n=="string"?n.startsWith("$t:")?t(n.slice(3)):n:e.value}function Z5(e,t,n){if(n==="")return!0;const r=n.toLowerCase();return e.value.toLowerCase().includes(r)||t.toLowerCase().includes(r)?!0:(e.keywords??[]).some(s=>s.toLowerCase().includes(r))}function Q5({entry:e}){const t=e.preview;return t?.html?h.jsx("span",{className:"g7le-icon-preview",dangerouslySetInnerHTML:{__html:t.html},style:Od}):t?.className?h.jsx("span",{className:`g7le-icon-preview ${t.className}`,style:Od}):h.jsx("i",{className:`fas fa-${e.value}`,style:Od,"aria-hidden":"true"})}function J5({control:e,value:t,onChange:n,t:r}){const s=K5(e),o=t==null?"":String(t),i=typeof e.iconColumns=="number"?e.iconColumns:6,a=e.iconSearchPlaceholder,l=typeof a=="string"&&a.startsWith("$t:")?r(a.slice(3)):r("layout_editor.icon_picker.search_placeholder"),[c,d]=v.useState("");if(s.length===0)return h.jsx("div",{className:"g7le-widget g7le-widget--icon-picker","data-testid":"g7le-widget-icon-picker",style:ob,children:h.jsx("input",{type:"text",className:"g7le-widget g7le-widget--icon-free","data-testid":"g7le-widget-icon-picker-free",value:o,placeholder:r("layout_editor.icon_picker.free_input_hint"),onChange:g=>n(g.target.value===""?void 0:g.target.value),style:ib})});const u=g=>q5(g,r),f=s.filter(g=>Z5(g,u(g),c));return h.jsxs("div",{className:"g7le-widget g7le-widget--icon-picker","data-testid":"g7le-widget-icon-picker",style:ob,children:[h.jsx("input",{type:"text",className:"g7le-widget g7le-widget--icon-search","data-testid":"g7le-widget-icon-picker-search",value:c,placeholder:l,onChange:g=>d(g.target.value),style:ib}),h.jsxs("div",{className:"g7le-icon-grid","data-testid":"g7le-widget-icon-picker-grid",style:{...R5,gridTemplateColumns:`repeat(${i}, minmax(0, 1fr))`},children:[h.jsx("button",{type:"button","data-testid":"g7le-icon-cell-none",title:r("layout_editor.icon_picker.none"),onClick:()=>n(void 0),"aria-pressed":o==="",style:o===""?ab:Td,children:h.jsx("span",{style:eA,children:"—"})}),f.map(g=>{const m=g.value===o;return h.jsx("button",{type:"button","data-testid":`g7le-icon-cell-${g.value}`,title:u(g),"aria-pressed":m,onClick:()=>n(g.value),style:m?ab:Td,children:h.jsx(Q5,{entry:g})},g.value)})]}),f.length===0&&h.jsx("div",{"data-testid":"g7le-widget-icon-picker-empty",style:tA,children:r("layout_editor.icon_picker.no_results")})]})}const ob={display:"flex",flexDirection:"column",gap:6,width:"100%"},ib={padding:"5px 8px",fontSize:12,border:"1px solid #cbd5e1",borderRadius:6,width:"100%"},R5={display:"grid",gap:4,maxHeight:220,overflowY:"auto",padding:2},Td={display:"flex",alignItems:"center",justifyContent:"center",minHeight:34,padding:4,border:"1px solid #e2e8f0",borderRadius:6,background:"#fff",cursor:"pointer"},ab={...Td,border:"2px solid #2563eb",background:"#eff6ff"},Od={display:"inline-flex",alignItems:"center",justifyContent:"center",fontSize:16,lineHeight:1},eA={fontSize:10,color:"#475569",fontFamily:"monospace",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",maxWidth:"100%"},tA={fontSize:11,color:"#94a3b8",fontStyle:"italic",padding:"4px 2px"},lb=130,Dn=20;function nA(e){const t=e?.arrayProp;return typeof t=="string"&&t?t:"tabs"}function rA(e){let t=0;for(const n of e){const r=typeof n.id=="number"?n.id:Number(n.id);Number.isFinite(r)&&r>t&&(t=r)}return t+1}function sA({node:e,params:t,cellBoxes:n,t:r,onPatchNode:s}){const o=nA(t),i=v.useMemo(()=>{const g=(e.props??{})[o];return typeof g=="string"||!Array.isArray(g)?null:g},[e,o]),a=v.useMemo(()=>{const g=new Map,m=`props.${o}.`;for(const p of n??[]){if(!p.path.startsWith(m))continue;const y=Number(p.path.slice(m.length));Number.isInteger(y)&&g.set(y,{top:p.top,left:p.left,width:p.width,height:p.height})}return g},[n,o]),l=v.useCallback(g=>{const m=e.props??{};s({...e,props:{...m,[o]:g}})},[e,o,s]),c=v.useCallback(g=>{if(!i)return;const p={id:rA(i),label:r("editor.tabnav_inplace.new_tab")},y=i.slice();y.splice(g,0,p),l(y)},[i,l,r]),d=v.useCallback(g=>{if(!i)return;const m=i.slice();m.splice(g,1),l(m)},[i,l]),u=v.useCallback((g,m)=>{if(!i)return;const p=g+m;if(p<0||p>=i.length)return;const y=i.slice(),[b]=y.splice(g,1);y.splice(p,0,b),l(y)},[i,l]);if(!i||a.size===0)return null;const f={position:"absolute",width:Dn,height:Dn,display:"flex",alignItems:"center",justifyContent:"center",fontSize:12,lineHeight:1,border:"1px solid rgba(0,0,0,0.15)",borderRadius:4,background:"#ffffff",color:"#1f2937",cursor:"pointer",boxShadow:"0 1px 2px rgba(0,0,0,0.18)",pointerEvents:"auto",padding:0,zIndex:lb};return h.jsx("div",{className:"g7le-tabnav-inplace","data-testid":"g7le-tabnav-inplace",style:{position:"absolute",inset:0,pointerEvents:"none",zIndex:lb},children:i.map((g,m)=>{const p=a.get(m);if(!p)return null;const y=p.left+p.width;return h.jsxs("div",{"data-testid":`g7le-tabnav-aff-${m}`,children:[h.jsx("button",{type:"button","data-testid":`g7le-tabnav-remove-${m}`,title:r("editor.tabnav_inplace.remove"),"aria-label":r("editor.tabnav_inplace.remove"),style:{...f,left:y-Dn/2,top:p.top-Dn/2,color:"#b91c1c"},onClick:b=>{b.preventDefault(),b.stopPropagation(),d(m)},children:"✕"}),m>0&&h.jsx("button",{type:"button","data-testid":`g7le-tabnav-move-left-${m}`,title:r("editor.tabnav_inplace.move_left"),"aria-label":r("editor.tabnav_inplace.move_left"),style:{...f,left:p.left,top:p.top+p.height-Dn/2},onClick:b=>{b.preventDefault(),b.stopPropagation(),u(m,-1)},children:"◀"}),m{b.preventDefault(),b.stopPropagation(),u(m,1)},children:"▶"}),m===0&&h.jsx("button",{type:"button","data-testid":"g7le-tabnav-add-0",title:r("editor.tabnav_inplace.add"),"aria-label":r("editor.tabnav_inplace.add"),style:{...f,left:p.left-Dn,top:p.top+p.height/2-Dn/2,color:"#15803d"},onClick:b=>{b.preventDefault(),b.stopPropagation(),c(0)},children:"+"}),h.jsx("button",{type:"button","data-testid":`g7le-tabnav-add-${m+1}`,title:r("editor.tabnav_inplace.add"),"aria-label":r("editor.tabnav_inplace.add"),style:{...f,left:y-Dn/2,top:p.top+p.height/2-Dn/2,color:"#15803d"},onClick:b=>{b.preventDefault(),b.stopPropagation(),c(m+1)},children:"+"})]},`tab-aff-${g.id}-${m}`)})})}const Ad=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic:EditorWidgets")??{log:(...e)=>console.log("[Template:sirsoft-admin_basic:EditorWidgets]",...e),warn:(...e)=>console.warn("[Template:sirsoft-admin_basic:EditorWidgets]",...e)};function oA(){if(typeof window>"u")return;const e=window.G7Core?.layoutEditor;if(!e?.registerWidget){Ad.warn("G7Core.layoutEditor 미노출 — icon-picker 위젯 등록 보류");return}e.registerWidget("icon-picker",J5),Ad.log("편집기 위젯 'icon-picker' 등록됨(Font Awesome 검색 그리드)"),e.registerCanvasOverlay&&(e.registerCanvasOverlay("tabnav",sA),Ad.log("편집기 캔버스 오버레이 'tabnav' 등록됨(TabNavigation 탭 인플레이스)"))}const Dd=window.G7Core?.createLogger?.("Template:sirsoft-admin_basic")??{log:(...e)=>console.log("[Template:sirsoft-admin_basic]",...e),warn:(...e)=>console.warn("[Template:sirsoft-admin_basic]",...e),error:(...e)=>console.error("[Template:sirsoft-admin_basic]",...e)};typeof window<"u"&&(window.G7TemplateHandlers=Fa);function cb(){if(typeof window<"u"){let e=0;const t=50,n=()=>{const r=window.G7Core?.getActionDispatcher?.();r?(Object.entries(Fa).forEach(([s,o])=>{r.registerHandler(s,o)}),Dd.log(`${Object.keys(Fa).length} custom handler(s) registered:`,Object.keys(Fa)),X5(),nb()):(e++,e<=t?(Dd.warn(`ActionDispatcher not found, retrying... (${e}/${t})`),setTimeout(n,100)):Dd.error("Failed to register handlers: ActionDispatcher not available after maximum retries"))};document.readyState==="complete"?n():window.addEventListener("load",n)}}return oA(),cb(),R.A=Wr,R.Accordion=dc,R.ActionMenu=Ls,R.AdminFooter=Ky,R.AdminHeader=Wy,R.AdminSidebar=jy,R.Alert=wu,R.AlertDialog=Ly,R.Badge=jd,R.BarChart=i5,R.Breadcrumb=bu,R.Button=ae,R.Card=Mb,R.CardGrid=by,R.CategoryTree=d_,R.Checkbox=_n,R.ChipCheckbox=ZE,R.Code=Cb,R.CodeEditor=dx,R.ColumnSelector=bx,R.ConfirmDialog=pu,R.Container=l5,R.DataGrid=uy,R.Dialog=gu,R.Div=I,R.DonutChart=a5,R.Dropdown=vy,R.DropdownButton=QE,R.DropdownMenuItem=JE,R.DynamicFieldList=lM,R.EmptyState=Hy,R.ExtensionBadge=gh,R.FileInput=Pd,R.FileUploader=zp,R.FilterGroup=Cy,R.FilterVisibilitySelector=WE,R.Flex=d5,R.Form=Ga,R.FormField=hM,R.Fragment=_b,R.Grid=c5,R.H1=Ba,R.H2=or,R.H3=jr,R.H4=$d,R.Hr=Eb,R.HtmlContent=ol,R.HtmlEditor=HE,R.I=pt,R.Icon=oe,R.IconButton=Ay,R.IconName=ue,R.IconSelect=s_,R.ImageGallery=Zi,R.Img=Sn,R.Input=kt,R.Label=ln,R.LanguageSelector=Xy,R.LayoutEditorHeader=Ry,R.LayoutFileList=ev,R.LayoutHistoryPanel=fx,R.LayoutWarnings=jE,R.Li=Wa,R.LoadingSpinner=By,R.LoginForm=hx,R.Modal=Zo,R.MultilingualInput=Za,R.MultilingualLocaleContext=Bc,R.MultilingualTabPanel=fM,R.MultilingualTagInput=fC,R.Nav=Go,R.NotificationCenter=Gy,R.Ol=mb,R.Optgroup=xb,R.Option=vb,R.P=yt,R.PageHeader=qy,R.PageLoading=wx,R.PageTransitionIndicator=vx,R.Pagination=Yo,R.PermissionTree=l_,R.Pre=Sb,R.ProductCard=xy,R.RadioGroup=FE,R.RichSelect=KE,R.SearchBar=ky,R.SearchableDropdown=hC,R.Section=Fd,R.SectionHeader=Qy,R.SectionLayout=u5,R.Select=Gr,R.SlotContainer=XE,R.SortableMenuItem=rg,R.SortableMenuList=n_,R.Span=J,R.StatCard=wy,R.StatusBadge=Xo,R.Svg=Xn,R.TabNavigation=Sy,R.TabNavigationScroll=Ty,R.Table=Ya,R.TagInput=cC,R.TagSelect=gC,R.Tbody=Ka,R.Td=Jt,R.TemplateCard=Jy,R.Textarea=sr,R.Tfoot=qa,R.Th=Yr,R.Thead=Xa,R.ThemeToggle=px,R.ThreeColumnLayout=f5,R.Toast=zy,R.Toggle=Qa,R.Tr=ir,R.Ul=ja,R.UnifiedIcon=kb,R.UserProfile=Yy,R.VersionList=Fu,R.configureJSONLanguageService=p5,R.defaultJSONSchemas=qm,R.executeImageDownload=Fc,R.initTemplate=cb,R.jsonEditorDefaults=h5,R.monacoInitOptions=g5,R.templateMetadata=m5,R.useCurrentMultilingualLocale=Gp,R.useImageGallery=Sp,Object.defineProperty(R,Symbol.toStringTag,{value:"Module"}),R})({},ReactJSXRuntime,React,ReactDOM); //# sourceMappingURL=components.iife.js.map diff --git a/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/DataGrid.d.ts b/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/DataGrid.d.ts index d26e2d48..b563f03a 100644 --- a/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/DataGrid.d.ts +++ b/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/DataGrid.d.ts @@ -114,7 +114,20 @@ export interface DataGridProps { alwaysShowPagination?: boolean; serverSidePagination?: boolean; serverCurrentPage?: number; - serverTotalPages?: number; + /** + * 서버가 알려준 마지막 페이지 번호 + * + * 총 건수를 상한까지만 센 목록은 마지막 페이지를 계산할 수 없어 서버가 `null` 을 보낸다. + * 1 로 채우면 화면이 "1페이지뿐" 이라고 잘못 말하므로 null 을 그대로 흘려보낸다. + */ + serverTotalPages?: number | null; + /** + * 다음 페이지 존재 여부 (총 건수를 모르는 목록에서 사용) + * + * 총 건수를 몰라도 서버는 이 값을 정확히 판정한다. 마지막 페이지를 계산할 수 없어도 + * "다음" 이동은 이 값으로 끝까지 열어 둘 수 있다. + */ + serverHasMorePages?: boolean; onPageChange?: (page: number) => void; prevText?: string; nextText?: string; diff --git a/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/Pagination.d.ts b/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/Pagination.d.ts index 3321daee..223574fc 100644 --- a/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/Pagination.d.ts +++ b/templates/_bundled/sirsoft-admin_basic/dist/src/components/composite/Pagination.d.ts @@ -2,10 +2,40 @@ import { default as React } from 'react'; import { EditorAttrs } from '../../types'; export interface PaginationProps { currentPage: number; - totalPages: number; + /** + * 마지막 페이지 번호 + * + * 총 건수가 상한을 넘겨 정확히 세지 않은 목록은 마지막 페이지를 계산할 수 없다. + * 그 경우 서버가 `null` 을 보내며, 페이지 번호 목록과 마지막 페이지 점프가 사라지고 + * 이전/다음 이동만 남는다. 0 이나 1 로 채우면 화면이 "1페이지뿐" 이라고 잘못 말하게 된다. + */ + totalPages: number | null; onPageChange: (page: number) => void; maxVisiblePages?: number; + /** + * 첫/마지막 페이지 버튼 표시 여부 (두 버튼의 기본값) + * + * 마지막 페이지만 따로 감춰야 하는 경우가 있으므로 `showLast` 로 덮어쓸 수 있다. + */ showFirstLast?: boolean; + /** + * 첫 페이지 버튼 표시 여부 (미지정 시 showFirstLast 를 따른다) + */ + showFirst?: boolean; + /** + * 마지막 페이지 버튼 표시 여부 (미지정 시 showFirstLast 를 따른다) + * + * 총 건수가 부정확한 목록에서는 마지막 페이지를 계산할 수 없으므로, + * `totalPages` 가 null 이면 이 값과 무관하게 감춰진다. + */ + showLast?: boolean; + /** + * 다음 페이지 존재 여부 (총 건수를 모르는 목록에서 사용) + * + * 총 건수를 몰라도 다음 페이지 존재 여부는 서버가 정확히 판정한다. + * 지정하면 "다음" 버튼의 활성 여부를 이 값으로 정한다. + */ + hasMorePages?: boolean; className?: string; style?: React.CSSProperties; prevText?: string; diff --git a/templates/_bundled/sirsoft-admin_basic/lang/partial/en/admin.json b/templates/_bundled/sirsoft-admin_basic/lang/partial/en/admin.json index ac1dfd61..4a3c16aa 100644 --- a/templates/_bundled/sirsoft-admin_basic/lang/partial/en/admin.json +++ b/templates/_bundled/sirsoft-admin_basic/lang/partial/en/admin.json @@ -1764,6 +1764,12 @@ "dev_dashboard": "Dev Dashboard", "sql_query_log": "SQL Query Log", "sql_query_log_desc": "Log executed SQL queries. Log file location: /storage/logs/query.log", + "pagination": "List limits", + "pagination_desc": "How far totals are counted on large lists, and the highest page number that can be requested directly. Beyond the cap the total is shown as \"N+\" and only the last-page jump is hidden — moving to the next page stays available.", + "pagination_result_cap": "Total count cap", + "pagination_result_cap_desc": "Counts exactly up to this many rows. 0 always counts everything (lists may slow down on large data).", + "pagination_max_page": "Maximum page number", + "pagination_max_page_desc": "Highest page number that can be requested directly via URL. 0 means no limit. Normal next-page navigation is unaffected.", "core_update": "Update Settings", "core_update_desc": "Configure GitHub authentication for core and extension (module/plugin/template) updates.", "core_update_github_url": "GitHub Repository URL", @@ -2303,7 +2309,8 @@ "no_value": "-" }, "pagination": { - "total": "Total {{count}} items" + "total": "Total {{count}} items", + "total_at_least": "Total {{count}}+ items" }, "empty": { "title": "No activity logs found", diff --git a/templates/_bundled/sirsoft-admin_basic/lang/partial/ko/admin.json b/templates/_bundled/sirsoft-admin_basic/lang/partial/ko/admin.json index a9b2fd4b..d58cf921 100644 --- a/templates/_bundled/sirsoft-admin_basic/lang/partial/ko/admin.json +++ b/templates/_bundled/sirsoft-admin_basic/lang/partial/ko/admin.json @@ -1768,6 +1768,12 @@ "dev_dashboard": "개발 대시보드", "sql_query_log": "SQL 쿼리 로그", "sql_query_log_desc": "실행된 SQL 쿼리를 로그에 기록합니다. 로그 파일 위치: /storage/logs/query.log", + "pagination": "목록 한계값", + "pagination_desc": "대용량 목록에서 총 건수를 세는 범위와 직접 요청 가능한 페이지 번호의 상한입니다. 상한을 넘으면 총 건수를 \"N건 이상\" 으로 표시하고 마지막 페이지 점프만 감춥니다 — 다음 페이지 이동은 그대로 열려 있습니다.", + "pagination_result_cap": "총 건수 집계 상한", + "pagination_result_cap_desc": "이 건수까지만 정확히 셉니다. 0 이면 항상 전부 셉니다(대용량에서 목록이 느려질 수 있습니다).", + "pagination_max_page": "페이지 번호 상한", + "pagination_max_page_desc": "주소로 직접 요청할 수 있는 최대 페이지 번호입니다. 0 이면 제한하지 않습니다. 정상적인 다음 페이지 이동은 이 값과 무관하게 계속 가능합니다.", "core_update": "업데이트 설정", "core_update_desc": "코어 및 확장(모듈/플러그인/템플릿) 업데이트에 사용할 GitHub 인증 정보를 설정합니다.", "core_update_github_url": "GitHub 저장소 URL", @@ -2307,7 +2313,8 @@ "no_value": "-" }, "pagination": { - "total": "총 {{count}}건" + "total": "총 {{count}}건", + "total_at_least": "총 {{count}}건 이상" }, "empty": { "title": "활동 로그가 없습니다", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/admin_activity_log_list.json b/templates/_bundled/sirsoft-admin_basic/layouts/admin_activity_log_list.json index 39a0ca38..c165a299 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/admin_activity_log_list.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/admin_activity_log_list.json @@ -160,7 +160,7 @@ "name": "Icon", "props": { "name": "refresh", - "className": "w-5 h-5" + "className": "text-xl" } } ] @@ -194,12 +194,24 @@ }, "children": [ { + "comment": "총 건수를 끝까지 센 경우", "type": "basic", "name": "Span", + "if": "{{activityLogs?.data?.pagination?.total_is_exact !== false}}", "props": { "className": "text-label" }, "text": "$t:admin.activity_log.pagination.total|count={{activityLogs?.data?.pagination?.total || 0}}" + }, + { + "comment": "총 건수가 상한에 걸려 잘린 경우 — 세지 않은 값을 정확한 것처럼 말하지 않는다", + "type": "basic", + "name": "Span", + "if": "{{activityLogs?.data?.pagination?.total_is_exact === false}}", + "props": { + "className": "text-label" + }, + "text": "$t:admin.activity_log.pagination.total_at_least|count={{activityLogs?.data?.pagination?.total || 0}}" } ] }, @@ -409,7 +421,7 @@ "name": "Button", "props": { "type": "button", - "className": "flex-center gap-2 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed", + "className": "flex-center gap-2 px-4 py-2 bg-red-600 dark:bg-red-700 text-white dark:text-white rounded-lg hover:bg-red-700 dark:hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed", "disabled": "{{_global.isDeleting}}" }, "actions": [ @@ -584,7 +596,7 @@ "name": "Button", "props": { "type": "button", - "className": "flex-center gap-2 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed", + "className": "flex-center gap-2 px-4 py-2 bg-red-600 dark:bg-red-700 text-white dark:text-white rounded-lg hover:bg-red-700 dark:hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed", "disabled": "{{_global.isDeleting}}" }, "actions": [ diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/admin_module_list.json b/templates/_bundled/sirsoft-admin_basic/layouts/admin_module_list.json index af46dd55..3c34691e 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/admin_module_list.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/admin_module_list.json @@ -643,7 +643,8 @@ "alwaysShowPagination": true, "serverSidePagination": true, "serverCurrentPage": "{{modules?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{modules?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{modules?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{modules?.data?.pagination?.has_more_pages ?? false}}", "cardClassName": "border border-gray-200 dark:border-gray-700 rounded-lg p-4 relative", "cardColumns": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/admin_plugin_list.json b/templates/_bundled/sirsoft-admin_basic/layouts/admin_plugin_list.json index 3ead2e1b..3a825316 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/admin_plugin_list.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/admin_plugin_list.json @@ -652,7 +652,8 @@ "alwaysShowPagination": true, "serverSidePagination": true, "serverCurrentPage": "{{plugins?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{plugins?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{plugins?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{plugins?.data?.pagination?.has_more_pages ?? false}}", "cardClassName": "border border-gray-200 dark:border-gray-700 rounded-lg p-4 relative", "cardColumns": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/admin_role_list.json b/templates/_bundled/sirsoft-admin_basic/layouts/admin_role_list.json index 20e3cc22..dbe6f348 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/admin_role_list.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/admin_role_list.json @@ -449,7 +449,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{roles?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{roles?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{roles?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{roles?.data?.pagination?.has_more_pages ?? false}}", "pageSize": 20, "alwaysShowPagination": true, "idField": "id", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/admin_user_list.json b/templates/_bundled/sirsoft-admin_basic/layouts/admin_user_list.json index d10884bf..0a5fbb5f 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/admin_user_list.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/admin_user_list.json @@ -981,7 +981,8 @@ "pageSize": 15, "serverSidePagination": true, "serverCurrentPage": "{{users?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{users?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{users?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{users?.data?.pagination?.has_more_pages ?? false}}", "selectable": "{{users?.data?.abilities?.can_update === true}}", "selectedIds": "{{_global.selectedIds_admin_users || []}}", "idField": "uuid", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_activity_log_list/_partial_datagrid.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_activity_log_list/_partial_datagrid.json index 58dac106..c1fa576a 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_activity_log_list/_partial_datagrid.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_activity_log_list/_partial_datagrid.json @@ -11,7 +11,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{activityLogs?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{activityLogs?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{activityLogs?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{activityLogs?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "idField": "id", @@ -290,7 +291,7 @@ "props": { "className": "text-label-subtle" }, - "text": "{{row.number}}" + "text": "{{row.number ?? '-'}}" } ] }, diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_identity_logs/_partial_datagrid.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_identity_logs/_partial_datagrid.json index 4206ed72..d20fc250 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_identity_logs/_partial_datagrid.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_identity_logs/_partial_datagrid.json @@ -11,7 +11,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{identityLogs?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{identityLogs?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{identityLogs?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{identityLogs?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "idField": "id", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_language_pack_list/_content.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_language_pack_list/_content.json index 696caa22..cea70160 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_language_pack_list/_content.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_language_pack_list/_content.json @@ -671,7 +671,8 @@ "alwaysShowPagination": true, "serverSidePagination": true, "serverCurrentPage": "{{language_packs?.data?.meta?.current_page ?? 1}}", - "serverTotalPages": "{{language_packs?.data?.meta?.last_page ?? 1}}", + "serverTotalPages": "{{language_packs?.data?.meta?.last_page ?? null}}", + "serverHasMorePages": "{{language_packs?.data?.meta?.has_more_pages ?? false}}", "cardClassName": "border border-gray-200 dark:border-gray-700 rounded-lg p-4 relative", "cardColumns": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_notification_log_list/_partial_datagrid.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_notification_log_list/_partial_datagrid.json index f87d0858..b80ae897 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_notification_log_list/_partial_datagrid.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_notification_log_list/_partial_datagrid.json @@ -11,7 +11,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{notificationLogs?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{notificationLogs?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{notificationLogs?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{notificationLogs?.data?.pagination?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "idField": "id", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_modal_history.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_modal_history.json index 875f828e..602752ec 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_modal_history.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_modal_history.json @@ -57,7 +57,7 @@ "name": "Icon", "props": { "name": "clock", - "className": "w-12 h-12 mx-auto mb-2 text-gray-300 dark:text-gray-600" + "className": "text-5xl mx-auto mb-2 text-gray-300 dark:text-gray-600" } }, { @@ -233,7 +233,7 @@ { "type": "basic", "name": "Div", - "if": "{{schedule_history?.data?.last_page > 1}}", + "if": "{{((schedule_history?.data?.last_page ?? 0) > 1) || (schedule_history?.data?.has_more_pages === true) || ((schedule_history?.data?.current_page ?? 1) > 1)}}", "props": { "className": "mt-4 flex justify-center" }, @@ -243,7 +243,8 @@ "name": "Pagination", "props": { "currentPage": "{{schedule_history?.data?.current_page ?? 1}}", - "totalPages": "{{schedule_history?.data?.last_page ?? 1}}", + "totalPages": "{{schedule_history?.data?.last_page ?? null}}", + "hasMorePages": "{{schedule_history?.data?.has_more_pages ?? false}}", "totalItems": "{{schedule_history?.data?.total ?? 0}}", "perPage": 10, "showAlways": false diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_tab_schedules.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_tab_schedules.json index 84ee4895..6367c85d 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_tab_schedules.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_schedule_list/_tab_schedules.json @@ -1641,7 +1641,8 @@ "pagination": true, "serverSidePagination": true, "serverCurrentPage": "{{schedules?.data?.current_page ?? 1}}", - "serverTotalPages": "{{schedules?.data?.last_page ?? 1}}", + "serverTotalPages": "{{schedules?.data?.last_page ?? null}}", + "serverHasMorePages": "{{schedules?.data?.has_more_pages ?? false}}", "pageSize": "{{+(query.per_page || 20)}}", "alwaysShowPagination": true, "idField": "id", diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_advanced.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_advanced.json index 794bf643..d2cbd402 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_advanced.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_advanced.json @@ -822,7 +822,7 @@ "href": "/dev", "target": "_blank", "rel": "noopener noreferrer", - "className": "px-3 py-1.5 bg-indigo-600 text-white text-xs font-medium rounded-md hover:bg-indigo-700 transition-colors whitespace-nowrap no-underline" + "className": "px-3 py-1.5 bg-indigo-600 dark:bg-indigo-500 text-white dark:text-white text-xs font-medium rounded-md hover:bg-indigo-700 dark:hover:bg-indigo-600 transition-colors whitespace-nowrap no-underline" }, "text": "$t:admin.settings.advanced.dev_dashboard" }, @@ -910,6 +910,147 @@ } ] }, + { + "id": "card_pagination_settings", + "type": "basic", + "name": "Div", + "props": { + "className": "card-stack-item" + }, + "children": [ + { + "type": "basic", + "name": "H3", + "props": { + "className": "card-title" + }, + "text": "$t:admin.settings.advanced.pagination" + }, + { + "type": "basic", + "name": "Div", + "props": { + "className": "card-description" + }, + "text": "$t:admin.settings.advanced.pagination_desc" + }, + { + "type": "basic", + "name": "Div", + "props": { + "className": "row-stack" + }, + "children": [ + { + "id": "input_pagination_result_cap", + "type": "basic", + "name": "Div", + "children": [ + { + "type": "basic", + "name": "Div", + "props": { + "className": "flex-center" + }, + "children": [ + { + "type": "basic", + "name": "Span", + "props": { + "className": "text-heading" + }, + "text": "$t:admin.settings.advanced.pagination_result_cap" + } + ] + }, + { + "type": "basic", + "name": "P", + "props": { + "className": "text-label-subtle" + }, + "text": "$t:admin.settings.advanced.pagination_result_cap_desc" + }, + { + "type": "basic", + "name": "Input", + "props": { + "type": "number", + "name": "advanced.pagination_result_cap", + "min": "{{settings?.data?._meta?.limits?.advanced_pagination_result_cap_min ?? 0}}", + "max": "{{settings?.data?._meta?.limits?.advanced_pagination_result_cap_max ?? 1000000}}", + "disabled": "{{_computed.isReadOnly}}", + "className": "input {{_local.errors?.['advanced.pagination_result_cap'] ? 'input-error' : ''}}" + } + }, + { + "type": "basic", + "name": "Span", + "if": "{{_local.errors?.['advanced.pagination_result_cap']}}", + "props": { + "className": "form-error" + }, + "text": "{{_local.errors?.['advanced.pagination_result_cap']?.[0] ?? ''}}" + } + ] + }, + { + "id": "input_pagination_max_page", + "type": "basic", + "name": "Div", + "children": [ + { + "type": "basic", + "name": "Div", + "props": { + "className": "flex-center" + }, + "children": [ + { + "type": "basic", + "name": "Span", + "props": { + "className": "text-heading" + }, + "text": "$t:admin.settings.advanced.pagination_max_page" + } + ] + }, + { + "type": "basic", + "name": "P", + "props": { + "className": "text-label-subtle" + }, + "text": "$t:admin.settings.advanced.pagination_max_page_desc" + }, + { + "type": "basic", + "name": "Input", + "props": { + "type": "number", + "name": "advanced.pagination_max_page", + "min": "{{settings?.data?._meta?.limits?.advanced_pagination_max_page_min ?? 0}}", + "max": "{{settings?.data?._meta?.limits?.advanced_pagination_max_page_max ?? 100000}}", + "disabled": "{{_computed.isReadOnly}}", + "className": "input {{_local.errors?.['advanced.pagination_max_page'] ? 'input-error' : ''}}" + } + }, + { + "type": "basic", + "name": "Span", + "if": "{{_local.errors?.['advanced.pagination_max_page']}}", + "props": { + "className": "form-error" + }, + "text": "{{_local.errors?.['advanced.pagination_max_page']?.[0] ?? ''}}" + } + ] + } + ] + } + ] + }, { "id": "card_core_update_settings", "type": "basic", @@ -1267,7 +1408,7 @@ "props": { "type": "button", "disabled": "{{_computed.isReadOnly || _local.geoipUpdating === true}}", - "className": "w-full md:w-auto px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white text-sm font-medium rounded-lg transition-colors whitespace-nowrap" + "className": "w-full md:w-auto px-4 py-2 bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 text-white dark:text-white text-sm font-medium rounded-lg transition-colors whitespace-nowrap" }, "text": "{{_local.geoipUpdating === true ? $t('admin.settings.advanced.geoip_updating') : $t('admin.settings.advanced.geoip_manual_update')}}", "actions": [ diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_messages.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_messages.json index cc94e06c..91a5d58d 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_messages.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_messages.json @@ -628,7 +628,8 @@ "name": "Pagination", "props": { "currentPage": "{{identityMessages?.data?.meta?.current_page ?? identityMessages?.data?.pagination?.current_page ?? 1}}", - "totalPages": "{{identityMessages?.data?.meta?.last_page ?? identityMessages?.data?.pagination?.last_page ?? 1}}" + "totalPages": "{{identityMessages?.data?.meta?.last_page ?? identityMessages?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{identityMessages?.data?.meta?.has_more_pages ?? identityMessages?.data?.pagination?.has_more_pages ?? false}}" }, "actions": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_policies.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_policies.json index b8186adc..e27471f3 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_policies.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_identity_policies.json @@ -1094,7 +1094,7 @@ "props": { "className": "text-tertiary" }, - "text": "$t:admin.identity.policies.pagination_summary|total={{policies?.data?.meta?.total ?? 0}}|page={{policies?.data?.meta?.current_page ?? 1}}|last={{policies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.pagination_summary|total={{policies?.data?.meta?.total ?? 0}}|page={{policies?.data?.meta?.current_page ?? 1}}|last={{policies?.data?.meta?.last_page ?? '?'}}" }, { "type": "basic", @@ -1147,14 +1147,14 @@ "props": { "className": "px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-200" }, - "text": "$t:admin.identity.policies.page_indicator|page={{policies?.data?.meta?.current_page ?? 1}}|last={{policies?.data?.meta?.last_page ?? 1}}" + "text": "$t:admin.identity.policies.page_indicator|page={{policies?.data?.meta?.current_page ?? 1}}|last={{policies?.data?.meta?.last_page ?? '?'}}" }, { "type": "basic", "name": "Button", "props": { "type": "button", - "disabled": "{{(policies?.data?.meta?.current_page ?? 1) >= (policies?.data?.meta?.last_page ?? 1)}}", + "disabled": "{{!((policies?.data?.meta?.has_more_pages === true) || ((policies?.data?.meta?.current_page ?? 1) < (policies?.data?.meta?.last_page ?? 0)))}}", "className": "px-3 py-1.5 text-xs font-medium bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed" }, "actions": [ @@ -1167,7 +1167,7 @@ "mergeQuery": true, "query": { "tab": "identity", - "page": "{{Math.min(policies?.data?.meta?.last_page ?? 1, (policies?.data?.meta?.current_page ?? 1) + 1)}}" + "page": "{{policies?.data?.meta?.last_page ? Math.min(policies.data.meta.last_page, (policies?.data?.meta?.current_page ?? 1) + 1) : ((policies?.data?.meta?.current_page ?? 1) + 1)}}" }, "transition_overlay_target": "policy_table_card", "scroll": { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_mail_templates.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_mail_templates.json index 8a39e86d..14374940 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_mail_templates.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_mail_templates.json @@ -687,8 +687,9 @@ "type": "composite", "name": "Pagination", "props": { - "currentPage": "{{mailTemplates?.data?.pagination?.current_page || 1}}", - "totalPages": "{{mailTemplates?.data?.pagination?.last_page || 1}}" + "currentPage": "{{mailTemplates?.data?.pagination?.current_page ?? 1}}", + "totalPages": "{{mailTemplates?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{mailTemplates?.data?.pagination?.has_more_pages ?? false}}" }, "actions": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_notification_definitions.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_notification_definitions.json index d41eb9e1..0b9cd425 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_notification_definitions.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_settings/_tab_notification_definitions.json @@ -705,8 +705,9 @@ "type": "composite", "name": "Pagination", "props": { - "currentPage": "{{notificationDefinitions?.data?.pagination?.current_page || 1}}", - "totalPages": "{{notificationDefinitions?.data?.pagination?.last_page || 1}}" + "currentPage": "{{notificationDefinitions?.data?.pagination?.current_page ?? 1}}", + "totalPages": "{{notificationDefinitions?.data?.pagination?.last_page ?? null}}", + "hasMorePages": "{{notificationDefinitions?.data?.pagination?.has_more_pages ?? false}}" }, "actions": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_admin.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_admin.json index cca91ad7..d1147e6d 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_admin.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_admin.json @@ -105,7 +105,8 @@ "alwaysShowPagination": true, "serverSidePagination": true, "serverCurrentPage": "{{templates?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{templates?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{templates?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{templates?.data?.pagination?.has_more_pages ?? false}}", "cardClassName": "border border-gray-200 dark:border-gray-700 rounded-lg p-4 relative", "cardColumns": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_user.json b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_user.json index ec53c661..fa5a4b4d 100644 --- a/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_user.json +++ b/templates/_bundled/sirsoft-admin_basic/layouts/partials/admin_template_list/_tab_user.json @@ -105,7 +105,8 @@ "alwaysShowPagination": true, "serverSidePagination": true, "serverCurrentPage": "{{templates?.data?.pagination?.current_page ?? 1}}", - "serverTotalPages": "{{templates?.data?.pagination?.last_page ?? 1}}", + "serverTotalPages": "{{templates?.data?.pagination?.last_page ?? null}}", + "serverHasMorePages": "{{templates?.data?.pagination?.has_more_pages ?? false}}", "cardClassName": "border border-gray-200 dark:border-gray-700 rounded-lg p-4 relative", "cardColumns": [ { diff --git a/templates/_bundled/sirsoft-admin_basic/src/components/composite/DataGrid.tsx b/templates/_bundled/sirsoft-admin_basic/src/components/composite/DataGrid.tsx index 4e3ed18f..71b8fb24 100644 --- a/templates/_bundled/sirsoft-admin_basic/src/components/composite/DataGrid.tsx +++ b/templates/_bundled/sirsoft-admin_basic/src/components/composite/DataGrid.tsx @@ -236,7 +236,20 @@ export interface DataGridProps { // 서버 사이드 페이지네이션 (API에서 페이지네이션 처리 시) serverSidePagination?: boolean; serverCurrentPage?: number; - serverTotalPages?: number; + /** + * 서버가 알려준 마지막 페이지 번호 + * + * 총 건수를 상한까지만 센 목록은 마지막 페이지를 계산할 수 없어 서버가 `null` 을 보낸다. + * 1 로 채우면 화면이 "1페이지뿐" 이라고 잘못 말하므로 null 을 그대로 흘려보낸다. + */ + serverTotalPages?: number | null; + /** + * 다음 페이지 존재 여부 (총 건수를 모르는 목록에서 사용) + * + * 총 건수를 몰라도 서버는 이 값을 정확히 판정한다. 마지막 페이지를 계산할 수 없어도 + * "다음" 이동은 이 값으로 끝까지 열어 둘 수 있다. + */ + serverHasMorePages?: boolean; onPageChange?: (page: number) => void; // 다국어 지원 텍스트 (레이아웃 JSON에서 $t: 문법으로 전달) @@ -651,6 +664,7 @@ export const DataGrid: React.FC = ({ serverSidePagination = false, serverCurrentPage, serverTotalPages, + serverHasMorePages, onPageChange, // 다국어 텍스트 prevText, @@ -926,7 +940,18 @@ export const DataGrid: React.FC = ({ // 서버 사이드 페이지네이션인 경우 서버에서 전달된 값 사용 const effectiveCurrentPage = serverSidePagination ? (serverCurrentPage || 1) : currentPage; - const effectiveTotalPages = serverSidePagination ? (serverTotalPages || 1) : Math.ceil((sortedData?.length || 0) / pageSize); + // 총 건수를 상한까지만 센 목록은 마지막 페이지가 null 로 온다. 1 로 채우면 + // 페이저가 통째로 접혀 1페이지 밖 행에 도달할 방법이 사라지므로 null 을 보존한다. + const effectiveTotalPages: number | null = serverSidePagination + ? (serverTotalPages ?? null) + : Math.ceil((sortedData?.length || 0) / pageSize); + + // 마지막 페이지를 모르더라도 "다음" 이 열려 있거나 이미 2페이지 이상이면 페이저는 필요하다. + const shouldShowPagination = + alwaysShowPagination || + (effectiveTotalPages ?? 0) > 1 || + serverHasMorePages === true || + effectiveCurrentPage > 1; // 페이지 변경 핸들러 const handlePageChange = useCallback((page: number) => { @@ -1293,11 +1318,12 @@ export const DataGrid: React.FC = ({ )} {/* 페이지네이션 */} - {pagination && (alwaysShowPagination || effectiveTotalPages > 1) && ( + {pagination && shouldShowPagination && (
= ({
{/* 페이지네이션 */} - {pagination && (alwaysShowPagination || effectiveTotalPages > 1) && ( + {pagination && shouldShowPagination && (
) => export interface PaginationProps { currentPage: number; - totalPages: number; + /** + * 마지막 페이지 번호 + * + * 총 건수가 상한을 넘겨 정확히 세지 않은 목록은 마지막 페이지를 계산할 수 없다. + * 그 경우 서버가 `null` 을 보내며, 페이지 번호 목록과 마지막 페이지 점프가 사라지고 + * 이전/다음 이동만 남는다. 0 이나 1 로 채우면 화면이 "1페이지뿐" 이라고 잘못 말하게 된다. + */ + totalPages: number | null; onPageChange: (page: number) => void; maxVisiblePages?: number; + /** + * 첫/마지막 페이지 버튼 표시 여부 (두 버튼의 기본값) + * + * 마지막 페이지만 따로 감춰야 하는 경우가 있으므로 `showLast` 로 덮어쓸 수 있다. + */ showFirstLast?: boolean; + /** + * 첫 페이지 버튼 표시 여부 (미지정 시 showFirstLast 를 따른다) + */ + showFirst?: boolean; + /** + * 마지막 페이지 버튼 표시 여부 (미지정 시 showFirstLast 를 따른다) + * + * 총 건수가 부정확한 목록에서는 마지막 페이지를 계산할 수 없으므로, + * `totalPages` 가 null 이면 이 값과 무관하게 감춰진다. + */ + showLast?: boolean; + /** + * 다음 페이지 존재 여부 (총 건수를 모르는 목록에서 사용) + * + * 총 건수를 몰라도 다음 페이지 존재 여부는 서버가 정확히 판정한다. + * 지정하면 "다음" 버튼의 활성 여부를 이 값으로 정한다. + */ + hasMorePages?: boolean; className?: string; style?: React.CSSProperties; prevText?: string; @@ -50,6 +80,9 @@ export const Pagination: React.FC = ({ onPageChange, maxVisiblePages = 5, showFirstLast = true, + showFirst, + showLast, + hasMorePages, className = '', style, prevText, @@ -57,14 +90,35 @@ export const Pagination: React.FC = ({ id, editorAttrs, }) => { + // 총 건수를 정확히 세지 못한 목록은 마지막 페이지를 계산할 수 없다. + // 이때는 페이지 번호 목록과 마지막 페이지 점프를 감추고 이전/다음 이동만 남긴다 — + // 끝까지 넘겨 보는 것은 그대로 가능하다. + const isBounded = totalPages !== null && totalPages !== undefined; + const resolvedTotalPages = isBounded ? (totalPages as number) : 0; + + // 마지막 페이지를 알면 페이지 산술이 언제나 옳으므로 그쪽을 먼저 본다. + // `hasMorePages` 를 우선하면, 응답에 `has_more_pages` 가 없어 레이아웃이 `?? false` 로 + // 채운 값이 정상 페이저의 "다음" 을 막아버린다 (실측: 1/2 페이지인데 다음 비활성). + // 서버 판정이 필요한 자리는 마지막 페이지를 모르는 목록뿐이다. + const canGoNext = isBounded + ? currentPage < resolvedTotalPages + : (hasMorePages ?? false); + + const displayFirst = (showFirst ?? showFirstLast) && isBounded; + const displayLast = (showLast ?? showFirstLast) && isBounded; // 페이지 번호 생성 알고리즘 const pageNumbers = useMemo(() => { const pages: (number | string)[] = []; - if (totalPages <= maxVisiblePages + 2) { + if (!isBounded) { + // 마지막 페이지를 모르면 번호 목록을 만들 수 없다 (현재 페이지만 별도로 표시) + return pages; + } + + if (resolvedTotalPages <= maxVisiblePages + 2) { // 페이지가 적으면 모두 표시 - for (let i = 1; i <= totalPages; i++) { + for (let i = 1; i <= resolvedTotalPages; i++) { pages.push(i); } } else { @@ -72,13 +126,13 @@ export const Pagination: React.FC = ({ const halfVisible = Math.floor(maxVisiblePages / 2); let startPage = Math.max(2, currentPage - halfVisible); - let endPage = Math.min(totalPages - 1, currentPage + halfVisible); + let endPage = Math.min(resolvedTotalPages - 1, currentPage + halfVisible); // 시작 또는 끝 근처일 때 조정 if (currentPage <= halfVisible + 1) { endPage = maxVisiblePages; - } else if (currentPage >= totalPages - halfVisible) { - startPage = totalPages - maxVisiblePages + 1; + } else if (currentPage >= resolvedTotalPages - halfVisible) { + startPage = resolvedTotalPages - maxVisiblePages + 1; } // 첫 페이지 @@ -95,21 +149,22 @@ export const Pagination: React.FC = ({ } // 끝 생략 부호 - if (endPage < totalPages - 1) { + if (endPage < resolvedTotalPages - 1) { pages.push('...'); } // 마지막 페이지 - if (totalPages > 1) { - pages.push(totalPages); + if (resolvedTotalPages > 1) { + pages.push(resolvedTotalPages); } } return pages; - }, [currentPage, totalPages, maxVisiblePages]); + }, [currentPage, resolvedTotalPages, isBounded, maxVisiblePages]); const handlePageClick = (page: number) => { - if (page < 1 || page > totalPages || page === currentPage) return; + if (page < 1 || page === currentPage) return; + if (isBounded && page > resolvedTotalPages) return; onPageChange(page); }; @@ -120,7 +175,7 @@ export const Pagination: React.FC = ({ }; const handleNext = () => { - if (currentPage < totalPages) { + if (canGoNext) { onPageChange(currentPage + 1); } }; @@ -134,7 +189,7 @@ export const Pagination: React.FC = ({ id={id} {...editorAttrs} > {/* First Button */} - {showFirstLast && ( + {displayFirst && ( - {/* Page Numbers */} + {/* Page Numbers — 마지막 페이지를 모르면 현재 페이지만 표시 */}
+ {!isBounded && ( + + {currentPage} + + )} {pageNumbers.map((page, index) => { if (page === '...') { return ( @@ -189,7 +249,7 @@ export const Pagination: React.FC = ({ {/* Next Button */} {/* Last Button */} - {showFirstLast && ( + {displayLast && ( - {/* Page Numbers */} + {/* Page Numbers — 마지막 페이지를 모르면 현재 페이지만 표시 */}
+ {!isBounded && ( + + {currentPage} + + )} {pageNumbers.map((page, index) => { if (page === '...') { return ( @@ -195,7 +255,7 @@ export const Pagination: React.FC = ({ {/* Next Button */} {/* Last Button */} - {showFirstLast && ( + {displayLast && (