diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ab9beac --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +- Commit after each change. +- Use conventional commit syntax. Include a pargraph in the commit message dsecribing in detail what you did. +- Always use Playwright to check your changes. diff --git a/content/_index.md b/content/_index.md index ada9ed9..8e951ac 100644 --- a/content/_index.md +++ b/content/_index.md @@ -1,107 +1,111 @@ --- -Title: FizzBee – Design Reliable, Scalable Distributed Systems -Description: Designing a distributed system? FizzBee makes it easy to model, visualize, and - validate your design—catching flaws before you code. The easiest-ever formal methods, - built for developers. -#geekdocNav: false -geekdocAlign: center +Title: FizzBee - Design Reliable Distributed Systems +Description: FizzBee helps engineers model, visualize, validate, and test distributed system designs before implementation. +geekdocNav: false +geekdocAlign: left geekdocAnchor: false geekdocBreadcrumb: false --- - -{{< columns >}} - -### Analyze & Visualize Your Design - -- Specify your system design as code. -- FizzBee automatically: - - Generates sequence & block diagrams - - Checks for behavioral correctness - - Analyzes performance metrics - -<---> - -### Generate & Run Tests - -- Specify how the design maps to your code. -- FizzBee automatically: - - Exhaustively tests every behavior - - Simulates faults and edge cases - - Validates concurrency and integration - -{{< /columns >}} - -{{% rawhtml %}} - -
-

What Engineers Are Saying

-
-
-

"FizzBee upholds the rigor of TLA+ while making formal verification simpler and more accessible for engineers. By leveraging Python, it reduces the learning curve, with the potential to surpass TLA+ as the go-to tool for engineers."

- — Jack Vanlightly, Principal Technologist, Confluent -
-
-

"I discovered FizzBee while designing the manifest for SlateDB, an embedded key-value store. FizzBee’s concepts were easy to grasp in hours, and by the next day, I had a working spec that uncovered a real concurrency bug!"

- — Vignesh Chandramohan, Engineering Manager, Doordash +{{< rawhtml >}} +
+
+
+
+

Design reliable software

+

Specify your system design in a Pythonic modeling language. Let FizzBee verify your design and generate a test harness for your code.

+ -
-

"FizzBee’s Python-like syntax made it easy to learn and use, unlike other formal methods languages. I picked it up over a weekend and successfully modeled our streaming ingestion platform to identify correctness bugs. It's intuitive and incredibly effective!"

- — Franklyn D'souza
Staff Software Developer, Shopify
-
-
-

"FizzBee cured my fear of formal methods after struggling with TLA+ years ago. It's surprisingly easy to learn and a refreshing experience—something I never thought I could master. Universities should teach FizzBee!"

- — Li Yazhou
Tech Lead, Cloud Platform, Databend
+
+
-
-{{% /rawhtml %}} - -## Why Model Your System? - -{{< figure src="https://storage.googleapis.com/fizzbee-public/website/homepage/what_fizzbee_does_graph.png" alt="Why Model Your System" caption="Modeling helps you explore edge cases, eliminate ambiguity, catch bugs early, and iterate with confidence. In addition to validating the design, you can verify the implementation" >}} +
- -## Try Fizz - -Read the [quick start guide](/design/tutorials/getting-started/) to learn how to write your first FizzBee model -or comb through the [examples](/design/examples/) -or tinker with the [FizzBee online playground](/play) - -### An example: Travel Booking Service using Two Phase Commit - -{{% fizzbee %}} ---- -deadlock_detection: false ---- -""" -How does a travel booking service ensure that all components in the itinerary are -booked or nothing is booked? Using Two Phase Commit protocol. - -Phase 1: The Coordinator (Booking Service) asks all participants (Airline, Hotel, Car Rental) to place a hold on -the resource. -Phase 2: - a. If all the participants successfully placed a hold on the resource, - the Coordinator (Booking Service) asks all participants to commit the booking. - b. If any of the participants fail to place a hold, - the Coordinator (Booking Service) asks all participants to abort the booking. - -The core logic is in the Coordinator.Checkout action -""" - -role Participant: +
+
+

Look familiar?

+

FizzBee uses Starlark, the same Python dialect as Bazel. Your models stay both human-readable and AI-friendly.

+ Run in playground +
+
+
+
+ travel_booking.fizz +
+
+
role Participant:
   action Init:
       self.status = "init"
- 
+
   func placehold():
       vote = any ["accepted", "aborted"]
-      self.status = vote 
+      self.status = vote
       return self.status
- 
+
   func finalize(decision):
       self.status = decision
 
@@ -109,7 +113,7 @@ role Participant:
 role Coordinator:
     action Init:
         self.status = "init"
- 
+
     action Checkout:
         require(self.status == "init")
         self.status = "inprogress"
@@ -118,18 +122,18 @@ role Coordinator:
               if vote == "aborted":
                   self.finalize("aborted")
                   return
- 
+
         self.finalize("committed")
- 
- 
+
+
     func finalize(decision):
         self.status = decision
         for p in participants:
             p.finalize(decision)
 
- 
+
 NUM_PARTICIPANTS=2
- 
+
 action Init:
     coordinator = Coordinator()
     participants = []
@@ -141,8 +145,173 @@ always assertion ParticipantsConsistent:
     for p2 in participants:
       if p1.status == 'committed' and p2.status == 'aborted':
         return False
-  return True
+  return True
+
+
+
+
-{{% /fizzbee %}} +
+
+

Verify your designs

+

Run the model checker and get a concrete result: explored states, generated artifacts, failed assumptions, and deadlock scenarios.

+ Read the model checking guide +
+
+
+
+ $ fizz --output-dir /tmp/fizzbee-home-run /tmp/travel_booking_home.fizz +
+
DeprecationWarning: 'VAR = any COLLECTION' is deprecated, use 'VAR = oneof COLLECTION' instead
+Model checking /tmp/travel_booking_home.json
+configFileName: /tmp/fizz.yaml
+fizz.yaml not found. Using default options
+StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
+Nodes: 32, queued: 0, elapsed: 8.489375ms
+Time taken for model checking: 8.509541ms
+Writen graph dotfile: /tmp/fizzbee-home-run/graph.dot
+Writen communication diagram dotfile: /tmp/fizzbee-home-run/communication.dot
+DEADLOCK detected
+FAILED: Model checker failed
+------
+Init
+--
+state: {"coordinator":"role Coordinator#0","participants":["role Participant#0","role Participant#1"]}
+Coordinator#0: fields(status = "init")
+Participant#0: fields(status = "init")
+Participant#1: fields(status = "init")
+------
+Coordinator#0.Checkout
+--
+Coordinator#0: fields(status = "inprogress")
+Participant#0: fields(status = "init")
+Participant#1: fields(status = "init")
+------
+crash
+--
+Coordinator#0: fields(status = "inprogress")
+Participant#0: fields(status = "init")
+Participant#1: fields(status = "init")
+------
+Writen graph dotfile: /tmp/fizzbee-home-run/error-graph.dot
+Writen error states as html: /tmp/fizzbee-home-run/error-states.html
+
+
+
+
+
+

