tramway 3.1.2.8 → 4.0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +134 -18
  3. data/app/assets/builds/tailwind.css +2 -0
  4. data/app/assets/javascripts/tramway/tramway.js +202 -1
  5. data/app/assets/stylesheets/font-awesome.css +2337 -0
  6. data/app/assets/tailwind/application.css +29 -0
  7. data/app/components/tramway/chat_component.html.haml +1 -1
  8. data/app/components/tramway/containers/main_component.rb +1 -3
  9. data/app/components/tramway/containers/narrow_component.rb +5 -4
  10. data/app/components/tramway/form/builder.rb +1 -2
  11. data/app/components/tramway/grid/card_component.html.haml +2 -0
  12. data/app/components/tramway/grid/card_component.rb +37 -0
  13. data/app/components/tramway/grid_component.html.haml +18 -0
  14. data/app/components/tramway/grid_component.rb +76 -0
  15. data/app/components/tramway/native_text_component.rb +1 -2
  16. data/app/components/tramway/nav/item_component.rb +4 -2
  17. data/app/components/tramway/navbar_component.html.haml +131 -102
  18. data/app/components/tramway/navbar_component.rb +84 -2
  19. data/app/components/tramway/table/cell_component.rb +9 -1
  20. data/app/components/tramway/table/header_component.rb +23 -2
  21. data/app/components/tramway/table/row_component.rb +27 -10
  22. data/app/components/tramway/table_component.rb +28 -1
  23. data/app/controllers/tramway/entities_controller.rb +13 -6
  24. data/app/views/tramway/entities/_list.html.haml +2 -1
  25. data/app/views/tramway/layouts/application.html.haml +8 -6
  26. data/config/routes.rb +1 -2
  27. data/config/tailwind.config.js +57 -0
  28. data/lib/generators/tramway/install/font_awesome_helpers.rb +39 -0
  29. data/lib/generators/tramway/install/install_generator.rb +133 -66
  30. data/lib/generators/tramway/install/layout_helpers.rb +95 -0
  31. data/lib/tramway/configs/entities/page.rb +1 -0
  32. data/lib/tramway/engine.rb +12 -0
  33. data/lib/tramway/errors.rb +45 -0
  34. data/lib/tramway/forms/validations.rb +1 -2
  35. data/lib/tramway/helpers/navbar_helper.rb +3 -1
  36. data/lib/tramway/helpers/table_helper.rb +27 -0
  37. data/lib/tramway/helpers/views_helper.rb +26 -2
  38. data/lib/tramway/pg_searchable.rb +62 -0
  39. data/lib/tramway/searchable.rb +8 -26
  40. data/lib/tramway/version.rb +1 -1
  41. metadata +44 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4734bad656c9c6530213422f8e87e54ce595404ef127010b781e45af3f94967a
4
- data.tar.gz: d9b3ee1e03ccfe40912955fd490ccc88a6b127965696b9956ecf5d5ced27e727
3
+ metadata.gz: c7d9ecf679835522a59184b0ffdbc6edf5cfa66aa5f1126429828497b3115e12
4
+ data.tar.gz: efa55f1a6971b64000f41f387b6a85b7d41756b4c5240c35ce3f283f6f878f4c
5
5
  SHA512:
