You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by kr...@apache.org on 2025/08/17 19:13:08 UTC

(iggy-website) branch staging updated: style(repo): Add new WiP site with Zola blog (#22)

This is an automated email from the ASF dual-hosted git repository.

kranti pushed a commit to branch staging
in repository https://gitbox.apache.org/repos/asf/iggy-website.git


The following commit(s) were added to refs/heads/staging by this push:
     new c6bd6f795 style(repo): Add new WiP site with Zola blog (#22)
c6bd6f795 is described below

commit c6bd6f795c6f9cfc31a9c36c690b887caeb8e55e
Author: Jerzy Nowak <je...@landingpro.pl>
AuthorDate: Sun Aug 17 21:13:03 2025 +0200

    style(repo): Add new WiP site with Zola blog (#22)
    
    merging to continue development against the staging site
---
 401.html                                           |    45 +
 404.html                                           |    33 +
 LICENSE.txt                                        |   205 -
 README.md                                          |    49 -
 blog-src/config.toml                               |    51 +
 blog-src/content/_index.md                         |     8 +
 blog-src/content/about.md                          |     4 +
 .../content/posts}/2023-08-16-welcome.md           |     6 +-
 .../content/posts}/2023-08-19-message-headers.md   |    14 +-
 .../content/posts}/2023-08-22-message-state.md     |    12 +-
 .../content/posts}/2023-08-27-message-expiry.md    |    18 +-
 .../content/posts}/2023-08-31-updated-schemas.md   |    10 +-
 .../posts}/2023-09-20-users-and-permissions.md     |    10 +-
 .../posts}/2023-10-02-consumer-identifier.md       |    10 +-
 .../posts}/2023-10-12-personal-access-tokens.md    |    14 +-
 .../content/posts}/2023-11-26-release-0.1.0.md     |     8 +-
 ...023-12-29-building-message-streaming-in-rust.md |    38 +-
 ...9-one-year-of-building-the-message-streaming.md |    44 +-
 ...24-10-28-technology-radar-and-currrent-goals.md |    24 +-
 .../content/posts}/2025-02-10-apache-incubator.md  |    12 +-
 .../posts}/2025-02-17-transparent-benchmarks.md    |    12 +-
 .../posts}/2025-05-08-zero-copy-deserialization.md |    10 +-
 .../posts}/2025-06-06-connectors-runtime.md        |     6 +-
 blog-src/content/posts/_index.md                   |    10 +
 blog-src/content/posts/about.md                    |     5 +
 blog-src/content/projects/_index.md                |     5 +
 blog-src/content/projects/project-1.jpg            |   Bin 0 -> 6356537 bytes
 blog-src/content/projects/project_1.md             |    11 +
 blog-src/content/projects/project_2.md             |    11 +
 blog-src/content/projects/project_3.md             |    10 +
 blog-src/content/projects/project_4.md             |    10 +
 blog-src/content/projects/project_5.md             |     7 +
 .../static}/apache-incubator/iggy_logo.png         |   Bin
 .../building-message-streaming/gh_trending.jpeg    |   Bin
 .../building-message-streaming/iggy_bench.jpeg     |   Bin
 .../building-message-streaming/iggy_ci_cd.jpeg     |   Bin
 .../building-message-streaming/iggy_cli.jpeg       |   Bin
 .../building-message-streaming/iggy_sample.jpeg    |   Bin
 .../building-message-streaming/iggy_server.jpeg    |   Bin
 .../building-message-streaming/iggy_web_ui.jpeg    |   Bin
 .../static}/building-message-streaming/iggys.jpeg  |   Bin
 .../leader_elections.jpeg                          |   Bin
 blog-src/static/custom.css                         |   216 +
 .../static}/img/AdvancedFeatures.svg               |     0
 .../static}/img/ExtremelyEfficient.svg             |     0
 .../static}/img/FullyOpenSourced.svg               |     0
 .../static}/img/ManagementInterface.svg            |     0
 .../static}/img/MultipleTransports.svg             |     0
 {static => blog-src/static}/img/Sdk.svg            |     0
 .../static}/img/apache-iggy-light-bg0.5x.png       |   Bin
 .../static}/img/apache-iggy0.5x.png                |   Bin
 {static => blog-src/static}/img/architecture.png   |   Bin
 {static => blog-src/static}/img/bench_cli.png      |   Bin
 {static => blog-src/static}/img/bench_platform.png |   Bin
 .../static}/img/docusaurus-social-card.jpg         |   Bin
 {static => blog-src/static}/img/docusaurus.png     |   Bin
 {static => blog-src/static}/img/favicon.ico        |   Bin
 {static => blog-src/static}/img/iggy-logo-1.png    |   Bin
 {static => blog-src/static}/img/iggy.png           |   Bin
 {static => blog-src/static}/img/iggy_0_1_0.jpg     |   Bin
 .../static}/img/iggy_bench_0_1_0.jpeg              |   Bin
 {static => blog-src/static}/img/iggy_ci_0_1_0.jpeg |   Bin
 blog-src/static/img/iggy_cli.png                   |   Bin 0 -> 446479 bytes
 .../static}/img/iggy_cli_0_1_0.jpeg                |   Bin
 {static => blog-src/static}/img/iggy_server.png    |   Bin
 .../static}/img/iggy_server_0_1_0.jpeg             |   Bin
 {static => blog-src/static}/img/iggy_web_ui.png    |   Bin
 .../static}/img/iggy_web_ui_0_1_0.jpeg             |   Bin
 .../static/img/logo-darkbg.svg                     |     0
 {static => blog-src/static}/img/logo.png           |   Bin
 {static => blog-src/static}/img/logo.svg           |     0
 .../static}/img/undraw_docusaurus_mountain.svg     |     0
 .../static}/img/undraw_docusaurus_react.svg        |     0
 .../static}/img/undraw_docusaurus_tree.svg         |     0
 .../iggy_bench.png                                 |   Bin
 .../iggy_cli.png                                   |   Bin
 .../iggy_cluster.png                               |   Bin
 .../iggy_perf.png                                  |   Bin
 .../iggy_rocket.png                                |   Bin
 .../iggy_stars.png                                 |   Bin
 .../iggy_tpc.png                                   |   Bin
 .../iggy_web.png                                   |   Bin
 .../iggy_docker.png                                |   Bin
 .../iggy_technology_radar.png                      |   Bin
 .../iggy_vsr.png                                   |   Bin
 .../transparent-benchmarks/iggy_bench_cli.png      |   Bin
 .../iggy_bench_dashboard.png                       |   Bin
 .../iggy_batch_schema_1.png                        |   Bin
 .../iggy_batch_schema_2.png                        |   Bin
 .../no_zero_copy_consumer.png                      |   Bin
 .../no_zero_copy_producer.png                      |   Bin
 .../zero_copy_consumer.png                         |   Bin
 .../zero_copy_producer.png                         |   Bin
 blog-src/templates/partials/header.html            |   214 +
 blog-src/themes/apollo/.editorconfig               |     3 +
 blog-src/themes/apollo/.github/workflows/zola.yml  |    24 +
 blog-src/themes/apollo/.gitignore                  |     1 +
 blog-src/themes/apollo/LICENSE                     |    21 +
 blog-src/themes/apollo/README.md                   |    73 +
 blog-src/themes/apollo/config.toml                 |    57 +
 blog-src/themes/apollo/configuration.md            |   243 +
 blog-src/themes/apollo/content/_index.md           |     8 +
 blog-src/themes/apollo/content/about.md            |     4 +
 blog-src/themes/apollo/content/posts/_index.md     |    10 +
 .../themes/apollo/content/posts/configuration.md   |   243 +
 blog-src/themes/apollo/content/posts/markdown.md   |    86 +
 .../themes/apollo/content/posts/math-symbol.md     |    25 +
 blog-src/themes/apollo/content/posts/mermaid.md    |    28 +
 blog-src/themes/apollo/content/posts/shortcode.md  |    81 +
 blog-src/themes/apollo/content/projects/_index.md  |     5 +
 .../themes/apollo/content/projects/project-1.jpg   |   Bin 0 -> 6356537 bytes
 .../themes/apollo/content/projects/project_1.md    |    11 +
 .../themes/apollo/content/projects/project_2.md    |    11 +
 .../themes/apollo/content/projects/project_3.md    |    10 +
 .../themes/apollo/content/projects/project_4.md    |    10 +
 .../themes/apollo/content/projects/project_5.md    |     7 +
 blog-src/themes/apollo/markdown.md                 |    86 +
 blog-src/themes/apollo/math-symbol.md              |    25 +
 blog-src/themes/apollo/mermaid.md                  |    28 +
 blog-src/themes/apollo/sass/fonts.scss             |    31 +
 blog-src/themes/apollo/sass/main.scss              |    53 +
 blog-src/themes/apollo/sass/parts/_cards.scss      |    55 +
 blog-src/themes/apollo/sass/parts/_code.scss       |   237 +
 blog-src/themes/apollo/sass/parts/_header.scss     |   166 +
 blog-src/themes/apollo/sass/parts/_image.scss      |    31 +
 blog-src/themes/apollo/sass/parts/_mermaid.scss    |     9 +
 blog-src/themes/apollo/sass/parts/_misc.scss       |   132 +
 blog-src/themes/apollo/sass/parts/_note.scss       |    91 +
 blog-src/themes/apollo/sass/parts/_search.scss     |   222 +
 blog-src/themes/apollo/sass/parts/_table.scss      |    15 +
 blog-src/themes/apollo/sass/parts/_tags.scss       |    26 +
 blog-src/themes/apollo/sass/parts/_toc.scss        |    24 +
 blog-src/themes/apollo/sass/theme/dark.scss        |    26 +
 blog-src/themes/apollo/sass/theme/light.scss       |    25 +
 blog-src/themes/apollo/screenshot-dark.png         |   Bin 0 -> 66679 bytes
 blog-src/themes/apollo/screenshot.png              |   Bin 0 -> 66329 bytes
 blog-src/themes/apollo/shell.nix                   |    12 +
 blog-src/themes/apollo/shortcode.md                |    81 +
 .../fonts/JetbrainsMono/JetBrainsMono-Bold.ttf     |   Bin 0 -> 114240 bytes
 .../JetbrainsMono/JetBrainsMono-BoldItalic.ttf     |   Bin 0 -> 117108 bytes
 .../JetbrainsMono/JetBrainsMono-ExtraBold.ttf      |   Bin 0 -> 114232 bytes
 .../JetBrainsMono-ExtraBoldItalic.ttf              |   Bin 0 -> 117156 bytes
 .../JetbrainsMono/JetBrainsMono-ExtraLight.ttf     |   Bin 0 -> 114508 bytes
 .../JetBrainsMono-ExtraLightItalic.ttf             |   Bin 0 -> 117436 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-Italic.ttf   |   Bin 0 -> 117108 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-Light.ttf    |   Bin 0 -> 114444 bytes
 .../JetbrainsMono/JetBrainsMono-LightItalic.ttf    |   Bin 0 -> 117312 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-Medium.ttf   |   Bin 0 -> 114348 bytes
 .../JetbrainsMono/JetBrainsMono-MediumItalic.ttf   |   Bin 0 -> 117236 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-Regular.ttf  |   Bin 0 -> 114320 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-SemiBold.ttf |   Bin 0 -> 114304 bytes
 .../JetbrainsMono/JetBrainsMono-SemiBoldItalic.ttf |   Bin 0 -> 117176 bytes
 .../fonts/JetbrainsMono/JetBrainsMono-Thin.ttf     |   Bin 0 -> 114420 bytes
 .../JetbrainsMono/JetBrainsMono-ThinItalic.ttf     |   Bin 0 -> 117340 bytes
 .../fonts/SpaceGrotesk/SpaceGrotesk-Bold.ttf       |   Bin 0 -> 86400 bytes
 .../fonts/SpaceGrotesk/SpaceGrotesk-Light.ttf      |   Bin 0 -> 86556 bytes
 .../fonts/SpaceGrotesk/SpaceGrotesk-Medium.ttf     |   Bin 0 -> 86528 bytes
 .../fonts/SpaceGrotesk/SpaceGrotesk-Regular.ttf    |   Bin 0 -> 86504 bytes
 .../fonts/SpaceGrotesk/SpaceGrotesk-SemiBold.ttf   |   Bin 0 -> 86452 bytes
 blog-src/themes/apollo/static/icons/moon.svg       |     1 +
 blog-src/themes/apollo/static/icons/search.svg     |     1 +
 blog-src/themes/apollo/static/icons/social/LICENSE |     1 +
 .../themes/apollo/static/icons/social/apple.svg    |     1 +
 .../themes/apollo/static/icons/social/bitcoin.svg  |     1 +
 .../themes/apollo/static/icons/social/bluesky.svg  |     3 +
 .../apollo/static/icons/social/deviantart.svg      |     1 +
 .../themes/apollo/static/icons/social/diaspora.svg |     1 +
 .../themes/apollo/static/icons/social/discord.svg  |     1 +
 .../apollo/static/icons/social/discourse.svg       |     1 +
 .../themes/apollo/static/icons/social/email.svg    |     1 +
 .../themes/apollo/static/icons/social/ethereum.svg |     1 +
 .../themes/apollo/static/icons/social/etsy.svg     |     1 +
 .../themes/apollo/static/icons/social/facebook.svg |     1 +
 .../apollo/static/icons/social/fediverse.svg       |     1 +
 .../themes/apollo/static/icons/social/github.svg   |     1 +
 .../themes/apollo/static/icons/social/gitlab.svg   |     1 +
 .../apollo/static/icons/social/google-scholar.svg  |    21 +
 .../themes/apollo/static/icons/social/google.svg   |     1 +
 .../apollo/static/icons/social/hacker-news.svg     |     1 +
 .../apollo/static/icons/social/instagram.svg       |     1 +
 .../themes/apollo/static/icons/social/linkedin.svg |     1 +
 .../themes/apollo/static/icons/social/mastodon.svg |     1 +
 .../themes/apollo/static/icons/social/matrix.svg   |     7 +
 .../themes/apollo/static/icons/social/orcid.svg    |    16 +
 .../themes/apollo/static/icons/social/paypal.svg   |     1 +
 .../apollo/static/icons/social/pinterest.svg       |     1 +
 .../themes/apollo/static/icons/social/quora.svg    |     1 +
 .../themes/apollo/static/icons/social/reddit.svg   |     1 +
 blog-src/themes/apollo/static/icons/social/rss.svg |     1 +
 .../themes/apollo/static/icons/social/skype.svg    |     1 +
 .../themes/apollo/static/icons/social/slack.svg    |     1 +
 .../themes/apollo/static/icons/social/snapchat.svg |     1 +
 .../apollo/static/icons/social/soundcloud.svg      |     1 +
 .../themes/apollo/static/icons/social/spotify.svg  |     1 +
 .../apollo/static/icons/social/stack-exchange.svg  |     1 +
 .../apollo/static/icons/social/stack-overflow.svg  |     1 +
 .../themes/apollo/static/icons/social/steam.svg    |     1 +
 .../themes/apollo/static/icons/social/telegram.svg |     1 +
 .../themes/apollo/static/icons/social/twitter.svg  |     1 +
 .../themes/apollo/static/icons/social/vimeo.svg    |     1 +
 .../themes/apollo/static/icons/social/whatsapp.svg |     1 +
 .../apollo/static/icons/social/x-twitter.svg       |     1 +
 .../themes/apollo/static/icons/social/youtube.svg  |     1 +
 blog-src/themes/apollo/static/icons/sun.svg        |     1 +
 blog-src/themes/apollo/static/js/codeblock.js      |   103 +
 blog-src/themes/apollo/static/js/count.js          |   271 +
 blog-src/themes/apollo/static/js/imamu.js          |     2 +
 blog-src/themes/apollo/static/js/main.js           |    25 +
 blog-src/themes/apollo/static/js/mermaid.js        |  1760 ++
 blog-src/themes/apollo/static/js/note.js           |    14 +
 .../themes/apollo/static/js/searchElasticlunr.js   |  3201 ++++
 .../apollo/static/js/searchElasticlunr.min.js      |     1 +
 blog-src/themes/apollo/static/js/themetoggle.js    |    57 +
 blog-src/themes/apollo/static/js/toc.js            |    15 +
 blog-src/themes/apollo/templates/404.html          |     8 +
 .../themes/apollo/templates/_giscus_script.html    |     2 +
 blog-src/themes/apollo/templates/base.html         |    30 +
 blog-src/themes/apollo/templates/cards.html        |    35 +
 blog-src/themes/apollo/templates/homepage.html     |     5 +
 blog-src/themes/apollo/templates/index.html        |     1 +
 .../themes/apollo/templates/macros/macros.html     |   246 +
 blog-src/themes/apollo/templates/page.html         |     5 +
 .../themes/apollo/templates/partials/header.html   |   214 +
 blog-src/themes/apollo/templates/partials/nav.html |    83 +
 blog-src/themes/apollo/templates/section.html      |    39 +
 .../apollo/templates/shortcodes/mermaid.html       |     5 +
 .../themes/apollo/templates/shortcodes/note.html   |    28 +
 .../themes/apollo/templates/taxonomy_list.html     |    21 +
 .../themes/apollo/templates/taxonomy_single.html   |     7 +
 blog-src/themes/apollo/theme.toml                  |    16 +
 blog-src/themes/apollo/treefmt.toml                |    15 +
 blog-src/themes/config.toml                        |     2 +
 .../Logo/0.5x/iggy-apache-b&wdark@0.5x.png         |   Bin 36299 -> 0 bytes
 .../Logo/0.5x/iggy-apache-b&wlight@0.5x.png        |   Bin 36828 -> 0 bytes
 .../Logo/0.5x/iggy-apache-color-darkbg@0.5x.png    |   Bin 96024 -> 0 bytes
 .../Logo/0.5x/iggy-apache-color-lightbg@0.5x.png   |   Bin 97130 -> 0 bytes
 .../0.5x/iggy-apache-logo-wo-sygnet-dark@0.5x.png  |   Bin 14714 -> 0 bytes
 .../0.5x/iggy-apache-logo-wo-sygnet-light@0.5x.png |   Bin 13915 -> 0 bytes
 .../Logo/0.5x/iggy-apache-sygnet-b&wdark@0.5x.png  |   Bin 23012 -> 0 bytes
 .../Logo/0.5x/iggy-apache-sygnet-b&wlight@0.5x.png |   Bin 23210 -> 0 bytes
 .../0.5x/iggy-apache-sygnet-color-darkbg@0.5x.png  |   Bin 68197 -> 0 bytes
 .../0.5x/iggy-apache-sygnet-color-lightbg@0.5x.png |   Bin 68525 -> 0 bytes
 brand-assets/Logo/4x/iggy-apache-b&wdark@4x.png    |   Bin 440202 -> 0 bytes
 brand-assets/Logo/4x/iggy-apache-b&wlight@4x.png   |   Bin 430715 -> 0 bytes
 .../Logo/4x/iggy-apache-color-darkbg@4x.png        |   Bin 1312915 -> 0 bytes
 .../Logo/4x/iggy-apache-color-lightbg@4x.png       |   Bin 1340584 -> 0 bytes
 .../Logo/4x/iggy-apache-logo-wo-sygnet-dark@4x.png |   Bin 207686 -> 0 bytes
 .../4x/iggy-apache-logo-wo-sygnet-light@4x.png     |   Bin 197503 -> 0 bytes
 .../Logo/4x/iggy-apache-sygnet-b&wdark@4x.png      |   Bin 290806 -> 0 bytes
 .../Logo/4x/iggy-apache-sygnet-b&wlight@4x.png     |   Bin 288572 -> 0 bytes
 .../Logo/4x/iggy-apache-sygnet-color-darkbg@4x.png |   Bin 863513 -> 0 bytes
 .../4x/iggy-apache-sygnet-color-lightbg@4x.png     |   Bin 869788 -> 0 bytes
 brand-assets/Logo/SVG/iggy-apache-b&wdark.svg      |    63 -
 brand-assets/Logo/SVG/iggy-apache-b&wlight.svg     |    77 -
 .../Logo/SVG/iggy-apache-color-lightbg.svg         |   112 -
 .../Logo/SVG/iggy-apache-logo-wo-sygnet-dark.svg   |    11 -
 .../Logo/SVG/iggy-apache-logo-wo-sygnet-light.svg  |    18 -
 .../Logo/SVG/iggy-apache-sygnet-b&wdark.svg        |    49 -
 .../Logo/SVG/iggy-apache-sygnet-b&wlight.svg       |    61 -
 .../Logo/SVG/iggy-apache-sygnet-color-darkbg.svg   |    98 -
 .../Logo/SVG/iggy-apache-sygnet-color-lightbg.svg  |    98 -
 {src/css => css}/components.css                    |    16 -
 css/iggywebsite.css                                | 10142 +++++++++++
 {src/css => css}/normalize.css                     |     0
 docs/cli/_category_.json                           |     4 -
 docs/cli/start.md                                  |    67 -
 docs/connectors/_category_.json                    |     4 -
 docs/connectors/introduction.md                    |    69 -
 docs/connectors/runtime.md                         |    30 -
 docs/connectors/sdk.md                             |    24 -
 docs/connectors/sink.md                            |   218 -
 docs/connectors/source.md                          |   228 -
 docs/iggy.md                                       |    10 -
 docs/intro.tempmd                                  |    47 -
 docs/introduction/_category_.json                  |     4 -
 docs/introduction/about.md                         |    83 -
 docs/introduction/architecture.md                  |    78 -
 docs/introduction/getting_started.md               |   540 -
 docs/introduction/high_level_sdk.md                |   107 -
 docs/introduction/stream_builder.md                |   408 -
 docs/sdk/_category_.json                           |     4 -
 docs/sdk/csharp/_category_.json                    |     4 -
 docs/sdk/csharp/examples.md                        |     6 -
 docs/sdk/csharp/intro.md                           |     8 -
 docs/sdk/go/_category_.json                        |     4 -
 docs/sdk/go/examples.md                            |     6 -
 docs/sdk/go/intro.md                               |     8 -
 docs/sdk/java/_category_.json                      |     4 -
 docs/sdk/java/examples.md                          |     6 -
 docs/sdk/java/intro.md                             |     9 -
 docs/sdk/node/_category_.json                      |     4 -
 docs/sdk/node/examples.md                          |     6 -
 docs/sdk/node/intro.md                             |     8 -
 docs/sdk/python/_category_.json                    |     4 -
 docs/sdk/python/examples.md                        |     6 -
 docs/sdk/python/intro.md                           |     8 -
 docs/sdk/rust/_category_.json                      |     4 -
 docs/sdk/rust/examples.md                          |    14 -
 docs/sdk/rust/intro.md                             |    10 -
 docs/server/_category_.json                        |     4 -
 docs/server/benchmarking.md                        |    74 -
 docs/server/configuration.md                       |   518 -
 docs/server/docker.md                              |    40 -
 docs/server/introduction.md                        |    22 -
 docs/server/schema.md                              |   712 -
 docs/web_ui/_category_.json                        |     4 -
 docs/web_ui/start.md                               |    75 -
 docusaurus.config.ts                               |   209 -
 empty-page.html                                    |    45 +
 {static/fonts => fonts}/Inter-Medium.ttf           |   Bin
 {static/fonts => fonts}/Inter-Regular.ttf          |   Bin
 {static/fonts => fonts}/Inter-SemiBold.ttf         |   Bin
 {static/img => images}/Avatar-Placeholder.svg      |     0
 images/Go-Logo_Blue.svg                            |    60 +
 {static/img => images}/Image-Placeholder-1.svg     |     0
 {static/img => images}/Image-Placeholder-2.svg     |     0
 {static/img => images}/Image-Placeholder-3.svg     |     0
 images/Logo_C_sharp.svg                            |    10 +
 images/Python-logo-notext.svg                      |     1 +
 images/Rust_programming_language_logo-orange.svg   |    68 +
 {static/img => images}/Switch-Handle.svg           |     0
 {static/img => images}/SystemFlow-Logo-White.svg   |     0
 {static/img => images}/SystemFlow-Logo.svg         |     0
 {static/img => images}/Video-Placeholder.svg       |     0
 {static/img => images}/asf_logo.svg                |     0
 {static/img => images}/check.svg                   |     0
 {static/img => images}/cookies-1.svg               |     0
 {static/img => images}/copy-icon.svg               |     0
 {static/img => images}/dc-icon-p-130x130q80.png    |   Bin
 {static/img => images}/dc-icon.png                 |   Bin
 {static/img => images}/emetlabs-light.svg          |     0
 {static/img => images}/favicon.png                 |   Bin
 {static/img => images}/gh-icon-orange.svg          |     0
 {static/img => images}/github-mark-white.svg       |     0
 {static/img => images}/hero-main-p-1080.webp       |   Bin
 {static/img => images}/hero-main-p-1600.webp       |   Bin
 {static/img => images}/hero-main-p-500.webp        |   Bin
 {static/img => images}/hero-main-p-800.webp        |   Bin
 {static/img => images}/hero-main.webp              |   Bin
 {static/img => images}/hugin-light.svg             |     0
 .../img => images}/iggy-apache-color-darkbg.svg    |     0
 images/java-4-logo.svg                             |     2 +
 {static/img => images}/neferdata-light.svg         |     0
 images/nodejsStackedLight.svg                      |    44 +
 {static/img => images}/solidhash.png               |   Bin
 {static/img => images}/webclip.png                 |   Bin
 index.html                                         |   746 +
 js/iggywebsite.js                                  |     1 +
 netlify.toml                                       |    12 +
 package-lock.json                                  | 16672 -------------------
 package.json                                       |    47 -
 sidebars.ts                                        |    33 -
 src/components/HomepageFeatures/index.tsx          |   154 -
 src/components/HomepageFeatures/styles.module.css  |    11 -
 src/css/custom.css                                 |  1041 --
 src/css/iggywebsite.css                            |   887 -
 src/pages/downloads.md                             |    78 -
 src/pages/index.module.css                         |    29 -
 src/pages/index.tsx                                |    91 -
 src/pages/markdown-page.md                         |     7 -
 static/.asf.yaml                                   |    29 -
 static/.nojekyll                                   |     0
 static/img/hero-main copy.webp                     |   Bin 150984 -> 0 bytes
 static/img/hero-main-p-1080 copy.webp              |   Bin 58010 -> 0 bytes
 static/img/hero-main-p-1600 copy.webp              |   Bin 103532 -> 0 bytes
 static/img/hero-main-p-500 copy.webp               |   Bin 18198 -> 0 bytes
 static/img/hero-main-p-800 copy.webp               |   Bin 36490 -> 0 bytes
 static/img/iggy_cli.png                            |   Bin 231092 -> 0 bytes
 tsconfig.json                                      |     8 -
 yarn.lock                                          |  9033 ----------
 370 files changed, 20491 insertions(+), 32842 deletions(-)

diff --git a/401.html b/401.html
new file mode 100644
index 000000000..8df4088a4
--- /dev/null
+++ b/401.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html><!--  Last Published: Wed Jul 30 2025 21:47:32 GMT+0000 (Coordinated Universal Time)  -->
+<html data-wf-page="683e090d3817dd3c3bca6c44" data-wf-site="683e090d3817dd3c3bca6c22" lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>Protected page</title>
+  <meta content="Protected page" property="og:title">
+  <meta content="Protected page" property="twitter:title">
+  <meta content="width=device-width, initial-scale=1" name="viewport">
+  <link href="css/normalize.css" rel="stylesheet" type="text/css">
+  <link href="css/components.css" rel="stylesheet" type="text/css">
+  <link href="css/iggywebsite.css" rel="stylesheet" type="text/css">
+  <link href="https://fonts.googleapis.com" rel="preconnect">
+  <link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous">
+  <script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
+  <script type="text/javascript">WebFont.load({  google: {    families: ["Inconsolata:400,700","Manrope:200,300,regular,500,600,700,800"]  }});</script>
+  <script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script>
+  <link href="images/favicon.png" rel="shortcut icon" type="image/x-icon">
+  <link href="images/webclip.png" rel="apple-touch-icon">
+</head>
+<body>
+  <div class="utility-page-wrap">
+    <div class="utility-page-content w-password-page w-form">
+      <form action="/.wf_auth" method="post" id="email-form" name="email-form" data-name="Email Form" class="utility-page-form w-password-page" data-wf-page-id="683e090d3817dd3c3bca6c44" data-wf-element-id="5f96c0d6a8d7427c41652a8900000000000c"><img src="https://d3e54v103j8qbb.cloudfront.net/static/password-page-lock.832ca8e2c9.svg" alt="">
+        <h2>Protected Page</h2><input class="w-password-page w-input" autofocus="true" maxlength="256" name="pass" data-name="field" placeholder="Enter your password" type="password" id="field"><input type="submit" data-wait="Please wait..." class="w-password-page w-button" value="Submit">
+        <div class="w-password-page w-form-fail">
+          <div>Incorrect password. Please try again.</div>
+        </div>
+        <div style="display:none" class="w-password-page w-embed w-script"><input type="hidden" name="path" value="<%WF_FORM_VALUE_PATH%>"><input type="hidden" name="page" value="<%WF_FORM_VALUE_PAGE%>"></div>
+        <div style="display:none" class="w-password-page w-embed w-script">
+          <script type="application/javascript">(function _handlePasswordPageOnload() {
+	  if (/[?&]e=1(&|$)/.test(document.location.search)) {
+	    document.querySelector('.w-password-page.w-form-fail').style.display = 'block';
+	  }
+	})()</script>
+        </div>
+      </form>
+    </div>
+  </div>
+  <script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=683e090d3817dd3c3bca6c22" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
+  <script src="js/iggywebsite.js" type="text/javascript"></script><!--  Dynamic Copyright Year  -->
+  <script>
+window.onload=function(){var e=(new Date).getFullYear(),n=document.getElementById("year");n&&(n.innerHTML=e)};
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/404.html b/404.html
new file mode 100644
index 000000000..5cd1dc31d
--- /dev/null
+++ b/404.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html><!--  Last Published: Wed Jul 30 2025 21:47:32 GMT+0000 (Coordinated Universal Time)  -->
+<html data-wf-page="683e090d3817dd3c3bca6c55" data-wf-site="683e090d3817dd3c3bca6c22" lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>Not Found</title>
+  <meta content="Not Found" property="og:title">
+  <meta content="Not Found" property="twitter:title">
+  <meta content="width=device-width, initial-scale=1" name="viewport">
+  <link href="css/normalize.css" rel="stylesheet" type="text/css">
+  <link href="css/components.css" rel="stylesheet" type="text/css">
+  <link href="css/iggywebsite.css" rel="stylesheet" type="text/css">
+  <link href="https://fonts.googleapis.com" rel="preconnect">
+  <link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous">
+  <script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
+  <script type="text/javascript">WebFont.load({  google: {    families: ["Inconsolata:400,700","Manrope:200,300,regular,500,600,700,800"]  }});</script>
+  <script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script>
+  <link href="images/favicon.png" rel="shortcut icon" type="image/x-icon">
+  <link href="images/webclip.png" rel="apple-touch-icon">
+</head>
+<body>
+  <div class="utility-page-wrap">
+    <div class="utility-page-content w-form"><img src="https://d3e54v103j8qbb.cloudfront.net/static/page-not-found.211a85e40c.svg" alt="">
+      <h2>Page Not Found</h2>
+      <div>The page you are looking for doesn&#x27;t exist or has been moved.</div>
+    </div>
+  </div>
+  <script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=683e090d3817dd3c3bca6c22" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
+  <script src="js/iggywebsite.js" type="text/javascript"></script><!--  Dynamic Copyright Year  -->
+  <script>
+window.onload=function(){var e=(new Date).getFullYear(),n=document.getElementById("year");n&&(n.innerHTML=e)};
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 43e7580df..000000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,205 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
---------------------------------------------------
-
diff --git a/README.md b/README.md
deleted file mode 100644
index 3a3483135..000000000
--- a/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-Welcome to Apache Iggy (Incubating)
-
-# Website
-
-This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
-
-### Installation
-
-```
-$ yarn
-```
-
-### Local Development
-
-```
-$ yarn start
-```
-
-This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
-
-### Build
-
-```
-$ yarn build
-```
-
-This command generates static content into the `build` directory and can be served using any static contents hosting service.
-
-### Deployment
-
-Using SSH:
-
-```
-$ USE_SSH=true yarn deploy
-```
-
-Not using SSH:
-
-```
-$ GIT_USER=<Your GitHub username> yarn deploy
-```
-
-If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
-
-
-### Publishing
-
-Production Website: iggy.apache.org is published from `asf-site` branch
-Staging Website: iggy.staged.apache.org is published from `asf-staging` branch
\ No newline at end of file
diff --git a/blog-src/config.toml b/blog-src/config.toml
new file mode 100644
index 000000000..7237d8436
--- /dev/null
+++ b/blog-src/config.toml
@@ -0,0 +1,51 @@
+#  main zola config 
+base_url = "/blog"
+title    = "Apache Iggy Blog"
+description = "Apache Iggy Blog"
+default_language = "en"
+theme    = "apollo"
+
+compile_sass       = true        # Apollo kompiluje SCSS → CSS
+build_search_index = true
+
+taxonomies = [
+  { name = "tags", feed = true },
+]
+
+#  markdown settings
+[markdown]
+highlight_code  = true
+highlight_theme = "base16-ocean-dark"   # dowolny motyw z listy Zoli
+
+#  apollo theme data
+
+[extra]
+theme   = "dark"    # 'light' | 'dark' | 'auto'
+logo	= "/img/logo-darkbg.svg"
+socials = []
+menu    = []
+stylesheets = ["custom.css"]
+
+[extra.authors.piotr]
+name      = "Piotr Gankiewicz"
+title     = "Apache Iggy founder"
+url       = "https://github.com/spetz"
+image_url = "https://github.com/spetz.png"
+
+logo = "/img/logo.svg"
+stylesheets = []               
+extra_head = """
+<link rel="stylesheet" href="/custom.css">
+"""
+
+[extra.authors.grzegorz]
+name      = "Grzegorz Koszyk"
+title     = "Apache Iggy PPMC Member"
+url       = "https://github.com/numinnex"
+image_url = "https://github.com/numinnex.png"
+
+[extra.authors.hubert]
+name      = "Hubert Gruszecki"
+title     = "Apache Iggy PPMC Member"
+url       = "https://github.com/hubcio"
+image_url = "https://github.com/hubcio.png"
diff --git a/blog-src/content/_index.md b/blog-src/content/_index.md
new file mode 100644
index 000000000..6ea49e1b1
--- /dev/null
+++ b/blog-src/content/_index.md
@@ -0,0 +1,8 @@
++++
+title= "Apollo Theme"
+template = "section.html"
+
+
+[extra]
+section_path = "posts/_index.md"      
++++
diff --git a/blog-src/content/about.md b/blog-src/content/about.md
new file mode 100644
index 000000000..1f0623ea8
--- /dev/null
+++ b/blog-src/content/about.md
@@ -0,0 +1,4 @@
++++
+title = "About"
+path = "about"
++++
diff --git a/blog/2023-08-16-welcome.md b/blog-src/content/posts/2023-08-16-welcome.md
similarity index 80%
rename from blog/2023-08-16-welcome.md
rename to blog-src/content/posts/2023-08-16-welcome.md
index febcb837f..79a68f308 100644
--- a/blog/2023-08-16-welcome.md
+++ b/blog-src/content/posts/2023-08-16-welcome.md
@@ -1,11 +1,7 @@
 ---
 title: Welcome to the changelog
 slug: welcome-to-the-changelog
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [hello]
 hide_table_of_contents: false
 date: 2023-08-16
diff --git a/blog/2023-08-19-message-headers.md b/blog-src/content/posts/2023-08-19-message-headers.md
similarity index 92%
rename from blog/2023-08-19-message-headers.md
rename to blog-src/content/posts/2023-08-19-message-headers.md
index 5a07a17a4..0137a3ce3 100644
--- a/blog/2023-08-19-message-headers.md
+++ b/blog-src/content/posts/2023-08-19-message-headers.md
@@ -1,11 +1,7 @@
 ---
 title: Message headers
 slug: message-headers
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, message-headers, breaking-changes]
 hide_table_of_contents: false
 ---
@@ -16,17 +12,17 @@ The optional message headers which you can think of as an additional metadata fo
 
 ## Breaking changes
 
-The first draft of the message headers implementation starts with the initial commit [#bc1d0b3](https://github.com/apache/iggy/commit/bc1d0b369a268783bd329436aba3f6920aae478e). The breaking change to the streaming server has been introduced in the commit [#7c73bd3](https://github.com/apache/iggy/commit/7c73bd3d9d59fb685cbaaca5901dec6cac43ddaf) - up until this point, the existing implementation of the `SendMessages`, `PollMessages` and the underlying file system messages' storage does wor [...]
+The first draft of the message headers implementation starts with the initial commit [#bc1d0b3](https://github.com/iggy-rs/iggy/commit/bc1d0b369a268783bd329436aba3f6920aae478e). The breaking change to the streaming server has been introduced in the commit [#7c73bd3](https://github.com/iggy-rs/iggy/commit/7c73bd3d9d59fb685cbaaca5901dec6cac43ddaf) - up until this point, the existing implementation of the `SendMessages`, `PollMessages` and the underlying file system messages' storage does w [...]
 
 ## Introduction
 
-Message headers are an **optional** part of the message which can be used to provide the additional metadata for the message (they are not a part of the message body. Similar to e.g. HTTP headers, you can think of them as a **key-value pairs**, or more precisely the dictionary or map. The headers consist of the key and the value, where the key is a string (**UTF-8 bytes**) and the value is a byte array.
+Message headers are an **optional** part of the message which can be used to provide the additional metadata for the message (they are not a part of the message body. Similar to e.g. HTTP headers, you can think of them as a **key-value pairs**, or more precisely the dictionary or map. The headers consist of the key and the value, where the key is a string (**UTF-8 bytes**) and the value is a byte array. 
 
 Since the headers are optional, you can send a message without any headers at all. There's no limit on the number of headers you can send, but the total size of the headers is currently limited to **100 KB**. The header key is **case-sensitive**, so it's best to send the **lowercase** keys. The maximum length of the key and the value is **255 bytes** (the maximum value of the `u8` type).
 
 For now, there are no reserved headers, so you can use any key you want. However, in the future, we might introduce some reserved headers used by the streaming server for the specific purposes such as the message compression, distributed tracing and so no.
 
-The **sample applications** using the message headers can be found [here](https://github.com/apache/iggy/tree/master/core/examples/src/message-headers).
+The **sample applications** using the message headers can be found [here](https://github.com/iggy-rs/iggy/tree/master/examples/src/message-headers).
 
 ## Implementation
 
@@ -251,4 +247,4 @@ When you poll the messages and want to read the header value based on its kind,
 let value1 = headers.get(&HeaderKey::new("key 1")?)?.as_str()?;
 let value2 = headers.get(&HeaderKey::new("key-2")?)?.as_bool()?;
 let value3 = headers.get(&HeaderKey::new("key_3")?)?.as_uint64()?;
-```
+```
\ No newline at end of file
diff --git a/blog/2023-08-22-message-state.md b/blog-src/content/posts/2023-08-22-message-state.md
similarity index 88%
rename from blog/2023-08-22-message-state.md
rename to blog-src/content/posts/2023-08-22-message-state.md
index 360991f8e..2b1e71547 100644
--- a/blog/2023-08-22-message-state.md
+++ b/blog-src/content/posts/2023-08-22-message-state.md
@@ -1,11 +1,7 @@
 ---
 title: Message state
 slug: message-state
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, message-state, message-checksum]
 hide_table_of_contents: false
 ---
@@ -16,11 +12,11 @@ The message state is a simple field which extends the existing `message` and pro
 
 ## Breaking changes
 
-Starting with the commit [#b07f23d](https://github.com/apache/iggy/commit/b07f23db798ffcda7c39650f34547f20137ff725) the breaking changes related to polling the messages by the client have been introduced. The `Message` struct used when invoking `PollMessage` command has been extended with new fields, which results in updated binary schema. The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.40.
+Starting with the commit [#b07f23d](https://github.com/iggy-rs/iggy/commit/b07f23db798ffcda7c39650f34547f20137ff725) the breaking changes related to polling the messages by the client have been introduced. The `Message` struct used when invoking `PollMessage` command has been extended with new fields, which results in updated binary schema. The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.40.
 
 ## Introduction
 
-The `state` is an additional field added to the `message` struct stored on the server-side, as well as polled by the client, which describes the current state of the message. By default, the message state is set to `available` (`code = 1``, see the possible codes below), meaning that the message is available to be consumed by the client.
+The `state` is an additional field added to the `message` struct stored on the server-side, as well as polled by the client, which describes the current state of the message. By default, the message state is set to `available` (`code = 1``, see the possible codes below), meaning that the message is availabe to be consumed by the client.
 
 However, the state might have a different value, for example `marked_for_deletion` (to be used by the upcoming retention policy feature), `poisoned` (to be used by the upcoming dead-letter queue feature) etc. In the future releases, the server will also provide a way to change the state of the message (for example to mark it as `poisoned` by the client), as well as filtering the messages by the state.
 
@@ -145,4 +141,4 @@ And the serialization has changed to:
 Offset (8 bytes) + State (1 byte) + Timestamp (8 bytes) + ID (16 bytes) + Checksum (4 bytes) + Headers (N bytes) + Length (4 bytes) + Payload (`Length` bytes)
 ```
 
-And that's it - simply (de)serialize the `state` field right after the `offset` field, and the `checksum` field right after the `id` field.
+And that's it - simply (de)serialize the `state` field right after the `offset` field, and the `checksum` field right after the `id` field.
\ No newline at end of file
diff --git a/blog/2023-08-27-message-expiry.md b/blog-src/content/posts/2023-08-27-message-expiry.md
similarity index 92%
rename from blog/2023-08-27-message-expiry.md
rename to blog-src/content/posts/2023-08-27-message-expiry.md
index 9ad8e4b78..359c071b1 100644
--- a/blog/2023-08-27-message-expiry.md
+++ b/blog-src/content/posts/2023-08-27-message-expiry.md
@@ -1,11 +1,7 @@
 ---
 title: Message expiry
 slug: message-expiry
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, message-expiry]
 hide_table_of_contents: false
 ---
@@ -16,15 +12,15 @@ The message expiration is a server-side feature, allowing to automatically delet
 
 ## Breaking changes
 
-No breaking changes have been introduced with this feature. The only breaking changes are related to the updated `CreateTopic`, `CreateStream` commands and `Stream` + `Topic` responses, which have been introduced in the latest commit [#ea3bf9c](https://github.com/apache/iggy/commit/ea3bf9c16dd5f93e1c80c140e9e1d14cfa70f570). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.50.
+No breaking changes have been introduced with this feature. The only breaking changes are related to the updated `CreateTopic`, `CreateStream` commands and `Stream` + `Topic` responses, which have been introduced in the latest commit [#ea3bf9c](https://github.com/iggy-rs/iggy/commit/ea3bf9c16dd5f93e1c80c140e9e1d14cfa70f570). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.50.
 
 ## Introduction
 
 The expiration policy is defined per topic, and it's being set when creating the topic. The value can be either provided via server configuration file, or via `CreateTopic` command. If the expiration is set in the configuration, it will be used as a default value for all the topics, unless when explicitly set when creating the topic via available `message_expiry` field.
 
-The expiration policy is defined as `u32` value, which is the number of seconds after which the message should be marked as expired.
+The expiration policy is defined as `u32` value, which is the number of seconds after which the message should be marked as expired. 
 
-For example, the value of 604800 represents 1 week (60 \* 60 \* 24 \* 7) - after this time, the messages will be marked as expired, and the whole segment will be deleted, as long as all the messages in this segment are expired.
+For example, the value of 604800 represents 1 week (60 \* 60 \* 24 \* 7) - after this time, the messages will be marked as expired, and the whole segment will be deleted, as long as all the messages in this segment are expired. 
 
 If there are still some messages which are not expired, the segment will be kept on the disk. By default, the new segment is automatically created when the previous one is full - either, it has reached the maximum size (defined by `size_bytes` property) or the currently stored messages have been expired, based on the current server timestamp and configured expiry value.
 
@@ -82,11 +78,11 @@ fn as_bytes(&self) -> Vec<u8> {
 ```
 
 
-Additionally, the `name_length` field has been added to the binary serialization of the `CreateTopic` and `CreateStream` commands, which allows specifying the length of the name - it should be serialized as `u8` value, before the actual name, with a maximum length of 255 characters.
+Additionally, the `name_length` field has been added to the binary serialization of the `CreateTopic` and `CreateStream` commands, which allows specifying the length of the name - it should be serialized as `u8` value, before the actual name, with a maximum length of 255 characters. 
 
 The following **regex** is used to validate the topic and stream names: `^[\w\.\-\s]+$`. The name will always be lowercased, and the whitespace will be trimmed and then replaced with the `.` character. The following rule applies to all the resources that contain the name field.
 
-Also, the `Topic` and `TopicDetails` returned when fetching the topic(s), have been extended with the `message_expiry` field, and the `name_length` has changed value from `u32 to `u8` (1 byte instead of 4 bytes).
+Also, the `Topic` and `TopicDetails` returned when fetching the topic(s), have been extended with the `message_expiry` field, and the `name_length` has changed value from `u32 to `u8` (1 byte instead of 4 bytes). 
 
 Serialization:
 
@@ -98,4 +94,4 @@ The same changes (`name_length`) apply to the `Stream` and `StreamDetails` struc
 
 ```
 Stream ID (4 bytes) + Topics count (4 bytes) + Size (8 bytes) + Messages count (8 bytes) + Name length (1 byte) + Name (`Name length` bytes)
-```
+```
\ No newline at end of file
diff --git a/blog/2023-08-31-updated-schemas.md b/blog-src/content/posts/2023-08-31-updated-schemas.md
similarity index 86%
rename from blog/2023-08-31-updated-schemas.md
rename to blog-src/content/posts/2023-08-31-updated-schemas.md
index 4b75cc432..e76d49a53 100644
--- a/blog/2023-08-31-updated-schemas.md
+++ b/blog-src/content/posts/2023-08-31-updated-schemas.md
@@ -1,11 +1,7 @@
 ---
 title: Updated schemas
 slug: updated-schemas
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [update, schema]
 hide_table_of_contents: false
 ---
@@ -16,7 +12,7 @@ The latest update introduces the changes to the `PollMessages` and `GetConsumerO
 
 ## Breaking changes
 
-Breaking changes have been introduced with the commit [#670a8ba](https://github.com/apache/iggy/commit/670a8baba8617bb7e52bd51d556a621349e2e9f0). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.60.
+Breaking changes have been introduced with the commit [#670a8ba](https://github.com/iggy-rs/iggy/commit/670a8baba8617bb7e52bd51d556a621349e2e9f0). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.60.
 
 ## PollMessages
 
@@ -58,4 +54,4 @@ Partition ID (4 bytes) + Current offset (8 bytes) + Stored offset (8 bytes)
 
 The `Stream`, `Topic` and `Partition` structs (along with the additional `Details` models when fetching the single object, not a list) have been extended with the `created_at` field, which contains the timestamp of the creation time (EPOCH in microseconds).
 
-The `created_at` field which is `u64` (8 bytes) is always serialized right after the `id` field, and then the remaining fields follow.
+The `created_at` field which is `u64` (8 bytes) is always serialized right after the `id` field, and then the remaining fields follow.
\ No newline at end of file
diff --git a/blog/2023-09-20-users-and-permissions.md b/blog-src/content/posts/2023-09-20-users-and-permissions.md
similarity index 96%
rename from blog/2023-09-20-users-and-permissions.md
rename to blog-src/content/posts/2023-09-20-users-and-permissions.md
index 963decca7..73b32f433 100644
--- a/blog/2023-09-20-users-and-permissions.md
+++ b/blog-src/content/posts/2023-09-20-users-and-permissions.md
@@ -1,11 +1,7 @@
 ---
 title: Users and permissions
 slug: users-and-permissions
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, users, permissions]
 hide_table_of_contents: false
 ---
@@ -32,7 +28,7 @@ The `user_id` field is optional, and it's only returned when the user is authent
 
 Keep in mind that the `client` represents the application connected to the server - since it's possible that multiple applications would share the same user credentials, thus you can think of a `client` as a single connection from the application to the server (1 user -> N clients).
 
-This breaking change has been introduced with the commit [#23b3309](https://github.com/apache/iggy/commit/23b330928aa38463ef907c95a9f672fa7b728881). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.80.
+This breaking change has been introduced with the commit [#23b3309](https://github.com/iggy-rs/iggy/commit/23b330928aa38463ef907c95a9f672fa7b728881). The available [iggy crate](https://crates.io/crates/iggy) supports these changes since version 0.0.80.
 
 ## Configuration
 
@@ -368,4 +364,4 @@ Content-Type: application/json
 
 DELETE {{url}}/users/user1
 Authorization: Bearer {{token}}
-```
+```
\ No newline at end of file
diff --git a/blog/2023-10-02-consumer-identifier.md b/blog-src/content/posts/2023-10-02-consumer-identifier.md
similarity index 91%
rename from blog/2023-10-02-consumer-identifier.md
rename to blog-src/content/posts/2023-10-02-consumer-identifier.md
index c89894ea8..8a5934b4b 100644
--- a/blog/2023-10-02-consumer-identifier.md
+++ b/blog-src/content/posts/2023-10-02-consumer-identifier.md
@@ -1,11 +1,7 @@
 ---
 title: Consumer identifier
 slug: consumer-identifier
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, consumers, identifier]
 hide_table_of_contents: false
 ---
@@ -16,7 +12,7 @@ In the latest update, the Iggy server as well as the clients for all the availab
 
 ## Breaking changes
 
-The breaking changes have been introduced in Iggy server, starting from version `0.0.30` and Iggy SDK, starting from version `0.0.90`. The final commit containing all the mentioned changes is [#655f9b6](https://github.com/apache/iggy/commit/655f9b6bccb0ae4148422f32475a9bedc09827d2).
+The breaking changes have been introduced in Iggy server, starting from version `0.0.30` and Iggy SDK, starting from version `0.0.90`. The final commit containing all the mentioned changes is [#655f9b6](https://github.com/iggy-rs/iggy/commit/655f9b6bccb0ae4148422f32475a9bedc09827d2).
 
 ## Consumer identifier
 
@@ -91,4 +87,4 @@ pub struct ConsumerGroupDetails {
 }
 ```
 
-When fetching the consumer group(s), the `ConsumerGroup` and `ConsumerGroupDetails` structs have been extended with the `name` field, which serialized after `members_count`.
+When fetching the consumer group(s), the `ConsumerGroup` and `ConsumerGroupDetails` structs have been extended with the `name` field, which serialized after `members_count`.
\ No newline at end of file
diff --git a/blog/2023-10-12-personal-access-tokens.md b/blog-src/content/posts/2023-10-12-personal-access-tokens.md
similarity index 92%
rename from blog/2023-10-12-personal-access-tokens.md
rename to blog-src/content/posts/2023-10-12-personal-access-tokens.md
index e4bf72df4..8967a9b9e 100644
--- a/blog/2023-10-12-personal-access-tokens.md
+++ b/blog-src/content/posts/2023-10-12-personal-access-tokens.md
@@ -1,11 +1,7 @@
 ---
 title: Personal access tokens
 slug: personal-access-tokens
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [new-features, personal-access-tokens, pat]
 hide_table_of_contents: false
 ---
@@ -16,12 +12,12 @@ Since the most recent update, Iggy.rs supports personal access tokens, which can
 
 ## Breaking changes
 
-No breaking changes have been introduced in neither Iggy server, nor Iggy SDK. The server does support the new `PAT` authentication method since version `0.0.40`, and the SDK since version `0.0.100`. The initial changes are part of the commit [#e74c25e](https://github.com/apache/iggy/commit/e74c25e058b1f39119ee89b5ada5d93f171cb221).
+No breaking changes have been introduced in neither Iggy server, nor Iggy SDK. The server does support the new `PAT` authentication method since version `0.0.40`, and the SDK since version `0.0.100`. The initial changes are part of the commit [#e74c25e](https://github.com/iggy-rs/iggy/commit/e74c25e058b1f39119ee89b5ada5d93f171cb221).
 
 ## Personal access tokens
 
 PAT is a simple idea, which allows authenticating the clients using a token, instead of the regular credentials (username and password). This approach might feel safer for some users, as the token can be deleted at any time, and it's not tied to the user's password. The tokens can be created, listed and deleted using the available APIs. PAT has an optional expiry, which can be set when creating the token.
-
+ 
 
 ```rust
 pub struct CreatePersonalAccessToken {
@@ -64,7 +60,7 @@ async fn login_with_personal_access_token(
 ) -> Result<IdentityInfo, Error>;
 ```
 
-When creating the token, the server returns the `RawPersonalAccessToken` struct, which contains the string `token` value.
+When creating the token, the server returns the `RawPersonalAccessToken` struct, which contains the string `token` value. 
 
 ```rust
 pub struct RawPersonalAccessToken {
@@ -93,4 +89,4 @@ On the server side, you can enable the background task responsible for deleting
 [personal_access_token_cleaner]
 enabled = true
 interval = 60
-```
+```
\ No newline at end of file
diff --git a/blog/2023-11-26-release-0.1.0.md b/blog-src/content/posts/2023-11-26-release-0.1.0.md
similarity index 88%
rename from blog/2023-11-26-release-0.1.0.md
rename to blog-src/content/posts/2023-11-26-release-0.1.0.md
index 41784ba6f..72d7efe29 100644
--- a/blog/2023-11-26-release-0.1.0.md
+++ b/blog-src/content/posts/2023-11-26-release-0.1.0.md
@@ -1,11 +1,7 @@
 ---
 title: Iggy 0.1.0 release
 slug: iggy-0-1-0-release
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: [release]
 hide_table_of_contents: false
 date: 2023-11-26
@@ -43,4 +39,4 @@ While Rust **SDK** is the most mature one, and the C# is a close second, the rem
 
 Last but not least, we are **extremely grateful to the community**, which is growing every day. We are getting more and more feedback, and we are doing our best to address all the issues and feature requests. We are also getting more and more contributions, which is a great sign that the project is going in the right direction.
 
-Come and join our [Discord](https://discord.gg/C5Sux5NcRa) server, and let us know what you think about the project. We are looking forward to hearing from you!
\ No newline at end of file
+Come and join our [Discord](https://iggy.rs/discord) server, and let us know what you think about the project. We are looking forward to hearing from you!
\ No newline at end of file
diff --git a/blog/2023-12-29-building-message-streaming-in-rust.md b/blog-src/content/posts/2023-12-29-building-message-streaming-in-rust.md
similarity index 90%
rename from blog/2023-12-29-building-message-streaming-in-rust.md
rename to blog-src/content/posts/2023-12-29-building-message-streaming-in-rust.md
index bcd359c50..6d33d3e81 100644
--- a/blog/2023-12-29-building-message-streaming-in-rust.md
+++ b/blog-src/content/posts/2023-12-29-building-message-streaming-in-rust.md
@@ -1,10 +1,6 @@
 ---
 title: Iggy.rs - building message streaming in Rust
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2023-12-29
@@ -12,15 +8,13 @@ date: 2023-12-29
 
 ## Origins
 
-Over half a year ago (in April, to be exact), I eventually decided to learn Rust for good. My previous attempt during the [2022 AoC](https://github.com/spetz/advent-of-code-2022) had failed rather quickly, after a few days of completing the exercises - I finally realized that **I needed a real project to work on**. For the last few years, I've been dealing with the different kinds of distributed systems (mostly using C#), including the typical microservices architecture or Web3. Regardle [...]
-
-<!--truncate-->
+Over half a year ago (in April, to be exact), I eventually decided to learn Rust for good. My previous attempt during the [2022 AoC](https://github.com/spetz/advent-of-code-2022) had failed rather quickly, after a few days of completing the exercises - I finally realized that **I needed a real project to work on**. For the last few years, I've been dealing with the different kinds of distributed systems (mostly using C#), including the typical microservices architecture or Web3. Regardle [...]
 
 After a few months of trying to figure out (or just staying in the limbo I guess), what would be the best project to work on, I decided to build the **message streaming platform** (keep in mind that streaming is [not the same](https://blog.iron.io/message-queue-vs-streaming/) as regular message broker). The other reason (besides getting to know Rust) was to **truly understand the internals of the messaging systems** and the trade-offs that were made by their developers - some of them bei [...]
 
 And this is how the [Iggy.rs](https://iggy.rs) was born. The name is an abbreviation of the Italian Greyhound (yes, I own [two of them](https://www.instagram.com/fabio.and.cookie/)), small yet extremely fast dogs, the best in their class.
 
-![image](/building-message-streaming/iggys.jpeg)
+![image](/building-message-streaming/iggys.jpeg) 
 
 Therefore, what I want, or actually **what we want** (since there's a few of us working on it already) for Iggy.rs to be - the best message streaming platform in its class. **Lightweight** in terms of the resource consumption, **fast** (and predictable) when it comes to the throughput and latency, and **easy to use** when speaking of its API, SDK and configuration of the project.
 
@@ -28,11 +22,11 @@ Therefore, what I want, or actually **what we want** (since there's a few of us
 
 At the very beginning, Iggy had rather limited functionality, and everything was handled using the [QUIC](https://www.chromium.org/quic/) protocol based on [Quinn](https://github.com/quinn-rs/quinn) library. You could connect multiple applications into the server, and start exchanging the messages between them, simply by appending the data to the stream (from the producer perspective), and fetching the records on the consumer side, by providing an offset (numeric value specifying from wh [...]
 
-After spending a few weeks on building the initial version, and then another few weeks on rewriting its core part (yes, prototyping + validation repeated in a continuous loop worked quite well), I managed to implement the persistent streaming server being capable of parallel writes/reads to/from independent streams supporting many distinct apps connected into it. Simply put, one could easily have many applications, and even **thousands of the streams** (depending on how do you decide to  [...]
+After spending a few weeks on building the initial version, and then another few weeks on rewriting its core part (yes, prototyping + validation repeated in a continuous loop worked quite well), I managed to implement the persistent streaming server being capable of parallel writes/reads to/from independent streams supporting many distinct apps connected into it. Simply put, one could easily have many applications, and even **thousands of the streams** (depending on how do you decide to  [...]
 
 On top of this, the support for **TCP** and **HTTP** protocols have been added. Under the hood, the typical architecture of streams, consisting of the topics being split into the partitions, which eventually operate on a raw file data using so-called segments has been implemented as well.
 
-![image](/building-message-streaming/iggy_sample.jpeg)
+![image](/building-message-streaming/iggy_sample.jpeg) 
 
 **It was one of the "aha" moments**, when reimplementing the parallel access to the data with the usage of underlying synchronization mechanism (*RwLock* etc.), optimized data structures e.g. for dealing with [bytes](https://crates.io/crates/bytes), along with the [Tokio](https://tokio.rs) **work stealing** approach, yielded the great improvements for the overall throughput.
 
@@ -50,9 +44,9 @@ let polled_messages = client.poll_messages(&PollMessages {
 }).await?;
 ```
 
-After running some benchmarks (yes, we have a dedicated app for the **[benchmarking purposes](https://github.com/apache/iggy/tree/master/core/bench)**) and seeing the promising numbers (**range of 2-6 GB/s for both, writes & reads when processing millions of messages**), I eventually decided to give it a long-term shot. Being fully aware that there's still lots to be done (speaking of many months, or even years), I couldn't be more happy to find out that there's also someone else out the [...]
+After running some benchmarks (yes, we have a dedicated app for the **[benchmarking purposes](https://github.com/iggy-rs/iggy/tree/master/bench)**) and seeing the promising numbers (**range of 2-6 GB/s for both, writes & reads when processing millions of messages**), I eventually decided to give it a long-term shot. Being fully aware that there's still lots to be done (speaking of many months, or even years), I couldn't be more happy to find out that there's also someone else out there,  [...]
 
-![image](/building-message-streaming/iggy_bench.jpeg)
+![image](/building-message-streaming/iggy_bench.jpeg) 
 
 ## Team
 
@@ -60,7 +54,7 @@ At the time of writing this post, **Iggy consists of around 10 members** contrib
 
 Well, I wish I had an answer to that question - honestly, in case of Iggy I wasn't actually looking for anyone, as I didn't think this could be an interesting project to work on (except for myself). Then how did that happen anyway? There were only 2 things in common - all the people that joined the project were part of the same Discord communities, yet more importantly **they all shared the passion for programming**, and I'm not talking about Rust language specifically. From junior to se [...]
 
-![image](/building-message-streaming/iggy_server.jpeg)
+![image](/building-message-streaming/iggy_server.jpeg) 
 
 For example, when I asked one guy what was the reason behind building an SDK in Go, the reply was the need of playing with and learning a new language. Why C# SDK? Well, the other guy wanted to dive more into the low-level concepts and decided to squeeze out great performance from the managed runtime. Why build Web UI in Svelte? At work, I mostly use React, and I wanted to learn a new framework - another member said.
 
@@ -113,17 +107,17 @@ And as already mentioned, we've been working on SDKs for the multiple programmin
 
 Please keep in mind, though, that some of them e.g. for Rust or C# are more up to date with the recent server changes, while the other ones might still need to do some catching up with the latest features. However, given the amount of available methods on the server's API and the underlying TCP/UDP stack with custom serialization to be implemented from the scratch (except for HTTP transport, that's the easier one), I'd say we're doing quite ok, and I can't stress enough **how grateful I  [...]
 
-But wait, there's even more - what would be a message streaming platform without some additional tooling for managing it? We've also been developing the **[CLI](https://github.com/apache/iggy/tree/master/cmd)**.
+But wait, there's even more - what would be a message streaming platform without some additional tooling for managing it? We've also been developing the **[CLI](https://github.com/iggy-rs/iggy/tree/master/cmd)**.
 
-![image](/building-message-streaming/iggy_cli.jpeg)
+![image](/building-message-streaming/iggy_cli.jpeg) 
 
 As well as modern **[Web UI](https://github.com/iggy-rs/iggy-web-ui)** to make it happen :)
 
-![image](/building-message-streaming/iggy_web_ui.jpeg)
+![image](/building-message-streaming/iggy_web_ui.jpeg) 
 
-Last but not least, we've got a fully-featured [CI/CD pipeline](https://github.com/apache/iggy/actions) responsible for running all the checks and tests on multiple platforms, and finally producing the release artifacts and [Docker images](https://hub.docker.com/u/iggyrs).
+Last but not least, we've got a fully-featured [CI/CD pipeline](https://github.com/iggy-rs/iggy/actions) responsible for running all the checks and tests on multiple platforms, and finally producing the release artifacts and [Docker images](https://hub.docker.com/u/iggyrs).
 
-![image](/building-message-streaming/iggy_ci_cd.jpeg)
+![image](/building-message-streaming/iggy_ci_cd.jpeg) 
 
 At first glance, it might look like there's plenty of features already in place, but for anyone who has ever worked with the message streaming infrastructure before, that might be just a tip of an iceberg, thus let's discuss the roadmap.
 
@@ -133,7 +127,7 @@ After gaining some traction a few months ago (mostly due to landing on the GitHu
 
 ![image](/building-message-streaming/gh_trending.jpeg)
 
-Considering what's already there, being worked on or planned for the future releases, such as interactive CLI, modern Web UI, optional data compression and archivization, plugin support or multiple SDKs, there are at least three additional challenges to overcome:
+Considering what's already there, being worked on or planned for the future releases, such as interactive CLI, modern Web UI, optional data compression and archivization, plugin support or multiple SDKs, there are at least three additonal challenges to overcome:
 
 **Clustering** - the possibility of having a **highly available and fault tolerant** distributed message streaming platform in a production environment, is typically one of the most important aspects when considering the particular tool. While it wouldn't be too difficult to implement the extension (think of a simple proxy/load balancer), allowing to monitor and deliver the data either to the primary or secondary replica (treated as a fallback server) and switch between them when one of  [...]
 
@@ -147,8 +141,8 @@ Considering what's already there, being worked on or planned for the future rele
 
 **Why building another message streaming then?** A few months ago, I would probably answer - strictly for fun. Yet, after exploring more in-depth the status quo, what we would like to achieve is sort of twofold - on one hand, it'd be great to deliver the general-purpose tool, such as Kafka. On the other hand, why not to try and really push hard the OS and hardware to its limits when speaking of the performance, reliability, throughput and latency, something what e.g. Aeron does? And what [...]
 
-Only the time will tell, but **we're already excited enough to challenge ourselves**. We'd love to hear your thoughts, ideas and **feedback** - anything that will help us in building the best message streaming platform in Rust that you will enjoy using! Feel free to join our [Discord](https://discord.gg/C5Sux5NcRa) community and let us know what do you think :)
+Only the time will tell, but **we're already excited enough to challenge ourselves**. We'd love to hear your thoughts, ideas and **feedback** - anything that will help us in building the best message streaming platform in Rust that you will enjoy using! Feel free to join our [Discord](https://iggy.rs/discord) community and let us know what do you think :)
 
 P.S.
 
-This blog uses [Rust](https://www.getzola.org/).
+This blog uses [Rust](https://www.getzola.org/).
\ No newline at end of file
diff --git a/blog/2024-05-29-one-year-of-building-the-message-streaming.md b/blog-src/content/posts/2024-05-29-one-year-of-building-the-message-streaming.md
similarity index 87%
rename from blog/2024-05-29-one-year-of-building-the-message-streaming.md
rename to blog-src/content/posts/2024-05-29-one-year-of-building-the-message-streaming.md
index e0d00ce1e..7b050ffb2 100644
--- a/blog/2024-05-29-one-year-of-building-the-message-streaming.md
+++ b/blog-src/content/posts/2024-05-29-one-year-of-building-the-message-streaming.md
@@ -1,10 +1,6 @@
 ---
 title: Iggy.rs — one year of building the message streaming
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2024-05-29
@@ -12,23 +8,21 @@ date: 2024-05-29
 
 ## Throwback
 
-**It's been a little over a year**, since the [Iggy.rs](https://iggy.rs) was born. The initial idea of building a side project (as a way of studying Rust) — an infrastructure for the message streaming (think of Kafka, RedPanda, Aeron etc.) — eventually turned out to be something much bigger that I could've ever imagined. In the [previous post](https://blog.iggy.rs/posts/building-message-streaming-in-rust/) (from almost half a year ago), I did describe what's [Iggy.rs](https://github.com/ [...]
+**It's been a little over a year**, since the [Iggy.rs](https://iggy.rs) was born. The initial idea of building a side project (as a way of studying Rust) — an infrastructure for the message streaming (think of Kafka, RedPanda, Aeron etc.) — eventually turned out to be something much bigger that I could've ever imagined. In the [previous post](https://blog.iggy.rs/posts/building-message-streaming-in-rust/) (from almost half a year ago), I did describe what's [Iggy.rs](https://github.com/ [...]
 
-<!--truncate-->
-
-This **particular article** turned out to be a sort of **catalyst**, as it received a really nice traction on [Reddit](https://www.reddit.com/r/rust/comments/18tgubd/iggyrs_building_message_streaming_in_rust/), and was also mentioned on the main site of [Hacker News](https://news.ycombinator.com/item?id=38868115), which I do believe were the two main reasons for the growing interest & community since then. At this point, I'd like to **thank you all very much** for such a kind feedback —  [...]
+This **particular article** turned out to be a sort of **catalyst**, as it received a really nice traction on [Reddit](https://www.reddit.com/r/rust/comments/18tgubd/iggyrs_building_message_streaming_in_rust/), and was also mentioned on the main site of [Hacker News](https://news.ycombinator.com/item?id=38868115), which I do believe were the two main reasons for the growing interest & community since then. At this point, I'd like to **thank you all very much** for such a kind feedback —  [...]
 
 And although it may seem as if the project development has recently slowed down a bit, I'd say it's quite the opposite — let me quickly summary, what we've achieved so far during the last few months and what we're focusing on now, as the future looks bright :)
 
-![image](/one-year-of-building-the-message-streaming/iggy_stars.png)
+![image](/one-year-of-building-the-message-streaming/iggy_stars.png) 
 
 ## Community
 
-First and foremost, if it weren't for the community, we wouldn't have seen such an enormous growth of the Iggy's ecosystem — we've received the dozens of pull requests and there's ~250 members on our [Discord](https://discord.gg/C5Sux5NcRa). Whether we talk about bug fixes, improvements, new features, or just sharing the experiences and discussing potential ideas — it's all equally important.
+First and foremost, if it weren't for the community, we wouldn't have seen such an enormous growth of the Iggy's ecosystem — we've received the dozens of pull requests and there's ~250 members on our [Discord](https://iggy.rs/discord). Whether we talk about bug fixes, improvements, new features, or just sharing the experiences and discussing potential ideas — it's all equally important.
 
 And it's been even more than that — we've seen our community members take on **building the new SDKs** in their favorite programming languages, fully on their own. Today, you can find the following list of supported SDKs for Iggy.rs — some of them could be lagging behind, but it's expected, as the project is still evolving, and it's not an easy task, to come up with a great development experience from the very beginning.
 
-- [Rust](https://github.com/apache/iggy)
+- [Rust](https://github.com/iggy-rs/iggy)
 - [C#](https://github.com/iggy-rs/iggy-dotnet-client)
 - [C++](https://github.com/iggy-rs/iggy-cpp-client)
 - [Node (TS)](https://github.com/iggy-rs/iggy-node-client)
@@ -53,23 +47,23 @@ Adding the brand new SDKs wasn't the only great thing that has happened during l
 
 And at the same time, we've been experimenting a lot with some fancy stuff, which you can read about in the last paragraphs :)
 
-![image](/one-year-of-building-the-message-streaming/iggy_perf.png)
+![image](/one-year-of-building-the-message-streaming/iggy_perf.png) 
 
 ## Tooling
 
-The core message streaming server and multiple SDKs might sound as the most important parts of the whole ecosystem, but let's not forget about the **management tools**. How to quickly connect to the server, create new topics, validate if the messages are being sent correctly, change the user permissions or check the node statistics?
+The core message streaming server and multiple SDKs might sound as the most important parts of the whole ecosystem, but let's not forget about the **management tools**. How to quickly connect to the the server, create new topics, validate if the messages are being sent correctly, change the user permissions or check the node statistics?
 
-This is where our **[CLI](https://github.com/apache/iggy/tree/master/core/cli) and [Web UI](https://github.com/iggy-rs/iggy-web-ui) come in handy**. If you're a fan of working with the terminal and used to the great developer experience, you'll find our CLI a joy to work with.
+This is where our **[CLI](https://github.com/iggy-rs/iggy/tree/master/cli) and [Web UI](https://github.com/iggy-rs/iggy-web-ui) come in handy**. If you're a fan of working with the terminal and used to the great developer experience, you'll find our CLI a joy to work with.
 
-![image](/one-year-of-building-the-message-streaming/iggy_cli.png)
+![image](/one-year-of-building-the-message-streaming/iggy_cli.png) 
 
 On the other hand, if you prefer a graphical interface accessible via your browser, Web UI has got you covered. What's even more impressive, is that both of these tools have been developed by the single developers.
 
-![image](/one-year-of-building-the-message-streaming/iggy_web.png)
+![image](/one-year-of-building-the-message-streaming/iggy_web.png) 
 
-Last but not least, in order to run the benchmarks, we have our own [bench](https://github.com/apache/iggy/tree/master/core/bench) available as a part of the core repository — you can easily configure the number of producers, consumers, streams, etc. and get an overview of the possible streaming performance on your machine.
+Last but not least, in order to run the benchmarks, we have our own [bench](https://github.com/iggy-rs/iggy/tree/master/bench) available as a part of the core repository — you can easily configure the number of producers, consumers, streams, etc. and get an overview of the possible streaming performance on your machine.
 
-![image](/one-year-of-building-the-message-streaming/iggy_bench.png)
+![image](/one-year-of-building-the-message-streaming/iggy_bench.png) 
 
 ## Early adopters
 
@@ -77,7 +71,7 @@ Overall, coding and implementing new features is one side of the story, the othe
 
 In our case, I do believe, that it was a mix of two things — a limited amount of such tooling in the Rust ecosystem (so that the language enthusiasts could to try out something fresh), as well as a much more lightweight and (hopefully) performant message streaming infrastructure than some of the well-established solutions.
 
-I'm fully aware that it's a bold claim, and running the synthetic benchmarks is not a viable proof (e.g. on my 7950X, I was able to hit **3GB/s writes and up to 10 GB/s reads** with some additional caching enabled), yet, most of our early adopters were very happy with their results e.g. outnumbering Kafka while utilizing much less memory. For example, [Marvin](https://github.com/apache/iggy/issues/606) wrote:
+I'm fully aware that it's a bold claim, and running the synthetic benchmarks is not a viable proof (e.g. on my 7950X, I was able to hit **3GB/s writes and up to 10 GB/s reads** with some additional caching enabled), yet, most of our early adopters were very happy with their results e.g. outnumbering Kafka while utilizing much less memory. For example, [Marvin](https://github.com/iggy-rs/iggy/issues/606) wrote:
 
 > 20 million msg/sec via tcp is pretty nuts and already blows several commercial systems out of the water.
 
@@ -89,7 +83,7 @@ One of the most frequent questions we receive is whether we plan to incorporate
 
 And yes, we will certainly implement clustering in Iggy — as a matter of fact, we've already built its basic version in the [sandbox repository](https://github.com/iggy-rs/iggy-cluster-sandbox). In order to achieve that, we've decided to (at least for now) stick to the [Raft](https://raft.github.io) consensus algorithm. However, adding the data replication feature to the core Iggy project will require a new way of storing the server metadata (most likely in a way of event-sourced message [...]
 
-![image](/one-year-of-building-the-message-streaming/iggy_cluster.png)
+![image](/one-year-of-building-the-message-streaming/iggy_cluster.png) 
 
 ## io_uring & thread-per-core + share-nothing
 
@@ -99,11 +93,11 @@ However, due to the nature of tasks being shared across the different threads, *
 
 Certainly, there are already existing solutions dealing with such challenges, such as [Aeron](https://aeron.io), but **we do believe, that we can make Iggy something much easier to use** — one that can handle the typical workloads, as well as the very demanding ones, without the need of getting a PhD in specific tooling :)
 
-We've decided to **experiment with [io_uring](https://unixism.net/loti/what_is_io_uring.html) to maximize the I/O performance** (and at the same time vastly reduce the need of context switches), and at the same time utilize **thread-per-core architecture**, where **each thread is pinned to the CPU core**, thus keeping the data locally, without the need of sharing it with the other threads (share-nothing). In order to achieve this, we've picked up [monoio](https://github.com/bytedance/mon [...]
+We've decided to **experiment with [io_uring](https://unixism.net/loti/what_is_io_uring.html) to maximize the I/O performance** (and at the same time vastly reduce the need of context switches), and at the same time utilize **thread-per-core architecture**, where **each thread is pinned to the CPU core**, thus keeping the data locally, without the need of sharing it with the other threads (share-nothing). In order to achieve this, we've picked up [monoio](https://github.com/bytedance/mon [...]
 
 And just recently, we've established yet another [sandbox repository](https://github.com/iggy-rs/iggy-thread-per-core-sandbox) to tackle the different challenges before deciding on the best solution possible and merging these changes into core Iggy streaming server.
 
-![image](/one-year-of-building-the-message-streaming/iggy_tpc.png)
+![image](/one-year-of-building-the-message-streaming/iggy_tpc.png) 
 
 So far, we've got a very simple prototype in place, but there's still lots to be done, especially when thinking of:
 
@@ -126,10 +120,10 @@ As mentioned before, **some of our users already experiment with Iggy** — and
 
 We can't guarantee that it will always be like this, but at least for now, we do not see anything that would dramatically impact the existing solution. One of such things could be the **redesigned storage, I/O, and clustering feature** (as described in the previous paragraph), but even then, we'll do our best to make it as seamless upgrade as possible — and once we achieve that, it means, we're getting very close to the version 1.0.
 
-For the time being, if you're fine with a **single-node solution that delivers a really good message streaming performance**, give Iggy a try, or at least run the benchmarks to see what are its possibilities and please share your results and thoughts on our [Discord](https://discord.gg/C5Sux5NcRa) — **your opinion is really important to us and we respect it no matter what**.
+For the time being, if you're fine with a **single-node solution that delivers a really good message streaming performance**, give Iggy a try, or at least run the benchmarks to see what are its possibilities and please share your results and thoughts on our [Discord](https://iggy.rs/discord) — **your opinion is really important to us and we respect it no matter what**.
 
 It's been a very productive year for our core team (we've been and still are doing this in our free time), and once again, huge thanks to all our supporters and contributors!
 
 **We've got the fundamentals right, now it's high time to make Iggy blazingly fast!**
 
-![image](/one-year-of-building-the-message-streaming/iggy_rocket.png)
+![image](/one-year-of-building-the-message-streaming/iggy_rocket.png) 
\ No newline at end of file
diff --git a/blog/2024-10-28-technology-radar-and-currrent-goals.md b/blog-src/content/posts/2024-10-28-technology-radar-and-currrent-goals.md
similarity index 82%
rename from blog/2024-10-28-technology-radar-and-currrent-goals.md
rename to blog-src/content/posts/2024-10-28-technology-radar-and-currrent-goals.md
index 13353975a..4f42ecaa1 100644
--- a/blog/2024-10-28-technology-radar-and-currrent-goals.md
+++ b/blog-src/content/posts/2024-10-28-technology-radar-and-currrent-goals.md
@@ -1,21 +1,15 @@
 ---
 title: Iggy.rs - Technology Radar & current goals
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2024-10-28
 ---
 ## Technology Radar
 
-Quite recently (a few days ago), [Iggy](https://github.com/apache/iggy) has been listed on [Technology Radar](https://www.thoughtworks.com/radar/platforms/summary/iggy) by [Thoughtworks](https://www.thoughtworks.com/) - a well-known technology consulting company.
+Quite recently (a few days ago), [Iggy](https://github.com/iggy-rs/iggy) has been listed on [Technology Radar](https://www.thoughtworks.com/radar/platforms/summary/iggy) by [Thoughtworks](https://www.thoughtworks.com/) - a well-known technology consulting company.
 
-If you're not familiar with the Technology Radar, it's essentially an opinionated set (updated twice a year and subscribed by the thousands of developers worldwide) of the tools, platforms, frameworks, techniques etc. which you may want to try out & explore in your IT projects. Everything is split into the different categories, depending on the maturity or popularity of the particular tool.
-
-<!--truncate-->
+If you're not familiar with the Technology Radar, it's essentially an opinionated set (updated twice a year and subscribed by the thousands of developers worldwide) of the tools, platforms, frameworks, techniques etc. which you may want to try out & explore in your IT projects. Everything is split into the different categories, depending on the maturity or popularity of the particular tool. 
 
 ![image](/technology-radar-and-currrent-goals/iggy_technology_radar.png)
 
@@ -23,7 +17,7 @@ As you can see, we were put right into the **assess** bucket (next to such renow
 
 Besides gaining an additional amount of trust & recognition, it has led us to another conclusion - someone out there we don't know yet about (maybe even one of their customers) is using/experimenting with Iggy :)
 
-And if you are (or will be) one of such persons, please hop onto our [Discord](https://discord.gg/C5Sux5NcRa) and share your invaluable feedback with us!
+And if you are (or will be) one of such persons, please hop onto our [Discord](https://iggy.rs/discord) and share your invaluable feedback with us!
 
 Now, given the recent publication and increased activity within our [OSS community](https://github.com/iggy-rs/) building the core streaming server & SDKs in multiple programming languages, it's worth mentioning what are the current goals for Iggy.
 
@@ -33,7 +27,7 @@ Now, given the recent publication and increased activity within our [OSS communi
 
 Without a doubt, being able to run your infrastructure (which processes & stores the data) as a cluster, gives much more confidence and greatly impacts the overall reliability.
 
-We've started [experimenting](https://github.com/iggy-rs/iggy-cluster-sandbox) with the replication over half a year ago already by implementing the basic, Raft based consensus algorithm for the simple message streaming server.
+We've started [experimenting](https://github.com/iggy-rs/iggy-cluster-sandbox) with the replication over half a year ago already by implementing the basic, Raft based consensus algorithm for the simple message streaming server. 
 
 At the same time, we were researching the other possible solutions, after we've finally decided to move on with **Viewstamped Replication** (in its [revisited form](https://pmg.csail.mit.edu/papers/vr-revisited.pdf)), which was successfully used by e.g. [TigerBeetle](https://tigerbeetle.com/).
 
@@ -47,7 +41,7 @@ Below is our very first draft for the initial implementation of VSR.
 
 ### S3 storage
 
-A few months ago, we did [implement](https://github.com/apache/iggy/pull/1053) an optional archiver for the server state log & streaming data (messages etc.) which supports any S3 compatible storage (just pick up your favorite cloud provider). The configuration is as simple as this example:
+A few months ago, we did [implement](https://github.com/iggy-rs/iggy/pull/1053) an optional archiver for the server state log & streaming data (messages etc.) which supports any S3 compatible storage (just pick up your favorite cloud provider). The configuration is as simple as this example:
 
 ```toml
 [data_maintenance.archiver]
@@ -93,7 +87,7 @@ Or, you could just ignore your server's RAM & SSD, and do all the writes and rea
 
 ### OpenTelemetry
 
-Speaking of the Quickwit, we've also [implemented](https://github.com/apache/iggy/pull/1294) a support for [OpenTelemetry](https://opentelemetry.io/) logs & traces for the server. Since our SDK already uses the logging & tracing libraries, we thought that adding such a feature on the server, could help you gain even better, real-time observability into what's happening under the hood.
+Speaking of the Quickwit, we've also [implemented](https://github.com/iggy-rs/iggy/pull/1294) a support for [OpenTelemetry](https://opentelemetry.io/) logs & traces for the server. Since our SDK already uses the logging & tracing libraries, we thought that adding such a feature on the server, could help you gain even better, real-time observability into what's happening under the hood.
 
 ```toml
 # OpenTelemetry configuration
@@ -118,7 +112,7 @@ transport = "grpc"
 endpoint = "http://localhost:7281/v1/traces"
 ```
 
-**And just like with S3 storage, it's merely a beginning** - one of the members on our [Discord](https://discord.gg/C5Sux5NcRa) had already thought of extending this implementation by **propagating the trace context** (via existing message headers metadata) between the clients & server in order to get full understanding of the distributed systems and its dependencies, which could be further visualized by tools like [Zipkin](https://zipkin.io/) or [Jaeger](https://www.jaegertracing.io/).
+**And just like with S3 storage, it's merely a beginning** - one of the members on our [Discord](https://iggy.rs/discord) had already thought of extending this implementation by **propagating the trace context** (via existing message headers metadata) between the clients & server in order to get full understanding of the distributed systems and its dependencies, which could be further visualized by tools like [Zipkin](https://zipkin.io/) or [Jaeger](https://www.jaegertracing.io/).
 
 ### Optimizations
 
@@ -134,4 +128,4 @@ This will certainly require to have its own blog post, as there's so much to tal
 
 Yes, at this point you might call us crazy (**io_uring** won't happen before we release the first version of the VSR clustering anyway), but if you want to tick all the possible boxes, it's hard to find a generic framework that will meet your demands, especially when mixed altogether with VSR clustering, thread-per-core & shared-nothing design (if will turn out to be suitable), zero-copy deserialization libraries and other things we might even not be aware of yet.
 
-**To innovate, one must experiment**, and although we do all these things in our spare time, it's been an exciting journey so far (and lots of experience gained in the meantime) for all of our team members building something fresh, from the very ground up, and regardless of the final outcome, we already know it was all worth it :)
+**To innovate, one must experiment**, and although we do all these things in our spare time, it's been an exciting journey so far (and lots of experience gained in the meantime) for all of our team members building something fresh, from the very ground up, and regardless of the final outcome, we already know it was all worth it :)
\ No newline at end of file
diff --git a/blog/2025-02-10-apache-incubator.md b/blog-src/content/posts/2025-02-10-apache-incubator.md
similarity index 94%
rename from blog/2025-02-10-apache-incubator.md
rename to blog-src/content/posts/2025-02-10-apache-incubator.md
index e47490054..ce28e41d8 100644
--- a/blog/2025-02-10-apache-incubator.md
+++ b/blog-src/content/posts/2025-02-10-apache-incubator.md
@@ -1,18 +1,12 @@
 ---
 title: Iggy joins the Apache Incubator
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2025-02-10
 ---
 We are thrilled to announce that **Iggy** has officially joined the **[Apache Incubator](https://incubator.apache.org/)**! This marks a major milestone in our journey to redefine message streaming — one that is **blazingly fast, hyper-efficient, and built for the future**. Since the very first day, **Iggy was always meant to be a truly FOSS project** — not just open-source in name, but deeply rooted in the values of transparency, collaboration, and community-driven innovation.
 
-<!--truncate-->
-
 ## 🚀 A Journey Two Years in the Making
 It’s been almost two years since the very first line of code was written, and we’ve come a long way since the initial release. What started as a bold idea has grown into a high-performance, cost-efficient message streaming solution with a thriving community of contributors, users, and advocates. We’ve received invaluable feedback, contributions, and support, and we’re more excited than ever to take Iggy to the next level.
 
@@ -66,7 +60,7 @@ And this is just the beginning. **Welcome to the era of Hyper-Efficient Message
 
 👉 Follow us, contribute, and help to build the future of the message streaming!
 
-- [Discord](https://discord.gg/C5Sux5NcRa)
+- [Discord](https://iggy.rs/discord)
 - [X (Twitter)](https://x.com/ApacheIggy)
 - [Bluesky](https://bsky.app/profile/iggy.rs)
-- [LinkedIn](https://www.linkedin.com/company/apache-iggy/)
+- [LinkedIn](https://www.linkedin.com/company/apache-iggy/)
\ No newline at end of file
diff --git a/blog/2025-02-17-transparent-benchmarks.md b/blog-src/content/posts/2025-02-17-transparent-benchmarks.md
similarity index 92%
rename from blog/2025-02-17-transparent-benchmarks.md
rename to blog-src/content/posts/2025-02-17-transparent-benchmarks.md
index 805334d96..1f348fb32 100644
--- a/blog/2025-02-17-transparent-benchmarks.md
+++ b/blog-src/content/posts/2025-02-17-transparent-benchmarks.md
@@ -1,10 +1,6 @@
 ---
 title: Transparent Benchmarking with Apache Iggy
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2025-02-17
@@ -13,15 +9,13 @@ date: 2025-02-17
 
 In the world of software development, **benchmarks are often treated as a second-class citizen**. They're more of an addition to the codebase, rather than a crucial part of it, which should be the other way around, especially when it comes to the performance-critical systems or infrastructure tools.
 
-<!--truncate-->
-
 Sometimes, the benchmarking results are nothing more than just a **cherry-picking of the best-case scenarios**, which are not representative of real-world usage. In such a case, they simply serve a sole purpose of either making the project look better than it is or how well it does outperform the competition, under the extremely optimized conditions when comparing with its counterparts.
 
 **Trying to reproduce the benchmarks is often a nightmare**, as the environment setup is not documented, the code is unavailable, or the instructions are not clear enough. This makes it close to impossible to verify the results, which are then taken for granted.
 
 Or even worse, the **benchmarking tool might be so complex, that it's hard to understand how it works**, and what are the assumptions behind it. ALl of these, does result in hard to extend or modify the existing benchmarks, which are not covering the particular use case you're interested in. It's just here to tell everyone that we do have benchmarks, but how we do it, and what they measure, is a mystery.
 
-**Which is why at [Iggy](https://github.com/apache/iggy/), we've decided to make the benchmarks a first-class citizen**.
+**Which is why at [Iggy](https://github.com/iggy-rs/iggy/), we've decided to make the benchmarks a first-class citizen**.
 
 Our `iggy-bench` tool, which is used to run the benchmarks and is part of the core open source repository (can be found under the `bench` directory), has come a long way and has been serving us well.
 
@@ -35,7 +29,7 @@ We use it to do quick performance checks, regression testing, and to see how the
 
 This is our community-driven effort, where everyone can contribute, and add their own benchmarks. For all the information on how to run the benchmarks, render them on the dashboard, upload your results or contribute to the project, please check the [iggy-bench-dashboard](https://github.com/iggy-rs/iggy-bench-dashboard) repository. In general, it's as simple as:
 
-- Building the [Iggy](https://github.com/apache/iggy) in the release mode with `cargo build --release`
+- Building the [Iggy](https://github.com/iggy-rs/iggy) in the release mode with `cargo build --release`
 - Starting your Iggy server with `cargo r --bin iggy-server -r` (feel free to adjust the configuration in `server.toml` or via environment variables)
 - Running the `iggy-bench` tool with the desired parameters, e.g. `cargo r --bin iggy-bench -r pinned-producer tcp`
 - Extending your benchmark with the output (HTML charts, JSON sampling etc.) `cargo r --bin iggy-bench -r pinned-producer tcp output -o performance_results --identifier spetz`
diff --git a/blog/2025-05-08-zero-copy-deserialization.md b/blog-src/content/posts/2025-05-08-zero-copy-deserialization.md
similarity index 96%
rename from blog/2025-05-08-zero-copy-deserialization.md
rename to blog-src/content/posts/2025-05-08-zero-copy-deserialization.md
index b5d437bb4..669f31e31 100644
--- a/blog/2025-05-08-zero-copy-deserialization.md
+++ b/blog-src/content/posts/2025-05-08-zero-copy-deserialization.md
@@ -1,14 +1,6 @@
 ---
 title: Zero-copy (de)serialization
-authors:
-  - name: Grzegorz Koszyk
-    title: Apache Iggy PPMC Member
-    url: https://github.com/numinnex
-   #  image_url: https://github.com/numinnex.png
-  - name: Hubert Gruszecki
-    title: Apache Iggy PPMC Member
-    url: https://github.com/hubcio
-   #  image_url: https://github.com/hubcio.png
+authors: ["Grzegorz Koszyk","Hubert Gruszecki"]
 tags: []
 hide_table_of_contents: false
 date: 2025-05-08
diff --git a/blog/2025-06-06-connectors-runtime.md b/blog-src/content/posts/2025-06-06-connectors-runtime.md
similarity index 97%
rename from blog/2025-06-06-connectors-runtime.md
rename to blog-src/content/posts/2025-06-06-connectors-runtime.md
index 864a4af82..b5b9d30a7 100644
--- a/blog/2025-06-06-connectors-runtime.md
+++ b/blog-src/content/posts/2025-06-06-connectors-runtime.md
@@ -1,10 +1,6 @@
 ---
 title: Connectors runtime
-authors:
-  - name: Piotr Gankiewicz
-    title: Apache Iggy founder
-    url: https://github.com/spetz
-    # image_url: https://github.com/spetz.png
+authors: ["Piotr Gankiewicz"]
 tags: []
 hide_table_of_contents: false
 date: 2025-06-06
diff --git a/blog-src/content/posts/_index.md b/blog-src/content/posts/_index.md
new file mode 100644
index 000000000..0aa9d1078
--- /dev/null
+++ b/blog-src/content/posts/_index.md
@@ -0,0 +1,10 @@
++++
+paginate_by = 15
+title = "Posts"
+sort_by = "date"
+
+insert_anchor_links = "heading"
+
+[extra]
+comment = true
++++
diff --git a/blog-src/content/posts/about.md b/blog-src/content/posts/about.md
new file mode 100644
index 000000000..3c97c3c3f
--- /dev/null
+++ b/blog-src/content/posts/about.md
@@ -0,0 +1,5 @@
++++
+title = "About"
+path = "about"
+draft = true
++++
diff --git a/blog-src/content/projects/_index.md b/blog-src/content/projects/_index.md
new file mode 100644
index 000000000..9b00a1298
--- /dev/null
+++ b/blog-src/content/projects/_index.md
@@ -0,0 +1,5 @@
++++
+title = "Projects"
+sort_by = "weight"
+template = "cards.html"
++++
diff --git a/blog-src/content/projects/project-1.jpg b/blog-src/content/projects/project-1.jpg
new file mode 100644
index 000000000..db5232ef1
Binary files /dev/null and b/blog-src/content/projects/project-1.jpg differ
diff --git a/blog-src/content/projects/project_1.md b/blog-src/content/projects/project_1.md
new file mode 100644
index 000000000..420e59a99
--- /dev/null
+++ b/blog-src/content/projects/project_1.md
@@ -0,0 +1,11 @@
++++
+title = "Apollo"
+description = "Modern and minimalistic blog theme."
+weight = 1
+
+[extra]
+local_image = "/projects/project-1.jpg"
+link_to = "https://github.com/not-matthias/apollo"
++++
+
+Example project page
diff --git a/blog-src/content/projects/project_2.md b/blog-src/content/projects/project_2.md
new file mode 100644
index 000000000..43704c7db
--- /dev/null
+++ b/blog-src/content/projects/project_2.md
@@ -0,0 +1,11 @@
++++
+title = "Project 2"
+description = "Example description"
+weight = 1
+
+[extra]
+# You can also crop the image in the url by adjusting w=/h=
+remote_image = "https://images.unsplash.com/photo-1523821741446-edb2b68bb7a0?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80"
++++
+
+Example project page
diff --git a/blog-src/content/projects/project_3.md b/blog-src/content/projects/project_3.md
new file mode 100644
index 000000000..de8e315b8
--- /dev/null
+++ b/blog-src/content/projects/project_3.md
@@ -0,0 +1,10 @@
++++
+title = "Project 3"
+description = "Example description"
+weight = 1
+
+[extra]
+remote_image = "https://images.unsplash.com/photo-1462556791646-c201b8241a94?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1465&q=80"
++++
+
+Example project page
diff --git a/blog-src/content/projects/project_4.md b/blog-src/content/projects/project_4.md
new file mode 100644
index 000000000..a3666631e
--- /dev/null
+++ b/blog-src/content/projects/project_4.md
@@ -0,0 +1,10 @@
++++
+title = "Project 4"
+description = "Example description with a lot of words but without any meaning. Why use lorem ipsum when you can just write a lot of text that has no underlying meaning?"
+weight = 1
+
+[extra]
+remote_image = "https://images.unsplash.com/photo-1620121692029-d088224ddc74?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1632&q=80"
++++
+
+Example project page
diff --git a/blog-src/content/projects/project_5.md b/blog-src/content/projects/project_5.md
new file mode 100644
index 000000000..920d5f0b3
--- /dev/null
+++ b/blog-src/content/projects/project_5.md
@@ -0,0 +1,7 @@
++++
+title = "Project 4"
+description = "Example description"
+weight = 1
++++
+
+Example project page
diff --git a/static/apache-incubator/iggy_logo.png b/blog-src/static/apache-incubator/iggy_logo.png
similarity index 100%
rename from static/apache-incubator/iggy_logo.png
rename to blog-src/static/apache-incubator/iggy_logo.png
diff --git a/static/building-message-streaming/gh_trending.jpeg b/blog-src/static/building-message-streaming/gh_trending.jpeg
similarity index 100%
rename from static/building-message-streaming/gh_trending.jpeg
rename to blog-src/static/building-message-streaming/gh_trending.jpeg
diff --git a/static/building-message-streaming/iggy_bench.jpeg b/blog-src/static/building-message-streaming/iggy_bench.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_bench.jpeg
rename to blog-src/static/building-message-streaming/iggy_bench.jpeg
diff --git a/static/building-message-streaming/iggy_ci_cd.jpeg b/blog-src/static/building-message-streaming/iggy_ci_cd.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_ci_cd.jpeg
rename to blog-src/static/building-message-streaming/iggy_ci_cd.jpeg
diff --git a/static/building-message-streaming/iggy_cli.jpeg b/blog-src/static/building-message-streaming/iggy_cli.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_cli.jpeg
rename to blog-src/static/building-message-streaming/iggy_cli.jpeg
diff --git a/static/building-message-streaming/iggy_sample.jpeg b/blog-src/static/building-message-streaming/iggy_sample.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_sample.jpeg
rename to blog-src/static/building-message-streaming/iggy_sample.jpeg
diff --git a/static/building-message-streaming/iggy_server.jpeg b/blog-src/static/building-message-streaming/iggy_server.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_server.jpeg
rename to blog-src/static/building-message-streaming/iggy_server.jpeg
diff --git a/static/building-message-streaming/iggy_web_ui.jpeg b/blog-src/static/building-message-streaming/iggy_web_ui.jpeg
similarity index 100%
rename from static/building-message-streaming/iggy_web_ui.jpeg
rename to blog-src/static/building-message-streaming/iggy_web_ui.jpeg
diff --git a/static/building-message-streaming/iggys.jpeg b/blog-src/static/building-message-streaming/iggys.jpeg
similarity index 100%
rename from static/building-message-streaming/iggys.jpeg
rename to blog-src/static/building-message-streaming/iggys.jpeg
diff --git a/static/building-message-streaming/leader_elections.jpeg b/blog-src/static/building-message-streaming/leader_elections.jpeg
similarity index 100%
rename from static/building-message-streaming/leader_elections.jpeg
rename to blog-src/static/building-message-streaming/leader_elections.jpeg
diff --git a/blog-src/static/custom.css b/blog-src/static/custom.css
new file mode 100644
index 000000000..db3b6a5cf
--- /dev/null
+++ b/blog-src/static/custom.css
@@ -0,0 +1,216 @@
+/* Font: Manrope 300/400/500/700 */
+@import url("https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;700&display=swap");
+
+/* Global variables */
+:root {
+  --primary-color:       #ff9103 !important;
+  --primary-color-light: color-mix(in srgb, #ff9103 70%, white) !important;
+  --primary-color-dark:  color-mix(in srgb, #ff9103 80%, black) !important;
+  --font-sans:           "Manrope", ui-sans-serif, system-ui, sans-serif;
+}
+
+/* Logo: 4 rem height overrides Tailwind h-8/h-10 */
+header img,
+.navbar img,
+img.logo,
+img[alt~="logo"] {
+  height: 4rem !important;
+  width: auto !important;
+  max-height: none !important;
+}
+
+/* Base background */
+html,
+body {
+  background-color: #070c17 !important;
+}
+
+/* Disable native selection highlight */
+html::selection,
+body::selection,
+html::-moz-selection,
+body::-moz-selection {
+  background-color: #070c17;
+  color: #070c17;
+}
+
+/* Header halo */
+header {
+  position: relative;
+  display: flex;
+  align-items: center;
+}
+header > * { position: relative; z-index: 1; }
+
+header::after {
+  content: "";
+  position: absolute;
+  inset: -110px -48px;
+  z-index: 0;
+  pointer-events: none;
+  background:
+    radial-gradient(900px 500px at 0% 0%,   #273555d9 0%, transparent 70%),
+    radial-gradient(880px 460px at 105% -5%, #384b73cc 0%, transparent 70%),
+    radial-gradient(700px 520px at 50% 15%,  #131e3680 0%, transparent 75%);
+  filter: blur(70px);
+  opacity: .9;
+}
+@media (max-width: 640px) {
+  header::after { inset: -80px -32px; filter: blur(50px); }
+}
+
+/* Search button: base size */
+header button.search,
+header button.search-toggle,
+header button[data-search-toggle] {
+  width: 2rem;
+  height: 2rem;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0;
+}
+header button.search svg,
+header button.search-toggle svg,
+header button[data-search-toggle] svg {
+  width: 100%;
+  height: 100%;
+}
+
+/* Search button (current markup) */
+header button.search-button {
+  width: 2rem !important;
+  height: 2rem !important;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0 !important;
+  position: relative;
+  border-radius: 50%;
+  isolation: isolate;
+  transition: all .25s ease;
+}
+header button.search-button img.search-icon,
+header img[src$="search.svg"] {
+  width: 100% !important;
+  height: 100% !important;
+  filter: brightness(0) invert(1) !important; /* white icon */
+}
+
+/* Search button glow */
+header button.search-button::before,
+header #search-button::before {
+  content: "";
+  position: absolute;
+  inset: 0;
+  border-radius: 50%;
+  background: #ffffff;
+  opacity: 0;
+  filter: blur(4px);
+  z-index: -1;
+  transition: opacity .25s ease;
+}
+header button.search-button:hover::before,
+header button.search-button:focus-visible::before,
+header #search-button:hover::before,
+header #search-button:focus-visible::before {
+  opacity: .12;
+}
+header button.search-button:hover img,
+header #search-button:hover img {
+  transform: none;
+}
+
+/* Page header weight */
+.page-header { font-weight: 600 !important; }
+
+/* Hover color override for both themes */
+:root.dark  { --hover-color: #070c17; }
+:root.light { --hover-color: #070c17; }
+
+/* Content stacking context reset */
+main,
+.content,
+article {
+  position: relative;
+  z-index: auto;
+}
+
+/* Bottom halo (fixed to viewport) */
+body::after {
+  content: "";
+  position: fixed;
+  left: -25%;
+  bottom: -140px;
+  width: 150%;
+  height: 420px;
+  z-index: 0;
+  pointer-events: none;
+  background:
+    radial-gradient(900px 520px at 50% 100%, #273555c2 0%, transparent 70%),
+    radial-gradient(780px 460px at 50% 110%, #384b73a0 0%, transparent 75%),
+    radial-gradient(580px 480px at 50% 120%, #131e3666 0%, transparent 80%);
+  filter: blur(80px);
+  opacity: .55;
+  mix-blend-mode: screen;
+}
+@media (max-width: 640px) {
+  body::after {
+    bottom: -100px;
+    height: 300px;
+    filter: blur(60px);
+    opacity: .45;
+  }
+}
+
+/* Article typography */
+article p,
+article li,
+article blockquote,
+article code {
+  font-weight: 300;
+  color: #ababab;
+  letter-spacing: -0.01em;
+}
+article li::marker { font-weight: 300; color: #ababab; }
+article blockquote {
+  border-left: 2px solid #384b73;
+  padding-left: 1rem;
+  font-style: italic;
+  opacity: .9;
+}
+article strong,
+article b {
+  font-weight: 700;
+  color: #d5d5d5;
+}
+
+/* Bottom spacing inside article */
+article { padding-bottom: 4rem; }
+
+/* Search modal: always on top */
+dialog[open],
+#search-modal,
+.search-modal,
+.search-overlay,
+[role="dialog"][aria-label="Search"],
+dialog[open][id="search"],
+dialog[open].search-modal {
+  position: fixed !important;
+  inset: 0 !important;
+  z-index: 2147483647 !important;
+}
+
+/* Reset any z-index applied inside article */
+article * {
+  position: relative;
+  z-index: auto !important;
+}
+
+/* Inline images in article */
+article img {
+  border: 0 !important;
+  max-width: 100%;
+  margin: 4rem 0;
+  display: block;
+}
diff --git a/static/img/AdvancedFeatures.svg b/blog-src/static/img/AdvancedFeatures.svg
similarity index 100%
rename from static/img/AdvancedFeatures.svg
rename to blog-src/static/img/AdvancedFeatures.svg
diff --git a/static/img/ExtremelyEfficient.svg b/blog-src/static/img/ExtremelyEfficient.svg
similarity index 100%
rename from static/img/ExtremelyEfficient.svg
rename to blog-src/static/img/ExtremelyEfficient.svg
diff --git a/static/img/FullyOpenSourced.svg b/blog-src/static/img/FullyOpenSourced.svg
similarity index 100%
rename from static/img/FullyOpenSourced.svg
rename to blog-src/static/img/FullyOpenSourced.svg
diff --git a/static/img/ManagementInterface.svg b/blog-src/static/img/ManagementInterface.svg
similarity index 100%
rename from static/img/ManagementInterface.svg
rename to blog-src/static/img/ManagementInterface.svg
diff --git a/static/img/MultipleTransports.svg b/blog-src/static/img/MultipleTransports.svg
similarity index 100%
rename from static/img/MultipleTransports.svg
rename to blog-src/static/img/MultipleTransports.svg
diff --git a/static/img/Sdk.svg b/blog-src/static/img/Sdk.svg
similarity index 100%
rename from static/img/Sdk.svg
rename to blog-src/static/img/Sdk.svg
diff --git a/static/img/apache-iggy-light-bg0.5x.png b/blog-src/static/img/apache-iggy-light-bg0.5x.png
similarity index 100%
rename from static/img/apache-iggy-light-bg0.5x.png
rename to blog-src/static/img/apache-iggy-light-bg0.5x.png
diff --git a/static/img/apache-iggy0.5x.png b/blog-src/static/img/apache-iggy0.5x.png
similarity index 100%
rename from static/img/apache-iggy0.5x.png
rename to blog-src/static/img/apache-iggy0.5x.png
diff --git a/static/img/architecture.png b/blog-src/static/img/architecture.png
similarity index 100%
rename from static/img/architecture.png
rename to blog-src/static/img/architecture.png
diff --git a/static/img/bench_cli.png b/blog-src/static/img/bench_cli.png
similarity index 100%
rename from static/img/bench_cli.png
rename to blog-src/static/img/bench_cli.png
diff --git a/static/img/bench_platform.png b/blog-src/static/img/bench_platform.png
similarity index 100%
rename from static/img/bench_platform.png
rename to blog-src/static/img/bench_platform.png
diff --git a/static/img/docusaurus-social-card.jpg b/blog-src/static/img/docusaurus-social-card.jpg
similarity index 100%
rename from static/img/docusaurus-social-card.jpg
rename to blog-src/static/img/docusaurus-social-card.jpg
diff --git a/static/img/docusaurus.png b/blog-src/static/img/docusaurus.png
similarity index 100%
rename from static/img/docusaurus.png
rename to blog-src/static/img/docusaurus.png
diff --git a/static/img/favicon.ico b/blog-src/static/img/favicon.ico
similarity index 100%
rename from static/img/favicon.ico
rename to blog-src/static/img/favicon.ico
diff --git a/static/img/iggy-logo-1.png b/blog-src/static/img/iggy-logo-1.png
similarity index 100%
rename from static/img/iggy-logo-1.png
rename to blog-src/static/img/iggy-logo-1.png
diff --git a/static/img/iggy.png b/blog-src/static/img/iggy.png
similarity index 100%
rename from static/img/iggy.png
rename to blog-src/static/img/iggy.png
diff --git a/static/img/iggy_0_1_0.jpg b/blog-src/static/img/iggy_0_1_0.jpg
similarity index 100%
rename from static/img/iggy_0_1_0.jpg
rename to blog-src/static/img/iggy_0_1_0.jpg
diff --git a/static/img/iggy_bench_0_1_0.jpeg b/blog-src/static/img/iggy_bench_0_1_0.jpeg
similarity index 100%
rename from static/img/iggy_bench_0_1_0.jpeg
rename to blog-src/static/img/iggy_bench_0_1_0.jpeg
diff --git a/static/img/iggy_ci_0_1_0.jpeg b/blog-src/static/img/iggy_ci_0_1_0.jpeg
similarity index 100%
rename from static/img/iggy_ci_0_1_0.jpeg
rename to blog-src/static/img/iggy_ci_0_1_0.jpeg
diff --git a/blog-src/static/img/iggy_cli.png b/blog-src/static/img/iggy_cli.png
new file mode 100644
index 000000000..50131be4c
Binary files /dev/null and b/blog-src/static/img/iggy_cli.png differ
diff --git a/static/img/iggy_cli_0_1_0.jpeg b/blog-src/static/img/iggy_cli_0_1_0.jpeg
similarity index 100%
rename from static/img/iggy_cli_0_1_0.jpeg
rename to blog-src/static/img/iggy_cli_0_1_0.jpeg
diff --git a/static/img/iggy_server.png b/blog-src/static/img/iggy_server.png
similarity index 100%
rename from static/img/iggy_server.png
rename to blog-src/static/img/iggy_server.png
diff --git a/static/img/iggy_server_0_1_0.jpeg b/blog-src/static/img/iggy_server_0_1_0.jpeg
similarity index 100%
rename from static/img/iggy_server_0_1_0.jpeg
rename to blog-src/static/img/iggy_server_0_1_0.jpeg
diff --git a/static/img/iggy_web_ui.png b/blog-src/static/img/iggy_web_ui.png
similarity index 100%
rename from static/img/iggy_web_ui.png
rename to blog-src/static/img/iggy_web_ui.png
diff --git a/static/img/iggy_web_ui_0_1_0.jpeg b/blog-src/static/img/iggy_web_ui_0_1_0.jpeg
similarity index 100%
rename from static/img/iggy_web_ui_0_1_0.jpeg
rename to blog-src/static/img/iggy_web_ui_0_1_0.jpeg
diff --git a/brand-assets/Logo/SVG/iggy-apache-color-darkbg.svg b/blog-src/static/img/logo-darkbg.svg
similarity index 100%
rename from brand-assets/Logo/SVG/iggy-apache-color-darkbg.svg
rename to blog-src/static/img/logo-darkbg.svg
diff --git a/static/img/logo.png b/blog-src/static/img/logo.png
similarity index 100%
rename from static/img/logo.png
rename to blog-src/static/img/logo.png
diff --git a/static/img/logo.svg b/blog-src/static/img/logo.svg
similarity index 100%
rename from static/img/logo.svg
rename to blog-src/static/img/logo.svg
diff --git a/static/img/undraw_docusaurus_mountain.svg b/blog-src/static/img/undraw_docusaurus_mountain.svg
similarity index 100%
rename from static/img/undraw_docusaurus_mountain.svg
rename to blog-src/static/img/undraw_docusaurus_mountain.svg
diff --git a/static/img/undraw_docusaurus_react.svg b/blog-src/static/img/undraw_docusaurus_react.svg
similarity index 100%
rename from static/img/undraw_docusaurus_react.svg
rename to blog-src/static/img/undraw_docusaurus_react.svg
diff --git a/static/img/undraw_docusaurus_tree.svg b/blog-src/static/img/undraw_docusaurus_tree.svg
similarity index 100%
rename from static/img/undraw_docusaurus_tree.svg
rename to blog-src/static/img/undraw_docusaurus_tree.svg
diff --git a/static/one-year-of-building-the-message-streaming/iggy_bench.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_bench.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_bench.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_bench.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_cli.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_cli.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_cli.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_cli.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_cluster.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_cluster.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_cluster.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_cluster.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_perf.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_perf.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_perf.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_perf.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_rocket.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_rocket.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_rocket.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_rocket.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_stars.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_stars.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_stars.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_stars.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_tpc.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_tpc.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_tpc.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_tpc.png
diff --git a/static/one-year-of-building-the-message-streaming/iggy_web.png b/blog-src/static/one-year-of-building-the-message-streaming/iggy_web.png
similarity index 100%
rename from static/one-year-of-building-the-message-streaming/iggy_web.png
rename to blog-src/static/one-year-of-building-the-message-streaming/iggy_web.png
diff --git a/static/technology-radar-and-currrent-goals/iggy_docker.png b/blog-src/static/technology-radar-and-currrent-goals/iggy_docker.png
similarity index 100%
rename from static/technology-radar-and-currrent-goals/iggy_docker.png
rename to blog-src/static/technology-radar-and-currrent-goals/iggy_docker.png
diff --git a/static/technology-radar-and-currrent-goals/iggy_technology_radar.png b/blog-src/static/technology-radar-and-currrent-goals/iggy_technology_radar.png
similarity index 100%
rename from static/technology-radar-and-currrent-goals/iggy_technology_radar.png
rename to blog-src/static/technology-radar-and-currrent-goals/iggy_technology_radar.png
diff --git a/static/technology-radar-and-currrent-goals/iggy_vsr.png b/blog-src/static/technology-radar-and-currrent-goals/iggy_vsr.png
similarity index 100%
rename from static/technology-radar-and-currrent-goals/iggy_vsr.png
rename to blog-src/static/technology-radar-and-currrent-goals/iggy_vsr.png
diff --git a/static/transparent-benchmarks/iggy_bench_cli.png b/blog-src/static/transparent-benchmarks/iggy_bench_cli.png
similarity index 100%
rename from static/transparent-benchmarks/iggy_bench_cli.png
rename to blog-src/static/transparent-benchmarks/iggy_bench_cli.png
diff --git a/static/transparent-benchmarks/iggy_bench_dashboard.png b/blog-src/static/transparent-benchmarks/iggy_bench_dashboard.png
similarity index 100%
rename from static/transparent-benchmarks/iggy_bench_dashboard.png
rename to blog-src/static/transparent-benchmarks/iggy_bench_dashboard.png
diff --git a/static/zero-copy-deserialization/iggy_batch_schema_1.png b/blog-src/static/zero-copy-deserialization/iggy_batch_schema_1.png
similarity index 100%
rename from static/zero-copy-deserialization/iggy_batch_schema_1.png
rename to blog-src/static/zero-copy-deserialization/iggy_batch_schema_1.png
diff --git a/static/zero-copy-deserialization/iggy_batch_schema_2.png b/blog-src/static/zero-copy-deserialization/iggy_batch_schema_2.png
similarity index 100%
rename from static/zero-copy-deserialization/iggy_batch_schema_2.png
rename to blog-src/static/zero-copy-deserialization/iggy_batch_schema_2.png
diff --git a/static/zero-copy-deserialization/no_zero_copy_consumer.png b/blog-src/static/zero-copy-deserialization/no_zero_copy_consumer.png
similarity index 100%
rename from static/zero-copy-deserialization/no_zero_copy_consumer.png
rename to blog-src/static/zero-copy-deserialization/no_zero_copy_consumer.png
diff --git a/static/zero-copy-deserialization/no_zero_copy_producer.png b/blog-src/static/zero-copy-deserialization/no_zero_copy_producer.png
similarity index 100%
rename from static/zero-copy-deserialization/no_zero_copy_producer.png
rename to blog-src/static/zero-copy-deserialization/no_zero_copy_producer.png
diff --git a/static/zero-copy-deserialization/zero_copy_consumer.png b/blog-src/static/zero-copy-deserialization/zero_copy_consumer.png
similarity index 100%
rename from static/zero-copy-deserialization/zero_copy_consumer.png
rename to blog-src/static/zero-copy-deserialization/zero_copy_consumer.png
diff --git a/static/zero-copy-deserialization/zero_copy_producer.png b/blog-src/static/zero-copy-deserialization/zero_copy_producer.png
similarity index 100%
rename from static/zero-copy-deserialization/zero_copy_producer.png
rename to blog-src/static/zero-copy-deserialization/zero_copy_producer.png
diff --git a/blog-src/templates/partials/header.html b/blog-src/templates/partials/header.html
new file mode 100644
index 000000000..4f177cac4
--- /dev/null
+++ b/blog-src/templates/partials/header.html
@@ -0,0 +1,214 @@
+{% import "macros/macros.html" as post_macros %}
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="base" content="{{ config.base_url }}">
+
+    {% if page.extra.meta %}
+        <!-- the meta data config goes here  -->
+        {% for data in page.extra.meta %}
+            <meta {% for key, value in data %} {% if key == "property" and value == "og:title" %} {% set_global page_has_og_title = true -%} {% endif %}
+                  {% if key == "property" and value == "og:description" %} {% set_global page_has_og_description = true -%} {% endif %}
+                  {% if key == "name" and value == "description" %} {% set_global page_has_description = true -%} {% endif %}
+                  {{ key }}
+                  None="{{ value }}"
+                  {% endfor %} />
+        {% endfor %}
+    {% endif %}
+
+    {# Site title #}
+    {% set current_path = current_path | default(value="/") %}
+    {% if current_path == "/" %}
+        <title>{{ config.title | default(value="Home") }}</title>
+        {% if not page_has_og_title %}
+            <meta property="og:title"
+                  content="{{ config.title | default(value="Home") }}" />
+        {% endif %}
+
+    {% else %}
+        <title>
+            {% if page.title %}
+                {{ page.title }}
+            {% elif section.title %}
+                {{ section.title }}
+            {% elif config.title %}
+                {{ config.title }}
+            {% else %}
+                Post
+            {% endif %}
+        </title>
+
+        {% if not page_has_og_title %}
+            <meta property="og:title"
+                  content="{% if page.title -%}{{ page.title }}{% elif config.title -%}{{ config.title }}{% else -%}Post{% endif -%}" />
+        {% endif %}
+    {% endif %}
+
+    {% if not page_has_og_description %}
+        {% if page.description %}
+            <meta property="og:description" content="{{ page.description }}" />
+        {% elif config.description %}
+            <meta property="og:description" content="{{ config.description }}" />
+        {% endif %}
+    {% endif %}
+
+    {% if not page_has_description %}
+        {% if page.description %}
+            <meta name="description" content="{{ page.description }}" />
+        {% elif config.description %}
+            <meta name="description" content="{{ config.description }}" />
+        {% endif %}
+    {% endif %}
+
+    {# Favicon #}
+    {% if config.extra.favicon %}<link rel="icon" type="image/png" href={{ config.extra.favicon }} />{% endif %}
+
+    {# Font from cdn or disk #}
+    {% if config.extra.use_cdn | default(value=false) %}
+        <link rel="stylesheet"
+              href="https://cdn.jsdelivr.net/npm/jetbrains-mono@1.0.6/css/jetbrains-mono.min.css">
+        <link rel="stylesheet"
+              href="https://cdn.jsdelivr.net/npm/@fontsource/space-grotesk@4.5.8/index.min.css">
+    {% else %}
+        <link href={{ get_url(path="fonts.css") }} rel="stylesheet" />
+    {% endif %}
+
+    {# Analytics #}
+    {% if config.extra.analytics.enabled %}
+        {% if config.extra.analytics.umami.website_id %}
+            {% set website_id = config.extra.analytics.umami.website_id %}
+            {% set host_url = config.extra.analytics.umami.host_url | default(value="https://api-gateway.umami.dev/") %}
+
+            <script defer
+                    src="/js/imamu.js"
+                    data-website-id="{{ website_id }}"
+                    data-host-url="{{ host_url }}"></script>
+        {% endif %}
+
+        {% if config.extra.analytics.goatcounter.user %}
+            {% set user = config.extra.analytics.goatcounter.user %}
+            {% set host = config.extra.analytics.goatcounter.host | default(value="goatcounter.com") %}
+
+            <script data-goatcounter="https://{{ user }}.{{ host }}/count"
+                    async
+                    src="{{ get_url(path="js/count.js") }}"></script>
+            <noscript>
+                {# EasyList blocks '.com/count?', so we have to use '.com//count' #}
+                <img src="https://{{ user }}.{{ host }}//count?p={{ current_path }}&t={{ page.title | default(value=config.title) }}">
+            </noscript>
+        {% endif %}
+    {% endif %}
+
+    {# Fancy Codeblock #}
+    {% if config.extra.fancy_code %}<script src={{ get_url(path="js/codeblock.js") }}></script>{% endif %}
+
+    {# Table of contents #}
+    {% if config.extra.toc | default(value=false) %}<script src={{ get_url(path="js/toc.js") }}></script>{% endif %}
+
+    {# Dynamic Note #}
+    {% if config.extra.dynamic_note | default(value=false) %}
+        <script src={{ get_url(path="js/note.js") }}></script>
+    {% endif %}
+
+    {% if config.extra.mathjax | default(value=false) %}
+        {% if config.extra.mathjax_dollar_inline_enable | default(value=false) %}
+            <script>
+            MathJax = {
+              tex: {
+                inlineMath: [['$', '$'], ['\\(', '\\)']]
+              }
+            };
+            </script>
+        {% endif %}
+        <script type="text/javascript" id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
+        </script>
+    {% endif %}
+
+    {# RSS #}
+    <link rel="alternate"
+          type="application/atom+xml"
+          title="{{ config.title }}"
+          href="{{ get_url(path="atom.xml", trailing_slash=false) }}">
+
+
+    {% set theme = config.extra.theme | default(value="toggle") %}
+    {% if theme == "dark" %}
+        <link rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path='theme/dark.css') }}" />
+    {% elif theme == "light" %}
+        <link rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path="theme/light.css") }}" />
+    {% elif theme == "auto" %}
+        <link rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path="theme/light.css") }}" />
+        <link rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path='theme/dark.css') }}"
+              media="(prefers-color-scheme: dark)" />
+    {% elif theme == "toggle" %}
+        <link rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path="theme/light.css") }}" />
+        <link id="darkModeStyle"
+              rel="stylesheet"
+              type="text/css"
+              href="{{ get_url(path='theme/dark.css') }}" />
+    {% endif %}
+
+    <!-- Set the correct theme in the script -->
+
+    {% if theme == "auto" or theme == "toggle" %}
+        <script src={{ get_url(path="js/themetoggle.js") }}></script>
+
+        {% if theme == "auto" %}
+            <script>
+                if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+                    setTheme("dark");
+                } else {
+                    setTheme("light");
+                }
+            </script>
+        {% else %}
+            <script>setTheme(getSavedTheme());</script>
+        {% endif %}
+    {% endif %}
+
+
+    <link rel="stylesheet"
+          type="text/css"
+          media="screen"
+          href="{{ get_url(path="main.css") }}" />
+
+    {% if config.extra.stylesheets %}
+        {% for stylesheet in config.extra.stylesheets %}
+            <link rel="stylesheet" href="{{ get_url(path=stylesheet) }}">
+        {% endfor %}
+    {% endif %}
+
+    {# Search #}
+    {%- if config.build_search_index -%}
+        {%- if config.search.index_format -%}
+            {%- set search_index_format = config.search.index_format -%}
+        {%- elif config.extra.index_format -%}
+            {# Necessary to support Zola 0.17.X, as it doesn't have access to config.search.index_format #}
+            {# See: https://github.com/getzola/zola/issues/2165 #}
+            {%- set search_index_format = config.extra.index_format -%}
+        {%- else -%}
+            {%- set search_index_format = "elasticlunr_json" -%}
+        {%- endif -%}
+
+        {%- if search_index_format == "elasticlunr_javascript" -%}
+            <script defer
+                    src="{{ get_url(path='search_index.' ~ lang ~ '.js', cachebust=true) | safe }}"></script>
+        {%- endif -%}
+
+        {# Main search script #}
+        <script defer
+                src="{{ get_url(path='js/searchElasticlunr.min.js', cachebust=true) | safe }}"></script>
+    {%- endif -%}
+</head>
diff --git a/blog-src/themes/apollo/.editorconfig b/blog-src/themes/apollo/.editorconfig
new file mode 100644
index 000000000..01f51b3c5
--- /dev/null
+++ b/blog-src/themes/apollo/.editorconfig
@@ -0,0 +1,3 @@
+[*.css]
+indent_style = space
+indent_size = 2
\ No newline at end of file
diff --git a/blog-src/themes/apollo/.github/workflows/zola.yml b/blog-src/themes/apollo/.github/workflows/zola.yml
new file mode 100644
index 000000000..09246d8a3
--- /dev/null
+++ b/blog-src/themes/apollo/.github/workflows/zola.yml
@@ -0,0 +1,24 @@
+name: Zola
+
+on:
+  push:
+    branches: [ main ]
+  workflow_dispatch:
+
+# Allow one concurrent deployment
+concurrency:
+  group: "pages"
+  cancel-in-progress: true
+
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    
+    steps:
+    - uses: actions/checkout@v3
+      
+    - name: Build and deploy
+      uses: shalzz/zola-deploy-action@master
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/blog-src/themes/apollo/.gitignore b/blog-src/themes/apollo/.gitignore
new file mode 100644
index 000000000..d298be107
--- /dev/null
+++ b/blog-src/themes/apollo/.gitignore
@@ -0,0 +1 @@
+public/
\ No newline at end of file
diff --git a/blog-src/themes/apollo/LICENSE b/blog-src/themes/apollo/LICENSE
new file mode 100644
index 000000000..e65c04b8a
--- /dev/null
+++ b/blog-src/themes/apollo/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 not-matthias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/blog-src/themes/apollo/README.md b/blog-src/themes/apollo/README.md
new file mode 100644
index 000000000..0afa19931
--- /dev/null
+++ b/blog-src/themes/apollo/README.md
@@ -0,0 +1,73 @@
+# apollo
+
+Modern and minimalistic blog theme powered by [Zola](https://getzola.org). See a live preview [here](https://not-matthias.github.io/apollo).
+
+<sub><sup>Named after the greek god of knowledge, wisdom and intellect</sup></sub>
+
+<details open>
+  <summary>Dark theme</summary>
+
+![blog-dark](./screenshot-dark.png)
+
+</details>
+
+<details>
+  <summary>Light theme</summary>
+
+![blog-light](./screenshot.png)
+
+</details>
+
+## Features
+
+- [x] Pagination
+- [x] Themes (light, dark, auto)
+- [x] Projects page
+- [x] Analytics using [GoatCounter](https://www.goatcounter.com/) / [Umami](https://umami.is/)
+- [x] Social Links
+- [x] MathJax Rendering
+- [x] Taxonomies
+- [x] Meta Tags For Individual Pages
+- [x] Custom homepage
+- [x] Comments
+- [x] Search
+
+## Installation
+
+1. Download the theme
+
+```
+git submodule add https://github.com/not-matthias/apollo themes/apollo
+```
+
+2. Add the following to the top of your `config.toml`
+
+```toml
+theme = "apollo"
+taxonomies = [{ name = "tags" }]
+
+[extra]
+theme = "auto"
+socials = [
+  # Configure socials here
+]
+menu = [
+  # Configure menu bar here
+]
+
+# See this for more options: https://github.com/not-matthias/apollo/blob/main/config.toml#L14
+```
+
+3. Copy the example content
+
+```
+cp -r themes/apollo/content/* content/
+```
+
+## Configuration
+
+You can find all the configuration options [here](./content/posts/configuration.md)
+
+## References
+
+This theme is based on [archie-zola](https://github.com/XXXMrG/archie-zola/).
diff --git a/blog-src/themes/apollo/config.toml b/blog-src/themes/apollo/config.toml
new file mode 100644
index 000000000..d59b081e8
--- /dev/null
+++ b/blog-src/themes/apollo/config.toml
@@ -0,0 +1,57 @@
+base_url = "https://not-matthias.github.io/apollo/"
+title = "not-matthias"
+description = "This is an example description"
+build_search_index = true
+generate_feeds = true
+compile_sass = true
+minify_html = true
+taxonomies = [{ name = "tags" }]
+
+[search]
+include_title = true
+include_description = true
+include_path = true
+include_content = true
+index_format = "elasticlunr_json"
+
+[markdown]
+highlight_code = true
+highlight_theme = "ayu-light"
+
+[extra]
+toc = true
+use_cdn = false
+favicon = "/icon/favicon.png"
+theme = "toggle"              # light, dark, auto, toggle
+fancy_code = true
+dynamic_note = true           # a note that can be toggled
+mathjax = true
+mathjax_dollar_inline_enable = true
+repo_url = "https://github.com/not-matthias/apollo/tree/main/content/"
+
+menu = [
+    { name = "/posts", url = "/posts", weight = 1 },
+    { name = "/projects", url = "/projects", weight = 2 },
+    { name = "/about", url = "/about", weight = 3 },
+    { name = "/tags", url = "/tags", weight = 4 },
+]
+
+socials = [
+    { name = "twitter", url = "https://twitter.com/not_matthias", icon = "twitter" },
+    { name = "github", url = "https://github.com/not-matthias/", icon = "github" },
+]
+
+stylesheets = [
+    # "custom.css"    # at /static/custom.css
+]
+
+[extra.analytics]
+enabled = false
+
+[extra.analytics.goatcounter]
+user = "your_user"
+host = "example.com" # default= goatcounter.com
+
+[extra.analytics.umami]
+website_id = "43929cd1-1e83...."
+host_url = "https://stats.mywebsite.com" # default: https://api-gateway.umami.dev/
diff --git a/blog-src/themes/apollo/configuration.md b/blog-src/themes/apollo/configuration.md
new file mode 100644
index 000000000..acf3bc2b3
--- /dev/null
+++ b/blog-src/themes/apollo/configuration.md
@@ -0,0 +1,243 @@
++++
+title = "Configuring Apollo"
+date = "2024-07-09"
+
+[taxonomies]
+tags=["documentation"]
+
+[extra]
+repo_view = true
+comment = true
++++
+
+# Site Configuration
+
+## Search (`build_search_index`)
+
+Enables or disables the search functionality for your blog.
+
+- Type: Boolean
+- Default: false
+- Usage: `build_search_index = false`
+
+When enabled, a search index will be generated for your blog, allowing visitors to search for specific content.
+Additionally, a search button will be displayed in the navigation bar.
+
+Configure the search like this:
+
+```toml
+build_search_index = true
+
+[search]
+include_title = true
+include_description = true
+include_path = true
+include_content = true
+index_format = "elasticlunr_json"
+```
+
+## Theme Mode (`theme`)
+
+Sets the color theme for your blog.
+
+- Type: String
+- Options: "light", "dark", "auto", "toggle"
+- Default: "toggle"
+- Usage: `theme = "toggle"`
+
+The "toggle" option allows users to switch between light and dark modes, while "auto" typically follows the user's system preferences.
+
+## Menu
+
+Defines the navigation menu items for your blog.
+
+- Type: Array of objects
+- Default: []
+- Usage:
+  ```toml
+  menu = [
+    { name = "/posts", url = "/posts", weight = 1 },
+    { name = "/projects", url = "/projects", weight = 2 },
+    { name = "/about", url = "/about", weight = 3 },
+    { name = "/tags", url = "/tags", weight = 4 },
+  ]
+  ```
+
+## Logo
+
+Defines the site logo image file.
+
+- Type: String
+- Usage:
+  ```toml
+  logo = "site_logo.svg"
+  ```
+
+## Socials
+
+Defines the social media links.
+
+- Type: Array of objects
+- Default: []
+- Usage:
+  ```toml
+  socials = [
+    { name = "twitter", url = "https://twitter.com/not_matthias", icon = "twitter" },
+    { name = "github", url = "https://github.com/not-matthias/", icon = "github" },
+  ]
+  ```
+
+## Table of Contents (`toc`)
+
+Enables or disables the table of contents for posts.
+
+- Type: Boolean
+- Default: true
+- Usage: `toc = true`
+
+When enabled, a table of contents will be generated for posts, making it easier for readers to navigate through longer articles.
+
+Note: This feature adds additional JavaScript to your site.
+
+## CDN Usage (`use_cdn`)
+
+Determines whether to use a Content Delivery Network (CDN) for assets.
+
+- Type: Boolean
+- Default: false
+- Usage: `use_cdn = false`
+
+When set to true, the theme will attempt to load assets from a CDN, which can improve loading times for visitors from different geographic locations.
+
+## Favicon (`favicon`)
+
+Specifies the path to the favicon image for your blog.
+
+- Type: String
+- Default: "/icon/favicon.png"
+- Usage: `favicon = "/icon/favicon.png"`
+
+This sets the small icon that appears in the browser tab for your website.
+
+## Custom Stylesheets (`stylesheets`)
+
+Allows you to add custom stylesheets to your blog.
+
+- Type: Array of files located in the `static` directory
+- Default: []
+- Usage:
+  ```toml
+  stylesheets = [
+    "custom.css",           # static/custom.css
+    "/css/another.css"      # static/css/another.css
+  ]
+  ```
+
+## Fancy Code Styling (`fancy_code`)
+
+Enables enhanced styling for code blocks.
+
+- Type: Boolean
+- Default: true
+- Usage: `fancy_code = true`
+
+This option adds the language label and a copy button.
+
+## Dynamic Notes (`dynamic_note`)
+
+Allows for the creation of togglable note sections in your content.
+
+- Type: Boolean
+- Default: true
+- Usage: `dynamic_note = true`
+
+When enabled, you can create expandable/collapsible note sections in your blog posts.
+
+## Anchor Links
+
+You can add anchor links by adding the following to your `_index.md`:
+
+```toml
+insert_anchor_links = "heading"
+```
+
+## Analytics
+
+Enable or disable analytics tracking:
+
+```toml
+[extra.analytics]
+enabled = false
+```
+
+After enabling analytics, configure GoatCounter or Umami.
+
+### GoatCounter
+
+Configure GoatCounter analytics:
+
+```toml
+[extra.analytics.goatcounter]
+user = "your_user"           # Your GoatCounter username
+host = "example.com"         # Optional: Custom host
+```
+
+### Umami Analytics
+
+Configure Umami analytics:
+
+```toml
+[extra.analytics.umami]
+website_id = "43929cd1-1e83...."                    # Your Umami website ID
+host_url = "https://stats.mywebsite.com"            # Optional: Custom host URL
+```
+
+---
+
+# Page configuration
+
+## Source code (`repo_view`)
+
+Do you want to link to the source code of your blog post? You can turn on the `repo_view` inside the `[extra]` section of your blog post.
+
+```toml
+[extra]
+repo_view = true
+repo_url = "https://github.com/not-matthias/apollo/tree/main/content" # Alternatively add the repo here
+```
+
+The `repo_url` can be set in the `[extra]` section or in your `config.toml`.
+
+## Comments (`comment`)
+
+Enables or disables the comment system for posts.
+
+- Type: Boolean
+- Default: false
+- Usage: `comment = false`
+
+After making `comment = true` in `[extra]` section of you post, save your script from [Giscus](https://giscus.app) to `templates/_giscus_script.html`.
+When enabled, this allows readers to leave comments on your blog posts. This feature has to be set for each individual post and is not supported at higher levels.
+
+Example configuration in [content/posts/configuration.md](https://github.com/not-matthias/apollo/blob/main/content/posts/configuration.md):
+
+```toml
++++
+title = "Configuring Apollo"
+
+[extra]
+comment = true
++++
+```
+
+Comments via [utterances](https://utteranc.es) can be configured in `template/_giscus_script.html` like this:
+
+```html
+<script src="https://utteranc.es/client.js"
+        repo="YOUR_NAME/YOUR_REPO"
+        issue-term="pathname"
+        theme="github-light"
+        crossorigin="anonymous"
+        async>
+</script>
+```
diff --git a/blog-src/themes/apollo/content/_index.md b/blog-src/themes/apollo/content/_index.md
new file mode 100644
index 000000000..1813f3242
--- /dev/null
+++ b/blog-src/themes/apollo/content/_index.md
@@ -0,0 +1,8 @@
++++
+title= "Apollo Theme"
+template = "homepage.html"
++++
+
+Thanks for checking out this theme!
+
+Checkout all the [options you can configure](./posts/configuration) and the [example pages](./tags/example/).
diff --git a/blog-src/themes/apollo/content/about.md b/blog-src/themes/apollo/content/about.md
new file mode 100644
index 000000000..1f0623ea8
--- /dev/null
+++ b/blog-src/themes/apollo/content/about.md
@@ -0,0 +1,4 @@
++++
+title = "About"
+path = "about"
++++
diff --git a/blog-src/themes/apollo/content/posts/_index.md b/blog-src/themes/apollo/content/posts/_index.md
new file mode 100644
index 000000000..b0c6a013f
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/_index.md
@@ -0,0 +1,10 @@
++++
+paginate_by = 7
+title = "Posts"
+sort_by = "date"
+
+insert_anchor_links = "heading"
+
+[extra]
+comment = true
++++
diff --git a/blog-src/themes/apollo/content/posts/configuration.md b/blog-src/themes/apollo/content/posts/configuration.md
new file mode 100644
index 000000000..acf3bc2b3
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/configuration.md
@@ -0,0 +1,243 @@
++++
+title = "Configuring Apollo"
+date = "2024-07-09"
+
+[taxonomies]
+tags=["documentation"]
+
+[extra]
+repo_view = true
+comment = true
++++
+
+# Site Configuration
+
+## Search (`build_search_index`)
+
+Enables or disables the search functionality for your blog.
+
+- Type: Boolean
+- Default: false
+- Usage: `build_search_index = false`
+
+When enabled, a search index will be generated for your blog, allowing visitors to search for specific content.
+Additionally, a search button will be displayed in the navigation bar.
+
+Configure the search like this:
+
+```toml
+build_search_index = true
+
+[search]
+include_title = true
+include_description = true
+include_path = true
+include_content = true
+index_format = "elasticlunr_json"
+```
+
+## Theme Mode (`theme`)
+
+Sets the color theme for your blog.
+
+- Type: String
+- Options: "light", "dark", "auto", "toggle"
+- Default: "toggle"
+- Usage: `theme = "toggle"`
+
+The "toggle" option allows users to switch between light and dark modes, while "auto" typically follows the user's system preferences.
+
+## Menu
+
+Defines the navigation menu items for your blog.
+
+- Type: Array of objects
+- Default: []
+- Usage:
+  ```toml
+  menu = [
+    { name = "/posts", url = "/posts", weight = 1 },
+    { name = "/projects", url = "/projects", weight = 2 },
+    { name = "/about", url = "/about", weight = 3 },
+    { name = "/tags", url = "/tags", weight = 4 },
+  ]
+  ```
+
+## Logo
+
+Defines the site logo image file.
+
+- Type: String
+- Usage:
+  ```toml
+  logo = "site_logo.svg"
+  ```
+
+## Socials
+
+Defines the social media links.
+
+- Type: Array of objects
+- Default: []
+- Usage:
+  ```toml
+  socials = [
+    { name = "twitter", url = "https://twitter.com/not_matthias", icon = "twitter" },
+    { name = "github", url = "https://github.com/not-matthias/", icon = "github" },
+  ]
+  ```
+
+## Table of Contents (`toc`)
+
+Enables or disables the table of contents for posts.
+
+- Type: Boolean
+- Default: true
+- Usage: `toc = true`
+
+When enabled, a table of contents will be generated for posts, making it easier for readers to navigate through longer articles.
+
+Note: This feature adds additional JavaScript to your site.
+
+## CDN Usage (`use_cdn`)
+
+Determines whether to use a Content Delivery Network (CDN) for assets.
+
+- Type: Boolean
+- Default: false
+- Usage: `use_cdn = false`
+
+When set to true, the theme will attempt to load assets from a CDN, which can improve loading times for visitors from different geographic locations.
+
+## Favicon (`favicon`)
+
+Specifies the path to the favicon image for your blog.
+
+- Type: String
+- Default: "/icon/favicon.png"
+- Usage: `favicon = "/icon/favicon.png"`
+
+This sets the small icon that appears in the browser tab for your website.
+
+## Custom Stylesheets (`stylesheets`)
+
+Allows you to add custom stylesheets to your blog.
+
+- Type: Array of files located in the `static` directory
+- Default: []
+- Usage:
+  ```toml
+  stylesheets = [
+    "custom.css",           # static/custom.css
+    "/css/another.css"      # static/css/another.css
+  ]
+  ```
+
+## Fancy Code Styling (`fancy_code`)
+
+Enables enhanced styling for code blocks.
+
+- Type: Boolean
+- Default: true
+- Usage: `fancy_code = true`
+
+This option adds the language label and a copy button.
+
+## Dynamic Notes (`dynamic_note`)
+
+Allows for the creation of togglable note sections in your content.
+
+- Type: Boolean
+- Default: true
+- Usage: `dynamic_note = true`
+
+When enabled, you can create expandable/collapsible note sections in your blog posts.
+
+## Anchor Links
+
+You can add anchor links by adding the following to your `_index.md`:
+
+```toml
+insert_anchor_links = "heading"
+```
+
+## Analytics
+
+Enable or disable analytics tracking:
+
+```toml
+[extra.analytics]
+enabled = false
+```
+
+After enabling analytics, configure GoatCounter or Umami.
+
+### GoatCounter
+
+Configure GoatCounter analytics:
+
+```toml
+[extra.analytics.goatcounter]
+user = "your_user"           # Your GoatCounter username
+host = "example.com"         # Optional: Custom host
+```
+
+### Umami Analytics
+
+Configure Umami analytics:
+
+```toml
+[extra.analytics.umami]
+website_id = "43929cd1-1e83...."                    # Your Umami website ID
+host_url = "https://stats.mywebsite.com"            # Optional: Custom host URL
+```
+
+---
+
+# Page configuration
+
+## Source code (`repo_view`)
+
+Do you want to link to the source code of your blog post? You can turn on the `repo_view` inside the `[extra]` section of your blog post.
+
+```toml
+[extra]
+repo_view = true
+repo_url = "https://github.com/not-matthias/apollo/tree/main/content" # Alternatively add the repo here
+```
+
+The `repo_url` can be set in the `[extra]` section or in your `config.toml`.
+
+## Comments (`comment`)
+
+Enables or disables the comment system for posts.
+
+- Type: Boolean
+- Default: false
+- Usage: `comment = false`
+
+After making `comment = true` in `[extra]` section of you post, save your script from [Giscus](https://giscus.app) to `templates/_giscus_script.html`.
+When enabled, this allows readers to leave comments on your blog posts. This feature has to be set for each individual post and is not supported at higher levels.
+
+Example configuration in [content/posts/configuration.md](https://github.com/not-matthias/apollo/blob/main/content/posts/configuration.md):
+
+```toml
++++
+title = "Configuring Apollo"
+
+[extra]
+comment = true
++++
+```
+
+Comments via [utterances](https://utteranc.es) can be configured in `template/_giscus_script.html` like this:
+
+```html
+<script src="https://utteranc.es/client.js"
+        repo="YOUR_NAME/YOUR_REPO"
+        issue-term="pathname"
+        theme="github-light"
+        crossorigin="anonymous"
+        async>
+</script>
+```
diff --git a/blog-src/themes/apollo/content/posts/markdown.md b/blog-src/themes/apollo/content/posts/markdown.md
new file mode 100644
index 000000000..ec840455b
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/markdown.md
@@ -0,0 +1,86 @@
++++
+title = "Markdown Test"
+date = "2022-01-01"
+updated = "2022-05-01"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+# H1
+
+## H2
+
+### H3
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aliquet sagittis id consectetur purus ut. In pellentesque massa placerat duis ultricies. Neque laoreet suspendisse interdum consectetur libero id. Justo nec ultrices dui sapien eget mi proin. Nunc consequat interdum varius sit amet mattis vulputate. Sollicitudin tempor id eu nisl nunc mi ipsum. Non odio euismod lacinia at quis. Sit amet nisl suscipit adipiscing. Ame [...]
+
+![Markdown Logo](https://markdown-here.com/img/icon256.png)
+
+## Code Block
+
+```rust
+fn main() {
+    println!("Hello World");
+}
+```
+
+```rust,hl_lines=2,linenos
+fn main() {
+    println!("Hello World");
+}
+```
+
+## Ordered List
+
+1. First item
+2. Second item
+3. Third item
+
+## Unordered List
+
+- List item
+- Another item
+- And another item
+
+## Nested list
+
+- Fruit
+  - Apple
+  - Orange
+  - Banana
+- Dairy
+  - Milk
+  - Cheese
+
+## Quote
+
+> Two things are infinite: the universe and human stupidity; and I'm not sure about the
+> universe.<br>
+> — <cite>Albert Einstein</cite>
+
+## Table Inline Markdown
+
+| Italics   | Bold     | Code   | StrikeThrough     |
+| --------- | -------- | ------ | ----------------- |
+| _italics_ | **bold** | `code` | ~~strikethrough~~ |
+
+## Foldable Text
+
+<details>
+    <summary>Title 1</summary>
+    <p>IT'S A SECRET TO EVERYBODY.</p>
+</details>
+
+<details>
+    <summary>Title 2</summary>
+    <p>Stay awhile, and listen!</p>
+</details>
+
+## Code tags
+
+Lorem ipsum `dolor` sit amet, `consectetur adipiscing` elit.
+`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`
diff --git a/blog-src/themes/apollo/content/posts/math-symbol.md b/blog-src/themes/apollo/content/posts/math-symbol.md
new file mode 100644
index 000000000..4b7e6bbc9
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/math-symbol.md
@@ -0,0 +1,25 @@
++++
+title = "Math Symbol Example"
+date = "2023-01-06"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+Note: This requires the `mathjax` and `mathjax_dollar_inline_enable` option set to `true` in `[extra]` section.
+
+# Inline Math
+
+- $(a+b)^2$ = $a^2 + 2ab + b^2$
+- A polynomial P of degree d over $\mathbb{F}_p$ is an expression of the form
+  $P(s) = a_0 + a_1 . s + a_2 . s^2 + ... + a_d . s^d$ for some
+  $a_0,..,a_d \in \mathbb{F}_p$
+
+# Displayed Math
+
+$$
+p := (\sum_{k∈I}{c_k.v_k} + \delta_v.t(x))·(\sum_{k∈I}{c_k.w_k} + \delta_w.t(x)) − (\sum_{k∈I}{c_k.y_k} + \delta_y.t(x))
+$$
diff --git a/blog-src/themes/apollo/content/posts/mermaid.md b/blog-src/themes/apollo/content/posts/mermaid.md
new file mode 100644
index 000000000..6d11d262f
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/mermaid.md
@@ -0,0 +1,28 @@
++++
+title = "Mermaid Example"
+date = "2024-12-26"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+This Theme supports [mermaid](https://mermaid.js.org/) markdown diagram rendering.
+
+To use mermaid diagrams in your posts, see the example in the raw markdown code.
+https://raw.githubusercontent.com/not-matthias/apollo/refs/heads/main/content/posts/mermaid.md
+
+## Rendered Example
+
+{% mermaid() %}
+graph LR
+    A[Start] --> B[Initialize]
+    B --> C[Processing]
+    C --> D[Complete]
+    D --> E[Success]
+    
+    style A fill:#f9f,stroke:#333
+    style E fill:#9f9,stroke:#333
+{% end %}
diff --git a/blog-src/themes/apollo/content/posts/shortcode.md b/blog-src/themes/apollo/content/posts/shortcode.md
new file mode 100644
index 000000000..1b630e788
--- /dev/null
+++ b/blog-src/themes/apollo/content/posts/shortcode.md
@@ -0,0 +1,81 @@
++++
+title = "Shortcode Example"
+date = "2024-06-14"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+
+## Note
+
+Here is an example of the `note` shortcode:
+
+This one is static!
+{{ note(header="Note!", body="This blog assumes basic terminal maturity") }}
+
+This one is clickable!
+{{ note(clickable=true, hidden = true, header="Quiz!", body="The answer to the quiz!") }}
+
+
+Syntax:
+```
+{{/* note(header="Note!", body="This blog assumes basic terminal maturity") */}}
+{{/* note(clickable=true, hidden = true, header="Quiz!", body="The answer to the quiz!") */}}
+```
+
+You can also use some HTML in the text:
+{{ note(header="Note!", body="<h1>This blog assumes basic terminal maturity</h1>") }}
+
+
+Literal shortcode:
+```
+{{/* note(header="Note!", body="<h1>This blog assumes basic terminal maturity</h1>") */}}
+```
+
+Pretty cool, right?
+
+Finally, you can do something like this (hopefully):
+
+{% note(clickable=true, header="Quiz!") %}
+
+# Hello this is markdown inside a note shortcode
+
+```rust
+fn main() {
+    println!("Hello World");
+}
+```
+
+We can't call another shortcode inside a shortcode, but this is good enough.
+
+{% end %}
+
+Here is the raw markdown:
+
+```markdown
+{{/* note(clickable=true, header="Quiz!") */}}
+
+# Hello this is markdown inside a note shortcode
+
+\`\`\`rust
+fn main() {
+    println!("Hello World");
+}
+\`\`\`
+
+We can't call another shortcode inside a shortcode, but this is good enough.
+
+{{/* end */}}
+```
+
+Finally, we have center
+{{ note(center=true, header="Centered Text", body="This is centered text") }}
+
+```markdown
+{{/* note(center=true, header="Centered Text", body="This is centered text") */}}
+```
+It works good enough for me!
diff --git a/blog-src/themes/apollo/content/projects/_index.md b/blog-src/themes/apollo/content/projects/_index.md
new file mode 100644
index 000000000..9b00a1298
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/_index.md
@@ -0,0 +1,5 @@
++++
+title = "Projects"
+sort_by = "weight"
+template = "cards.html"
++++
diff --git a/blog-src/themes/apollo/content/projects/project-1.jpg b/blog-src/themes/apollo/content/projects/project-1.jpg
new file mode 100644
index 000000000..db5232ef1
Binary files /dev/null and b/blog-src/themes/apollo/content/projects/project-1.jpg differ
diff --git a/blog-src/themes/apollo/content/projects/project_1.md b/blog-src/themes/apollo/content/projects/project_1.md
new file mode 100644
index 000000000..420e59a99
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/project_1.md
@@ -0,0 +1,11 @@
++++
+title = "Apollo"
+description = "Modern and minimalistic blog theme."
+weight = 1
+
+[extra]
+local_image = "/projects/project-1.jpg"
+link_to = "https://github.com/not-matthias/apollo"
++++
+
+Example project page
diff --git a/blog-src/themes/apollo/content/projects/project_2.md b/blog-src/themes/apollo/content/projects/project_2.md
new file mode 100644
index 000000000..43704c7db
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/project_2.md
@@ -0,0 +1,11 @@
++++
+title = "Project 2"
+description = "Example description"
+weight = 1
+
+[extra]
+# You can also crop the image in the url by adjusting w=/h=
+remote_image = "https://images.unsplash.com/photo-1523821741446-edb2b68bb7a0?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80"
++++
+
+Example project page
diff --git a/blog-src/themes/apollo/content/projects/project_3.md b/blog-src/themes/apollo/content/projects/project_3.md
new file mode 100644
index 000000000..de8e315b8
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/project_3.md
@@ -0,0 +1,10 @@
++++
+title = "Project 3"
+description = "Example description"
+weight = 1
+
+[extra]
+remote_image = "https://images.unsplash.com/photo-1462556791646-c201b8241a94?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1465&q=80"
++++
+
+Example project page
diff --git a/blog-src/themes/apollo/content/projects/project_4.md b/blog-src/themes/apollo/content/projects/project_4.md
new file mode 100644
index 000000000..a3666631e
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/project_4.md
@@ -0,0 +1,10 @@
++++
+title = "Project 4"
+description = "Example description with a lot of words but without any meaning. Why use lorem ipsum when you can just write a lot of text that has no underlying meaning?"
+weight = 1
+
+[extra]
+remote_image = "https://images.unsplash.com/photo-1620121692029-d088224ddc74?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1632&q=80"
++++
+
+Example project page
diff --git a/blog-src/themes/apollo/content/projects/project_5.md b/blog-src/themes/apollo/content/projects/project_5.md
new file mode 100644
index 000000000..920d5f0b3
--- /dev/null
+++ b/blog-src/themes/apollo/content/projects/project_5.md
@@ -0,0 +1,7 @@
++++
+title = "Project 4"
+description = "Example description"
+weight = 1
++++
+
+Example project page
diff --git a/blog-src/themes/apollo/markdown.md b/blog-src/themes/apollo/markdown.md
new file mode 100644
index 000000000..ec840455b
--- /dev/null
+++ b/blog-src/themes/apollo/markdown.md
@@ -0,0 +1,86 @@
++++
+title = "Markdown Test"
+date = "2022-01-01"
+updated = "2022-05-01"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+# H1
+
+## H2
+
+### H3
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aliquet sagittis id consectetur purus ut. In pellentesque massa placerat duis ultricies. Neque laoreet suspendisse interdum consectetur libero id. Justo nec ultrices dui sapien eget mi proin. Nunc consequat interdum varius sit amet mattis vulputate. Sollicitudin tempor id eu nisl nunc mi ipsum. Non odio euismod lacinia at quis. Sit amet nisl suscipit adipiscing. Ame [...]
+
+![Markdown Logo](https://markdown-here.com/img/icon256.png)
+
+## Code Block
+
+```rust
+fn main() {
+    println!("Hello World");
+}
+```
+
+```rust,hl_lines=2,linenos
+fn main() {
+    println!("Hello World");
+}
+```
+
+## Ordered List
+
+1. First item
+2. Second item
+3. Third item
+
+## Unordered List
+
+- List item
+- Another item
+- And another item
+
+## Nested list
+
+- Fruit
+  - Apple
+  - Orange
+  - Banana
+- Dairy
+  - Milk
+  - Cheese
+
+## Quote
+
+> Two things are infinite: the universe and human stupidity; and I'm not sure about the
+> universe.<br>
+> — <cite>Albert Einstein</cite>
+
+## Table Inline Markdown
+
+| Italics   | Bold     | Code   | StrikeThrough     |
+| --------- | -------- | ------ | ----------------- |
+| _italics_ | **bold** | `code` | ~~strikethrough~~ |
+
+## Foldable Text
+
+<details>
+    <summary>Title 1</summary>
+    <p>IT'S A SECRET TO EVERYBODY.</p>
+</details>
+
+<details>
+    <summary>Title 2</summary>
+    <p>Stay awhile, and listen!</p>
+</details>
+
+## Code tags
+
+Lorem ipsum `dolor` sit amet, `consectetur adipiscing` elit.
+`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`
diff --git a/blog-src/themes/apollo/math-symbol.md b/blog-src/themes/apollo/math-symbol.md
new file mode 100644
index 000000000..4b7e6bbc9
--- /dev/null
+++ b/blog-src/themes/apollo/math-symbol.md
@@ -0,0 +1,25 @@
++++
+title = "Math Symbol Example"
+date = "2023-01-06"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+Note: This requires the `mathjax` and `mathjax_dollar_inline_enable` option set to `true` in `[extra]` section.
+
+# Inline Math
+
+- $(a+b)^2$ = $a^2 + 2ab + b^2$
+- A polynomial P of degree d over $\mathbb{F}_p$ is an expression of the form
+  $P(s) = a_0 + a_1 . s + a_2 . s^2 + ... + a_d . s^d$ for some
+  $a_0,..,a_d \in \mathbb{F}_p$
+
+# Displayed Math
+
+$$
+p := (\sum_{k∈I}{c_k.v_k} + \delta_v.t(x))·(\sum_{k∈I}{c_k.w_k} + \delta_w.t(x)) − (\sum_{k∈I}{c_k.y_k} + \delta_y.t(x))
+$$
diff --git a/blog-src/themes/apollo/mermaid.md b/blog-src/themes/apollo/mermaid.md
new file mode 100644
index 000000000..6d11d262f
--- /dev/null
+++ b/blog-src/themes/apollo/mermaid.md
@@ -0,0 +1,28 @@
++++
+title = "Mermaid Example"
+date = "2024-12-26"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+This Theme supports [mermaid](https://mermaid.js.org/) markdown diagram rendering.
+
+To use mermaid diagrams in your posts, see the example in the raw markdown code.
+https://raw.githubusercontent.com/not-matthias/apollo/refs/heads/main/content/posts/mermaid.md
+
+## Rendered Example
+
+{% mermaid() %}
+graph LR
+    A[Start] --> B[Initialize]
+    B --> C[Processing]
+    C --> D[Complete]
+    D --> E[Success]
+    
+    style A fill:#f9f,stroke:#333
+    style E fill:#9f9,stroke:#333
+{% end %}
diff --git a/blog-src/themes/apollo/sass/fonts.scss b/blog-src/themes/apollo/sass/fonts.scss
new file mode 100644
index 000000000..22ca4e31d
--- /dev/null
+++ b/blog-src/themes/apollo/sass/fonts.scss
@@ -0,0 +1,31 @@
+@font-face {
+  font-family: "Jetbrains Mono";
+  font-style: normal;
+  font-weight: 400;
+  src: url("fonts/JetbrainsMono/JetBrainsMono-Regular.ttf"), local("ttf");
+  font-display: swap;
+}
+
+@font-face {
+  font-family: "Jetbrains Mono";
+  font-style: normal;
+  font-weight: 700;
+  src: url("fonts/JetbrainsMono/JetBrainsMono-Bold.ttf"), local("ttf");
+  font-display: swap;
+}
+
+@font-face {
+  font-family: "Space Grotesk";
+  font-style: normal;
+  font-weight: 400;
+  src: url("fonts/SpaceGrotesk/SpaceGrotesk-Regular.ttf"), local("ttf");
+  font-display: swap;
+}
+
+@font-face {
+  font-family: "Space Grotesk";
+  font-style: normal;
+  font-weight: 700;
+  src: url("fonts/SpaceGrotesk/SpaceGrotesk-Bold.ttf"), local("ttf");
+  font-display: swap;
+}
diff --git a/blog-src/themes/apollo/sass/main.scss b/blog-src/themes/apollo/sass/main.scss
new file mode 100644
index 000000000..1ff5cdce6
--- /dev/null
+++ b/blog-src/themes/apollo/sass/main.scss
@@ -0,0 +1,53 @@
+@import "parts/_cards.scss";
+@import "parts/_code.scss";
+@import "parts/_header.scss";
+@import "parts/_image.scss";
+@import "parts/_toc.scss";
+@import "parts/_note.scss";
+@import "parts/_misc.scss";
+@import "parts/_table.scss";
+@import "parts/_tags.scss";
+@import "parts/_mermaid.scss";
+@import "parts/_search.scss";
+
+:root {
+  /* Used for: block comment, hr, ... */
+  --border-color: var(--border-color);
+
+  /* Fonts */
+  --text-font: "Manrope";
+  --header-font: "Manrope", "Helvetica", sans-serif;
+  --code-font: "Jetbrains Mono";
+}
+
+html {
+  background-color: var(--bg-0);
+  color: var(--text-0);
+  font-family: var(--text-font);
+  line-height: 1.6em;
+}
+
+.content {
+  max-width: 1000px;
+  margin: 0 auto;
+  padding: 0 24px;
+  word-wrap: break-word;
+}
+
+@media all and (min-width: 640px) {
+  html {
+    font-size: 16.5px;
+  }
+}
+
+@media all and (min-width: 720px) {
+  html {
+    font-size: 17px;
+  }
+}
+
+@media all and (min-width: 960px) {
+  html {
+    font-size: 18px;
+  }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_cards.scss b/blog-src/themes/apollo/sass/parts/_cards.scss
new file mode 100644
index 000000000..1105396ee
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_cards.scss
@@ -0,0 +1,55 @@
+.cards {
+  display: grid;
+  grid-template-rows: auto;
+  gap: 24px;
+  padding: 12px 0;
+}
+
+@media all and (min-width: 640px) {
+  .cards {
+    grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
+  }
+}
+
+@media all and (max-width: 640px) {
+  .cards {
+    grid-template-columns: repeat(auto-fill, 1fr);
+  }
+}
+
+.card {
+  min-height: 100px;
+  background: var(--bg-1);
+  border: 2px solid var(--border-color);
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+.card-info {
+  padding: 0 24px 24px 24px;
+}
+
+.card-title {
+  margin-top: 0.7em;
+}
+
+.card-image {
+  border: unset;
+  width: 100%;
+}
+
+.card-image-placeholder {
+  height: 12px;
+  width: 100%;
+}
+
+.card-description {
+  margin-top: 0.5em;
+  overflow: hidden;
+}
+
+@media all and (max-width: 720px) {
+  .cards {
+    gap: 18px;
+  }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_code.scss b/blog-src/themes/apollo/sass/parts/_code.scss
new file mode 100644
index 000000000..d3c7b1cd1
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_code.scss
@@ -0,0 +1,237 @@
+// Define base colors and fonts for light and dark themes
+:root {
+  --code-font: "Courier New", monospace;
+  --bg-primary: var(--bg-1);
+  --text-color: var(--text-0); // Color of the code text
+  --label-color: #f0f0f0; // Text color of the label
+
+  --hightlight-color: #f0f0f0;
+}
+
+:root.dark {
+  --hightlight-color: #204e8a;
+}
+
+// Define language colors map
+$language-colors: (
+  "js": (
+    #f7df1e,
+    "JavaScript",
+  ),
+  "yaml": (
+    #f71e6a,
+    "YAML",
+  ),
+  "shell": (
+    #4eaa25,
+    "Shell",
+  ),
+  // Updated to a more specific green shade
+  "json": (
+      dodgerblue,
+      "JSON",
+    ),
+  "python": (
+    #3572a5,
+    "Python",
+  ),
+  // Using the specific Python blue
+  "css": (
+      #264de4,
+      "CSS",
+    ),
+  "go": (
+    #00add8,
+    "Go",
+  ),
+  // Official Go color
+  "markdown": (
+      #0000ff,
+      "Markdown",
+    ),
+  "rust": (
+    #ff4647,
+    "Rust",
+  ),
+  // Adjusted to match Rust's branding
+  "java": (
+      #f89820,
+      "Java",
+    ),
+  // Oracle Java color
+  "csharp": (
+      #178600,
+      "C#",
+    ),
+  "ruby": (
+    #701516,
+    "Ruby",
+  ),
+  "swift": (
+    #f05138,
+    "Swift",
+  ),
+  "php": (
+    #777bb4,
+    "PHP",
+  ),
+  "typescript": (
+    #3178c6,
+    "TypeScript",
+  ),
+  "scala": (
+    #c22d40,
+    "Scala",
+  ),
+  "kotlin": (
+    #f18e33,
+    "Kotlin",
+  ),
+  "lua": (
+    #000080,
+    "Lua",
+  ),
+  "perl": (
+    #0298c3,
+    "Perl",
+  ),
+  "haskell": (
+    #5e5086,
+    "Haskell",
+  ),
+  "r": (
+    #198ce7,
+    "R",
+  ),
+  "dart": (
+    #00d2b8,
+    "Dart",
+  ),
+  "elixir": (
+    #6e4a7e,
+    "Elixir",
+  ),
+  "clojure": (
+    #5881d8,
+    "Clojure",
+  ),
+  "bash": (
+    #4eaa25,
+    "Bash",
+  ),
+  "default": (
+    #333,
+    "Code",
+  ),
+);
+
+@mixin base-label-style($bg-color, $text-color: var(--label-color)) {
+  background: $bg-color;
+  color: $text-color;
+  border-radius: 0 0 0.25rem 0.25rem;
+  font-size: 12px;
+  letter-spacing: 0.025rem;
+  padding: 0.1rem 0.5rem;
+  text-align: right;
+  text-transform: uppercase;
+  position: absolute;
+  right: 0;
+  top: 0;
+  margin-top: 0.1rem;
+}
+
+// Example usage within a specific class for clarity
+.code-label {
+  @include base-label-style(#333); // Default background color
+}
+
+@each $lang, $color-info in $language-colors {
+  .label-#{$lang} {
+    @include base-label-style(nth($color-info, 1));
+  }
+}
+
+code {
+  background-color: var(--bg-primary);
+  padding: 0.1em 0.2em;
+  border-radius: 5px;
+  border: 1px solid var(--border-color);
+  font-family: var(--code-font);
+}
+
+pre {
+  background-color: var(--bg-primary) !important;
+  border-radius: 5px;
+  border: 1px solid var(--border-color);
+  line-height: 1.4;
+  overflow-x: auto;
+  padding: 1em;
+  position: relative;
+
+  mark {
+    background-color: var(
+      --hightlight-color
+    ) !important; // Ensure mark uses the theme background
+    padding: 0;
+    border-radius: 0px;
+  }
+
+  code {
+    background-color: transparent !important;
+    color: var(--text-color);
+    font-size: 100%;
+    padding: 0;
+    border: none;
+    font-family: var(--code-font);
+
+    table {
+      margin: 0;
+      border-collapse: collapse;
+      font-family: var(--code-font);
+
+      mark {
+        display: block;
+        color: unset;
+        padding: 0;
+        background-color: var(--hightlight-color) !important;
+        filter: brightness(1.2); // Example to slightly increase brightness
+      }
+    }
+
+    td,
+    th,
+    tr {
+      padding: 0;
+      border-bottom: none;
+      border: none; // Ensure no borders around rows
+    }
+
+    tbody td:first-child {
+      text-align: center;
+      user-select: none;
+      min-width: 60px;
+      border-right: none;
+    }
+
+    tbody tr:nth-child(even),
+    thead tr {
+      background-color: unset;
+    }
+  }
+}
+
+.clipboard-button,
+.clipboard-button svg {
+  all: unset;
+  cursor: pointer;
+  position: absolute;
+  bottom: 5px;
+  /* 5px from the bottom */
+  right: 5px;
+  /* 5px from the right */
+  z-index: 10;
+  background-color: transparent;
+  border: none;
+  fill: #ef5350;
+  /* Sets the color of the SVG, assuming it's an SVG icon */
+}
diff --git a/blog-src/themes/apollo/sass/parts/_header.scss b/blog-src/themes/apollo/sass/parts/_header.scss
new file mode 100644
index 000000000..762617382
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_header.scss
@@ -0,0 +1,166 @@
+.page-header {
+  font-size: 2.5em;
+  line-height: 100%;
+  font-family: var(--header-font);
+  margin: 4rem 0px 1rem 0px;
+}
+
+.centered-header {
+  font-family: var(--header-font);
+  position: absolute;
+  top: 40%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  text-align: center;
+  font-size: 4em;
+}
+
+.centered-header span {
+  line-height: 100%;
+}
+
+header {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  padding: 1em 0;
+}
+
+header .main {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 12px;
+  font-size: 1.5rem;
+
+  /* Otherwise header and menu is too close on small screens*/
+  margin-bottom: 5px;
+}
+
+header .social img,
+header #dark-mode-toggle img {
+  width: 16px;
+  height: 16px;
+}
+
+header .socials {
+  margin-bottom: 10px;
+  /* Space between social icons and menu items */
+}
+
+#dark-mode-toggle {
+  justify-content: center;
+}
+
+.logo {
+  border-bottom: unset;
+  background-image: unset;
+}
+
+.logo > img {
+  border: unset;
+  width: auto;
+  height: 24px;
+  vertical-align: middle;
+}
+
+.logo:hover {
+  background-color: transparent;
+}
+
+.socials {
+  /* flex-child */
+  flex-grow: 0;
+  /* flex-container */
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: flex-start;
+  align-items: flex-end;
+  gap: 6px;
+}
+
+.social {
+  border-bottom: unset;
+  background-image: unset;
+  padding: 2px;
+}
+
+.social > img {
+  border: unset;
+  width: 24px;
+  height: 24px;
+}
+
+/* Mobile-specific adjustments */
+@media (max-width: 600px) {
+  header {
+    flex-direction: column;
+    align-items: center;
+    padding: 1em 0;
+  }
+
+  header .main a {
+    font-size: 20px;
+  }
+}
+
+.meta {
+  color: #999;
+  display: flexbox;
+  /* This changes the meta class to use flexbox, which ensures inline display */
+  align-items: center;
+  /* Aligns items vertically in the middle */
+  flex-wrap: wrap;
+  /* Allows items to wrap as needed */
+}
+
+#dark-mode-toggle > img {
+  display: none;
+  width: 15px;
+  height: 15px;
+  border: unset;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: 1.2rem;
+  margin-top: 2em;
+}
+
+h1::before {
+  color: var(--primary-color);
+  content: "# ";
+}
+
+h2::before {
+  color: var(--primary-color);
+  content: "## ";
+}
+
+h3::before {
+  color: var(--primary-color);
+  content: "### ";
+}
+
+h4::before {
+  color: var(--primary-color);
+  content: "#### ";
+}
+
+h5::before {
+  color: var(--primary-color);
+  content: "##### ";
+}
+
+h6::before {
+  color: var(--primary-color);
+  content: "###### ";
+}
diff --git a/blog-src/themes/apollo/sass/parts/_image.scss b/blog-src/themes/apollo/sass/parts/_image.scss
new file mode 100644
index 000000000..2848e0d25
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_image.scss
@@ -0,0 +1,31 @@
+img {
+  border: 3px solid #ececec;
+  max-width: 100%;
+}
+
+figure {
+  box-sizing: border-box;
+  display: inline-block;
+  margin: 0;
+  max-width: 100%;
+}
+
+figure img {
+  max-height: 500px;
+}
+
+@media screen and (min-width: 600px) {
+  figure {
+    padding: 0 40px;
+  }
+}
+
+figure h4 {
+  font-size: 1rem;
+  margin: 0;
+  margin-bottom: 1em;
+}
+
+figure h4::before {
+  content: "↳ ";
+}
diff --git a/blog-src/themes/apollo/sass/parts/_mermaid.scss b/blog-src/themes/apollo/sass/parts/_mermaid.scss
new file mode 100644
index 000000000..3b21d3316
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_mermaid.scss
@@ -0,0 +1,9 @@
+.mermaid {
+  text-align: center;
+  margin-top: 1em;
+  margin-bottom: 1em;
+
+  strong {
+    font-weight: bold;
+  }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_misc.scss b/blog-src/themes/apollo/sass/parts/_misc.scss
new file mode 100644
index 000000000..7417dfb15
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_misc.scss
@@ -0,0 +1,132 @@
+.primary-color {
+  color: var(--primary-color);
+}
+
+.draft-label {
+  color: var(--hover-color);
+  text-decoration: none;
+  padding: 2px 4px;
+  border-radius: 4px;
+  margin-left: 6px;
+  background-color: var(--primary-color);
+}
+
+::-moz-selection {
+  background: var(--primary-color);
+  color: var(--hover-color);
+  text-shadow: none;
+}
+
+::selection {
+  background: var(--primary-color);
+  color: var(--hover-color);
+}
+
+p {
+  line-height: 1.5;
+}
+
+hr {
+  border: 0;
+  border-top: 3px solid var(--border-color);
+  margin: 1em 0;
+}
+
+blockquote {
+  border-left: 3px solid var(--primary-color);
+  color: #737373;
+  margin: 0;
+  padding-left: 1em;
+}
+
+a {
+  border-bottom: 3px solid var(--primary-color);
+  color: inherit;
+  text-decoration: none;
+
+  // Make sure the underline is at the top
+  position: relative; // needed for z-index
+  z-index: 1;
+}
+
+a.zola-anchor {
+  border-bottom: none;
+}
+
+a:hover {
+  background-color: var(--primary-color);
+  color: var(--hover-color);
+}
+
+time {
+  color: grey;
+}
+
+/* Remove post list padding */
+.list > ul {
+  margin: 0;
+  padding: 1rem 0 0 0;
+}
+
+/* Post list */
+.list-item {
+  margin-bottom: 30px;
+  list-style-type: none;
+}
+
+// change the line-through color
+del {
+  text-decoration-color: var(--primary-color);
+  text-decoration-thickness: 3px;
+}
+
+@media all and (max-width: 640px) {
+  .post-header {
+    display: grid;
+    grid-template-rows: auto 1fr;
+
+    h1 {
+      margin-top: 0;
+      // font-size: 130%;
+
+      a {
+        border-bottom: none;
+      }
+    }
+  }
+}
+
+/* Post list */
+@media all and (min-width: 640px) {
+  .post-header {
+    display: grid;
+    gap: 1rem;
+    grid-row-gap: 1.5rem;
+    grid-template-columns: auto 1fr;
+
+    h1 {
+      margin: 0;
+      font-size: 130%;
+
+      a {
+        border-bottom: none;
+      }
+    }
+  }
+}
+
+/* Remove styling from theme toggle button */
+#dark-mode-toggle {
+  border-bottom: none;
+
+  &:hover {
+    background-color: transparent;
+  }
+}
+
+.MathJax_Display,
+.MJXc-display,
+.MathJax_SVG_Display {
+  overflow-x: auto;
+  overflow-y: hidden;
+}
diff --git a/blog-src/themes/apollo/sass/parts/_note.scss b/blog-src/themes/apollo/sass/parts/_note.scss
new file mode 100644
index 000000000..c96f0525d
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_note.scss
@@ -0,0 +1,91 @@
+:root {
+  --note-header-bg: var(--bg-2);
+  --note-header-color: var(--text-0);
+  --note-content-bg: var(--bg-1);
+}
+
+.note-container {
+  border-radius: 4px;
+  overflow: hidden;
+  margin: 1em 0;
+  position: relative;
+  border-left: 3px solid var(--primary-color);
+  font-family: var(--paragraph-font);
+}
+
+.note-toggle,
+.note-header {
+  color: var(--note-header-color);
+  background-color: var(--note-header-bg);
+  padding: 10px 25px;
+  text-align: left;
+  border: none;
+  width: 100%;
+  position: relative;
+  outline: none;
+  font-size: 1.2em;
+  transition: background-color 0.3s ease;
+
+  p {
+    margin: 0;
+  }
+
+  .note-center {
+    text-align: center;
+    padding-right: 50px;
+  }
+
+  .note-icon,
+  .note-icon {
+    padding-left: 25px;
+  }
+}
+
+.note-toggle {
+  font-family: inherit;
+  padding: 10px 25px; /* Stop header intersecting with note-icon */
+  cursor: pointer;
+  position: relative;
+}
+
+.note-toggle::before {
+  content: "▼";
+  position: absolute;
+  right: 20px;
+  /* Position the arrow to the right */
+  top: 50%;
+  /* Center vertically */
+  transform: translateY(-50%);
+  /* Center vertically */
+}
+
+.note-toggle:hover,
+.note-toggle:focus {
+  color: var(--note-header-color);
+  background-color: var(--note-header-bg);
+  outline: none;
+}
+
+.note-content {
+  padding: 10px 20px;
+  background-color: var(--note-content-bg);
+}
+
+.note-icon::before {
+  content: "✎";
+  color: var(--primary-color);
+  position: absolute;
+  left: 20px;
+  top: 50%;
+  transform: translateY(-50%);
+}
+
+summary {
+    padding-left: 0.5em;
+
+    &:hover {
+        background-color: var(--primary-color);
+        color: var(--hover-color);
+        cursor: pointer;
+    }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_search.scss b/blog-src/themes/apollo/sass/parts/_search.scss
new file mode 100644
index 000000000..9adc1a7aa
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_search.scss
@@ -0,0 +1,222 @@
+$icon-size: 1.3rem;
+
+// Search button in navbar
+.search-button {
+  background: none;
+  border: none;
+  padding: 2px;
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  margin-left: 0.25em; // Match your other nav items' margin
+
+  img {
+    border: none;
+  }
+
+  .search-icon {
+    width: 16px; // Match your theme button size
+    height: 16px;
+  }
+
+  &:hover {
+    background-color: transparent;
+  }
+}
+
+// Theme-specific styles for search icon
+html.dark .search-button .search-icon {
+  filter: invert(1);
+}
+
+html.light .search-button .search-icon {
+  filter: invert(0);
+}
+
+// Search modal
+.search-modal {
+  display: none;
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1000;
+  background: rgba(0, 0, 0, 0.2);
+  backdrop-filter: blur(8px);
+  -webkit-backdrop-filter: blur(8px);
+
+  #modal-content {
+    position: relative;
+    margin: 8% auto;
+    width: 80%;
+    max-width: 28rem;
+    background-color: var(--bg-0);
+    border: 1px solid var(--bg-1);
+    border-radius: 8px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  }
+
+  #searchBar {
+    display: flex;
+    align-items: center;
+    padding: 1rem;
+    gap: 0.5rem;
+
+    #searchInput {
+      flex: 1;
+      padding: 0.75rem 2.5rem;
+      font-size: 1rem;
+      color: var(--text-0);
+      background: var(--bg-1);
+      border: 1px solid var(--bg-1);
+      border-radius: 20px;
+      width: 100%;
+
+      &:focus {
+        outline: none;
+        border-color: var(--primary-color);
+      }
+
+      &::placeholder {
+        color: var(--text-1);
+      }
+    }
+
+    .close-icon {
+      position: absolute;
+      right: 1.5rem;
+      display: none;
+      padding: 4px;
+      cursor: pointer;
+
+      svg {
+        width: $icon-size;
+        height: $icon-size;
+        fill: var(--text-1);
+      }
+    }
+  }
+
+  #results-container {
+    display: none;
+    border-top: 1px solid var(--bg-1);
+
+    #results-info {
+      padding: 0.5rem;
+      color: var(--text-1);
+      font-size: 0.8rem;
+      text-align: center;
+    }
+
+    #results {
+      max-height: 50vh;
+      overflow-y: auto;
+
+      > div {
+        padding: 0.75rem 1rem;
+        cursor: pointer;
+
+        &[aria-selected="true"] {
+          background: var(--primary-color);
+
+          * {
+            color: var(--hover-color) !important;
+          }
+        }
+
+        span:first-child {
+          display: block;
+          color: var(--text-0);
+          font-weight: 500;
+          margin-bottom: 0.25rem;
+        }
+
+        span:nth-child(2) {
+          display: block;
+          color: var(--text-1);
+          font-size: 0.9rem;
+        }
+
+        &:hover:not([aria-selected="true"]) {
+          background: var(--bg-1);
+        }
+      }
+    }
+  }
+
+  #modal-content {
+    position: relative;
+    margin: 8% auto;
+    width: 80%;
+    max-width: 28rem;
+    background-color: var(--bg-0);
+    border: 1px solid var(--bg-1);
+    border-radius: 8px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    padding: 1rem;
+
+    h1 {
+      margin-top: 0; // Override the default 2em margin
+      margin-bottom: 1rem;
+      font-size: 1.2rem;
+
+      &::before {
+        color: var(--primary-color);
+        content: "# ";
+      }
+    }
+  }
+}
+
+#searchBar {
+  position: relative;
+  display: flex;
+  align-items: center;
+  padding: 1rem;
+
+  // Clear button styling
+  .clear-button {
+    position: absolute;
+    right: 1.5rem;
+    background: none;
+    border: none;
+    padding: 4px;
+    cursor: pointer;
+    display: none; // Initially hidden, shown via JS when input has text
+    width: 24px;
+    height: 24px;
+
+    svg {
+      width: 100%;
+      height: 100%;
+      fill: var(--text-1); // Use your theme text color
+    }
+
+    &:hover {
+      svg {
+        fill: var(--primary-color);
+      }
+    }
+  }
+
+  // Make sure input accommodates the clear button
+  #searchInput {
+    padding-right: 2.5rem; // Give space for the clear button
+  }
+}
+
+// Mobile adjustments
+@media only screen and (max-width: 600px) {
+  .search-modal {
+    #modal-content {
+      margin: 4% auto;
+      width: 92%;
+    }
+
+    #results {
+      max-height: 70vh;
+    }
+  }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_table.scss b/blog-src/themes/apollo/sass/parts/_table.scss
new file mode 100644
index 000000000..d380dc595
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_table.scss
@@ -0,0 +1,15 @@
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+}
+
+table th {
+  padding: 6px 13px;
+  border: 1px solid #dfe2e5;
+  font-size: large;
+}
+
+table td {
+  padding: 6px 13px;
+  border: 1px solid #dfe2e5;
+}
diff --git a/blog-src/themes/apollo/sass/parts/_tags.scss b/blog-src/themes/apollo/sass/parts/_tags.scss
new file mode 100644
index 000000000..c249bcb94
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_tags.scss
@@ -0,0 +1,26 @@
+.tags {
+  ul {
+    margin-left: 0;
+    padding-left: 0;
+  }
+
+  li {
+    list-style-type: none;
+  }
+
+  a {
+    border-bottom: 3px solid var(--primary-color);
+    font-family: var(--text-font);
+  }
+
+  a:hover {
+    color: var(--hover-color);
+    background-color: var(--primary-color);
+  }
+
+  a::before {
+    content: "🏷 "; /* Ensure there's a space after the emoji for spacing */
+    display: inline;
+    white-space: nowrap !important;
+  }
+}
diff --git a/blog-src/themes/apollo/sass/parts/_toc.scss b/blog-src/themes/apollo/sass/parts/_toc.scss
new file mode 100644
index 000000000..978d85abe
--- /dev/null
+++ b/blog-src/themes/apollo/sass/parts/_toc.scss
@@ -0,0 +1,24 @@
+.toc-container {
+  .toc-title {
+    cursor: pointer;
+    position: relative;
+    padding-left: 20px; // Space for the arrow
+
+    &:before {
+      content: "▼"; // Down arrow
+      position: absolute;
+      left: 0;
+      transition: transform 0.3s ease; // Smooth transformation
+    }
+
+    &:hover:before,
+    &.expanded:before {
+      transform: rotate(180deg); // Arrow points up when expanded or on hover
+    }
+  }
+
+  .toc-list {
+    display: none; // ToC is hidden by default
+    // Removed transition on display, not effective
+  }
+}
diff --git a/blog-src/themes/apollo/sass/theme/dark.scss b/blog-src/themes/apollo/sass/theme/dark.scss
new file mode 100644
index 000000000..3fe608ef9
--- /dev/null
+++ b/blog-src/themes/apollo/sass/theme/dark.scss
@@ -0,0 +1,26 @@
+$bg-0: #121212;
+
+// Dark theme needs a bigger contrast compared to the white theme.
+$bg-1: lighten($bg-0, 5%);
+$bg-2: lighten($bg-1, 10%);
+
+$text-0: lighten($bg-0, 87%);
+$text-1: lighten($bg-0, 60%);
+
+:root.dark {
+  --text-0: #{$text-0};
+  --text-1: #{$text-1};
+
+  --bg-0: #{$bg-0};
+  --bg-1: #{$bg-1};
+  --bg-2: #{$bg-2};
+
+  --border-color: var(--bg-2);
+
+  --primary-color: #ef5350;
+  --hover-color: white;
+
+  .social > img {
+    filter: invert(1);
+  }
+}
diff --git a/blog-src/themes/apollo/sass/theme/light.scss b/blog-src/themes/apollo/sass/theme/light.scss
new file mode 100644
index 000000000..f82437a33
--- /dev/null
+++ b/blog-src/themes/apollo/sass/theme/light.scss
@@ -0,0 +1,25 @@
+$bg-0: #fff;
+$bg-1: darken($bg-0, 2%);
+$bg-2: darken($bg-1, 8%);
+
+$text-0: darken($bg-0, 87%);
+$text-1: darken($bg-0, 60%);
+
+:root.light {
+  --text-0: #{$text-0};
+  --text-1: #{$text-1};
+
+  --bg-0: #{$bg-0};
+  --bg-1: #{$bg-1};
+  --bg-2: #{$bg-2};
+
+  --border-color: var(--bg-2);
+
+  --primary-color: #ef5350;
+  --hover-color: white;
+
+  // invert colour on hover for consistency
+  .social :hover {
+    filter: invert(1);
+  }
+}
diff --git a/blog-src/themes/apollo/screenshot-dark.png b/blog-src/themes/apollo/screenshot-dark.png
new file mode 100644
index 000000000..aa1af5be8
Binary files /dev/null and b/blog-src/themes/apollo/screenshot-dark.png differ
diff --git a/blog-src/themes/apollo/screenshot.png b/blog-src/themes/apollo/screenshot.png
new file mode 100644
index 000000000..914524433
Binary files /dev/null and b/blog-src/themes/apollo/screenshot.png differ
diff --git a/blog-src/themes/apollo/shell.nix b/blog-src/themes/apollo/shell.nix
new file mode 100644
index 000000000..d95549826
--- /dev/null
+++ b/blog-src/themes/apollo/shell.nix
@@ -0,0 +1,12 @@
+{pkgs ? import <nixpkgs> {}}:
+pkgs.mkShell {
+  nativeBuildInputs = with pkgs; [
+    zola
+
+    # Formatters
+    treefmt
+    nodePackages.prettier
+    alejandra
+    djlint
+  ];
+}
diff --git a/blog-src/themes/apollo/shortcode.md b/blog-src/themes/apollo/shortcode.md
new file mode 100644
index 000000000..1b630e788
--- /dev/null
+++ b/blog-src/themes/apollo/shortcode.md
@@ -0,0 +1,81 @@
++++
+title = "Shortcode Example"
+date = "2024-06-14"
+
+[taxonomies]
+tags=["example"]
+
+[extra]
+comment = true
++++
+
+
+## Note
+
+Here is an example of the `note` shortcode:
+
+This one is static!
+{{ note(header="Note!", body="This blog assumes basic terminal maturity") }}
+
+This one is clickable!
+{{ note(clickable=true, hidden = true, header="Quiz!", body="The answer to the quiz!") }}
+
+
+Syntax:
+```
+{{/* note(header="Note!", body="This blog assumes basic terminal maturity") */}}
+{{/* note(clickable=true, hidden = true, header="Quiz!", body="The answer to the quiz!") */}}
+```
+
+You can also use some HTML in the text:
+{{ note(header="Note!", body="<h1>This blog assumes basic terminal maturity</h1>") }}
+
+
+Literal shortcode:
+```
+{{/* note(header="Note!", body="<h1>This blog assumes basic terminal maturity</h1>") */}}
+```
+
+Pretty cool, right?
+
+Finally, you can do something like this (hopefully):
+
+{% note(clickable=true, header="Quiz!") %}
+
+# Hello this is markdown inside a note shortcode
+
+```rust
+fn main() {
+    println!("Hello World");
+}
+```
+
+We can't call another shortcode inside a shortcode, but this is good enough.
+
+{% end %}
+
+Here is the raw markdown:
+
+```markdown
+{{/* note(clickable=true, header="Quiz!") */}}
+
+# Hello this is markdown inside a note shortcode
+
+\`\`\`rust
+fn main() {
+    println!("Hello World");
+}
+\`\`\`
+
+We can't call another shortcode inside a shortcode, but this is good enough.
+
+{{/* end */}}
+```
+
+Finally, we have center
+{{ note(center=true, header="Centered Text", body="This is centered text") }}
+
+```markdown
+{{/* note(center=true, header="Centered Text", body="This is centered text") */}}
+```
+It works good enough for me!
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Bold.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Bold.ttf
new file mode 100644
index 000000000..710c34b84
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Bold.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-BoldItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-BoldItalic.ttf
new file mode 100644
index 000000000..fdff00fae
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-BoldItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBold.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBold.ttf
new file mode 100644
index 000000000..eb9430098
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBold.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBoldItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBoldItalic.ttf
new file mode 100644
index 000000000..b70b4e7dc
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraBoldItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLight.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLight.ttf
new file mode 100644
index 000000000..74efced57
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLight.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLightItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLightItalic.ttf
new file mode 100644
index 000000000..1a9d2d3d6
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ExtraLightItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Italic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Italic.ttf
new file mode 100644
index 000000000..ffd5d778d
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Italic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Light.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Light.ttf
new file mode 100644
index 000000000..c0682f9c0
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Light.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-LightItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-LightItalic.ttf
new file mode 100644
index 000000000..f20bac981
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-LightItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Medium.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Medium.ttf
new file mode 100644
index 000000000..17ff94562
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Medium.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-MediumItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-MediumItalic.ttf
new file mode 100644
index 000000000..9b699bb34
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-MediumItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Regular.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Regular.ttf
new file mode 100644
index 000000000..9a5202eb1
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Regular.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBold.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBold.ttf
new file mode 100644
index 000000000..84b779558
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBold.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBoldItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBoldItalic.ttf
new file mode 100644
index 000000000..ffa1f39a7
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-SemiBoldItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Thin.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Thin.ttf
new file mode 100644
index 000000000..1317bfee0
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-Thin.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ThinItalic.ttf b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ThinItalic.ttf
new file mode 100644
index 000000000..bc7243998
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/JetbrainsMono/JetBrainsMono-ThinItalic.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Bold.ttf b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Bold.ttf
new file mode 100644
index 000000000..869a60f04
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Bold.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Light.ttf b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Light.ttf
new file mode 100644
index 000000000..76a195f12
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Light.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Medium.ttf b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Medium.ttf
new file mode 100644
index 000000000..667905f40
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Medium.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Regular.ttf b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Regular.ttf
new file mode 100644
index 000000000..792fe1b39
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-Regular.ttf differ
diff --git a/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-SemiBold.ttf b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-SemiBold.ttf
new file mode 100644
index 000000000..021930261
Binary files /dev/null and b/blog-src/themes/apollo/static/fonts/SpaceGrotesk/SpaceGrotesk-SemiBold.ttf differ
diff --git a/blog-src/themes/apollo/static/icons/moon.svg b/blog-src/themes/apollo/static/icons/moon.svg
new file mode 100644
index 000000000..dbf7c6cf5
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/moon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/search.svg b/blog-src/themes/apollo/static/icons/search.svg
new file mode 100644
index 000000000..8710306dd
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/search.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/LICENSE b/blog-src/themes/apollo/static/icons/social/LICENSE
new file mode 100644
index 000000000..993facc0b
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/LICENSE
@@ -0,0 +1 @@
+All icons in this directory are downloaded from [FontAwesome](https://fontawesome.com/). They are part of the [free offer](https://fontawesome.com/license/free) and are licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/apple.svg b/blog-src/themes/apollo/static/icons/social/apple.svg
new file mode 100644
index 000000000..d0532d53b
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/apple.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 4 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/bitcoin.svg b/blog-src/themes/apollo/static/icons/social/bitcoin.svg
new file mode 100644
index 000000000..941d9b087
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/bitcoin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zm-141.651-35.33c4.937-32.999-20.191-50.739-54.55-62.573l11.146-44.702-27.213-6.781-10.851 43.524c-7.154-1.783-14.502-3.464-21.803-5.13l10.929-43.81-27.198-6.781-1 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/bluesky.svg b/blog-src/themes/apollo/static/icons/social/bluesky.svg
new file mode 100644
index 000000000..6d555f43e
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/bluesky.svg
@@ -0,0 +1,3 @@
+<svg width="568" height="501" viewBox="0 0 568 501" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M123.121 33.6637C188.241 82.5526 258.281 181.681 284 234.873C309.719 181.681 379.759 82.5526 444.879 33.6637C491.866 -1.61183 568 -28.9064 568 57.9464C568 75.2916 558.055 203.659 552.222 224.501C531.947 296.954 458.067 315.434 392.347 304.249C507.222 323.8 536.444 388.56 473.333 453.32C353.473 576.312 301.061 422.461 287.631 383.039C285.169 375.812 284.017 372.431 284 375.306C283.983 372.431 282.831 375.812 280.369 383.039C266.939 422.461 214.527 576.312 94.6667 453.32C31.5556 3 [...]
+</svg>
diff --git a/blog-src/themes/apollo/static/icons/social/deviantart.svg b/blog-src/themes/apollo/static/icons/social/deviantart.svg
new file mode 100644
index 000000000..7dbd0b627
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/deviantart.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M320 93.2l-98.2 179.1 7.4 9.5H320v127.7H159.1l-13.5 9.2-43.7 84c-.3 0-8.6 8.6-9.2 9.2H0v-93.2l93.2-179.4-7.4-9.2H0V102.5h156l13.5-9.2 43.7-84c.3 0 8.6-8.6 9.2-9.2H320v93.1z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/diaspora.svg b/blog-src/themes/apollo/static/icons/social/diaspora.svg
new file mode 100644
index 000000000..55527b53e
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/diaspora.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M251.64 354.55c-1.4 0-88 119.9-88.7 119.9S76.34 414 76 413.25s86.6-125.7 86.6-127.4c0-2.2-129.6-44-137.6-47.1-1.3-.5 31.4-101.8 31.7-102.1.6-.7 144.4 47 145.5 47 .4 0 .9-.6 1-1.3.4-2 1-148.6 1.7-149.6.8-1.2 104.5-.7 105.1-.3 1.5 1 3.5 156.1 6.1 156.1 1. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/discord.svg b/blog-src/themes/apollo/static/icons/social/discord.svg
new file mode 100644
index 000000000..f0dfeab51
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/discord.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.08 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/discourse.svg b/blog-src/themes/apollo/static/icons/social/discourse.svg
new file mode 100644
index 000000000..343bea650
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/discourse.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M225.9 32C103.3 32 0 130.5 0 252.1 0 256 .1 480 .1 480l225.8-.2c122.7 0 222.1-102.3 222.1-223.9C448 134.3 348.6 32 225.9 32zM224 384c-19.4 0-37.9-4.3-54.4-12.1L88.5 392l22.9-75c-9.8-18.1-15.4-38.9-15.4-61 0-70.7 57.3-128 128-128s128 57.3 128 128-57.3 12 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/email.svg b/blog-src/themes/apollo/static/icons/social/email.svg
new file mode 100644
index 000000000..85245e206
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/email.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/ethereum.svg b/blog-src/themes/apollo/static/icons/social/ethereum.svg
new file mode 100644
index 000000000..af202de35
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/ethereum.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M311.9 260.8L160 353.6 8 260.8 160 0l151.9 260.8zM160 383.4L8 290.6 160 512l152-221.4-152 92.8z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/etsy.svg b/blog-src/themes/apollo/static/icons/social/etsy.svg
new file mode 100644
index 000000000..ebc040aad
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/etsy.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M384 348c-1.75 10.75-13.75 110-15.5 132-117.879-4.299-219.895-4.743-368.5 0v-25.5c45.457-8.948 60.627-8.019 61-35.25 1.793-72.322 3.524-244.143 0-322-1.029-28.46-12.13-26.765-61-36v-25.5c73.886 2.358 255.933 8.551 362.999-3.75-3.5 38.25-7.75 126.5-7.75  [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/facebook.svg b/blog-src/themes/apollo/static/icons/social/facebook.svg
new file mode 100644
index 000000000..0afaf7a25
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/facebook.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/fediverse.svg b/blog-src/themes/apollo/static/icons/social/fediverse.svg
new file mode 100644
index 000000000..30e05d3f7
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/fediverse.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="742.753" height="742.753" viewBox="0 0 196.52 196.52"><path fill="#000000" d="M47.924 72.797a18.23 18.23 0 0 1-7.796 7.76l42.799 42.965 10.318-5.23zm56.453 56.67-10.319 5.23 21.686 21.77a18.23 18.23 0 0 1 7.798-7.76z"/><path fill="#000000" d="m129.665 102.077 1.786 11.427 27.415-13.895a18.23 18.23 0 0 1-4.972-9.812zm-14.066 7.128-57.29 29.034a18.23 18.23 0 0 1 4.973 9.813l54.103-27.42z"/><path fill="#000000" d="m69.531 91.654 8.162 8.193 29. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/github.svg b/blog-src/themes/apollo/static/icons/social/github.svg
new file mode 100644
index 000000000..e32807ad9
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/github.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/gitlab.svg b/blog-src/themes/apollo/static/icons/social/gitlab.svg
new file mode 100644
index 000000000..b577d3f38
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/gitlab.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M503.5 204.6L502.8 202.8L433.1 21.02C431.7 17.45 429.2 14.43 425.9 12.38C423.5 10.83 420.8 9.865 417.9 9.57C415 9.275 412.2 9.653 409.5 10.68C406.8 11.7 404.4 13.34 402.4 15.46C400.5 17.58 399.1 20.13 398.3 22.9L351.3 166.9H160.8L113.7 22.9C112.9 20.13  [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/google-scholar.svg b/blog-src/themes/apollo/static/icons/social/google-scholar.svg
new file mode 100644
index 000000000..578789d35
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/google-scholar.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 57.1 63.8" style="enable-background:new 0 0 57.1 63.8;" xml:space="preserve">
+<g>
+	<path d="M53.8,7.1c0.7,0.1,1,0.7,1,1.7v21.4c0,0.3-0.1,0.6-0.2,0.9c-0.2,0.3-0.4,0.5-0.7,0.7C53.6,32,53.3,32,53,32h-0.7
+		c-0.2,0-0.5-0.1-0.7-0.2c-0.3-0.2-0.5-0.4-0.7-0.7c-0.2-0.3-0.2-0.6-0.2-0.9V8.8c0-1.1,0.3-1.7,1-1.7V4.3L42.2,12
+		c0.1,0.1,0.2,0.2,0.2,0.3l0.2,0.2c0.9,1.6,1.3,3.4,1.3,5.6c0,1.7-0.3,3.2-0.9,4.6c-0.6,1.4-1.3,2.5-2.1,3.3
+		c-0.8,0.8-1.6,1.6-2.4,2.3c-0.8,0.7-1.6,1.5-2.2,2.2c-0.6,0.8-0.9,1.6-0.9,2.3c0,0.8,0.4,1.6,1.2,2.4c0.8,0.8,1.7,1.7,2.8,2.6
+		c1,0.8,2,1.7,3.2,2.7c1.1,0.9,2,2.1,2.7,3.7c0.7,1.6,1.1,3.2,1.1,5c0,2.4-0.6,4.6-1.8,6.6c-1.2,1.9-2.8,3.5-4.8,4.6s-4.1,2-6.3,2.6
+		c-2.2,0.6-4.4,0.8-6.6,0.8c-1.4,0-2.9-0.1-4.3-0.3c-1.4-0.2-2.9-0.6-4.3-1.2c-1.4-0.6-2.7-1.2-3.8-2.1c-1.1-0.8-2-1.9-2.7-3.2
+		c-0.7-1.3-1.1-2.7-1.1-4.4c0-1.9,0.5-3.6,1.6-5.2c1.1-1.6,2.5-3,4.2-4.1c3.1-1.9,7.9-3,14.3-3.5c-1.4-1.9-2.2-3.7-2.2-5.3
+		c0-0.9,0.2-1.9,0.7-3c-0.8,0.1-1.6,0.2-2.3,0.2c-3.5,0-6.5-1.1-8.9-3.4c-2.4-2.3-3.6-5.2-3.6-8.7c0-0.3,0-0.7,0-1H0L21.6,0h35.5
+		l-3.3,2.5V7.1z M37.2,45c-0.7-0.6-1.1-0.9-1.2-1c-0.2-0.1-0.6-0.4-1.4-1c-0.8-0.6-1.2-0.9-1.3-1c-0.4-0.1-1-0.2-1.7-0.2
+		c-1.7,0-3.3,0.2-4.8,0.5c-1.6,0.3-3.1,0.8-4.6,1.4c-1.5,0.6-2.7,1.6-3.7,2.8c-0.9,1.3-1.4,2.8-1.4,4.6c0,1.6,0.4,3,1.2,4.2
+		c0.8,1.3,1.9,2.3,3.2,3c1.3,0.7,2.7,1.2,4.2,1.6c1.5,0.4,3,0.7,4.5,0.7c3.1,0,5.7-0.7,7.9-2.1c2.2-1.4,3.2-3.5,3.2-6.4
+		c0-0.6-0.1-1.1-0.2-1.7c-0.2-0.6-0.3-1.1-0.5-1.5c-0.2-0.4-0.5-0.9-0.9-1.5c-0.4-0.4-0.8-0.8-1.2-1.2C38.3,46,37.9,45.5,37.2,45z
+		 M35.4,27.3c1.2-1.5,1.8-3.3,1.8-5.6c0-1.9-0.3-3.9-1-6c-0.7-2.1-1.7-4-3.2-5.6s-3.2-2.5-5.2-2.5c-2.2,0-3.9,0.8-5.1,2.4
+		c-1.2,1.6-1.7,3.5-1.7,5.7c0,2,0.3,4,0.9,6c0.6,2,1.7,3.8,3.2,5.4c1.5,1.6,3.2,2.4,5.2,2.4C32.4,29.6,34.1,28.8,35.4,27.3z"/>
+</g>
+</svg>
diff --git a/blog-src/themes/apollo/static/icons/social/google.svg b/blog-src/themes/apollo/static/icons/social/google.svg
new file mode 100644
index 000000000..b3776b0f5
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/google.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 488 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/hacker-news.svg b/blog-src/themes/apollo/static/icons/social/hacker-news.svg
new file mode 100644
index 000000000..23e3980f9
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/hacker-news.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M0 32v448h448V32H0zm21.2 197.2H21c.1-.1.2-.3.3-.4 0 .1 0 .3-.1.4zm218 53.9V384h-31.4V281.3L128 128h37.3c52.5 98.3 49.2 101.2 59.3 125.6 12.3-27 5.8-24.4 60.6-125.6H320l-80.8 155.1z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/instagram.svg b/blog-src/themes/apollo/static/icons/social/instagram.svg
new file mode 100644
index 000000000..89f63c404
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/instagram.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-1 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/linkedin.svg b/blog-src/themes/apollo/static/icons/social/linkedin.svg
new file mode 100644
index 000000000..d54fcf594
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/linkedin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5- [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/mastodon.svg b/blog-src/themes/apollo/static/icons/social/mastodon.svg
new file mode 100644
index 000000000..5e12f81ca
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/mastodon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M433 179.11c0-97.2-63.71-125.7-63.71-125.7-62.52-28.7-228.56-28.4-290.48 0 0 0-63.72 28.5-63.72 125.7 0 115.7-6.6 259.4 105.63 289.1 40.51 10.7 75.32 13 103.33 11.4 50.81-2.8 79.32-18.1 79.32-18.1l-1.7-36.9s-36.31 11.4-77.12 10.1c-40.41-1.4-83-4.4-89.63 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/matrix.svg b/blog-src/themes/apollo/static/icons/social/matrix.svg
new file mode 100644
index 000000000..bc41720a2
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/matrix.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 520 520" style="enable-background:new 0 0 520 520;" xml:space="preserve">
+<path d="M13.7,11.9v496.2h35.7V520H0V0h49.4v11.9H13.7z"/>
+<path d="M166.3,169.2v25.1h0.7c6.7-9.6,14.8-17,24.2-22.2c9.4-5.3,20.3-7.9,32.5-7.9c11.7,0,22.4,2.3,32.1,6.8  c9.7,4.5,17,12.6,22.1,24c5.5-8.1,13-15.3,22.4-21.5c9.4-6.2,20.6-9.3,33.5-9.3c9.8,0,18.9,1.2,27.3,3.6c8.4,2.4,15.5,6.2,21.5,11.5  c6,5.3,10.6,12.1,14,20.6c3.3,8.5,5,18.7,5,30.7v124.1h-50.9V249.6c0-6.2-0.2-12.1-0.7-17.6c-0.5-5.5-1.8-10.3-3.9-14.3  c-2.2-4.1-5.3-7.3-9.5-9.7c-4.2-2.4-9.9-3.6-17-3.6c-7.2,0-13,1.4-17.4,4.1c-4.4,2.8-7.9,6.3-10.4,10.8c-2.5,4.4-4.2,9.4-5,15.1  c-0.8,5.6-1. [...]
+<path d="M506.3,508.1V11.9h-35.7V0H520v520h-49.4v-11.9H506.3z"/>
+</svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/orcid.svg b/blog-src/themes/apollo/static/icons/social/orcid.svg
new file mode 100644
index 000000000..e80808089
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/orcid.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FFFFFF;}
+</style>
+<path d="M256,128c0,70.7-57.3,128-128,128S0,198.7,0,128S57.3,0,128,0S256,57.3,256,128z"/>
+<g>
+	<path class="st0" d="M86.3,186.2H70.9V79.1h15.4v48.4V186.2z"/>
+	<path class="st0" d="M108.9,79.1h41.6c39.6,0,57,28.3,57,53.6c0,27.5-21.5,53.6-56.8,53.6h-41.8C108.9,186.3,108.9,79.1,108.9,79.1
+		z M124.3,172.4h24.5c34.9,0,42.9-26.5,42.9-39.7C191.7,111.2,178,93,148,93h-23.7L124.3,172.4L124.3,172.4z"/>
+	<path class="st0" d="M88.7,56.8c0,5.5-4.5,10.1-10.1,10.1s-10.1-4.6-10.1-10.1c0-5.6,4.5-10.1,10.1-10.1
+		C84.2,46.7,88.7,51.3,88.7,56.8z"/>
+</g>
+</svg>
diff --git a/blog-src/themes/apollo/static/icons/social/paypal.svg b/blog-src/themes/apollo/static/icons/social/paypal.svg
new file mode 100644
index 000000000..efdc81af0
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/paypal.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M111.4 295.9c-3.5 19.2-17.4 108.7-21.5 134-.3 1.8-1 2.5-3 2.5H12.3c-7.6 0-13.1-6.6-12.1-13.9L58.8 46.6c1.5-9.6 10.1-16.9 20-16.9 152.3 0 165.1-3.7 204 11.4 60.1 23.3 65.6 79.5 44 140.3-21.5 62.6-72.5 89.5-140.1 90.3-43.4.7-69.5-7-75.3 24.2zM357.1 152c-1 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/pinterest.svg b/blog-src/themes/apollo/static/icons/social/pinterest.svg
new file mode 100644
index 000000000..eb977c2b8
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/pinterest.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M496 256c0 137-111 248-248 248-25.6 0-50.2-3.9-73.4-11.1 10.1-16.5 25.2-43.5 30.8-65 3-11.6 15.4-59 15.4-59 8.1 15.4 31.7 28.5 56.8 28.5 74.8 0 128.7-68.8 128.7-154.3 0-81.9-66.9-143.2-152.9-143.2-107 0-163.9 71.8-163.9 150.1 0 36.4 19.4 81.7 50.3 96.1  [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/quora.svg b/blog-src/themes/apollo/static/icons/social/quora.svg
new file mode 100644
index 000000000..375d3027a
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/quora.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M440.5 386.7h-29.3c-1.5 13.5-10.5 30.8-33 30.8-20.5 0-35.3-14.2-49.5-35.8 44.2-34.2 74.7-87.5 74.7-153C403.5 111.2 306.8 32 205 32 105.3 32 7.3 111.7 7.3 228.7c0 134.1 131.3 221.6 249 189C276 451.3 302 480 351.5 480c81.8 0 90.8-75.3 89-93.3zM297 329.2C2 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/reddit.svg b/blog-src/themes/apollo/static/icons/social/reddit.svg
new file mode 100644
index 000000000..a8a3a96e7
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/reddit.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M201.5 305.5c-13.8 0-24.9-11.1-24.9-24.6 0-13.8 11.1-24.9 24.9-24.9 13.6 0 24.6 11.1 24.6 24.9 0 13.6-11.1 24.6-24.6 24.6zM504 256c0 137-111 248-248 248S8 393 8 256 119 8 256 8s248 111 248 248zm-132.3-41.2c-9.4 0-17.7 3.9-23.8 10-22.4-15.5-52.6-25.5-86. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/rss.svg b/blog-src/themes/apollo/static/icons/social/rss.svg
new file mode 100644
index 000000000..b86288616
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/rss.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M0 64C0 46.3 14.3 32 32 32c229.8 0 416 186.2 416 416c0 17.7-14.3 32-32 32s-32-14.3-32-32C384 253.6 226.4 96 32 96C14.3 96 0 81.7 0 64zM128 416c0 35.3-28.7 64-64 64s-64-28.7-64-64s28.7-64 64-64s64 28.7 64 64zM32 160c159.1 0 288 128.9 288 288c0 17.7-14.3  [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/skype.svg b/blog-src/themes/apollo/static/icons/social/skype.svg
new file mode 100644
index 000000000..3369aba68
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/skype.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M424.7 299.8c2.9-14 4.7-28.9 4.7-43.8 0-113.5-91.9-205.3-205.3-205.3-14.9 0-29.7 1.7-43.8 4.7C161.3 40.7 137.7 32 112 32 50.2 32 0 82.2 0 144c0 25.7 8.7 49.3 23.3 68.2-2.9 14-4.7 28.9-4.7 43.8 0 113.5 91.9 205.3 205.3 205.3 14.9 0 29.7-1.7 43.8-4.7 19 1 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/slack.svg b/blog-src/themes/apollo/static/icons/social/slack.svg
new file mode 100644
index 000000000..0dbc26d18
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/slack.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M94.12 315.1c0 25.9-21.16 47.06-47.06 47.06S0 341 0 315.1c0-25.9 21.16-47.06 47.06-47.06h47.06v47.06zm23.72 0c0-25.9 21.16-47.06 47.06-47.06s47.06 21.16 47.06 47.06v117.84c0 25.9-21.16 47.06-47.06 47.06s-47.06-21.16-47.06-47.06V315.1zm47.06-188.98c-25.9 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/snapchat.svg b/blog-src/themes/apollo/static/icons/social/snapchat.svg
new file mode 100644
index 000000000..2cd79ddd8
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/snapchat.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M384,32H64A64,64,0,0,0,0,96V416a64,64,0,0,0,64,64H384a64,64,0,0,0,64-64V96A64,64,0,0,0,384,32Zm-3.907,319.309-.083.1a32.364,32.364,0,0,1-8.717,6.823,90.26,90.26,0,0,1-20.586,8.2,12.694,12.694,0,0,0-3.852,1.76c-2.158,1.909-2.1,4.64-4.4,8.55a23.137,23.137 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/soundcloud.svg b/blog-src/themes/apollo/static/icons/social/soundcloud.svg
new file mode 100644
index 000000000..4724d740a
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/soundcloud.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M111.4 256.3l5.8 65-5.8 68.3c-.3 2.5-2.2 4.4-4.4 4.4s-4.2-1.9-4.2-4.4l-5.6-68.3 5.6-65c0-2.2 1.9-4.2 4.2-4.2 2.2 0 4.1 2 4.4 4.2zm21.4-45.6c-2.8 0-4.7 2.2-5 5l-5 105.6 5 68.3c.3 2.8 2.2 5 5 5 2.5 0 4.7-2.2 4.7-5l5.8-68.3-5.8-105.6c0-2.8-2.2-5-4.7-5zm25. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/spotify.svg b/blog-src/themes/apollo/static/icons/social/spotify.svg
new file mode 100644
index 000000000..1d393ba38
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/spotify.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/stack-exchange.svg b/blog-src/themes/apollo/static/icons/social/stack-exchange.svg
new file mode 100644
index 000000000..0a3177f50
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/stack-exchange.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M17.7 332.3h412.7v22c0 37.7-29.3 68-65.3 68h-19L259.3 512v-89.7H83c-36 0-65.3-30.3-65.3-68v-22zm0-23.6h412.7v-85H17.7v85zm0-109.4h412.7v-85H17.7v85zM365 0H83C47 0 17.7 30.3 17.7 67.7V90h412.7V67.7C430.3 30.3 401 0 365 0z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/stack-overflow.svg b/blog-src/themes/apollo/static/icons/social/stack-overflow.svg
new file mode 100644
index 000000000..2ca50c7bd
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/stack-overflow.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M290.7 311L95 269.7 86.8 309l195.7 41zm51-87L188.2 95.7l-25.5 30.8 153.5 128.3zm-31.2 39.7L129.2 179l-16.7 36.5L293.7 300zM262 32l-32 24 119.3 160.3 32-24zm20.5 328h-200v39.7h200zm39.7 80H42.7V320h-40v160h359.5V320h-40z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/steam.svg b/blog-src/themes/apollo/static/icons/social/steam.svg
new file mode 100644
index 000000000..b61f37423
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/steam.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M496 256c0 137-111.2 248-248.4 248-113.8 0-209.6-76.3-239-180.4l95.2 39.3c6.4 32.1 34.9 56.4 68.9 56.4 39.2 0 71.9-32.4 70.2-73.5l84.5-60.2c52.1 1.3 95.8-40.9 95.8-93.5 0-51.6-42-93.5-93.7-93.5s-93.7 42-93.7 93.5v1.2L176.6 279c-15.5-.9-30.7 3.4-43.5 12. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/telegram.svg b/blog-src/themes/apollo/static/icons/social/telegram.svg
new file mode 100644
index 000000000..02f48c060
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/telegram.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M248,8C111.033,8,0,119.033,0,256S111.033,504,248,504,496,392.967,496,256,384.967,8,248,8ZM362.952,176.66c-3.732,39.215-19.881,134.378-28.1,178.3-3.476,18.584-10.322,24.816-16.948,25.425-14.4,1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-5 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/twitter.svg b/blog-src/themes/apollo/static/icons/social/twitter.svg
new file mode 100644
index 000000000..0778f7252
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/twitter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/vimeo.svg b/blog-src/themes/apollo/static/icons/social/vimeo.svg
new file mode 100644
index 000000000..d98368e1b
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/vimeo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M403.2 32H44.8C20.1 32 0 52.1 0 76.8v358.4C0 459.9 20.1 480 44.8 480h358.4c24.7 0 44.8-20.1 44.8-44.8V76.8c0-24.7-20.1-44.8-44.8-44.8zM377 180.8c-1.4 31.5-23.4 74.7-66 129.4-44 57.2-81.3 85.8-111.7 85.8-18.9 0-34.8-17.4-47.9-52.3-25.5-93.3-36.4-148-57.4 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/whatsapp.svg b/blog-src/themes/apollo/static/icons/social/whatsapp.svg
new file mode 100644
index 000000000..d25914255
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/whatsapp.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M380.9 97.1C339 55.1 283.2 32 223.9 32c-122.4 0-222 99.6-222 222 0 39.1 10.2 77.3 29.6 111L0 480l117.7-30.9c32.4 17.7 68.9 27 106.1 27h.1c122.3 0 224.1-99.6 224.1-222 0-59.3-25.2-115-67.1-157zm-157 341.6c-33.2 0-65.7-8.9-94-25.7l-6.7-4-69.8 18.3L72 359. [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/x-twitter.svg b/blog-src/themes/apollo/static/icons/social/x-twitter.svg
new file mode 100644
index 000000000..f5feed78f
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/x-twitter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/social/youtube.svg b/blog-src/themes/apollo/static/icons/social/youtube.svg
new file mode 100644
index 000000000..287dca293
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/social/youtube.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.82 [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/icons/sun.svg b/blog-src/themes/apollo/static/icons/sun.svg
new file mode 100644
index 000000000..7f51b94d1
--- /dev/null
+++ b/blog-src/themes/apollo/static/icons/sun.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><li [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/js/codeblock.js b/blog-src/themes/apollo/static/js/codeblock.js
new file mode 100644
index 000000000..def68bcc9
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/codeblock.js
@@ -0,0 +1,103 @@
+const successIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-check-lg" viewBox="0 0 16 16">
+            <path d="M13.485 1.85a.5.5 0 0 1 1.065.02.75.75 0 0 1-.02 1.065L5.82 12.78a.75.75 0 0 1-1.106.02L1.476 9.346a.75.75 0 1 1 1.05-1.07l2.74 2.742L12.44 2.92a.75.75 0 0 1 1.045-.07z"/>
+        </svg>`;
+const errorIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-x-lg" viewBox="0 0 16 16">
+            <path d="M2.293 2.293a1 1 0 0 1 1.414 0L8 6.586l4.293-4.293a1 1 0 0 1 1.414 1.414L9.414 8l4.293 4.293a1 1 0 0 1-1.414 1.414L8 9.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L6.586 8 2.293 3.707a1 1 0 0 1 0-1.414z"/>
+        </svg>`;
+const copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-clipboard" viewBox="0 0 16 16">
+                <path d="M10 1.5a.5.5 0 0 1 .5-.5h2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h2a.5.5 0 0 1 .5.5V3h3V1.5zM6.5 3V2h3v1h-3zm4 0v1h2a1 1 0 0 0-1-1h-2V3zm-5 0H3a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H5.5V3z"/>
+            </svg>`;
+
+// Function to change icons after copying
+const changeIcon = (button, isSuccess) => {
+    button.innerHTML = isSuccess ? successIcon : errorIcon;
+    setTimeout(() => {
+        button.innerHTML = copyIcon; // Reset to copy icon
+    }, 2000);
+};
+
+// Function to get code text from tables, skipping line numbers
+const getCodeFromTable = (codeBlock) => {
+    return [...codeBlock.querySelectorAll('tr')]
+        .map(row => row.querySelector('td:last-child')?.innerText ?? '')
+        .join('');
+};
+
+// Function to get code text from non-table blocks
+const getNonTableCode = (codeBlock) => {
+    return codeBlock.textContent.trim();
+};
+
+document.addEventListener('DOMContentLoaded', function () {
+    // Select all `pre` elements containing `code`
+
+    const observer = new IntersectionObserver((entries) => {
+        entries.forEach(entry => {
+            const pre = entry.target.parentNode;
+            const clipboardBtn = pre.querySelector('.clipboard-button');
+            const label = pre.querySelector('.code-label');
+
+            if (clipboardBtn) {
+                // Adjust the position of the clipboard button when the `code` is not fully visible
+                clipboardBtn.style.right = entry.isIntersecting ? '5px' : `-${entry.boundingClientRect.right - pre.clientWidth + 5}px`;
+            }
+
+            if (label) {
+                // Adjust the position of the label similarly
+                label.style.right = entry.isIntersecting ? '0px' : `-${entry.boundingClientRect.right - pre.clientWidth}px`;
+            }
+        });
+    }, {
+        root: null, // observing relative to viewport
+        rootMargin: '0px',
+        threshold: 1.0 // Adjust this to control when the callback fires
+    });
+
+    document.querySelectorAll('pre code').forEach(codeBlock => {
+        const pre = codeBlock.parentNode;
+        pre.style.position = 'relative'; // Ensure parent `pre` can contain absolute elements
+
+        // Create and append the copy button
+        const copyBtn = document.createElement('button');
+        copyBtn.className = 'clipboard-button';
+        copyBtn.innerHTML = copyIcon;
+        copyBtn.setAttribute('aria-label', 'Copy code to clipboard');
+        pre.appendChild(copyBtn);
+
+        // Attach event listener to copy button
+        copyBtn.addEventListener('click', async () => {
+            // Determine if the code is in a table or not
+            const isTable = codeBlock.querySelector('table');
+            const codeToCopy = isTable ? getCodeFromTable(codeBlock) : getNonTableCode(codeBlock);
+            try {
+                await navigator.clipboard.writeText(codeToCopy);
+                changeIcon(copyBtn, true); // Show success icon
+            } catch (error) {
+                console.error('Failed to copy text: ', error);
+                changeIcon(copyBtn, false); // Show error icon
+            }
+        });
+
+        const langClass = codeBlock.className.match(/language-(\w+)/);
+        const lang = langClass ? langClass[1] : 'default';
+
+        // Create and append the label
+        const label = document.createElement('span');
+        label.className = 'code-label label-' + lang; // Use the specific language class
+        label.textContent = lang.toUpperCase(); // Display the language as label
+        pre.appendChild(label);
+
+        let ticking = false;
+        pre.addEventListener('scroll', () => {
+            if (!ticking) {
+                window.requestAnimationFrame(() => {
+                    copyBtn.style.right = `-${pre.scrollLeft}px`;
+                    label.style.right = `-${pre.scrollLeft}px`;
+                    ticking = false;
+                });
+                ticking = true;
+            }
+        });
+
+    });
+});
diff --git a/blog-src/themes/apollo/static/js/count.js b/blog-src/themes/apollo/static/js/count.js
new file mode 100644
index 000000000..8798d119b
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/count.js
@@ -0,0 +1,271 @@
+// GoatCounter: https://www.goatcounter.com
+// This file (and *only* this file) is released under the ISC license: https://opensource.org/licenses/ISC
+;(function() {
+	'use strict';
+
+	if (window.goatcounter && window.goatcounter.vars)  // Compatibility with very old version; do not use.
+		window.goatcounter = window.goatcounter.vars
+	else
+		window.goatcounter = window.goatcounter || {}
+
+	// Load settings from data-goatcounter-settings.
+	var s = document.querySelector('script[data-goatcounter]')
+	if (s && s.dataset.goatcounterSettings) {
+		try         { var set = JSON.parse(s.dataset.goatcounterSettings) }
+		catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) }
+		for (var k in set)
+			if (['no_onload', 'no_events', 'allow_local', 'allow_frame', 'path', 'title', 'referrer', 'event'].indexOf(k) > -1)
+				window.goatcounter[k] = set[k]
+	}
+
+	var enc = encodeURIComponent
+
+	// Get all data we're going to send off to the counter endpoint.
+	var get_data = function(vars) {
+		var data = {
+			p: (vars.path     === undefined ? goatcounter.path     : vars.path),
+			r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer),
+			t: (vars.title    === undefined ? goatcounter.title    : vars.title),
+			e: !!(vars.event || goatcounter.event),
+			s: [window.screen.width, window.screen.height, (window.devicePixelRatio || 1)],
+			b: is_bot(),
+			q: location.search,
+		}
+
+		var rcb, pcb, tcb  // Save callbacks to apply later.
+		if (typeof(data.r) === 'function') rcb = data.r
+		if (typeof(data.t) === 'function') tcb = data.t
+		if (typeof(data.p) === 'function') pcb = data.p
+
+		if (is_empty(data.r)) data.r = document.referrer
+		if (is_empty(data.t)) data.t = document.title
+		if (is_empty(data.p)) data.p = get_path()
+
+		if (rcb) data.r = rcb(data.r)
+		if (tcb) data.t = tcb(data.t)
+		if (pcb) data.p = pcb(data.p)
+		return data
+	}
+
+	// Check if a value is "empty" for the purpose of get_data().
+	var is_empty = function(v) { return v === null || v === undefined || typeof(v) === 'function' }
+
+	// See if this looks like a bot; there is some additional filtering on the
+	// backend, but these properties can't be fetched from there.
+	var is_bot = function() {
+		// Headless browsers are probably a bot.
+		var w = window, d = document
+		if (w.callPhantom || w._phantom || w.phantom)
+			return 150
+		if (w.__nightmare)
+			return 151
+		if (d.__selenium_unwrapped || d.__webdriver_evaluate || d.__driver_evaluate)
+			return 152
+		if (navigator.webdriver)
+			return 153
+		return 0
+	}
+
+	// Object to urlencoded string, starting with a ?.
+	var urlencode = function(obj) {
+		var p = []
+		for (var k in obj)
+			if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
+				p.push(enc(k) + '=' + enc(obj[k]))
+		return '?' + p.join('&')
+	}
+
+	// Show a warning in the console.
+	var warn = function(msg) {
+		if (console && 'warn' in console)
+			console.warn('goatcounter: ' + msg)
+	}
+
+	// Get the endpoint to send requests to.
+	var get_endpoint = function() {
+		var s = document.querySelector('script[data-goatcounter]')
+		if (s && s.dataset.goatcounter)
+			return s.dataset.goatcounter
+		return (goatcounter.endpoint || window.counter)  // counter is for compat; don't use.
+	}
+
+	// Get current path.
+	var get_path = function() {
+		var loc = location,
+			c = document.querySelector('link[rel="canonical"][href]')
+		if (c) {  // May be relative or point to different domain.
+			var a = document.createElement('a')
+			a.href = c.href
+			if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))
+				loc = a
+		}
+		return (loc.pathname + loc.search) || '/'
+	}
+
+	// Run function after DOM is loaded.
+	var on_load = function(f) {
+		if (document.body === null)
+			document.addEventListener('DOMContentLoaded', function() { f() }, false)
+		else
+			f()
+	}
+
+	// Filter some requests that we (probably) don't want to count.
+	goatcounter.filter = function() {
+		if ('visibilityState' in document && document.visibilityState === 'prerender')
+			return 'visibilityState'
+		if (!goatcounter.allow_frame && location !== parent.location)
+			return 'frame'
+		if (!goatcounter.allow_local && location.hostname.match(/(localhost$|^127\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^0\.0\.0\.0$)/))
+			return 'localhost'
+		if (!goatcounter.allow_local && location.protocol === 'file:')
+			return 'localfile'
+		if (localStorage && localStorage.getItem('skipgc') === 't')
+			return 'disabled with #toggle-goatcounter'
+		return false
+	}
+
+	// Get URL to send to GoatCounter.
+	window.goatcounter.url = function(vars) {
+		var data = get_data(vars || {})
+		if (data.p === null)  // null from user callback.
+			return
+		data.rnd = Math.random().toString(36).substr(2, 5)  // Browsers don't always listen to Cache-Control.
+
+		var endpoint = get_endpoint()
+		if (!endpoint)
+			return warn('no endpoint found')
+
+		return endpoint + urlencode(data)
+	}
+
+	// Count a hit.
+	window.goatcounter.count = function(vars) {
+		var f = goatcounter.filter()
+		if (f)
+			return warn('not counting because of: ' + f)
+		var url = goatcounter.url(vars)
+		if (!url)
+			return warn('not counting because path callback returned null')
+
+		if (!navigator.sendBeacon(url)) {
+			// This mostly fails due to being blocked by CSP; try again with an
+			// image-based fallback.
+			var img = document.createElement('img')
+			img.src = url
+			img.style.position = 'absolute'  // Affect layout less.
+			img.style.bottom = '0px'
+			img.style.width = '1px'
+			img.style.height = '1px'
+			img.loading = 'eager'
+			img.setAttribute('alt', '')
+			img.setAttribute('aria-hidden', 'true')
+
+			var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) }
+			img.addEventListener('load', rm, false)
+			document.body.appendChild(img)
+		}
+	}
+
+	// Get a query parameter.
+	window.goatcounter.get_query = function(name) {
+		var s = location.search.substr(1).split('&')
+		for (var i = 0; i < s.length; i++)
+			if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
+				return s[i].substr(name.length + 1)
+	}
+
+	// Track click events.
+	window.goatcounter.bind_events = function() {
+		if (!document.querySelectorAll)  // Just in case someone uses an ancient browser.
+			return
+
+		var send = function(elem) {
+			return function() {
+				goatcounter.count({
+					event:    true,
+					path:     (elem.dataset.goatcounterClick || elem.name || elem.id || ''),
+					title:    (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),
+					referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),
+				})
+			}
+		}
+
+		Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
+			if (elem.dataset.goatcounterBound)
+				return
+			var f = send(elem)
+			elem.addEventListener('click', f, false)
+			elem.addEventListener('auxclick', f, false)  // Middle click.
+			elem.dataset.goatcounterBound = 'true'
+		})
+	}
+
+	// Add a "visitor counter" frame or image.
+	window.goatcounter.visit_count = function(opt) {
+		on_load(function() {
+			opt        = opt        || {}
+			opt.type   = opt.type   || 'html'
+			opt.append = opt.append || 'body'
+			opt.path   = opt.path   || get_path()
+			opt.attr   = opt.attr   || {width: '200', height: (opt.no_branding ? '60' : '80')}
+
+			opt.attr['src'] = get_endpoint() + 'er/' + enc(opt.path) + '.' + enc(opt.type) + '?'
+			if (opt.no_branding) opt.attr['src'] += '&no_branding=1'
+			if (opt.style)       opt.attr['src'] += '&style=' + enc(opt.style)
+			if (opt.start)       opt.attr['src'] += '&start=' + enc(opt.start)
+			if (opt.end)         opt.attr['src'] += '&end='   + enc(opt.end)
+
+			var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type]
+			if (!tag)
+				return warn('visit_count: unknown type: ' + opt.type)
+
+			if (opt.type === 'html') {
+				opt.attr['frameborder'] = '0'
+				opt.attr['scrolling']   = 'no'
+			}
+
+			var d = document.createElement(tag)
+			for (var k in opt.attr)
+				d.setAttribute(k, opt.attr[k])
+
+			var p = document.querySelector(opt.append)
+			if (!p)
+				return warn('visit_count: append not found: ' + opt.append)
+			p.appendChild(d)
+		})
+	}
+
+	// Make it easy to skip your own views.
+	if (location.hash === '#toggle-goatcounter') {
+		if (localStorage.getItem('skipgc') === 't') {
+			localStorage.removeItem('skipgc', 't')
+			alert('GoatCounter tracking is now ENABLED in this browser.')
+		}
+		else {
+			localStorage.setItem('skipgc', 't')
+			alert('GoatCounter tracking is now DISABLED in this browser until ' + location + ' is loaded again.')
+		}
+	}
+
+	if (!goatcounter.no_onload)
+		on_load(function() {
+			// 1. Page is visible, count request.
+			// 2. Page is not yet visible; wait until it switches to 'visible' and count.
+			// See #487
+			if (!('visibilityState' in document) || document.visibilityState === 'visible')
+				goatcounter.count()
+			else {
+				var f = function(e) {
+					if (document.visibilityState !== 'visible')
+						return
+					document.removeEventListener('visibilitychange', f)
+					goatcounter.count()
+				}
+				document.addEventListener('visibilitychange', f)
+			}
+
+			if (!goatcounter.no_events)
+				goatcounter.bind_events()
+		})
+})();
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/js/imamu.js b/blog-src/themes/apollo/static/js/imamu.js
new file mode 100644
index 000000000..12f982cbd
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/imamu.js
@@ -0,0 +1,2 @@
+// https://cloud.umami.is/script.js
+!function(){"use strict";(t=>{const{screen:{width:e,height:a},navigator:{language:r},location:n,document:i,history:c}=t,{hostname:s,href:o,origin:u}=n,{currentScript:l,referrer:d}=i,h=o.startsWith("data:")?void 0:t.localStorage;if(!l)return;const m="data-",f=l.getAttribute.bind(l),p=f(m+"website-id"),g=f(m+"host-url"),y=f(m+"tag"),b="false"!==f(m+"auto-track"),v="true"===f(m+"exclude-search"),w=f(m+"domains")||"",S=w.split(",").map((t=>t.trim())),N=`${(g||"https://api-gateway.umami.dev"| [...]
\ No newline at end of file
diff --git a/blog-src/themes/apollo/static/js/main.js b/blog-src/themes/apollo/static/js/main.js
new file mode 100644
index 000000000..f33a93d73
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/main.js
@@ -0,0 +1,25 @@
+mmdElements = document.getElementsByClassName("mermaid");
+const mmdHTML = [];
+for (let i = 0; i < mmdElements.length; i++) {
+	mmdHTML[i] = mmdElements[i].innerHTML;
+}
+
+function mermaidRender(theme) {
+	if (theme == "dark") {
+		initOptions = {
+			startOnLoad: false,
+			theme: "dark",
+		};
+	} else {
+		initOptions = {
+			startOnLoad: false,
+			theme: "neutral",
+		};
+	}
+	for (let i = 0; i < mmdElements.length; i++) {
+		delete mmdElements[i].dataset.processed;
+		mmdElements[i].innerHTML = mmdHTML[i];
+	}
+	mermaid.initialize(initOptions);
+	mermaid.run();
+}
diff --git a/blog-src/themes/apollo/static/js/mermaid.js b/blog-src/themes/apollo/static/js/mermaid.js
new file mode 100644
index 000000000..885a7aa8c
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/mermaid.js
@@ -0,0 +1,1760 @@
+
+(function(BL,Z0){typeof exports=="object"&&typeof module<"u"?module.exports=Z0():typeof define=="function"&&define.amd?define(Z0):(BL=typeof globalThis<"u"?globalThis:BL||self,BL.mermaid=Z0())})(this,function(){var mVt,vVt;"use strict";function BL(i){for(var a=[],f=1;f<arguments.length;f++)a[f-1]=arguments[f];var g=Array.from(typeof i=="string"?[i]:i);g[g.length-1]=g[g.length-1].replace(/\r?\n([\t ]*)$/,"");var w=g.reduce(function(E,S){var _=S.match(/\n([\t ]+|(?!\s).)/g);return _?E.conc [...]
+[	 ]{`+Math.min.apply(Math,w)+"}","g");g=g.map(function(E){return E.replace(v,`
+`)})}g[0]=g[0].replace(/^\r?\n/,"");var b=g[0];return a.forEach(function(E,S){var _=b.match(/(?:^|\n)( *)$/),M=_?_[1]:"",F=E;typeof E=="string"&&E.includes(`
+`)&&(F=String(E).split(`
+`).map(function(R,B){return B===0?R:""+M+R}).join(`
+`)),b+=F+g[S+1]}),b}var Z0=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function e9(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var yOt={exports:{}};(function(i,a){(function(f,g){i.exports=g()})(Z0,function(){var f=1e3,g=6e4,w=36e5,v="millisecond",b="second",E="minute",S="hour",_="day",M="week",F="month",R="quarter",B="year",z="date",Y="Invalid Date",nt=/^(\d{4})[-/]?(\d{1,2})?[-/]? [...]
+`+Zc),Ne&&(Zc=Hv(Zc,K," "),Zc=Hv(Zc,yt," "),Zc=Hv(Zc,V," ")),wt&&Br?wt.createHTML(Zc):Zc},a.setConfig=function(Gi){Ic(Gi),_e=!0},a.clearConfig=function(){d1=null,_e=!1},a.isValidAttribute=function(Gi,cn,Nr){d1||Ic({});const Si=Zs(Gi),vc=Zs(cn);return w0(Si,vc,Nr)},a.addHook=function(Gi,cn){typeof cn=="function"&&(dt[Gi]=dt[Gi]||[],vF(dt[Gi],cn))},a.removeHook=function(Gi){if(dt[Gi])return EPt(dt[Gi])},a.removeHooks=function(Gi){dt[Gi]&&(dt[Gi]=[])},a.removeAllHooks=function(){dt={}},a}va [...]
+`);for(const[f,{detector:g}]of Object.entries(JL))if(g(i,a))return f;throw new VPt(`No diagram type detected matching given configuration for text: ${i}`)},UPt=(...i)=>{for(const{id:a,detector:f,loader:g}of i)GPt(a,f,g)},GPt=(i,a,f)=>{JL[i]?Kt.error(`Detector with key ${i} already exists`):JL[i]={detector:a,loader:f},Kt.debug(`Detector with key ${i} added${f?" with loader":""}`)},DMe=i=>JL[i].loader,ggt=(i,a,{depth:f=2,clobber:g=!1}={})=>{const w={depth:f,clobber:g};return Array.isArray( [...]
+`,"ig");i=i.trim().replace(f,"").replace(/'/gm,'"'),Kt.debug(`Detecting diagram directive${a!==null?" type:"+a:""} based on the text:${i}`);let g;const w=[];for(;(g=TF.exec(i))!==null;)if(g.index===TF.lastIndex&&TF.lastIndex++,g&&!a||a&&g[1]&&g[1].match(a)||a&&g[2]&&g[2].match(a)){const v=g[1]?g[1]:g[2],b=g[3]?g[3].trim():g[4]?JSON.parse(g[4].trim()):null;w.push({type:v,args:b})}return w.length===0?{type:i,args:null}:w.length===1?w[0]:w}catch(f){return Kt.error(`ERROR: ${f.message} - Una [...]
+    font-family: ${f.fontFamily};
+    font-size: ${f.fontSize};
+    fill: ${f.textColor}
+  }
+
+  /* Classes common for multiple diagrams */
+
+  & .error-icon {
+    fill: ${f.errorBkgColor};
+  }
+  & .error-text {
+    fill: ${f.errorTextColor};
+    stroke: ${f.errorTextColor};
+  }
+
+  & .edge-thickness-normal {
+    stroke-width: 2px;
+  }
+  & .edge-thickness-thick {
+    stroke-width: 3.5px
+  }
+  & .edge-pattern-solid {
+    stroke-dasharray: 0;
+  }
+
+  & .edge-pattern-dashed{
+    stroke-dasharray: 3;
+  }
+  .edge-pattern-dotted {
+    stroke-dasharray: 2;
+  }
+
+  & .marker {
+    fill: ${f.lineColor};
+    stroke: ${f.lineColor};
+  }
+  & .marker.cross {
+    stroke: ${f.lineColor};
+  }
+
+  & svg {
+    font-family: ${f.fontFamily};
+    font-size: ${f.fontSize};
+  }
+
+  ${g}
+
+  ${a}
+`},fPe=(i,a)=>{a!==void 0&&(mY[i]=a)},dPe=hPe;let Ogt="",Ngt="",Pgt="";const Fgt=i=>Ef(i,o1()),ag=()=>{Ogt="",Pgt="",Ngt=""},og=i=>{Ogt=Fgt(i).replace(/^\s+/g,"")},Xg=()=>Ogt,Qg=i=>{Pgt=Fgt(i).replace(/\n\s+/g,`
+`)},Jg=()=>Pgt,S2=i=>{Ngt=Fgt(i)},Zg=()=>Ngt,uBt=Object.freeze(Object.defineProperty({__proto__:null,clear:ag,getAccDescription:Jg,getAccTitle:Xg,getDiagramTitle:Zg,setAccDescription:Qg,setAccTitle:og,setDiagramTitle:S2},Symbol.toStringTag,{value:"Module"})),gPe=Kt,pPe=d0t,Re=o1,bPe=qFt,lBt=cD,wPe=i=>Ef(i,Re()),hBt=w8,mPe=()=>uBt,vY={},yY=(i,a,f)=>{var g;if(vY[i])throw new Error(`Diagram ${i} already registered.`);vY[i]=a,f&&GPt(i,f),fPe(i,a.styles),(g=a.injectUtils)==null||g.call(a,gPe, [...]
+`);const g=FF(a);g.attr("viewBox","0 0 2412 512"),sg(g,100,512,!0);const w=g.append("g");w.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.0 [...]
+`;const g=o1();try{this.type=QW(a,g)}catch(v){this.type="error",this.detectError=v}const w=Bgt(this.type);Kt.debug("Type "+this.type),this.db=w.db,this.renderer=w.renderer,this.parser=w.parser,this.parser.parser.yy=this.db,this.init=w.init,this.parse()}parse(){var f,g,w,v,b;if(this.detectError)throw this.detectError;(g=(f=this.db).clear)==null||g.call(f);const a=o1();(w=this.init)==null||w.call(this,a),this.metadata.title&&((b=(v=this.db).setDiagramTitle)==null||b.call(v,this.metadata.ti [...]
+
+`+i.mark.snippet),g+" "+f):g}function RF(i,a){Error.call(this),this.name="YAMLException",this.reason=i,this.mark=a,this.message=ABt(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}RF.prototype=Object.create(Error.prototype),RF.prototype.constructor=RF,RF.prototype.toString=function(a){return this.name+": "+ABt(this,a)};var Ox=RF;function qgt(i,a,f,g,w){var v="",b="",E=Math.floor(w/2)-1;return g-a>E&&(v=" ... ",a=g-E+v.lengt [...]
+`+E;for(_=qgt(i.buffer,g[b],w[b],i.position,F),E+=tp.repeat(" ",a.indent)+Hgt((i.line+1).toString(),M)+" | "+_.str+`
+`,E+=tp.repeat("-",a.indent+M+3+_.pos)+`^
+`,S=1;S<=a.linesAfter&&!(b+S>=w.length);S++)_=qgt(i.buffer,g[b+S],w[b+S],i.position-(g[b]-g[b+S]),F),E+=tp.repeat(" ",a.indent)+Hgt((i.line+S+1).toString(),M)+" | "+_.str+`
+`;return E.replace(/\n$/,"")}var uFe=cFe,lFe=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],hFe=["scalar","sequence","mapping"];function fFe(i){var a={};return i!==null&&Object.keys(i).forEach(function(f){i[f].forEach(function(g){a[String(g)]=f})}),a}function dFe(i,a){if(a=a||{},Object.keys(a).forEach(function(f){if(lFe.indexOf(f)===-1)throw new Ox('Unknown option "'+f+'" is met in definition of "'+i+'" YAML type. [...]
+\r`;function YFe(i){if(i===null)return!1;var a,f,g=0,w=i.length,v=Ugt;for(f=0;f<w;f++)if(a=v.indexOf(i.charAt(f)),!(a>64)){if(a<0)return!1;g+=6}return g%8===0}function XFe(i){var a,f,g=i.replace(/[\r\n=]/g,""),w=g.length,v=Ugt,b=0,E=[];for(a=0;a<w;a++)a%4===0&&a&&(E.push(b>>16&255),E.push(b>>8&255),E.push(b&255)),b=b<<6|v.indexOf(g.charAt(a));return f=w%4*6,f===0?(E.push(b>>16&255),E.push(b>>8&255),E.push(b&255)):f===18?(E.push(b>>10&255),E.push(b>>2&255)):f===12&&E.push(b>>4&255),new Ui [...]
+`:i===118?"\v":i===102?"\f":i===114?"\r":i===101?"\x1B":i===32?" ":i===34?'"':i===47?"/":i===92?"\\":i===78?"…":i===95?" ":i===76?"\u2028":i===80?"\u2029":""}function xBe(i){return i<=65535?String.fromCharCode(i):String.fromCharCode((i-65536>>10)+55296,(i-65536&1023)+56320)}for(var $Bt=new Array(256),zBt=new Array(256),dD=0;dD<256;dD++)$Bt[dD]=jBt(dD)?1:0,zBt[dD]=jBt(dD);function kBe(i,a){this.input=i,this.filename=a.filename||null,this.schema=a.schema||dBe,this.onWarning=a.onWarning||null,this.legacy=a.legacy||!1,this.json=a.json||!1,this.listener=a.listener||null,this.implicitTypes=this.schema. [...]
+`,a-1))}function EBe(i,a,f){var g,w,v,b,E,S,_,M,F=i.kind,R=i.result,B;if(B=i.input.charCodeAt(i.position),sb(B)||fD(B)||B===35||B===38||B===42||B===33||B===124||B===62||B===39||B===34||B===37||B===64||B===96||(B===63||B===45)&&(w=i.input.charCodeAt(i.position+1),sb(w)||f&&fD(w)))return!1;for(i.kind="scalar",i.result="",v=b=i.position,E=!1;B!==0;){if(B===58){if(w=i.input.charCodeAt(i.position+1),sb(w)||f&&fD(w))break}else if(B===35){if(g=i.input.charCodeAt(i.position-1),sb(g))break}else{i [...]
+`,v?1+S:S):w===Ggt&&v&&(i.result+=`
+`);break}for(g?C9(F)?(_=!0,i.result+=tp.repeat(`
+`,v?1+S:S)):_?(_=!1,i.result+=tp.repeat(`
+`,S+1)):S===0?v&&(i.result+=" "):i.result+=tp.repeat(`
+`,S):i.result+=tp.repeat(`
+`,v?1+S:S),v=!0,b=!0,S=0,f=i.position;!r4(F)&&F!==0;)F=i.input.charCodeAt(++i.position);v8(i,f,i.position,!1)}return!0}function UBt(i,a){var f,g=i.tag,w=i.anchor,v=[],b,E=!1,S;if(i.firstTabInLine!==-1)return!1;for(i.anchor!==null&&(i.anchorMap[i.anchor]=v),S=i.input.charCodeAt(i.position);S!==0&&(i.firstTabInLine!==-1&&(i.position=i.firstTabInLine,Ks(i,"tab characters must not be used in indentation")),!(S!==45||(b=i.input.charCodeAt(i.position+1),!sb(b))));){if(E=!0,i.position++,c1(i,!0 [...]
+`),i.charCodeAt(0)===65279&&(i=i.slice(1)));var f=new kBe(i,a),g=i.indexOf("\0");for(g!==-1&&(f.position=g,Ks(f,"null byte is not allowed in input")),f.input+="\0";f.input.charCodeAt(f.position)===32;)f.lineIndent+=1,f.position+=1;for(;f.position<f.length-1;)IBe(f);return f.documents}function OBe(i,a,f){a!==null&&typeof a=="object"&&typeof f>"u"&&(f=a,a=null);var g=GBt(i,f);if(typeof a!="function")return g;for(var w=0,v=g.length;w<v;w+=1)a(g[w])}function NBe(i,a){var f=GBt(i,a);if(f.leng [...]
+`).replace(/<(\w+)([^>]*)>/g,(a,f,g)=>"<"+f+g.replace(/="([^"]*)"/g,"='$1'")+">"),qBe=i=>{const{text:a,metadata:f}=$Be(i),{displayMode:g,title:w,config:v={}}=f;return g&&(v.gantt||(v.gantt={}),v.gantt.displayMode=g),{title:w,config:v,text:a}},HBe=i=>{const a=uo.detectInit(i)??{},f=uo.detectDirective(i,"wrap");return Array.isArray(f)?a.wrap=f.some(({type:g})=>{}):(f==null?void 0:f.type)==="wrap"&&(a.wrap=!0),{text:uNe(i),directive:a}};function KBt(i){const a=zBe(i),f=qBe(a),g=HBe(f.text), [...]
+.${i} ${a} { ${f.join(" !important; ")} !important; }`,aRe=(i,a={})=>{var g;let f="";if(i.themeCSS!==void 0&&(f+=`
+${i.themeCSS}`),i.fontFamily!==void 0&&(f+=`
+:root { --mermaid-font-family: ${i.fontFamily}}`),i.altFontFamily!==void 0&&(f+=`
+:root { --mermaid-alt-font-family: ${i.altFontFamily}}`),!BF(a)){const E=i.htmlLabels||((g=i.flowchart)==null?void 0:g.htmlLabels)?["> *","span"]:["rect","polygon","ellipse","circle","path"];for(const S in a){const _=a[S];BF(_.styles)||E.forEach(M=>{f+=YBt(_.id,M,_.styles)}),BF(_.textStyles)||(f+=YBt(_.id,"tspan",_.textStyles))}}return f},oRe=(i,a,f,g)=>{const w=aRe(i,f),v=dPe(a,w,i.themeVariables);return Igt(BNe(`${g}{${v}}`),jNe)},cRe=(i="",a,f)=>{let g=i;return!f&&!a&&(g=g.replace(/ma [...]
+  ${nRe}
+</iframe>`},XBt=(i,a,f,g,w)=>{const v=i.append("div");v.attr("id",f),g&&v.attr("style",g);const b=v.append("svg").attr("id",a).attr("width","100%").attr("xmlns",WBe);return w&&b.attr("xmlns:xlink",w),b.append("g"),i};function QBt(i,a){return i.append("iframe").attr("id",a).attr("style","width: 100%; height: 100%;").attr("sandbox","")}const lRe=(i,a,f,g)=>{var w,v,b;(w=i.getElementById(a))==null||w.remove(),(v=i.getElementById(f))==null||v.remove(),(b=i.getElementById(g))==null||b.remove( [...]
+ * Wait for document loaded before starting the execution
+ */window.addEventListener("load",tRt,!1)}const vRe=function(i){_2.parseError=i},CY=[];let Xgt=!1;const eRt=async()=>{if(!Xgt){for(Xgt=!0;CY.length>0;){const i=CY.shift();if(i)try{await i()}catch(a){Kt.error("Error executing queue",a)}}Xgt=!1}},yRe=async(i,a)=>new Promise((f,g)=>{const w=()=>new Promise((v,b)=>{S9.parse(i,a).then(E=>{v(E),f(E)},E=>{var S;Kt.error("Error parsing",E),(S=_2.parseError)==null||S.call(_2,E),b(E),g(E)})});CY.push(w),eRt().catch(g)}),nRt=(i,a,f)=>new Promise((g [...]
+`+ds.showPosition()+`
+Expecting `+ya.join(", ")+", got '"+(this.terminals_[Ic]||Ic)+"'":ea="Parse error on line "+(qa+1)+": Unexpected "+(Ic==va?"end of input":"'"+(this.terminals_[Ic]||Ic)+"'"),this.parseError(ea,{text:ds.match,token:this.terminals_[Ic]||Ic,line:ds.yylineno,loc:d1,expected:ya})}if(Ea[0]instanceof Array&&Ea.length>1)throw new Error("Parse Error: multiple actions possible at state: "+wc+", token: "+Ic);switch(Ea[0]){case 1:Bn.push(Ic),pe.push(ds.yytext),Je.push(ds.yylloc),Bn.push(Ea[1]),Ic=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(An){this.unput(this.match.slice(An))},pastInput:function(){var An=this.matched.substr(0,this.matched.length-this.match.length);return(An.length>20?"...":"")+An.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var An=this.match;return An.length<20&&(An+=this._input.substr(0,20-An.length)),(An.substr(0,20)+(An.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var An=this.pastInput [...]
+`+Nn+"^"},test_match:function(An,Nn){var Bn,ke,pe;if(this.options.backtrack_lexer&&(pe={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var Nn=this.next();return Nn||this.lex()},begin:function(Nn){this.conditionStack.push(Nn)},popState:function(){var Nn=this.conditionStack.length-1;return Nn>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+    stroke: ${i.personBorder};
+    fill: ${i.personBkg};
+  }
+`,init:({c4:i,wrap:a})=>{dRt.setConf(i),Zgt.setWrap(a)}}},Symbol.toStringTag,{value:"Module"}));var ipt=function(){var i=function(Od,cs,As,to){for(As=As||{},to=Od.length;to--;As[Od[to]]=cs);return As},a=[1,4],f=[1,3],g=[1,5],w=[1,8,9,10,11,27,34,36,38,42,58,81,82,83,84,85,86,99,102,103,106,108,111,112,113,118,119,120,121],v=[2,2],b=[1,13],E=[1,14],S=[1,15],_=[1,16],M=[1,23],F=[1,25],R=[1,26],B=[1,27],z=[1,49],Y=[1,48],nt=[1,29],ot=[1,30],ft=[1,31],wt=[1,32],mt=[1,33],ct=[1,44],rt=[1,46], [...]
+`+th.showPosition()+`
+Expecting `+b4.join(", ")+", got '"+(this.terminals_[U1]||U1)+"'":ay="Parse error on line "+(g1+1)+": Unexpected "+(U1==D8?"end of input":"'"+(this.terminals_[U1]||U1)+"'"),this.parseError(ay,{text:th.match,token:this.terminals_[U1]||U1,line:th.yylineno,loc:M8,expected:b4})}if(Pd[0]instanceof Array&&Pd.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Nd+", token: "+U1);switch(Pd[0]){case 1:to.push(U1),pu.push(th.yytext),Le.push(th.yylloc),to.push(Pd[1]),U1=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(cs){this.unput(this.match.slice(cs))},pastInput:function(){var cs=this.matched.substr(0,this.matched.length-this.match.length);return(cs.length>20?"...":"")+cs.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var cs=this.match;return cs.length<20&&(cs+=this._input.substr(0,20-cs.length)),(cs.substr(0,20)+(cs.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var cs=this.pastInput [...]
+`+As+"^"},test_match:function(cs,As){var to,Ci,pu;if(this.options.backtrack_lexer&&(pu={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var As=this.next();return As||this.lex()},begin:function(As){this.conditionStack.push(As)},popState:function(){var As=this.conditionStack.length-1;return As>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+
+Initialize mermaid with maxEdges set to a higher number to allow more edges. 
+You cannot set this config via configuration inside the diagram as it is a secure config. 
+You have to call mermaid.initialize.`)},wRt=function(i,a,f){Kt.info("addLink (abc78)",i,a,f);let g,w;for(g=0;g<i.length;g++)for(w=0;w<a.length;w++)bRt(i[g],a[w],f)},mRt=function(i,a){i.forEach(function(f){f==="default"?Qv.defaultInterpolate=a:Qv[f].interpolate=a})},vRt=function(i,a){i.forEach(function(f){if(f>=Qv.length)throw new Error(`The index ${f} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${Qv.length-1}. (Help: Ensure that the index is within the r [...]
+`),w=0;w<g.length;w++)f.append("tspan").attr("xml:space","preserve").attr("dy","1em").attr("x","1").text(g[w]);return a4(f,a.labelStyle),f}function qVe(i){for(var a="",f=!1,g,w=0;w<i.length;++w)if(g=i[w],f){switch(g){case"n":a+=`
+`;break;default:a+=g}f=!1}else g==="\\"?f=!0:a+=g;return a}function Ipt(i,a,f){var g=a.label,w=i.append("g");a.labelType==="svg"?$Ve(w,a):typeof g!="string"||a.labelType==="html"?Mpt(w,a):zVe(w,a);var v=w.node().getBBox(),b;switch(f){case"top":b=-a.height/2;break;case"bottom":b=a.height/2-v.height;break;default:b=-v.height/2}return w.attr("transform","translate("+-v.width/2+","+b+")"),w}var Opt=function(i,a){var f=a.nodes().filter(function(v){return t$t(a,v)}),g=i.selectAll("g.cluster"). [...]
+`),b.style===void 0&&(M.style=M.style||"stroke: #333; stroke-width: 1.5px;fill:none"),M.labelStyle=M.labelStyle.replace("color:","fill:"))),M.id=E,M.class=S+" "+_,M.minlen=b.length||1,a.setEdge(f.db.lookUpDomId(b.start),f.db.lookUpDomId(b.end),M,g)})},wUe={setConf:bUe,addVertices:v$t,addEdges:y$t,getClasses:function(i,a){return Kt.info("Extracting classes"),a.db.getClasses()},draw:function(i,a,f,g){Kt.info("Drawing flowchart");const{securityLevel:w,flowchart:v}=Re();let b;w==="sandbox"&& [...]
+`;break}case-3:{b=`\r
+`;break}case-2:{b=a?" ":"	";break}case-1:{if(!a&&w)continue;b=" ";break}default:b=String.fromCharCode(v)}w=v===-2,g.push(b)}return g.join("")}const lKe=Object.freeze(Object.defineProperty({__proto__:null,attentionMarkers:{null:[42,95]},contentInitial:{91:fGe},disable:{null:[]},document:{42:cb,43:cb,45:cb,48:cb,49:cb,50:cb,51:cb,52:cb,53:cb,54:cb,55:cb,56:cb,57:cb,62:_$t},flow:{35:mGe,42:QY,45:[R$t,QY],60:kGe,61:R$t,95:QY,96:I$t,126:I$t},flowInitial:{[-2]:Upt,[-1]:Upt,32:Upt},insideSpan:{ [...]
+`);return BL(a)}function yKe(i){const a=vKe(i),{children:f}=G$t(a),g=[[]];let w=0;function v(b,E="normal"){b.type==="text"?b.value.split(`
+`).forEach((_,M)=>{M!==0&&(w++,g.push([])),_.split(" ").forEach(F=>{F&&g[w].push({content:F,type:E})})}):(b.type==="strong"||b.type==="emphasis")&&b.children.forEach(S=>{v(S,b.type)})}return f.forEach(b=>{b.type==="paragraph"&&b.children.forEach(E=>{v(E)})}),g}function xKe(i){const{children:a}=G$t(i);function f(g){return g.type==="text"?g.value.replace(/\n/g,"<br/>"):g.type==="strong"?`<strong>${g.children.map(f).join("")}</strong>`:g.type==="emphasis"?`<em>${g.children.map(f).join("")}< [...]
+`)))throw new Error("splitLineToFitWidth does not support newlines in the line");return Ypt(i,a)}function Ypt(i,a,f=[],g=[]){if(i.length===0)return g.length>0&&f.push(g),f.length>0?f:[];let w="";i[0].content===" "&&(w=" ",i.shift());const v=i.shift()??{content:" ",type:"normal"},b=[...g];if(w!==""&&b.push({content:w,type:"normal"}),b.push(v),a(b))return Ypt(i,a,f,b);if(g.length>0)f.push(g),i.unshift(v);else if(v.content){const[E,S]=EKe(a,v);f.push([E]),S.content&&i.unshift(S)}return Ypt( [...]
+    <span class="${S} ${g}" `+(a.labelStyle?'style="'+a.labelStyle+'"':"")+">"+E+"</span>"),CKe(b,a.labelStyle),b.style("display","table-cell"),b.style("white-space","nowrap"),b.style("max-width",f+"px"),b.attr("xmlns","http://www.w3.org/1999/xhtml"),w&&b.attr("class","labelBkg");let _=b.node().getBoundingClientRect();return _.width===f&&(b.style("display","table"),b.style("white-space","break-spaces"),b.style("width",f+"px"),_=b.node().getBoundingClientRect()),v.style("width",_.width),v [...]
+  outsidePoint: ${JSON.stringify(a)}
+  insidePoint : ${JSON.stringify(f)}
+  node        : x:${i.x} y:${i.y} w:${i.width} h:${i.height}`);const g=i.x,w=i.y,v=Math.abs(g-f.x),b=i.width/2;let E=f.x<a.x?b-v:b+v;const S=i.height/2,_=Math.abs(a.y-f.y),M=Math.abs(a.x-f.x);if(Math.abs(w-a.y)*b>Math.abs(g-a.x)*S){let F=f.y<a.y?a.y-S-w:w-S-a.y;E=M*F/_;const R={x:f.x<a.x?f.x+E:f.x-M+E,y:f.y<a.y?f.y+_-F:f.y-_+F};return E===0&&(R.x=a.x,R.y=a.y),M===0&&(R.x=a.x),_===0&&(R.y=a.y),Kt.debug(`abc89 topp/bott calc, Q ${_}, q ${F}, R ${M}, r ${E}`,R),R}else{f.x<a.x?E=a.x-b-g:E=g- [...]
+`),E.style===void 0&&(R.style=R.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),R.labelStyle=R.labelStyle.replace("color:","fill:"),R.id=_,R.classes="flowchart-link "+M+" "+F,a.setEdge(E.start,E.end,R,g)})},nbt={setConf:pWe,addVertices:bzt,addEdges:wzt,getClasses:function(i,a){return a.db.getClasses()},draw:async function(i,a,f,g){Kt.info("Drawing flowchart");let w=g.db.getDirection();w===void 0&&(w="TD");const{securityLevel:v,flowchart:b}=Re(),E=b.nodeSpacing||50,S=b.rankSpacing| [...]
+    font-family: ${i.fontFamily};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+  .cluster-label text {
+    fill: ${i.titleColor};
+  }
+  .cluster-label span,p {
+    color: ${i.titleColor};
+  }
+
+  .label text,span,p {
+    fill: ${i.nodeTextColor||i.textColor};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+
+  .node rect,
+  .node circle,
+  .node ellipse,
+  .node polygon,
+  .node path {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+    stroke-width: 1px;
+  }
+  .flowchart-label text {
+    text-anchor: middle;
+  }
+  // .flowchart-label .text-outer-tspan {
+  //   text-anchor: middle;
+  // }
+  // .flowchart-label .text-inner-tspan {
+  //   text-anchor: start;
+  // }
+
+  .node .label {
+    text-align: center;
+  }
+  .node.clickable {
+    cursor: pointer;
+  }
+
+  .arrowheadPath {
+    fill: ${i.arrowheadColor};
+  }
+
+  .edgePath .path {
+    stroke: ${i.lineColor};
+    stroke-width: 2.0px;
+  }
+
+  .flowchart-link {
+    stroke: ${i.lineColor};
+    fill: none;
+  }
+
+  .edgeLabel {
+    background-color: ${i.edgeLabelBackground};
+    rect {
+      opacity: 0.5;
+      background-color: ${i.edgeLabelBackground};
+      fill: ${i.edgeLabelBackground};
+    }
+    text-align: center;
+  }
+
+  /* For html labels only */
+  .labelBkg {
+    background-color: ${bWe(i.edgeLabelBackground,.5)};
+    // background-color: 
+  }
+
+  .cluster rect {
+    fill: ${i.clusterBkg};
+    stroke: ${i.clusterBorder};
+    stroke-width: 1px;
+  }
+
+  .cluster text {
+    fill: ${i.titleColor};
+  }
+
+  .cluster span,p {
+    color: ${i.titleColor};
+  }
+  /* .cluster div {
+    color: ${i.titleColor};
+  } */
+
+  div.mermaidTooltip {
+    position: absolute;
+    text-align: center;
+    max-width: 200px;
+    padding: 2px;
+    font-family: ${i.fontFamily};
+    font-size: 12px;
+    background: ${i.tertiaryColor};
+    border: 1px solid ${i.border2};
+    border-radius: 2px;
+    pointer-events: none;
+    z-index: 100;
+  }
+
+  .flowchartTitleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.textColor};
+  }
+`,wWe=Object.freeze(Object.defineProperty({__proto__:null,diagram:{parser:spt,db:A9,renderer:nbt,styles:mzt,init:i=>{i.flowchart||(i.flowchart={}),i.flowchart.arrowMarkerAbsolute=i.arrowMarkerAbsolute,wUe.setConf(i.flowchart),A9.clear(),A9.setGen("gen-1")}}},Symbol.toStringTag,{value:"Module"})),mWe=Object.freeze(Object.defineProperty({__proto__:null,diagram:{parser:spt,db:A9,renderer:nbt,styles:mzt,init:i=>{i.flowchart||(i.flowchart={}),i.flowchart.arrowMarkerAbsolute=i.arrowMarkerAbsol [...]
+`+Fe.showPosition()+`
+Expecting `+Br.join(", ")+", got '"+(this.terminals_[sn]||sn)+"'":we="Parse error on line "+(xt+1)+": Unexpected "+(sn==kt?"end of input":"'"+(this.terminals_[sn]||sn)+"'"),this.parseError(we,{text:Fe.match,token:this.terminals_[sn]||sn,line:Fe.yylineno,loc:Ae,expected:Br})}if(Ne[0]instanceof Array&&Ne.length>1)throw new Error("Parse Error: multiple actions possible at state: "+be+", token: "+sn);switch(Ne[0]){case 1:K.push(sn),V.push(Fe.yytext),Ot.push(Fe.yylloc),K.push(Ne[1]),sn=null,F [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(Q){this.unput(this.match.slice(Q))},pastInput:function(){var Q=this.matched.substr(0,this.matched.length-this.match.length);return(Q.length>20?"...":"")+Q.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var Q=this.match;return Q.length<20&&(Q+=this._input.substr(0,20-Q.length)),(Q.substr(0,20)+(Q.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var Q=this.pastInput(),dt=new Ar [...]
+`+dt+"^"},test_match:function(Q,dt){var K,yt,V;if(this.options.backtrack_lexer&&(V={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges& [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var dt=this.next();return dt||this.lex()},begin:function(dt){this.conditionStack.push(dt)},popState:function(){var dt=this.conditionStack.length-1;return dt>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+  .entityBox {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+  }
+
+  .attributeBoxOdd {
+    fill: ${i.attributeBackgroundColorOdd};
+    stroke: ${i.nodeBorder};
+  }
+
+  .attributeBoxEven {
+    fill:  ${i.attributeBackgroundColorEven};
+    stroke: ${i.nodeBorder};
+  }
+
+  .relationshipLabelBox {
+    fill: ${i.tertiaryColor};
+    opacity: 0.7;
+    background-color: ${i.tertiaryColor};
+      rect {
+        opacity: 0.5;
+      }
+  }
+
+    .relationshipLine {
+      stroke: ${i.lineColor};
+    }
+
+  .entityTitleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.textColor};
+  }    
+  #MD_PARENT_START {
+    fill: #f5f5f5 !important;
+    stroke: ${i.lineColor} !important;
+    stroke-width: 1;
+  }
+  #MD_PARENT_END {
+    fill: #f5f5f5 !important;
+    stroke: ${i.lineColor} !important;
+    stroke-width: 1;
+  }
+  
+`}},Symbol.toStringTag,{value:"Module"}));var abt=function(){var i=function(ct,rt,it,gt){for(it=it||{},gt=ct.length;gt--;it[ct[gt]]=rt);return it},a=[1,3],f=[1,6],g=[1,4],w=[1,5],v=[2,5],b=[1,12],E=[5,7,13,19,21,23,24,26,28,31,37,40,47],S=[7,13,19,21,23,24,26,28,31,37,40],_=[7,12,13,19,21,23,24,26,28,31,37,40],M=[7,13,47],F=[1,42],R=[1,41],B=[7,13,29,32,35,38,47],z=[1,55],Y=[1,56],nt=[1,57],ot=[7,13,32,35,42,47],ft={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,GG:5,document:6 [...]
+`+Ct.showPosition()+`
+Expecting `+xe.join(", ")+", got '"+(this.terminals_[Ae]||Ae)+"'":Mn="Parse error on line "+(Ot+1)+": Unexpected "+(Ae==xt?"end of input":"'"+(this.terminals_[Ae]||Ae)+"'"),this.parseError(Mn,{text:Ct.match,token:this.terminals_[Ae]||Ae,line:Ct.yylineno,loc:Fe,expected:xe})}if(tn[0]instanceof Array&&tn.length>1)throw new Error("Parse Error: multiple actions possible at state: "+ye+", token: "+Ae);switch(tn[0]){case 1:gt.push(Ae),dt.push(Ct.yytext),K.push(Ct.yylloc),gt.push(tn[1]),Ae=null [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(rt){this.unput(this.match.slice(rt))},pastInput:function(){var rt=this.matched.substr(0,this.matched.length-this.match.length);return(rt.length>20?"...":"")+rt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var rt=this.match;return rt.length<20&&(rt+=this._input.substr(0,20-rt.length)),(rt.substr(0,20)+(rt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var rt=this.pastInput [...]
+`+it+"^"},test_match:function(rt,it){var gt,Q,dt;if(this.options.backtrack_lexer&&(dt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.rang [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var it=this.next();return it||this.lex()},begin:function(it){this.conditionStack.push(it)},popState:function(){var it=this.conditionStack.length-1;return it>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+          ${b-rt.width/2-4/2},${gt+2}
+          ${b-rt.width/2-4/2},${gt-2}
+          ${z-rt.width/2-4},${gt-it-2}
+          ${z+rt.width/2+4},${gt-it-2}
+          ${z+rt.width/2+4},${gt+it+2}
+          ${z-rt.width/2-4},${gt+it+2}`),mt.attr("cx",b-rt.width/2+4/2).attr("cy",gt).attr("r",1.5).attr("class","tag-hole"),f1==="TB"&&(wt.attr("class","tag-label-bkg").attr("points",`
+            ${nt},${b+2}
+            ${nt},${b-2}
+            ${nt+M},${b-it-2}
+            ${nt+M+rt.width+4},${b-it-2}
+            ${nt+M+rt.width+4},${b+it+2}
+            ${nt+M},${b+it+2}`).attr("transform","translate(12,12) rotate(45, "+nt+","+b+")"),mt.attr("cx",nt+4/2).attr("cy",b).attr("transform","translate(12,12) rotate(45, "+nt+","+b+")"),ct.attr("x",nt+5).attr("y",b+3).attr("transform","translate(14,14) rotate(45, "+nt+","+b+")"))}}b+=F+M,b>sB&&(sB=b)})},fYe=(i,a,f,g,w)=>{const b=(f1==="TB"?f.x<g.x:f.y<g.y)?a.branch:i.branch,E=_=>_.branch===b,S=_=>_.seq>i.seq&&_.seq<a.seq;return Object.values(w).some(_=>S(_)&&E(_))},aB=(i,a,f=0)=>{con [...]
+`,"id:",a,f),iB=g.db.getCommits();const b=g.db.getBranchesAsObjArray();f1=g.db.getDirection();const E=Tr(`[id="${a}"]`);let S=0;b.forEach((_,M)=>{const F=Lzt(_.name),R=E.append("g"),B=R.insert("g").attr("class","branchLabel"),z=B.insert("g").attr("class","label branch-label");z.node().appendChild(F);let Y=F.getBBox();Cf[_.name]={pos:S,index:M},S+=50+(v.rotateCommitLabel?40:0)+(f1==="TB"?Y.width/2:0),z.remove(),B.remove(),R.remove()}),Dzt(E,iB,!1),v.showBranches&&pYe(E,b),gYe(E,iB),Dzt(E, [...]
+  .commit-id,
+  .commit-msg,
+  .branch-label {
+    fill: lightgrey;
+    color: lightgrey;
+    font-family: 'trebuchet ms', verdana, arial, sans-serif;
+    font-family: var(--mermaid-font-family);
+  }
+  ${[0,1,2,3,4,5,6,7].map(a=>`
+        .branch-label${a} { fill: ${i["gitBranchLabel"+a]}; }
+        .commit${a} { stroke: ${i["git"+a]}; fill: ${i["git"+a]}; }
+        .commit-highlight${a} { stroke: ${i["gitInv"+a]}; fill: ${i["gitInv"+a]}; }
+        .label${a}  { fill: ${i["git"+a]}; }
+        .arrow${a} { stroke: ${i["git"+a]}; }
+        `).join(`
+`)}
+
+  .branch {
+    stroke-width: 1;
+    stroke: ${i.lineColor};
+    stroke-dasharray: 2;
+  }
+  .commit-label { font-size: ${i.commitLabelFontSize}; fill: ${i.commitLabelColor};}
+  .commit-label-bkg { font-size: ${i.commitLabelFontSize}; fill: ${i.commitLabelBackground}; opacity: 0.5; }
+  .tag-label { font-size: ${i.tagLabelFontSize}; fill: ${i.tagLabelColor};}
+  .tag-label-bkg { fill: ${i.tagLabelBackground}; stroke: ${i.tagLabelBorder}; }
+  .tag-hole { fill: ${i.textColor}; }
+
+  .commit-merge {
+    stroke: ${i.primaryColor};
+    fill: ${i.primaryColor};
+  }
+  .commit-reverse {
+    stroke: ${i.primaryColor};
+    fill: ${i.primaryColor};
+    stroke-width: 3;
+  }
+  .commit-highlight-outer {
+  }
+  .commit-highlight-inner {
+    stroke: ${i.primaryColor};
+    fill: ${i.primaryColor};
+  }
+
+  .arrow { stroke-width: 8; stroke-linecap: round; fill: none}
+  .gitTitleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.textColor};
+  }
+`}},Symbol.toStringTag,{value:"Module"}));var cbt=function(){var i=function(K,yt,V,Ot){for(V=V||{},Ot=K.length;Ot--;V[K[Ot]]=yt);return V},a=[6,8,10,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,32,33,35,37],f=[1,25],g=[1,26],w=[1,27],v=[1,28],b=[1,29],E=[1,30],S=[1,31],_=[1,9],M=[1,10],F=[1,11],R=[1,12],B=[1,13],z=[1,14],Y=[1,15],nt=[1,16],ot=[1,18],ft=[1,19],wt=[1,20],mt=[1,21],ct=[1,22],rt=[1,24],it=[1,32],gt={trace:function(){},yy:{},symbols_:{error:2,start:3,gantt:4,document [...]
+`+Ae.showPosition()+`
+Expecting `+mi.join(", ")+", got '"+(this.terminals_[Xt]||Xt)+"'":ga="Parse error on line "+(kt+1)+": Unexpected "+(Xt==Mt?"end of input":"'"+(this.terminals_[Xt]||Xt)+"'"),this.parseError(ga,{text:Ae.match,token:this.terminals_[Xt]||Xt,line:Ae.yylineno,loc:sn,expected:mi})}if(xe[0]instanceof Array&&xe.length>1)throw new Error("Parse Error: multiple actions possible at state: "+_e+", token: "+Xt);switch(xe[0]){case 1:Ot.push(Xt),jt.push(Ae.yytext),xt.push(Ae.yylloc),Ot.push(xe[1]),Xt=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(yt){this.unput(this.match.slice(yt))},pastInput:function(){var yt=this.matched.substr(0,this.matched.length-this.match.length);return(yt.length>20?"...":"")+yt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var yt=this.match;return yt.length<20&&(yt+=this._input.substr(0,20-yt.length)),(yt.substr(0,20)+(yt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var yt=this.pastInput [...]
+`+V+"^"},test_match:function(yt,V){var Ot,It,jt;if(this.options.backtrack_lexer&&(jt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.range [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var V=this.next();return V||this.lex()},begin:function(V){this.conditionStack.push(V)},popState:function(){var V=this.conditionStack.length-1;return V>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rul [...]
+  .mermaid-main-font {
+    font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
+  }
+
+  .exclude-range {
+    fill: ${i.excludeBkgColor};
+  }
+
+  .section {
+    stroke: none;
+    opacity: 0.2;
+  }
+
+  .section0 {
+    fill: ${i.sectionBkgColor};
+  }
+
+  .section2 {
+    fill: ${i.sectionBkgColor2};
+  }
+
+  .section1,
+  .section3 {
+    fill: ${i.altSectionBkgColor};
+    opacity: 0.2;
+  }
+
+  .sectionTitle0 {
+    fill: ${i.titleColor};
+  }
+
+  .sectionTitle1 {
+    fill: ${i.titleColor};
+  }
+
+  .sectionTitle2 {
+    fill: ${i.titleColor};
+  }
+
+  .sectionTitle3 {
+    fill: ${i.titleColor};
+  }
+
+  .sectionTitle {
+    text-anchor: start;
+    font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
+  }
+
+
+  /* Grid and axis */
+
+  .grid .tick {
+    stroke: ${i.gridColor};
+    opacity: 0.8;
+    shape-rendering: crispEdges;
+  }
+
+  .grid .tick text {
+    font-family: ${i.fontFamily};
+    fill: ${i.textColor};
+  }
+
+  .grid path {
+    stroke-width: 0;
+  }
+
+
+  /* Today line */
+
+  .today {
+    fill: none;
+    stroke: ${i.todayLineColor};
+    stroke-width: 2px;
+  }
+
+
+  /* Task styling */
+
+  /* Default task */
+
+  .task {
+    stroke-width: 2;
+  }
+
+  .taskText {
+    text-anchor: middle;
+    font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
+  }
+
+  .taskTextOutsideRight {
+    fill: ${i.taskTextDarkColor};
+    text-anchor: start;
+    font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
+  }
+
+  .taskTextOutsideLeft {
+    fill: ${i.taskTextDarkColor};
+    text-anchor: end;
+  }
+
+
+  /* Special case clickable */
+
+  .task.clickable {
+    cursor: pointer;
+  }
+
+  .taskText.clickable {
+    cursor: pointer;
+    fill: ${i.taskTextClickableColor} !important;
+    font-weight: bold;
+  }
+
+  .taskTextOutsideLeft.clickable {
+    cursor: pointer;
+    fill: ${i.taskTextClickableColor} !important;
+    font-weight: bold;
+  }
+
+  .taskTextOutsideRight.clickable {
+    cursor: pointer;
+    fill: ${i.taskTextClickableColor} !important;
+    font-weight: bold;
+  }
+
+
+  /* Specific task settings for the sections*/
+
+  .taskText0,
+  .taskText1,
+  .taskText2,
+  .taskText3 {
+    fill: ${i.taskTextColor};
+  }
+
+  .task0,
+  .task1,
+  .task2,
+  .task3 {
+    fill: ${i.taskBkgColor};
+    stroke: ${i.taskBorderColor};
+  }
+
+  .taskTextOutside0,
+  .taskTextOutside2
+  {
+    fill: ${i.taskTextOutsideColor};
+  }
+
+  .taskTextOutside1,
+  .taskTextOutside3 {
+    fill: ${i.taskTextOutsideColor};
+  }
+
+
+  /* Active task */
+
+  .active0,
+  .active1,
+  .active2,
+  .active3 {
+    fill: ${i.activeTaskBkgColor};
+    stroke: ${i.activeTaskBorderColor};
+  }
+
+  .activeText0,
+  .activeText1,
+  .activeText2,
+  .activeText3 {
+    fill: ${i.taskTextDarkColor} !important;
+  }
+
+
+  /* Completed task */
+
+  .done0,
+  .done1,
+  .done2,
+  .done3 {
+    stroke: ${i.doneTaskBorderColor};
+    fill: ${i.doneTaskBkgColor};
+    stroke-width: 2;
+  }
+
+  .doneText0,
+  .doneText1,
+  .doneText2,
+  .doneText3 {
+    fill: ${i.taskTextDarkColor} !important;
+  }
+
+
+  /* Tasks on the critical line */
+
+  .crit0,
+  .crit1,
+  .crit2,
+  .crit3 {
+    stroke: ${i.critBorderColor};
+    fill: ${i.critBkgColor};
+    stroke-width: 2;
+  }
+
+  .activeCrit0,
+  .activeCrit1,
+  .activeCrit2,
+  .activeCrit3 {
+    stroke: ${i.critBorderColor};
+    fill: ${i.activeTaskBkgColor};
+    stroke-width: 2;
+  }
+
+  .doneCrit0,
+  .doneCrit1,
+  .doneCrit2,
+  .doneCrit3 {
+    stroke: ${i.critBorderColor};
+    fill: ${i.doneTaskBkgColor};
+    stroke-width: 2;
+    cursor: pointer;
+    shape-rendering: crispEdges;
+  }
+
+  .milestone {
+    transform: rotate(45deg) scale(0.8,0.8);
+  }
+
+  .milestoneText {
+    font-style: italic;
+  }
+  .doneCritText0,
+  .doneCritText1,
+  .doneCritText2,
+  .doneCritText3 {
+    fill: ${i.taskTextDarkColor} !important;
+  }
+
+  .activeCritText0,
+  .activeCritText1,
+  .activeCritText2,
+  .activeCritText3 {
+    fill: ${i.taskTextDarkColor} !important;
+  }
+
+  .titleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.titleColor||i.textColor};
+    font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
+  }
+`}},Symbol.toStringTag,{value:"Module"}));var xbt=function(){var i=function(v,b,E,S){for(E=E||{},S=v.length;S--;E[v[S]]=b);return E},a=[6,9,10],f={trace:function(){},yy:{},symbols_:{error:2,start:3,info:4,document:5,EOF:6,line:7,statement:8,NL:9,showInfo:10,$accept:0,$end:1},terminals_:{2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],performAction:function(b,E,S,_,M,F,R){switch(F.length-1,M){case 1:return _;case 4:break;case 6:_.setIn [...]
+`+wt.showPosition()+`
+Expecting `+xt.join(", ")+", got '"+(this.terminals_[Q]||Q)+"'":Ft="Parse error on line "+(z+1)+": Unexpected "+(Q==ot?"end of input":"'"+(this.terminals_[Q]||Q)+"'"),this.parseError(Ft,{text:wt.match,token:this.terminals_[Q]||Q,line:wt.yylineno,loc:rt,expected:xt})}if(K[0]instanceof Array&&K.length>1)throw new Error("Parse Error: multiple actions possible at state: "+dt+", token: "+Q);switch(K[0]){case 1:S.push(Q),M.push(wt.yytext),F.push(wt.yylloc),S.push(K[1]),Q=null,Y=wt.yyleng,B=wt. [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(b){this.unput(this.match.slice(b))},pastInput:function(){var b=this.matched.substr(0,this.matched.length-this.match.length);return(b.length>20?"...":"")+b.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var b=this.match;return b.length<20&&(b+=this._input.substr(0,20-b.length)),(b.substr(0,20)+(b.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var b=this.pastInput(),E=new Arr [...]
+`+E+"^"},test_match:function(b,E){var S,_,M;if(this.options.backtrack_lexer&&(M={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(M [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var E=this.next();return E||this.lex()},begin:function(E){this.conditionStack.push(E)},popState:function(){var E=this.conditionStack.length-1;return E>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rul [...]
+`+i);const g=FF(a);sg(g,100,400,!0),g.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${f}`)}}}},Symbol.toStringTag,{value:"Module"}));var Ebt=function(){var i=function(wt,mt,ct,rt){for(ct=ct||{},rt=wt.length;rt--;ct[wt[rt]]=mt);return ct},a=[1,3],f=[1,4],g=[1,5],w=[1,6],v=[1,10,12,14,16,18,19,20,21,22],b=[2,4],E=[1,5,10,12,14,16,18,19,20,21,22],S=[20,21,22],_=[2,7],M=[1,12],F=[1,13],R=[1,14],B=[1,15] [...]
+`+xt.showPosition()+`
+Expecting `+Xt.join(", ")+", got '"+(this.terminals_[Mt]||Mt)+"'":_e="Parse error on line "+(yt+1)+": Unexpected "+(Mt==It?"end of input":"'"+(this.terminals_[Mt]||Mt)+"'"),this.parseError(_e,{text:xt.match,token:this.terminals_[Mt]||Mt,line:xt.yylineno,loc:kt,expected:Xt})}if(Ae[0]instanceof Array&&Ae.length>1)throw new Error("Parse Error: multiple actions possible at state: "+pn+", token: "+Mt);switch(Ae[0]){case 1:rt.push(Mt),gt.push(xt.yytext),Q.push(xt.yylloc),rt.push(Ae[1]),Mt=null [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(mt){this.unput(this.match.slice(mt))},pastInput:function(){var mt=this.matched.substr(0,this.matched.length-this.match.length);return(mt.length>20?"...":"")+mt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var mt=this.match;return mt.length<20&&(mt+=this._input.substr(0,20-mt.length)),(mt.substr(0,20)+(mt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var mt=this.pastInput [...]
+`+ct+"^"},test_match:function(mt,ct){var rt,it,gt;if(this.options.backtrack_lexer&&(gt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var ct=this.next();return ct||this.lex()},begin:function(ct){this.conditionStack.push(ct)},popState:function(){var ct=this.conditionStack.length-1;return ct>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+  .pieCircle{
+    stroke: ${i.pieStrokeColor};
+    stroke-width : ${i.pieStrokeWidth};
+    opacity : ${i.pieOpacity};
+  }
+  .pieOuterCircle{
+    stroke: ${i.pieOuterStrokeColor};
+    stroke-width: ${i.pieOuterStrokeWidth};
+    fill: none;
+  }
+  .pieTitleText {
+    text-anchor: middle;
+    font-size: ${i.pieTitleTextSize};
+    fill: ${i.pieTitleTextColor};
+    font-family: ${i.fontFamily};
+  }
+  .slice {
+    font-family: ${i.fontFamily};
+    fill: ${i.pieSectionTextColor};
+    font-size:${i.pieSectionTextSize};
+    // fill: white;
+  }
+  .legend text {
+    fill: ${i.pieLegendTextColor};
+    font-family: ${i.fontFamily};
+    font-size: ${i.pieLegendTextSize};
+  }
+`,fXe=i=>{const a=Object.entries(i).map(g=>({label:g[0],value:g[1]})).sort((g,w)=>w.value-g.value);return pDe().value(g=>g.value)(a)},dXe=Object.freeze(Object.defineProperty({__proto__:null,diagram:{parser:cXe,db:lXe,renderer:{draw:(i,a,f,g)=>{Kt.debug(`rendering pie chart
+`+i);const w=g.db,v=Re(),b=MF(w.getConfig(),v.pie),E=40,S=18,_=4,M=450,F=M,R=FF(a),B=R.append("g"),z=w.getSections();B.attr("transform","translate("+F/2+","+M/2+")");const{themeVariables:Y}=v;let[nt]=k9(Y.pieOuterStrokeWidth);nt??(nt=2);const ot=b.textPosition,ft=Math.min(F,M)/2-E,wt=YL().innerRadius(0).outerRadius(ft),mt=YL().innerRadius(ft*ot).outerRadius(ft*ot);B.append("circle").attr("cx",0).attr("cy",0).attr("r",ft+nt/2).attr("class","pieOuterCircle");const ct=fXe(z),rt=[Y.pie1,Y.pi [...]
+`+mi.showPosition()+`
+Expecting `+xo.join(", ")+", got '"+(this.terminals_[Nn]||Nn)+"'":ko="Parse error on line "+(Mn+1)+": Unexpected "+(Nn==we?"end of input":"'"+(this.terminals_[Nn]||Nn)+"'"),this.parseError(ko,{text:mi.match,token:this.terminals_[Nn]||Nn,line:mi.yylineno,loc:za,expected:xo})}if(ke[0]instanceof Array&&ke.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Bn+", token: "+Nn);switch(ke[0]){case 1:sn.push(Nn),Ne.push(mi.yytext),Xt.push(mi.yylloc),sn.push(ke[1]),Nn=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(ye){this.unput(this.match.slice(ye))},pastInput:function(){var ye=this.matched.substr(0,this.matched.length-this.match.length);return(ye.length>20?"...":"")+ye.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var ye=this.match;return ye.length<20&&(ye+=this._input.substr(0,20-ye.length)),(ye.substr(0,20)+(ye.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var ye=this.pastInput [...]
+`+tn+"^"},test_match:function(ye,tn){var sn,be,Ne;if(this.options.backtrack_lexer&&(Ne={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var tn=this.next();return tn||this.lex()},begin:function(tn){this.conditionStack.push(tn)},popState:function(){var tn=this.conditionStack.length-1;return tn>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+`+i);const S=E.securityLevel;let _;S==="sandbox"&&(_=Tr("#i"+a));const F=Tr(S==="sandbox"?_.nodes()[0].contentDocument.body:"body").select(`[id="${a}"]`),R=F.append("g").attr("class","main"),B=((gt=E.quadrantChart)==null?void 0:gt.chartWidth)||500,z=((Q=E.quadrantChart)==null?void 0:Q.chartHeight)||500;sg(F,z,B,((dt=E.quadrantChart)==null?void 0:dt.useMaxWidth)||!0),F.attr("viewBox","0 0 "+B+" "+z),g.db.setHeight(z),g.db.setWidth(B);const Y=g.db.getQuadrantData(),nt=R.append("g").attr("c [...]
+`+be.showPosition()+`
+Expecting `+br.join(", ")+", got '"+(this.terminals_[ce]||ce)+"'":An="Parse error on line "+(pn+1)+": Unexpected "+(ce==tn?"end of input":"'"+(this.terminals_[ce]||ce)+"'"),this.parseError(An,{text:be.match,token:this.terminals_[ce]||ce,line:be.yylineno,loc:_e,expected:br})}if(we[0]instanceof Array&&we.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Br+", token: "+ce);switch(we[0]){case 1:Ft.push(ce),kt.push(be.yytext),Gt.push(be.yylloc),Ft.push(we[1]),ce=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(jt){this.unput(this.match.slice(jt))},pastInput:function(){var jt=this.matched.substr(0,this.matched.length-this.match.length);return(jt.length>20?"...":"")+jt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var jt=this.match;return jt.length<20&&(jt+=this._input.substr(0,20-jt.length)),(jt.substr(0,20)+(jt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var jt=this.pastInput [...]
+`+xt+"^"},test_match:function(jt,xt){var Ft,Ct,kt;if(this.options.backtrack_lexer&&(kt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var xt=this.next();return xt||this.lex()},begin:function(xt){this.conditionStack.push(xt)},popState:function(){var xt=this.conditionStack.length-1;return xt>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+`+i);const M=FF(a),F=M.append("g").attr("class","main"),R=F.append("rect").attr("width",b.width).attr("height",b.height).attr("class","background");sg(M,b.height,b.width,!0),M.attr("viewBox",`0 0 ${b.width} ${b.height}`),R.attr("fill",v.backgroundColor),w.setTmpSVGG(M.append("g").attr("class","mermaid-tmp-group"));const B=w.getDrawableElem(),z={};function Y(nt){let ot=F,ft="";for(const[wt]of nt.entries()){let mt=F;wt>0&&z[ft]&&(mt=z[ft]),ft+=nt[wt],ot=z[ft],ot||(ot=z[ft]=mt.append("g").a [...]
+`+mi.showPosition()+`
+Expecting `+xo.join(", ")+", got '"+(this.terminals_[Nn]||Nn)+"'":ko="Parse error on line "+(Mn+1)+": Unexpected "+(Nn==we?"end of input":"'"+(this.terminals_[Nn]||Nn)+"'"),this.parseError(ko,{text:mi.match,token:this.terminals_[Nn]||Nn,line:mi.yylineno,loc:za,expected:xo})}if(ke[0]instanceof Array&&ke.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Bn+", token: "+Nn);switch(ke[0]){case 1:sn.push(Nn),Ne.push(mi.yytext),Xt.push(mi.yylloc),sn.push(ke[1]),Nn=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(ye){this.unput(this.match.slice(ye))},pastInput:function(){var ye=this.matched.substr(0,this.matched.length-this.match.length);return(ye.length>20?"...":"")+ye.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var ye=this.match;return ye.length<20&&(ye+=this._input.substr(0,20-ye.length)),(ye.substr(0,20)+(ye.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var ye=this.pastInput [...]
+`+tn+"^"},test_match:function(ye,tn){var sn,be,Ne;if(this.options.backtrack_lexer&&(Ne={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var tn=this.next();return tn||this.lex()},begin:function(tn){this.conditionStack.push(tn)},popState:function(){var tn=this.conditionStack.length-1;return tn>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+
+  marker {
+    fill: ${i.relationColor};
+    stroke: ${i.relationColor};
+  }
+
+  marker.cross {
+    stroke: ${i.lineColor};
+  }
+
+  svg {
+    font-family: ${i.fontFamily};
+    font-size: ${i.fontSize};
+  }
+
+  .reqBox {
+    fill: ${i.requirementBackground};
+    fill-opacity: 1.0;
+    stroke: ${i.requirementBorderColor};
+    stroke-width: ${i.requirementBorderSize};
+  }
+  
+  .reqTitle, .reqLabel{
+    fill:  ${i.requirementTextColor};
+  }
+  .reqLabelBox {
+    fill: ${i.relationLabelBackground};
+    fill-opacity: 1.0;
+  }
+
+  .req-title-line {
+    stroke: ${i.requirementBorderColor};
+    stroke-width: ${i.requirementBorderSize};
+  }
+  .relationshipLine {
+    stroke: ${i.relationColor};
+    stroke-width: 1;
+  }
+  .relationshipLabel {
+    fill: ${i.relationLabelColor};
+  }
+
+`,Ibt={CONTAINS:"contains",ARROW:"arrow"},oqt={ReqMarkers:Ibt,insertLineEndings:(i,a)=>{let f=i.append("defs").append("marker").attr("id",Ibt.CONTAINS+"_line_ending").attr("refX",0).attr("refY",a.line_height/2).attr("markerWidth",a.line_height).attr("markerHeight",a.line_height).attr("orient","auto").append("g");f.append("circle").attr("cx",a.line_height/2).attr("cy",a.line_height/2).attr("r",a.line_height/2).attr("fill","none"),f.append("line").attr("x1",0).attr("x2",a.line_height).attr [...]
+      L${a.line_height},${a.line_height/2}
+      M${a.line_height},${a.line_height/2}
+      L0,${a.line_height}`).attr("stroke-width",1)}};let Xh={},cqt=0;const uqt=(i,a)=>i.insert("rect","#"+a).attr("class","req reqBox").attr("x",0).attr("y",0).attr("width",Xh.rect_min_width+"px").attr("height",Xh.rect_min_height+"px"),lqt=(i,a,f)=>{let g=Xh.rect_min_width/2,w=i.append("text").attr("class","req reqLabel reqTitle").attr("id",a).attr("x",g).attr("y",Xh.rect_padding).attr("dominant-baseline","hanging"),v=0;f.forEach(_=>{v==0?w.append("tspan").attr("text-anchor","middle").at [...]
+`+br.showPosition()+`
+Expecting `+ds.join(", ")+", got '"+(this.terminals_[Je]||Je)+"'":Fu="Parse error on line "+(Fi+1)+": Unexpected "+(Je==Ss?"end of input":"'"+(this.terminals_[Je]||Je)+"'"),this.parseError(Fu,{text:br.match,token:this.terminals_[Je]||Je,line:br.yylineno,loc:Bn,expected:ds})}if(xn[0]instanceof Array&&xn.length>1)throw new Error("Parse Error: multiple actions possible at state: "+an+", token: "+Je);switch(xn[0]){case 1:_e.push(Je),Mn.push(br.yytext),ce.push(br.yylloc),_e.push(xn[1]),Je=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(Ne){this.unput(this.match.slice(Ne))},pastInput:function(){var Ne=this.matched.substr(0,this.matched.length-this.match.length);return(Ne.length>20?"...":"")+Ne.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var Ne=this.match;return Ne.length<20&&(Ne+=this._input.substr(0,20-Ne.length)),(Ne.substr(0,20)+(Ne.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var Ne=this.pastInput [...]
+`+Xt+"^"},test_match:function(Ne,Xt){var _e,xe,Mn;if(this.options.backtrack_lexer&&(Mn={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var Xt=this.next();return Xt||this.lex()},begin:function(Xt){this.conditionStack.push(Xt)},popState:function(){var Xt=this.conditionStack.length-1;return Xt>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+    stroke: ${i.actorBorder};
+    fill: ${i.actorBkg};
+  }
+
+  text.actor > tspan {
+    fill: ${i.actorTextColor};
+    stroke: none;
+  }
+
+  .actor-line {
+    stroke: ${i.actorLineColor};
+  }
+
+  .messageLine0 {
+    stroke-width: 1.5;
+    stroke-dasharray: none;
+    stroke: ${i.signalColor};
+  }
+
+  .messageLine1 {
+    stroke-width: 1.5;
+    stroke-dasharray: 2, 2;
+    stroke: ${i.signalColor};
+  }
+
+  #arrowhead path {
+    fill: ${i.signalColor};
+    stroke: ${i.signalColor};
+  }
+
+  .sequenceNumber {
+    fill: ${i.sequenceNumberColor};
+  }
+
+  #sequencenumber {
+    fill: ${i.signalColor};
+  }
+
+  #crosshead path {
+    fill: ${i.signalColor};
+    stroke: ${i.signalColor};
+  }
+
+  .messageText {
+    fill: ${i.signalTextColor};
+    stroke: none;
+  }
+
+  .labelBox {
+    stroke: ${i.labelBoxBorderColor};
+    fill: ${i.labelBoxBkgColor};
+  }
+
+  .labelText, .labelText > tspan {
+    fill: ${i.labelTextColor};
+    stroke: none;
+  }
+
+  .loopText, .loopText > tspan {
+    fill: ${i.loopTextColor};
+    stroke: none;
+  }
+
+  .loopLine {
+    stroke-width: 2px;
+    stroke-dasharray: 2, 2;
+    stroke: ${i.labelBoxBorderColor};
+    fill: ${i.labelBoxBorderColor};
+  }
+
+  .note {
+    //stroke: #decc93;
+    stroke: ${i.noteBorderColor};
+    fill: ${i.noteBkgColor};
+  }
+
+  .noteText, .noteText > tspan {
+    fill: ${i.noteTextColor};
+    stroke: none;
+  }
+
+  .activation0 {
+    fill: ${i.activationBkgColor};
+    stroke: ${i.activationBorderColor};
+  }
+
+  .activation1 {
+    fill: ${i.activationBkgColor};
+    stroke: ${i.activationBorderColor};
+  }
+
+  .activation2 {
+    fill: ${i.activationBkgColor};
+    stroke: ${i.activationBorderColor};
+  }
+
+  .actorPopupMenu {
+    position: absolute;
+  }
+
+  .actorPopupMenuPanel {
+    position: absolute;
+    fill: ${i.actorBkg};
+    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+    filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
+}
+  .actor-man line {
+    stroke: ${i.actorBorder};
+    fill: ${i.actorBkg};
+  }
+  .actor-man circle, line {
+    stroke: ${i.actorBorder};
+    fill: ${i.actorBkg};
+    stroke-width: 2px;
+  }
+`,B9=18*2,vqt="actor-top",yqt="actor-bottom",Fbt=function(i,a){return LY(i,a)},FQe=function(i,a,f,g,w){if(a.links===void 0||a.links===null||Object.keys(a.links).length===0)return{height:0,width:0};const v=a.links,b=a.actorCnt,E=a.rectData;var S="none";w&&(S="block !important");const _=i.append("g");_.attr("id","actor"+b+"_popup"),_.attr("class","actorPopupMenu"),_.attr("display",S);var M="";E.class!==void 0&&(M=" "+E.class);let F=E.width>f?E.width:f;const R=_.append("rect");if(R.attr("cl [...]
+`+Ha.showPosition()+`
+Expecting `+ca.join(", ")+", got '"+(this.terminals_[ll]||ll)+"'":ya="Parse error on line "+(xn+1)+": Unexpected "+(ll==ko?"end of input":"'"+(this.terminals_[ll]||ll)+"'"),this.parseError(ya,{text:Ha.match,token:this.terminals_[ll]||ll,line:Ha.yylineno,loc:Zs,expected:ca})}if(wc[0]instanceof Array&&wc.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Ic+", token: "+ll);switch(wc[0]){case 1:Nn.push(ll),ke.push(Ha.yytext),pe.push(Ha.yylloc),Nn.push(wc[1]),ll=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(br){this.unput(this.match.slice(br))},pastInput:function(){var br=this.matched.substr(0,this.matched.length-this.match.length);return(br.length>20?"...":"")+br.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var br=this.match;return br.length<20&&(br+=this._input.substr(0,20-br.length)),(br.substr(0,20)+(br.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var br=this.pastInput [...]
+`+An+"^"},test_match:function(br,An){var Nn,Bn,ke;if(this.options.backtrack_lexer&&(ke={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var An=this.next();return An||this.lex()},begin:function(An){this.conditionStack.push(An)},popState:function(){var An=this.conditionStack.length-1;return An>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+  fill: ${i.nodeBorder||i.classText};
+  stroke: none;
+  font-family: ${i.fontFamily};
+  font-size: 10px;
+
+  .title {
+    font-weight: bolder;
+  }
+
+}
+
+.nodeLabel, .edgeLabel {
+  color: ${i.classText};
+}
+.edgeLabel .label rect {
+  fill: ${i.mainBkg};
+}
+.label text {
+  fill: ${i.classText};
+}
+.edgeLabel .label span {
+  background: ${i.mainBkg};
+}
+
+.classTitle {
+  font-weight: bolder;
+}
+.node rect,
+  .node circle,
+  .node ellipse,
+  .node polygon,
+  .node path {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+    stroke-width: 1px;
+  }
+
+
+.divider {
+  stroke: ${i.nodeBorder};
+  stroke-width: 1;
+}
+
+g.clickable {
+  cursor: pointer;
+}
+
+g.classGroup rect {
+  fill: ${i.mainBkg};
+  stroke: ${i.nodeBorder};
+}
+
+g.classGroup line {
+  stroke: ${i.nodeBorder};
+  stroke-width: 1;
+}
+
+.classLabel .box {
+  stroke: none;
+  stroke-width: 0;
+  fill: ${i.mainBkg};
+  opacity: 0.5;
+}
+
+.classLabel .label {
+  fill: ${i.nodeBorder};
+  font-size: 10px;
+}
+
+.relation {
+  stroke: ${i.lineColor};
+  stroke-width: 1;
+  fill: none;
+}
+
+.dashed-line{
+  stroke-dasharray: 3;
+}
+
+.dotted-line{
+  stroke-dasharray: 1 2;
+}
+
+#compositionStart, .composition {
+  fill: ${i.lineColor} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#compositionEnd, .composition {
+  fill: ${i.lineColor} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#dependencyStart, .dependency {
+  fill: ${i.lineColor} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#dependencyStart, .dependency {
+  fill: ${i.lineColor} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#extensionStart, .extension {
+  fill: transparent !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#extensionEnd, .extension {
+  fill: transparent !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#aggregationStart, .aggregation {
+  fill: transparent !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#aggregationEnd, .aggregation {
+  fill: transparent !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#lollipopStart, .lollipop {
+  fill: ${i.mainBkg} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+#lollipopEnd, .lollipop {
+  fill: ${i.mainBkg} !important;
+  stroke: ${i.lineColor} !important;
+  stroke-width: 1;
+}
+
+.edgeTerminals {
+  font-size: 11px;
+  line-height: initial;
+}
+
+.classTitleText {
+  text-anchor: middle;
+  font-size: 18px;
+  fill: ${i.textColor};
+}
+`;let Bqt=0;const IJe=function(i,a,f,g,w){const v=function(ft){switch(ft){case w.db.relationType.AGGREGATION:return"aggregation";case w.db.relationType.EXTENSION:return"extension";case w.db.relationType.COMPOSITION:return"composition";case w.db.relationType.DEPENDENCY:return"dependency";case w.db.relationType.LOLLIPOP:return"lollipop"}};a.points=a.points.filter(ft=>!Number.isNaN(ft.y));const b=a.points,E=Ax().x(function(ft){return ft.x}).y(function(ft){return ft.y}).curve(wF),S=i.append( [...]
+`);S.forEach(function(R){Kt.debug(`Adding line: ${R}`),E.append("tspan").text(R).attr("class","title").attr("dy",f.textHeight)});const _=b.node().getBBox(),F=b.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",_.width+2*f.padding).attr("height",_.height+S.length*f.textHeight+f.padding+.5*f.dividerMargin).node().getBBox().width;return E.node().childNodes.forEach(function(R){R.setAttribute("x",(F-R.getBBox().width)/2)}),v.width=F,v.height=_.height+S.length*f.textHeight+f.p [...]
+`),w.style===void 0&&(v.style=v.style||"stroke: #333; stroke-width: 1.5px;fill:none"),v.labelStyle=v.labelStyle.replace("color:","fill:"))),a.setEdge(w.id1,w.id2,v,g)})},$Je=function(i){Gbt={...Gbt,...i}},zJe=async function(i,a,f,g){Kt.info("Drawing class - ",a);const w=Re().flowchart??Re().class,v=Re().securityLevel;Kt.info("config:",w);const b=(w==null?void 0:w.nodeSpacing)??50,E=(w==null?void 0:w.rankSpacing)??50,S=new l1({multigraph:!0,compound:!0}).setGraph({rankdir:g.db.getDirectio [...]
+`+Xt.showPosition()+`
+Expecting `+Nn.join(", ")+", got '"+(this.terminals_[we]||we)+"'":Bn="Parse error on line "+(ye+1)+": Unexpected "+(we==be?"end of input":"'"+(this.terminals_[we]||we)+"'"),this.parseError(Bn,{text:Xt.match,token:this.terminals_[we]||we,line:Xt.yylineno,loc:Mn,expected:Nn})}if(mi[0]instanceof Array&&mi.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Fi+", token: "+we);switch(mi[0]){case 1:kt.push(we),Fe.push(Xt.yytext),Mt.push(Xt.yylloc),kt.push(mi[1]),we=nul [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(Ft){this.unput(this.match.slice(Ft))},pastInput:function(){var Ft=this.matched.substr(0,this.matched.length-this.match.length);return(Ft.length>20?"...":"")+Ft.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var Ft=this.match;return Ft.length<20&&(Ft+=this._input.substr(0,20-Ft.length)),(Ft.substr(0,20)+(Ft.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var Ft=this.pastInput [...]
+`+Ct+"^"},test_match:function(Ft,Ct){var kt,Gt,Fe;if(this.options.backtrack_lexer&&(Fe={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var Ct=this.next();return Ct||this.lex()},begin:function(Ct){this.conditionStack.push(Ct)},popState:function(){var Ct=this.conditionStack.length-1;return Ct>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+defs #statediagram-barbEnd {
+    fill: ${i.transitionColor};
+    stroke: ${i.transitionColor};
+  }
+g.stateGroup text {
+  fill: ${i.nodeBorder};
+  stroke: none;
+  font-size: 10px;
+}
+g.stateGroup text {
+  fill: ${i.textColor};
+  stroke: none;
+  font-size: 10px;
+
+}
+g.stateGroup .state-title {
+  font-weight: bolder;
+  fill: ${i.stateLabelColor};
+}
+
+g.stateGroup rect {
+  fill: ${i.mainBkg};
+  stroke: ${i.nodeBorder};
+}
+
+g.stateGroup line {
+  stroke: ${i.lineColor};
+  stroke-width: 1;
+}
+
+.transition {
+  stroke: ${i.transitionColor};
+  stroke-width: 1;
+  fill: none;
+}
+
+.stateGroup .composit {
+  fill: ${i.background};
+  border-bottom: 1px
+}
+
+.stateGroup .alt-composit {
+  fill: #e0e0e0;
+  border-bottom: 1px
+}
+
+.state-note {
+  stroke: ${i.noteBorderColor};
+  fill: ${i.noteBkgColor};
+
+  text {
+    fill: ${i.noteTextColor};
+    stroke: none;
+    font-size: 10px;
+  }
+}
+
+.stateLabel .box {
+  stroke: none;
+  stroke-width: 0;
+  fill: ${i.mainBkg};
+  opacity: 0.5;
+}
+
+.edgeLabel .label rect {
+  fill: ${i.labelBackgroundColor};
+  opacity: 0.5;
+}
+.edgeLabel .label text {
+  fill: ${i.transitionLabelColor||i.tertiaryTextColor};
+}
+.label div .edgeLabel {
+  color: ${i.transitionLabelColor||i.tertiaryTextColor};
+}
+
+.stateLabel text {
+  fill: ${i.stateLabelColor};
+  font-size: 10px;
+  font-weight: bold;
+}
+
+.node circle.state-start {
+  fill: ${i.specialStateColor};
+  stroke: ${i.specialStateColor};
+}
+
+.node .fork-join {
+  fill: ${i.specialStateColor};
+  stroke: ${i.specialStateColor};
+}
+
+.node circle.state-end {
+  fill: ${i.innerEndBackground};
+  stroke: ${i.background};
+  stroke-width: 1.5
+}
+.end-state-inner {
+  fill: ${i.compositeBackground||i.background};
+  // stroke: ${i.background};
+  stroke-width: 1.5
+}
+
+.node rect {
+  fill: ${i.stateBkg||i.mainBkg};
+  stroke: ${i.stateBorder||i.nodeBorder};
+  stroke-width: 1px;
+}
+.node polygon {
+  fill: ${i.mainBkg};
+  stroke: ${i.stateBorder||i.nodeBorder};;
+  stroke-width: 1px;
+}
+#statediagram-barbEnd {
+  fill: ${i.lineColor};
+}
+
+.statediagram-cluster rect {
+  fill: ${i.compositeTitleBackground};
+  stroke: ${i.stateBorder||i.nodeBorder};
+  stroke-width: 1px;
+}
+
+.cluster-label, .nodeLabel {
+  color: ${i.stateLabelColor};
+}
+
+.statediagram-cluster rect.outer {
+  rx: 5px;
+  ry: 5px;
+}
+.statediagram-state .divider {
+  stroke: ${i.stateBorder||i.nodeBorder};
+}
+
+.statediagram-state .title-state {
+  rx: 5px;
+  ry: 5px;
+}
+.statediagram-cluster.statediagram-cluster .inner {
+  fill: ${i.compositeBackground||i.background};
+}
+.statediagram-cluster.statediagram-cluster-alt .inner {
+  fill: ${i.altBackground?i.altBackground:"#efefef"};
+}
+
+.statediagram-cluster .inner {
+  rx:0;
+  ry:0;
+}
+
+.statediagram-state rect.basic {
+  rx: 5px;
+  ry: 5px;
+}
+.statediagram-state rect.divider {
+  stroke-dasharray: 10,10;
+  fill: ${i.altBackground?i.altBackground:"#efefef"};
+}
+
+.note-edge {
+  stroke-dasharray: 5;
+}
+
+.statediagram-note rect {
+  fill: ${i.noteBkgColor};
+  stroke: ${i.noteBorderColor};
+  stroke-width: 1px;
+  rx: 0;
+  ry: 0;
+}
+.statediagram-note rect {
+  fill: ${i.noteBkgColor};
+  stroke: ${i.noteBorderColor};
+  stroke-width: 1px;
+  rx: 0;
+  ry: 0;
+}
+
+.statediagram-note text {
+  fill: ${i.noteTextColor};
+}
+
+.statediagram-note .nodeLabel {
+  color: ${i.noteTextColor};
+}
+.statediagram .edgeLabel {
+  color: red; // ${i.noteTextColor};
+}
+
+#dependencyStart, #dependencyEnd {
+  fill: ${i.lineColor};
+  stroke: ${i.lineColor};
+  stroke-width: 1;
+}
+
+.statediagramTitleText {
+  text-anchor: middle;
+  font-size: 18px;
+  fill: ${i.textColor};
+}
+`,t2t={},fZe=(i,a)=>{t2t[i]=a},dZe=i=>t2t[i],rHt=()=>Object.keys(t2t),gZe={get:dZe,set:fZe,keys:rHt,size:()=>rHt().length},pZe=i=>i.append("circle").attr("class","start-state").attr("r",Re().state.sizeUnit).attr("cx",Re().state.padding+Re().state.sizeUnit).attr("cy",Re().state.padding+Re().state.sizeUnit),bZe=i=>i.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",Re().state.textHeight).attr("class","divider").attr("x2",Re().state.textHeight*2).attr("y1",0).att [...]
+`+Q.showPosition()+`
+Expecting `+Mt.join(", ")+", got '"+(this.terminals_[It]||It)+"'":pn="Parse error on line "+(mt+1)+": Unexpected "+(It==it?"end of input":"'"+(this.terminals_[It]||It)+"'"),this.parseError(pn,{text:Q.match,token:this.terminals_[It]||It,line:Q.yylineno,loc:yt,expected:Mt})}if(xt[0]instanceof Array&&xt.length>1)throw new Error("Parse Error: multiple actions possible at state: "+jt+", token: "+It);switch(xt[0]){case 1:z.push(It),nt.push(Q.yytext),ot.push(Q.yylloc),z.push(xt[1]),It=null,ct=Q [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(R){this.unput(this.match.slice(R))},pastInput:function(){var R=this.matched.substr(0,this.matched.length-this.match.length);return(R.length>20?"...":"")+R.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var R=this.match;return R.length<20&&(R+=this._input.substr(0,20-R.length)),(R.substr(0,20)+(R.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var R=this.pastInput(),B=new Arr [...]
+`+B+"^"},test_match:function(R,B){var z,Y,nt;if(this.options.backtrack_lexer&&(nt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&& [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var B=this.next();return B||this.lex()},begin:function(B){this.conditionStack.push(B)},popState:function(){var B=this.conditionStack.length-1;return B>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rul [...]
+    font-family: 'trebuchet ms', verdana, arial, sans-serif;
+    font-family: var(--mermaid-font-family);
+    color: ${i.textColor};
+  }
+  .mouth {
+    stroke: #666;
+  }
+
+  line {
+    stroke: ${i.textColor}
+  }
+
+  .legend {
+    fill: ${i.textColor};
+  }
+
+  .label text {
+    fill: #333;
+  }
+  .label {
+    color: ${i.textColor}
+  }
+
+  .face {
+    ${i.faceColor?`fill: ${i.faceColor}`:"fill: #FFF8DC"};
+    stroke: #999;
+  }
+
+  .node rect,
+  .node circle,
+  .node ellipse,
+  .node polygon,
+  .node path {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+    stroke-width: 1px;
+  }
+
+  .node .label {
+    text-align: center;
+  }
+  .node.clickable {
+    cursor: pointer;
+  }
+
+  .arrowheadPath {
+    fill: ${i.arrowheadColor};
+  }
+
+  .edgePath .path {
+    stroke: ${i.lineColor};
+    stroke-width: 1.5px;
+  }
+
+  .flowchart-link {
+    stroke: ${i.lineColor};
+    fill: none;
+  }
+
+  .edgeLabel {
+    background-color: ${i.edgeLabelBackground};
+    rect {
+      opacity: 0.5;
+    }
+    text-align: center;
+  }
+
+  .cluster rect {
+  }
+
+  .cluster text {
+    fill: ${i.titleColor};
+  }
+
+  div.mermaidTooltip {
+    position: absolute;
+    text-align: center;
+    max-width: 200px;
+    padding: 2px;
+    font-family: 'trebuchet ms', verdana, arial, sans-serif;
+    font-family: var(--mermaid-font-family);
+    font-size: 12px;
+    background: ${i.tertiaryColor};
+    border: 1px solid ${i.border2};
+    border-radius: 2px;
+    pointer-events: none;
+    z-index: 100;
+  }
+
+  .task-type-0, .section-type-0  {
+    ${i.fillType0?`fill: ${i.fillType0}`:""};
+  }
+  .task-type-1, .section-type-1  {
+    ${i.fillType0?`fill: ${i.fillType1}`:""};
+  }
+  .task-type-2, .section-type-2  {
+    ${i.fillType0?`fill: ${i.fillType2}`:""};
+  }
+  .task-type-3, .section-type-3  {
+    ${i.fillType0?`fill: ${i.fillType3}`:""};
+  }
+  .task-type-4, .section-type-4  {
+    ${i.fillType0?`fill: ${i.fillType4}`:""};
+  }
+  .task-type-5, .section-type-5  {
+    ${i.fillType0?`fill: ${i.fillType5}`:""};
+  }
+  .task-type-6, .section-type-6  {
+    ${i.fillType0?`fill: ${i.fillType6}`:""};
+  }
+  .task-type-7, .section-type-7  {
+    ${i.fillType0?`fill: ${i.fillType7}`:""};
+  }
+
+  .actor-0 {
+    ${i.actor0?`fill: ${i.actor0}`:""};
+  }
+  .actor-1 {
+    ${i.actor1?`fill: ${i.actor1}`:""};
+  }
+  .actor-2 {
+    ${i.actor2?`fill: ${i.actor2}`:""};
+  }
+  .actor-3 {
+    ${i.actor3?`fill: ${i.actor3}`:""};
+  }
+  .actor-4 {
+    ${i.actor4?`fill: ${i.actor4}`:""};
+  }
+  .actor-5 {
+    ${i.actor5?`fill: ${i.actor5}`:""};
+  }
+`,c2t=function(i,a){return LY(i,a)},itn=function(i,a){const g=i.append("circle").attr("cx",a.cx).attr("cy",a.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),w=i.append("g");w.append("circle").attr("cx",a.cx-15/3).attr("cy",a.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),w.append("circle").attr("cx",a.cx+15/3).attr("cy",a.cy-15/3).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666") [...]
+`,Hu(Va(t,e[0])))>=0;)++e[0]}function $0n(t,e){Su(t,e==null||bz((Wn(e),e))||isNaN((Wn(e),e))?0:(Wn(e),e))}function z0n(t,e){_u(t,e==null||bz((Wn(e),e))||isNaN((Wn(e),e))?0:(Wn(e),e))}function q0n(t,e){qm(t,e==null||bz((Wn(e),e))||isNaN((Wn(e),e))?0:(Wn(e),e))}function H0n(t,e){zm(t,e==null||bz((Wn(e),e))||isNaN((Wn(e),e))?0:(Wn(e),e))}function V0n(t,e,n){return R6(new ae(n.e.a+n.f.a/2,n.e.b+n.f.b/2),t)==(Wn(e),e)}function U0n(t,e){return Lt(e,102)&&u(e,19).Bb&po?new Krt(e,t):new o7(e,t)} [...]
+	`)}return e&&(e.stack||n(t[zct]))}function $5t(t,e){switch(e){case 3:return t.f!=0;case 4:return t.g!=0;case 5:return t.i!=0;case 6:return t.j!=0}return $4t(t,e)}function nle(t){switch(t.g){case 0:return new ktt;case 1:return new bu;default:throw at(new zn(jut+(t.f!=null?t.f:""+t.g)))}}function nwn(t){switch(t.g){case 0:return new lC;case 1:return new yc;default:throw at(new zn(Clt+(t.f!=null?t.f:""+t.g)))}}function rwn(t){switch(t.g){case 1:return new btt;case 2:return new qJt;default: [...]
+`;return n.substring(0,r.length)==r&&(n=n.substring(r.length)),n.split(`
+`)}return[]}function lvn(t){var e;return e=(Doe(),Yve),e[t>>>28]|e[t>>24&15]<<4|e[t>>20&15]<<8|e[t>>16&15]<<12|e[t>>12&15]<<16|e[t>>8&15]<<20|e[t>>4&15]<<24|e[t&15]<<28}function Lhe(t){var e,n,r;t.b==t.c&&(r=t.a.length,n=W4t(b.Math.max(8,r))<<1,t.b!=0?(e=Ff(t.a,n),Uoe(t,e,r),t.a=e,t.b=0):wy(t.a,n),t.c=r)}function hvn(t,e){var n;return n=t.b,n.pf((ai(),Pl))?n.ag()==(ve(),Gn)?-n.Mf().a-Rt(Ht(n.of(Pl))):e+Rt(Ht(n.of(Pl))):n.ag()==(ve(),Gn)?-n.Mf().a:e}function EO(t){var e;return t.b.c.lengt [...]
+\r\f]+`);try{TL=lt(ut(E_n,1),Ln,2114,0,[new AM((wmt(),mH("yyyy-MM-dd'T'HH:mm:ss'.'SSSZ",hI((h$(),h$(),mA))))),new AM(mH("yyyy-MM-dd'T'HH:mm:ss'.'SSS",hI(mA))),new AM(mH("yyyy-MM-dd'T'HH:mm:ss",hI(mA))),new AM(mH("yyyy-MM-dd'T'HH:mm",hI(mA))),new AM(mH("yyyy-MM-dd",hI(mA)))])}catch(t){if(t=es(t),!Lt(t,82))throw at(t)}}function H7n(t,e){var n,r,s,o;if(s=Dl(t.d,1)!=0,r=ykt(t,e),r==0&&Ie(Me(X(e.j,(oe(),i2)))))return 0;!Ie(Me(X(e.j,(oe(),i2))))&&!Ie(Me(X(e.j,U5)))||$t(X(e.j,(De(),U0)))===$t(( [...]
+\r\r  `),jo(bx,ght,r),jo(JT,ght,i3(r)),r=new ih(4),vO(r,_ve),jo(bx,wA,r),jo(JT,wA,i3(r)),r=new ih(4),vO(r,_ve),jo(bx,wA,r),jo(JT,wA,i3(r)),r=new ih(4),vO(r,Ave),a3(r,u(nu(bx,wA),122)),jo(bx,dht,r),jo(JT,dht,i3(r)),r=new ih(4),vO(r,"-.0:AZ__az··ÀÖØöøıĴľŁňŊžƀǃǍǰǴǵǺȗɐʨʻˁːˑ̀͠͡ͅΆΊΌΌΎΡΣώϐϖϚϚϜϜϞϞϠϠϢϳЁЌЎяёќўҁ҃҆ҐӄӇӈӋӌӐӫӮӵӸӹԱՖՙՙաֆֹֻֽֿֿׁׂ֑֣֡ׄׄאתװײءغـْ٠٩ٰڷںھۀێېۓە۪ۭۨ۰۹ँःअह़्॑॔क़ॣ०९ঁঃঅঌএঐওনপরললশহ়়াৄেৈো্ৗৗড়ঢ়য়ৣ০ৱਂਂਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹ਼਼ਾੂੇੈੋ੍ਖ਼ੜਫ਼ਫ਼੦ੴઁઃઅઋઍઍએઑઓનપરલળવહ઼ૅેૉો્ૠૠ૦૯ଁଃଅଌଏଐଓନପରଲଳଶହ଼ୃେୈୋ୍ୖୗଡ଼ଢ଼ୟୡ୦୯ஂஃஅஊஎஐஒக [...]
+`,g2e=`)]}"' 	\r
+`,p2e="The given string contains parts that cannot be parsed as numbers.",yN="org.eclipse.elk.core.math",b2e={3:1,4:1,140:1,214:1,423:1},w2e={3:1,4:1,107:1,214:1,423:1},$p="org.eclipse.elk.alg.layered.graph.transform",m2e="ElkGraphImporter",v2e="ElkGraphImporter/lambda$1$Type",y2e="ElkGraphImporter/lambda$2$Type",x2e="ElkGraphImporter/lambda$4$Type",Yn="org.eclipse.elk.alg.layered.intermediate",k2e="Node margin calculation",E2e="ONE_SIDED_GREEDY_SWITCH",T2e="TWO_SIDED_GREEDY_SWITCH",jut= [...]
+`,e=he(0),h=0;h<this.p;h++){for(r+=""+e.a,e=he(iZt(e.a)),s=0;s<this.o;s++)n=iat(this,s,h),$c(n,0)==0?r+="_":$c(n,1)==0?r+="X":r+="0";r+=`
+`}return Lh(r,0,r.length-1)},l.o=0,l.p=0,I(AV,"TwoBitGrid",1317),D(330,1317,{330:1},U5t),l.j=0,l.k=0,I(AV,"PlanarGrid",330),D(176,330,{330:1,176:1}),l.g=0,l.i=0,I(AV,"Polyomino",176);var o_n=os(fN,Qbe);D(137,1,P8t,As),l.qf=function(e,n){return oO(this,e,n)},l.nf=function(){return $te(this)},l.of=function(e){return X(this,e)},l.pf=function(e){return qi(this,e)},I(fN,"MapPropertyHolder",137),D(1318,137,P8t,Vde),I(AV,"Polyominoes",1318);var s3e=!1,kA,eCt;D(1828,1,ir,to),l.Cd=function(e){Gge [...]
+	start=`,tu(e,this.i),e.a+=`
+	end=`,tu(e,this.a),e.a+=`
+	nodeStartEdge=`,tu(e,this.e),e.a+=`
+	startEndEdge=`,tu(e,this.j),e.a+=`
+	originalEdge=`,tu(e,this.f),e.a+=`
+	startInLayerDummy=`,tu(e,this.k),e.a+=`
+	startInLayerEdge=`,tu(e,this.n),e.a+=`
+	endInLayerDummy=`,tu(e,this.b),e.a+=`
+	endInLayerEdge=`,tu(e,this.c),e.a},I(i0,"BreakingPointInserter/BPInfo",313),D(661,1,{661:1},wUt),l.a=!1,l.b=0,l.c=0,I(i0,"BreakingPointInserter/Cut",661),D(1545,1,zi,MJ),l.Kf=function(e,n){m6n(u(e,36),n)},I(i0,"BreakingPointProcessor",1545),D(1546,1,Kr,IJ),l.Mb=function(e){return Bae(u(e,10))},I(i0,"BreakingPointProcessor/0methodref$isEnd$Type",1546),D(1547,1,Kr,OJ),l.Mb=function(e){return Rae(u(e,10))},I(i0,"BreakingPointProcessor/1methodref$isStart$Type",1547),D(1548,1,zi,NJ),l.Kf=fun [...]
+`;for(n=Dr(this.a,0);n.b!=n.d.c;)e=u(Ar(n),65),o+=(e.b&&e.c?Z2(e.b)+"->"+Z2(e.c):"e_"+$i(e))+`
+`;return o};var p_n=I(nA,"TGraph",121);D(643,508,{3:1,508:1,643:1,96:1,137:1}),I(nA,"TShape",643),D(39,643,{3:1,508:1,39:1,643:1,96:1,137:1},Eat),l.Ib=function(){return Z2(this)};var aK=I(nA,"TNode",39);D(236,1,F0,gg),l.Jc=function(e){Fa(this,e)},l.Kc=function(){var e;return e=Dr(this.a.d,0),new M4(e)},I(nA,"TNode/2",236),D(329,1,wa,M4),l.Nb=function(e){Na(this,e)},l.Pb=function(){return u(Ar(this.a),65).c},l.Ob=function(){return IM(this.a)},l.Qb=function(){Qst(this.a)},I(nA,"TNode/2/1", [...]
+`),o.length!=2)throw at(new zn("Exactly two numbers are expected, "+o.length+" were found."));try{this.a=Jy(Zy(o[0])),this.b=Jy(Zy(o[1]))}catch(h){throw h=es(h),Lt(h,130)?(r=h,at(new zn(p2e+r))):at(h)}},l.Ib=function(){return"("+this.a+","+this.b+")"},l.a=0,l.b=0;var la=I(yN,"KVector",8);D(75,67,{3:1,4:1,20:1,31:1,56:1,16:1,67:1,15:1,75:1,423:1},Qu,b$,UJt),l.Pc=function(){return xbn(this)},l.cg=function(e){var n,r,s,o,h,d;s=s3(e,`,|;|\\(|\\)|\\[|\\]|\\{|\\}| |	|
+`),nh(this);try{for(r=0,h=0,o=0,d=0;r<s.length;)s[r]!=null&&Zy(s[r]).length>0&&(h%2==0?o=Jy(s[r]):d=Jy(s[r]),h>0&&h%2!=0&&Zr(this,new ae(o,d)),++h),++r}catch(m){throw m=es(m),Lt(m,130)?(n=m,at(new zn("The given string does not match the expected format for vectors."+n))):at(m)}},l.Ib=function(){var e,n,r;for(e=new eh("("),n=Dr(this,0);n.b!=n.d.c;)r=u(Ar(n),8),ni(e,r.a+","+r.b),n.b!=n.d.c&&(e.a+="; ");return(e.a+=")",e).a};var sMt=I(yN,"KVectorChain",75);D(255,22,{3:1,34:1,22:1,255:1},hS) [...]
+Consider installing the package or pass your own 'workerFactory' to ELK's constructor.
+... Falling back to non-web worker version.`);if(!B.workerFactory){var nt=f("./elk-worker.min.js"),ot=nt.Worker;B.workerFactory=function(ft){return new ot(ft)}}return b(this,(F.__proto__||Object.getPrototypeOf(F)).call(this,B))}return F}(S);Object.defineProperty(g.exports,"__esModule",{value:!0}),g.exports=_,_.default=_},{"./elk-api.js":1,"./elk-worker.min.js":2,"web-worker":4}],4:[function(f,g,w){g.exports=Worker},{}]},{},[3])(3)})})(THt);var ptn=THt.exports;const btn=e9(ptn),CHt=new bt [...]
+`),_.style===void 0&&(z.style=z.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),z.labelStyle=z.labelStyle.replace("color:","fill:"),z.id=F,z.classes="flowchart-link "+R+" "+B;const ot=tbt(w,z),{source:ft,target:wt,sourceId:mt,targetId:ct}=vtn(_,b);Kt.debug("abc78 source and target",ft,wt),f.edges.push({id:"e"+_.start+_.end,sources:[ft],targets:[wt],sourceId:mt,targetId:ct,labelEl:ot,labels:[{width:z.width,height:z.height,orgWidth:z.width,orgHeight:z.height,text:z.label,layoutOptio [...]
+      .subgraph-lvl-${f} {
+        fill: ${i[`surface${f}`]};
+        stroke: ${i[`surfacePeer${f}`]};
+      }
+    `;return a},Ltn=Object.freeze(Object.defineProperty({__proto__:null,diagram:{db:wje,renderer:_tn,parser:spt,styles:i=>`.label {
+    font-family: ${i.fontFamily};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+  .cluster-label text {
+    fill: ${i.titleColor};
+  }
+  .cluster-label span {
+    color: ${i.titleColor};
+  }
+
+  .label text,span {
+    fill: ${i.nodeTextColor||i.textColor};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+
+  .node rect,
+  .node circle,
+  .node ellipse,
+  .node polygon,
+  .node path {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+    stroke-width: 1px;
+  }
+
+  .node .label {
+    text-align: center;
+  }
+  .node.clickable {
+    cursor: pointer;
+  }
+
+  .arrowheadPath {
+    fill: ${i.arrowheadColor};
+  }
+
+  .edgePath .path {
+    stroke: ${i.lineColor};
+    stroke-width: 2.0px;
+  }
+
+  .flowchart-link {
+    stroke: ${i.lineColor};
+    fill: none;
+  }
+
+  .edgeLabel {
+    background-color: ${i.edgeLabelBackground};
+    rect {
+      opacity: 0.85;
+      background-color: ${i.edgeLabelBackground};
+      fill: ${i.edgeLabelBackground};
+    }
+    text-align: center;
+  }
+
+  .cluster rect {
+    fill: ${i.clusterBkg};
+    stroke: ${i.clusterBorder};
+    stroke-width: 1px;
+  }
+
+  .cluster text {
+    fill: ${i.titleColor};
+  }
+
+  .cluster span {
+    color: ${i.titleColor};
+  }
+  /* .cluster div {
+    color: ${i.titleColor};
+  } */
+
+  div.mermaidTooltip {
+    position: absolute;
+    text-align: center;
+    max-width: 200px;
+    padding: 2px;
+    font-family: ${i.fontFamily};
+    font-size: 12px;
+    background: ${i.tertiaryColor};
+    border: 1px solid ${i.border2};
+    border-radius: 2px;
+    pointer-events: none;
+    z-index: 100;
+  }
+
+  .flowchartTitleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.textColor};
+  }
+  .subgraph {
+    stroke-width:2;
+    rx:3;
+  }
+  // .subgraph-lvl-1 {
+  //   fill:#ccc;
+  //   // stroke:black;
+  // }
+
+  .flowchart-label text {
+    text-anchor: middle;
+  }
+
+  ${Atn(i)}
+`}},Symbol.toStringTag,{value:"Module"}));var l2t=function(){var i=function(R,B,z,Y){for(z=z||{},Y=R.length;Y--;z[R[Y]]=B);return z},a=[6,8,10,11,12,14,16,17,20,21],f=[1,9],g=[1,10],w=[1,11],v=[1,12],b=[1,13],E=[1,16],S=[1,17],_={trace:function(){},yy:{},symbols_:{error:2,start:3,timeline:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,period_statement:18,event_statem [...]
+`+dt.showPosition()+`
+Expecting `+pn.join(", ")+", got '"+(this.terminals_[jt]||jt)+"'":Ae="Parse error on line "+(ct+1)+": Unexpected "+(jt==gt?"end of input":"'"+(this.terminals_[jt]||jt)+"'"),this.parseError(Ae,{text:dt.match,token:this.terminals_[jt]||jt,line:dt.yylineno,loc:V,expected:pn})}if(Ft[0]instanceof Array&&Ft.length>1)throw new Error("Parse Error: multiple actions possible at state: "+xt+", token: "+jt);switch(Ft[0]){case 1:Y.push(jt),ot.push(dt.yytext),ft.push(dt.yylloc),Y.push(Ft[1]),jt=null,r [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(B){this.unput(this.match.slice(B))},pastInput:function(){var B=this.matched.substr(0,this.matched.length-this.match.length);return(B.length>20?"...":"")+B.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var B=this.match;return B.length<20&&(B+=this._input.substr(0,20-B.length)),(B.substr(0,20)+(B.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var B=this.pastInput(),z=new Arr [...]
+`+z+"^"},test_match:function(B,z){var Y,nt,ot;if(this.options.backtrack_lexer&&(ot={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges& [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var z=this.next();return z||this.lex()},begin:function(z){this.conditionStack.push(z)},popState:function(){var z=this.conditionStack.length-1;return z>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rul [...]
+    .section-${f-1} rect, .section-${f-1} path, .section-${f-1} circle, .section-${f-1} path  {
+      fill: ${i["cScale"+f]};
+    }
+    .section-${f-1} text {
+     fill: ${i["cScaleLabel"+f]};
+    }
+    .node-icon-${f-1} {
+      font-size: 40px;
+      color: ${i["cScaleLabel"+f]};
+    }
+    .section-edge-${f-1}{
+      stroke: ${i["cScale"+f]};
+    }
+    .edge-depth-${f-1}{
+      stroke-width: ${g};
+    }
+    .section-${f-1} line {
+      stroke: ${i["cScaleInv"+f]} ;
+      stroke-width: 3;
+    }
+
+    .lineWrapper line{
+      stroke: ${i["cScaleLabel"+f]} ;
+    }
+
+    .disabled, .disabled circle, .disabled text {
+      fill: lightgray;
+    }
+    .disabled text {
+      fill: #efefef;
+    }
+    `}return a},Wtn=Object.freeze(Object.defineProperty({__proto__:null,diagram:{db:Mtn,renderer:Gtn,parser:Dtn,styles:i=>`
+  .edge {
+    stroke-width: 3;
+  }
+  ${Ktn(i)}
+  .section-root rect, .section-root path, .section-root circle  {
+    fill: ${i.git0};
+  }
+  .section-root text {
+    fill: ${i.gitBranchLabel0};
+  }
+  .icon-container {
+    height:100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  .edge {
+    fill: none;
+  }
+  .eventWrapper  {
+   filter: brightness(120%);
+  }
+`}},Symbol.toStringTag,{value:"Module"}));var d2t=function(){var i=function(mt,ct,rt,it){for(rt=rt||{},it=mt.length;it--;rt[mt[it]]=ct);return rt},a=[1,4],f=[1,13],g=[1,12],w=[1,15],v=[1,16],b=[1,20],E=[1,19],S=[6,7,8],_=[1,26],M=[1,24],F=[1,25],R=[6,7,11],B=[1,6,13,15,16,19,22],z=[1,33],Y=[1,34],nt=[1,6,7,11,13,15,16,19,22],ot={trace:function(){},yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,MINDMAP:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14, [...]
+`+Ft.showPosition()+`
+Expecting `+_e.join(", ")+", got '"+(this.terminals_[pn]||pn)+"'":xe="Parse error on line "+(V+1)+": Unexpected "+(pn==jt?"end of input":"'"+(this.terminals_[pn]||pn)+"'"),this.parseError(xe,{text:Ft.match,token:this.terminals_[pn]||pn,line:Ft.yylineno,loc:Gt,expected:_e})}if(ye[0]instanceof Array&&ye.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Ae+", token: "+pn);switch(ye[0]){case 1:it.push(pn),Q.push(Ft.yytext),dt.push(Ft.yylloc),it.push(ye[1]),pn=null, [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(ct){this.unput(this.match.slice(ct))},pastInput:function(){var ct=this.matched.substr(0,this.matched.length-this.match.length);return(ct.length>20?"...":"")+ct.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var ct=this.match;return ct.length<20&&(ct+=this._input.substr(0,20-ct.length)),(ct.substr(0,20)+(ct.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var ct=this.pastInput [...]
+`+rt+"^"},test_match:function(ct,rt){var it,gt,Q;if(this.options.backtrack_lexer&&(Q={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.range [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var rt=this.next();return rt||this.lex()},begin:function(rt){this.conditionStack.push(rt)},popState:function(){var rt=this.conditionStack.length-1;return rt>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var B=typeof window>"u"?null:window,z=B?B.navigator:null;B&&B.document;var Y=f(""),nt=f({}),ot=f(function(){}),ft=typeof HTMLElement>"u"?"undefined":f(HTMLElement),wt=function(p){return p&&p.instanceString&&ct(p.instanceString)?p.instanceString():null},mt=function(p){return p!=null&&f(p)==Y},ct=function(p){return p!=null&&f(p)===ot},rt=function(p){return!yt(p)&&(Array.isArray?Array.isArray(p):p!=null&&p  [...]
+Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
+Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
+Licensed under The MIT License (http://opensource.org/licenses/MIT)
+*/var oR=0,cR=1,uR=2,am=function y(p){if(!(this instanceof y))return new y(p);this.id="Thenable/1.0.7",this.state=oR,this.fulfillValue=void 0,this.rejectReason=void 0,this.onFulfilled=[],this.onRejected=[],this.proxy={then:this.then.bind(this)},typeof p=="function"&&p.call(this,this.fulfill.bind(this),this.reject.bind(this))};am.prototype={fulfill:function(p){return lR(this,cR,"fulfillValue",p)},reject:function(p){return lR(this,uR,"rejectReason",p)},then:function(p,x){var C=this,L=new a [...]
+    	  Event object based on jQuery events, MIT license
+
+    	  https://jquery.org/license/
+    	  https://tldrlegal.com/license/mit-license
+    	  https://github.com/jquery/jquery/blob/master/src/event.js
+    	  */var K8=function(p,x){this.recycle(p,x)};function W8(){return!1}function Y8(){return!0}K8.prototype={instanceString:function(){return"event"},recycle:function(p,x){if(this.isImmediatePropagationStopped=this.isPropagationStopped=this.isDefaultPrevented=W8,p!=null&&p.preventDefault?(this.type=p.type,this.isDefaultPrevented=p.defaultPrevented?Y8:W8):p!=null&&p.type?x=p:this.type=p,x!=null&&(this.originalEvent=x.originalEvent,this.type=x.type!=null?x.type:this.type,this.cy=x.cy,this. [...]
+`),Z=y.pstyle("text-max-width").pfValue,st=y.pstyle("text-overflow-wrap").value,bt=st==="anywhere",pt=[],_t=/[\s\u200b]+/,St=bt?"":" ",Et=0;Et<W.length;Et++){var Nt=W[Et],Pt=this.calculateLabelDimensions(y,Nt),qt=Pt.width;if(bt){var re=Nt.split("").join(q);Nt=re}if(qt>Z){for(var ee=Nt.split(_t),Ut="",de=0;de<ee.length;de++){var Yt=ee[de],Ee=Ut.length===0?Yt:Ut+St+Yt,Te=this.calculateLabelDimensions(y,Ee),ie=Te.width;ie<=Z?Ut+=Yt+St:(Ut&&pt.push(Ut),Ut=Yt+St)}Ut.match(/^[\s\u200b]+$/)||pt [...]
+`)),N("labelWrapKey",H)}else if(j==="ellipsis"){var ze=y.pstyle("text-max-width").pfValue,me="",yn="…",nn=!1;if(this.calculateLabelDimensions(y,L).width<ze)return L;for(var qe=0;qe<L.length;qe++){var Tn=this.calculateLabelDimensions(y,me+L[qe]+yn).width;if(Tn>ze)break;me+=L[qe],qe===L.length-1&&(nn=!0)}return nn||(me+=yn),me}return L},j2.getLabelJustification=function(y){var p=y.pstyle("text-justification").strValue,x=y.pstyle("text-halign").strValue;if(p==="auto")if(y.isNode())switch(x) [...]
+`),Et=0;Et<St.length;Et++){var Nt=St[Et],Pt=st.measureText(Nt),qt=Math.ceil(Pt.width),re=H;pt=Math.max(qt,pt),_t+=re}return pt+=N,_t+=N,L[C]={width:pt,height:_t}},j2.calculateLabelAngle=function(y,p){var x=y._private,C=x.rscratch,L=y.isEdge(),O=p?p+"-":"",N=y.pstyle(O+"text-rotation"),j=N.strValue;return j==="none"?0:L&&j==="autorotate"?C.labelAutoAngle:j==="autorotate"?0:N.pfValue},j2.calculateLabelAngles=function(y){var p=this,x=y.isEdge(),C=y._private,L=C.rscratch;L.labelAngle=p.calcu [...]
+      a${E},${E} 1 0,1 ${g*.4},${-1*g*.1}
+      a${b},${b} 1 0,1 ${g*.35},${1*g*.2}
+
+      a${v},${v} 1 0,1 ${g*.15},${1*w*.35}
+      a${S},${S} 1 0,1 ${-1*g*.15},${1*w*.65}
+
+      a${b},${v} 1 0,1 ${-1*g*.25},${g*.15}
+      a${E},${E} 1 0,1 ${-1*g*.5},0
+      a${v},${v} 1 0,1 ${-1*g*.25},${-1*g*.15}
+
+      a${v},${v} 1 0,1 ${-1*g*.1},${-1*w*.35}
+      a${S},${S} 1 0,1 ${g*.1},${-1*w*.65}
+
+    H0 V0 Z`)},len=function(i,a,f){const g=f.width,w=f.height,v=.15*g;a.append("path").attr("id","node-"+f.id).attr("class","node-bkg node-"+i.type2Str(f.type)).attr("d",`M0 0 a${v},${v} 1 0,0 ${g*.25},${-1*w*.1}
+      a${v},${v} 1 0,0 ${g*.25},0
+      a${v},${v} 1 0,0 ${g*.25},0
+      a${v},${v} 1 0,0 ${g*.25},${1*w*.1}
+
+      a${v},${v} 1 0,0 ${g*.15},${1*w*.33}
+      a${v*.8},${v*.8} 1 0,0 0,${1*w*.34}
+      a${v},${v} 1 0,0 ${-1*g*.15},${1*w*.33}
+
+      a${v},${v} 1 0,0 ${-1*g*.25},${w*.15}
+      a${v},${v} 1 0,0 ${-1*g*.25},0
+      a${v},${v} 1 0,0 ${-1*g*.25},0
+      a${v},${v} 1 0,0 ${-1*g*.25},${-1*w*.15}
+
+      a${v},${v} 1 0,0 ${-1*g*.1},${-1*w*.33}
+      a${v*.8},${v*.8} 1 0,0 0,${-1*w*.34}
+      a${v},${v} 1 0,0 ${g*.1},${-1*w*.33}
+
+    H0 V0 Z`)},hen=function(i,a,f){a.append("circle").attr("id","node-"+f.id).attr("class","node-bkg node-"+i.type2Str(f.type)).attr("r",f.width/2)};function fen(i,a,f,g,w){return i.insert("polygon",":first-child").attr("points",g.map(function(v){return v.x+","+v.y}).join(" ")).attr("transform","translate("+(w.width-a)/2+", "+f+")")}const den=function(i,a,f){const g=f.height,v=g/4,b=f.width-f.padding+2*v,E=[{x:v,y:0},{x:b-v,y:0},{x:b,y:-g/2},{x:b-v,y:-g},{x:v,y:-g},{x:0,y:-g/2}];fen(a,b, [...]
+`);ZY(M,F,{useHtmlLabels:v,width:f.width,classes:"mindmap-node-label"}),v||M.attr("dy","1em").attr("alignment-baseline","middle").attr("dominant-baseline","middle").attr("text-anchor","middle");const R=M.node().getBBox(),[B]=k9(w.fontSize);if(f.height=R.height+B*1.1*.5+f.padding,f.width=R.width+2*f.padding,f.icon)if(f.type===i.nodeType.CIRCLE)f.height+=50,f.width+=50,E.append("foreignObject").attr("height","50px").attr("width",f.width).attr("style","text-align: center;").append("div").at [...]
+`+i);const w=g.db,v=w.getMindmap();if(!v)return;const b=Re();b.htmlLabels=!1;const E=FF(a),S=E.append("g");S.attr("class","mindmap-edges");const _=E.append("g");_.attr("class","mindmap-nodes"),QHt(w,_,v,-1,b);const M=await men(v,b);wen(S,M),ven(w,M),w8(void 0,E,((F=b.mindmap)==null?void 0:F.padding)??Bl.mindmap.padding,((R=b.mindmap)==null?void 0:R.useMaxWidth)??Bl.mindmap.useMaxWidth)}},xen=i=>{let a="";for(let f=0;f<i.THEME_COLOR_LIMIT;f++)i["lineColor"+f]=i["lineColor"+f]||i["cScaleIn [...]
+    .section-${f-1} rect, .section-${f-1} path, .section-${f-1} circle, .section-${f-1} polygon, .section-${f-1} path  {
+      fill: ${i["cScale"+f]};
+    }
+    .section-${f-1} text {
+     fill: ${i["cScaleLabel"+f]};
+    }
+    .node-icon-${f-1} {
+      font-size: 40px;
+      color: ${i["cScaleLabel"+f]};
+    }
+    .section-edge-${f-1}{
+      stroke: ${i["cScale"+f]};
+    }
+    .edge-depth-${f-1}{
+      stroke-width: ${g};
+    }
+    .section-${f-1} line {
+      stroke: ${i["cScaleInv"+f]} ;
+      stroke-width: 3;
+    }
+
+    .disabled, .disabled circle, .disabled text {
+      fill: lightgray;
+    }
+    .disabled text {
+      fill: #efefef;
+    }
+    `}return a},ken=Object.freeze(Object.defineProperty({__proto__:null,diagram:{db:ten,renderer:yen,parser:Ytn,styles:i=>`
+  .edge {
+    stroke-width: 3;
+  }
+  ${xen(i)}
+  .section-root rect, .section-root path, .section-root circle, .section-root polygon  {
+    fill: ${i.git0};
+  }
+  .section-root text {
+    fill: ${i.gitBranchLabel0};
+  }
+  .icon-container {
+    height:100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  .edge {
+    fill: none;
+  }
+  .mindmap-node-label {
+    dy: 1em;
+    alignment-baseline: middle;
+    text-anchor: middle;
+    dominant-baseline: middle;
+    text-align: center;
+  }
+`}},Symbol.toStringTag,{value:"Module"}));var w2t=function(){var i=function(E,S,_,M){for(_=_||{},M=E.length;M--;_[E[M]]=S);return _},a=[1,9],f=[1,10],g=[1,5,10,12],w={trace:function(){},yy:{},symbols_:{error:2,start:3,SANKEY:4,NEWLINE:5,csv:6,opt_eof:7,record:8,csv_tail:9,EOF:10,"field[source]":11,COMMA:12,"field[target]":13,"field[value]":14,field:15,escaped:16,non_escaped:17,DQUOTE:18,ESCAPED_TEXT:19,NON_ESCAPED_TEXT:20,$accept:0,$end:1},terminals_:{2:"error",4:"SANKEY",5:"NEWLINE",10: [...]
+`+ct.showPosition()+`
+Expecting `+Ct.join(", ")+", got '"+(this.terminals_[K]||K)+"'":kt="Parse error on line "+(nt+1)+": Unexpected "+(K==wt?"end of input":"'"+(this.terminals_[K]||K)+"'"),this.parseError(kt,{text:ct.match,token:this.terminals_[K]||K,line:ct.yylineno,loc:gt,expected:Ct})}if(V[0]instanceof Array&&V.length>1)throw new Error("Parse Error: multiple actions possible at state: "+yt+", token: "+K);switch(V[0]){case 1:M.push(K),R.push(ct.yytext),B.push(ct.yylloc),M.push(V[1]),K=null,ot=ct.yyleng,Y=c [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(S){this.unput(this.match.slice(S))},pastInput:function(){var S=this.matched.substr(0,this.matched.length-this.match.length);return(S.length>20?"...":"")+S.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var S=this.match;return S.length<20&&(S+=this._input.substr(0,20-S.length)),(S.substr(0,20)+(S.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var S=this.pastInput(),_=new Arr [...]
+`+_+"^"},test_match:function(S,_){var M,F,R;if(this.options.backtrack_lexer&&(R={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(R [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var _=this.next();return _||this.lex()},begin:function(_){this.conditionStack.push(_)},popState:function(){var _=this.conditionStack.length-1;return _>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rul [...]
+${z}${Math.round(K*100)/100}${Y}`:dt;_.append("g").attr("class","node-labels").attr("font-family","sans-serif").attr("font-size",14).selectAll("text").data(ot.nodes).join("text").attr("x",dt=>dt.x0<M/2?dt.x1+6:dt.x0-6).attr("y",dt=>(dt.y1+dt.y0)/2).attr("dy",`${nt?"0":"0.35"}em`).attr("text-anchor",dt=>dt.x0<M/2?"start":"end").text(rt);const it=_.append("g").attr("class","links").attr("fill","none").attr("stroke-opacity",.5).selectAll(".link").data(ot.links).join("g").attr("class","link" [...]
+`).trim(),Qen=NX.parse.bind(NX);NX.parse=i=>Qen(Xen(i));const Jen=Object.freeze(Object.defineProperty({__proto__:null,diagram:{parser:NX,db:_en,renderer:Yen}},Symbol.toStringTag,{value:"Module"}));var E2t=function(){var i=function(wt,mt,ct,rt){for(ct=ct||{},rt=wt.length;rt--;ct[wt[rt]]=mt);return ct},a=[1,7],f=[1,13],g=[1,14],w=[1,15],v=[1,19],b=[1,16],E=[1,17],S=[1,18],_=[8,30],M=[8,21,28,29,30,31,32,40,44,47],F=[1,23],R=[1,24],B=[8,15,16,21,28,29,30,31,32,40,44,47],z=[8,15,16,21,27,28, [...]
+`+xt.showPosition()+`
+Expecting `+Xt.join(", ")+", got '"+(this.terminals_[Mt]||Mt)+"'":_e="Parse error on line "+(yt+1)+": Unexpected "+(Mt==It?"end of input":"'"+(this.terminals_[Mt]||Mt)+"'"),this.parseError(_e,{text:xt.match,token:this.terminals_[Mt]||Mt,line:xt.yylineno,loc:kt,expected:Xt})}if(Ae[0]instanceof Array&&Ae.length>1)throw new Error("Parse Error: multiple actions possible at state: "+pn+", token: "+Mt);switch(Ae[0]){case 1:rt.push(Mt),gt.push(xt.yytext),Q.push(xt.yylloc),rt.push(Ae[1]),Mt=null [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(mt){this.unput(this.match.slice(mt))},pastInput:function(){var mt=this.matched.substr(0,this.matched.length-this.match.length);return(mt.length>20?"...":"")+mt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var mt=this.match;return mt.length<20&&(mt+=this._input.substr(0,20-mt.length)),(mt.substr(0,20)+(mt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var mt=this.pastInput [...]
+`+ct+"^"},test_match:function(mt,ct){var rt,it,gt;if(this.options.backtrack_lexer&&(gt={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ran [...]
+`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var ct=this.next();return ct||this.lex()},begin:function(ct){this.conditionStack.push(ct)},popState:function(){var ct=this.conditionStack.length-1;return ct>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITI [...]
+    font-family: ${i.fontFamily};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+  .cluster-label text {
+    fill: ${i.titleColor};
+  }
+  .cluster-label span,p {
+    color: ${i.titleColor};
+  }
+
+
+
+  .label text,span,p {
+    fill: ${i.nodeTextColor||i.textColor};
+    color: ${i.nodeTextColor||i.textColor};
+  }
+
+  .node rect,
+  .node circle,
+  .node ellipse,
+  .node polygon,
+  .node path {
+    fill: ${i.mainBkg};
+    stroke: ${i.nodeBorder};
+    stroke-width: 1px;
+  }
+  .flowchart-label text {
+    text-anchor: middle;
+  }
+  // .flowchart-label .text-outer-tspan {
+  //   text-anchor: middle;
+  // }
+  // .flowchart-label .text-inner-tspan {
+  //   text-anchor: start;
+  // }
+
+  .node .label {
+    text-align: center;
+  }
+  .node.clickable {
+    cursor: pointer;
+  }
+
+  .arrowheadPath {
+    fill: ${i.arrowheadColor};
+  }
+
+  .edgePath .path {
+    stroke: ${i.lineColor};
+    stroke-width: 2.0px;
+  }
+
+  .flowchart-link {
+    stroke: ${i.lineColor};
+    fill: none;
+  }
+
+  .edgeLabel {
+    background-color: ${i.edgeLabelBackground};
+    rect {
+      opacity: 0.5;
+      background-color: ${i.edgeLabelBackground};
+      fill: ${i.edgeLabelBackground};
+    }
+    text-align: center;
+  }
+
+  /* For html labels only */
+  .labelBkg {
+    background-color: ${jX(i.edgeLabelBackground,.5)};
+    // background-color:
+  }
+
+  .node .cluster {
+    // fill: ${jX(i.mainBkg,.5)};
+    fill: ${jX(i.clusterBkg,.5)};
+    stroke: ${jX(i.clusterBorder,.2)};
+    box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
+    stroke-width: 1px;
+  }
+
+  .cluster text {
+    fill: ${i.titleColor};
+  }
+
+  .cluster span,p {
+    color: ${i.titleColor};
+  }
+  /* .cluster div {
+    color: ${i.titleColor};
+  } */
+
+  div.mermaidTooltip {
+    position: absolute;
+    text-align: center;
+    max-width: 200px;
+    padding: 2px;
+    font-family: ${i.fontFamily};
+    font-size: 12px;
+    background: ${i.tertiaryColor};
+    border: 1px solid ${i.border2};
+    border-radius: 2px;
+    pointer-events: none;
+    z-index: 100;
+  }
+
+  .flowchartTitleText {
+    text-anchor: middle;
+    font-size: 18px;
+    fill: ${i.textColor};
+  }
+`;function pVt(i,a,f=!1){var R,B,z;const g=i;let w="default";(((R=g==null?void 0:g.classes)==null?void 0:R.length)||0)>0&&(w=((g==null?void 0:g.classes)||[]).join(" ")),w=w+" flowchart-label";let v=0,b="",E;switch(g.type){case"round":v=5,b="rect";break;case"composite":v=0,b="composite",E=0;break;case"square":b="rect";break;case"diamond":b="question";break;case"hexagon":b="hexagon";break;case"block_arrow":b="block_arrow";break;case"odd":b="rect_left_inv_arrow";break;case"lean_right":b="le [...]
diff --git a/blog-src/themes/apollo/static/js/note.js b/blog-src/themes/apollo/static/js/note.js
new file mode 100644
index 000000000..ec7a14132
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/note.js
@@ -0,0 +1,14 @@
+document.addEventListener('DOMContentLoaded', function() {
+  document.querySelectorAll('.note-toggle').forEach(function(toggleButton) {
+    var content = toggleButton.nextElementSibling;
+    var isHidden = content.style.display === 'none';
+    toggleButton.setAttribute('aria-expanded', !isHidden);
+
+    toggleButton.addEventListener('click', function() {
+      var expanded = this.getAttribute('aria-expanded') === 'true';
+      this.setAttribute('aria-expanded', !expanded);
+      content.style.display = expanded ? 'none' : 'block';
+    });
+  });
+});
+
diff --git a/blog-src/themes/apollo/static/js/searchElasticlunr.js b/blog-src/themes/apollo/static/js/searchElasticlunr.js
new file mode 100644
index 000000000..9ad09e1aa
--- /dev/null
+++ b/blog-src/themes/apollo/static/js/searchElasticlunr.js
@@ -0,0 +1,3201 @@
+/**
+ * elasticlunr - http://weixsong.github.io
+ * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5
+ *
+ * Copyright (C) 2017 Oliver Nightingale
+ * Copyright (C) 2017 Wei Song
+ * MIT Licensed
+ * @license
+ */
+(function () {
+    /*!
+     * elasticlunr.js
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * Convenience function for instantiating a new elasticlunr index and configuring it
+     * with the default pipeline functions and the passed config function.
+     *
+     * When using this convenience function a new index will be created with the
+     * following functions already in the pipeline:
+     *
+     * 1. elasticlunr.trimmer - trim non-word character
+     * 2. elasticlunr.StopWordFilter - filters out any stop words before they enter the
+     * index
+     * 3. elasticlunr.stemmer - stems the tokens before entering the index.
+     *
+     *
+     * Example:
+     *
+     *     var idx = elasticlunr(function () {
+     *       this.addField('id');
+     *       this.addField('title');
+     *       this.addField('body');
+     *
+     *       //this.setRef('id'); // default ref is 'id'
+     *
+     *       this.pipeline.add(function () {
+     *         // some custom pipeline function
+     *       });
+     *     });
+     *
+     *    idx.addDoc({
+     *      id: 1,
+     *      title: 'Oracle released database 12g',
+     *      body: 'Yestaday, Oracle has released their latest database, named 12g, more robust. this product will increase Oracle profit.'
+     *    });
+     *
+     *    idx.addDoc({
+     *      id: 2,
+     *      title: 'Oracle released annual profit report',
+     *      body: 'Yestaday, Oracle has released their annual profit report of 2015, total profit is 12.5 Billion.'
+     *    });
+     *
+     *    # simple search
+     *    idx.search('oracle database');
+     *
+     *    # search with query-time boosting
+     *    idx.search('oracle database', {fields: {title: {boost: 2}, body: {boost: 1}}});
+     *
+     * @param {Function} config A function that will be called with the new instance
+     * of the elasticlunr.Index as both its context and first parameter. It can be used to
+     * customize the instance of new elasticlunr.Index.
+     * @namespace
+     * @module
+     * @return {elasticlunr.Index}
+     *
+     */
+    const elasticlunr = function (config) {
+        const idx = new elasticlunr.Index();
+
+        idx.pipeline.add(
+            elasticlunr.trimmer,
+            elasticlunr.stopWordFilter,
+            elasticlunr.stemmer
+        );
+
+        if (config) config.call(idx, idx);
+
+        return idx;
+    };
+
+    elasticlunr.version = '0.9.5';
+
+    // only used this to make elasticlunr.js compatible with lunr-languages
+    // this is a trick to define a global alias of elasticlunr
+    lunr = elasticlunr;
+
+    /*!
+     * elasticlunr.utils
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * A namespace containing utils for the rest of the elasticlunr library
+     */
+    elasticlunr.utils = {};
+
+    /**
+     * Print a warning message to the console.
+     *
+     * @param {String} message The message to be printed.
+     * @memberOf Utils
+     */
+    elasticlunr.utils.warn = (function (global) {
+        return function (message) {
+            if (global.console && console.warn) {
+                console.warn(message);
+            }
+        };
+    })(this);
+
+    /**
+     * Convert an object to string.
+     *
+     * In the case of `null` and `undefined` the function returns
+     * an empty string, in all other cases the result of calling
+     * `toString` on the passed object is returned.
+     *
+     * @param {object} obj The object to convert to a string.
+     * @return {String} string representation of the passed object.
+     * @memberOf Utils
+     */
+    elasticlunr.utils.toString = function (obj) {
+        if (obj === void 0 || obj === null) {
+            return '';
+        }
+
+        return obj.toString();
+    };
+    /*!
+     * elasticlunr.EventEmitter
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.EventEmitter is an event emitter for elasticlunr.
+     * It manages adding and removing event handlers and triggering events and their handlers.
+     *
+     * Each event could has multiple corresponding functions,
+     * these functions will be called as the sequence that they are added into the event.
+     *
+     * @constructor
+     */
+    elasticlunr.EventEmitter = function () {
+        this.events = {};
+    };
+
+    /**
+     * Binds a handler function to a specific event(s).
+     *
+     * Can bind a single function to many different events in one call.
+     *
+     * @param {String} [eventName] The name(s) of events to bind this function to.
+     * @param {Function} fn The function to call when an event is fired.
+     * @memberOf EventEmitter
+     */
+    elasticlunr.EventEmitter.prototype.addListener = function () {
+        const args = Array.prototype.slice.call(arguments);
+        const fn = args.pop();
+        const names = args;
+
+        if (typeof fn !== 'function')
+            throw new TypeError('last argument must be a function');
+
+        names.forEach(function (name) {
+            if (!this.hasHandler(name)) this.events[name] = [];
+            this.events[name].push(fn);
+        }, this);
+    };
+
+    /**
+     * Removes a handler function from a specific event.
+     *
+     * @param {String} eventName The name of the event to remove this function from.
+     * @param {Function} fn The function to remove from an event.
+     * @memberOf EventEmitter
+     */
+    elasticlunr.EventEmitter.prototype.removeListener = function (name, fn) {
+        if (!this.hasHandler(name)) return;
+
+        const fnIndex = this.events[name].indexOf(fn);
+        if (fnIndex === -1) return;
+
+        this.events[name].splice(fnIndex, 1);
+
+        if (this.events[name].length === 0) delete this.events[name];
+    };
+
+    /**
+     * Call all functions that bounded to the given event.
+     *
+     * Additional data can be passed to the event handler as arguments to `emit`
+     * after the event name.
+     *
+     * @param {String} eventName The name of the event to emit.
+     * @memberOf EventEmitter
+     */
+    elasticlunr.EventEmitter.prototype.emit = function (name) {
+        if (!this.hasHandler(name)) return;
+
+        const args = Array.prototype.slice.call(arguments, 1);
+
+        this.events[name].forEach(function (fn) {
+            fn.apply(undefined, args);
+        }, this);
+    };
+
+    /**
+     * Checks whether a handler has ever been stored against an event.
+     *
+     * @param {String} eventName The name of the event to check.
+     * @private
+     * @memberOf EventEmitter
+     */
+    elasticlunr.EventEmitter.prototype.hasHandler = function (name) {
+        return name in this.events;
+    };
+    /*!
+     * elasticlunr.tokenizer
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * A function for splitting a string into tokens.
+     * Currently English is supported as default.
+     * Uses `elasticlunr.tokenizer.seperator` to split strings, you could change
+     * the value of this property to set how you want strings are split into tokens.
+     * IMPORTANT: use elasticlunr.tokenizer.seperator carefully, if you are not familiar with
+     * text process, then you'd better not change it.
+     *
+     * @module
+     * @param {String} str The string that you want to tokenize.
+     * @see elasticlunr.tokenizer.seperator
+     * @return {Array}
+     */
+    elasticlunr.tokenizer = function (str) {
+        if (!arguments.length || str === null || str === undefined) return [];
+        if (Array.isArray(str)) {
+            let arr = str.filter(function (token) {
+                if (token === null || token === undefined) {
+                    return false;
+                }
+
+                return true;
+            });
+
+            arr = arr.map(function (t) {
+                return elasticlunr.utils.toString(t).toLowerCase();
+            });
+
+            let out = [];
+            arr.forEach(function (item) {
+                const tokens = item.split(elasticlunr.tokenizer.seperator);
+                out = out.concat(tokens);
+            }, this);
+
+            return out;
+        }
+
+        return str
+            .toString()
+            .trim()
+            .toLowerCase()
+            .split(elasticlunr.tokenizer.seperator);
+    };
+
+    /**
+     * Default string seperator.
+     */
+    elasticlunr.tokenizer.defaultSeperator = /[\s-]+/;
+
+    /**
+     * The sperator used to split a string into tokens. Override this property to change the behaviour of
+     * `elasticlunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.
+     *
+     * @static
+     * @see elasticlunr.tokenizer
+     */
+    elasticlunr.tokenizer.seperator = elasticlunr.tokenizer.defaultSeperator;
+
+    /**
+     * Set up customized string seperator
+     *
+     * @param {Object} sep The customized seperator that you want to use to tokenize a string.
+     */
+    elasticlunr.tokenizer.setSeperator = function (sep) {
+        if (sep !== null && sep !== undefined && typeof sep === 'object') {
+            elasticlunr.tokenizer.seperator = sep;
+        }
+    };
+
+    /**
+     * Reset string seperator
+     *
+     */
+    elasticlunr.tokenizer.resetSeperator = function () {
+        elasticlunr.tokenizer.seperator = elasticlunr.tokenizer.defaultSeperator;
+    };
+
+    /**
+     * Get string seperator
+     *
+     */
+    elasticlunr.tokenizer.getSeperator = function () {
+        return elasticlunr.tokenizer.seperator;
+    };
+    /*!
+     * elasticlunr.Pipeline
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.Pipelines maintain an ordered list of functions to be applied to
+     * both documents tokens and query tokens.
+     *
+     * An instance of elasticlunr.Index will contain a pipeline
+     * with a trimmer, a stop word filter, an English stemmer. Extra
+     * functions can be added before or after either of these functions or these
+     * default functions can be removed.
+     *
+     * When run the pipeline, it will call each function in turn.
+     *
+     * The output of the functions in the pipeline will be passed to the next function
+     * in the pipeline. To exclude a token from entering the index the function
+     * should return undefined, the rest of the pipeline will not be called with
+     * this token.
+     *
+     * For serialisation of pipelines to work, all functions used in an instance of
+     * a pipeline should be registered with elasticlunr.Pipeline. Registered functions can
+     * then be loaded. If trying to load a serialised pipeline that uses functions
+     * that are not registered an error will be thrown.
+     *
+     * If not planning on serialising the pipeline then registering pipeline functions
+     * is not necessary.
+     *
+     * @constructor
+     */
+    elasticlunr.Pipeline = function () {
+        this._queue = [];
+    };
+
+    elasticlunr.Pipeline.registeredFunctions = {};
+
+    /**
+     * Register a function in the pipeline.
+     *
+     * Functions that are used in the pipeline should be registered if the pipeline
+     * needs to be serialised, or a serialised pipeline needs to be loaded.
+     *
+     * Registering a function does not add it to a pipeline, functions must still be
+     * added to instances of the pipeline for them to be used when running a pipeline.
+     *
+     * @param {Function} fn The function to register.
+     * @param {String} label The label to register this function with
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.registerFunction = function (fn, label) {
+        if (label in elasticlunr.Pipeline.registeredFunctions) {
+            elasticlunr.utils.warn(
+                'Overwriting existing registered function: ' + label
+            );
+        }
+
+        fn.label = label;
+        elasticlunr.Pipeline.registeredFunctions[label] = fn;
+    };
+
+    /**
+     * Get a registered function in the pipeline.
+     *
+     * @param {String} label The label of registered function.
+     * @return {Function}
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.getRegisteredFunction = function (label) {
+        if (label in elasticlunr.Pipeline.registeredFunctions !== true) {
+            return null;
+        }
+
+        return elasticlunr.Pipeline.registeredFunctions[label];
+    };
+
+    /**
+     * Warns if the function is not registered as a Pipeline function.
+     *
+     * @param {Function} fn The function to check for.
+     * @private
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
+        const isRegistered = fn.label && fn.label in this.registeredFunctions;
+
+        if (!isRegistered) {
+            elasticlunr.utils.warn(
+                'Function is not registered with pipeline. This may cause problems when serialising the index.\n',
+                fn
+            );
+        }
+    };
+
+    /**
+     * Loads a previously serialised pipeline.
+     *
+     * All functions to be loaded must already be registered with elasticlunr.Pipeline.
+     * If any function from the serialised data has not been registered then an
+     * error will be thrown.
+     *
+     * @param {Object} serialised The serialised pipeline to load.
+     * @return {elasticlunr.Pipeline}
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.load = function (serialised) {
+        const pipeline = new elasticlunr.Pipeline();
+
+        serialised.forEach(function (fnName) {
+            const fn = elasticlunr.Pipeline.getRegisteredFunction(fnName);
+
+            if (fn) {
+                pipeline.add(fn);
+            } else {
+                throw new Error('Cannot load un-registered function: ' + fnName);
+            }
+        });
+
+        return pipeline;
+    };
+
+    /**
+     * Adds new functions to the end of the pipeline.
+     *
+     * Logs a warning if the function has not been registered.
+     *
+     * @param {Function} functions Any number of functions to add to the pipeline.
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.add = function () {
+        const fns = Array.prototype.slice.call(arguments);
+
+        fns.forEach(function (fn) {
+            elasticlunr.Pipeline.warnIfFunctionNotRegistered(fn);
+            this._queue.push(fn);
+        }, this);
+    };
+
+    /**
+     * Adds a single function after a function that already exists in the
+     * pipeline.
+     *
+     * Logs a warning if the function has not been registered.
+     * If existingFn is not found, throw an Exception.
+     *
+     * @param {Function} existingFn A function that already exists in the pipeline.
+     * @param {Function} newFn The new function to add to the pipeline.
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.after = function (existingFn, newFn) {
+        elasticlunr.Pipeline.warnIfFunctionNotRegistered(newFn);
+
+        const pos = this._queue.indexOf(existingFn);
+        if (pos === -1) {
+            throw new Error('Cannot find existingFn');
+        }
+
+        this._queue.splice(pos + 1, 0, newFn);
+    };
+
+    /**
+     * Adds a single function before a function that already exists in the
+     * pipeline.
+     *
+     * Logs a warning if the function has not been registered.
+     * If existingFn is not found, throw an Exception.
+     *
+     * @param {Function} existingFn A function that already exists in the pipeline.
+     * @param {Function} newFn The new function to add to the pipeline.
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.before = function (existingFn, newFn) {
+        elasticlunr.Pipeline.warnIfFunctionNotRegistered(newFn);
+
+        const pos = this._queue.indexOf(existingFn);
+        if (pos === -1) {
+            throw new Error('Cannot find existingFn');
+        }
+
+        this._queue.splice(pos, 0, newFn);
+    };
+
+    /**
+     * Removes a function from the pipeline.
+     *
+     * @param {Function} fn The function to remove from the pipeline.
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.remove = function (fn) {
+        const pos = this._queue.indexOf(fn);
+        if (pos === -1) {
+            return;
+        }
+
+        this._queue.splice(pos, 1);
+    };
+
+    /**
+     * Runs the current list of functions that registered in the pipeline against the
+     * input tokens.
+     *
+     * @param {Array} tokens The tokens to run through the pipeline.
+     * @return {Array}
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.run = function (tokens) {
+        const out = [];
+        const tokenLength = tokens.length;
+        const pipelineLength = this._queue.length;
+
+        for (let i = 0; i < tokenLength; i++) {
+            let token = tokens[i];
+
+            for (let j = 0; j < pipelineLength; j++) {
+                token = this._queue[j](token, i, tokens);
+                if (token === void 0 || token === null) break;
+            }
+
+            if (token !== void 0 && token !== null) out.push(token);
+        }
+
+        return out;
+    };
+
+    /**
+     * Resets the pipeline by removing any existing processors.
+     *
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.reset = function () {
+        this._queue = [];
+    };
+
+    /**
+     * Get the pipeline if user want to check the pipeline.
+     *
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.get = function () {
+        return this._queue;
+    };
+
+    /**
+     * Returns a representation of the pipeline ready for serialisation.
+     * Only serialize pipeline function's name. Not storing function, so when
+     * loading the archived JSON index file, corresponding pipeline function is
+     * added by registered function of elasticlunr.Pipeline.registeredFunctions
+     *
+     * Logs a warning if the function has not been registered.
+     *
+     * @return {Array}
+     * @memberOf Pipeline
+     */
+    elasticlunr.Pipeline.prototype.toJSON = function () {
+        return this._queue.map(function (fn) {
+            elasticlunr.Pipeline.warnIfFunctionNotRegistered(fn);
+            return fn.label;
+        });
+    };
+    /*!
+     * elasticlunr.Index
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.Index is object that manages a search index.  It contains the indexes
+     * and stores all the tokens and document lookups.  It also provides the main
+     * user facing API for the library.
+     *
+     * @constructor
+     */
+    elasticlunr.Index = function () {
+        this._fields = [];
+        this._ref = 'id';
+        this.pipeline = new elasticlunr.Pipeline();
+        this.documentStore = new elasticlunr.DocumentStore();
+        this.index = {};
+        this.eventEmitter = new elasticlunr.EventEmitter();
+        this._idfCache = {};
+
+        this.on(
+            'add',
+            'remove',
+            'update',
+            function () {
+                this._idfCache = {};
+            }.bind(this)
+        );
+    };
+
+    /**
+     * Bind a handler to events being emitted by the index.
+     *
+     * The handler can be bound to many events at the same time.
+     *
+     * @param {String} [eventName] The name(s) of events to bind the function to.
+     * @param {Function} fn The serialised set to load.
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.on = function () {
+        const args = Array.prototype.slice.call(arguments);
+        return this.eventEmitter.addListener.apply(this.eventEmitter, args);
+    };
+
+    /**
+     * Removes a handler from an event being emitted by the index.
+     *
+     * @param {String} eventName The name of events to remove the function from.
+     * @param {Function} fn The serialised set to load.
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.off = function (name, fn) {
+        return this.eventEmitter.removeListener(name, fn);
+    };
+
+    /**
+     * Loads a previously serialised index.
+     *
+     * Issues a warning if the index being imported was serialised
+     * by a different version of elasticlunr.
+     *
+     * @param {Object} serialisedData The serialised set to load.
+     * @return {elasticlunr.Index}
+     * @memberOf Index
+     */
+    elasticlunr.Index.load = function (serialisedData) {
+        if (serialisedData.version !== elasticlunr.version) {
+            elasticlunr.utils.warn(
+                'version mismatch: current ' +
+                    elasticlunr.version +
+                    ' importing ' +
+                    serialisedData.version
+            );
+        }
+
+        const idx = new this();
+
+        idx._fields = serialisedData.fields;
+        idx._ref = serialisedData.ref;
+        idx.documentStore = elasticlunr.DocumentStore.load(
+            serialisedData.documentStore
+        );
+        idx.pipeline = elasticlunr.Pipeline.load(serialisedData.pipeline);
+        idx.index = {};
+        for (const field in serialisedData.index) {
+            idx.index[field] = elasticlunr.InvertedIndex.load(
+                serialisedData.index[field]
+            );
+        }
+
+        return idx;
+    };
+
+    /**
+     * Adds a field to the list of fields that will be searchable within documents in the index.
+     *
+     * Remember that inner index is build based on field, which means each field has one inverted index.
+     *
+     * Fields should be added before any documents are added to the index, fields
+     * that are added after documents are added to the index will only apply to new
+     * documents added to the index.
+     *
+     * @param {String} fieldName The name of the field within the document that should be indexed
+     * @return {elasticlunr.Index}
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.addField = function (fieldName) {
+        this._fields.push(fieldName);
+        this.index[fieldName] = new elasticlunr.InvertedIndex();
+        return this;
+    };
+
+    /**
+     * Sets the property used to uniquely identify documents added to the index,
+     * by default this property is 'id'.
+     *
+     * This should only be changed before adding documents to the index, changing
+     * the ref property without resetting the index can lead to unexpected results.
+     *
+     * @param {String} refName The property to use to uniquely identify the
+     * documents in the index.
+     * @param {Boolean} emitEvent Whether to emit add events, defaults to true
+     * @return {elasticlunr.Index}
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.setRef = function (refName) {
+        this._ref = refName;
+        return this;
+    };
+
+    /**
+     *
+     * Set if the JSON format original documents are save into elasticlunr.DocumentStore
+     *
+     * Defaultly save all the original JSON documents.
+     *
+     * @param {Boolean} save Whether to save the original JSON documents.
+     * @return {elasticlunr.Index}
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.saveDocument = function (save) {
+        this.documentStore = new elasticlunr.DocumentStore(save);
+        return this;
+    };
+
+    /**
+     * Add a JSON format document to the index.
+     *
+     * This is the way new documents enter the index, this function will run the
+     * fields from the document through the index's pipeline and then add it to
+     * the index, it will then show up in search results.
+     *
+     * An 'add' event is emitted with the document that has been added and the index
+     * the document has been added to. This event can be silenced by passing false
+     * as the second argument to add.
+     *
+     * @param {Object} doc The JSON format document to add to the index.
+     * @param {Boolean} emitEvent Whether or not to emit events, default true.
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.addDoc = function (doc, emitEvent) {
+        if (!doc) return;
+        var emitEvent = emitEvent === undefined ? true : emitEvent;
+
+        const docRef = doc[this._ref];
+
+        this.documentStore.addDoc(docRef, doc);
+        this._fields.forEach(function (field) {
+            const fieldTokens = this.pipeline.run(elasticlunr.tokenizer(doc[field]));
+            this.documentStore.addFieldLength(docRef, field, fieldTokens.length);
+
+            const tokenCount = {};
+            fieldTokens.forEach(function (token) {
+                if (token in tokenCount) tokenCount[token] += 1;
+                else tokenCount[token] = 1;
+            }, this);
+
+            for (const token in tokenCount) {
+                let termFrequency = tokenCount[token];
+                termFrequency = Math.sqrt(termFrequency);
+                this.index[field].addToken(token, { ref: docRef, tf: termFrequency });
+            }
+        }, this);
+
+        if (emitEvent) this.eventEmitter.emit('add', doc, this);
+    };
+
+    /**
+     * Removes a document from the index by doc ref.
+     *
+     * To make sure documents no longer show up in search results they can be
+     * removed from the index using this method.
+     *
+     * A 'remove' event is emitted with the document that has been removed and the index
+     * the document has been removed from. This event can be silenced by passing false
+     * as the second argument to remove.
+     *
+     * If user setting DocumentStore not storing the documents, then remove doc by docRef is not allowed.
+     *
+     * @param {String|Integer} docRef The document ref to remove from the index.
+     * @param {Boolean} emitEvent Whether to emit remove events, defaults to true
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.removeDocByRef = function (docRef, emitEvent) {
+        if (!docRef) return;
+        if (this.documentStore.isDocStored() === false) {
+            return;
+        }
+
+        if (!this.documentStore.hasDoc(docRef)) return;
+        const doc = this.documentStore.getDoc(docRef);
+        this.removeDoc(doc, false);
+    };
+
+    /**
+     * Removes a document from the index.
+     * This remove operation could work even the original doc is not store in the DocumentStore.
+     *
+     * To make sure documents no longer show up in search results they can be
+     * removed from the index using this method.
+     *
+     * A 'remove' event is emitted with the document that has been removed and the index
+     * the document has been removed from. This event can be silenced by passing false
+     * as the second argument to remove.
+     *
+     *
+     * @param {Object} doc The document ref to remove from the index.
+     * @param {Boolean} emitEvent Whether to emit remove events, defaults to true
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.removeDoc = function (doc, emitEvent) {
+        if (!doc) return;
+
+        var emitEvent = emitEvent === undefined ? true : emitEvent;
+
+        const docRef = doc[this._ref];
+        if (!this.documentStore.hasDoc(docRef)) return;
+
+        this.documentStore.removeDoc(docRef);
+
+        this._fields.forEach(function (field) {
+            const fieldTokens = this.pipeline.run(elasticlunr.tokenizer(doc[field]));
+            fieldTokens.forEach(function (token) {
+                this.index[field].removeToken(token, docRef);
+            }, this);
+        }, this);
+
+        if (emitEvent) this.eventEmitter.emit('remove', doc, this);
+    };
+
+    /**
+     * Updates a document in the index.
+     *
+     * When a document contained within the index gets updated, fields changed,
+     * added or removed, to make sure it correctly matched against search queries,
+     * it should be updated in the index.
+     *
+     * This method is just a wrapper around `remove` and `add`
+     *
+     * An 'update' event is emitted with the document that has been updated and the index.
+     * This event can be silenced by passing false as the second argument to update. Only
+     * an update event will be fired, the 'add' and 'remove' events of the underlying calls
+     * are silenced.
+     *
+     * @param {Object} doc The document to update in the index.
+     * @param {Boolean} emitEvent Whether to emit update events, defaults to true
+     * @see Index.prototype.remove
+     * @see Index.prototype.add
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.updateDoc = function (doc, emitEvent) {
+        var emitEvent = emitEvent === undefined ? true : emitEvent;
+
+        this.removeDocByRef(doc[this._ref], false);
+        this.addDoc(doc, false);
+
+        if (emitEvent) this.eventEmitter.emit('update', doc, this);
+    };
+
+    /**
+     * Calculates the inverse document frequency for a token within the index of a field.
+     *
+     * @param {String} token The token to calculate the idf of.
+     * @param {String} field The field to compute idf.
+     * @see Index.prototype.idf
+     * @private
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.idf = function (term, field) {
+        const cacheKey = '@' + field + '/' + term;
+        if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey))
+            return this._idfCache[cacheKey];
+
+        const df = this.index[field].getDocFreq(term);
+        const idf = 1 + Math.log(this.documentStore.length / (df + 1));
+        this._idfCache[cacheKey] = idf;
+
+        return idf;
+    };
+
+    /**
+     * get fields of current index instance
+     *
+     * @return {Array}
+     */
+    elasticlunr.Index.prototype.getFields = function () {
+        return this._fields.slice();
+    };
+
+    /**
+     * Searches the index using the passed query.
+     * Queries should be a string, multiple words are allowed.
+     *
+     * If config is null, will search all fields defaultly, and lead to OR based query.
+     * If config is specified, will search specified with query time boosting.
+     *
+     * All query tokens are passed through the same pipeline that document tokens
+     * are passed through, so any language processing involved will be run on every
+     * query term.
+     *
+     * Each query term is expanded, so that the term 'he' might be expanded to
+     * 'hello' and 'help' if those terms were already included in the index.
+     *
+     * Matching documents are returned as an array of objects, each object contains
+     * the matching document ref, as set for this index, and the similarity score
+     * for this document against the query.
+     *
+     * @param {String} query The query to search the index with.
+     * @param {JSON} userConfig The user query config, JSON format.
+     * @return {Object}
+     * @see Index.prototype.idf
+     * @see Index.prototype.documentVector
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.search = function (query, userConfig) {
+        if (!query) return [];
+        if (typeof query === 'string') {
+            query = { any: query };
+        } else {
+            query = JSON.parse(JSON.stringify(query));
+        }
+
+        let configStr = null;
+        if (userConfig != null) {
+            configStr = JSON.stringify(userConfig);
+        }
+
+        const config = new elasticlunr.Configuration(configStr, this.getFields()).get();
+
+        const queryTokens = {};
+        const queryFields = Object.keys(query);
+
+        for (let i = 0; i < queryFields.length; i++) {
+            const key = queryFields[i];
+
+            queryTokens[key] = this.pipeline.run(elasticlunr.tokenizer(query[key]));
+        }
+
+        const queryResults = {};
+
+        for (const field in config) {
+            const tokens = queryTokens[field] || queryTokens.any;
+            if (!tokens) {
+                continue;
+            }
+
+            const fieldSearchResults = this.fieldSearch(tokens, field, config);
+            const fieldBoost = config[field].boost;
+
+            for (var docRef in fieldSearchResults) {
+                fieldSearchResults[docRef] = fieldSearchResults[docRef] * fieldBoost;
+            }
+
+            for (var docRef in fieldSearchResults) {
+                if (docRef in queryResults) {
+                    queryResults[docRef] += fieldSearchResults[docRef];
+                } else {
+                    queryResults[docRef] = fieldSearchResults[docRef];
+                }
+            }
+        }
+
+        const results = [];
+        let result;
+        for (var docRef in queryResults) {
+            result = { ref: docRef, score: queryResults[docRef] };
+            if (this.documentStore.hasDoc(docRef)) {
+                result.doc = this.documentStore.getDoc(docRef);
+            }
+            results.push(result);
+        }
+
+        results.sort(function (a, b) {
+            return b.score - a.score;
+        });
+        return results;
+    };
+
+    /**
+     * search queryTokens in specified field.
+     *
+     * @param {Array} queryTokens The query tokens to query in this field.
+     * @param {String} field Field to query in.
+     * @param {elasticlunr.Configuration} config The user query config, JSON format.
+     * @return {Object}
+     */
+    elasticlunr.Index.prototype.fieldSearch = function (
+        queryTokens,
+        fieldName,
+        config
+    ) {
+        const booleanType = config[fieldName].bool;
+        const expand = config[fieldName].expand;
+        const boost = config[fieldName].boost;
+        let scores = null;
+        const docTokens = {};
+
+        // Do nothing if the boost is 0
+        if (boost === 0) {
+            return;
+        }
+
+        queryTokens.forEach(function (token) {
+            let tokens = [token];
+            if (expand === true) {
+                tokens = this.index[fieldName].expandToken(token);
+            }
+            // Consider every query token in turn. If expanded, each query token
+            // corresponds to a set of tokens, which is all tokens in the
+            // index matching the pattern queryToken* .
+            // For the set of tokens corresponding to a query token, find and score
+            // all matching documents. Store those scores in queryTokenScores,
+            // keyed by docRef.
+            // Then, depending on the value of booleanType, combine the scores
+            // for this query token with previous scores.  If booleanType is OR,
+            // then merge the scores by summing into the accumulated total, adding
+            // new document scores are required (effectively a union operator).
+            // If booleanType is AND, accumulate scores only if the document
+            // has previously been scored by another query token (an intersection
+            // operation0.
+            // Furthermore, since when booleanType is AND, additional
+            // query tokens can't add new documents to the result set, use the
+            // current document set to limit the processing of each new query
+            // token for efficiency (i.e., incremental intersection).
+
+            const queryTokenScores = {};
+            tokens.forEach(function (key) {
+                let docs = this.index[fieldName].getDocs(key);
+                const idf = this.idf(key, fieldName);
+
+                if (scores && booleanType === 'AND') {
+                    // special case, we can rule out documents that have been
+                    // already been filtered out because they weren't scored
+                    // by previous query token passes.
+                    const filteredDocs = {};
+                    for (var docRef in scores) {
+                        if (docRef in docs) {
+                            filteredDocs[docRef] = docs[docRef];
+                        }
+                    }
+                    docs = filteredDocs;
+                }
+                // only record appeared token for retrieved documents for the
+                // original token, not for expaned token.
+                // beause for doing coordNorm for a retrieved document, coordNorm only care how many
+                // query token appear in that document.
+                // so expanded token should not be added into docTokens, if added, this will pollute the
+                // coordNorm
+                if (key === token) {
+                    this.fieldSearchStats(docTokens, key, docs);
+                }
+
+                for (var docRef in docs) {
+                    const tf = this.index[fieldName].getTermFrequency(key, docRef);
+                    const fieldLength = this.documentStore.getFieldLength(
+                        docRef,
+                        fieldName
+                    );
+                    let fieldLengthNorm = 1;
+                    if (fieldLength !== 0) {
+                        fieldLengthNorm = 1 / Math.sqrt(fieldLength);
+                    }
+
+                    let penality = 1;
+                    if (key !== token) {
+                        // currently I'm not sure if this penality is enough,
+                        // need to do verification
+                        penality =
+                            (1 - (key.length - token.length) / key.length) * 0.15;
+                    }
+
+                    const score = tf * idf * fieldLengthNorm * penality;
+
+                    if (docRef in queryTokenScores) {
+                        queryTokenScores[docRef] += score;
+                    } else {
+                        queryTokenScores[docRef] = score;
+                    }
+                }
+            }, this);
+
+            scores = this.mergeScores(scores, queryTokenScores, booleanType);
+        }, this);
+
+        scores = this.coordNorm(scores, docTokens, queryTokens.length);
+        return scores;
+    };
+
+    /**
+     * Merge the scores from one set of tokens into an accumulated score table.
+     * Exact operation depends on the op parameter. If op is 'AND', then only the
+     * intersection of the two score lists is retained. Otherwise, the union of
+     * the two score lists is returned. For internal use only.
+     *
+     * @param {Object} bool accumulated scores. Should be null on first call.
+     * @param {String} scores new scores to merge into accumScores.
+     * @param {Object} op merge operation (should be 'AND' or 'OR').
+     *
+     */
+
+    elasticlunr.Index.prototype.mergeScores = function (accumScores, scores, op) {
+        if (!accumScores) {
+            return scores;
+        }
+        if (op === 'AND') {
+            const intersection = {};
+            for (var docRef in scores) {
+                if (docRef in accumScores) {
+                    intersection[docRef] = accumScores[docRef] + scores[docRef];
+                }
+            }
+            return intersection;
+        } else {
+            for (var docRef in scores) {
+                if (docRef in accumScores) {
+                    accumScores[docRef] += scores[docRef];
+                } else {
+                    accumScores[docRef] = scores[docRef];
+                }
+            }
+            return accumScores;
+        }
+    };
+
+    /**
+     * Record the occuring query token of retrieved doc specified by doc field.
+     * Only for inner user.
+     *
+     * @param {Object} docTokens a data structure stores which token appears in the retrieved doc.
+     * @param {String} token query token
+     * @param {Object} docs the retrieved documents of the query token
+     *
+     */
+    elasticlunr.Index.prototype.fieldSearchStats = function (docTokens, token, docs) {
+        for (const doc in docs) {
+            if (doc in docTokens) {
+                docTokens[doc].push(token);
+            } else {
+                docTokens[doc] = [token];
+            }
+        }
+    };
+
+    /**
+     * coord norm the score of a doc.
+     * if a doc contain more query tokens, then the score will larger than the doc
+     * contains less query tokens.
+     *
+     * only for inner use.
+     *
+     * @param {Object} results first results
+     * @param {Object} docs field search results of a token
+     * @param {Integer} n query token number
+     * @return {Object}
+     */
+    elasticlunr.Index.prototype.coordNorm = function (scores, docTokens, n) {
+        for (const doc in scores) {
+            if (!(doc in docTokens)) continue;
+            const tokens = docTokens[doc].length;
+            scores[doc] = (scores[doc] * tokens) / n;
+        }
+
+        return scores;
+    };
+
+    /**
+     * Returns a representation of the index ready for serialisation.
+     *
+     * @return {Object}
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.toJSON = function () {
+        const indexJson = {};
+        this._fields.forEach(function (field) {
+            indexJson[field] = this.index[field].toJSON();
+        }, this);
+
+        return {
+            version: elasticlunr.version,
+            fields: this._fields,
+            ref: this._ref,
+            documentStore: this.documentStore.toJSON(),
+            index: indexJson,
+            pipeline: this.pipeline.toJSON(),
+        };
+    };
+
+    /**
+     * Applies a plugin to the current index.
+     *
+     * A plugin is a function that is called with the index as its context.
+     * Plugins can be used to customise or extend the behaviour the index
+     * in some way. A plugin is just a function, that encapsulated the custom
+     * behaviour that should be applied to the index.
+     *
+     * The plugin function will be called with the index as its argument, additional
+     * arguments can also be passed when calling use. The function will be called
+     * with the index as its context.
+     *
+     * Example:
+     *
+     *     var myPlugin = function (idx, arg1, arg2) {
+     *       // `this` is the index to be extended
+     *       // apply any extensions etc here.
+     *     }
+     *
+     *     var idx = elasticlunr(function () {
+     *       this.use(myPlugin, 'arg1', 'arg2')
+     *     })
+     *
+     * @param {Function} plugin The plugin to apply.
+     * @memberOf Index
+     */
+    elasticlunr.Index.prototype.use = function (plugin) {
+        const args = Array.prototype.slice.call(arguments, 1);
+        args.unshift(this);
+        plugin.apply(this, args);
+    };
+    /*!
+     * elasticlunr.DocumentStore
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.DocumentStore is a simple key-value document store used for storing sets of tokens for
+     * documents stored in index.
+     *
+     * elasticlunr.DocumentStore store original JSON format documents that you could build search snippet by this original JSON document.
+     *
+     * user could choose whether original JSON format document should be store, if no configuration then document will be stored defaultly.
+     * If user care more about the index size, user could select not store JSON documents, then this will has some defects, such as user
+     * could not use JSON document to generate snippets of search results.
+     *
+     * @param {Boolean} save If the original JSON document should be stored.
+     * @constructor
+     * @module
+     */
+    elasticlunr.DocumentStore = function (save) {
+        if (save === null || save === undefined) {
+            this._save = true;
+        } else {
+            this._save = save;
+        }
+
+        this.docs = {};
+        this.docInfo = {};
+        this.length = 0;
+    };
+
+    /**
+     * Loads a previously serialised document store
+     *
+     * @param {Object} serialisedData The serialised document store to load.
+     * @return {elasticlunr.DocumentStore}
+     */
+    elasticlunr.DocumentStore.load = function (serialisedData) {
+        const store = new this();
+
+        store.length = serialisedData.length;
+        store.docs = serialisedData.docs;
+        store.docInfo = serialisedData.docInfo;
+        store._save = serialisedData.save;
+
+        return store;
+    };
+
+    /**
+     * check if current instance store the original doc
+     *
+     * @return {Boolean}
+     */
+    elasticlunr.DocumentStore.prototype.isDocStored = function () {
+        return this._save;
+    };
+
+    /**
+     * Stores the given doc in the document store against the given id.
+     * If docRef already exist, then update doc.
+     *
+     * Document is store by original JSON format, then you could use original document to generate search snippets.
+     *
+     * @param {Integer|String} docRef The key used to store the JSON format doc.
+     * @param {Object} doc The JSON format doc.
+     */
+    elasticlunr.DocumentStore.prototype.addDoc = function (docRef, doc) {
+        if (!this.hasDoc(docRef)) this.length++;
+
+        if (this._save === true) {
+            this.docs[docRef] = clone(doc);
+        } else {
+            this.docs[docRef] = null;
+        }
+    };
+
+    /**
+     * Retrieves the JSON doc from the document store for a given key.
+     *
+     * If docRef not found, return null.
+     * If user set not storing the documents, return null.
+     *
+     * @param {Integer|String} docRef The key to lookup and retrieve from the document store.
+     * @return {Object}
+     * @memberOf DocumentStore
+     */
+    elasticlunr.DocumentStore.prototype.getDoc = function (docRef) {
+        if (this.hasDoc(docRef) === false) return null;
+        return this.docs[docRef];
+    };
+
+    /**
+     * Checks whether the document store contains a key (docRef).
+     *
+     * @param {Integer|String} docRef The id to look up in the document store.
+     * @return {Boolean}
+     * @memberOf DocumentStore
+     */
+    elasticlunr.DocumentStore.prototype.hasDoc = function (docRef) {
+        return docRef in this.docs;
+    };
+
+    /**
+     * Removes the value for a key in the document store.
+     *
+     * @param {Integer|String} docRef The id to remove from the document store.
+     * @memberOf DocumentStore
+     */
+    elasticlunr.DocumentStore.prototype.removeDoc = function (docRef) {
+        if (!this.hasDoc(docRef)) return;
+
+        delete this.docs[docRef];
+        delete this.docInfo[docRef];
+        this.length--;
+    };
+
+    /**
+     * Add field length of a document's field tokens from pipeline results.
+     * The field length of a document is used to do field length normalization even without the original JSON document stored.
+     *
+     * @param {Integer|String} docRef document's id or reference
+     * @param {String} fieldName field name
+     * @param {Integer} length field length
+     */
+    elasticlunr.DocumentStore.prototype.addFieldLength = function (
+        docRef,
+        fieldName,
+        length
+    ) {
+        if (docRef === null || docRef === undefined) return;
+        if (this.hasDoc(docRef) == false) return;
+
+        if (!this.docInfo[docRef]) this.docInfo[docRef] = {};
+        this.docInfo[docRef][fieldName] = length;
+    };
+
+    /**
+     * Update field length of a document's field tokens from pipeline results.
+     * The field length of a document is used to do field length normalization even without the original JSON document stored.
+     *
+     * @param {Integer|String} docRef document's id or reference
+     * @param {String} fieldName field name
+     * @param {Integer} length field length
+     */
+    elasticlunr.DocumentStore.prototype.updateFieldLength = function (
+        docRef,
+        fieldName,
+        length
+    ) {
+        if (docRef === null || docRef === undefined) return;
+        if (this.hasDoc(docRef) == false) return;
+
+        this.addFieldLength(docRef, fieldName, length);
+    };
+
+    /**
+     * get field length of a document by docRef
+     *
+     * @param {Integer|String} docRef document id or reference
+     * @param {String} fieldName field name
+     * @return {Integer} field length
+     */
+    elasticlunr.DocumentStore.prototype.getFieldLength = function (docRef, fieldName) {
+        if (docRef === null || docRef === undefined) return 0;
+
+        if (!(docRef in this.docs)) return 0;
+        if (!(fieldName in this.docInfo[docRef])) return 0;
+        return this.docInfo[docRef][fieldName];
+    };
+
+    /**
+     * Returns a JSON representation of the document store used for serialisation.
+     *
+     * @return {Object} JSON format
+     * @memberOf DocumentStore
+     */
+    elasticlunr.DocumentStore.prototype.toJSON = function () {
+        return {
+            docs: this.docs,
+            docInfo: this.docInfo,
+            length: this.length,
+            save: this._save,
+        };
+    };
+
+    /**
+     * Cloning object
+     *
+     * @param {Object} object in JSON format
+     * @return {Object} copied object
+     */
+    function clone(obj) {
+        if (obj === null || typeof obj !== 'object') return obj;
+
+        const copy = obj.constructor();
+
+        for (const attr in obj) {
+            if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+        }
+
+        return copy;
+    }
+    /*!
+     * elasticlunr.stemmer
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
+     */
+
+    /**
+     * elasticlunr.stemmer is an english language stemmer, this is a JavaScript
+     * implementation of the PorterStemmer taken from http://tartarus.org/~martin
+     *
+     * @module
+     * @param {String} str The string to stem
+     * @return {String}
+     * @see elasticlunr.Pipeline
+     */
+    elasticlunr.stemmer = (function () {
+        const step2list = {
+            ational: 'ate',
+            tional: 'tion',
+            enci: 'ence',
+            anci: 'ance',
+            izer: 'ize',
+            bli: 'ble',
+            alli: 'al',
+            entli: 'ent',
+            eli: 'e',
+            ousli: 'ous',
+            ization: 'ize',
+            ation: 'ate',
+            ator: 'ate',
+            alism: 'al',
+            iveness: 'ive',
+            fulness: 'ful',
+            ousness: 'ous',
+            aliti: 'al',
+            iviti: 'ive',
+            biliti: 'ble',
+            logi: 'log',
+        };
+
+        const step3list = {
+            icate: 'ic',
+            ative: '',
+            alize: 'al',
+            iciti: 'ic',
+            ical: 'ic',
+            ful: '',
+            ness: '',
+        };
+
+        const c = '[^aeiou]'; // consonant
+        const v = '[aeiouy]'; // vowel
+        const C = c + '[^aeiouy]*'; // consonant sequence
+        const V = v + '[aeiou]*'; // vowel sequence
+
+        const mgr0 = '^(' + C + ')?' + V + C; // [C]VC... is m>0
+        const meq1 = '^(' + C + ')?' + V + C + '(' + V + ')?$'; // [C]VC[V] is m=1
+        const mgr1 = '^(' + C + ')?' + V + C + V + C; // [C]VCVC... is m>1
+        const s_v = '^(' + C + ')?' + v; // vowel in stem
+
+        const re_mgr0 = new RegExp(mgr0);
+        const re_mgr1 = new RegExp(mgr1);
+        const re_meq1 = new RegExp(meq1);
+        const re_s_v = new RegExp(s_v);
+
+        const re_1a = /^(.+?)(ss|i)es$/;
+        const re2_1a = /^(.+?)([^s])s$/;
+        const re_1b = /^(.+?)eed$/;
+        const re2_1b = /^(.+?)(ed|ing)$/;
+        const re_1b_2 = /.$/;
+        const re2_1b_2 = /(at|bl|iz)$/;
+        const re3_1b_2 = new RegExp('([^aeiouylsz])\\1$');
+        const re4_1b_2 = new RegExp('^' + C + v + '[^aeiouwxy]$');
+
+        const re_1c = /^(.+?[^aeiou])y$/;
+        const re_2 =
+            /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+
+        const re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+
+        const re_4 =
+            /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+        const re2_4 = /^(.+?)(s|t)(ion)$/;
+
+        const re_5 = /^(.+?)e$/;
+        const re_5_1 = /ll$/;
+        const re3_5 = new RegExp('^' + C + v + '[^aeiouwxy]$');
+
+        const porterStemmer = function porterStemmer(w) {
+            let stem, suffix, firstch, re, re2, re3, re4;
+
+            if (w.length < 3) {
+                return w;
+            }
+
+            firstch = w.substr(0, 1);
+            if (firstch == 'y') {
+                w = firstch.toUpperCase() + w.substr(1);
+            }
+
+            // Step 1a
+            re = re_1a;
+            re2 = re2_1a;
+
+            if (re.test(w)) {
+                w = w.replace(re, '$1$2');
+            } else if (re2.test(w)) {
+                w = w.replace(re2, '$1$2');
+            }
+
+            // Step 1b
+            re = re_1b;
+            re2 = re2_1b;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                re = re_mgr0;
+                if (re.test(fp[1])) {
+                    re = re_1b_2;
+                    w = w.replace(re, '');
+                }
+            } else if (re2.test(w)) {
+                var fp = re2.exec(w);
+                stem = fp[1];
+                re2 = re_s_v;
+                if (re2.test(stem)) {
+                    w = stem;
+                    re2 = re2_1b_2;
+                    re3 = re3_1b_2;
+                    re4 = re4_1b_2;
+                    if (re2.test(w)) {
+                        w = w + 'e';
+                    } else if (re3.test(w)) {
+                        re = re_1b_2;
+                        w = w.replace(re, '');
+                    } else if (re4.test(w)) {
+                        w = w + 'e';
+                    }
+                }
+            }
+
+            // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)
+            re = re_1c;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                stem = fp[1];
+                w = stem + 'i';
+            }
+
+            // Step 2
+            re = re_2;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                stem = fp[1];
+                suffix = fp[2];
+                re = re_mgr0;
+                if (re.test(stem)) {
+                    w = stem + step2list[suffix];
+                }
+            }
+
+            // Step 3
+            re = re_3;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                stem = fp[1];
+                suffix = fp[2];
+                re = re_mgr0;
+                if (re.test(stem)) {
+                    w = stem + step3list[suffix];
+                }
+            }
+
+            // Step 4
+            re = re_4;
+            re2 = re2_4;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                stem = fp[1];
+                re = re_mgr1;
+                if (re.test(stem)) {
+                    w = stem;
+                }
+            } else if (re2.test(w)) {
+                var fp = re2.exec(w);
+                stem = fp[1] + fp[2];
+                re2 = re_mgr1;
+                if (re2.test(stem)) {
+                    w = stem;
+                }
+            }
+
+            // Step 5
+            re = re_5;
+            if (re.test(w)) {
+                var fp = re.exec(w);
+                stem = fp[1];
+                re = re_mgr1;
+                re2 = re_meq1;
+                re3 = re3_5;
+                if (re.test(stem) || (re2.test(stem) && !re3.test(stem))) {
+                    w = stem;
+                }
+            }
+
+            re = re_5_1;
+            re2 = re_mgr1;
+            if (re.test(w) && re2.test(w)) {
+                re = re_1b_2;
+                w = w.replace(re, '');
+            }
+
+            // and turn initial Y back to y
+
+            if (firstch == 'y') {
+                w = firstch.toLowerCase() + w.substr(1);
+            }
+
+            return w;
+        };
+
+        return porterStemmer;
+    })();
+
+    elasticlunr.Pipeline.registerFunction(elasticlunr.stemmer, 'stemmer');
+    /*!
+     * elasticlunr.stopWordFilter
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.stopWordFilter is an English language stop words filter, any words
+     * contained in the stop word list will not be passed through the filter.
+     *
+     * This is intended to be used in the Pipeline. If the token does not pass the
+     * filter then undefined will be returned.
+     * Currently this StopwordFilter using dictionary to do O(1) time complexity stop word filtering.
+     *
+     * @module
+     * @param {String} token The token to pass through the filter
+     * @return {String}
+     * @see elasticlunr.Pipeline
+     */
+    elasticlunr.stopWordFilter = function (token) {
+        if (token && elasticlunr.stopWordFilter.stopWords[token] !== true) {
+            return token;
+        }
+    };
+
+    /**
+     * Remove predefined stop words
+     * if user want to use customized stop words, user could use this function to delete
+     * all predefined stopwords.
+     *
+     * @return {null}
+     */
+    elasticlunr.clearStopWords = function () {
+        elasticlunr.stopWordFilter.stopWords = {};
+    };
+
+    /**
+     * Add customized stop words
+     * user could use this function to add customized stop words
+     *
+     * @params {Array} words customized stop words
+     * @return {null}
+     */
+    elasticlunr.addStopWords = function (words) {
+        if (words == null || Array.isArray(words) === false) return;
+
+        words.forEach(function (word) {
+            elasticlunr.stopWordFilter.stopWords[word] = true;
+        }, this);
+    };
+
+    /**
+     * Reset to default stop words
+     * user could use this function to restore default stop words
+     *
+     * @return {null}
+     */
+    elasticlunr.resetStopWords = function () {
+        elasticlunr.stopWordFilter.stopWords = elasticlunr.defaultStopWords;
+    };
+
+    elasticlunr.defaultStopWords = {
+        '': true,
+        a: true,
+        able: true,
+        about: true,
+        across: true,
+        after: true,
+        all: true,
+        almost: true,
+        also: true,
+        am: true,
+        among: true,
+        an: true,
+        and: true,
+        any: true,
+        are: true,
+        as: true,
+        at: true,
+        be: true,
+        because: true,
+        been: true,
+        but: true,
+        by: true,
+        can: true,
+        cannot: true,
+        could: true,
+        dear: true,
+        did: true,
+        do: true,
+        does: true,
+        either: true,
+        else: true,
+        ever: true,
+        every: true,
+        for: true,
+        from: true,
+        get: true,
+        got: true,
+        had: true,
+        has: true,
+        have: true,
+        he: true,
+        her: true,
+        hers: true,
+        him: true,
+        his: true,
+        how: true,
+        however: true,
+        i: true,
+        if: true,
+        in: true,
+        into: true,
+        is: true,
+        it: true,
+        its: true,
+        just: true,
+        least: true,
+        let: true,
+        like: true,
+        likely: true,
+        may: true,
+        me: true,
+        might: true,
+        most: true,
+        must: true,
+        my: true,
+        neither: true,
+        no: true,
+        nor: true,
+        not: true,
+        of: true,
+        off: true,
+        often: true,
+        on: true,
+        only: true,
+        or: true,
+        other: true,
+        our: true,
+        own: true,
+        rather: true,
+        said: true,
+        say: true,
+        says: true,
+        she: true,
+        should: true,
+        since: true,
+        so: true,
+        some: true,
+        than: true,
+        that: true,
+        the: true,
+        their: true,
+        them: true,
+        then: true,
+        there: true,
+        these: true,
+        they: true,
+        this: true,
+        tis: true,
+        to: true,
+        too: true,
+        twas: true,
+        us: true,
+        wants: true,
+        was: true,
+        we: true,
+        were: true,
+        what: true,
+        when: true,
+        where: true,
+        which: true,
+        while: true,
+        who: true,
+        whom: true,
+        why: true,
+        will: true,
+        with: true,
+        would: true,
+        yet: true,
+        you: true,
+        your: true,
+    };
+
+    elasticlunr.stopWordFilter.stopWords = elasticlunr.defaultStopWords;
+
+    elasticlunr.Pipeline.registerFunction(elasticlunr.stopWordFilter, 'stopWordFilter');
+    /*!
+     * elasticlunr.trimmer
+     * Copyright (C) 2017 Oliver Nightingale
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.trimmer is a pipeline function for trimming non word
+     * characters from the begining and end of tokens before they
+     * enter the index.
+     *
+     * This implementation may not work correctly for non latin
+     * characters and should either be removed or adapted for use
+     * with languages with non-latin characters.
+     *
+     * @module
+     * @param {String} token The token to pass through the filter
+     * @return {String}
+     * @see elasticlunr.Pipeline
+     */
+    elasticlunr.trimmer = function (token) {
+        if (token === null || token === undefined) {
+            throw new Error('token should not be undefined');
+        }
+
+        return token.replace(/^\W+/, '').replace(/\W+$/, '');
+    };
+
+    elasticlunr.Pipeline.registerFunction(elasticlunr.trimmer, 'trimmer');
+    /*!
+     * elasticlunr.InvertedIndex
+     * Copyright (C) 2017 Wei Song
+     * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
+     */
+
+    /**
+     * elasticlunr.InvertedIndex is used for efficiently storing and
+     * lookup of documents that contain a given token.
+     *
+     * @constructor
+     */
+    elasticlunr.InvertedIndex = function () {
+        this.root = { docs: {}, df: 0 };
+    };
+
+    /**
+     * Loads a previously serialised inverted index.
+     *
+     * @param {Object} serialisedData The serialised inverted index to load.
+     * @return {elasticlunr.InvertedIndex}
+     */
+    elasticlunr.InvertedIndex.load = function (serialisedData) {
+        const idx = new this();
+        idx.root = serialisedData.root;
+
+        return idx;
+    };
+
+    /**
+     * Adds a {token: tokenInfo} pair to the inverted index.
+     * If the token already exist, then update the tokenInfo.
+     *
+     * tokenInfo format: { ref: 1, tf: 2}
+     * tokenInfor should contains the document's ref and the tf(token frequency) of that token in
+     * the document.
+     *
+     * By default this function starts at the root of the current inverted index, however
+     * it can start at any node of the inverted index if required.
+     *
+     * @param {String} token
+     * @param {Object} tokenInfo format: { ref: 1, tf: 2}
+     * @param {Object} root An optional node at which to start looking for the
+     * correct place to enter the doc, by default the root of this elasticlunr.InvertedIndex
+     * is used.
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.addToken = function (token, tokenInfo, root) {
+        var root = root || this.root;
+        let idx = 0;
+
+        while (idx <= token.length - 1) {
+            const key = token[idx];
+
+            if (!(key in root)) root[key] = { docs: {}, df: 0 };
+            idx += 1;
+            root = root[key];
+        }
+
+        const docRef = tokenInfo.ref;
+        if (!root.docs[docRef]) {
+            // if this doc not exist, then add this doc
+            root.docs[docRef] = { tf: tokenInfo.tf };
+            root.df += 1;
+        } else {
+            // if this doc already exist, then update tokenInfo
+            root.docs[docRef] = { tf: tokenInfo.tf };
+        }
+    };
+
+    /**
+     * Checks whether a token is in this elasticlunr.InvertedIndex.
+     *
+     *
+     * @param {String} token The token to be checked
+     * @return {Boolean}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.hasToken = function (token) {
+        if (!token) return false;
+
+        let node = this.root;
+
+        for (let i = 0; i < token.length; i++) {
+            if (!node[token[i]]) return false;
+            node = node[token[i]];
+        }
+
+        return true;
+    };
+
+    /**
+     * Retrieve a node from the inverted index for a given token.
+     * If token not found in this InvertedIndex, return null.
+     *
+     *
+     * @param {String} token The token to get the node for.
+     * @return {Object}
+     * @see InvertedIndex.prototype.get
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.getNode = function (token) {
+        if (!token) return null;
+
+        let node = this.root;
+
+        for (let i = 0; i < token.length; i++) {
+            if (!node[token[i]]) return null;
+            node = node[token[i]];
+        }
+
+        return node;
+    };
+
+    /**
+     * Retrieve the documents of a given token.
+     * If token not found, return {}.
+     *
+     *
+     * @param {String} token The token to get the documents for.
+     * @return {Object}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.getDocs = function (token) {
+        const node = this.getNode(token);
+        if (node == null) {
+            return {};
+        }
+
+        return node.docs;
+    };
+
+    /**
+     * Retrieve term frequency of given token in given docRef.
+     * If token or docRef not found, return 0.
+     *
+     *
+     * @param {String} token The token to get the documents for.
+     * @param {String|Integer} docRef
+     * @return {Integer}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.getTermFrequency = function (token, docRef) {
+        const node = this.getNode(token);
+
+        if (node == null) {
+            return 0;
+        }
+
+        if (!(docRef in node.docs)) {
+            return 0;
+        }
+
+        return node.docs[docRef].tf;
+    };
+
+    /**
+     * Retrieve the document frequency of given token.
+     * If token not found, return 0.
+     *
+     *
+     * @param {String} token The token to get the documents for.
+     * @return {Object}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.getDocFreq = function (token) {
+        const node = this.getNode(token);
+
+        if (node == null) {
+            return 0;
+        }
+
+        return node.df;
+    };
+
+    /**
+     * Remove the document identified by document's ref from the token in the inverted index.
+     *
+     *
+     * @param {String} token Remove the document from which token.
+     * @param {String} ref The ref of the document to remove from given token.
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.removeToken = function (token, ref) {
+        if (!token) return;
+        const node = this.getNode(token);
+
+        if (node == null) return;
+
+        if (ref in node.docs) {
+            delete node.docs[ref];
+            node.df -= 1;
+        }
+    };
+
+    /**
+     * Find all the possible suffixes of given token using tokens currently in the inverted index.
+     * If token not found, return empty Array.
+     *
+     * @param {String} token The token to expand.
+     * @return {Array}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.expandToken = function (token, memo, root) {
+        if (token == null || token == '') return [];
+        var memo = memo || [];
+
+        if (root == void 0) {
+            root = this.getNode(token);
+            if (root == null) return memo;
+        }
+
+        if (root.df > 0) memo.push(token);
+
+        for (const key in root) {
+            if (key === 'docs') continue;
+            if (key === 'df') continue;
+            this.expandToken(token + key, memo, root[key]);
+        }
+
+        return memo;
+    };
+
+    /**
+     * Returns a representation of the inverted index ready for serialisation.
+     *
+     * @return {Object}
+     * @memberOf InvertedIndex
+     */
+    elasticlunr.InvertedIndex.prototype.toJSON = function () {
+        return {
+            root: this.root,
+        };
+    };
+
+    /*!
+     * elasticlunr.Configuration
+     * Copyright (C) 2017 Wei Song
+     */
+
+    /**
+     * elasticlunr.Configuration is used to analyze the user search configuration.
+     *
+     * By elasticlunr.Configuration user could set query-time boosting, boolean model in each field.
+     *
+     * Currently configuration supports:
+     * 1. query-time boosting, user could set how to boost each field.
+     * 2. boolean model chosing, user could choose which boolean model to use for each field.
+     * 3. token expandation, user could set token expand to True to improve Recall. Default is False.
+     *
+     * Query time boosting must be configured by field category, "boolean" model could be configured
+     * by both field category or globally as the following example. Field configuration for "boolean"
+     * will overwrite global configuration.
+     * Token expand could be configured both by field category or golbally. Local field configuration will
+     * overwrite global configuration.
+     *
+     * configuration example:
+     * {
+     *   fields:{
+     *     title: {boost: 2},
+     *     body: {boost: 1}
+     *   },
+     *   bool: "OR"
+     * }
+     *
+     * "bool" field configuation overwrite global configuation example:
+     * {
+     *   fields:{
+     *     title: {boost: 2, bool: "AND"},
+     *     body: {boost: 1}
+     *   },
+     *   bool: "OR"
+     * }
+     *
+     * "expand" example:
+     * {
+     *   fields:{
+     *     title: {boost: 2, bool: "AND"},
+     *     body: {boost: 1}
+     *   },
+     *   bool: "OR",
+     *   expand: true
+     * }
+     *
+     * "expand" example for field category:
+     * {
+     *   fields:{
+     *     title: {boost: 2, bool: "AND", expand: true},
+     *     body: {boost: 1}
+     *   },
+     *   bool: "OR"
+     * }
+     *
+     * setting the boost to 0 ignores the field (this will only search the title):
+     * {
+     *   fields:{
+     *     title: {boost: 1},
+     *     body: {boost: 0}
+     *   }
+     * }
+     *
+     * then, user could search with configuration to do query-time boosting.
+     * idx.search('oracle database', {fields: {title: {boost: 2}, body: {boost: 1}}});
+     *
+     *
+     * @constructor
+     *
+     * @param {String} config user configuration
+     * @param {Array} fields fields of index instance
+     * @module
+     */
+    elasticlunr.Configuration = function (config, fields) {
+        var config = config || '';
+
+        if (fields == undefined || fields == null) {
+            throw new Error('fields should not be null');
+        }
+
+        this.config = {};
+
+        let userConfig;
+        try {
+            userConfig = JSON.parse(config);
+            this.buildUserConfig(userConfig, fields);
+        } catch (error) {
+            elasticlunr.utils.warn(
+                'user configuration parse failed, will use default configuration'
+            );
+            this.buildDefaultConfig(fields);
+        }
+    };
+
+    /**
+     * Build default search configuration.
+     *
+     * @param {Array} fields fields of index instance
+     */
+    elasticlunr.Configuration.prototype.buildDefaultConfig = function (fields) {
+        this.reset();
+        fields.forEach(function (field) {
+            this.config[field] = {
+                boost: 1,
+                bool: 'OR',
+                expand: false,
+            };
+        }, this);
+    };
+
+    /**
+     * Build user configuration.
+     *
+     * @param {JSON} config User JSON configuratoin
+     * @param {Array} fields fields of index instance
+     */
+    elasticlunr.Configuration.prototype.buildUserConfig = function (config, fields) {
+        let global_bool = 'OR';
+        let global_expand = false;
+
+        this.reset();
+        if ('bool' in config) {
+            global_bool = config.bool || global_bool;
+        }
+
+        if ('expand' in config) {
+            global_expand = config.expand || global_expand;
+        }
+
+        if ('fields' in config) {
+            for (const field in config.fields) {
+                if (fields.indexOf(field) > -1) {
+                    const field_config = config.fields[field];
+                    let field_expand = global_expand;
+                    if (field_config.expand != undefined) {
+                        field_expand = field_config.expand;
+                    }
+
+                    this.config[field] = {
+                        boost:
+                            field_config.boost || field_config.boost === 0
+                                ? field_config.boost
+                                : 1,
+                        bool: field_config.bool || global_bool,
+                        expand: field_expand,
+                    };
+                } else {
+                    elasticlunr.utils.warn(
+                        'field name in user configuration not found in index instance fields'
+                    );
+                }
+            }
+        } else {
+            this.addAllFields2UserConfig(global_bool, global_expand, fields);
+        }
+    };
+
+    /**
+     * Add all fields to user search configuration.
+     *
+     * @param {String} bool Boolean model
+     * @param {String} expand Expand model
+     * @param {Array} fields fields of index instance
+     */
+    elasticlunr.Configuration.prototype.addAllFields2UserConfig = function (
+        bool,
+        expand,
+        fields
+    ) {
+        fields.forEach(function (field) {
+            this.config[field] = {
+                boost: 1,
+                bool,
+                expand,
+            };
... 46216 lines suppressed ...