Visualize complexity

+

The same run emits graph data for the state explorer. The error graph shows the short path from Init to Checkout to the crash state.

+ Explore visualizations +
+
+
+ FizzBee state graph for the travel booking model +
+
+
+
+
+

Generate Test Harnesses

+

Convert your model into a test harness for your production code. FizzBee offers Go, Java, and Rust harness support. Connect the harness and let FizzBee drive state exploration.

+ Read the testing guide +
+
+
+
+ checkout_mbt_test.rs +
+
use async_trait::async_trait;
+use fizzbee_mbt::{
+    run_mbt_test,
+    traits::{DispatchModel, Model},
+    types::{Arg, RoleId},
+    value::Value,
+    TestOptions,
+};
+
+struct BookingHarness {
+    service: TravelBookingService,
+}
+
+#[async_trait]
+impl Model for BookingHarness {
+    async fn init(&mut self) -> Result<(), fizzbee_mbt::error::MbtError> {
+        self.service.reset().await?;
+        Ok(())
+    }
+
+    async fn cleanup(&mut self) -> Result<(), fizzbee_mbt::error::MbtError> {
+        self.service.close().await?;
+        Ok(())
+    }
+}
+
+#[async_trait]
+impl DispatchModel for BookingHarness {
+    async fn execute(
+        &self,
+        role_id: &RoleId,
+        function_name: &str,
+        args: &[Arg],
+    ) -> Result<Value, fizzbee_mbt::error::MbtError> {
+        match (role_id.role_name.as_str(), function_name) {
+            ("Coordinator", "Checkout") => self.service.checkout(args).await,
+            ("Participant", "placehold") => self.service.placehold(role_id.index).await,
+            _ => Ok(Value::None),
+        }
+    }
+
+    fn get_roles(&self) -> Result<Vec<RoleId>, fizzbee_mbt::error::MbtError> {
+        Ok(vec![RoleId { role_name: "Coordinator".into(), index: 0 }])
+    }
+}
+
+#[test]
+fn checkout_matches_model() {
+    run_mbt_test(
+        BookingHarness::new(),
+        TestOptions { max_actions: Some(12), max_parallel_runs: Some(32), ..Default::default() },
+    ).unwrap();
+}
+
+
+
+ +
+
+

Formal methods made easy

+
+
+
+
FizzBee upholds the rigor of TLA+ while making formal verification simpler and more accessible for engineers. By leveraging Python, it reduces the learning curve, with the potential to surpass TLA+ as the go-to tool for engineers.
+
Jack Vanlightly, Principal Technologist, Confluent
+
+
+
I discovered FizzBee while designing the manifest for SlateDB, an embedded key-value store. FizzBee's concepts were easy to grasp in hours, and by the next day, I had a working spec that uncovered a real concurrency bug!
+
Vignesh Chandramohan, Engineering Manager, DoorDash
+
+
+
FizzBee's Python-like syntax made it easy to learn and use, unlike other formal methods languages. I picked it up over a weekend and successfully modeled our streaming ingestion platform to identify correctness bugs. It's intuitive and incredibly effective!
+
Franklyn D'souza, Staff Software Developer, Shopify
+
+
+
FizzBee cured my fear of formal methods after struggling with TLA+ years ago. It's surprisingly easy to learn and a refreshing experience, something I never thought I could master. Universities should teach FizzBee!
+
Li Yazhou, Tech Lead, Cloud Platform, Databend
+
+
+
+ +
+
+

Install FizzBee today

+

Tap Homebrew, install the CLI, and add AI agent skills.

+
+
brew tap fizzbee-io/fizzbee
+brew install fizzbee
+fizz install-skills
+
+ +{{< /rawhtml >}} diff --git a/data/menu/more.yaml b/data/menu/more.yaml deleted file mode 100644 index 8e655f4..0000000 --- a/data/menu/more.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -more: - - name: Blog - ref: "/posts" - - name: "GitHub" - ref: "https://github.com/fizzbee-io/fizzbee" - external: true - icon: "gdoc_github" - - name: "Discord" - ref: "https://discord.gg/f3mk6eb9Bc" - external: true - icon: "discord" diff --git a/hugo.toml b/hugo.toml index 7c8084f..3d37460 100644 --- a/hugo.toml +++ b/hugo.toml @@ -18,6 +18,7 @@ enableRobotsTXT = true geekdocLogo = "bee-left-to-right-512x512.png" geekdocPrivacyPolicy = "/privacy.html" geekdocTagsToMenu = false + geekdocDarkModeToggle = false # Needed for mermaid shortcodes [markup] diff --git a/layouts/index.html b/layouts/index.html new file mode 100644 index 0000000..dd209c2 --- /dev/null +++ b/layouts/index.html @@ -0,0 +1,5 @@ +{{ define "main" }} +
+ {{ partial "utils/content" . }} +
+{{ end }} diff --git a/layouts/partials/consent.html b/layouts/partials/consent.html index 63ec363..3f4f6df 100644 --- a/layouts/partials/consent.html +++ b/layouts/partials/consent.html @@ -1,36 +1,3 @@ - + {{ partial "consent" . }} diff --git a/layouts/partials/site-header.html b/layouts/partials/site-header.html new file mode 100644 index 0000000..2409b79 --- /dev/null +++ b/layouts/partials/site-header.html @@ -0,0 +1,66 @@ +
+
+ {{ if .MenuEnabled }} + + {{ end }} + +
+ + + + {{ .Root.Site.Title }} + + +
+ +
+ + {{ if .Root.Site.Data.menu.extra.header }} + {{ partial "menu-extra" (dict "current" .Root "source" .Root.Site.Data.menu.extra.header "target" "header") }} + {{ end }} + + + + + {{ i18n "button_homepage" }} + + + + + + {{ partial "language" .Root }} + + + + + + +
+
+
diff --git a/layouts/posts/list.html b/layouts/posts/list.html new file mode 100644 index 0000000..2103ade --- /dev/null +++ b/layouts/posts/list.html @@ -0,0 +1,33 @@ +{{ define "main" }} + {{ range .Paginator.Pages }} +
+
+

+ {{ partial "utils/title" . }} +

+
+
+ {{ .Summary }} +
+
+ {{ if .Truncated }} + + {{ i18n "posts_read_more" }} + gdoc_arrow_right_alt + + {{ end }} +
+ + +
+ {{ end }} + {{ partial "pagination.html" . }} +{{ end }} diff --git a/layouts/posts/single.html b/layouts/posts/single.html new file mode 100644 index 0000000..ee767ca --- /dev/null +++ b/layouts/posts/single.html @@ -0,0 +1,13 @@ +{{ define "main" }} + {{ partial "page-header" . }} + +
+
+

{{ partial "utils/title" . }}