6
- metadata.gz: 54ade07629589d86733af1c559579cae2d1dab1d35f21616bd3e01ddcd3e964f027ce8fa71a44dede44061382b5637887151c680236b22ca2eb5abba5af82346
7
- data.tar.gz: 9c37d9d2b6e868e34c533034e3f40e4953c21120986c0d68aaadf351f21732e7ef31955a354048a2bae669e624711691e743778507cd6107de57fd66211dc04c
6
+ metadata.gz: 67ac2fd8508f6a2d172638f3ac7f808721e5d4c1aa24f13e247238196c1fc90cdc5ddc1e0f7d5aa41cf13c1e47d0fa0d118d807f073afa1372ebc83b9b0f873b
7
+ data.tar.gz: 2bd0d71dbade50601a643f2c6c3567b00440d2104db60f53fc0f1a3937c73c77fde8509a8cddea9b68c908dfe92214c036e6b14bee02919c9a1a0b2f4ef5b1be
data/README.md CHANGED
@@ -20,6 +20,7 @@ Codex instruction that points agents to the Tramway skill for Tramway-native cod
20
20
  * [Tramway Tooltip](https://github.com/Purple-Magic/tramway#tramway-tooltip)
21
21
  * [Tramway Chat](https://github.com/Purple-Magic/tramway#tramway-chat)
22
22
  * [Tramway Table Component](https://github.com/Purple-Magic/tramway#tramway-table-component)
23
+ * [Tramway Grid](https://github.com/Purple-Magic/tramway#tramway-grid)
23
24
  * [Tailwind-styled forms](https://github.com/Purple-Magic/tramway#tailwind-styled-forms)
24
25
  * [Stimulus-based inputs](https://github.com/Purple-Magic/tramway#stimulus-based-inputs)
25
26
  * [Tailwind-styled pagination](https://github.com/Purple-Magic/tramway?tab=readme-ov-file#tailwind-styled-pagination-for-kaminari)
@@ -29,14 +30,14 @@ Codex instruction that points agents to the Tramway skill for Tramway-native cod
29
30
 
30
31
  Tramway is actively verified against the following Ruby and Rails versions.
31
32
 
32
- | Ruby \ Rails | 7.1* | 7.2 | 8.0 | 8.1 |
33
- | ------------- | ---- | --- | --- | --- |
34
- | 3.2 | ✅ | ✅ | ✅ | ✅ |
35
- | 3.3 | ✅ | ✅ | ✅ | ✅ |
36
- | 3.4 | ✅ | ✅ | ✅ | ✅ |
37
- | 4.0 | ✅ | ✅ | ✅ | ✅ |
33
+ | Ruby \ Rails | 8.0 | 8.1 |
34
+ | ------------- | --- | --- |
35
+ | 3.2 | ✅ | ✅ |
36
+ | 3.3 | ✅ | ✅ |
37
+ | 3.4 | ✅ | ✅ |
38
+ | 4.0 | ✅ | ✅ |
38
39
 
39
- \* Rails 7.1 receives only residual support because it no longer receives updates from the Rails core team. See the [announcement](https://rubyonrails.org/2025/10/29/new-rails-releases-and-end-of-support-announcement) for details.
40
+ Rails 7 is no longer supported. See the [announcement](https://rubyonrails.org/2025/10/29/new-rails-releases-and-end-of-support-announcement) for details.
40
41
 
41
42
  ## Installation
42
43
  Add this line to your application's Gemfile:
@@ -218,8 +219,47 @@ Tramway.configure do |config|
218
219
  end
219
220
  ```
220
221
 
221
- When search is enabled, Tramway uses `Model.search(query)` if defined. If not, it falls back to `Model.tramway_search(query)` and logs a warning.
222
- The fallback is generic and not tailored to your data structure, so it is not intended for long-term use and may be slow or not scalable.
222
+ When search is enabled, a text input and a search icon button appear at the top of the index page. Tramway uses
223
+ `Model.search(query)` if you define it (e.g. a `pg_search_scope` or any custom scope named `search`). If not, it falls back
224
+ to `Model.tramway_search(query)`, which searches across all string/text columns of the model using the [`pg_search`](https://github.com/Casecommons/pg_search)
225
+ gem, and logs a warning. The fallback is generic and not tailored to your data structure, so it is not intended for
226
+ long-term use and may be slow or not scalable.
227
+
228
+ `pg_search` requires PostgreSQL's full-text search functions. If your application's database is not PostgreSQL, calling
229
+ the fallback raises `Tramway::Errors::UnsupportedDatabaseAdapterError` with an explanation and a suggestion to define your
230
+ own `Model.search(query)` scope instead.
231
+
232
+ `bin/rails g tramway:install` adds `gem "pg_search"` to your Gemfile automatically (like Tramway's other dependencies), so
233
+ run it (and `bundle install`) if you see the fallback raise `Tramway::Errors::MissingGemError`. That error also fires if
234
+ `pg_search` can't otherwise be loaded, and explains how to fix it or define your own `Model.search(query)` scope.
235
+
236
+ **includes**
237
+
238
+ If a decorator's `index_attributes` reads an association (e.g. `object.comments.map(&:text)`), each row triggers its own
239
+ query unless the association is eager-loaded. Set `includes` on the `:index` page entry to preload it, using the same
240
+ shape Rails' `.includes` accepts (a symbol, array, or nested hash):
241
+
242
+ *config/initializers/tramway.rb*
243
+ ```ruby
244
+ Tramway.configure do |config|
245
+ config.entities = [
246
+ {
247
+ name: :campaign,
248
+ namespace: :admin,
249
+ pages: [
250
+ {
251
+ action: :index,
252
+ includes: [:user, :comments]
253
+ }
254
+ ]
255
+ }
256
+ ]
257
+ end
258
+ ```
259
+
260
+ In this example, Tramway calls `Campaign.includes(:user, :comments)` before applying `scope`/`search`, so accessing
261
+ `campaign.user` or `campaign.comments` in the decorator's `index_attributes` does not issue an extra query per row.
262
+ Omitting `includes` produces identical behavior to before this option existed.
223
263
 
224
264
  **show page**
225
265
 
@@ -920,10 +960,54 @@ background:
920
960
  with_entities: Show Tramway CRUD index page links to navbar. Default: true
921
961
  ```
922
962
 
963
+ When you use the default vertical navbar layout, it renders as a fixed left sidebar on desktop. The icon-only
964
+ sidebar toggle collapses the whole sidebar and keeps the main content offset in sync, so the page shifts between
965
+ `md:pl-72` and `md:pl-24` as the sidebar changes width.
966
+
967
+ The collapse/expand motion is tuned per element rather than a blanket `transition-all`: the sidebar width and the
968
+ main content's `padding-left` each transition only that one property (`transition-[width]` /
969
+ `transition-[padding-left]`) over 300ms on the `ease-in-out-strong` curve (`cubic-bezier(0.77, 0, 0.175, 1)`,
970
+ registered in `config/tailwind.config.js`), since both are on-screen movement. The header and nav-item list fade
971
+ with `transition-opacity` on the `ease-out-strong` curve (`cubic-bezier(0.23, 1, 0.32, 1)`), matching that they're
972
+ entering/exiting rather than moving. The toggle chevron no longer swaps between `fa-chevron-left` and
973
+ `fa-chevron-right` — it stays a single `fa-chevron-left` icon that rotates 180° (`transition-transform duration-200
974
+ ease-in-out-strong`) to indicate state instead of snapping instantly. All of the above respect
975
+ `prefers-reduced-motion` via `motion-reduce:duration-0` on the movement/rotation transitions; the opacity fade is
976
+ left intact since it aids comprehension without moving anything.
977
+
978
+ Tramway now ships with Font Awesome loaded through the engine, and the vertical navbar collapse control uses a
979
+ single `fa-chevron-left` icon for both states (rotated when collapsed).
980
+ Tramway ships a plain `font-awesome.css` asset for Propshaft compatibility and adds Font Awesome's font
981
+ directory to the host app asset load path, so `stylesheet_link_tag "font-awesome"` resolves without extra
982
+ app-side asset configuration.
983
+ On mobile, `tramway_navbar` always renders the same top navbar regardless of `direction:`. Desktop `:horizontal`
984
+ renders the classic top navbar, and desktop `:vertical` renders the collapsible sidebar.
985
+ When `tramway_navbar` renders before `tramway_main_container`, the main container helper keeps the vertical
986
+ sidebar offset in sync automatically and removes it for horizontal pages.
987
+
988
+ The collapsed/expanded state of the vertical sidebar persists across page navigations. The Stimulus controller
989
+ saves the state to the browser's `localStorage` (key `tramway-navbar-expanded`) whenever the toggle button is
990
+ used, and reads it back on every page load, so collapsing the sidebar on one page keeps it collapsed everywhere
991
+ else until it's expanded again. This is per-browser state: it isn't synced across devices or persisted server-side,
992
+ and if `localStorage` is unavailable (e.g. private browsing with storage blocked), the sidebar simply falls back
993
+ to always starting expanded.
994
+
995
+ Since the server always renders the sidebar expanded (it has no way to know the browser's stored preference) and
996
+ the Stimulus controller only applies the collapsed classes once it connects, `tramway_navbar` also renders a small
997
+ blocking inline `<script>` alongside its markup. It reads `localStorage` synchronously, before the browser paints,
998
+ and — if the sidebar was left collapsed — sets a `data-tramway-navbar-expanded="false"` attribute on `<html>`. A
999
+ matching `@media (min-width: 768px)` CSS block (rendered alongside the navbar) keys off that attribute to apply the
1000
+ collapsed width, main-container padding, and faded header/menu immediately, before any JavaScript module has had a
1001
+ chance to load and run. This is what prevents the sidebar from flashing expanded and then snapping to collapsed on
1002
+ page load. The Stimulus controller keeps that `<html>` attribute in sync afterwards (on load and on every toggle),
1003
+ so this pre-paint styling and the controller's own classes never fight each other.
1004
+
923
1005
  **NOTE:** `tramway_navbar` method called without arguments and block of code will render only [Tramway CRUD](https://github.com/Purple-Magic/tramway#tramway-crud) links on the left.
924
1006
 
925
1007
  In case you want to hide entity links you can pass `with_entities: false`.
926
1008
 
1009
+ The default `:vertical` navbar now renders as a fixed left sidebar on desktop screens. If your page content uses a full-width layout, wrap it with `tramway_main_container class: 'md:pl-72'` so the sidebar has room.
1010
+
927
1011
  ```erb
928
1012
  <% if current_user.present? %>
929
1013
  <%= tramway_navbar title: 'WaiWai' do |nav| %>
@@ -1134,17 +1218,23 @@ implementations.
1134
1218
  <% end %>
1135
1219
  ```
1136
1220
 
1137
- `tramway_table` accepts the same optional `options` hash as `Tramway::TableComponent`. The hash is forwarded as HTML
1138
- attributes, so you can pass things like `id`, `data` attributes, or additional classes. If you do not supply your own width
1139
- utility (e.g. a class that starts with `w-`), the component automatically appends `w-full` to keep the table responsive. This
1140
- allows you to extend the default styling without losing the sensible defaults provided by the component.
1221
+ `tramway_table` accepts an optional `size:` argument plus the same `options` hash as `Tramway::TableComponent`. Supported
1222
+ sizes are `:small`, `:medium`, and `:large`. The default is `:medium`, and it renders exactly like the current table
1223
+ implementation. The `options` hash is forwarded as HTML attributes, so you can pass things like `id`, `data` attributes, or
1224
+ additional classes. The table itself carries the default dark background (`bg-zinc-950`), so you can override it directly
1225
+ with a utility like `class: '!bg-red-500'`. If you do not supply your own width utility (e.g. a class that starts with `w-`),
1226
+ the component automatically appends `w-full` to keep the table responsive.
1227
+
1228
+ `tramway_row`, `tramway_header`, and `tramway_cell` automatically follow the active table size, so a single `size:` on
1229
+ `tramway_table` controls the whole table.
1141
1230
 
1142
- Use the optional `href:` argument on `tramway_row` to turn an entire row into a link. Linked rows gain pointer and hover styles
1143
- (`cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700`) to indicate interactivity.
1231
+ Use the optional `href:` argument on `tramway_row` to turn an entire row into a link. Linked rows keep their transparent
1232
+ background and gain pointer and hover styles (`cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700`) to indicate
1233
+ interactivity.
1144
1234
 
1145
1235
  ```erb
1146
- <%= tramway_table class: 'max-w-3xl border border-gray-200', data: { controller: 'table' } do %>
1147
- <%= tramway_header', headers: ['Name', 'Email'] %>
1236
+ <%= tramway_table size: :large, class: 'max-w-3xl border border-gray-200', data: { controller: 'table' } do %>
1237
+ <%= tramway_header headers: ['Name', 'Email'] %>
1148
1238
 
1149
1239
  <%= tramway_row href: user_path(user) do %>
1150
1240
  <%= tramway_cell do %>
@@ -1171,6 +1261,32 @@ details panel. Pass `preview: false` when you want a row without the preview pan
1171
1261
  <% end %>
1172
1262
  ```
1173
1263
 
1264
+ ### Tramway Grid
1265
+
1266
+ Use `tramway_grid` when you want dashboard-style layouts with cards that span multiple rows and columns while keeping the
1267
+ existing Tramway dark zinc styling. The grid accepts `rows:` and `columns:`, and it also accepts `outline: true` when you
1268
+ want to show separators between cells. Those separators are drawn in the gap between grid cells, so they stay centered
1269
+ when cards have content. Cards use `size: [rows, columns]`, with responsive spans on desktop and stacked cards on smaller
1270
+ screens. Each grid cell is rendered as a real `20em x 20em` box, and the grid always builds the exact `rows x columns`
1271
+ matrix even when the slot is empty. Cards participate in the same grid, so `size: [1, 1]` occupies exactly one cell.
1272
+
1273
+ ```haml
1274
+ = tramway_grid rows: 8, columns: 8, outline: true do
1275
+ = tramway_card size: [1, 2] do
1276
+ .text-sm.font-medium.text-zinc-400 Revenue
1277
+ .mt-2.text-3xl.font-semibold.text-zinc-50 $128,240
1278
+
1279
+ = tramway_card size: [2, 3] do
1280
+ .text-sm.font-medium.text-zinc-400 Activity
1281
+ .mt-4.space-y-2
1282
+ .flex.items-center.justify-between
1283
+ %span.text-zinc-300 Orders
1284
+ %span.text-zinc-50 48
1285
+ .flex.items-center.justify-between
1286
+ %span.text-zinc-300 Refunds
1287
+ %span.text-zinc-50 3
1288
+ ```
1289
+
1174
1290
  `tramway_cell` also forwards any HTML options to the underlying cell wrapper. Use this when you need per-cell classes,
1175
1291
  data attributes, ids, or other attributes:
1176
1292
 
@@ -1598,7 +1714,7 @@ end
1598
1714
 
1599
1715
  ## Contributing
1600
1716
 
1601
- Install [lefthook](https://github.com/evilmartians/lefthook)
1717
+ `lefthook` is pinned in [.tool-versions](/home/kalashnikovisme/projects/tramway/.tool-versions) so `mise` can resolve the shim used by `make install`.
1602
1718
 
1603
1719
  ```
1604
1720
  make install
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @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-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight: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}}}@layer theme{:root,:host{--font-sans:-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-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--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-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-400:oklch(79.2% .209 151.711);--color-green-600:oklch(62.7% .194 149.214);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-teal-100:oklch(95.3% .051 180.801);--color-teal-200:oklch(91% .096 180.426);--color-teal-700:oklch(51.1% .096 186.391);--color-teal-800:oklch(43.7% .078 188.216);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--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-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-800:oklch(39.8% .195 277.366);--color-indigo-900:oklch(35.9% .144 278.697);--color-violet-400:oklch(70.2% .183 293.541);--color-violet-800:oklch(43.2% .232 292.759);--color-violet-900:oklch(38% .189 293.745);--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-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-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-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-3xl:48rem;--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:calc(1.5 / 1);--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:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--leading-tight:1.25;--leading-snug:1.375;--radius-sm:.25rem;--radius-md:.375rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--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;-webkit-text-decoration: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}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-4{top:calc(var(--spacing) * 4)}.right-0{right:0}.right-4{right:calc(var(--spacing) * 4)}.right-6{right:calc(var(--spacing) * 6)}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.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-1{margin:var(--spacing)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-2{margin-block:calc(var(--spacing) * 2)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.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-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mt-auto{margin-top:auto}.mb-0{margin-bottom:0}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.box-border{box-sizing:border-box}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-1\/2{height:50%}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-15{height:calc(var(--spacing) * 15)}.h-dvh{height:100dvh}.h-fit{height:fit-content}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-2{min-height:calc(var(--spacing) * 2)}.min-h-4{min-height:calc(var(--spacing) * 4)}.min-h-6{min-height:calc(var(--spacing) * 6)}.min-h-8{min-height:calc(var(--spacing) * 8)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-12{min-height:calc(var(--spacing) * 12)}.min-h-15{min-height:calc(var(--spacing) * 15)}.min-h-40{min-height:calc(var(--spacing) * 40)}.min-h-60{min-height:calc(var(--spacing) * 60)}.min-h-dvh{min-height:100dvh}.w-3{width:calc(var(--spacing) * 3)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-72{width:calc(var(--spacing) * 72)}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.w-px{width:1px}.w-screen{width:100vw}.max-w-3xl{max-width:var(--container-3xl)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:0}.min-w-2{min-width:calc(var(--spacing) * 2)}.min-w-4{min-width:calc(var(--spacing) * 4)}.min-w-6{min-width:calc(var(--spacing) * 6)}.min-w-40{min-width:calc(var(--spacing) * 40)}.min-w-96{min-width:calc(var(--spacing) * 96)}.flex-1{flex:1}.flex-auto{flex:auto}.flex-initial{flex:0 auto}.flex-none{flex:none}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);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)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-none{appearance:none}.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))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-nowrap{flex-wrap:nowrap}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}: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)))}.gap-x-1{column-gap:var(--spacing)}: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-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)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.rounded-tl-md{border-top-left-radius:var(--radius-md)}.rounded-tr-md{border-top-right-radius:var(--radius-md)}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.rounded-b-xl{border-bottom-right-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.rounded-bl-2xl{border-bottom-left-radius:var(--radius-2xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dotted{--tw-border-style:dotted;border-style:dotted}.border-blue-800{border-color:var(--color-blue-800)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.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-green-800{border-color:var(--color-green-800)}.border-indigo-800{border-color:var(--color-indigo-800)}.border-orange-800{border-color:var(--color-orange-800)}.border-red-600{border-color:var(--color-red-600)}.border-red-800{border-color:var(--color-red-800)}.border-teal-200{border-color:var(--color-teal-200)}.border-teal-700{border-color:var(--color-teal-700)}.border-transparent{border-color:#0000}.border-violet-800{border-color:var(--color-violet-800)}.border-white{border-color:var(--color-white)}.border-yellow-800{border-color:var(--color-yellow-800)}.border-zinc-50{border-color:var(--color-zinc-50)}.border-zinc-100\/60{border-color:#f4f4f599}@supports (color:color-mix(in lab, red, red)){.border-zinc-100\/60{border-color:color-mix(in oklab, var(--color-zinc-100) 60%, transparent)}}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-700\/50{border-color:#3f3f4680}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/50{border-color:color-mix(in oklab, var(--color-zinc-700) 50%, transparent)}}.border-zinc-800{border-color:var(--color-zinc-800)}.\!bg-red-500{background-color:var(--color-red-500)!important}.\!bg-zinc-950{background-color:var(--color-zinc-950)!important}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-blue-900\/30{background-color:#1c398e4d}@supports (color:color-mix(in lab, red, red)){.bg-blue-900\/30{background-color:color-mix(in oklab, var(--color-blue-900) 30%, transparent)}}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-800\/60{background-color:#1e293999}@supports (color:color-mix(in lab, red, red)){.bg-gray-800\/60{background-color:color-mix(in oklab, var(--color-gray-800) 60%, transparent)}}.bg-gray-900{background-color:var(--color-gray-900)}.bg-gray-900\/30{background-color:#1018284d}@supports (color:color-mix(in lab, red, red)){.bg-gray-900\/30{background-color:color-mix(in oklab, var(--color-gray-900) 30%, transparent)}}.bg-green-600{background-color:var(--color-green-600)}.bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab, red, red)){.bg-green-900\/30{background-color:color-mix(in oklab, var(--color-green-900) 30%, transparent)}}.bg-indigo-900\/30{background-color:#312c854d}@supports (color:color-mix(in lab, red, red)){.bg-indigo-900\/30{background-color:color-mix(in oklab, var(--color-indigo-900) 30%, transparent)}}.bg-orange-400{background-color:var(--color-orange-400)}.bg-orange-900\/30{background-color:#7e2a0c4d}@supports (color:color-mix(in lab, red, red)){.bg-orange-900\/30{background-color:color-mix(in oklab, var(--color-orange-900) 30%, transparent)}}.bg-red-100{background-color:var(--color-red-100)}.bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab, red, red)){.bg-red-900\/30{background-color:color-mix(in oklab, var(--color-red-900) 30%, transparent)}}.bg-teal-100{background-color:var(--color-teal-100)}.bg-transparent{background-color:#0000}.bg-violet-900\/30{background-color:#4d179a4d}@supports (color:color-mix(in lab, red, red)){.bg-violet-900\/30{background-color:color-mix(in oklab, var(--color-violet-900) 30%, transparent)}}.bg-white{background-color:var(--color-white)}.bg-yellow-900\/30{background-color:#733e0a4d}@supports (color:color-mix(in lab, red, red)){.bg-yellow-900\/30{background-color:color-mix(in oklab, var(--color-yellow-900) 30%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-900\/30{background-color:#18181b4d}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/30{background-color:color-mix(in oklab, var(--color-zinc-900) 30%, transparent)}}.bg-zinc-900\/80{background-color:#18181bcc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/80{background-color:color-mix(in oklab, var(--color-zinc-900) 80%, transparent)}}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-zinc-950\/80{background-color:#09090bcc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-950\/80{background-color:color-mix(in oklab, var(--color-zinc-950) 80%, transparent)}}.bg-zinc-950\/95{background-color:#09090bf2}@supports (color:color-mix(in lab, red, red)){.bg-zinc-950\/95{background-color:color-mix(in oklab, var(--color-zinc-950) 95%, transparent)}}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.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)}.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-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pt-16{padding-top:calc(var(--spacing) * 16)}.pb-1{padding-bottom:var(--spacing)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-6{padding-left:calc(var(--spacing) * 6)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.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-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))}.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-none{--tw-leading:1;line-height:1}.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);font-weight:var(--font-weight-bold)}.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)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.\!text-zinc-50{color:var(--color-zinc-50)!important}.text-blue-100\/90{color:#dbeafee6}@supports (color:color-mix(in lab, red, red)){.text-blue-100\/90{color:color-mix(in oklab, var(--color-blue-100) 90%, transparent)}}.text-blue-400{color:var(--color-blue-400)}.text-blue-800{color:var(--color-blue-800)}.text-current{color:currentColor}.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-green-400{color:var(--color-green-400)}.text-green-800{color:var(--color-green-800)}.text-indigo-400{color:var(--color-indigo-400)}.text-indigo-800{color:var(--color-indigo-800)}.text-orange-400{color:var(--color-orange-400)}.text-orange-800{color:var(--color-orange-800)}.text-red-200{color:var(--color-red-200)}.text-red-400{color:var(--color-red-400)}.text-red-800{color:var(--color-red-800)}.text-teal-800{color:var(--color-teal-800)}.text-violet-400{color:var(--color-violet-400)}.text-violet-800{color:var(--color-violet-800)}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-800{color:var(--color-yellow-800)}.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-800{color:var(--color-zinc-800)}.text-zinc-950{color:var(--color-zinc-950)}.capitalize{text-transform:capitalize}.italic{font-style:italic}.underline{text-decoration-line:underline}.placeholder-gray-400::placeholder{color:var(--color-gray-400)}.placeholder-gray-500::placeholder{color:var(--color-gray-500)}.opacity-0{opacity:0}.opacity-100{opacity:1}.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-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-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)}.ring,.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-gray-200{--tw-ring-color:var(--color-gray-200)}.ring-gray-700{--tw-ring-color:var(--color-gray-700)}.ring-zinc-800{--tw-ring-color:var(--color-zinc-800)}.ring-offset-zinc-950{--tw-ring-offset-color:var(--color-zinc-950)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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,)}.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-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))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}@media (hover:hover){.group-hover\:visible:is(:where(.group):hover *){visibility:visible}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.peer-hover\:visible:is(:where(.peer):hover~*){visibility:visible}.peer-hover\:opacity-100:is(:where(.peer):hover~*){opacity:1}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-70:is(:where(.peer):disabled~*){opacity:.7}.marker\:hidden ::marker{display:none}.marker\:hidden::marker{display:none}.marker\:hidden ::-webkit-details-marker{display:none}.marker\:hidden::-webkit-details-marker{display:none}.placeholder\:text-gray-400::placeholder{color:var(--color-gray-400)}.placeholder\:text-zinc-500::placeholder{color:var(--color-zinc-500)}.first\:block:first-child{display:block}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.open\:block:is([open],:popover-open,:open){display:block}@media (hover:hover){.hover\:border-gray-300:hover{border-color:var(--color-gray-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-900:hover{background-color:var(--color-blue-900)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.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-400:hover{background-color:var(--color-green-400)}.hover\:bg-green-900:hover{background-color:var(--color-green-900)}.hover\:bg-indigo-900:hover{background-color:var(--color-indigo-900)}.hover\:bg-orange-200:hover{background-color:var(--color-orange-200)}.hover\:bg-orange-900:hover{background-color:var(--color-orange-900)}.hover\:bg-red-300:hover{background-color:var(--color-red-300)}.hover\:bg-red-900:hover{background-color:var(--color-red-900)}.hover\:bg-violet-900:hover{background-color:var(--color-violet-900)}.hover\:bg-yellow-900:hover{background-color:var(--color-yellow-900)}.hover\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:bg-zinc-800\/80:hover{background-color:#27272acc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-zinc-800\/80:hover{background-color:color-mix(in oklab, var(--color-zinc-800) 80%, transparent)}}.hover\:bg-zinc-900:hover{background-color:var(--color-zinc-900)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-zinc-50:hover{color:var(--color-zinc-50)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-blue-400:focus{border-color:var(--color-blue-400)}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:border-gray-300:focus{border-color:var(--color-gray-300)}.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-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-blue-200:focus{--tw-ring-color:var(--color-blue-200)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-blue-500\/30:focus{--tw-ring-color:#3080ff4d}@supports (color:color-mix(in lab, red, red)){.focus\:ring-blue-500\/30:focus{--tw-ring-color:color-mix(in oklab, var(--color-blue-500) 30%, transparent)}}.focus\:ring-gray-300:focus{--tw-ring-color:var(--color-gray-300)}.focus\:ring-gray-600:focus{--tw-ring-color:var(--color-gray-600)}.focus\:ring-zinc-400:focus{--tw-ring-color:var(--color-zinc-400)}.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-zinc-950:focus{--tw-ring-offset-color:var(--color-zinc-950)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-0:focus-visible{--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-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-zinc-300:focus-visible{--tw-ring-color:var(--color-zinc-300)}.focus-visible\:ring-zinc-400:focus-visible{--tw-ring-color:var(--color-zinc-400)}.focus-visible\:ring-offset-0:focus-visible{--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-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\:ring-offset-zinc-950:focus-visible{--tw-ring-offset-color:var(--color-zinc-950)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{background-color:var(--color-gray-100)}.disabled\:bg-gray-200:disabled{background-color:var(--color-gray-200)}.disabled\:text-gray-400:disabled{color:var(--color-gray-400)}.disabled\:text-gray-500:disabled{color:var(--color-gray-500)}.disabled\:opacity-50:disabled{opacity:.5}.data-\[state\=checked\]\:border-zinc-50[data-state=checked]{border-color:var(--color-zinc-50)}.data-\[state\=checked\]\:bg-zinc-50[data-state=checked]{background-color:var(--color-zinc-50)}.data-\[state\=checked\]\:text-zinc-950[data-state=checked]{color:var(--color-zinc-950)}@supports ((-webkit-backdrop-filter:var(--tw)) or (backdrop-filter:var(--tw))){.supports-\[backdrop-filter\]\:bg-zinc-950\/80{background-color:#09090bcc}@supports (color:color-mix(in lab, red, red)){.supports-\[backdrop-filter\]\:bg-zinc-950\/80{background-color:color-mix(in oklab, var(--color-zinc-950) 80%, transparent)}}}@media (min-width:40rem){.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:px-3{padding-inline:calc(var(--spacing) * 3)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:py-2{padding-block:calc(var(--spacing) * 2)}.sm\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:48rem){.md\:fixed{position:fixed}.md\:top-0{top:0}.md\:left-0{left:0}.md\:left-1\/2{left:50%}.md\:left-auto{left:auto}.md\:z-40{z-index:40}.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-dvh{height:100dvh}.md\:w-24{width:calc(var(--spacing) * 24)}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:translate-x-0{--tw-translate-x:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}.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-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:grid-cols-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:flex-col{flex-direction:column}.md\:justify-between{justify-content:space-between}.md\:gap-1{gap:var(--spacing)}: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)))}.md\:overflow-hidden{overflow:hidden}.md\:rounded-full{border-radius:3.40282e38px}.md\:rounded-xl{border-radius:var(--radius-xl)}.md\:border{border-style:var(--tw-border-style);border-width:1px}.md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.md\:border-zinc-800{border-color:var(--color-zinc-800)}.md\:bg-zinc-950{background-color:var(--color-zinc-950)}.md\:p-4{padding:calc(var(--spacing) * 4)}.md\:p-6{padding:calc(var(--spacing) * 6)}.md\:px-4{padding-inline:calc(var(--spacing) * 4)}.md\:py-3{padding-block:calc(var(--spacing) * 3)}.md\:pt-2{padding-top:calc(var(--spacing) * 2)}.md\:pr-72{padding-right:calc(var(--spacing) * 72)}.md\:pl-24{padding-left:calc(var(--spacing) * 24)}.md\:pl-72{padding-left:calc(var(--spacing) * 72)}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\: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)}.md\: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,)}.md\: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))}.md\:duration-300{--tw-duration:.3s;transition-duration:.3s}.md\:ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}}@media (min-width:64rem){.lg\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}}.rtl\:text-right:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (prefers-color-scheme:dark){@media (hover:hover){.dark\:hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}}}.tramway-scrollbar{scrollbar-width:thin;scrollbar-color:#52525be6 #09090beb}.tramway-scrollbar::-webkit-scrollbar{width:.5rem}.tramway-scrollbar::-webkit-scrollbar-track{background:#09090beb}.tramway-scrollbar::-webkit-scrollbar-thumb{background:#52525be6;border-radius:9999px}.tramway-scrollbar::-webkit-scrollbar-thumb:hover{background:#71717af2}.tramway-scrollbar::-webkit-scrollbar-corner{background:0 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-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-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{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:"<percentage>";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:"<percentage>";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:"<length>";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:"<percentage>";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}@keyframes spin{to{transform:rotate(360deg)}}
@@ -455,4 +455,205 @@ class Tooltip extends Controller {
455
455
  }
456
456
  }
457
457
 
458
- export { TramwaySelect, TableRowPreview, UiCheckbox, Tooltip }
458
+ class Navbar extends Controller {
459
+ connect() {
460
+ this.root = document.documentElement
461
+ this.desktopBreakpoint = 768
462
+ this.menuHiddenClass = 'hidden'
463
+ this.menuOffscreenClass = '-translate-x-full'
464
+ this.rootLockClass = 'overflow-hidden'
465
+ this.desktopNavbarExpandedWidthClass = 'md:w-72'
466
+ this.desktopNavbarCollapsedWidthClass = 'md:w-24'
467
+ this.desktopMainExpandedPaddingClass = 'md:pl-72'
468
+ this.desktopMainCollapsedPaddingClass = 'md:pl-24'
469
+ this.hiddenInteractionClasses = ['opacity-0', 'pointer-events-none']
470
+ this.expandedStorageKey = 'tramway-navbar-expanded'
471
+
472
+ this.desktopNavbar = this.element
473
+ this.desktopHeader = document.getElementById('desktop-navbar-header')
474
+ this.desktopMenu = document.getElementById('desktop-navbar-content')
475
+ this.desktopMainContainer = document.getElementById('tramway-main-container')
476
+ this.desktopToggleButton = document.getElementById('desktop-navbar-toggle-button')
477
+ this.desktopToggleIcon = document.getElementById('desktop-navbar-toggle-icon')
478
+ this.desktopToggleWrapper = document.getElementById('desktop-navbar-toggle-wrapper')
479
+ this.mobileButton = document.getElementById('mobile-menu-button')
480
+ this.mobileCloseButton = document.getElementById('mobile-menu-close-button')
481
+ this.mobileMenu = document.getElementById('mobile-menu')
482
+
483
+ this.handleMobileToggle = this.toggleMobileMenu.bind(this)
484
+ this.handleMobileClose = this.closeMobileMenu.bind(this)
485
+ this.handleBeforeCache = this.handleBeforeCache.bind(this)
486
+ this.handleResize = this.handleResize.bind(this)
487
+ this.handleDesktopToggle = this.toggleDesktopExpanded.bind(this)
488
+
489
+ this.bindMobileControls()
490
+ this.bindDesktopControls()
491
+ document.addEventListener('turbo:before-cache', this.handleBeforeCache)
492
+ window.addEventListener('resize', this.handleResize)
493
+
494
+ this.syncDesktopExpandedState()
495
+ }
496
+
497
+ readStoredExpanded() {
498
+ try {
499
+ return window.localStorage.getItem(this.expandedStorageKey)
500
+ } catch {
501
+ return null
502
+ }
503
+ }
504
+
505
+ writeStoredExpanded(expanded) {
506
+ try {
507
+ window.localStorage.setItem(this.expandedStorageKey, expanded ? 'true' : 'false')
508
+ } catch {
509
+ /* localStorage unavailable (e.g. private mode) — state simply won't persist */
510
+ }
511
+ }
512
+
513
+ disconnect() {
514
+ document.removeEventListener('turbo:before-cache', this.handleBeforeCache)
515
+ window.removeEventListener('resize', this.handleResize)
516
+ }
517
+
518
+ bindMobileControls() {
519
+ if (this.mobileButton) {
520
+ this.mobileButton.addEventListener('click', this.handleMobileToggle)
521
+ }
522
+
523
+ if (this.mobileCloseButton) {
524
+ this.mobileCloseButton.addEventListener('click', this.handleMobileClose)
525
+ }
526
+
527
+ if (this.mobileMenu) {
528
+ this.mobileMenu.querySelectorAll('a').forEach((link) => {
529
+ link.addEventListener('click', () => this.closeMobileMenu())
530
+ })
531
+ }
532
+ }
533
+
534
+ bindDesktopControls() {
535
+ if (this.isVertical() && this.desktopToggleButton) {
536
+ this.desktopToggleButton.addEventListener('click', this.handleDesktopToggle)
537
+ }
538
+ }
539
+
540
+ handleBeforeCache() {
541
+ this.closeMobileMenu({ hideImmediately: true })
542
+ }
543
+
544
+ handleResize() {
545
+ if (window.innerWidth >= 640) {
546
+ this.closeMobileMenu({ hideImmediately: true })
547
+ }
548
+
549
+ if (this.isDesktopViewport()) {
550
+ this.syncDesktopExpandedState()
551
+ }
552
+ }
553
+
554
+ toggleMobileMenu() {
555
+ if (!this.mobileMenu) return
556
+
557
+ if (this.mobileMenu.classList.contains(this.menuHiddenClass)) {
558
+ this.openMobileMenu()
559
+ return
560
+ }
561
+
562
+ this.closeMobileMenu()
563
+ }
564
+
565
+ openMobileMenu() {
566
+ if (!this.mobileMenu) return
567
+
568
+ this.mobileMenu.classList.remove(this.menuHiddenClass)
569
+ window.requestAnimationFrame(() => {
570
+ this.mobileMenu.classList.remove(this.menuOffscreenClass)
571
+ this.root.classList.add(this.rootLockClass)
572
+ })
573
+ }
574
+
575
+ closeMobileMenu({ hideImmediately = false } = {}) {
576
+ if (!this.mobileMenu) {
577
+ this.root.classList.remove(this.rootLockClass)
578
+ return
579
+ }
580
+
581
+ this.mobileMenu.classList.add(this.menuOffscreenClass)
582
+ this.root.classList.remove(this.rootLockClass)
583
+
584
+ if (hideImmediately) {
585
+ this.mobileMenu.classList.add(this.menuHiddenClass)
586
+ return
587
+ }
588
+
589
+ this.mobileMenu.addEventListener(
590
+ 'transitionend',
591
+ () => this.mobileMenu.classList.add(this.menuHiddenClass),
592
+ { once: true }
593
+ )
594
+ }
595
+
596
+ toggleDesktopExpanded() {
597
+ this.setDesktopExpanded(this.desktopNavbar.dataset.expanded === 'false', { persist: true })
598
+ }
599
+
600
+ syncDesktopExpandedState() {
601
+ const storedExpanded = this.readStoredExpanded()
602
+ const expanded = storedExpanded === null ? this.desktopNavbar.dataset.expanded !== 'false' : storedExpanded !== 'false'
603
+
604
+ this.setDesktopExpanded(expanded)
605
+ }
606
+
607
+ setDesktopExpanded(expanded, { persist = false } = {}) {
608
+ if (!this.isVertical() || !this.desktopNavbar || !this.desktopHeader || !this.desktopMenu || !this.desktopMainContainer || !this.desktopToggleButton || !this.desktopToggleWrapper) {
609
+ return
610
+ }
611
+
612
+ if (persist) {
613
+ this.writeStoredExpanded(expanded)
614
+ }
615
+
616
+ if (expanded) {
617
+ document.documentElement.removeAttribute('data-tramway-navbar-expanded')
618
+ } else {
619
+ document.documentElement.setAttribute('data-tramway-navbar-expanded', 'false')
620
+ }
621
+
622
+ this.desktopNavbar.classList.toggle(this.desktopNavbarExpandedWidthClass, expanded)
623
+ this.desktopNavbar.classList.toggle(this.desktopNavbarCollapsedWidthClass, !expanded)
624
+ this.desktopNavbar.dataset.expanded = expanded ? 'true' : 'false'
625
+
626
+ this.desktopMainContainer.classList.toggle(this.desktopMainExpandedPaddingClass, expanded)
627
+ this.desktopMainContainer.classList.toggle(this.desktopMainCollapsedPaddingClass, !expanded)
628
+
629
+ this.hiddenInteractionClasses.forEach((className) => {
630
+ this.desktopHeader.classList.toggle(className, !expanded)
631
+ this.desktopMenu.classList.toggle(className, !expanded)
632
+ })
633
+
634
+ this.desktopHeader.toggleAttribute('inert', !expanded)
635
+ this.desktopMenu.toggleAttribute('inert', !expanded)
636
+ this.desktopHeader.setAttribute('aria-hidden', expanded ? 'false' : 'true')
637
+ this.desktopMenu.setAttribute('aria-hidden', expanded ? 'false' : 'true')
638
+
639
+ this.desktopToggleWrapper.classList.toggle('justify-end', expanded)
640
+ this.desktopToggleWrapper.classList.toggle('justify-center', !expanded)
641
+ if (this.desktopToggleIcon) {
642
+ this.desktopToggleIcon.classList.toggle('rotate-180', !expanded)
643
+ }
644
+
645
+ this.desktopToggleButton.setAttribute('aria-label', expanded ? 'Collapse sidebar' : 'Expand sidebar')
646
+
647
+ this.desktopToggleButton.setAttribute('aria-expanded', expanded ? 'true' : 'false')
648
+ }
649
+
650
+ isVertical() {
651
+ return this.element.dataset.direction === 'vertical'
652
+ }
653
+
654
+ isDesktopViewport() {
655
+ return this.isVertical() && window.innerWidth >= this.desktopBreakpoint
656
+ }
657
+ }
658
+
659
+ export { TramwaySelect, TableRowPreview, UiCheckbox, Tooltip, Navbar }