diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d311a44260..324fff8b6d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,14 +19,17 @@ This serves two purposes: ### Added - Added a configuration option to disable the footer scroll-to-top button independently of the footer in https://github.com/hydephp/develop/pull/2459 - Added Blade Blocks for rendering Blade and Blade components from fenced code blocks in Markdown pages. They are controlled by the existing `markdown.enable_blade` option. ([#2504](https://github.com/hydephp/develop/pull/2504)) -- Added built-in `terminal` fenced code blocks with command prompt styling and optional Symfony Console formatting using the `terminal xml` info string. ([#2188](https://github.com/hydephp/develop/issues/2188), [#2485](https://github.com/hydephp/develop/issues/2485)) +- Added built-in `terminal` fenced code blocks with command prompt styling, an optional window title using the `title="…"` modifier, and optional Symfony Console formatting using the `terminal xml` info string. ([#2188](https://github.com/hydephp/develop/issues/2188), [#2485](https://github.com/hydephp/develop/issues/2485)) - Added support for lazy `InMemoryPage` contents closures. The current page is passed as the first argument whenever the contents are requested. +- Added a `title="…"` modifier to set a code block's filepath label on the fence, as an alternative to the `// filepath:` comment. The language is optional when using it, and the modifier takes precedence if you use both. ### Changed - Blade in Markdown is now enabled by default. The `markdown.enable_blade` option controls both `[Blade]:` directives and executable Blade Blocks; set it to `false` when compiling untrusted or unreviewed Markdown. - Raw HTML in Markdown is now enabled by default. Set `markdown.allow_html` to `false` when compiling untrusted or unreviewed Markdown to strip potentially unsafe HTML tags. - `InMemoryPage` now requires callers to select either `contents` or `view`; configuring both throws an `InvalidArgumentException` instead of silently giving contents precedence. - `InMemoryPage` now treats an empty string as an omitted `view`, matching the existing compile-time behavior and allowing literal contents to be used with an empty view value. +- Fenced code blocks are now rendered through the publishable `components/markdown/code-block.blade.php` view, which receives the block's finished markup as `$contents`, along with `$language` and `$label`. Syntax highlighting is unchanged, as the view wraps the highlighted code rather than producing it. The generated markup around the code has changed accordingly, with `hyde-code-block` and `hyde-code-block-label` as stable hooks for your own CSS. +- Breaking: Renamed the filepath label view from `components/filepath-label.blade.php` to `components/markdown/code-block-label.blade.php`. A published copy of the old view is no longer used, so port any customizations over to the new one. ### Deprecated - for changes that will be removed in upcoming releases. @@ -36,6 +39,8 @@ This serves two purposes: - Removed the `InMemoryPage` instance macro API. Use a contents closure for dynamic output, or extend `InMemoryPage` to add custom methods and behavior. ### Fixed +- Filepath labels are now only read from the first line of a fenced code block, as documented. A matching comment anywhere else in the document, including further down a code block, was previously turned into a label too. +- Fixed the closing delimiter of a `/* filepath: … */` or `` comment ending up in the rendered label. - Improved documentation page detection in MarkdownService so it works for child classes in https://github.com/hydephp/develop/pull/2332 - Fixed bug causing build manifest to not generate when a site has dynamic pages in https://github.com/hydephp/develop/pull/2450 - Fixed bug causing errors in the build manifest task not showing in console in https://github.com/hydephp/develop/pull/2451 diff --git a/HYDEPHP_V3_PLANNING.md b/HYDEPHP_V3_PLANNING.md index d01d85878c5..5f6095326ff 100644 --- a/HYDEPHP_V3_PLANNING.md +++ b/HYDEPHP_V3_PLANNING.md @@ -28,6 +28,8 @@ Having this document in code lets us know the devlopment state at any given poin ### Feature Changes +- Fenced code blocks are now rendered through a publishable Blade view, `components/markdown/code-block.blade.php`, in the same way terminal blocks are. The view receives the rendered code block markup as `$contents`, along with `$language` and `$label`, and decides what goes around it, so changing what surrounds a code block is a view change instead of a framework change. Highlighting itself is unaffected: the fence stays in the syntax tree as the wrapper's child, and is rendered by whichever renderer the environment already had for it, be it Torchlight, a third-party extension, or CommonMark's own. +- Code block filepath labels can now also be set with a `title="…"` modifier on the fence, using the same attribute syntax as terminal block titles, such as ` ```php title="app/Model.php" `. The language is optional, so ` ``` title=".env" ` labels a block that declares none. The existing `// filepath:` comment syntax is unchanged and continues to work. When a block uses both, the modifier wins, and the comment is stripped from the code either way. Labels are still controlled by the `markdown.features.codeblock_filepaths` option, which now turns off both syntaxes. - Blade in Markdown is now enabled by default. The `markdown.enable_blade` option controls both `[Blade]:` directives and executable Blade Blocks. Hyde sites generally treat project content as trusted and reviewed; sites that compile untrusted or unreviewed Markdown can disable both forms with this option. - Raw HTML in Markdown is now enabled by default. Hyde sites generally treat project content as trusted and reviewed; sites that compile untrusted or unreviewed Markdown can set `markdown.allow_html` to `false` to strip potentially unsafe HTML tags. - `InMemoryPage` contents now accept lazy closures in addition to literal strings. Closures are invoked each time contents are requested with the current page as their first argument, without being rebound. @@ -37,6 +39,11 @@ Having this document in code lets us know the devlopment state at any given poin - The `Redirect` page class constructor now accepts an optional `$matter` parameter, used by the framework to hide the generated documentation root redirect from navigation menus. Existing usages are unaffected. +- Removed `Hyde\Markdown\Processing\CodeblockFilepathProcessor`, along with the `` marker comments it passed between its own pre- and post-processing steps. Both were internal implementation details: the processor list is hardcoded in an internal trait, so there was no supported way to register the class, and the markers only ever existed part-way through a single conversion. Neither is documented in the changelog or upgrade guide for that reason. Filepath labels are now resolved on the syntax tree by `PrepareCodeBlocks`. +- Changed the generated HTML for fenced code blocks, which now comes from the Blade view. Site output is not part of the backward compatibility promise, so this is noted for awareness rather than as a breaking change. The `hyde-code-block` and `hyde-code-block-label` classes are stable hooks for projects styling code blocks from their own CSS. +- Filepath comments are now only recognized on the first line of a fenced code block, which is what the documentation always described. The old pre-processor matched the comment pattern on any line of the document, including inside blocks it did not belong to, because it ran before the Markdown was parsed. +- The closing delimiter of a `/* filepath: … */` or `` comment no longer ends up in the label. Only `-->` was being trimmed, and the tests covering both syntaxes used unterminated comments, so neither caught it. Only the delimiter closing the comment that was opened is taken off, so one written inside the label survives. + - Removed the legacy `checkForDeprecatedRunMixCommandUsage` check and the placeholder `--run-dev`/`--run-prod` options from the `build` command, which were kept in v2 only to surface a helpful error message. ([#2461](https://github.com/hydephp/develop/pull/2461)) - Removed the deprecated `hyde.server.dashboard` boolean config check from `DashboardController::enabled()`, which was kept in v2 for backwards compatibility but had since then been replaced with `hyde.server.dashboard.enabled`. ([#2461](https://github.com/hydephp/develop/pull/2462)) - Upgraded the bundled `vite` dependency from v7 to v8, and widened the `hyde-vite-plugin`'s `vite` peer dependency range from `>=6.3.5 <8.0.0` to `>=6.3.5 <9.0.0` so downstream projects can adopt Vite 8 without waiting for a new plugin major. The plugin's build config now targets Vite 8's Rolldown-based bundler (`rolldownOptions` instead of `rollupOptions`). ([#2414](https://github.com/hydephp/develop/pull/2414)) @@ -47,6 +54,7 @@ Having this document in code lets us know the devlopment state at any given poin - Removed the `InMemoryPage` instance macro API. Dynamic contents should now be supplied with a closure, while custom methods and other behavior belong in an `InMemoryPage` subclass. - Removed `InMemoryPage` content-source precedence. Calls that previously supplied both `contents` and `view` must retain only the intended source; positional view calls that used an empty-string contents placeholder must use `null` instead. +- Renamed the filepath label view from `components/filepath-label.blade.php` to `components/markdown/code-block-label.blade.php`. **A published copy of the old view is ignored after upgrading**, and the site renders with the shipped label until the customizations are ported over. The rename is what makes that the outcome: published views take precedence over the framework's own, and a copy written for the label's old position inside `` places it outside the code block entirely, so keeping the name would have kept those copies in use and broken the layout of every site that had customized one. - Removed the `rebuild` command (`RebuildPageCommand`). It was originally added to build a single file to disk before the realtime compiler existed, and later used internally by the RC to build-and-serve a path, but the RC now renders everything in-memory, leaving `rebuild` with no remaining consumer. It also had no safe user-facing use case: a single-page build only produces a correct `_site` when the page is self-contained, while a page change routinely invalidates aggregate outputs (sitemap, RSS, search index, post listings, navigation), so single-path building could silently leave a stale output directory that looked complete. The underlying single-page build capability remains available internally via the `StaticPageBuilder` action. ([#2490](https://github.com/hydephp/develop/pull/2490)) ### Upgrade guide @@ -60,6 +68,8 @@ Please fill in UPGRADE.md as you make changes. - Move any calls to `Redirect::create()` or `Redirect::store()` into the `redirects` array in `config/hyde.php`, using the old path as the key and the destination as the value. - Move `InMemoryPage` `compile` macro callbacks into the contents argument, and replace other instance macros with methods on an `InMemoryPage` subclass. - Update `InMemoryPage` calls to supply only `contents` or `view`. Replace an empty-string positional contents placeholder with `null`, or use the named `view` argument. +- Compare a few pages against your old site if you have custom CSS for code blocks or filepath labels, since the generated markup changed. The `hyde-code-block` and `hyde-code-block-label` classes are stable hooks to target instead of the markup structure. +- Port any customizations from a published `filepath-label.blade.php` to the new `markdown/code-block-label.blade.php`. The old file is ignored after upgrading, so the site renders with the shipped label until they are moved. Customizations that shaped the area around the code block belong in `markdown/code-block.blade.php` instead. ## `InMemoryPage` content-source motivation @@ -150,3 +160,108 @@ terminal window could be rendered from PHP without writing Markdown. Each is a f change, judged on its own merits. More blocks may gain the same backing later, which is also a separate change; terminal blocks are the only one today. + +## Composable code block motivation + +Code blocks were the last Markdown construct still assembled by string manipulation. A pre-processor rewrote filepath +comments into `` markers, and a post-processor spliced the rendered label back into the finished +HTML with a regex matching `
`. That approach limited what the feature could ever be: the
+label could only be a fragment placed inside the `` element, because that was the only place a string
+replacement could reach. There was nowhere to put a copy button, a header bar, or a collapsed state.
+
+Rendering the whole block through a Blade view removes that limit. The view owns the markup around the code, so
+changing what surrounds a code block becomes a view change instead of a framework change, which is what terminal
+blocks already offer.
+
+The view is given the rendered code block markup rather than the raw code, which is what keeps highlighting working.
+Torchlight highlights through its API, so Hyde cannot render the code itself without replacing the highlighter.
+
+Getting hold of that markup is the whole design problem, because the highlighter is another renderer registered for
+the same node, and CommonMark's registry is not a middleware chain. The document renderer owns the iteration, asking
+each renderer in priority order and stopping at the first non-null result, and a node renderer only ever receives a
+`ChildNodeRendererInterface`, which renders child nodes and offers no way to call the next renderer.
+
+Three attempts are worth recording, because each one failed for a reason that shaped the next.
+
+The first reached back into the environment with `getRenderersForClass()` and called the renderers below itself,
+replaying the dispatcher by hand with no cursor for which renderers had already been tried.
+
+The second named Torchlight directly and injected it, with a fallback to CommonMark's renderer. That only worked for
+the highlighter we happen to ship with: a project using any other one got its code rendered by the fallback instead.
+
+The third rendered the node through a second converter built from the same extensions but without Hyde's renderer.
+That was highlighter-agnostic on the surface and wrong underneath. Registering an extension with two environments
+calls `register()` twice, so an extension that creates state there — a listener and a renderer sharing a collection,
+which is the ordinary way to write a highlighter — ends up with the document populating one copy and the renderer
+reading the other. It also invoked any renderer above Hyde's twice for the same block, once per environment, and
+skipped the document render events that `renderDocument()` dispatches but `renderNodes()` does not.
+
+What it settled on is structural rather than a second dispatch. Once the document is parsed and every listener has
+seen it, each remaining `FencedCode` is wrapped in a `CodeBlock` node of Hyde's own, and it is that wrapper the
+renderer is registered for. The fence is still in the tree, as the wrapper's only child, and rendering it through the
+`ChildNodeRendererInterface` the renderer is handed dispatches it back through the same environment — where Hyde is
+not a candidate, because Hyde renders `CodeBlock`, not `FencedCode`.
+
+One environment, one registration per extension, and each fence rendered exactly once by whoever the environment
+already had for it. Priority stops being part of the contract: a highlighter renders inside the view whether it
+registers above or below anything of ours. A block is only taken over completely by replacing the node itself, which
+is a deliberate act rather than a side effect of picking a number.
+
+Wrapping happens on `DocumentPreRenderEvent` at the lowest priority, so every parse-time listener has already run
+against a tree shaped the way it expects. The label is resolved separately, on `DocumentParsedEvent`, above the
+priority listeners register at by default, so a highlighter collects code that has already had the Hyde syntax taken
+out of it. The old pre-processor achieved the same thing by running before the parser.
+
+The title modifier is taken out of the info string once it has been read, for the same reason: CommonMark treats the
+first info word as the language, so a fence that sets only a title would otherwise hand `title="foo.php"` to the
+highlighter as one. Other modifiers are left alone, since a highlighter may read its own from there, as Torchlight
+does with `theme:`.
+
+Resolving the label on the syntax tree also fixes something the string approach could not. The pre-processor matched
+its comment pattern on any line of the document, so a `// filepath:` line that was never meant as a label was matched
+anyway. The label is now read only from the first line of a fenced block, which is what the documentation described
+all along.
+
+The second syntax, `title="…"`, is the terminal block title modifier applied to code blocks, which is the kind of
+reuse the extensibility argument for that syntax anticipated. It also matches what Docusaurus and Expressive Code call
+the same thing, so readers coming from other documentation generators will recognize it. The comment syntax is kept
+because it is what existing sites are written with, and because it reads naturally when the label names the file the
+code came from.
+
+A fence may set a title without a language, which the terminal block case never had to consider, since its first word
+is always `terminal`. The first info token is therefore only taken as the language when it does not look like a title
+modifier. That also keeps the error for a malformed title consistent, so ` ```title=Foo ` is rejected the same way
+` ```php title=Foo ` is, rather than being quietly read as a language named `title=Foo`.
+
+The modifier takes precedence when a block sets both. It is the more explicit of the two, and it lives on the fence
+rather than in the code, so a block carrying both is most likely mid-migration, and the newer form is the one the
+author meant. The comment is removed from the code either way, since leaving it behind would look like a mistake.
+
+The label keeps its own view instead of being inlined into the code block view. The two divide along a genuine
+boundary: one decides what surrounds the code, the other decides what the label looks like, and either can be
+published without the other.
+
+That view is renamed rather than reused, from `filepath-label.blade.php` to `markdown/code-block-label.blade.php`.
+Published views take precedence over the framework's own, so keeping the name would have left every customized copy in
+use. A copy written for the old position floats the label outside the code block once the label is no longer inside the
+`` element, which would have quietly broken the layout of those sites. Renaming means such a copy is ignored
+instead, costing the customization rather than the page.
+
+`CodeBlockViewModel` is internal, matching `TerminalBlockViewModel`. It exists to type the view data and document its
+shape in one place, not to be an entry point: a code block is rendered by writing Markdown, and the interface projects
+work against is the Blade view, not the class that feeds it. Exposing it publicly was considered and dropped, since
+nothing needs it yet and a public class is much harder to take back than to add.
+
+The view is given a `$language` variable it does not currently use, because it is intrinsic to the block and views
+building their own header bars want it. It was not given a flag for whether Torchlight produced the markup. The v2
+label needed one to offset its position, but the wrapper makes that offset uniform, so the flag would have been view
+data that nothing reads, and it would have named a specific highlighter in the view contract.
+
+The extension registration model is unchanged by all of this. Torchlight is registered by class name exactly as it was
+in v2, each extension is constructed once for one environment, and the code block wiring sits alongside the heading
+renderer in the converter setup, where the rest of Hyde's own rendering already lives.
+
+The preparation listener is the one piece registered out of line: it goes on before any extension, at the highest
+priority, because listeners sharing a priority run in registration order and a third-party listener is free to claim
+the same number. Wrapping is the mirror of that, registered afterwards at the lowest priority, so it happens once
+every other listener has had the document in the shape it expects.
diff --git a/UPGRADE.md b/UPGRADE.md
index e8783a21569..1f56162f62b 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -302,6 +302,27 @@ new InMemoryPage('example', view: '');
 new InMemoryPage('example', view: null);
 ```
 
+## Port Your Filepath Label View
+
+Fenced code blocks are now rendered through a Blade view, and the filepath label view moved along with them, from
+`components/filepath-label.blade.php` to `components/markdown/code-block-label.blade.php`.
+
+If you never published the label view, there is nothing to do here.
+
+If you did publish and customize it, your copy is no longer used, and your site renders with Hyde's default label
+until you move your changes over. Publish the new views, choosing them individually so your other published views are
+left alone, then re-apply your changes and delete the old file:
+
+```bash
+php hyde publish:views
+```
+
+>warning Passing the group name, as in `php hyde publish:views components`, publishes the whole group and overwrites every component view you have already customized.
+
+The markup around code blocks has also changed, so compare a few pages against your old site if you have custom CSS
+for them. The `hyde-code-block` and `hyde-code-block-label` classes are stable hooks you can target instead of
+matching the markup structure. Syntax highlighting is unaffected.
+
 ## Review Drafts and Future-Dated Blog Posts
 
 HydePHP v3 keeps two kinds of blog post out of your built site: those marked `draft: true` in front matter, and those whose date is set in the future. Drafts and scheduled posts are skipped during auto-discovery, so they get no route, are not compiled to `_site`, and are left out of post listings, the sitemap, and the RSS feed. The date rule applies to both front matter dates and filename date prefixes.
@@ -324,6 +345,8 @@ Use this checklist to track your upgrade progress:
 - [ ] Moved `InMemoryPage` `compile` macro callbacks into the contents argument and replaced other macros with subclass methods
 - [ ] Updated `InMemoryPage` calls to supply only one of `contents` and `view`
 - [ ] Checked `_posts` for drafts and blog posts dated in the future, and set up recurring builds if scheduling posts
+- [ ] Ported any `filepath-label.blade.php` customizations to `markdown/code-block-label.blade.php`, and deleted the old file
+- [ ] Compared pages against your old site if you have custom CSS for code blocks or filepath labels
 
 ## Troubleshooting
 
diff --git a/_media/app.css b/_media/app.css
index 7b14a86f786..c833a7e21b9 100644
--- a/_media/app.css
+++ b/_media/app.css
@@ -1,2 +1,2 @@
 /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
-@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-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-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking: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 base{pre code.torchlight .line-number,pre code.torchlight .summary-caret{margin-right:calc(var(--spacing) * 4)}.prose .torchlight-link,.torchlight-link{text-decoration-line:underline}.torchlight.has-focus-lines .line:not(.line-focus){filter:blur(.095rem);opacity:.65;transition:filter .35s,opacity .35s}.torchlight.has-focus-lines:hover .line:not(.line-focus){filter:blur();opacity:1}.torchlight summary:focus{--tw-outline-style:none;outline-style:none}.torchlight details>summary::marker{display:none}.torchlight details>summary::-webkit-details-marker{display:none}.torchlight details .summary-caret:after{pointer-events:none}.torchlight .summary-caret-empty:after,.torchlight details .summary-caret-middle:after,.torchlight details .summary-caret-end:after{content:" "}.torchlight details[open] .summary-caret-start:after{content:"-"}.torchlight details:not([open]) .summary-caret-start:after{content:"+"}.torchlight details[open] .summary-hide-when-open{display:none}.torchlight details:not([open]) .summary-hide-when-open{display:block}*,: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;-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{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 theme{:root,:host{--font-sans:ui-sans-serif, system-ui, 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-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-amber-500:oklch(76.9% .188 70.08);--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-green-500:oklch(72.3% .219 149.579);--color-blue-500:oklch(62.3% .214 259.815);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-800:oklch(27.9% .041 260.031);--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-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--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: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);--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;--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--drop-shadow-2xl:0 25px 25px #00000026;--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 components;@layer utilities{.invisible{visibility:hidden}.visible{visibility:visible}.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}.-top-1{top:calc(var(--spacing) * -1)}.top-0{top:0}.top-2\.5{top:calc(var(--spacing) * 2.5)}.top-4{top:calc(var(--spacing) * 4)}.top-16{top:calc(var(--spacing) * 16)}.top-auto{top:auto}.right-0{right:0}.right-1{right:var(--spacing)}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:0}.bottom-4{bottom:calc(var(--spacing) * 4)}.-left-64{left:calc(var(--spacing) * -64)}.left-0{left:0}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-12{grid-column:12}.float-left{float:left}.float-right{float:right}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.m-0{margin:0}.m-1{margin:var(--spacing)}.m-2{margin:calc(var(--spacing) * 2)}.m-8{margin:calc(var(--spacing) * 8)}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.mx-0{margin-inline:0}.mx-0\.5{margin-inline:calc(var(--spacing) * .5)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-8{margin-inline:calc(var(--spacing) * 8)}.mx-auto{margin-inline:auto}.my-0{margin-block:0}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.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-8{margin-block:calc(var(--spacing) * 8)}.my-auto{margin-block:auto}.prose{color:var(--tw-prose-body);max-width:96ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);margin-top:1.2em;margin-bottom:1.2em;font-size:1.25em;line-height:1.6}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:#5956eb;font-weight:500;text-decoration:none}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{color:#4f46e5}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:decimal}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:disc}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.25em;font-weight:600}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:unset;color:unset;border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";padding-inline-start:1em;background-color:#80808020;border-left-color:#d1d5db;margin-top:1em;margin-bottom:1em;padding-top:.25em;padding-bottom:.25em;padding-left:.75em;line-height:1.25em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p{margin-top:.25em;margin-bottom:.25em;padding-right:.25em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p:before,.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p:after{content:unset}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:0;margin-bottom:.888889em;font-size:2.25em;font-weight:800;line-height:1.11111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.75em;font-size:1.5em;font-weight:700;line-height:1.33333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.6em;margin-bottom:.6em;font-size:1.25em;font-weight:600;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em;display:block}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows), 0 3px 0 var(--tw-prose-kbd-shadows);padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;border-radius:.3125rem;padding-inline-start:.375em;font-family:inherit;font-size:.875em;font-weight:500}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:unset}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);padding-top:.857143em;padding-inline-end:1.14286em;padding-bottom:.857143em;background-color:#292d3e;border-radius:.25rem;margin-top:1rem;margin-bottom:1rem;padding-inline-start:1.14286em;font-size:.875em;font-weight:400;line-height:1.71429;overflow-x:auto}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)) code{font-family:Fira Code Regular,Consolas,monospace,Courier New}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit;background-color:#0000;border-width:0;border-radius:0;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){table-layout:auto;width:100%;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.71429}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);vertical-align:bottom;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em;font-weight:600}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);margin-top:.857143em;font-size:.875em;line-height:1.42857}.prose{--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:oklab(21% -.00316127 -.0338527/.1);--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:#ffffff1a;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1rem;line-height:1.5em}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.571429em;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose :where(code:not(pre code)):not(:where([class~=not-prose],[class~=not-prose] *)){font:unset;vertical-align:top;word-break:break-all;background-color:#80808033;border-radius:4px;max-width:80vw;margin-left:-2px;margin-right:1px;padding-left:4px;padding-right:4px;overflow-x:auto}.-mt-4{margin-top: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-12{margin-top:calc(var(--spacing) * 12)}.mt-auto{margin-top:auto}.mr-1{margin-right:var(--spacing)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mr-4{margin-right:calc(var(--spacing) * 4)}.mr-auto{margin-right:auto}.mb-0{margin-bottom:0}.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-8{margin-bottom:calc(var(--spacing) * 8)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.-ml-4{margin-left:calc(var(--spacing) * -4)}.-ml-6{margin-left:calc(var(--spacing) * -6)}.-ml-8{margin-left:calc(var(--spacing) * -8)}.ml-1{margin-left:var(--spacing)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.block\!{display:block!important}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1{height:var(--spacing)}.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-16{height:calc(var(--spacing) * 16)}.h-24{height:calc(var(--spacing) * 24)}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[75vh\]{max-height:75vh}.min-h-\[300px\]{min-height:300px}.min-h-\[calc\(100vh_-_4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.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-16{width:calc(var(--spacing) * 16)}.w-64{width:calc(var(--spacing) * 64)}.w-80{width:calc(var(--spacing) * 80)}.w-96{width:calc(var(--spacing) * 96)}.w-\[70ch\]{width:70ch}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.max-w-3xl{max-width:var(--container-3xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[1000px\]{max-width:1000px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.min-w-screen{min-width:100vw}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.border-collapse{border-collapse:collapse}.border-separate{border-collapse:separate}.origin-center{transform-origin:50%}.-rotate-45{rotate:-45deg}.rotate-45{rotate:45deg}.rotate-180{rotate:180deg}.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-auto{cursor:auto}.cursor-pointer{cursor:pointer}.resize{resize:both}.scroll-mt-2{scroll-margin-top:calc(var(--spacing) * 2)}.list-none{list-style-type:none}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}: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(.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-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}.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}.overflow-y-hidden{overflow-y:hidden}.scroll-smooth{scroll-behavior:smooth}.rounded{border-radius:.25rem}.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-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-tl{border-top-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-tr{border-top-right-radius:.25rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-br{border-bottom-right-radius:.25rem}.rounded-bl{border-bottom-left-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-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-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-l-\[0\.325rem\]{border-left-style:var(--tw-border-style);border-left-width:.325rem}.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-none{--tw-border-style:none;border-style:none}.border-solid{--tw-border-style:solid;border-style:solid}.border-amber-500{border-color:var(--color-amber-500)}.border-blue-500{border-color:var(--color-blue-500)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-500{border-color:var(--color-gray-500)}.border-gray-900{border-color:var(--color-gray-900)}.border-green-500{border-color:var(--color-green-500)}.border-indigo-500{border-color:#5956eb}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-yellow-400{border-color:var(--color-yellow-400)}.border-t-transparent{border-top-color:#0000}.border-r-gray-900{border-right-color:var(--color-gray-900)}.bg-\[\#28C840\]{background-color:#28c840}.bg-\[\#292D3E\]{background-color:#292d3e}.bg-\[\#212529\]{background-color:#212529}.bg-\[\#FEBC2E\]{background-color:#febc2e}.bg-\[\#FF5F57\]{background-color:#ff5f57}.bg-black{background-color:var(--color-black)}.bg-black\/5{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.bg-black\/5{background-color:color-mix(in oklab, var(--color-black) 5%, 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-current{background-color:currentColor}.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-500{background-color:var(--color-gray-500)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-linear-to-br{--tw-gradient-position:to bottom right}@supports (background-image:linear-gradient(in lab, red, red)){.bg-linear-to-br{--tw-gradient-position:to bottom right in oklab}}.bg-linear-to-br{background-image:linear-gradient(var(--tw-gradient-stops))}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-no-repeat{background-repeat:no-repeat}.fill-black{fill:var(--color-black)}.fill-current{fill:currentColor}.p-0{padding:0}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-8{padding:calc(var(--spacing) * 8)}.p-12{padding:calc(var(--spacing) * 12)}.px-0{padding-inline:0}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:0}.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-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-24{padding-block:calc(var(--spacing) * 24)}.py-32{padding-block:calc(var(--spacing) * 32)}.ps-2{padding-inline-start:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-8{padding-top:calc(var(--spacing) * 8)}.pt-10{padding-top:calc(var(--spacing) * 10)}.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-8{padding-bottom:calc(var(--spacing) * 8)}.pb-12{padding-bottom:calc(var(--spacing) * 12)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-end{text-align:end}.text-left{text-align:left}.text-right{text-align:right}.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);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-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-\[75\%\]{font-size:75%}.text-\[90\%\]{font-size:90%}.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-10{--tw-leading:calc(var(--spacing) * 10);line-height:calc(var(--spacing) * 10)}.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)}.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-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-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.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-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.text-\[\#89DDFF\]{color:#89ddff}.text-\[\#A6ACCD\]{color:#a6accd}.text-\[\#C3E88D\]{color:#c3e88d}.text-\[\#F07178\]{color:#f07178}.text-\[\#FFCB6B\]{color:#ffcb6b}.text-black{color:var(--color-black)}.text-blue-500{color:var(--color-blue-500)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.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-green-500{color:var(--color-green-500)}.text-indigo-500{color:#5956eb}.text-indigo-600{color:var(--color-indigo-600)}.text-red-500{color:var(--color-red-500)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-yellow-500{color:var(--color-yellow-500)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.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-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-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-900\/5{--tw-ring-color:#1018280d}@supports (color:color-mix(in lab, red, red)){.ring-gray-900\/5{--tw-ring-color:color-mix(in oklab, var(--color-gray-900) 5%, transparent)}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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,)}.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,)}.backdrop-filter{-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))}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-250{--tw-duration:.25s;transition-duration:.25s}.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-linear{--tw-ease:linear;transition-timing-function:linear}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\:grayscale-0:is(:where(.group):hover *){--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,)}.hover\:bg-black\/5:hover{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-black\/5:hover{background-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.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-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200\/20:hover{background-color:#e5e7eb33}@supports (color:color-mix(in lab, red, red)){.hover\:bg-gray-200\/20:hover{background-color:color-mix(in oklab, var(--color-gray-200) 20%, transparent)}}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.hover\:bg-indigo-500:hover{background-color:#5956eb}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-indigo-600:hover{color:var(--color-indigo-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.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)}}.focus\:not-sr-only:focus{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.focus\:absolute:focus{position:absolute}.focus\:mx-auto:focus{margin-inline:auto}.focus\:mt-2:focus{margin-top:calc(var(--spacing) * 2)}.focus\:w-64:focus{width:calc(var(--spacing) * 64)}.focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.focus\:bg-indigo-700:focus{background-color:var(--color-indigo-700)}.focus\:p-2:focus{padding:calc(var(--spacing) * 2)}.focus\:opacity-100:focus{opacity:1}.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-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.focus\:ring-indigo-600:focus{--tw-ring-color:var(--color-indigo-600)}.focus\:grayscale-0:focus{--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,)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (width>=40rem){.sm\:-mx-6{margin-inline:calc(var(--spacing) * -6)}.sm\:mx-auto{margin-inline:auto}.sm\:mt-4{margin-top:calc(var(--spacing) * 4)}.sm\:mb-0{margin-bottom:0}.sm\:block{display:block}.sm\:max-w-xl{max-width:var(--container-xl)}.sm\:rounded-lg{border-radius:var(--radius-lg)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:px-10{padding-inline:calc(var(--spacing) * 10)}.sm\:py-12{padding-block:calc(var(--spacing) * 12)}.sm\:leading-none{--tw-leading:1;line-height:1}.sm\: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)}}@media (width>=48rem){.md\:visible{visibility:visible}.md\:top-0{top:0}.md\:left-0{left:0}.md\:left-64{left:calc(var(--spacing) * 64)}.md\:mx-2{margin-inline:calc(var(--spacing) * 2)}.md\:my-0{margin-block:0}.md\:my-6{margin-block:calc(var(--spacing) * 6)}.md\:mt-0{margin-top:0}.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.md\:ml-0{margin-left:0}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:inline-block{display:inline-block}.md\:min-h-screen{min-height:100vh}.md\:w-1\/2{width:50%}.md\:w-\[calc\(100vw_-_16rem\)\]{width:calc(100vw - 16rem)}.md\:w-auto{width:auto}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:max-w-none{max-width:none}.md\:grow{flex-grow:1}.md\:grow-0{flex-grow:0}.md\:items-center{align-items:center}.md\:border-none{--tw-border-style:none;border-style:none}.md\:bg-transparent{background-color:#0000}.md\:bg-white{background-color:var(--color-white)}.md\:bg-left{background-position:0}.md\:px-16{padding-inline:calc(var(--spacing) * 16)}.md\:py-0{padding-block:0}.md\:py-16{padding-block:calc(var(--spacing) * 16)}.md\:pb-0{padding-bottom:0}.md\:pl-0{padding-left:0}.md\:text-center{text-align:center}.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-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.md\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.md\: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)}}@media (width>=64rem){.lg\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.lg\:ml-8{margin-left:calc(var(--spacing) * 8)}.lg\:flex{display:flex}.lg\:bg-center{background-position:50%}.lg\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.lg\:text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.lg\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media (width>=80rem){.xl\:mb-16{margin-bottom:calc(var(--spacing) * 16)}}.dark\:block:is(.dark *){display:block}.dark\:hidden:is(.dark *){display:none}:where(.dark\:divide-gray-700:is(.dark *)>:not(:last-child)){border-color:var(--color-gray-700)}.dark\:border-\[\#1b2533\]:is(.dark *){border-color:#1b2533}.dark\:border-gray-700:is(.dark *){border-color:var(--color-gray-700)}.dark\:bg-black\/10:is(.dark *){background-color:#0000001a}@supports (color:color-mix(in lab, red, red)){.dark\:bg-black\/10:is(.dark *){background-color:color-mix(in oklab, var(--color-black) 10%, transparent)}}.dark\:bg-gray-600:is(.dark *){background-color:var(--color-gray-600)}.dark\:bg-gray-700:is(.dark *){background-color:var(--color-gray-700)}.dark\:bg-gray-800:is(.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-900:is(.dark *){background-color:var(--color-gray-900)}.dark\:bg-slate-800:is(.dark *){background-color:var(--color-slate-800)}.dark\:bg-yellow-300:is(.dark *){background-color:var(--color-yellow-300)}.dark\:fill-gray-200:is(.dark *){fill:var(--color-gray-200)}.dark\:fill-white:is(.dark *){fill:var(--color-white)}.dark\:font-medium:is(.dark *){--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.dark\:prose-invert:is(.dark *){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.dark\:prose-invert:is(.dark *) :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:#818cf8}.dark\:prose-invert:is(.dark *) :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{color:#6366f1}.dark\:text-gray-100:is(.dark *){color:var(--color-gray-100)}.dark\:text-gray-200:is(.dark *){color:var(--color-gray-200)}.dark\:text-gray-300:is(.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:is(.dark *){color:var(--color-gray-400)}.dark\:text-indigo-400:is(.dark *){color:var(--color-indigo-400)}.dark\:text-white:is(.dark *){color:var(--color-white)}@media (hover:hover){.dark\:group-hover\:text-white:is(.dark *):is(:where(.group):hover *){color:var(--color-white)}.dark\:hover\:bg-black\/10:is(.dark *):hover{background-color:#0000001a}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-black\/10:is(.dark *):hover{background-color:color-mix(in oklab, var(--color-black) 10%, transparent)}}.dark\:hover\:bg-gray-500:is(.dark *):hover{background-color:var(--color-gray-500)}.dark\:hover\:bg-gray-600:is(.dark *):hover{background-color:var(--color-gray-600)}.dark\:hover\:text-white:is(.dark *):hover{color:var(--color-white)}}.dark\:focus\:text-white:is(.dark *):focus{color:var(--color-white)}.dark\:focus\:ring-indigo-500:is(.dark *):focus{--tw-ring-color:#5956eb}@media (width>=48rem){.dark\:md\:bg-transparent:is(.dark *){background-color:#0000}}@media print{.print\:top-0{top:0}.print\:hidden{display:none}}.prose-h1\:mb-3 :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:calc(var(--spacing) * 3)}.prose-p\:my-3 :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 3)}.prose-img\:inline :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){display:inline}}@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-divide-y-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-tracking{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}@keyframes spin{to{transform:rotate(360deg)}}
+@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-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-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking: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 base{pre code.torchlight .line-number,pre code.torchlight .summary-caret{margin-right:calc(var(--spacing) * 4)}.prose .torchlight-link,.torchlight-link{text-decoration-line:underline}.torchlight.has-focus-lines .line:not(.line-focus){filter:blur(.095rem);opacity:.65;transition:filter .35s,opacity .35s}.torchlight.has-focus-lines:hover .line:not(.line-focus){filter:blur();opacity:1}.torchlight summary:focus{--tw-outline-style:none;outline-style:none}.torchlight details>summary::marker{display:none}.torchlight details>summary::-webkit-details-marker{display:none}.torchlight details .summary-caret:after{pointer-events:none}.torchlight .summary-caret-empty:after,.torchlight details .summary-caret-middle:after,.torchlight details .summary-caret-end:after{content:" "}.torchlight details[open] .summary-caret-start:after{content:"-"}.torchlight details:not([open]) .summary-caret-start:after{content:"+"}.torchlight details[open] .summary-hide-when-open{display:none}.torchlight details:not([open]) .summary-hide-when-open{display:block}*,: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;-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{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 theme{:root,:host{--font-sans:ui-sans-serif, system-ui, 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-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-amber-500:oklch(76.9% .188 70.08);--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-green-500:oklch(72.3% .219 149.579);--color-blue-500:oklch(62.3% .214 259.815);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-800:oklch(27.9% .041 260.031);--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-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--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: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);--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;--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--drop-shadow-2xl:0 25px 25px #00000026;--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 components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.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}.top-0{top:0}.top-2\.5{top:calc(var(--spacing) * 2.5)}.top-3{top:calc(var(--spacing) * 3)}.top-4{top:calc(var(--spacing) * 4)}.top-16{top:calc(var(--spacing) * 16)}.top-auto{top:auto}.right-0{right:0}.right-3{right:calc(var(--spacing) * 3)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:0}.bottom-4{bottom:calc(var(--spacing) * 4)}.-left-64{left:calc(var(--spacing) * -64)}.left-0{left:0}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-12{grid-column:12}.float-left{float:left}.float-right{float:right}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.m-0{margin:0}.m-1{margin:var(--spacing)}.m-2{margin:calc(var(--spacing) * 2)}.m-8{margin:calc(var(--spacing) * 8)}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.mx-0{margin-inline:0}.mx-0\.5{margin-inline:calc(var(--spacing) * .5)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-8{margin-inline:calc(var(--spacing) * 8)}.mx-auto{margin-inline:auto}.my-0{margin-block:0}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.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-8{margin-block:calc(var(--spacing) * 8)}.my-auto{margin-block:auto}.prose{color:var(--tw-prose-body);max-width:96ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);margin-top:1.2em;margin-bottom:1.2em;font-size:1.25em;line-height:1.6}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:#5956eb;font-weight:500;text-decoration:none}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{color:#4f46e5}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:decimal}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em;list-style-type:disc}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.25em;font-weight:600}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:unset;color:unset;border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";padding-inline-start:1em;background-color:#80808020;border-left-color:#d1d5db;margin-top:1em;margin-bottom:1em;padding-top:.25em;padding-bottom:.25em;padding-left:.75em;line-height:1.25em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p{margin-top:.25em;margin-bottom:.25em;padding-right:.25em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p:before,.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) p:after{content:unset}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:0;margin-bottom:.888889em;font-size:2.25em;font-weight:800;line-height:1.11111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.75em;font-size:1.5em;font-weight:700;line-height:1.33333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.6em;margin-bottom:.6em;font-size:1.25em;font-weight:600;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);margin-top:1.5em;margin-bottom:.5em;font-weight:600;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em;display:block}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-kbd);box-shadow:0 0 0 1px var(--tw-prose-kbd-shadows), 0 3px 0 var(--tw-prose-kbd-shadows);padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;border-radius:.3125rem;padding-inline-start:.375em;font-family:inherit;font-size:.875em;font-weight:500}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:unset}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);padding-top:.857143em;padding-inline-end:1.14286em;padding-bottom:.857143em;background-color:#292d3e;border-radius:.25rem;margin-top:1rem;margin-bottom:1rem;padding-inline-start:1.14286em;font-size:.875em;font-weight:400;line-height:1.71429;overflow-x:auto}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)) code{font-family:Fira Code Regular,Consolas,monospace,Courier New}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit;background-color:#0000;border-width:0;border-radius:0;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before,.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){table-layout:auto;width:100%;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.71429}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);vertical-align:bottom;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em;font-weight:600}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);margin-top:.857143em;font-size:.875em;line-height:1.42857}.prose{--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:oklab(21% -.00316127 -.0338527/.1);--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:#ffffff1a;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1rem;line-height:1.5em}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.571429em;padding-inline-end:.571429em;padding-bottom:.571429em;padding-inline-start:.571429em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose :where(code:not(pre code)):not(:where([class~=not-prose],[class~=not-prose] *)){font:unset;vertical-align:top;word-break:break-all;background-color:#80808033;border-radius:4px;max-width:80vw;margin-left:-2px;margin-right:1px;padding-left:4px;padding-right:4px;overflow-x:auto}.-mt-4{margin-top: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-12{margin-top:calc(var(--spacing) * 12)}.mt-auto{margin-top:auto}.mr-1{margin-right:var(--spacing)}.mr-3{margin-right:calc(var(--spacing) * 3)}.mr-4{margin-right:calc(var(--spacing) * 4)}.mr-auto{margin-right:auto}.mb-0{margin-bottom:0}.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-8{margin-bottom:calc(var(--spacing) * 8)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.-ml-4{margin-left:calc(var(--spacing) * -4)}.-ml-6{margin-left:calc(var(--spacing) * -6)}.-ml-8{margin-left:calc(var(--spacing) * -8)}.ml-1{margin-left:var(--spacing)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.block\!{display:block!important}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1{height:var(--spacing)}.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-16{height:calc(var(--spacing) * 16)}.h-24{height:calc(var(--spacing) * 24)}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[75vh\]{max-height:75vh}.min-h-\[300px\]{min-height:300px}.min-h-\[calc\(100vh_-_4rem\)\]{min-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.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-16{width:calc(var(--spacing) * 16)}.w-64{width:calc(var(--spacing) * 64)}.w-80{width:calc(var(--spacing) * 80)}.w-96{width:calc(var(--spacing) * 96)}.w-\[70ch\]{width:70ch}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-screen{width:100vw}.max-w-3xl{max-width:var(--container-3xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[1000px\]{max-width:1000px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-sm{max-width:var(--container-sm)}.min-w-screen{min-width:100vw}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.table-auto{table-layout:auto}.table-fixed{table-layout:fixed}.border-collapse{border-collapse:collapse}.border-separate{border-collapse:separate}.origin-center{transform-origin:50%}.-rotate-45{rotate:-45deg}.rotate-45{rotate:45deg}.rotate-180{rotate:180deg}.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-auto{cursor:auto}.cursor-pointer{cursor:pointer}.resize{resize:both}.scroll-mt-2{scroll-margin-top:calc(var(--spacing) * 2)}.list-none{list-style-type:none}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}: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(.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-gray-300>:not(:last-child)){border-color:var(--color-gray-300)}.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}.overflow-y-hidden{overflow-y:hidden}.scroll-smooth{scroll-behavior:smooth}.rounded{border-radius:.25rem}.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-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-tl{border-top-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-tr{border-top-right-radius:.25rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-br{border-bottom-right-radius:.25rem}.rounded-bl{border-bottom-left-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-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-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-l-\[0\.325rem\]{border-left-style:var(--tw-border-style);border-left-width:.325rem}.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-none{--tw-border-style:none;border-style:none}.border-solid{--tw-border-style:solid;border-style:solid}.border-amber-500{border-color:var(--color-amber-500)}.border-blue-500{border-color:var(--color-blue-500)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-500{border-color:var(--color-gray-500)}.border-gray-900{border-color:var(--color-gray-900)}.border-green-500{border-color:var(--color-green-500)}.border-indigo-500{border-color:#5956eb}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-yellow-400{border-color:var(--color-yellow-400)}.border-t-transparent{border-top-color:#0000}.border-r-gray-900{border-right-color:var(--color-gray-900)}.bg-\[\#28C840\]{background-color:#28c840}.bg-\[\#292D3E\]{background-color:#292d3e}.bg-\[\#212529\]{background-color:#212529}.bg-\[\#FEBC2E\]{background-color:#febc2e}.bg-\[\#FF5F57\]{background-color:#ff5f57}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab, red, red)){.bg-amber-500\/10{background-color:color-mix(in oklab, var(--color-amber-500) 10%, transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/5{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.bg-black\/5{background-color:color-mix(in oklab, var(--color-black) 5%, 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-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab, red, red)){.bg-blue-500\/10{background-color:color-mix(in oklab, var(--color-blue-500) 10%, transparent)}}.bg-current{background-color:currentColor}.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-500{background-color:var(--color-gray-500)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab, red, red)){.bg-green-500\/10{background-color:color-mix(in oklab, var(--color-green-500) 10%, transparent)}}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-red-600\/10{background-color:#e400141a}@supports (color:color-mix(in lab, red, red)){.bg-red-600\/10{background-color:color-mix(in oklab, var(--color-red-600) 10%, transparent)}}.bg-slate-100{background-color:var(--color-slate-100)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-linear-to-br{--tw-gradient-position:to bottom right}@supports (background-image:linear-gradient(in lab, red, red)){.bg-linear-to-br{--tw-gradient-position:to bottom right in oklab}}.bg-linear-to-br{background-image:linear-gradient(var(--tw-gradient-stops))}.bg-cover{background-size:cover}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.bg-no-repeat{background-repeat:no-repeat}.fill-black{fill:var(--color-black)}.fill-current{fill:currentColor}.p-0{padding:0}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-8{padding:calc(var(--spacing) * 8)}.p-12{padding:calc(var(--spacing) * 12)}.px-0{padding-inline:0}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:0}.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-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-24{padding-block:calc(var(--spacing) * 24)}.py-32{padding-block:calc(var(--spacing) * 32)}.ps-2{padding-inline-start:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-8{padding-top:calc(var(--spacing) * 8)}.pt-10{padding-top:calc(var(--spacing) * 10)}.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-8{padding-bottom:calc(var(--spacing) * 8)}.pb-12{padding-bottom:calc(var(--spacing) * 12)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-end{text-align:end}.text-left{text-align:left}.text-right{text-align:right}.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);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-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-\[75\%\]{font-size:75%}.text-\[90\%\]{font-size:90%}.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-10{--tw-leading:calc(var(--spacing) * 10);line-height:calc(var(--spacing) * 10)}.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)}.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-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-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.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-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.text-\[\#89DDFF\]{color:#89ddff}.text-\[\#A6ACCD\]{color:#a6accd}.text-\[\#C3E88D\]{color:#c3e88d}.text-\[\#F07178\]{color:#f07178}.text-\[\#FFCB6B\]{color:#ffcb6b}.text-\[color\:var\(--tw-prose-pre-code\)\]{color:var(--tw-prose-pre-code)}.text-black{color:var(--color-black)}.text-blue-500{color:var(--color-blue-500)}.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-green-500{color:var(--color-green-500)}.text-indigo-500{color:#5956eb}.text-indigo-600{color:var(--color-indigo-600)}.text-red-500{color:var(--color-red-500)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-yellow-500{color:var(--color-yellow-500)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.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-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-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-900\/5{--tw-ring-color:#1018280d}@supports (color:color-mix(in lab, red, red)){.ring-gray-900\/5{--tw-ring-color:color-mix(in oklab, var(--color-gray-900) 5%, transparent)}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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,)}.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,)}.backdrop-filter{-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))}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-250{--tw-duration:.25s;transition-duration:.25s}.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-linear{--tw-ease:linear;transition-timing-function:linear}.select-none{-webkit-user-select:none;user-select:none}.\[heading\:\$heading\]{heading:$heading}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\:grayscale-0:is(:where(.group):hover *){--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,)}.hover\:bg-black\/5:hover{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-black\/5:hover{background-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.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-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200\/20:hover{background-color:#e5e7eb33}@supports (color:color-mix(in lab, red, red)){.hover\:bg-gray-200\/20:hover{background-color:color-mix(in oklab, var(--color-gray-200) 20%, transparent)}}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.hover\:bg-indigo-500:hover{background-color:#5956eb}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-indigo-600:hover{color:var(--color-indigo-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.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)}}.focus\:not-sr-only:focus{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.focus\:absolute:focus{position:absolute}.focus\:mx-auto:focus{margin-inline:auto}.focus\:mt-2:focus{margin-top:calc(var(--spacing) * 2)}.focus\:w-64:focus{width:calc(var(--spacing) * 64)}.focus\:bg-gray-700:focus{background-color:var(--color-gray-700)}.focus\:bg-indigo-700:focus{background-color:var(--color-indigo-700)}.focus\:p-2:focus{padding:calc(var(--spacing) * 2)}.focus\:opacity-100:focus{opacity:1}.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-gray-700:focus{--tw-ring-color:var(--color-gray-700)}.focus\:ring-indigo-600:focus{--tw-ring-color:var(--color-indigo-600)}.focus\:grayscale-0:focus{--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,)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}@media (width>=40rem){.sm\:-mx-6{margin-inline:calc(var(--spacing) * -6)}.sm\:mx-auto{margin-inline:auto}.sm\:mt-4{margin-top:calc(var(--spacing) * 4)}.sm\:mb-0{margin-bottom:0}.sm\:block{display:block}.sm\:max-w-xl{max-width:var(--container-xl)}.sm\:rounded-lg{border-radius:var(--radius-lg)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:px-10{padding-inline:calc(var(--spacing) * 10)}.sm\:py-12{padding-block:calc(var(--spacing) * 12)}.sm\:leading-none{--tw-leading:1;line-height:1}.sm\: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)}}@media (width>=48rem){.md\:visible{visibility:visible}.md\:top-0{top:0}.md\:left-0{left:0}.md\:left-64{left:calc(var(--spacing) * 64)}.md\:mx-2{margin-inline:calc(var(--spacing) * 2)}.md\:my-0{margin-block:0}.md\:my-6{margin-block:calc(var(--spacing) * 6)}.md\:mt-0{margin-top:0}.md\:mt-8{margin-top:calc(var(--spacing) * 8)}.md\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.md\:ml-0{margin-left:0}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:inline-block{display:inline-block}.md\:min-h-screen{min-height:100vh}.md\:w-1\/2{width:50%}.md\:w-\[calc\(100vw_-_16rem\)\]{width:calc(100vw - 16rem)}.md\:w-auto{width:auto}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:max-w-none{max-width:none}.md\:grow{flex-grow:1}.md\:grow-0{flex-grow:0}.md\:items-center{align-items:center}.md\:border-none{--tw-border-style:none;border-style:none}.md\:bg-transparent{background-color:#0000}.md\:bg-white{background-color:var(--color-white)}.md\:bg-left{background-position:0}.md\:px-16{padding-inline:calc(var(--spacing) * 16)}.md\:py-0{padding-block:0}.md\:py-16{padding-block:calc(var(--spacing) * 16)}.md\:pb-0{padding-bottom:0}.md\:pl-0{padding-left:0}.md\:text-center{text-align:center}.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-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.md\:text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.md\: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)}}@media (width>=64rem){.lg\:mb-12{margin-bottom:calc(var(--spacing) * 12)}.lg\:ml-8{margin-left:calc(var(--spacing) * 8)}.lg\:flex{display:flex}.lg\:bg-center{background-position:50%}.lg\:text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--line-height))}.lg\:text-7xl{font-size:var(--text-7xl);line-height:var(--tw-leading,var(--text-7xl--line-height))}.lg\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media (width>=80rem){.xl\:mb-16{margin-bottom:calc(var(--spacing) * 16)}}.dark\:block:is(.dark *){display:block}.dark\:hidden:is(.dark *){display:none}:where(.dark\:divide-gray-700:is(.dark *)>:not(:last-child)){border-color:var(--color-gray-700)}.dark\:border-\[\#1b2533\]:is(.dark *){border-color:#1b2533}.dark\:border-gray-700:is(.dark *){border-color:var(--color-gray-700)}.dark\:bg-black\/10:is(.dark *){background-color:#0000001a}@supports (color:color-mix(in lab, red, red)){.dark\:bg-black\/10:is(.dark *){background-color:color-mix(in oklab, var(--color-black) 10%, transparent)}}.dark\:bg-gray-600:is(.dark *){background-color:var(--color-gray-600)}.dark\:bg-gray-700:is(.dark *){background-color:var(--color-gray-700)}.dark\:bg-gray-800:is(.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-900:is(.dark *){background-color:var(--color-gray-900)}.dark\:bg-slate-800:is(.dark *){background-color:var(--color-slate-800)}.dark\:bg-yellow-300:is(.dark *){background-color:var(--color-yellow-300)}.dark\:fill-gray-200:is(.dark *){fill:var(--color-gray-200)}.dark\:fill-white:is(.dark *){fill:var(--color-white)}.dark\:font-medium:is(.dark *){--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.dark\:prose-invert:is(.dark *){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.dark\:prose-invert:is(.dark *) :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:#818cf8}.dark\:prose-invert:is(.dark *) :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{color:#6366f1}.dark\:text-gray-100:is(.dark *){color:var(--color-gray-100)}.dark\:text-gray-200:is(.dark *){color:var(--color-gray-200)}.dark\:text-gray-300:is(.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:is(.dark *){color:var(--color-gray-400)}.dark\:text-indigo-400:is(.dark *){color:var(--color-indigo-400)}.dark\:text-white:is(.dark *){color:var(--color-white)}@media (hover:hover){.dark\:group-hover\:text-white:is(.dark *):is(:where(.group):hover *){color:var(--color-white)}.dark\:hover\:bg-black\/10:is(.dark *):hover{background-color:#0000001a}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-black\/10:is(.dark *):hover{background-color:color-mix(in oklab, var(--color-black) 10%, transparent)}}.dark\:hover\:bg-gray-500:is(.dark *):hover{background-color:var(--color-gray-500)}.dark\:hover\:bg-gray-600:is(.dark *):hover{background-color:var(--color-gray-600)}.dark\:hover\:text-white:is(.dark *):hover{color:var(--color-white)}}.dark\:focus\:text-white:is(.dark *):focus{color:var(--color-white)}.dark\:focus\:ring-indigo-500:is(.dark *):focus{--tw-ring-color:#5956eb}@media (width>=48rem){.dark\:md\:bg-transparent:is(.dark *){background-color:#0000}}@media print{.print\:top-0{top:0}.print\:hidden{display:none}}.prose-h1\:mb-3 :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:calc(var(--spacing) * 3)}.prose-p\:my-3 :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-block:calc(var(--spacing) * 3)}.prose-img\:inline :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){display:inline}}@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-divide-y-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-tracking{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}@keyframes spin{to{transform:rotate(360deg)}}
diff --git a/docs/digging-deeper/advanced-markdown.md b/docs/digging-deeper/advanced-markdown.md
index c80380b2676..93ed5bce587 100644
--- a/docs/digging-deeper/advanced-markdown.md
+++ b/docs/digging-deeper/advanced-markdown.md
@@ -254,7 +254,8 @@ Note that these currently do not support multi-line blockquotes.
 ## Code Block Filepaths
 
 When browsing these documentation pages you may have noticed a label in the top right corner of code blocks specifying the file path.
-These are also created by using a custom Hyde feature that turns code comments into automatic code blocks.
+These are also created by using a custom Hyde feature that turns code comments into automatic labels.
+If you would rather keep the label out of the code, you can set it on the code fence instead.
 
 ### Usage
 
@@ -291,6 +292,37 @@ console.log('Hello World!');
 
 If you have a newline after the filepath, like in the first example, it will be removed so your code stays readable.
 
+### Title modifier
+
+You can also set the label with a `title` modifier on the code fence, using the same syntax as
+[terminal code blocks](#window-titles). This is handy when you would rather not add a comment to the code itself.
+
+````markdown
+```php title="hello-world.php"
+echo 'Hello World!';
+```
+````
+
+Which becomes:
+
+```php title="hello-world.php"
+echo 'Hello World!';
+```
+
+The quoting rules are the same as for terminal titles. Double quotes are canonical, single quotes are also accepted,
+and malformed values are reported as errors instead of being guessed at.
+
+The language is optional here, so you can label a block without one:
+
+````markdown
+``` title=".env"
+APP_NAME=HydePHP
+```
+````
+
+If you use both syntaxes on the same block, the modifier takes precedence. Either way, the comment is removed from
+the code.
+
 ### Advanced usage
 
 Since HTML in Markdown is enabled by default, anything within the path label will be rendered as HTML. This means you
@@ -307,6 +339,20 @@ can add links, or even images to the label. This requires `allow_html` to remain
 
 The filepaths are hidden on mobile devices using CSS to prevent them from overlapping with the code block.
 
+### Customizations
+
+The code block markup provides stable styling hooks for customization. Use `hyde-code-block` to style the block, and
+`hyde-code-block-label` to style the filepath label.
+
+You can customize the markup itself by publishing Hyde's Blade components:
+
+```bash
+php hyde publish:views
+```
+
+The code block component will be published to `resources/views/vendor/hyde/components/markdown/code-block.blade.php`,
+and the label component it renders to `markdown/code-block-label.blade.php`.
+
 
 ## Heading Permalinks
 
diff --git a/docs/digging-deeper/composable-markdown-blocks.md b/docs/digging-deeper/composable-markdown-blocks.md
index 2f30dc96587..2b85c7302d1 100644
--- a/docs/digging-deeper/composable-markdown-blocks.md
+++ b/docs/digging-deeper/composable-markdown-blocks.md
@@ -53,12 +53,14 @@ loops, `@include`, config calls, and the full Tailwind class set inside them.
 
 | Block                                   | Syntax                        | View                               | Mechanism            |
 |-----------------------------------------|-------------------------------|------------------------------------|----------------------|
+| [Code blocks](#code-blocks)             | ` ```php `                    | `markdown/code-block.blade.php`    | CommonMark renderer  |
 | [Terminal blocks](#terminal-blocks)     | ` ```terminal `               | `markdown/terminal.blade.php`      | CommonMark renderer  |
 | [Coloured blockquotes](#coloured-blockquotes) | `>info Text`            | `colored-blockquote.blade.php`     | Markdown pre-processor |
 | [Headings](#headings)                   | `## Heading`                  | `markdown-heading.blade.php`       | CommonMark renderer  |
-| [Filepath labels](#code-block-filepath-labels) | `// filepath: foo.php` | `filepath-label.blade.php`         | Markdown post-processor |
 | [Blade component blocks](#blade-component-blocks) | ` ```blade component="x" ` | *any component you write*    | Markdown pre-processor |
 
+Code blocks hand their filepath label off to a [second view](#view-contract), which can be published on its own.
+
 All view paths are relative to `resources/views/components/` in the framework package, and to
 `resources/views/vendor/hyde/components/` once published into your project.
 
@@ -78,9 +80,10 @@ Published views land in `resources/views/vendor/hyde/components/`, mirroring the
 ```
 resources/views/vendor/hyde/components/
 ├── colored-blockquote.blade.php
-├── filepath-label.blade.php
 ├── markdown-heading.blade.php
 └── markdown/
+    ├── code-block-label.blade.php
+    ├── code-block.blade.php
     └── terminal.blade.php
 ```
 
@@ -128,24 +131,80 @@ phases, orchestrated by the `MarkdownService`:
   placeholder comment so nothing downstream tries to parse their contents.
 - `BladeDownProcessor` — handles single-line `[Blade]:` directives.
 - `ShortcodeProcessor` — expands coloured blockquotes into rendered HTML.
-- `CodeblockFilepathProcessor` — converts filepath comments into `` markers.
 
 **2. CommonMark conversion** parses the Markdown into an abstract syntax tree and renders it.
 
 - `TerminalExtension` is always registered. It converts matching fenced code nodes into `TerminalBlock` nodes and
   renders them through the terminal view.
+- Remaining fenced code nodes get their label resolved, and are wrapped in the code block view. Your highlighter still
+  renders the code itself.
 - `HeadingRenderer` replaces CommonMark's default heading renderer with Hyde's Blade-backed one.
 - Any extensions listed in `markdown.extensions` are registered here too, as is the Torchlight extension when enabled.
 
 **3. Post-processors** run against the resulting HTML string.
 
 - `BladeBlockProcessor` swaps the placeholders back out for the rendered Blade output.
-- `CodeblockFilepathProcessor` replaces the markers with the rendered filepath label view.
 - `DynamicMarkdownLinkProcessor` resolves source-file links to routes.
 
-The distinction that matters: **AST-based blocks** (terminal, headings) are structurally aware — they only ever match
-real Markdown nodes. **String-based blocks** (shortcodes, filepath labels) work on lines of text and are cheaper to
-implement, but they are not fence-aware. See [Limitations](#limitations-and-gotchas).
+The distinction that matters: **AST-based blocks** (code blocks, terminals, headings) are structurally aware — they
+only ever match real Markdown nodes. **String-based blocks** (shortcodes, Blade blocks) work on lines of text and are
+cheaper to implement, but they are not fence-aware. See [Limitations](#limitations-and-gotchas).
+
+## Code Blocks
+
+Fenced code blocks go through a Blade view. The view doesn't render the code itself, it receives the rendered code
+block markup and decides what goes around it. Syntax highlighting is unaffected: whichever highlighter your site uses
+still renders the code, and the view wraps it.
+
+Indented code blocks are not affected.
+
+See [Advanced Markdown](advanced-markdown#code-block-filepaths) for the filepath label syntax, both the `// filepath:`
+comment and the `title` modifier on the fence.
+
+### View contract
+
+**View:** `hyde::components.markdown.code-block`
+
+| Variable    | Type                        | Description                                                                          |
+|-------------|-----------------------------|--------------------------------------------------------------------------------------|
+| `$contents` | `string`                    | The rendered code block markup, as your highlighter produced it. Echo with `{!! !!}`. |
+| `$language` | `string`/`null`             | The fence language, or `null` when the block declared none.                           |
+| `$label`    | `HtmlString`/`string`/`null`| The resolved label, or `null` when the block set none. An `HtmlString` when `markdown.allow_html` is enabled, so the label can contain links. |
+
+>danger `$contents` is finished markup, which is why the view echoes it unescaped. Do not re-escape it with `{{ }}` (you will see markup as text).
+
+The block view hands the label off to its own view, so you can restyle just the label without taking over the whole
+block.
+
+**View:** `hyde::components.markdown.code-block-label`
+
+| Variable | Type                  | Description                                               |
+|----------|-----------------------|-----------------------------------------------------------|
+| `$path`  | `HtmlString`/`string` | The label text, passed through from the block's `$label`.  |
+
+### Class hooks
+
+| Class                     | Targets                                    |
+|---------------------------|--------------------------------------------|
+| `hyde-code-block`         | The outer `
` wrapping the code block | +| `hyde-code-block-label` | The filepath label | + +### Customization example + +Say you want a header bar above the code, showing the language next to the label: + +```blade + +
+ @if($label || $language) +
+ {{ $label }} + {{ $language }} +
+ @endif + {!! $contents !!} +
+``` ## Terminal Blocks @@ -410,46 +469,6 @@ elements a divider rule, or render a self-linking anchor around the whole headin >warning The heading renderer post-processes the rendered output to tidy up empty attributes and collapse newlines. Keep your markup on the conservative side — deeply nested or whitespace-sensitive structures inside the heading tag can be affected. -## Code Block Filepath Labels - -A comment on the first line of a fenced code block becomes a filepath label: - -````markdown -```php -‎// filepath: hello-world.php -echo 'Hello World!'; -``` -```` - -```php -// filepath: hello-world.php -echo 'Hello World!'; -``` - -### View contract - -**View:** `hyde::components.filepath-label` - -| Variable | Type | Description | -|---------------------------|-----------------------|-----------------------------------------------------------------------------------| -| `$path` | `string\|HtmlString` | The label text. An `HtmlString` when `markdown.allow_html` is enabled, so the label can contain links. | -| `$highlightedByTorchlight`| `bool` | Whether the code block was highlighted by Torchlight, which needs slightly different positioning. | - -The shipped view hides the label on small screens to keep it from overlapping the code. If you would rather show it on -mobile as a block above the code, that's a view change: - -```blade - -@props(['path', 'highlightedByTorchlight' => false]) - - - Filepath: {{ $path }} - -``` - -This label is injected into the already-rendered HTML by a post-processor, which is why it is a plain fragment rather -than a wrapper around the code block. - ## Blade Component Blocks The blocks above are Hyde's own. The `blade component="name"` fenced block is the escape hatch that lets *any* component @@ -670,7 +689,7 @@ Coloured blockquotes are expanded line by line, before the Markdown parser runs. invisible `U+200E LEFT-TO-RIGHT MARK` character — it's the standard workaround for documenting the syntax without triggering it. -Blocks implemented as CommonMark extensions, like terminal blocks, do not have this problem. +Blocks implemented as CommonMark extensions, like code and terminal blocks, do not have this problem. ### Coloured blockquotes are single-line @@ -689,11 +708,11 @@ A published view is a copy, frozen at the version you published it from. Framewo markup, add a class hook, or pass a new variable will not reach it. Re-run `php hyde publish:views components` after major upgrades and diff your customizations against the new defaults. -### Torchlight changes the markup +### Your highlighter decides what `$contents` looks like -When Torchlight is enabled, code blocks are rendered by Torchlight rather than by CommonMark's default renderer. The -filepath label accounts for this via `$highlightedByTorchlight`, but a custom view that assumes one structure will look -wrong under the other. Test both if your site toggles Torchlight. +Torchlight and CommonMark's default renderer produce different markup for the same block, and a third-party +highlighter produces something different again. Your view receives whichever one as `$contents`, so a custom view that +digs into that markup expecting one structure will look wrong under another. Test both if your site toggles Torchlight. ## See Also diff --git a/packages/framework/resources/views/components/filepath-label.blade.php b/packages/framework/resources/views/components/filepath-label.blade.php deleted file mode 100644 index aaf8a1d88c7..00000000000 --- a/packages/framework/resources/views/components/filepath-label.blade.php +++ /dev/null @@ -1,5 +0,0 @@ -@props(['path', 'highlightedByTorchlight' => false]) - \ No newline at end of file diff --git a/packages/framework/resources/views/components/markdown/code-block-label.blade.php b/packages/framework/resources/views/components/markdown/code-block-label.blade.php new file mode 100644 index 00000000000..f7c3c045a58 --- /dev/null +++ b/packages/framework/resources/views/components/markdown/code-block-label.blade.php @@ -0,0 +1,2 @@ +@props(['path']) + diff --git a/packages/framework/resources/views/components/markdown/code-block.blade.php b/packages/framework/resources/views/components/markdown/code-block.blade.php new file mode 100644 index 00000000000..5e64792519c --- /dev/null +++ b/packages/framework/resources/views/components/markdown/code-block.blade.php @@ -0,0 +1,6 @@ +
+@isset($label) +@include('hyde::components.markdown.code-block-label', ['path' => $label]) +@endisset +{!! $contents !!} +
diff --git a/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php b/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php index bd080b6111e..26e5b9586b1 100644 --- a/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php +++ b/packages/framework/src/Framework/Concerns/Internal/SetsUpMarkdownConverter.php @@ -5,17 +5,25 @@ namespace Hyde\Framework\Concerns\Internal; use Hyde\Facades\Config; +use Hyde\Markdown\Extensions\Nodes\CodeBlock; +use Hyde\Markdown\Extensions\Processing\CodeBlockRenderer; +use Hyde\Markdown\Extensions\Processing\PrepareCodeBlocks; +use Hyde\Markdown\Extensions\Processing\WrapCodeBlocks; use Hyde\Markdown\Extensions\TerminalExtension; use Hyde\Markdown\Processing\BladeBlockProcessor; use Hyde\Markdown\Processing\BladeDownProcessor; use Hyde\Markdown\Processing\ShortcodeProcessor; -use Hyde\Markdown\Processing\CodeblockFilepathProcessor; use Hyde\Markdown\Processing\DynamicMarkdownLinkProcessor; +use League\CommonMark\Event\DocumentParsedEvent; +use League\CommonMark\Event\DocumentPreRenderEvent; use Torchlight\Commonmark\V2\TorchlightExtension; use function array_merge; use function in_array; +use const PHP_INT_MAX; +use const PHP_INT_MIN; + /** * Sets up the Markdown converter for the Markdown service. * @@ -55,6 +63,30 @@ public function initializeExtension(string $extensionClassName): void $this->converter->getEnvironment()->addExtension(new $extensionClassName()); } + /** + * Registered ahead of every extension, and at the highest priority, so that the code and info + * string a listener collects have already had the Hyde syntax taken out of them. Listeners + * sharing a priority run in registration order, so both of those matter. + */ + protected function prepareCodeBlocks(): void + { + $this->converter->getEnvironment()->addEventListener( + DocumentParsedEvent::class, new PrepareCodeBlocks(), PHP_INT_MAX + ); + } + + /** + * Hyde renders the view around a code block, not the code, which is left to whichever renderer + * the environment already had for the fence. So the fence is wrapped in a node of our own, last + * of all, once every listener has had the document in the shape it expects. + */ + protected function configureCodeBlockRenderer(): void + { + $this->converter->getEnvironment() + ->addEventListener(DocumentPreRenderEvent::class, new WrapCodeBlocks(), PHP_INT_MIN) + ->addRenderer(CodeBlock::class, new CodeBlockRenderer()); + } + protected function registerPreProcessors(): void { $enableBlade = Config::getBool('markdown.enable_blade', true); @@ -64,7 +96,6 @@ protected function registerPreProcessors(): void $this->registerPreProcessor(BladeDownProcessor::class, $enableBlade); $this->registerPreProcessor(ShortcodeProcessor::class); - $this->registerPreProcessor(CodeblockFilepathProcessor::class); } protected function registerPostProcessors(): void @@ -74,11 +105,6 @@ protected function registerPostProcessors(): void $this->registerPostProcessor(BladeBlockProcessor::class, $enableBlade); $this->registerPostProcessor(BladeDownProcessor::class, $enableBlade); - $this->registerPostProcessor( - CodeblockFilepathProcessor::class, - Config::getBool('markdown.features.codeblock_filepaths', true) - ); - $this->registerPostProcessor(DynamicMarkdownLinkProcessor::class); } diff --git a/packages/framework/src/Framework/Services/MarkdownService.php b/packages/framework/src/Framework/Services/MarkdownService.php index 45633b7edd9..0fcde12325d 100644 --- a/packages/framework/src/Framework/Services/MarkdownService.php +++ b/packages/framework/src/Framework/Services/MarkdownService.php @@ -87,11 +87,14 @@ protected function setupConverter(): void $this->converter = new MarkdownConverter($this->config); + $this->prepareCodeBlocks(); + foreach ($this->extensions as $extension) { $this->initializeExtension($extension); } $this->configureCustomHeadingRenderer(); + $this->configureCodeBlockRenderer(); $this->registerPreProcessors(); $this->registerPostProcessors(); @@ -124,6 +127,7 @@ protected function runPostProcessing(): void } } + /** @return array> */ public function getExtensions(): array { return $this->extensions; diff --git a/packages/framework/src/Markdown/Extensions/CodeBlockViewModel.php b/packages/framework/src/Markdown/Extensions/CodeBlockViewModel.php new file mode 100644 index 00000000000..139cdfc0ea8 --- /dev/null +++ b/packages/framework/src/Markdown/Extensions/CodeBlockViewModel.php @@ -0,0 +1,36 @@ +viewData())->render(); + } + + /** @return array{contents: string, language: ?string, label: \Illuminate\Support\HtmlString|string|null} */ + protected function viewData(): array + { + return [ + 'contents' => $this->contents, + 'language' => $this->language, + 'label' => $this->label, + ]; + } +} diff --git a/packages/framework/src/Markdown/Extensions/Concerns/ParsesFenceModifiers.php b/packages/framework/src/Markdown/Extensions/Concerns/ParsesFenceModifiers.php new file mode 100644 index 00000000000..fdfcfdb8119 --- /dev/null +++ b/packages/framework/src/Markdown/Extensions/Concerns/ParsesFenceModifiers.php @@ -0,0 +1,134 @@ +[\w-]+)=(?:"(?[^"]*)"|\'(?[^\']*)\')|(?\S+))(?=\s|$)/'; + + /** + * Tokenize the modifiers following the language, which are order-independent. + * + * @return array + */ + protected function tokenizeModifiers(string $info): array + { + preg_match_all(static::TOKEN_PATTERN, $info, $matches, PREG_SET_ORDER | PREG_UNMATCHED_AS_NULL); + + return $this->declaresLanguage($matches[0] ?? null) ? array_slice($matches, 1) : $matches; + } + + /** + * The first token is the language, which is not a modifier. A fence may open with a modifier + * instead, though, in which case it declares no language and every token is one. + * + * @param ?array{key: ?string, double: ?string, single: ?string, word: ?string} $token + */ + protected function declaresLanguage(?array $token): bool + { + return $token !== null && $token['word'] !== null && ! $this->looksLikeTitleModifier($token['word']); + } + + /** + * Take the title modifier out of an info string, leaving every other byte of it as it was, + * since the modifiers we don't know about belong to whichever extension does. + * + * Only whole tokens are taken, so a `title=` written inside another modifier's quoted value + * is left alone, being that modifier's business rather than a second title. + */ + protected function withoutTitleModifier(string $info): string + { + preg_match_all(static::TOKEN_PATTERN, $info, $matches, PREG_SET_ORDER | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE); + + foreach (array_reverse($matches) as $token) { + if ($token['key'][0] !== null && strtolower($token['key'][0]) === 'title') { + $info = $this->spliceToken($info, $token[0][1], strlen($token[0][0])); + } + } + + return $info; + } + + /** Takes the separator that followed the token with it, or the one before it at the end of a string. */ + protected function spliceToken(string $info, int $offset, int $length): string + { + $before = substr($info, 0, $offset); + $after = substr($info, $offset + $length); + + return ltrim($after) === $after ? rtrim($before) : $before.ltrim($after); + } + + /** + * Resolve the title modifier from a tokenized info string. + * + * @param array $tokens + * @param string $blockName The block being parsed, used in the error message. + */ + protected function parseTitleModifier(array $tokens, string $blockName): ?string + { + $title = null; + + foreach ($tokens as $token) { + if ($token['word'] === null) { + if (strtolower($token['key']) === 'title') { + $title = $token['double'] ?? $token['single']; + } + + continue; + } + + $this->assertTitleModifierIsNotMalformed($token['word'], $blockName); + } + + return $title; + } + + /** + * A modifier we don't know about may mean something in a future version, so it is ignored. + * A malformed title, on the other hand, is a typo we should not silently discard. + */ + protected function assertTitleModifierIsNotMalformed(string $word, string $blockName): void + { + if ($this->looksLikeTitleModifier($word)) { + throw new InvalidArgumentException(sprintf( + 'Invalid %s title [%s]. Expected syntax like title="My title".', $blockName, $word + )); + } + } + + protected function looksLikeTitleModifier(string $word): bool + { + $normalized = strtolower($word); + + return $normalized === 'title' || str_starts_with($normalized, 'title='); + } +} diff --git a/packages/framework/src/Markdown/Extensions/Nodes/CodeBlock.php b/packages/framework/src/Markdown/Extensions/Nodes/CodeBlock.php new file mode 100644 index 00000000000..bf3bfc3e6bd --- /dev/null +++ b/packages/framework/src/Markdown/Extensions/Nodes/CodeBlock.php @@ -0,0 +1,18 @@ +firstChild() instanceof FencedCode) { + throw new InvalidArgumentException(sprintf('Incompatible node type: %s', get_class($node))); + } + + /** @var \League\CommonMark\Extension\CommonMark\Node\Block\FencedCode $fence */ + $fence = $node->firstChild(); + + return (new CodeBlockViewModel( + contents: $childRenderer->renderNodes([$fence]), + language: ($fence->getInfoWords()[0] ?? '') ?: null, + label: $fence->data->get(PrepareCodeBlocks::LABEL_KEY, null), + ))->render(); + } +} diff --git a/packages/framework/src/Markdown/Extensions/Processing/PrepareCodeBlocks.php b/packages/framework/src/Markdown/Extensions/Processing/PrepareCodeBlocks.php new file mode 100644 index 00000000000..c6a29a7aff9 --- /dev/null +++ b/packages/framework/src/Markdown/Extensions/Processing/PrepareCodeBlocks.php @@ -0,0 +1,125 @@ + Each comment opener a filepath label can use, and the delimiter that closes it. */ + protected const COMMENT_SYNTAXES = [ + '//' => null, + '#' => null, + '/*' => '*/', + '', + ]; + + public function __invoke(DocumentParsedEvent $event): void + { + if (! Config::getBool('markdown.features.codeblock_filepaths', true)) { + return; + } + + foreach ($event->getDocument()->iterator() as $node) { + // Terminal blocks have their own syntax on the fence, which is not ours to read. + if ($node instanceof FencedCode && ! TransformTerminalBlocks::claims($node)) { + $this->prepare($node); + } + } + } + + protected function prepare(FencedCode $node): void + { + $info = $node->getInfo() ?? ''; + + $modifier = $this->parseTitleModifier($this->tokenizeModifiers($info), 'code block'); + + // Taken out of the code whether or not it is the one labelling the block. + $comment = $this->extractCommentLabel($node); + + $node->setInfo($this->withoutTitleModifier($info)); + + // The modifier is the more explicit of the two, so it wins when a block uses both. + $label = $modifier ?? $comment; + + if ($label !== null) { + $node->data->set(static::LABEL_KEY, $this->formatLabel($label)); + } + } + + protected function extractCommentLabel(FencedCode $node): ?string + { + $lines = explode("\n", $node->getLiteral()); + + if (! preg_match($this->commentPattern(), $lines[0] ?? '', $matches)) { + return null; + } + + array_shift($lines); + + // A blank line after the comment was only there to separate it from the code. + if (trim($lines[0] ?? '') === '') { + array_shift($lines); + } + + $node->setLiteral(implode("\n", $lines)); + + return $this->withoutCommentTerminator(trim($matches['label']), $matches['opener']); + } + + protected function commentPattern(): string + { + $openers = implode('|', array_map( + fn (string $opener): string => preg_quote($opener, '/'), array_keys(static::COMMENT_SYNTAXES) + )); + + return sprintf('/^(?%s) filepath:? (?