+ +
+ {{ partial "utils/content" . }} +
+{{ end }} diff --git a/layouts/shortcodes/fizzbee.html b/layouts/shortcodes/fizzbee.html index 8b8aa52..84f70dd 100644 --- a/layouts/shortcodes/fizzbee.html +++ b/layouts/shortcodes/fizzbee.html @@ -1,19 +1,10 @@ -{{ if not (.Page.Scratch.Get "jhandler_defined") }} - {{ .Page.Scratch.Set "jhandler_defined" true }} - -{{ end }} +{{ $id := printf "fizzbee-%d" .Ordinal }} +{{ $playURL := "/play" | relURL }} +{{ $targetURL := printf "%s#embed-%s" $playURL $id }} +{{ $content := trim .Inner "\r\n" }}
-Run in playground +Run in playground {{ printf "%s%s%s" "{{< highlight Shell \"linenos=python\" >}}" .Inner "{{< /highlight >}}" | markdownify }}
{{ with .Site.BaseURL }}{{ end }} diff --git a/static/custom.css b/static/custom.css index 54ce48e..773596b 100644 --- a/static/custom.css +++ b/static/custom.css @@ -1,131 +1,1638 @@ +:root, +:root[color-theme="light"], +:root[color-theme="dark"] { + --fb-ink: #16201c; + --fb-muted: #58645f; + --fb-soft: #f6f8f4; + --fb-panel: #ffffff; + --fb-line: #dce4dd; + --fb-honey: #f4b23f; + --fb-honey-dark: #7c4a03; + --fb-honey-link: #a96300; + --fb-honey-hover: #f6c257; + --fb-rust: #b75a32; + --header-background: #ffffff; + --header-font-color: var(--fb-ink); + --body-background: #ffffff; + --body-font-color: var(--fb-ink); + --mark-color: #ffe08a; + --button-background: var(--fb-honey); + --button-border-color: var(--fb-honey); + --link-color: var(--fb-honey-link); + --link-color-visited: var(--fb-honey-link); + --hint-link-color: var(--fb-honey-link); + --hint-link-color-visited: var(--fb-honey-link); + --accent-color-dark: #dfca9f; + --accent-color: #ead8b4; + --accent-color-lite: #fbf4e6; + --control-icons: #6e7b75; + --footer-background: #17211d; + --footer-font-color: #f8fbf8; + --footer-link-color: #f4c15d; + --footer-link-color-visited: #f4c15d; + --code-background: #f6f8f4; + --code-accent-color: #ead8b4; + --code-accent-color-lite: #fbf4e6; + --code-font-color: #203129; + --code-copy-background: #ffffff; + --code-copy-font-color: #58645f; + --code-copy-border-color: #cad6ce; + --header-font-family: "Inter", sans-serif; + --body-font-family: "Inter", sans-serif; + --code-font-family: "JetBrains Mono", monospace; + --code-max-height: 60rem; + --fb-home-max-width: 1140px; + --fb-home-gutter: 40px; +} + +@media (prefers-color-scheme: dark) { + :root:not([color-theme]) { + --header-background: #ffffff; + --header-font-color: var(--fb-ink); + --body-background: #ffffff; + --body-font-color: var(--fb-ink); + --link-color: var(--fb-honey-link); + --link-color-visited: var(--fb-honey-link); + --accent-color: #ead8b4; + --accent-color-lite: #fbf4e6; + --footer-background: #17211d; + --footer-font-color: #f8fbf8; + --code-background: #f6f8f4; + --code-font-color: #203129; + } +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + background: + linear-gradient(90deg, rgba(22, 32, 28, 0.035) 1px, transparent 1px), + linear-gradient(180deg, rgba(22, 32, 28, 0.035) 1px, transparent 1px), + var(--body-background); + background-size: 44px 44px; +} + .cookie-consent-banner { display: none; position: fixed; - bottom: 0; - left: 0; - right: 0; - background-color: #f8f9fa; - box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); - color: black; - padding: 15px; + bottom: 1rem; + left: 50%; + width: min(720px, calc(100% - 2rem)); + transform: translateX(-50%); + background-color: #ffffff; + border: 1px solid var(--fb-line); + border-radius: 8px; + box-shadow: 0 18px 48px rgba(22, 32, 28, 0.16); + color: var(--fb-ink); + padding: 1rem 1.15rem; font-size: 14px; - text-align: center; + text-align: left; z-index: 1000; } +.cookie-consent-banner h3 { + margin: 0 0 0.5rem; + font-size: 1rem; + line-height: 1.3; +} -.gdoc-markdown { - font-size: 1.2rem; - line-height: 1.8; +.cookie-consent-banner p { + margin: 0; + color: var(--fb-muted); + line-height: 1.5; } -.gdoc-markdown pre, .gdoc-markdown code { - font-size: 1rem; - line-height: 1.6; + +.cookie-consent-banner button { + min-height: 2.2rem; + margin-top: 0.8rem; + border: 1px solid var(--fb-honey); + border-radius: 6px; + background: var(--fb-honey); + color: var(--fb-ink); + font-weight: 700; } -.gdoc-columns__content ul { - text-align: left; +#btn-customize { + position: absolute; + left: -9999px; } -:root { - --testimonial-bg-light: #f9f9f9; - --testimonial-bg-dark: #2a2a2a; - --testimonial-text-color-light: #333; - --testimonial-text-color-dark: #ddd; - --testimonial-quote-color: #4ec58a; +#consent-options { + display: none; + margin-top: 0.8rem; } -/* Light mode theming */ -:root[color-theme="light"] { - --header-background: #243342; - --body-background: #ffffff; - --body-font-color: #111; +#btn-customize:checked ~ #consent-options { + display: grid; + gap: 0.5rem; +} - --testimonial-text-person: #555; +.customize-link { + display: inline-flex; + margin-left: 0.75rem; + color: var(--link-color); + cursor: pointer; + font-weight: 700; + text-decoration: underline; + text-underline-offset: 0.16em; } -@media (prefers-color-scheme: light) { - :root:not([color-theme]) { - --header-background: #243342; - --body-background: #ffffff; - --body-font-color: #111; - --testimonial-text-person: #555; - } +.gdoc-header { + position: sticky; + top: 0; + z-index: 20; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + background: #101712; + box-shadow: 0 8px 24px rgba(22, 32, 28, 0.18); } -/* Dark mode theming */ -:root[color-theme="dark"] { - --header-background: #243342; - --body-background: #1e1e1e; - --body-font-color: #e0e0e0; +.gdoc-header .container { + max-width: 1180px; + min-height: 68px; +} - --testimonial-text-person: #aaa; +.gdoc-brand__img { + width: 42px; + height: 42px; + object-fit: contain; + margin-right: 0.65rem; } -:root[color-theme="dark"] .testimonial { - background-color: var(--testimonial-bg-dark); - color: var(--testimonial-text-color-dark); + +.gdoc-brand__title { + font-size: 1.15rem; + font-weight: 800; } -@media (prefers-color-scheme: dark) { - :root:not([color-theme]) { - --header-background: #243342; - --body-background: #1e1e1e; - --body-font-color: #e0e0e0; +.gdoc-header__link, +.gdoc-header__link:visited { + color: #eef7f0; + text-decoration: none; +} + +.gdoc-header__link:hover { + color: #f4c15d; + background: none; +} + +.gdoc-header .gdoc-icon { + color: #eef7f0; +} + +.gdoc-menu-header__items { + gap: 0.35rem; + align-items: center; +} + +#gdoc-color-theme { + display: none; +} + +.wrapper > main.container { + max-width: 1180px; +} - --testimonial-text-person: #aaa; +.wrapper > main.container:has(.fb-home-article) { + padding-bottom: 0; +} + +.gdoc-nav { + padding-top: 1.5rem; +} + +.gdoc-nav nav { + border-right: 1px solid var(--fb-line); +} + +@media screen and (max-width: 41rem) { + .gdoc-nav nav { + border-right: 0; } - :root:not([color-theme]) .testimonial { - background-color: var(--testimonial-bg-dark); - color: var(--testimonial-text-color-dark); +} + +.gdoc-nav__entry, +.gdoc-nav__entry:visited { + color: #35423c; +} + +.gdoc-nav__entry:hover, +.gdoc-nav__entry.is-active { + color: var(--fb-honey-link); + text-decoration: none; +} + +.gdoc-search__input { + background: #ffffff; +} + +.gdoc-page { + padding: 2.5rem 0 4rem; +} + +.gdoc-page:has(.fb-home-article) { + padding-bottom: 0; +} + +.gdoc-page:has(.fb-home-article) .gdoc-page__footer { + display: none; +} + +@media (min-width: 41.001rem) { + .wrapper > main.container > .gdoc-nav + .gdoc-page { + margin-left: clamp(2rem, 3vw, 3rem); } } - /* Load Inter from Google Fonts in your HTML */ -/*@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap");*/ +.gdoc-markdown { + color: var(--fb-ink); + font-size: 1.04rem; + line-height: 1.75; +} -/* Font family override */ -:root { - --header-font-family: "Inter", sans-serif; - --body-font-family: "Inter", sans-serif; - --code-font-family: "JetBrains Mono", monospace; +.gdoc-markdown h1, +.gdoc-markdown h2, +.gdoc-markdown h3 { + color: var(--fb-ink); + line-height: 1.15; + letter-spacing: 0; +} - --code-max-height: 60rem; +.gdoc-markdown h1 { + font-size: 2.65rem; + margin: 0 0 1.3rem; } -.testimonial-slider { - display: flex; - overflow-x: auto; - gap: 2rem; - scroll-snap-type: x mandatory; - padding-bottom: 1rem; +.gdoc-markdown h2 { + font-size: 1.95rem; + margin-top: 2.6rem; +} + +.gdoc-markdown h3 { + font-size: 1.25rem; +} + +.gdoc-markdown .gdoc-page__anchorwrap { + align-items: center; +} + +.gdoc-markdown .gdoc-page__anchorwrap > :is(h1, h2, h3) { + margin-top: 0; + margin-bottom: 0; +} + +.gdoc-markdown .gdoc-page__anchorwrap:has(> h1) { + margin: 0 0 1.3rem; } -.testimonial { - flex: 0 0 450px; - background-color: var(--testimonial-bg-light); - color: var(--testimonial-text-color-light); - padding: 1.5rem 2rem; +.gdoc-markdown .gdoc-page__anchorwrap:has(> h2) { + margin: 2.6rem 0 calc(0.83 * 1.95rem); +} + +.gdoc-markdown .gdoc-page__anchorwrap:has(> h3) { + margin: 1.25rem 0; +} + +.gdoc-markdown .gdoc-page__anchor { + line-height: 1; +} + +.gdoc-markdown a { + color: var(--link-color); + text-decoration-thickness: 1px; + text-underline-offset: 0.18em; +} + +.gdoc-markdown pre, +.gdoc-markdown code { + border-radius: 6px; + font-size: 0.94rem; + line-height: 1.65; +} + +.gdoc-markdown pre { + border: 1px solid var(--code-accent-color); +} + +.gdoc-page__header { + border: 1px solid var(--fb-line); border-radius: 8px; - box-shadow: 0 4px 12px rgba(0,0,0,0.05); - scroll-snap-align: start; - font-size: 1rem; + background: rgba(255, 255, 255, 0.72); +} + +.gdoc-post { + border-top: 0; + padding: 0; +} + +.gdoc-post + .gdoc-post { + margin-top: 2.75rem; + padding-top: 2.75rem; + border-top: 1px solid var(--fb-line); +} + +.gdoc-post__title { + margin: 0 0 1.3rem; + color: var(--fb-ink); + font-size: 2.65rem; + font-weight: 800; + line-height: 1.15; + letter-spacing: 0; +} + +.gdoc-post__title a, +.gdoc-post__title a:visited { + color: inherit; + text-decoration: none; + border-bottom: 0; +} + +.gdoc-post__title a:hover, +.gdoc-post__title a:focus-visible { + color: var(--fb-honey-link); + background: none; + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.14em; +} + +.gdoc-post__summary > :first-child { + margin-top: 0; +} + +.gdoc-post__readmore { + margin: 1.45rem 0 1.75rem; +} + +.gdoc-post__readmore a, +.gdoc-post__readmore a:visited { + color: var(--fb-honey-link); + font-weight: 700; + text-decoration: none; +} + +.gdoc-post__readmore a:hover, +.gdoc-post__readmore a:focus-visible { + background: none; + text-decoration: underline; + text-underline-offset: 0.16em; +} + +.gdoc-post__meta { + gap: 0.45rem 0.7rem; + color: var(--fb-muted); + font-size: 0.95rem; + line-height: 1.4; +} + +.gdoc-post__meta > span { + margin: 0; +} + +.gdoc-post__meta span svg.gdoc-icon, +.gdoc-post__meta svg.gdoc-icon { + width: 1rem; + height: 1rem; + margin-left: 0; + color: var(--fb-muted); +} + +.gdoc-post__meta .gdoc-button { + margin: 0; + border-color: #ead8b4; + border-radius: 6px; + background: #fbf4e6; + color: var(--fb-ink); +} + +.gdoc-post__meta .gdoc-button__link { + padding: 0.14rem 0.48rem; +} + +.gdoc-post__meta .gdoc-button:hover { + border-color: var(--fb-honey); + background: #fff3d4; + color: var(--fb-ink); +} + +.gdoc-post__meta--head { + margin-bottom: 2rem; +} + +.fb-home-article { + overflow: visible; +} + +.fb-home { + width: 100vw; + margin-left: calc(50% - 50vw); + margin-top: calc(-2.5rem - 1.25rem); + color: var(--fb-ink); +} + +.fb-home p { + margin: 0; +} + +.fb-hero { + position: relative; + overflow: hidden; + min-height: min(calc(58svh - 68px), 420px); + display: grid; + align-items: center; + background: #17211d; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.fb-hero::before { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px), + linear-gradient(180deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px); + background-size: 54px 54px; + mask-image: linear-gradient(90deg, transparent 0, #000 18%, #000 100%); + z-index: 0; + pointer-events: none; +} + +.fb-hero__inner { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(400px, 520px); + gap: clamp(2rem, 6vw, 5rem); + align-items: center; + position: relative; + z-index: 1; + min-width: 0; + width: min(var(--fb-home-max-width), calc(100% - var(--fb-home-gutter))); + margin: 0 auto; + padding: 3rem 0 3.25rem; +} + +.fb-hero h1 { + max-width: 560px; + margin: 0; + color: #f8fbf8; + font-size: 4.35rem; + line-height: 1; + font-weight: 800; + letter-spacing: 0; +} + +.fb-home .fb-hero__lead { + max-width: 520px; + margin-top: 1.65rem; + color: #c6d4cc; + font-size: 1.08rem; line-height: 1.6; +} + +.fb-hero__visual { position: relative; + z-index: 2; + min-width: 0; } -.testimonial::before { - content: "\201C"; /* Fancy left quote */ - font-size: 3rem; - color: var(--testimonial-quote-color); +.fb-orbit-visual { + position: relative; + width: 100%; + height: 300px; + overflow: hidden; + isolation: isolate; + animation: fb-rise 700ms ease both; +} + +.fb-orbit-field { + position: relative; + width: 100%; + height: 100%; +} + +.fb-orbit-field::before, +.fb-orbit-field::after { + content: ""; + position: absolute; + pointer-events: none; +} + +.fb-orbit-field::before { + inset: 8% 2% 4%; + background: + radial-gradient(circle at 50% 48%, rgba(244, 193, 93, 0.18), transparent 24%), + radial-gradient(circle at 54% 52%, rgba(244, 178, 63, 0.2), transparent 38%), + radial-gradient(circle at 50% 50%, rgba(248, 251, 248, 0.08), transparent 62%); + filter: blur(8px); + opacity: 0.9; + z-index: -2; +} + +.fb-orbit-field::after { + inset: 18% 7% 12%; + border: 1px solid rgba(248, 251, 248, 0.06); + border-radius: 999px; + background: + linear-gradient(90deg, rgba(248, 251, 248, 0.035) 1px, transparent 1px), + linear-gradient(180deg, rgba(248, 251, 248, 0.035) 1px, transparent 1px); + background-size: 28px 28px; + mask-image: radial-gradient(ellipse at center, #000 42%, transparent 72%); + opacity: 0.74; + z-index: -3; +} + +.fb-orbit-ring { + position: absolute; + top: 50%; + left: 50%; + border: 1px solid rgba(248, 251, 248, 0.16); + border-radius: 50%; + transform: translate(-50%, -50%); +} + +.fb-orbit-ring--outer { + width: 98%; + height: 76%; + border-color: rgba(244, 193, 93, 0.22); + transform: translate(-50%, -50%) rotate(-10deg); +} + +.fb-orbit-ring--middle { + width: 76%; + height: 55%; + border-color: rgba(244, 178, 63, 0.2); + transform: translate(-50%, -50%) rotate(16deg); +} + +.fb-orbit-ring--inner { + width: 46%; + height: 34%; + border-color: rgba(248, 251, 248, 0.18); + transform: translate(-50%, -50%) rotate(-24deg); +} + +.fb-orbit-bee { + position: absolute; + top: 48%; + left: 51%; + width: clamp(8.8rem, 18vw, 11.4rem); + max-width: 46%; + aspect-ratio: 224 / 144; + overflow: visible; + transform: translate(-50%, -50%) rotate(-4deg); + filter: drop-shadow(0 26px 28px rgba(3, 9, 7, 0.38)); + animation: fb-hover 3.6s ease-in-out infinite; + z-index: 3; +} + +.fb-orbit-bee__body-outline { + fill: none; + stroke: rgba(16, 23, 18, 0.5); + stroke-width: 2.4; +} + +.fb-orbit-bee__stinger { + fill: #17180f; + stroke: rgba(244, 193, 93, 0.42); + stroke-width: 2; + stroke-linejoin: round; +} + +.fb-orbit-bee__head { + fill: url(#fb-bee-head-gold); + stroke: rgba(248, 251, 248, 0.1); + stroke-width: 1.4; +} + +.fb-orbit-bee__eye { + fill: url(#fb-bee-eye); +} + +.fb-orbit-bee__eye-shine { + fill: rgba(248, 251, 248, 0.86); +} + +.fb-orbit-bee__wing { + fill: url(#fb-bee-wing); + stroke: rgba(248, 251, 248, 0.32); + stroke-width: 1.4; +} + +.fb-orbit-bee__wing--rear { + opacity: 0.42; +} + +.fb-orbit-bee__wing--front { + opacity: 0.68; +} + +.fb-orbit-bee__wing-vein { + fill: none; + stroke: rgba(248, 251, 248, 0.22); + stroke-width: 1.5; + stroke-linecap: round; +} + +.fb-orbit-bee__leg, +.fb-orbit-bee__antenna { + fill: none; + stroke: #6f540f; + stroke-width: 6; + stroke-linecap: round; + stroke-linejoin: round; +} + +.fb-orbit-bee__leg { + stroke: #2b2107; +} + +.fb-orbit-bee__leg--far { + opacity: 0.64; +} + +.fb-orbit-bee__antenna { + stroke: rgba(244, 193, 93, 0.78); + stroke-width: 5; +} + +.fb-state-dot { + position: absolute; + width: 4.45rem; + aspect-ratio: 1; + display: grid; + place-items: center; + border: 1px solid rgba(248, 251, 248, 0.16); + border-radius: 999px; + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.24), transparent 28%), + rgba(248, 251, 248, 0.08); + box-shadow: 0 18px 48px rgba(3, 9, 7, 0.24); + color: rgba(248, 251, 248, 0.86); + transform: translate(-50%, -50%); + z-index: 2; +} + +.fb-state-dot::after { + content: ""; position: absolute; - top: 10px; - left: 10px; + inset: 0.52rem; + border: 1px solid rgba(248, 251, 248, 0.1); + border-radius: inherit; +} + +.fb-state-dot strong { + position: relative; + z-index: 1; + overflow-wrap: anywhere; + font-family: var(--code-font-family); + font-size: 0.64rem; + font-weight: 700; line-height: 1; + text-transform: uppercase; } -.testimonial strong { - display: block; - margin-top: 1rem; - font-size: 0.9rem; - color: var(--testimonial-text-person); +.fb-state-dot--init { + top: 50%; + left: 7%; + border-color: rgba(248, 251, 248, 0.16); +} + +.fb-state-dot--vote { + top: 17%; + left: 31%; + border-color: rgba(244, 193, 93, 0.32); + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.26), transparent 28%), + rgba(244, 193, 93, 0.11); +} + +.fb-state-dot--hold { + top: 21%; + left: 72%; + border-color: rgba(244, 178, 63, 0.34); + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.24), transparent 28%), + rgba(244, 178, 63, 0.12); +} + +.fb-state-dot--commit { + top: 78%; + left: 67%; + border-color: rgba(244, 178, 63, 0.4); + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.22), transparent 28%), + rgba(244, 178, 63, 0.15); +} + +.fb-state-dot--abort { + top: 76%; + left: 25%; + border-color: rgba(244, 193, 93, 0.34); + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.24), transparent 28%), + rgba(244, 193, 93, 0.1); +} + +.fb-state-dot--crash { + top: 51%; + left: 92%; + border-color: rgba(183, 90, 50, 0.46); + background: + radial-gradient(circle at 34% 28%, rgba(255, 255, 255, 0.22), transparent 28%), + rgba(183, 90, 50, 0.18); + color: #ffc2a5; +} + +.fb-actions { + display: flex; + flex-wrap: wrap; + gap: 0.85rem; + margin-top: 1.5rem; +} + +.fb-button, +.fb-button:visited { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 3rem; + padding: 0 1.15rem; + border-radius: 7px; + font-size: 0.94rem; + font-weight: 800; + text-decoration: none; + transition: transform 160ms ease, background-color 160ms ease, border-color 160ms ease; +} + +.fb-button:hover { + transform: translateY(-2px); + text-decoration: none; +} + +.fb-button--primary { + background: var(--fb-honey); + border: 1px solid var(--fb-honey); + color: var(--fb-ink); +} + +.fb-button--primary:hover { + background: var(--fb-honey-hover); + color: var(--fb-ink); +} + +.fb-button--secondary { + background: #ffffff; + border: 1px solid #aebfb4; + color: var(--fb-ink); +} + +.fb-button--secondary:hover { + border-color: rgba(244, 178, 63, 0.65); + color: var(--fb-honey-dark); +} + +.fb-workbench { + position: relative; + z-index: 4; + min-width: 0; + width: 100%; + overflow: hidden; + border: 1px solid rgba(22, 32, 28, 0.14); + border-radius: 8px; + background: #101712; + box-shadow: 0 36px 80px rgba(22, 32, 28, 0.22); + animation: fb-rise 700ms ease both; +} + +.fb-hero__copy { + position: relative; + z-index: 3; +} + +.fb-workbench__topbar { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 0.55rem; + align-items: center; + padding: 0.85rem 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + color: #9eb0a6; + font-size: 0.78rem; + font-family: var(--code-font-family); +} + +.fb-workbench__topbar strong { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.fb-workbench__body { + position: relative; + max-height: 31rem; + overflow: hidden; + padding: 1.35rem; +} + +.fb-workbench__body::after { + content: ""; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 7rem; + background: linear-gradient(to bottom, rgba(16, 23, 18, 0), rgba(16, 23, 18, 0.86) 68%, #101712 100%); + pointer-events: none; +} + +.fb-workbench pre { + max-width: 100%; + margin: 0; + overflow: hidden; + color: #dcece2; + background: transparent; + border: 0; + font-size: 0.92rem; + line-height: 1.65; +} + +.fb-workbench code { + padding: 0; + color: inherit; + background: transparent; + font-size: inherit; +} + +.gdoc-markdown .fb-button, +.gdoc-markdown .fb-button:visited, +.gdoc-markdown .fb-button:hover { + text-decoration: none; +} + +.gdoc-markdown .fb-button--primary, +.gdoc-markdown .fb-button--primary:visited, +.gdoc-markdown .fb-button--primary:hover { + color: var(--fb-ink); +} + +.gdoc-markdown .fb-button--secondary, +.gdoc-markdown .fb-button--secondary:visited { + color: var(--fb-ink); +} + +.gdoc-markdown .fb-button--secondary:hover { + color: var(--fb-honey-dark); +} + +.fb-section { + width: min(var(--fb-home-max-width), calc(100% - var(--fb-home-gutter))); + margin: 0 auto; + padding: 6rem 0; +} + +.fb-section.fb-final { + width: 100%; + max-width: none; + margin: 0; +} + +.fb-section__header { + max-width: 720px; +} + +.fb-section h2 { + margin: 0; + color: var(--fb-ink); + font-size: 3rem; + line-height: 1.08; + font-weight: 800; + letter-spacing: 0; +} + +.fb-section__header > p { + margin-top: 1rem; + color: var(--fb-muted); + font-size: 1.08rem; + line-height: 1.7; +} + +.fb-artifact { + display: grid; + grid-template-columns: minmax(280px, 0.78fr) minmax(420px, 1.22fr); + gap: 4rem; + align-items: center; + border-top: 1px solid rgba(22, 32, 28, 0.08); +} + +.fb-artifact__copy { + max-width: 520px; +} + +.fb-artifact__copy p { + margin-top: 1.4rem; + color: var(--fb-muted); + font-size: 1.08rem; + line-height: 1.72; +} + +.fb-artifact__media { + min-width: 0; +} + +.fb-text-link, +.fb-text-link:visited { + display: inline-flex; + align-items: center; + gap: 0.7rem; + min-height: 2.8rem; + margin-top: 1.4rem; + padding: 0 1rem; + border: 1px solid rgba(244, 178, 63, 0.46); + border-radius: 7px; + background: linear-gradient(180deg, rgba(244, 178, 63, 0.12), rgba(244, 178, 63, 0.075)); + box-shadow: inset 0 -1px 0 rgba(244, 178, 63, 0.34); + color: var(--fb-honey-dark); + font-size: 0.94rem; + font-weight: 800; + line-height: 1.2; + text-decoration: none !important; + transition: transform 160ms ease, background 160ms ease, border-color 160ms ease, box-shadow 160ms ease, color 160ms ease; +} + +.fb-text-link:hover, +.fb-text-link:focus-visible { + border-color: rgba(244, 178, 63, 0.72); + background: linear-gradient(180deg, rgba(244, 178, 63, 0.18), rgba(244, 178, 63, 0.11)); + box-shadow: inset 0 -1px 0 rgba(244, 178, 63, 0.42); + color: #5f3900; + text-decoration: none !important; + transform: translateY(-1px); +} + +.gdoc-markdown .fb-text-link, +.gdoc-markdown .fb-text-link:visited { + border-color: rgba(244, 178, 63, 0.46); + border-bottom-color: rgba(244, 178, 63, 0.46); + color: var(--fb-honey-dark); + text-decoration: none !important; +} + +.gdoc-markdown .fb-text-link:hover, +.gdoc-markdown .fb-text-link:focus-visible { + border-color: rgba(244, 178, 63, 0.72); + border-bottom-color: rgba(244, 178, 63, 0.72); + color: #5f3900; +} + +.fb-terminal, +.fb-code-window { + position: relative; + overflow: hidden; + border: 1px solid rgba(22, 32, 28, 0.16); + border-radius: 8px; + background: #101712; + box-shadow: 0 30px 72px rgba(22, 32, 28, 0.18); +} + +.fb-terminal::after, +.fb-code-window::after { + content: ""; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 7rem; + background: linear-gradient(to bottom, rgba(16, 23, 18, 0), rgba(16, 23, 18, 0.86) 68%, #101712 100%); + pointer-events: none; +} + +.fb-terminal__topbar, +.fb-code-window__topbar { + display: flex; + align-items: center; + min-height: 3.35rem; + padding: 0.85rem 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + color: #9eb0a6; + font-family: var(--code-font-family); + font-size: 0.78rem; +} + +.fb-terminal__topbar strong, +.fb-code-window__topbar strong { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.fb-terminal pre, +.fb-code-window pre { + max-height: 30rem; + margin: 0; + overflow: hidden; + padding: 1.25rem; + border: 0; + background: transparent; + color: #dcece2; + font-size: 0.78rem; + line-height: 1.62; +} + +.fb-code-window pre { + font-size: 0.8rem; +} + +.fb-terminal code, +.fb-code-window code { + padding: 0; + background: transparent; + color: inherit; + font-size: inherit; +} + +.fb-graph-frame { + margin: 0; + overflow: hidden; + border: 1px solid rgba(22, 32, 28, 0.14); + border-radius: 8px; + background: #ffffff; + box-shadow: 0 30px 72px rgba(22, 32, 28, 0.14); +} + +.fb-graph-frame img { + display: block; + width: 100%; + height: auto; +} + +.fb-section--quotes { + border-top: 1px solid rgba(22, 32, 28, 0.08); +} + +.fb-quotes { + display: grid; + grid-template-columns: repeat(4, minmax(260px, 1fr)); + gap: 1rem; + margin-top: 2.5rem; + overflow-x: auto; + scroll-snap-type: x mandatory; + padding-bottom: 0.5rem; +} + +.fb-quote { + min-width: 260px; + margin: 0; + padding: 1.25rem; + border: 1px solid var(--fb-line); + border-radius: 8px; + background: rgba(255, 255, 255, 0.78); + scroll-snap-align: start; +} + +.fb-quote blockquote { + margin: 0; + padding: 0; + border: 0; + color: var(--fb-ink); + font-size: 1rem; + line-height: 1.62; +} + +.fb-quote figcaption { + margin-top: 1rem; + color: var(--fb-muted); + font-size: 0.88rem; + line-height: 1.45; +} + +.fb-final { + display: grid; + grid-template-columns: minmax(0, 0.9fr) minmax(320px, 0.7fr); + gap: clamp(1.5rem, 4vw, 3.5rem); + align-items: center; + width: 100%; + max-width: none; + margin: 0; + padding: 3.25rem max(1.25rem, calc((100vw - var(--fb-home-max-width)) / 2)); + border: 0; + border-radius: 0; + background: linear-gradient(135deg, #f7bd45 0%, #e9851a 100%); + color: #17211d; +} + +.fb-final__copy { + max-width: 560px; +} + +.fb-final h2 { + color: inherit; +} + +.fb-final h2 { + max-width: 620px; + font-size: 2.15rem; +} + +.fb-final__copy p { + max-width: 34rem; + margin-top: 0.85rem; + color: rgba(23, 33, 29, 0.76); + font-size: 1rem; + line-height: 1.6; +} + +.gdoc-markdown .fb-final__commands { + margin: 0; + padding: 1.2rem 1.3rem; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 0; + background: + linear-gradient(90deg, rgba(244, 178, 63, 0.08) 1px, transparent 1px), + linear-gradient(180deg, rgba(244, 178, 63, 0.06) 1px, transparent 1px), + #101712; + background-size: 24px 24px; + box-shadow: 0 18px 40px rgba(23, 33, 29, 0.18); + color: #fff4d6; + font-size: 0.95rem; + line-height: 1.75; + white-space: pre; +} + +.gdoc-markdown .fb-final__commands code { + display: block; + padding: 0; + overflow: hidden; + background: transparent; + color: inherit; + font-size: inherit; + line-height: inherit; + white-space: pre; +} + +.gdoc-footer { + border-top: 1px solid rgba(255, 255, 255, 0.08); +} + +.fb-site-footer { + background: + linear-gradient(90deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px), + linear-gradient(180deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px), + #101712; + background-size: 44px 44px; + color: rgba(248, 251, 248, 0.72); +} + +.fb-footer-shell { + display: grid; + grid-template-columns: minmax(220px, 0.55fr) minmax(0, 2fr); + align-items: center; + width: min(1180px, calc(100% - 2.5rem)); + max-width: 1180px; + margin: 0 auto; + gap: 2rem; + padding: 2rem 0; +} + +.fb-footer-brand-block { + min-width: 0; +} + +.fb-footer-brand { + display: inline-flex; + color: #ffffff !important; + font-size: 1.05rem; + font-weight: 800; + line-height: 1; + text-decoration: none !important; +} + +.fb-footer-brand-block p { + max-width: 25rem; + margin: 0.7rem 0 0; + color: rgba(248, 251, 248, 0.58); + font-size: 0.92rem; + line-height: 1.55; +} + +.fb-footer-nav { + display: flex; + align-items: start; + justify-content: flex-end; + gap: 2rem; +} + +.fb-footer-link-columns { + display: grid; + grid-template-columns: repeat(2, minmax(10rem, max-content)); + align-items: start; + column-gap: 2.35rem; +} + +.fb-footer-link-column { + display: grid; + gap: 0.36rem; + align-content: start; +} + +.fb-footer-link, +.fb-footer-link:visited { + display: inline-flex; + align-items: center; + justify-content: flex-start; + min-height: 1.55rem; + padding: 0; + border: 1px solid transparent; + border-radius: 0; + background: transparent; + color: rgba(248, 251, 248, 0.58); + font-size: 0.86rem; + font-weight: 600; + line-height: 1.2; + text-decoration: none !important; + transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease; +} + +.fb-footer-link:hover, +.fb-footer-link:focus-visible { + background: transparent; + border-color: transparent; + color: #f4c15d !important; +} + +.fb-footer-link:not(.fb-footer-link--top):hover, +.fb-footer-link:not(.fb-footer-link--top):focus-visible, +.fb-footer-brand:hover, +.fb-footer-brand:focus-visible { + color: #f4c15d !important; + text-decoration: underline !important; + text-decoration-thickness: 1px !important; + text-underline-offset: 0.22em; +} + +.fb-footer-link--top { + align-self: start; + justify-content: center; + min-height: 2.35rem; + padding: 0 0.72rem; + border-color: rgba(244, 193, 93, 0.26); + border-radius: 6px; + background: rgba(244, 193, 93, 0.08); + color: #f4c15d !important; + font-weight: 700; +} + +.fb-footer-link--top:hover, +.fb-footer-link--top:focus-visible { + border-color: rgba(244, 193, 93, 0.42); + background: rgba(244, 193, 93, 0.12); + color: #ffe1a1 !important; + text-decoration: none !important; +} + +.fb-footer-link--top::after { + content: "\2191"; + margin-left: 0.45rem; + font-size: 0.85rem; + line-height: 1; +} + +@keyframes fb-rise { + from { + opacity: 0; + transform: translateY(18px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fb-hover { + 0%, + 100% { + transform: translate(-50%, -50%) rotate(-4deg); + } + + 50% { + transform: translate(-50%, calc(-50% - 8px)) rotate(2deg); + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 1ms !important; + animation-iteration-count: 1 !important; + transition-duration: 1ms !important; + } +} + +@media (max-width: 980px) { + .fb-hero__inner { + grid-template-columns: 1fr; + gap: 2.25rem; + padding-bottom: 3rem; + } + + .fb-hero__visual { + width: min(100%, 560px); + } + + .fb-orbit-visual { + height: 280px; + } + + .fb-artifact { + grid-template-columns: 1fr; + gap: 2rem; + } + + .fb-hero h1 { + font-size: 3.55rem; + } + + .fb-section h2 { + font-size: 2.35rem; + } + + .fb-footer-shell { + grid-template-columns: 1fr; + align-items: flex-start; + gap: 1.5rem; + } + + .fb-footer-nav { + justify-content: flex-start; + } + + .fb-final { + display: block; + } + + .gdoc-markdown .fb-final__commands { + margin-top: 1.15rem; + } +} + +@media (max-width: 640px) { + body { + background-size: 34px 34px; + } + + .gdoc-header .container { + min-height: 60px; + } + + .fb-site-footer { + background-size: 34px 34px; + } + + .fb-footer-shell { + width: calc(100% - 28px); + padding: 1.55rem 0 1.65rem; + gap: 1.25rem; + } + + .fb-footer-brand { + font-size: 1rem; + } + + .fb-footer-brand-block p { + max-width: 22rem; + margin-top: 0.55rem; + font-size: 0.86rem; + line-height: 1.45; + } + + .fb-footer-nav { + flex-direction: column; + width: 100%; + gap: 0.9rem; + } + + .fb-footer-link-columns { + grid-template-columns: 1fr; + width: 100%; + gap: 0.42rem; + } + + .fb-footer-link-column { + gap: 0.42rem; + } + + .fb-footer-link, + .fb-footer-link:visited { + justify-content: flex-start; + min-height: 1.7rem; + padding: 0; + border-color: transparent; + background: transparent; + font-size: 0.82rem; + line-height: 1.25; + text-align: left; + } + + .fb-footer-link--top { + width: 100%; + min-height: 2.8rem; + margin-top: 0; + justify-content: center; + border-color: rgba(244, 193, 93, 0.26); + border-radius: 6px; + background: rgba(244, 193, 93, 0.1); + } + + .gdoc-brand__img { + display: block; + width: 24px; + height: 24px; + margin-right: 0.4rem; + } + + .gdoc-brand__title { + font-size: 1rem; + } + + .gdoc-page { + padding-top: 1.5rem; + } + + .gdoc-markdown h1 { + font-size: 2.15rem; + } + + .gdoc-post__title { + font-size: 2.15rem; + } + + .fb-home { + width: calc(100% + 40px); + margin-left: -20px; + margin-top: calc(-1.5rem - 1.25rem); + } + + .fb-hero { + min-height: auto; + } + + .fb-hero__inner, + .fb-section { + width: calc(100% - var(--fb-home-gutter)); + max-width: var(--fb-home-max-width); + } + + .fb-hero__inner { + padding: 2rem 0 2.1rem; + gap: 1.65rem; + } + + .fb-hero h1 { + font-size: 2.15rem; + line-height: 1.04; + } + + .fb-home .fb-hero__lead { + margin-top: 1.15rem; + font-size: 0.96rem; + line-height: 1.55; + } + + .fb-hero .fb-actions { + display: flex; + margin-top: 1.25rem; + } + + .fb-hero .fb-button { + flex: 1 1 0; + width: auto; + padding: 0 0.7rem; + white-space: nowrap; + } + + .fb-text-link, + .fb-text-link:visited { + justify-content: flex-start; + width: 100%; + min-height: 3.1rem; + padding: 0 0.9rem; + } + + .fb-orbit-visual { + height: 270px; + } + + .fb-orbit-ring--outer { + width: 96%; + height: 72%; + } + + .fb-orbit-ring--middle { + width: 74%; + height: 54%; + } + + .fb-orbit-bee { + width: 7.25rem; + max-width: 36%; + } + + .fb-state-dot { + width: 3.7rem; + } + + .fb-state-dot strong { + font-size: 0.55rem; + } + + .fb-state-dot--init { + left: 8%; + } + + .fb-state-dot--vote { + top: 21%; + left: 30%; + } + + .fb-state-dot--hold { + top: 24%; + left: 71%; + } + + .fb-state-dot--commit { + top: 75%; + left: 70%; + } + + .fb-state-dot--abort { + top: 75%; + left: 28%; + } + + .fb-state-dot--crash { + left: 92%; + } + + .fb-button { + width: 100%; + } + + .fb-workbench__body { + padding: 0.85rem; + } + + .fb-workbench__body::after { + height: 3.4rem; + } + + .fb-terminal::after, + .fb-code-window::after { + height: 3.4rem; + } + + .fb-workbench pre { + max-height: 19rem; + overflow: hidden; + font-size: 0.7rem; + line-height: 1.5; + } + + .fb-terminal pre, + .fb-code-window pre { + max-height: 24rem; + padding: 0.9rem; + font-size: 0.66rem; + line-height: 1.5; + } + + .fb-section { + padding: 3.5rem 0; + } + + .fb-section h2 { + font-size: 2rem; + } + + .fb-artifact { + gap: 1.65rem; + } + + .fb-final { + grid-template-columns: 1fr; + padding: 1.5rem; + } + + .fb-final h2 { + font-size: 1.65rem; + } } diff --git a/static/img/fizzbee-travel-booking-state-graph.svg b/static/img/fizzbee-travel-booking-state-graph.svg new file mode 100644 index 0000000..707128b --- /dev/null +++ b/static/img/fizzbee-travel-booking-state-graph.svg @@ -0,0 +1,49 @@ + + FizzBee state graph for travel_booking.fizz + Three FizzBee states from the error graph: Init, Coordinator Checkout, and crash. + + + + + + + + + + + + + + + + + + + Init + Coordinator#0 + status = init + Participant#0, #1 + status = init + + + + + Coordinator#0.Checkout + Coordinator#0 + status = inprogress + Participant#0, #1 + status = init + threads: 0/1 + + + + + crash + Coordinator#0 + status = inprogress + Participants + status = init + + error-graph.dot from fizz --output-dir /tmp/fizzbee-home-run travel_booking.fizz + + diff --git a/themes/hugo-geekdoc/layouts/partials/foot.html b/themes/hugo-geekdoc/layouts/partials/foot.html index 2a115e5..bca4ae3 100644 --- a/themes/hugo-geekdoc/layouts/partials/foot.html +++ b/themes/hugo-geekdoc/layouts/partials/foot.html @@ -1,4 +1,4 @@ -{{ if default true .Site.Params.geekdocSearch }} +{{ if and (default true .Site.Params.geekdocSearch) (default true .Page.Params.geekdocNav) }} {{- $searchConfigFile := printf "search/%s.config.json" .Language.Lang -}} {{- $searchConfig := resources.Get "search/config.json" | resources.ExecuteAsTemplate $searchConfigFile . | resources.Minify -}} diff --git a/themes/hugo-geekdoc/layouts/partials/head/others.html b/themes/hugo-geekdoc/layouts/partials/head/others.html index 06f346d..4a10026 100644 --- a/themes/hugo-geekdoc/layouts/partials/head/others.html +++ b/themes/hugo-geekdoc/layouts/partials/head/others.html @@ -1,7 +1,7 @@ {{- if default true .Site.Params.geekdocDarkModeToggle }} {{- end }} - + %s
  • /
  • %s" $parent.RelPermalink $parent.RelPermalink $name $position .value) }} {{ template "breadcrumb" dict "page" $parent "value" $value "position" $position }}