This is an automated email from the ASF dual-hosted git repository. dpol1 pushed a commit to branch refresh in repository https://gitbox.apache.org/repos/asf/stormcrawler-site.git
commit 6b90832ca0373ddd6cd65aefaebad00115d78681 Author: Davide Polato <[email protected]> AuthorDate: Mon Aug 10 10:51:03 2026 +0200 Restyle site: Il Crawl direction Porcelain-and-charcoal palette with the logo's bolt red (#C33540) as single accent. Fluid orb web bleeding off the hero's top-right corner with the spider mark hung at its hub (in-flow below the copy on small screens), crawl pipeline as a vertical thread with numbered stages, mono small-caps labels, uppercase 900 display type. Archivo ships as a self-hosted variable woff2 (SIL OFL), following the css/resources precedent. Inner pages use a 1080px content column with prose capped at 860px; card grids auto-fit so desktop keeps full-size cards while narrow viewports wrap. The ApacheCon event widget is dropped from the footer in favour of the styled Community-over-Code pill. Browser-tab titles stay as in production. --- _includes/footer.html | 88 +- _includes/head.html | 4 +- _includes/header.html | 38 +- _layouts/default.html | 2 - contribute/index.html | 63 +- css/main.scss | 1967 +++++++++++++++++----------- css/resources/archivo-latin-variable.woff2 | Bin 0 -> 34928 bytes docs/index.html | 183 +-- download/index.html | 88 +- img/spider-mark-light.svg | 5 + index.html | 201 ++- js/site.js | 132 +- security/index.html | 99 +- support/index.html | 38 +- team/index.html | 9 +- 15 files changed, 1644 insertions(+), 1273 deletions(-) diff --git a/_includes/footer.html b/_includes/footer.html index 3d8f240..31b0511 100644 --- a/_includes/footer.html +++ b/_includes/footer.html @@ -1,56 +1,62 @@ <footer class="site-footer"> <div class="site-footer__inner"> - <div class="site-footer__cols"> - <div class="site-footer__brand"> + <div class="thread"> + <span class="thread__node thread__node--accent" style="left:0;"></span> + <span class="thread__node" style="left:39.9%;"></span> + <span class="thread__node" style="left:65.3%;"></span> + </div> + <div class="fcols"> + <div class="fbrand"> <a href="{{ site.baseurl }}/" aria-label="Apache StormCrawler home"> <img src="{{ site.baseurl }}/img/logo-dark.svg" alt="Apache StormCrawler"> </a> - <a class="site-footer__tag" href="https://www.apache.org/" target="_blank" rel="noopener"> - <img class="site-footer__oakleaf" src="{{ site.baseurl }}/img/asf-oakleaf.svg" alt="" height="20"> - An Apache Software Foundation project - </a> - <a class="icon-btn" href="https://github.com/apache/stormcrawler" aria-label="Apache StormCrawler on GitHub" title="GitHub"> - <svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2- [...] - </a> - <div class="footer-widget"> - <a class="acevent" data-format="wide" data-mode="dark"></a> + <div class="fbrand__row"> + <a class="icon-btn" href="https://github.com/apache/stormcrawler" target="_blank" rel="noopener" title="GitHub" aria-label="Apache StormCrawler on GitHub"> + <svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2. [...] + </a> + <a class="icon-btn" href="https://infra.apache.org/slack.html" target="_blank" rel="noopener" title="Slack — #stormcrawler" aria-label="ASF Slack"> + <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M5.04 15.17a2.52 2.52 0 1 1-2.52-2.53h2.52v2.53Zm1.27 0a2.52 2.52 0 0 1 5.05 0v6.31a2.52 2.52 0 1 1-5.05 0v-6.31ZM8.83 5.04a2.52 2.52 0 1 1 2.53-2.52v2.52H8.83Zm0 1.27a2.52 2.52 0 0 1 0 5.05H2.52a2.52 2.52 0 1 1 0-5.05h6.31Zm10.13 2.52a2.52 2.52 0 1 1 2.52 2.53h-2.52V8.83Zm-1.27 0a2.52 2.52 0 0 1-5.05 0V2.52a2.52 2.52 0 1 1 5.05 0v6.31Zm-2.52 10.13a2.52 2.52 0 1 1-2.53 2.52v-2.52h2 [...] + </a> + <a class="icon-btn" href="http://stackoverflow.com/questions/tagged/stormcrawler" target="_blank" rel="noopener" title="Stack Overflow — tag stormcrawler" aria-label="Stack Overflow"> + <svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M17.4 20.4H6.6v-5H5v6.6h14v-6.6h-1.6v5ZM8.7 14.2h6.6v1.6H8.7v-1.6Zm.2-3 6.4 1.4.3-1.6-6.4-1.3-.3 1.5Zm.9-3.5 6 2.8.6-1.4-6-2.8-.6 1.4Zm1.9-3.2 5 4.2 1-1.2-5-4.2-1 1.2Z"/></svg> + </a> + <a class="icon-btn" href="mailto:[email protected]" title="Subscribe to the dev mailing list" aria-label="Subscribe to the dev mailing list"> + <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg> + </a> + <a class="coc-pill" href="https://communityovercode.org/" target="_blank" rel="noopener"><span class="coc-dot"></span>COMMUNITY OVER CODE ↗</a> </div> </div> - <div class="site-footer__col"> + <div class="fcol"> <h3>Project</h3> - <ul> - <li><a href="{{ site.baseurl }}/download/">Download</a></li> - <li><a href="{{ site.baseurl }}/docs/">Documentation</a></li> - <li><a href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Quick Start</a></li> - <li><a href="{{ site.baseurl }}/team/">Team</a></li> - </ul> + <a href="{{ site.baseurl }}/download/">Download</a> + <a href="{{ site.baseurl }}/docs/">Documentation</a> + <a href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Quick Start</a> + <a href="{{ site.baseurl }}/team/">Team</a> </div> - <div class="site-footer__col"> + <div class="fcol"> <h3>Community</h3> - <ul> - <li><a href="{{ site.baseurl }}/contribute/">Contribute</a></li> - <li><a href="{{ site.baseurl }}/support/">Support</a></li> - <li><a href="{{ site.baseurl }}/security/">Security</a></li> - <li><a href="https://lists.apache.org/[email protected]" target="_blank" rel="noopener">Mailing lists ↗</a></li> - </ul> - </div> - <div class="site-footer__col"> - <h3>Foundation</h3> - <ul> - <li><a href="https://www.apache.org/" target="_blank" rel="noopener">Apache Home ↗</a></li> - <li><a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="noopener">License ↗</a></li> - <li><a href="https://privacy.apache.org/policies/privacy-policy-public.html" target="_blank" rel="noopener">Privacy Policy ↗</a></li> - <li><a href="https://www.apache.org/security/" target="_blank" rel="noopener">Security ↗</a></li> - <li><a href="https://www.apache.org/foundation/sponsorship" target="_blank" rel="noopener">Sponsorship ↗</a></li> - <li><a href="https://www.apache.org/foundation/sponsors" target="_blank" rel="noopener">Sponsors ↗</a></li> - <li><a href="https://www.apache.org/foundation/thanks" target="_blank" rel="noopener">Thanks ↗</a></li> - </ul> + <a href="{{ site.baseurl }}/contribute/">Contribute</a> + <a href="{{ site.baseurl }}/support/">Support</a> + <a href="{{ site.baseurl }}/security/">Security</a> + <a href="https://lists.apache.org/[email protected]" target="_blank" rel="noopener">Mailing lists ↗</a> </div> </div> - <div class="site-footer__legal"> - <p>© 2026 <a href="https://www.apache.org/">The Apache Software Foundation</a><br/> - Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>. <br/> Apache StormCrawler, StormCrawler, the Apache logo are trademarks of The Apache Software Foundation. <br/> All other marks mentioned may be trademarks or registered trademarks of their respective owners.</p> - <p><a href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy Policy</a> | <a href="https://www.apache.org/security/">Security</a> | <a href="https://www.apache.org/foundation/sponsorship">Sponsorship</a> | <a href="https://www.apache.org/foundation/sponsors">Sponsors</a></p> + <div class="asf-band"> + <span class="asf-band__seal"><img src="{{ site.baseurl }}/img/asf-oakleaf.svg" alt=""></span> + <div class="asf-band__copy"> + <p class="asf-band__title">The Apache Software Foundation</p> + <p class="asf-band__sub">Apache StormCrawler is a project of the ASF, made in the open by a community of volunteers.</p> + </div> + <span class="asf-band__links"> + <a href="https://www.apache.org/" target="_blank" rel="noopener">APACHE.ORG ↗</a> + <a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="noopener">LICENSE</a> + <a href="https://privacy.apache.org/policies/privacy-policy-public.html" target="_blank" rel="noopener">PRIVACY</a> + <a href="https://www.apache.org/security/" target="_blank" rel="noopener">SECURITY</a> + <a href="https://www.apache.org/foundation/sponsorship" target="_blank" rel="noopener">SPONSORSHIP</a> + <a href="https://www.apache.org/foundation/sponsors" target="_blank" rel="noopener">SPONSORS</a> + <a href="https://www.apache.org/events/current-event.html" target="_blank" rel="noopener">EVENTS</a> + </span> </div> + <p class="colophon">© 2026 The Apache Software Foundation · Licensed under the Apache License, Version 2.0<br>Apache StormCrawler, StormCrawler and the Apache logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.</p> </div> </footer> diff --git a/_includes/head.html b/_includes/head.html index 4e30f81..97f7acd 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -2,8 +2,8 @@ <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="theme-color" media="(prefers-color-scheme: light)" content="#FBFCFF"> - <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0B0E1A"> + <meta name="theme-color" media="(prefers-color-scheme: light)" content="#F7F7F3"> + <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#141312"> <title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title> <meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}"> diff --git a/_includes/header.html b/_includes/header.html index b7fe68b..fddc1a9 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -2,33 +2,27 @@ <div class="site-header__inner"> <div class="site-header__logo"> <a href="{{ site.baseurl }}/" aria-label="Apache StormCrawler home"> - <img class="logo-light" src="{{ site.baseurl }}/img/logo.png" alt="Apache StormCrawler"> - <img class="logo-dark" src="{{ site.baseurl }}/img/logo-dark.svg" alt="Apache StormCrawler"> + <img class="only-light" src="{{ site.baseurl }}/img/logo.png" alt="Apache StormCrawler"> + <img class="only-dark" src="{{ site.baseurl }}/img/logo-dark.svg" alt="Apache StormCrawler"> </a> </div> + <nav class="links" aria-label="Main menu"> + <a class="site-nav-link{% if page.slug == 'home' %} active{% endif %}" href="{{ site.baseurl }}/">Home</a> + <a class="site-nav-link{% if page.slug == 'download' %} active{% endif %}" href="{{ site.baseurl }}/download/">Download</a> + <a class="site-nav-link{% if page.slug == 'documentation' %} active{% endif %}" href="{{ site.baseurl }}/docs/">Documentation</a> + <a class="site-nav-link{% if page.slug == 'contribute' %} active{% endif %}" href="{{ site.baseurl }}/contribute/">Contribute</a> + <a class="site-nav-link{% if page.slug == 'security' %} active{% endif %}" href="{{ site.baseurl }}/security/">Security</a> + <a class="site-nav-link{% if page.slug == 'support' %} active{% endif %}" href="{{ site.baseurl }}/support/">Support</a> + <a class="site-nav-link{% if page.slug == 'team' %} active{% endif %}" href="{{ site.baseurl }}/team/">Team</a> + </nav> <div class="site-header__actions"> - <a class="icon-btn" href="https://github.com/apache/stormcrawler" aria-label="Apache StormCrawler on GitHub" title="GitHub"> - <svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.8 [...] + <a class="gh-pill" href="https://github.com/apache/stormcrawler" target="_blank" rel="noopener" aria-label="Apache StormCrawler on GitHub"> + <span class="gh-txt">GitHub ↗</span><svg class="gh-ico" width="17" height="17" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64- [...] </a> - <button class="icon-btn theme-toggle" id="theme-toggle" aria-label="Toggle dark mode" title="Toggle dark mode">☾</button> - <a class="icon-btn" href="{{ site.baseurl }}/download/" aria-label="Download" title="Download"> - <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v11m0 0-5-5m5 5 5-5M4 20h16"/></svg> - </a> - <button class="icon-btn nav-toggle" id="nav-toggle" aria-expanded="false" aria-controls="mobile-nav" aria-label="Menu"> - <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M2 4.5h14M2 9h14M2 13.5h14"/></svg> + <button class="icon-btn nav-burger" id="nav-toggle" aria-expanded="false" aria-controls="site-header" aria-label="Menu"> + <svg width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M2 4.5h14M2 9h14M2 13.5h14"/></svg> </button> </div> </div> - <nav class="mobile-nav" id="mobile-nav" aria-label="Main menu"> - <ul> - <li><a href="{{ site.baseurl }}/index.html"{% if page.slug == 'home' %} class="active"{% endif %}>Home</a></li> - <li><a href="{{ site.baseurl }}/download/index.html"{% if page.slug == 'download' %} class="active"{% endif %}>Download</a></li> - <li><a href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Quick Start</a></li> - <li><a href="{{ site.baseurl }}/docs/"{% if page.slug == 'documentation' %} class="active"{% endif %}>Documentation</a></li> - <li><a href="{{ site.baseurl }}/contribute/"{% if page.slug == 'contribute' %} class="active"{% endif %}>Contribute</a></li> - <li><a href="{{ site.baseurl }}/security/"{% if page.slug == 'security' %} class="active"{% endif %}>Security</a></li> - <li><a href="{{ site.baseurl }}/support/"{% if page.slug == 'support' %} class="active"{% endif %}>Support</a></li> - <li><a href="{{ site.baseurl }}/team/"{% if page.slug == 'team' %} class="active"{% endif %}>Team</a></li> - </ul> - </nav> </header> +<button class="icon-btn theme-toggle" id="theme-toggle" aria-label="Toggle dark mode" title="Toggle dark mode">☾</button> diff --git a/_layouts/default.html b/_layouts/default.html index 949afe0..dc8c0f6 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -59,8 +59,6 @@ </body> - <script src="https://www.apachecon.com/event-images/snippet.js"></script> - <!-- Matomo --> <script> var _paq = window._paq = window._paq || []; diff --git a/contribute/index.html b/contribute/index.html index b062fb8..5d0a864 100644 --- a/contribute/index.html +++ b/contribute/index.html @@ -2,32 +2,43 @@ layout: default slug: contribute title: How to contribute to Apache StormCrawler -eyebrow: Community +eyebrow: COMMUNITY heading: How to Contribute -description: Discussions, issues and pull requests. How to get involved in Apache StormCrawler. +description: Questions, bug reports and pull requests are all welcome. --- -<h2 id="the-apache-stormcrawler-community">The Apache StormCrawler Community</h2> - <p>If you have questions about the contribution process or want to discuss specific issues, please interact with the community using the following resources.</p> - - <ul> - <li><a href="https://lists.apache.org/[email protected]">StormCrawler dev mailing list</a>: for StormCrawler development discussions. <a href="mailto:[email protected]">Subscribe</a> <a href="mailto:[email protected]">Unsubscribe</a></li> - <li><a href="https://github.com/apache/stormcrawler/discussions">GitHub discussions</a>: for questions and general discussion.</li> - <li><a href="https://github.com/apache/stormcrawler/issues">GitHub issues</a>: for bug reports and feature requests. Please search the existing issues before creating new ones. If you are unsure whether you have found a bug, consider asking in GitHub discussions or the mailing list first.</li> - </ul> - - <p>With GitHub, the StormCrawler code is located in this <a href="https://github.com/apache/stormcrawler" target="_blank">repository</a>. The website code is in this <a href="https://github.com/apache/stormcrawler-site" target="_blank">repository</a>. Links for GitHub discussions and issues above are related to the core repository. If you are aware of the right repository, then please use the discussions and issues for that repository.</p> - - <h2 id="code-of-conduct">Code of Conduct</h2> - <p>Please see the Apache-wide <a href="https://www.apache.org/foundation/policies/conduct">Code of Conduct</a>.</p> - - <h2 id="committers">Committers</h2> - <p>The current list of Apache StormCrawler committers can be found in the Apache <a href="https://people.apache.org/phonebook.html?project=stormcrawler">Phonebook</a>.</p> - - <p>All code merges require approval from at least one StormCrawler committer. Most StormCrawler committers are also members of the PMC. Releases require that at least three StormCrawler PPMC members approve the release.</p> - - <p>We are looking to build up the number of committers and PMC members. We will invite people to join the team. This is based on meritocracy. Contributors who have become involved in the StormCrawler community and who have shown a good level of understanding of the StormCrawler code base will be considered. </p> - - <p>Code reviews and votes on releases can be made by anyone.</p> - - +<p>If you have questions about the contribution process or want to discuss specific issues, please interact with the community using the resources below.</p> + +<h2>Your thread into the web</h2> +<div class="vchain"> + <div class="vlink"> + <p class="slabel">01 — DISCUSS</p> + <h3>Join the conversation</h3> + <p>Development discussions happen on the <b>dev</b> mailing list and GitHub discussions.</p> + <div class="chip-cluster"><a class="chip" href="mailto:[email protected]">✉ SUBSCRIBE</a><a class="chip" href="https://lists.apache.org/[email protected]" target="_blank" rel="noopener">ARCHIVE ↗</a><a class="chip" href="https://github.com/apache/stormcrawler/discussions" target="_blank" rel="noopener">DISCUSSIONS ↗</a></div> + </div> + <div class="vlink"> + <p class="slabel">02 — REPORT</p> + <h3>File what you find</h3> + <p>Bug reports and feature requests go to GitHub issues. Please search existing issues before creating new ones.</p> + <div class="chip-cluster"><a class="chip" href="https://github.com/apache/stormcrawler/issues" target="_blank" rel="noopener">ISSUES ↗</a></div> + </div> + <div class="vlink"> + <p class="slabel">03 — PATCH</p> + <h3>Open a pull request</h3> + <p>All code merges require approval from at least one StormCrawler committer.</p> + <div class="chip-cluster"><a class="chip" href="https://github.com/apache/stormcrawler/pulls" target="_blank" rel="noopener">PULL REQUESTS ↗</a></div> + </div> + <div class="vlink"> + <p class="slabel">04 — RELEASE</p> + <h3>Ship it together</h3> + <p>Releases require that at least three StormCrawler PMC members approve the release.</p> + <div class="chip-cluster"><a class="chip" href="{{ site.baseurl }}/download/">DOWNLOAD →</a></div> + </div> +</div> + +<h2>Code of Conduct</h2> +<p>Please see the Apache-wide <a href="https://www.apache.org/foundation/policies/conduct.html" target="_blank" rel="noopener">Code of Conduct</a>.</p> + +<h2>Committers</h2> +<p>Most StormCrawler committers are also members of the PMC. Meet them on the <a href="{{ site.baseurl }}/team/">Team page</a>.</p> diff --git a/css/main.scss b/css/main.scss index a0e5a52..2ef819c 100644 --- a/css/main.scss +++ b/css/main.scss @@ -1,136 +1,112 @@ --- --- -// Charset -@charset "utf-8"; - -// --------------------------------------------------------------------------- -// "electric-storm" — Apache StormCrawler site theme -// Graphite storm-gray surfaces, deep spider-red accent, lightning-silver arcs. -// All components consume CSS custom properties; SCSS vars hold raw hex only. -// --------------------------------------------------------------------------- - -// Fonts: native system stack — no web fonts, no external requests. - -// --- Raw scales (SCSS source of truth) -------------------------------------- - -// Storm gray — graphite neutrals -$storm-950: #101113; -$storm-900: #16181B; -$storm-850: #1C1F23; -$storm-800: #24272C; -$storm-700: #33373E; -$storm-600: #4A505A; -$storm-400: #848B95; -$storm-200: #CDD2D9; -$storm-100: #E9EBEE; -$storm-50: #F4F5F7; - -// Spider red — brand accent; $brand-600 is the logo red itself. -// Deep maroon darks and muted rose tints on graphite, never red-on-black-on-white. -$brand-50: #FBEDED; -$brand-100: #F5D6D6; -$brand-200: #E8A9AB; -$brand-300: #DC7F82; -$brand-400: #C94F53; -$brand-500: #B03036; -$brand-600: #99181C; -$brand-700: #7C1114; -$brand-800: #5A0E12; -$brand-900: #3B0A0D; - -// Lightning silver — arcs and sparks -$arc-glow: #DDE3EB; // decorative only, never text -$arc-mid: #B9C2CE; // decorative only, never text -$arc-text-light: #5A6470; // text-safe on light (>=4.5:1 on --bg and --bg-alt) -$arc-text-dark: #C9D1DB; // text-safe on dark - -// Aliases for the eyebrow/divider accents (same red family) -$red-brand: #99181C; // logo red, text-safe on light backgrounds -$red-mid: #B03036; // decorative only -$red-text-dark: #DC7F82; // text-safe on dark - -// Font stacks — native system UI fonts (matching a plain, classic look) -$font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; -$font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; -$font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - -// --- Semantic tokens --------------------------------------------------------- +// ---------------------------------------------------------------------------- +// Apache StormCrawler — site stylesheet ("Il Crawl"). +// Single file, no imports, no webfonts (ASF rule: system stacks only). +// Sections: tokens · base · header/footer · home · inner pages. +// ---------------------------------------------------------------------------- + +// --- Webfont: Archivo, self-hosted (precedent: css/resources/ubuntu-mono.css) + +@font-face { + font-family: "Archivo"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("/css/resources/archivo-latin-variable.woff2") format("woff2"); +} + +// --- Tokens: colour --------------------------------------------------------- :root { - --bg: #FAFAFB; - --bg-alt: #{$storm-50}; + --bg: #F7F7F3; + --bg-alt: #EFEFE8; --surface: #FFFFFF; - --surface-raised: #{$storm-50}; - --text: #17191D; - --text-secondary: #4C525B; - --muted: #6B7280; - --border: #E0E3E8; - --border-strong: #{$brand-200}; - --accent: #{$brand-600}; - --accent-hover: #{$brand-700}; - --accent-2: #{$arc-text-light}; - --accent-2-glow: #{$arc-mid}; - --chip-bg: #{$brand-50}; - --chip-border: #{$brand-200}; - --chip-text: #{$brand-700}; - --icon-chip-bg: #{$brand-100}; - --icon-chip-fg: #{$brand-700}; - --code-bg: #{$storm-50}; - --nav-bg: rgba(255,255,255,.9); - --header-border: #{$storm-200}; - --focus-ring: #{$arc-text-light}; - --shadow-sm: 0 1px 2px rgba(22,24,27,.08); - --shadow-md: 0 4px 16px -2px rgba(22,24,27,.14); - --success: #177A4C; - --warning: #B26E00; + --text: #191A16; + --text-secondary: #474C42; + --muted: #71766A; + --border: #DBDDD2; + --border-strong: #191A16; + --accent: #C33540; + --accent-ink: #A32830; + --accent-hover: #D8464F; + --chip-bg: #FFFFFF; + --chip-border: #DBDDD2; + --chip-text: #474C42; + --icon-chip-bg: #191A16; + --icon-chip-fg: #E07078; + --code-bg: #191A16; + --code-fg: #D8D6D0; + --nav-bg: rgba(247,247,243,.92); + --header-border: #DBDDD2; + --focus-ring: #A32830; + --shadow-sm: 0 1px 2px rgba(25,26,22,.06); + --shadow-md: 0 6px 24px -4px rgba(25,26,22,.14); --error: #C0332B; - --brand-red: #{$red-brand}; - --brand-red-glow: #{$red-mid}; - - // Theme-invariant tokens: identical in both themes, used by the dark - // bookends (hero, CTA band, footer, topology panel). - --invariant-bg-deep: #{$storm-950}; - --invariant-surface: #{$storm-850}; - --invariant-surface-glass: rgba(28,31,35,.65); - --invariant-border: #{$storm-600}; - --invariant-text: #{$storm-100}; - --invariant-text-secondary: #{$storm-200}; + --web-stroke: #C0C4AC; + --invariant-bg-deep: #121110; + --invariant-ink: #191A16; + --invariant-border: #2E2B28; + --invariant-text: #F7F7F3; + --invariant-text-secondary: #ABA7A0; + --invariant-muted: #8D8983; } [data-theme="dark"] { - --bg: #{$storm-900}; - --bg-alt: #{$storm-850}; - --surface: #{$storm-800}; - --surface-raised: #22252A; - --text: #{$storm-100}; - --text-secondary: #{$storm-200}; - --muted: #{$storm-400}; - --border: #{$storm-700}; - --border-strong: #{$brand-600}; - --accent: #{$brand-300}; - --accent-hover: #{$brand-200}; - --accent-2: #{$arc-text-dark}; - --accent-2-glow: #{$arc-glow}; - --chip-bg: rgba(176,48,54,.18); - --chip-border: #{$brand-700}; - --chip-text: #{$brand-200}; - --icon-chip-bg: #{$brand-900}; - --icon-chip-fg: #{$brand-200}; - --code-bg: #141619; - --nav-bg: rgba(36,39,44,.92); - --header-border: #{$storm-600}; - --focus-ring: #{$arc-glow}; + --bg: #141312; + --bg-alt: #1A1917; + --surface: #1D1B19; + --text: #EDEBE7; + --text-secondary: #B8B4AD; + --muted: #8D8983; + --border: #2E2B28; + --border-strong: #4C4844; + --accent: #E07078; + --accent-ink: #E07078; + --accent-hover: #EC8B92; + --chip-bg: #1D1B19; + --chip-border: #2E2B28; + --chip-text: #B8B4AD; + --icon-chip-bg: #2E2B28; + --code-bg: #0E0D0B; + --nav-bg: rgba(20,19,18,.92); + --header-border: #2E2B28; + --focus-ring: #E07078; --shadow-sm: 0 1px 2px rgba(0,0,0,.5); - --shadow-md: 0 4px 20px -4px rgba(0,0,0,.6); - --success: #2BB673; - --warning: #F5A623; - --error: #F0564A; - --brand-red: #{$red-text-dark}; - --brand-red-glow: #{$red-mid}; + --shadow-md: 0 8px 28px -6px rgba(0,0,0,.6); + --error: #E07078; + --web-stroke: #403C36; } -// --- Reset & base ------------------------------------------------------------ +// --- Tokens: type, space, motion -------------------------------------------- + +:root { + --font-ui: "Archivo",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; + --font-mono: SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace; + --text-base: 16px; + --text-hero: 74px; + --text-section-h2: 38px; + --text-stage-h2: 30px; + --text-cta-h2: 44px; + --text-nav: 15px; + --text-code: 13px; + --text-legal: 10.5px; + --radius-pill: 100px; + --radius-card: 14px; + --radius-node: 50%; + --radius-panel: 10px; + --page-pad-x: 52px; + --gutter-x: max(var(--page-pad-x),calc((100% - 1176px)/2)); + --rail-x: calc(var(--gutter-x) + 23px); + --nav-h: 80px; + --section-pad: 76px 52px; + --width-content: 1080px; + --width-prose: 860px; + --motion-fast: .15s ease; +} + +// --- Base ------------------------------------------------------------------- * { margin: 0; @@ -139,515 +115,634 @@ $font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier -webkit-font-smoothing: antialiased; } -html { - scroll-padding-top: 5rem; - scroll-behavior: smooth; +html { scroll-padding-top: 6rem; } + +html,body { + max-width: 100%; + overflow-x: hidden; } body { background: var(--bg); color: var(--text); - font-family: $font-body; - font-size: 1rem; + font-family: var(--font-ui); + font-size: var(--text-base); line-height: 1.65; - font-weight: 400; + font-weight: 500; } -img { - max-width: 100%; -} +img { max-width: 100%; } a { - color: var(--accent); + color: var(--accent-ink); text-decoration: none; } -h1, h2, h3 { - font-family: $font-display; +a:hover { + color: var(--accent-ink); + text-decoration: underline; + text-underline-offset: 3px; +} + +h1,h2,h3 { color: var(--text); + font-family: var(--font-ui); +} + +code,pre,kbd { font-family: var(--font-mono); } + +:focus-visible { + outline: 2px solid var(--focus-ring); + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + * { + animation: none !important; + transition: none !important; + } +} + +.only-light { display: block; } + +.only-dark { display: none !important; } + +[data-theme="dark"] .only-light { display: none !important; } + +[data-theme="dark"] .only-dark { display: block !important; } + +// --- Blur-fade reveal on scroll; JS adds .in-view, no-js and reduced motion show everything --- + +.reveal { + opacity: 0; + filter: blur(6px); + transform: translateY(14px); + transition: opacity .6s ease,filter .6s ease,transform .6s ease; + transition-delay: calc(var(--i,0) * 90ms); } -code, pre, kbd, samp { - font-family: $font-mono; +.reveal.in-view { + opacity: 1; + filter: none; + transform: none; } -:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } +.no-js .reveal { + opacity: 1; + filter: none; + transform: none; +} @media (prefers-reduced-motion: reduce) { - html { scroll-behavior: auto; } + .reveal { + opacity: 1; + filter: none; + transform: none; + } +} + +// --- Shared bits ------------------------------------------------------------ + +.eyebrow { + display: inline-flex; + align-items: center; + gap: 8px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 12px; + line-height: 1.4; + letter-spacing: .12em; + text-transform: uppercase; + color: var(--text-secondary); +} + +.eyebrow .pill { + background: var(--accent); + color: #fff; + padding: 2px 8px; + border-radius: var(--radius-pill); } -// --- Shared primitives ------------------------------------------------------- +.slabel { + font-family: var(--font-mono); + font-weight: 600; + font-size: 11px; + letter-spacing: .14em; + text-transform: uppercase; + color: var(--accent-ink); +} .btn { display: inline-flex; align-items: center; justify-content: center; gap: .5rem; - border-radius: 8px; - padding: .75rem 1.5rem; - font-family: $font-display; - font-weight: 500; - font-size: .9375rem; + white-space: nowrap; + border-radius: var(--radius-pill); + padding: 15px 30px; + font-family: var(--font-ui); + font-weight: 600; + font-size: 15px; line-height: 1.2; - border: 1px solid transparent; + border: 1.5px solid transparent; cursor: pointer; - transition: background-color .15s ease, border-color .15s ease, color .15s ease, transform .15s ease; + transition: transform var(--motion-fast),background var(--motion-fast),color var(--motion-fast),border-color var(--motion-fast); } .btn--solid { - background: #{$brand-500}; - color: #fff; - &:hover { - background: #{$brand-600}; - color: #fff; - transform: translateY(-1px); - } + background: var(--icon-chip-bg); + color: var(--icon-chip-fg); } -.btn--ghost { - background: rgba(255,255,255,.08); - border-color: rgba(255,255,255,.22); - color: #fff; - &:hover { - background: rgba(255,255,255,.16); - color: #fff; - transform: translateY(-1px); - } +.btn--solid:hover { + transform: translateY(-1px); + color: var(--accent-hover); + text-decoration: none; } -// Chip-link -.chip { - display: inline-flex; - align-items: center; - gap: .375rem; - border-radius: 6px; - border: 1px solid var(--chip-border); - background: var(--chip-bg); - padding: .25rem .625rem; - font-family: $font-body; - font-weight: 500; - font-size: .75rem; - line-height: 1.33; - color: var(--chip-text); - transition: filter .15s ease; - svg { flex: 0 0 auto; } - &:hover { filter: brightness(.96); color: var(--chip-text); } +[data-theme="dark"] .btn--solid { + background: var(--accent); + color: #191A16; } -[data-theme="dark"] .chip:hover { filter: brightness(1.25); } +[data-theme="dark"] .btn--solid:hover { + background: var(--accent-hover); + color: #191A16; +} -// Solid badge (release strip, version cards) -.badge { - display: inline-block; - border-radius: 6px; - padding: .2rem .625rem; - font-family: $font-display; - font-weight: 700; - font-size: .75rem; - background: #{$brand-500}; - color: #fff; +.btn--ghost { + background: transparent; + border-color: var(--border-strong); + color: var(--text); +} + +.btn--ghost:hover { + transform: translateY(-1px); + border-color: var(--accent-ink); + text-decoration: none; } -.badge--muted { +.btn--ondark { background: transparent; - border: 1px solid var(--border); - color: var(--muted); - font-family: $font-body; - font-weight: 500; + border-color: #4C4844; + color: var(--invariant-text); } -// Icon chip -.icon-chip { - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - width: 40px; - height: 40px; - border-radius: 10px; - background: var(--icon-chip-bg); - color: var(--icon-chip-fg); - svg { display: block; } +.btn--ondark:hover { + border-color: var(--accent-hover); + color: #fff; + text-decoration: none; } -// Eyebrow + bolt divider (section openers) -.bolt-divider { - width: 48px; - height: 1px; - margin: 0 auto 1rem; - background: linear-gradient(90deg, transparent, var(--brand-red-glow), transparent); +.btn--cta { + background: var(--accent); + color: #fff; + font-weight: 700; } -.eyebrow { +.btn--cta:hover { + background: var(--accent-hover); + transform: translateY(-1px); + color: #fff; + text-decoration: none; +} + +[data-theme="dark"] .btn--cta,[data-theme="dark"] .btn--cta:hover { color: #191A16; } + +.chip { display: inline-flex; align-items: center; - gap: .375rem; - font-family: $font-body; - font-weight: 500; - font-size: .75rem; - line-height: 1.33; + gap: 6px; + white-space: nowrap; + border-radius: var(--radius-pill); + border: 1px solid var(--chip-border); + background: var(--chip-bg); + padding: 7px 12px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; - color: var(--brand-red); - svg { color: var(--brand-red-glow); } + color: var(--chip-text); + transition: border-color var(--motion-fast),color var(--motion-fast); } -// Section scaffolding -.section { - padding: 6rem 2rem; - @media (max-width: 720px) { padding: 3.5rem 1rem; } +.chip:hover { + border-color: var(--accent-ink); + color: var(--text); + text-decoration: none; } -.section--alt { background: var(--bg-alt); } +.chip svg { flex: 0 0 auto; } -.section__inner { - max-width: 1100px; - margin: 0 auto; +.chip-cluster { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; } -.section-head { - max-width: 680px; - margin: 0 auto; - text-align: center; - padding-bottom: 3rem; - h2 { - font-weight: 700; - font-size: clamp(1.6875rem, calc(1rem + 2.2vw), 2.25rem); - line-height: 1.15; - letter-spacing: -.025em; - margin: .625rem 0 .75rem; - } - p { - color: var(--text-secondary); - } +.badge { + display: inline-block; + border-radius: var(--radius-pill); + padding: 2px 8px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 12px; + background: var(--icon-chip-bg); + color: var(--icon-chip-fg); } -// Card recipe -.card { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 14px; - padding: 1.5rem; - box-shadow: var(--shadow-sm); - transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; - &:hover { - border-color: var(--border-strong); - box-shadow: var(--shadow-md); - transform: translateY(-2px); - } +.slink { + display: inline-flex; + align-items: center; + gap: 8px; + margin-top: 18px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 11px; + letter-spacing: .08em; + color: var(--text); + border-bottom: 2px solid var(--accent); + padding-bottom: 3px; } -.card--grid { - display: flex; - flex-direction: column; - gap: .75rem; - .chip { margin-top: auto; align-self: flex-start; } +.slink:hover { + color: var(--accent-ink); + text-decoration: none; } -.card__title { - font-family: $font-body; - font-weight: 600; - font-size: .9375rem; - line-height: 1.4; - color: var(--text); +.section { + padding: var(--section-pad); + border-top: 1px solid var(--border); } -.card__copy { - font-size: .84375rem; - line-height: 1.5; - color: var(--text-secondary); +.section--alt { background: var(--bg-alt); } + +.section__inner { + max-width: 1176px; + margin: 0 auto; } -.card--highlight { - background: linear-gradient(to right, var(--chip-bg), transparent); - border: 2px solid var(--chip-border); +.display-h2 { + font-size: var(--text-section-h2); + font-weight: 900; + letter-spacing: -.02em; + text-transform: uppercase; + color: var(--text); } -// Transition strips -.strip { - height: 6rem; +.icon-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + border-radius: var(--radius-pill); + border: 1.5px solid var(--border); + background: transparent; + color: var(--text); + cursor: pointer; + font-size: 16px; + line-height: 1; + transition: border-color var(--motion-fast),color var(--motion-fast),transform var(--motion-fast); } -.strip--hero { background: linear-gradient(to bottom, #{$brand-800}, var(--bg)); } -.strip--cta { background: linear-gradient(to bottom, var(--bg-alt), #{$storm-950}); } -.strip--page { - height: 3rem; - background: linear-gradient(to bottom, #{$brand-800}, var(--bg)); +.icon-btn:hover { + border-color: var(--accent-ink); + color: var(--accent-ink); } -// --- Header ------------------------------------------------------------------ +// --- Header ----------------------------------------------------------------- .site-header { position: sticky; top: 0; z-index: 50; background: var(--nav-bg); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); border-bottom: 1px solid var(--header-border); - box-shadow: var(--shadow-sm); } .site-header__inner { display: flex; align-items: center; - gap: 1.25rem; - max-width: 1240px; + gap: 30px; + max-width: 1280px; margin: 0 auto; - padding: .875rem 2rem; - @media (max-width: 720px) { padding: .875rem 1rem; } + padding: 0 var(--page-pad-x); + height: var(--nav-h); } .site-header__logo { + flex: 0 0 auto; + margin-right: auto; display: flex; align-items: center; - margin-right: auto; - a { display: flex; align-items: center; } - img { height: 34px; width: auto; display: block; } } -// Dark mode logo swap (mechanism unchanged) -.site-header__logo .logo-light { display: inline; } -.site-header__logo .logo-dark { display: none; } -[data-theme="dark"] .site-header__logo .logo-light { display: none; } -[data-theme="dark"] .site-header__logo .logo-dark { display: inline; } +.site-header__logo img { + height: 44px; + width: auto; + max-width: none; + flex: 0 0 auto; + display: block; +} .site-header__actions { display: flex; align-items: center; - gap: .5rem; + gap: 10px; + flex: 0 0 auto; } -.icon-btn { - display: inline-flex; +nav.links { + display: flex; align-items: center; - justify-content: center; - width: 36px; - height: 36px; - border-radius: 8px; - border: 1px solid var(--border); - background: transparent; + gap: 30px; +} + +nav.links a { white-space: nowrap; } + +.site-nav-link { + font-size: var(--text-nav); + font-weight: 500; color: var(--text-secondary); - cursor: pointer; - font-size: 1rem; - transition: border-color .15s ease, color .15s ease; - &:hover { border-color: var(--accent); color: var(--accent); } + position: relative; } -.theme-toggle { - font-size: 1.05rem; - line-height: 1; +.site-nav-link:hover { + color: var(--text); + text-decoration: none; +} + +.site-nav-link.active { + font-weight: 600; + color: var(--text); } -// The nav is always collapsed behind the burger, at every viewport width. -.mobile-nav { - display: none; +.site-nav-link.active::after { + content: ""; position: absolute; - top: 100%; left: 0; right: 0; - background: var(--bg); - border-bottom: 1px solid var(--border); + bottom: -8px; + height: 4px; + background: var(--accent); +} + +.gh-pill { + display: inline-flex; + align-items: center; + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + background: var(--accent); + color: #fff; + padding: 10px 16px; + border-radius: var(--radius-pill); + white-space: nowrap; + transition: background var(--motion-fast); +} + +.gh-pill:hover { + background: var(--accent-hover); + color: #fff; + text-decoration: none; +} + +.gh-pill .gh-ico { display: none; } + +[data-theme="dark"] .gh-pill,[data-theme="dark"] .gh-pill:hover { color: #191A16; } + +.nav-burger { display: none; } + +// --- Theme toggle: fixed bottom-right in every viewport, out of the header flow --- + +.theme-toggle { + position: fixed; + bottom: 18px; + right: 18px; + z-index: 60; + width: 46px; + height: 46px; + background: var(--surface); + border: 1.5px solid var(--border); box-shadow: var(--shadow-md); - &.open { display: block; } - ul { list-style: none; } - a { - display: flex; - align-items: center; - min-height: 44px; - padding: 0 1.5rem; - font-size: .9375rem; - font-weight: 500; - color: var(--text-secondary); - border-top: 1px solid var(--border); - &:hover, &.active { color: var(--accent); } - } - // On wider screens the open menu becomes a right-aligned dropdown card - @media (min-width: 720px) { - left: auto; - right: max(1rem, calc((100vw - 1240px) / 2 + 2rem)); - min-width: 240px; - border: 1px solid var(--border); - border-radius: 12px; - overflow: hidden; - margin-top: .375rem; - a { border-top: none; padding: 0 1.25rem; } - li + li a { border-top: 1px solid var(--border); } - } } -// --- Hero -------------------------------------------------------------------- +// --- Home: hero ------------------------------------------------------------- .hero { + // one square fluid box for the web; the spider hangs at its hub + --web: clamp(560px,56vw,900px); position: relative; - background: #{$storm-950}; + overflow: hidden; + padding: 92px var(--gutter-x) 96px; + background: var(--bg); + min-height: calc(var(--web) * .85); } -.hero__inner { +.hero__grid { position: relative; - display: flex; - align-items: center; - gap: 4rem; - max-width: 1240px; + max-width: 1280px; margin: 0 auto; - padding: 4.5rem 2rem 6.5rem; - @media (max-width: 960px) { - flex-direction: column; - align-items: stretch; - gap: 3rem; - } - @media (max-width: 720px) { padding: 3rem 1rem 4rem; } + z-index: 1; } .hero__copy { - max-width: 560px; - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 1.75rem; + max-width: 660px; + position: relative; } .hero__title { - color: #fff; - font-weight: 700; - font-size: clamp(2.125rem, calc(1rem + 3.6vw), 3.375rem); - line-height: 1.08; + font-size: var(--text-hero); + line-height: .98; letter-spacing: -.03em; + font-weight: 900; + text-transform: uppercase; + color: var(--text); + margin-bottom: 26px; } +.hero__title .dot { color: var(--accent-ink); } + .hero__lede { - font-size: 1.125rem; - line-height: 1.6; - color: var(--invariant-text-secondary); - strong { color: var(--invariant-text); font-weight: 600; } + font-size: 18px; + line-height: 1.65; + color: var(--text-secondary); + margin-bottom: 32px; + max-width: 520px; } +.hero__lede strong { color: var(--text); } + .hero__ctas { display: flex; flex-wrap: wrap; - gap: .75rem; + gap: 12px; + align-items: center; } .hero__assurance { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: .375rem 1rem; - font-size: .75rem; - color: var(--invariant-text-secondary); - span { - display: inline-flex; - align-items: center; - gap: .375rem; - } - svg { color: #{$arc-glow}; flex: 0 0 auto; } + font-family: var(--font-mono); + font-size: 12px; + color: var(--text-secondary); + margin-left: 8px; } -// Topology panel — the signature element -.hero__panel { - flex: 0 0 auto; - display: flex; - justify-content: center; - @media (max-width: 960px) { margin: 0 auto; } +.hero__webbox { + position: absolute; + width: var(--web); + height: var(--web); + top: calc(var(--web) * -.153); + right: calc(var(--web) * -.222); + pointer-events: none; } -.hero__mark { +.hero__webbox svg { + display: block; width: 100%; - max-width: 340px; + height: 100%; +} + +.hero__webbox .hero__mark { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%,-50%); + width: 32.8%; height: auto; - @media (max-width: 960px) { max-width: 240px; } + max-width: none; } -// --- Landing sections ---------------------------------------------------------- +// --- Home: pipeline rail 01→04 ---------------------------------------------- -.features-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-auto-rows: 1fr; - gap: 1rem; - @media (max-width: 720px) { grid-template-columns: 1fr; } +.rail { position: relative; } + +.rail::before { + content: ""; + position: absolute; + left: var(--rail-x); + top: 0; + bottom: 0; + width: 2px; + background: var(--border-strong); } -.about-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-auto-rows: 1fr; - gap: 1rem; - @media (max-width: 900px) { grid-template-columns: 1fr; } - .card { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: .75rem; - } - .card .chip { margin-top: auto; } +.stage { + position: relative; + padding: 64px var(--gutter-x) 64px calc(var(--gutter-x) + 98px); + border-top: 1px solid var(--border); } -// Quick start -.quickstart { - max-width: 760px; - margin: 0 auto; +.stage__node { + position: absolute; + left: var(--gutter-x); + top: 64px; + width: 46px; + height: 46px; + border-radius: var(--radius-node); + background: var(--bg); + border: 2px solid var(--border-strong); display: flex; - flex-direction: column; - gap: 1.5rem; + align-items: center; + justify-content: center; + font: 600 14px/1 var(--font-mono); + color: var(--text); +} + +.stage__node--accent { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} + +[data-theme="dark"] .stage__node--accent { color: #191A16; } + +.stage__title { + font-size: var(--text-stage-h2); + font-weight: 900; + letter-spacing: -.02em; + margin: 12px 0; + color: var(--text); +} + +.stage__copy { + font-size: 15px; + line-height: 1.7; + color: var(--text-secondary); + max-width: 560px; +} + +// --- Home: quick start ------------------------------------------------------ + +.qgrid { + display: grid; + grid-template-columns: 1.15fr 1fr; + gap: 48px; + align-items: start; } .code-row { position: relative; background: var(--code-bg); - border: 1px solid var(--border); - border-radius: 10px; - padding: 1rem 1.25rem; + border-radius: var(--radius-card); + padding: 26px 28px; overflow-x: auto; - pre { - font-family: $font-mono; - font-size: .875rem; - line-height: 1.5; - color: var(--text); - background: none; - padding: 0; - margin: 0; - border: 0; - } - .copy-chip { - position: absolute; - top: .625rem; - right: .625rem; - } +} + +.code-row pre { + font-family: var(--font-mono); + font-size: var(--text-code); + line-height: 1.9; + color: var(--code-fg); + background: none; + margin: 0; + padding: 0; + border: 0; + white-space: pre; +} + +.code-row .hl { color: var(--accent); } + +.code-row .copy-chip { + position: absolute; + top: 16px; + right: 16px; } .copy-chip { display: inline-flex; align-items: center; - gap: .375rem; - border-radius: 6px; - border: 1px solid var(--chip-border); - background: var(--chip-bg); - padding: .25rem .625rem; - font-family: $font-body; - font-weight: 500; - font-size: .75rem; - color: var(--chip-text); + gap: 6px; + border-radius: var(--radius-pill); + background: var(--accent); + border: 0; + padding: 5px 12px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 11px; + color: #fff; cursor: pointer; - transition: filter .15s ease; - &:hover { filter: brightness(.96); } } -[data-theme="dark"] .copy-chip:hover { filter: brightness(1.25); } +.copy-chip:hover { background: var(--accent-hover); } + +[data-theme="dark"] .copy-chip { color: #191A16; } .steps { display: flex; flex-direction: column; - gap: 1rem; + gap: 18px; list-style: none; } .step { display: flex; align-items: center; - gap: 1rem; - .chip { margin-left: auto; flex: 0 0 auto; } + gap: 16px; } .step__num { @@ -655,383 +750,391 @@ code, pre, kbd, samp { align-items: center; justify-content: center; flex: 0 0 auto; - width: 40px; - height: 40px; - border-radius: .75rem; + width: 34px; + height: 34px; + border-radius: var(--radius-node); + background: var(--surface); + border: 2px solid var(--border-strong); + font: 600 13px/1 var(--font-mono); + color: var(--text); +} + +.step:first-child .step__num { + background: var(--accent); + border-color: var(--accent); color: #fff; - font-family: $font-display; - font-weight: 700; - font-size: 1.125rem; } -.step:nth-child(1) .step__num { background: #{$brand-600}; } -.step:nth-child(2) .step__num { background: #{$brand-500}; } -.step:nth-child(3) .step__num { background: #{$brand-400}; } +[data-theme="dark"] .step:first-child .step__num { color: #191A16; } -.step__label { - font-size: .9375rem; - color: var(--text); +.step p { + font-size: 15px; + margin: 0; } -.chip-row { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: .75rem; +.step .chip { + margin-left: auto; + flex: 0 0 auto; } -// Powered-by logo band -.logo-band { +// --- Home: powered-by wall + closing band ----------------------------------- + +.logo-hang { background: var(--surface); border: 1px solid var(--border); - border-radius: 16px; - padding: 2.5rem; + border-radius: var(--radius-card); box-shadow: var(--shadow-sm); + margin-top: 40px; + padding: 2.5rem; display: flex; - flex-wrap: wrap; align-items: center; justify-content: center; - gap: 3rem; - img { - max-height: 44px; - width: auto; - filter: grayscale(1) opacity(.65); - transition: filter .15s ease; - } - a:hover img { filter: none; } + gap: 64px; + flex-wrap: wrap; } -[data-theme="dark"] .logo-band img { - filter: grayscale(1) invert(1) opacity(.65); +.logo-hang img { + filter: grayscale(1); + opacity: .85; + transition: filter var(--motion-fast),opacity var(--motion-fast); + max-width: none; + max-height: 44px; } -[data-theme="dark"] .logo-band a:hover img { - filter: invert(1); +.logo-hang a { + display: inline-flex; + align-items: center; + padding: 8px 12px; + border-radius: var(--radius-panel); + transition: background var(--motion-fast); } -.logo-band-more { - text-align: center; - margin-top: 1.5rem; +.logo-hang a:hover img { + filter: none; + opacity: 1; } -// Community -.community-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; - max-width: 1000px; - margin: 0 auto; - @media (max-width: 720px) { grid-template-columns: 1fr; } - .chip-cluster { - display: flex; - flex-wrap: wrap; - gap: .5rem; - margin-top: auto; - } +[data-theme="dark"] .logo-hang img { + filter: grayscale(1) invert(1); + opacity: .8; +} + +[data-theme="dark"] .logo-hang a:hover { background: #F7F7F3; } + +[data-theme="dark"] .logo-hang a:hover img { + filter: none; + opacity: 1; } -// Bottom CTA band .cta-band { - background: #{$storm-950}; + --web: clamp(420px,42vw,620px); + background: var(--invariant-ink); position: relative; - padding: 6rem 2rem; - text-align: center; - @media (max-width: 720px) { padding: 3.5rem 1rem; } + overflow: hidden; + padding: 84px var(--page-pad-x); } .cta-band__inner { position: relative; - max-width: 680px; + max-width: 1176px; margin: 0 auto; - display: flex; - flex-direction: column; - align-items: center; - gap: 1.25rem; - h2 { - color: #fff; - font-weight: 700; - font-size: clamp(1.6875rem, calc(1rem + 2.2vw), 2.25rem); - line-height: 1.15; - letter-spacing: -.025em; - } - p { color: var(--invariant-text-secondary); } - .hero__ctas { justify-content: center; } } -// --- Footer -------------------------------------------------------------------- - -.site-footer { - background: var(--invariant-bg-deep); - color: var(--invariant-text-secondary); - border-top: 1px solid #{$storm-700}; - padding: 3rem 2rem; - font-size: .84375rem; - @media (max-width: 720px) { padding: 2.5rem 1rem; } +.cta-band__web { + position: absolute; + width: var(--web); + height: var(--web); + right: calc(var(--web) * -.25); + bottom: calc(var(--web) * -.393); + pointer-events: none; } -.site-footer__inner { - max-width: 1100px; - margin: 0 auto; +.cta-band__web svg { + display: block; + width: 100%; + height: 100%; } -.site-footer__cols { - display: flex; - flex-wrap: wrap; - gap: 2rem; - padding-bottom: 2rem; +.cta-band h2 { + font-size: var(--text-cta-h2); + font-weight: 900; + letter-spacing: -.02em; + text-transform: uppercase; + color: var(--invariant-text); + margin-bottom: 14px; } -.site-footer__brand { - flex: 1 1 260px; - display: flex; - flex-direction: column; - align-items: flex-start; - gap: .75rem; - img { height: 32px; width: auto; } - .site-footer__tag { - display: inline-flex; - align-items: center; - gap: .5rem; - font-size: .75rem; - color: #{$storm-400}; - &:hover { color: #{$brand-200}; } - } - .site-footer__oakleaf { height: 20px; width: auto; } - .icon-btn { - border-color: #{$storm-700}; - color: #{$storm-400}; - &:hover { border-color: #{$brand-300}; color: #{$brand-200}; } - } +.cta-band p { + font-size: 16px; + color: var(--invariant-text-secondary); + margin-bottom: 30px; } -.site-footer__col { - flex: 1 1 140px; - h3 { - font-family: $font-body; - font-weight: 600; - font-size: .75rem; - letter-spacing: .08em; - text-transform: uppercase; - color: var(--invariant-text); - margin-bottom: .75rem; - } - ul { list-style: none; } - li { margin-bottom: .5rem; } - a { - color: #{$storm-400}; - transition: color .15s ease; - &:hover { color: #{$brand-200}; } - } -} +// --- Inner pages ------------------------------------------------------------ -.site-footer__legal { - border-top: 1px solid #{$storm-700}; - padding-top: 1.5rem; - text-align: center; - font-size: .75rem; - color: #{$storm-400}; - a { - color: #{$storm-400}; - text-decoration: underline; - text-underline-offset: 3px; - &:hover { color: #{$brand-200}; } - } - p { margin-bottom: .75rem; } +.page-hero { + padding: 72px var(--gutter-x) 40px; + border-bottom: 1px solid var(--border); } -.footer-widget { - margin-top: .5rem; +.page-hero__inner { + max-width: 1176px; + margin: 0 auto; } -// --- Secondary pages ------------------------------------------------------------- +.page-hero h1 { + font-size: 44px; + font-weight: 900; + letter-spacing: -.02em; + text-transform: uppercase; +} -.page-hero { - position: relative; - background: #{$storm-950}; - padding: 3.5rem 2rem 3rem; - @media (max-width: 720px) { padding: 2.5rem 1rem 2rem; } +.page-hero .page-hero__sub { + margin-top: 10px; + color: var(--text-secondary); + font-size: 16px; } -.page-hero__inner { - max-width: 800px; - margin: 0 auto; - .eyebrow { color: #{$red-text-dark}; svg { color: #{$red-text-dark}; } } - h1 { - color: #fff; - font-weight: 700; - font-size: clamp(1.75rem, calc(1rem + 2.4vw), 2.125rem); - line-height: 1.15; - letter-spacing: -.025em; - margin-top: .5rem; - } - .page-hero__sub { - margin-top: .75rem; - color: var(--invariant-text-secondary); - } +.page-hero .eyebrow { + color: var(--accent-ink); + letter-spacing: .14em; + font-size: 11px; + margin-bottom: 6px; } +.page-hero .eyebrow svg { display: none; } + .page-content { - max-width: 800px; + max-width: var(--width-content); margin: 0 auto; - padding: 3rem 2rem 5rem; - @media (max-width: 720px) { padding: 2rem 1rem 3.5rem; } - font-size: 1rem; + padding: 44px var(--page-pad-x) 90px; + font-size: 16px; + + // keep prose lines readable while grids and cards use the full width + > p, + > ul, + > ol, + .faq { max-width: var(--width-prose); } line-height: 1.7; +} - h2 { - font-weight: 700; - font-size: clamp(1.375rem, calc(1rem + 1vw), 1.625rem); - line-height: 1.25; - letter-spacing: -.02em; - margin: 3rem 0 1rem; - &::before { - content: ""; - display: block; - width: 32px; - height: 2px; - background: var(--accent-2-glow); - margin-bottom: .75rem; - } - &:first-child { margin-top: 0; } - } - - h3 { - font-weight: 500; - font-size: clamp(1.25rem, calc(1rem + .8vw), 1.375rem); - line-height: 1.3; - letter-spacing: -.01em; - margin: 2rem 0 .75rem; - } - - h4 { - font-family: $font-body; - font-weight: 600; - font-size: 1rem; - margin: 1.5rem 0 .5rem; - } +.page-content h2 { + font-weight: 800; + font-size: 26px; + letter-spacing: -.015em; + margin: 52px 0 16px; + padding-top: 20px; + border-top: 1px solid var(--border); +} - p { margin: 0 0 1.25rem; } +.page-content h2:first-child { + margin-top: 0; + border-top: 0; + padding-top: 0; +} - ul, ol { margin: 0 0 1.25rem 1.25rem; } - li { margin-bottom: .375rem; } +.page-content h3 { + font-weight: 700; + font-size: 19px; + margin: 34px 0 12px; +} - a { - color: var(--accent); - text-underline-offset: 3px; - &:hover { text-decoration: underline; color: var(--accent-hover); } - } +.page-content p { margin: 0 0 16px; } - code { - background: var(--code-bg); - border: 1px solid var(--border); - border-radius: 4px; - padding: .1em .35em; - font-size: .875em; - } +.page-content ul,.page-content ol { margin: 0 0 16px 22px; } - pre { - background: var(--code-bg); - border: 1px solid var(--border); - border-radius: 8px; - padding: 1rem 1.25rem; - overflow-x: auto; - margin: 0 0 1.25rem; - font-size: .875rem; - line-height: 1.5; - code { background: none; border: 0; padding: 0; font-size: 1em; } - } +.page-content li { margin-bottom: 6px; } - .table-wrap { overflow-x: auto; margin: 0 0 1.25rem; } - - table { - width: 100%; - border-collapse: collapse; - font-size: .9375rem; - th, td { - text-align: left; - padding: .625rem .75rem; - border-bottom: 1px solid var(--border); - } - thead tr { background: var(--bg-alt); } - .mono, code { font-family: $font-mono; } - } +.page-content code { + font-family: var(--font-mono); + font-size: .85em; + background: var(--bg-alt); + border: 1px solid var(--border); + border-radius: 5px; + padding: .12em .4em; + color: var(--text); } -// Callout box (release integrity, security reporting) -.callout { - border-left: 3px solid var(--accent-2-glow); +.page-content pre { background: var(--bg-alt); - border-radius: 0 10px 10px 0; - padding: 1rem 1.25rem; - margin: 0 0 1.25rem; - p:last-child { margin-bottom: 0; } + border: 1px solid var(--border); + border-radius: var(--radius-panel); + padding: 16px 18px; + overflow-x: auto; + margin: 0 0 18px; + font-size: 13px; + line-height: 1.75; + color: var(--text); } -.callout--alert { - border-left-color: var(--error); +.page-content pre code { + background: none; + border: 0; + padding: 0; + font-size: 1em; } -// Release card (download page) .release-card { - background: linear-gradient(to right, var(--chip-bg), transparent); - border: 2px solid var(--chip-border); - border-radius: 14px; - padding: 1.5rem; + background: var(--accent); + border: 2px solid var(--border-strong); + border-radius: var(--radius-card); + padding: 22px; margin: 0 0 1.5rem; - h3 { margin: 0 0 .5rem; } - .chip-cluster { - display: flex; - flex-wrap: wrap; - gap: .5rem; - margin-top: .75rem; - } + color: #fff; } -// Version cards (docs index) -.version-cards { +.release-card h3 { + margin: 0 0 .5rem; + color: inherit; +} + +[data-theme="dark"] .release-card { color: #191A16; } + +.vchain { display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; - margin: 0 0 1.5rem; - @media (max-width: 720px) { grid-template-columns: 1fr; } + grid-template-columns: repeat(auto-fit,minmax(215px,1fr)); + gap: 14px; + margin: 20px 0 26px; } -.version-card { +.vchain--3 { grid-template-columns: repeat(auto-fit,minmax(280px,1fr)); } + +.vlink { + border: 1px solid var(--border); + background: var(--surface); + border-radius: 12px; + padding: 18px; display: flex; flex-direction: column; - gap: .5rem; - .version-card__head { - display: flex; - align-items: center; - gap: .625rem; - } - .version-card__name { - font-family: $font-display; - font-weight: 700; - font-size: 1.25rem; - color: var(--text); - } - .chip-cluster { - display: flex; - flex-wrap: wrap; - gap: .5rem; - margin-top: auto; - } + gap: 8px; } -.chip-cluster { +.vlink h3 { + margin: 0; + font-size: 16px; +} + +.vlink p { + margin: 0; + font-size: 13.5px; + color: var(--text-secondary); + line-height: 1.55; +} + +.vlink .chip-cluster { + margin-top: auto; + padding-top: 10px; +} + +.doors { + display: grid; + grid-template-columns: repeat(auto-fit,minmax(260px,1fr)); + gap: 14px; + margin: 20px 0 30px; +} + +.door { + border: 1px solid var(--border); + background: var(--surface); + border-radius: var(--radius-card); + padding: 22px; display: flex; - flex-wrap: wrap; - gap: .5rem; + flex-direction: column; + gap: 10px; +} + +.door h3 { margin: 0; } + +.door p { + margin: 0; + color: var(--text-secondary); + font-size: 14px; + line-height: 1.6; +} + +.door .slink { + margin-top: auto; + padding-top: 8px; +} + +.lanes { + display: grid; + grid-template-columns: repeat(auto-fit,minmax(320px,1fr)); + gap: 16px; + margin: 20px 0 26px; +} + +.lane { + border: 1px solid var(--border); + background: var(--surface); + border-radius: var(--radius-card); + padding: 24px; } -// --- Team page --------------------------------------------------------------- +.lane h3 { margin: 8px 0 10px; } + +.lane .chip { white-space: nowrap; } + +.faq { + border-left: 2px solid var(--border); + padding: 2px 0 2px 18px; + margin: 0 0 20px; +} + +.faq .faq__q { + font-family: var(--font-mono); + font-size: 14px; + font-weight: 600; + color: var(--text); + margin: 0; +} + +.faq .faq__q::before { + content: "$ "; + color: var(--accent-ink); +} + +.faq .faq__a { margin: 8px 0 0; } + +.codebox { + position: relative; + background: var(--code-bg); + color: var(--code-fg); + border-radius: 12px; + padding: 18px 20px; + margin: 18px 0; + overflow: auto; +} + +.codebox pre { + font-family: var(--font-mono); + font-size: var(--text-code); + line-height: 1.8; + margin: 0; + background: none; + border: 0; + padding: 0; + color: inherit; +} + +.callout { + border: 1px solid var(--border); + border-left: 3px solid var(--accent); + border-radius: 0 var(--radius-panel) var(--radius-panel) 0; + padding: 14px 16px; + background: var(--bg-alt); + margin: 0 0 20px; +} + +.callout--alert { border-left-color: var(--error); } + +.callout p { margin: 0; } .contributor-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + grid-template-columns: repeat(auto-fill,minmax(240px,1fr)); gap: 12px; - margin: 0 0 30px 0; + margin: 24px 0; } .contributor-card { @@ -1040,46 +1143,39 @@ code, pre, kbd, samp { gap: 10px; padding: 10px 12px; border: 1px solid var(--border); - border-radius: 10px; + border-radius: var(--radius-pill); background: var(--surface); color: var(--text); - text-decoration: none; box-shadow: var(--shadow-sm); - transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; + transition: border-color var(--motion-fast),transform var(--motion-fast); +} - &:hover { - border-color: var(--border-strong); - box-shadow: var(--shadow-md); - text-decoration: none; - transform: translateY(-2px); - } +.contributor-card:hover { + border-color: var(--border-strong); + transform: translateY(-2px); + text-decoration: none; } .contributor-badge { flex: 0 0 auto; - width: 40px; - height: 40px; - border-radius: 50%; + width: 38px; + height: 38px; + border-radius: var(--radius-node); border: 2px solid var(--border); color: #fff; display: inline-flex; align-items: center; justify-content: center; - font-weight: 600; - font-size: 13px; - letter-spacing: 0.5px; - transition: transform .15s ease, border-color .15s ease; + font: 600 12px/1 var(--font-mono); + letter-spacing: .5px; } -.contributor-card:hover .contributor-badge { - transform: scale(1.08); - border-color: var(--accent); -} +.contributor-card:hover .contributor-badge { border-color: var(--accent-ink); } .contributor-name { flex: 1 1 auto; font-weight: 600; - font-size: .875rem; + font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -1088,43 +1184,338 @@ code, pre, kbd, samp { .contributor-role { flex: 0 0 auto; display: inline-block; - padding: 2px 6px; - border-radius: 4px; - background: var(--code-bg); + padding: 2px 8px; + border-radius: var(--radius-pill); + background: var(--bg-alt); color: var(--text-secondary); - font-size: 11px; - font-weight: 600; - letter-spacing: 0.5px; + font: 600 10.5px/1.5 var(--font-mono); + letter-spacing: .5px; border: 1px solid var(--border); } .contributor-role-chair { - background: var(--chip-bg); - color: var(--chip-text); - border-color: var(--chip-border); + background: var(--accent); + color: #fff; + border-color: var(--border-strong); } +[data-theme="dark"] .contributor-role-chair { color: #191A16; } + .contributor-legend { color: var(--text-secondary); font-size: 13px; - .contributor-role { margin: 0 2px; } } -.team-last-updated { - color: var(--muted); - font-size: 12px; - font-style: italic; +.contributor-legend .contributor-role { margin: 0 2px; } + +// --- Footer ----------------------------------------------------------------- + +.site-footer { + background: var(--invariant-bg-deep); + padding: 64px var(--page-pad-x) 30px; + color: var(--invariant-muted); + position: relative; + overflow: hidden; + font-size: 13.5px; +} + +.site-footer__inner { + max-width: 1176px; + margin: 0 auto; +} + +.site-footer h3 { + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: .12em; + text-transform: uppercase; + font-weight: 600; + margin: 0 0 14px; + color: #DDDFD2; +} + +.site-footer a { color: var(--invariant-muted); } + +.site-footer a:hover { + color: var(--accent-hover); + text-decoration: none; +} + +.site-footer .icon-btn { + border-color: var(--invariant-border); + color: var(--invariant-muted); + border-radius: var(--radius-panel); +} + +.site-footer .icon-btn:hover { + border-color: var(--accent-hover); + color: var(--accent-hover); + transform: translateY(-1px); +} + +.thread { + position: relative; + height: 1px; + background: var(--invariant-border); + margin-bottom: 46px; } -// --- Rouge syntax highlighting (re-keyed to tokens) --------------------------- +.thread__node { + position: absolute; + top: -4px; + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--invariant-border); +} + +.thread__node--accent { + background: var(--accent); + border: 1px solid #191A16; +} + +.fcols { + display: flex; + flex-wrap: wrap; + gap: 32px; + padding-bottom: 40px; + padding-right: 140px; +} + +.fcol { + flex: 1 1 130px; + min-width: 120px; +} + +.fcol a { + display: block; + font-size: 13.5px; + margin-bottom: 9px; +} + +.fbrand { + flex: 1 1 300px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 14px; +} + +.fbrand img { + height: 40px; + width: auto; +} + +.fbrand__row { + display: flex; + gap: 10px; + align-items: center; + flex-wrap: wrap; +} + +.coc-pill { + display: inline-flex; + align-items: center; + gap: 10px; + border: 1px solid var(--invariant-border); + border-radius: var(--radius-pill); + padding: 11px 16px; + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + letter-spacing: .08em; + color: #B4B7A6; +} + +.coc-pill:hover { + border-color: var(--accent-hover); + color: var(--accent-hover); +} + +.coc-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--accent); + flex: 0 0 auto; + transition: box-shadow var(--motion-fast); +} + +.coc-pill:hover .coc-dot { box-shadow: 0 0 12px var(--accent-hover); } + +.asf-band { + border-top: 1px solid var(--invariant-border); + margin-top: 4px; + padding: 26px 0 22px; + display: flex; + align-items: center; + gap: 18px; + flex-wrap: wrap; +} + +.asf-band__seal { + width: 52px; + height: 52px; + border-radius: 50%; + border: 1px solid var(--invariant-border); + background: #1A1816; + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} + +.asf-band__seal img { height: 26px; } + +.asf-band__copy { flex: 1 1 300px; } + +.asf-band__title { + margin: 0 0 3px; + font-size: 14px; + font-weight: 700; + color: #DDDFD2; +} + +.asf-band__sub { + margin: 0; + font-size: 12.5px; + color: var(--invariant-muted); +} + +.asf-band__links { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + letter-spacing: .06em; + display: inline-flex; + gap: 14px; + flex-wrap: wrap; +} + +.asf-band__links a { + color: #B4B7A6; + border-bottom: 2px solid transparent; + padding-bottom: 2px; + transition: border-color var(--motion-fast),color var(--motion-fast); +} + +.asf-band__links a:hover { border-bottom-color: var(--accent-hover); } + +.colophon { + margin: 0; + padding-top: 16px; + border-top: 1px solid #1E2016; + text-align: center; + font-family: var(--font-mono); + font-size: var(--text-legal); + line-height: 1.8; + color: #6E7162; +} + +// --- Responsive ------------------------------------------------------------- + +@media (max-width: 1180px) { + nav.links { gap: 16px; } + + .site-header__inner { gap: 16px; } +} + +@media (max-width: 1060px) { + nav.links { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + flex-direction: column; + align-items: stretch; + gap: 0; + background: var(--bg); + border-bottom: 1px solid var(--border); + padding: 6px 24px 18px; + box-shadow: var(--shadow-md); + } + + .site-header.menu-open nav.links { display: flex; } + + nav.links .site-nav-link { + padding: 13px 2px; + border-top: 1px solid var(--border); + font-size: 15px; + } + + nav.links .site-nav-link:first-child { border-top: 0; } + + nav.links .site-nav-link.active::after { display: none; } + + .nav-burger { display: inline-flex; } + + .hero__ctas .btn { + flex: 1 1 auto; + justify-content: center; + text-align: center; + } +} + +@media (max-width: 1000px) { + .hero { + display: flex; + flex-direction: column; + min-height: 0; + } + + .hero__grid { order: 1; } + + .hero__webbox { + order: 2; + position: relative; + inset: auto; + width: min(100%,520px); + height: auto; + aspect-ratio: 1; + margin: 26px auto 0; + } +} + +@media (max-width: 900px) { + .qgrid { grid-template-columns: 1fr; } +} + +@media (max-width: 700px) { + :root { + --page-pad-x: 18px; + --rail-x: calc(var(--gutter-x) + 19px); + } + + .site-header__inner { gap: 12px; } + + .stage { + padding-top: 48px; + padding-bottom: 48px; + padding-left: calc(var(--gutter-x) + 58px); + } + + .stage__node { + width: 38px; + height: 38px; + font-size: 13px; + top: 48px; + } + + .fcols { padding-right: 0; } +} + +@media (max-width: 540px) { + .gh-pill .gh-txt { display: none; } + + .gh-pill .gh-ico { display: block; } + + .gh-pill { + padding: 10px; + border-radius: var(--radius-node); + } +} -.highlight { - .c, .cm, .c1, .cs { color: var(--muted); font-style: italic; } // comments - .k, .kd, .kn, .kp, .kr, .kt, .o, .ow { color: var(--accent); } // keywords/operators - .s, .s1, .s2, .sb, .sc, .sd, .se, .sh, .si, .sx, .sr, .ss { color: var(--success); } // strings - .m, .mf, .mh, .mi, .mo, .il { color: var(--accent-2); } // numbers - .na, .nc, .nf, .nn { color: var(--text); font-weight: 600; } // names - .err { color: var(--error); } - .gd { color: var(--error); } - .gi { color: var(--success); } +@media (max-width: 380px) { + .site-header__logo img { height: 40px; } } diff --git a/css/resources/archivo-latin-variable.woff2 b/css/resources/archivo-latin-variable.woff2 new file mode 100644 index 0000000..716d4ab Binary files /dev/null and b/css/resources/archivo-latin-variable.woff2 differ diff --git a/docs/index.html b/docs/index.html index 50ca156..a62a128 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,144 +2,65 @@ layout: default slug: documentation title: Documentation of Apache StormCrawler -eyebrow: Documentation +eyebrow: LEARN heading: Documentation -description: User guides, versioned reference documentation and the API Javadoc for Apache StormCrawler. +description: Guides, configuration reference and Javadoc for every release. --- -<h2>User Documentation</h2> - -<p> - Additional documentation, including configuration guides and usage examples, - is available in the versioned documentation pages: -</p> +<h2>Start where you are</h2> +<div class="doors"> + <div class="door"> + <p class="slabel">NEW HERE</p> + <h3>Quick Start</h3> + <p>Generate a crawler from the Maven archetype and run your first crawl in local mode.</p> + <a class="slink" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">OPEN THE GUIDE <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + </div> + <div class="door"> + <p class="slabel">TUNING</p> + <h3>Configuration</h3> + <p>Seeds, politeness, fetch intervals, filters — every knob of the crawl, explained.</p> + <a class="slink" href="{{ site.baseurl }}/docs/latest/index.html#_configuration">CONFIGURATION <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + </div> + <div class="door"> + <p class="slabel">INTEGRATING</p> + <h3>External modules</h3> + <p><a href="https://opensearch.org/" target="_blank" rel="noopener">OpenSearch®</a>, <a href="https://solr.apache.org/" target="_blank" rel="noopener">Apache Solr®</a>, <a href="https://tika.apache.org/" target="_blank" rel="noopener">Apache Tika®</a>, sitemaps, feeds — plug-in resources maintained by the project.</p> + <a class="slink" href="https://github.com/apache/stormcrawler/tree/main/external" target="_blank" rel="noopener">MODULES <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + </div> +</div> -<div class="version-cards"> - <div class="card version-card card--highlight"> - <div class="version-card__head"> - <span class="version-card__name">3.7.0</span> - <span class="badge">Current</span> - </div> - <p class="card__copy">The latest release, also served as <code>docs/latest</code>.</p> - <div class="chip-cluster"> - <a class="chip" href="/docs/latest/index.html" target="_blank">Read the docs →</a> - <a class="chip" href="/docs/3.7.0/index.html" target="_blank">Pinned 3.7.0 →</a> - </div> - </div> - <div class="card version-card"> - <div class="version-card__head"> - <span class="version-card__name">3.6.0</span> - <span class="badge badge--muted">Previous</span> - </div> - <p class="card__copy">Documentation for the previous release.</p> - <div class="chip-cluster"> - <a class="chip" href="/docs/3.6.0/index.html" target="_blank">Read the docs →</a> - </div> - </div> - <div class="card version-card"> - <div class="version-card__head"> - <span class="version-card__name">3.5.1</span> - <span class="badge badge--muted">Older</span> - </div> - <p class="card__copy">Documentation for an older release.</p> - <div class="chip-cluster"> - <a class="chip" href="/docs/3.5.1/index.html" target="_blank">Read the docs →</a> - </div> - </div> +<h2>User Documentation</h2> +<p>Additional documentation, including configuration guides and usage examples, is available in the versioned documentation pages:</p> +<div class="chip-cluster" style="margin-bottom:26px;"> + <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html">LATEST (3.7.0) →</a> + <a class="chip" href="{{ site.baseurl }}/docs/3.7.0/index.html">3.7.0</a> + <a class="chip" href="{{ site.baseurl }}/docs/3.6.0/index.html">3.6.0</a> + <a class="chip" href="{{ site.baseurl }}/docs/3.5.1/index.html">3.5.1</a> </div> <h2>Javadoc</h2> - -<p> - The full API reference for Apache StormCrawler Javadoc.io: -</p> - -<div class="chip-cluster" style="margin-bottom:1.25rem;"> - <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.7.0/index.htm" target="_blank" rel="noopener">3.7.0 (current) <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.6.0/index.htm" target="_blank" rel="noopener">3.6.0 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.5.1/index.htm" target="_blank" rel="noopener">3.5.1 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.5.0/index.htm" target="_blank" rel="noopener">3.5.0 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.4.0/index.htm" target="_blank" rel="noopener">3.4.0 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> +<p>The full API reference for Apache StormCrawler on javadoc.io:</p> +<div class="chip-cluster" style="margin-bottom:26px;"> + <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.7.0" target="_blank" rel="noopener">3.7.0 (CURRENT) ↗</a> + <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.6.0" target="_blank" rel="noopener">3.6.0 ↗</a> + <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.5.1" target="_blank" rel="noopener">3.5.1 ↗</a> + <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.5.0" target="_blank" rel="noopener">3.5.0 ↗</a> + <a class="chip" href="https://javadoc.io/doc/org.apache.stormcrawler/stormcrawler-core/3.4.0" target="_blank" rel="noopener">3.4.0 ↗</a> </div> -<h2>FAQ</h2> - -<p><strong>Q: Topologies? Spouts? Bolts? I’m confused!</strong></p> -<p> - A: If you’re new to these concepts, it’s worth starting with - <a href="http://storm.apache.org/" target="_blank">Apache Storm®</a>. The - <a href="http://storm.apache.org/releases/current/Tutorial.html" target="_blank">tutorial</a> - and <a href="http://storm.apache.org/documentation/Concepts.html" target="_blank">concept pages</a> - provide a good introduction. In addition, you can have a look at our own documentation, which provides a quick overview. -</p> - -<p><strong>Q: Do I need an Apache Storm® cluster to run StormCrawler?</strong></p> -<p> - A: Not necessarily. StormCrawler can run in local mode, using Storm libraries as dependencies. - However, installing Storm in pseudo-distributed mode is useful if you want to use its UI - to monitor topologies. -</p> +<h2>Presentations</h2> +<p>The <a href="{{ site.baseurl }}/docs/latest/presentations.html">Presentations</a> page contains links to some recent presentations made about this project.</p> -<p><strong>Q: Why use Apache Storm®?</strong></p> -<p> - A: Apache Storm® is a robust, fault-tolerant framework for distributed stream processing. - It guarantees data processing, is simple to understand, actively maintained, and licensed under ASF 2.0. -</p> - -<p id="howfast"><strong>Q: How fast is StormCrawler?</strong></p> -<p> - A: Speed depends on the diversity of hostnames, your politeness settings and execution environment. For example, - if you have 1 million URLs from the same host and set a 1-second delay between requests, - you can fetch a maximum of ~86,400 pages per day. Actual performance will vary depending - on network speed, document size, parsing, and indexing overhead. This is true for any crawler. -</p> - -<p><strong>Q: Why choose StormCrawler over Apache Nutch?</strong></p> -<p> - A: StormCrawler processes URLs as a continuous stream, indexing them as they are fetched. - Nutch uses batch steps, which can slow down as the crawl grows and resources are unevenly used. - StormCrawler can handle streaming URLs or low-latency use cases efficiently. It is also more modern, modular, - and actively maintained, though Nutch excels in advanced scoring and deduplication. -</p> -<p> - Tutorials comparing both are available here: - <a href="http://digitalpebble.blogspot.co.uk/2015/09/index-web-with-aws-cloudsearch.html" target="_blank">Nutch & SC with - CloudSearch</a> - and a benchmark study: - <a href="http://digitalpebble.blogspot.co.uk/2017/01/the-battle-of-crawlers-apache-nutch-vs.html" target="_blank">Crawlers - benchmark</a>. -</p> - -<p><strong>Q: Do I need external storage? Which type?</strong></p> -<p> - A: Yes, StormCrawler needs storage for URLs. The type depends on your crawl: -</p> -<ul> - <li> - <strong>Non-recursive crawls:</strong> Messaging queues like - <a href="https://www.rabbitmq.com/" target="_blank">RabbitMQ</a>, - <a href="https://aws.amazon.com/sqs/" target="_blank">AWS SQS</a>, or - <a href="http://kafka.apache.org" target="_blank">Apache Kafka®</a> work well. Use a Spout implementation to read from the - queue. - </li> - <li> - <strong>Recursive crawls:</strong> Use storage with unique keys (e.g., a database) - to avoid duplicate URLs. StormCrawler provides external modules for - <a href="https://github.com/apache/stormcrawler/tree/master/external" target="_blank">Apache SOLR, OpenSearch, SQL</a>. - </li> -</ul> -<p> - The modularity of StormCrawler lets you plug in almost any storage backend. -</p> - -<p><strong>Q: Is StormCrawler polite?</strong></p> -<p> - A: Yes. It respects the <a href="http://www.robotstxt.org/" target="_blank">robots.txt</a> protocol - and can be configured with a politness dely between requests to the same host or domain. -</p> - -<p><strong>Q: How do I know when a crawl is finished?</strong></p> -<p> - A: Storm topologies run continuously by design, so there is no automatic “finished” state. - You need to monitor progress and stop the crawl manually or implement a custom termination mechanism. -</p> +<h2>FAQ</h2> +<div class="faq"> + <p class="faq__q">Do I need an <a href="https://storm.apache.org/" target="_blank" rel="noopener">Apache Storm®</a> cluster to run StormCrawler?</p> + <p class="faq__a">Not necessarily. StormCrawler can run in local mode, using Storm libraries as dependencies. However, installing <a href="https://storm.apache.org/" target="_blank" rel="noopener">Storm</a> in pseudo-distributed mode is useful if you want to use its UI to monitor topologies.</p> +</div> +<div class="faq"> + <p class="faq__q">Is StormCrawler polite?</p> + <p class="faq__a">Yes. It respects the <a href="https://en.wikipedia.org/wiki/Robots.txt" target="_blank" rel="noopener">robots.txt</a> protocol and can be configured with a politeness delay between requests to the same host or domain.</p> +</div> +<div class="faq"> + <p class="faq__q">How do I know when a crawl is finished?</p> + <p class="faq__a">Storm topologies run continuously by design, so there is no automatic "finished" state. You need to monitor progress and stop the crawl manually or implement a custom termination mechanism.</p> +</div> diff --git a/download/index.html b/download/index.html index 2000893..e110647 100644 --- a/download/index.html +++ b/download/index.html @@ -2,51 +2,73 @@ layout: default slug: download title: Download -eyebrow: Download +eyebrow: DOWNLOAD heading: Download description: Verified source releases of StormCrawler, signed by the release managers. --- -<h2>Release Integrity</h2> -<div class="callout"> - <p>You <b>must</b> <a href="https://www.apache.org/info/verification.html">verify</a> the integrity of the - downloaded files. We provide OpenPGP signatures (*.asc files) for every release file. This signature should be - matched against <a href="https://downloads.apache.org/stormcrawler/KEYS">KEYS</a> file which contains the - OpenPGP keys of StormCrawler’s Release Managers. We also provide SHA-512 checksums for every release file. - After you download the file, you should calculate a checksum for your download, and make sure it is the same as - ours. </p> - <p>More information about release signing and verifying signatures can be found <a - href="https://www.apache.org/dev/release-signing.html">here</a>.</p> -</div> +<p>StormCrawler™ software is an open source SDK for building low-latency, scalable web crawlers based on <a href="https://storm.apache.org/" target="_blank" rel="noopener">Apache Storm®</a>. You can <a href="https://www.apache.org/dyn/closer.lua/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz">download StormCrawler™ software here</a> (latest release: 3.7.0), or pick individual artifacts below.</p> -<h2>Downloads</h2> +<div class="release-card"> + <h3>Apache StormCrawler 3.7.0 <span class="badge">LATEST</span></h3> + <p style="margin:0;">Source release, signed and checksummed:</p> + <div class="chip-cluster"> + <a class="chip" href="https://www.apache.org/dyn/closer.lua/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz">SOURCE TAR.GZ</a> + <a class="chip" href="https://github.com/apache/stormcrawler/releases/tag/stormcrawler-3.7.0" target="_blank" rel="noopener">RELEASE NOTES ↗</a> + <a class="chip" href="https://search.maven.org/search?q=org.apache.stormcrawler" target="_blank" rel="noopener">MAVEN CENTRAL ↗</a> + </div> +</div> -<p>StormCrawler™ software is an open source SDK for building low-latency, scalable web crawlers based on Apache - Storm®. You can <a - href="https://www.apache.org/dyn/closer.lua/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz">download - StormCrawler™ software here</a> (latest release: 3.7.0), or pick individual artifacts below.</p> +<h2>Chain of trust</h2> +<p>You <b>must</b> <a href="https://www.apache.org/info/verification.html">verify</a> the integrity of the downloaded files — every release ships with everything you need to prove it came from us. The chain has four links:</p> -<div class="release-card"> - <div class="version-card__head"> - <span class="badge">3.7.0</span> - <h3 style="margin:0;">Apache StormCrawler 3.7.0</h3> +<div class="vchain"> + <div class="vlink"> + <p class="slabel">01 — GET</p> + <h3>Source archive</h3> + <p>Fetch the tar.gz from an Apache mirror.</p> + <div class="chip-cluster"> + <a class="chip" href="https://www.apache.org/dyn/closer.lua/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz">TAR.GZ</a> + </div> + </div> + <div class="vlink"> + <p class="slabel">02 — CHECK</p> + <h3>Integrity</h3> + <p>Your SHA-512 checksum must match ours.</p> + <div class="chip-cluster"> + <a class="chip" href="https://downloads.apache.org/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz.sha512">SHA512</a> </div> - <p style="margin:.75rem 0 0;">Source release, signed and checksummed:</p> + </div> + <div class="vlink"> + <p class="slabel">03 — PROVE</p> + <h3>Authenticity</h3> + <p>The OpenPGP signature (*.asc) is made by a release manager.</p> <div class="chip-cluster"> - <a class="chip" href="https://www.apache.org/dyn/closer.lua/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz">apache-stormcrawler-3.7.0-source-release.tar.gz →</a> - <a class="chip" href="https://downloads.apache.org/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz.sha512">sha512 →</a> - <a class="chip" href="https://downloads.apache.org/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz.asc">asc →</a> - <a class="chip" href="https://downloads.apache.org/stormcrawler/KEYS">KEYS →</a> - <a class="chip" href="https://github.com/apache/stormcrawler/releases/tag/stormcrawler-3.7.0" target="_blank" rel="noopener">Release notes <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - <a class="chip" href="https://search.maven.org/search?q=org.apache.stormcrawler" target="_blank" rel="noopener">Maven Central <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + <a class="chip" href="https://downloads.apache.org/stormcrawler/stormcrawler-3.7.0/apache-stormcrawler-3.7.0-source-release.tar.gz.asc">ASC</a> </div> + </div> + <div class="vlink"> + <p class="slabel">04 — TRUST</p> + <h3>Key ring</h3> + <p>Match the signature against the project's KEYS file.</p> + <div class="chip-cluster"> + <a class="chip" href="https://downloads.apache.org/stormcrawler/KEYS">KEYS</a> + </div> + </div> +</div> + +<h3>Verify like a release manager</h3> +<div class="codebox"> + <pre id="verify-cmd">$ sha512sum -c apache-stormcrawler-3.7.0-source-release.tar.gz.sha512 +$ gpg --import KEYS +$ gpg --verify apache-stormcrawler-3.7.0-source-release.tar.gz.asc</pre> + <button class="copy-chip" data-copy="#verify-cmd" hidden style="position:absolute;top:10px;right:10px;">copy</button> </div> +<p>More information about release signing and verifying signatures can be found <a href="https://www.apache.org/dev/release-signing.html">here</a>.</p> + <h3>Maven</h3> -<p>Artifacts are available via <a href="https://search.maven.org/search?q=org.apache.stormcrawler">Maven - Central</a></p> +<p>Artifacts are available via <a href="https://search.maven.org/search?q=org.apache.stormcrawler">Maven Central</a>.</p> <h3>Older Releases</h3> - -<p>Previous <b>non</b>-ASF releases can be found <a - href="https://search.maven.org/search?q=g:com.digitalpebble.stormcrawler">here</a>.</p> +<p>Previous <b>non</b>-ASF releases can be found <a href="https://search.maven.org/search?q=g:com.digitalpebble.stormcrawler">here</a>.</p> diff --git a/img/spider-mark-light.svg b/img/spider-mark-light.svg new file mode 100644 index 0000000..8f09d9e --- /dev/null +++ b/img/spider-mark-light.svg @@ -0,0 +1,5 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="360 1055 466 458" fill="none" role="img" aria-label="StormCrawler spider"> + <path d="m 363.319,1321.874 0.55901,0.4629 c 4.72792,-0.8207 8.98036,-6.0272 12.30461,-8.7735 8.59181,-7.0971 17.24776,-13.998 26.83197,-20.168 5.3966,-3.4595 11.22454,-7.7035 17.33403,-10.2357 3.49677,-1.4447 7.42556,-1.9259 11.18176,-2.5321 8.50485,-1.3734 17.0097,-2.1578 25.7092,-2.1578 4.57676,0 10.62071,-1.5871 15.11057,-0.302 1.46781,0.4106 2.52574,1.4804 3.90737,2.0686 2.11515,0.9102 4.72723,0.6237 6.71262,1.6226 3.58375,1.8191 6.75682,4.4584 10.05964,6.6159 6.75682,4.4223 13.21 [...] + <path d="m 554.41193,1309.8047 c -2.07451,4.2318 -3.76593,8.9734 -1.6915,13.4772 1.30849,2.8383 3.87843,5.2856 6.15575,7.699 -6.89962,0.4264 -12.6503,4.4868 -17.88126,7.5461 -7.62158,4.4698 -15.17548,8.7526 -22.3463,13.6474 -3.13448,2.1413 -6.22407,4.6397 -9.58349,6.5772 -2.95438,1.6995 -6.85558,2.7193 -9.15494,4.9796 -2.81909,2.7534 -3.42775,5.9996 -5.11925,9.1435 -2.54789,4.7927 -5.32135,9.7046 -8.18534,14.4463 -6.08806,9.9933 -10.77876,20.5306 -16.07733,30.8129 -1.50996,2.9231 -4.37 [...] + <path d="m 614.71607,1235.923 c 1.28493,0.4616 1.86117,1.4202 1.44164,2.2952 l -11.30535,23.136 h 20.85294 c 2.75418,0 4.17098,2.0643 2.28214,3.3357 l -50.23137,33.5889 c -2.28215,1.5192 -6.26889,0.068 -5.2717,-1.932 l 11.30464,-23.1359 h -20.85294 c -2.75417,0 -4.19723,-2.0805 -2.30838,-3.3355 l 50.23137,-33.5892 c 0.99721,-0.6776 2.5969,-0.8257 3.85559,-0.3626 z m 0,0" fill="#C33540"></path> +</svg> \ No newline at end of file diff --git a/index.html b/index.html index f1e8094..4b2f5d1 100644 --- a/index.html +++ b/index.html @@ -1,168 +1,119 @@ --- layout: default slug: home +description: Apache StormCrawler is an open source SDK for building low-latency, scalable, polite web crawlers on Apache Storm. Java, Apache License v2. --- <!-- ============================== Hero ============================== --> -<section class="hero" id="top"> - <div class="hero__inner"> +<div class="hero"> + <div class="hero__webbox" aria-hidden="true"> + <svg id="webHero" viewBox="0 0 720 720" fill="none"></svg> + <img class="hero__mark only-light" src="{{ site.baseurl }}/img/spider-mark-light.svg" alt=""> + <img class="hero__mark only-dark" src="{{ site.baseurl }}/img/spider-mark.svg" alt=""> + </div> + <div class="hero__grid"> <div class="hero__copy"> - <h1 class="hero__title">Build web crawlers that ride the <span class="grad-word">storm</span>.</h1> - <p class="hero__lede"><strong>Apache StormCrawler</strong> is an open source SDK for building distributed web crawlers based on <a href="https://storm.apache.org" style="color:inherit;text-decoration:underline;text-underline-offset:3px;">Apache Storm®</a>. Low-latency, scalable and polite: a collection of reusable components, written mostly in Java, under Apache License v2.</p> + <p class="eyebrow" style="margin-bottom:20px;">Apache StormCrawler · <span class="pill">v3.7.0</span></p> + <h1 class="hero__title">Every web<br>has its<br>spider<span class="dot">.</span></h1> + <p class="hero__lede"><strong>Apache StormCrawler</strong> is an open source SDK for building distributed web crawlers based on <a href="https://storm.apache.org/" target="_blank" rel="noopener">Apache Storm®</a>. Low-latency, scalable and polite: a collection of reusable components, written mostly in Java, under Apache License v2.</p> <div class="hero__ctas"> - <a class="btn btn--solid" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Get started →</a> - <a class="btn btn--ghost" href="{{ site.baseurl }}/download/">Download 3.6.0</a> + <a class="btn btn--solid" href="#quick-start">Get started <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style="flex:0 0 auto;"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg></a> + <a class="btn btn--ghost" href="{{ site.baseurl }}/download/">Download 3.7.0</a> </div> - <p class="hero__assurance"> - <span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12.5 9.5 18 20 6"/></svg> Apache License v2</span> - <span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12.5 9.5 18 20 6"/></svg> Java, on Apache Storm®</span> - </p> + <p class="hero__assurance" style="margin:18px 0 0;"><span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12.5 9.5 18 20 6"/></svg> Apache License v2</span> <span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12.5 [...] </div> - - <aside class="hero__panel"> - <img class="hero__mark" src="{{ site.baseurl }}/img/spider-mark.svg" alt="Apache StormCrawler" width="360" height="354"> - </aside> </div> -</section> - +</div> -<!-- ============================== What is ============================== --> -<section class="section" id="what-is"> - <div class="section__inner"> - <div class="section-head reveal"> - <div class="bolt-divider" aria-hidden="true"></div> - <p class="eyebrow"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg> What is StormCrawler</p> - <h2>A collection of resources for building low-latency, scalable web crawlers.</h2> - </div> - <div class="about-grid"> - <div class="card reveal" style="--i:0"> - <h3 class="card__title">A library to build on</h3> - <p class="card__copy">Apache StormCrawler is a library and collection of resources that developers can leverage to build their own crawlers. The good news is that doing so can be pretty straightforward.</p> - <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Getting Started <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - </div> - <div class="card reveal" style="--i:1"> - <h3 class="card__title">Reusable external resources</h3> - <p class="card__copy">Apart from the core components, we provide external resources you can reuse in your project: for instance our spout and bolts for OpenSearch®, or a ParserBolt that uses Apache Tika® to parse various document formats.</p> - <a class="chip" href="https://github.com/apache/stormcrawler/tree/main/external" target="_blank" rel="noopener">External modules <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - </div> - <div class="card reveal" style="--i:2"> - <h3 class="card__title">Streams and large-scale crawls</h3> - <p class="card__copy">Apache StormCrawler is perfectly suited to use cases where the URLs to fetch and parse come as streams, but is also an appropriate solution for large-scale recursive crawls, particularly where low latency is required.</p> - <a class="chip" href="{{ site.baseurl }}/docs/latest/powered-by.html">Who is using StormCrawler <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - </div> - </div> +<!-- ============================== Pipeline ============================== --> +<div class="rail"> + <div class="stage reveal" style="border-top:0;"> + <span class="stage__node stage__node--accent">01</span> + <p class="slabel">SEED — START FROM A LIBRARY</p> + <h2 class="stage__title">Feed it URLs, get a crawler.</h2> + <p class="stage__copy"><strong>Apache StormCrawler</strong> is a library and collection of resources that developers leverage to build their own crawlers. The good news: doing so can be pretty straightforward — seed it with your URLs and let the topology do the rest.</p> + <a class="slink" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">READ THE FULL GUIDE <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> </div> -</section> - -<!-- ============================== Features ============================== --> -<section class="section section--alt" id="features"> - <div class="section__inner"> - <div class="section-head reveal"> - <div class="bolt-divider" aria-hidden="true"></div> - <p class="eyebrow"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg> Why StormCrawler</p> - <h2>Built for crawling at scale.</h2> - </div> - <div class="features-grid"> - <div class="card card--grid reveal" style="--i:0"> - <span class="icon-chip"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg></span> - <h3 class="card__title">Scalable</h3> - <p class="card__copy">Fetch and parse across a whole Storm cluster; add workers, not complexity.</p> - </div> - <div class="card card--grid reveal" style="--i:1"> - <span class="icon-chip"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3 4.5 6v5c0 4.5 3 8.5 7.5 10 4.5-1.5 7.5-5.5 7.5-10V6L12 3Z"/><path d="M9 12l2 2 4-4.5"/></svg></span> - <h3 class="card__title">Resilient</h3> - <p class="card__copy">Storm's guaranteed processing means no URL is silently lost.</p> - </div> - <div class="card card--grid reveal" style="--i:2"> - <span class="icon-chip"><svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg></span> - <h3 class="card__title">Low latency</h3> - <p class="card__copy">URLs are processed as streams: crawl continuously, not in batches.</p> - </div> - <div class="card card--grid reveal" style="--i:3"> - <span class="icon-chip"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3v5M15 3v5M7 8h10v4a5 5 0 0 1-10 0V8ZM12 17v4"/></svg></span> - <h3 class="card__title">Easy to extend</h3> - <p class="card__copy">Pluggable protocols, parsers, filters and indexers, mostly plain Java.</p> - </div> - <div class="card card--grid reveal" style="--i:4"> - <span class="icon-chip"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/></svg></span> - <h3 class="card__title">Polite yet efficient</h3> - <p class="card__copy">robots.txt, delays and politeness settings built in from the start.</p> - </div> - <div class="card card--grid card--highlight reveal" style="--i:5"> - <h3 class="card__title">In production since 2014</h3> - <p class="card__copy">Battle-tested by search engines, archives, researchers and monitoring platforms.</p> - <a class="chip" href="{{ site.baseurl }}/docs/latest/powered-by.html">Who uses it <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - </div> - </div> + <div class="stage reveal"> + <span class="stage__node">02</span> + <p class="slabel">FETCH — POLITE, AT SCALE</p> + <h2 class="stage__title">Crawl continuously, not in batches.</h2> + <p class="stage__copy">URLs are processed as streams across a whole <a href="https://storm.apache.org/" target="_blank" rel="noopener">Storm</a> cluster — add workers, not complexity. Storm's <a href="https://storm.apache.org/releases/current/Guaranteeing-message-processing.html" target="_blank" rel="noopener">guaranteed processing</a> means no URL is silently lost, and <a href="https://www.rfc-editor.org/rfc/rfc9309" target="_blank" rel="noopener">robots.txt</a>, delays and po [...] + <a class="slink" href="{{ site.baseurl }}/docs/latest/index.html#_configuration">CONFIGURATION <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + </div> + <div class="stage reveal"> + <span class="stage__node">03</span> + <p class="slabel">PARSE — REUSABLE MODULES</p> + <h2 class="stage__title">Tika, sitemaps, feeds — plug them in.</h2> + <p class="stage__copy">Apart from the core components, we provide external resources you can reuse in your project: a ParserBolt that uses <a href="https://tika.apache.org/" target="_blank" rel="noopener">Apache Tika®</a> to parse various document formats, sitemap and feed parsers, filters and much more — pluggable, mostly plain Java.</p> + <a class="slink" href="https://github.com/apache/stormcrawler/tree/main/external/tika" target="_blank" rel="noopener">EXTERNAL MODULES <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> + </div> + <div class="stage" style="padding-bottom:76px;"> + <span class="stage__node">04</span> + <p class="slabel">INDEX — YOUR BACKEND</p> + <h2 class="stage__title">Straight into OpenSearch, Solr or SQL.</h2> + <p class="stage__copy">Spouts and bolts for <a href="https://opensearch.org/" target="_blank" rel="noopener">OpenSearch®</a>, <a href="https://solr.apache.org/" target="_blank" rel="noopener">Apache Solr®</a> and SQL storage ship as external modules — index documents as they are fetched, whether you crawl a stream of URLs or run large-scale recursive crawls where low latency is required.</p> + <a class="slink" href="https://github.com/apache/stormcrawler/tree/main/external" target="_blank" rel="noopener">STORAGE MODULES <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> </div> -</section> +</div> <!-- ============================== Quick start ============================== --> -<section class="section" id="quick-start"> +<div class="section section--alt"> <div class="section__inner"> - <div class="section-head reveal"> - <div class="bolt-divider" aria-hidden="true"></div> - <p class="eyebrow"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg> Get started</p> - <h2>From zero to crawling in minutes.</h2> - </div> - <div class="quickstart reveal"> + <h2 class="display-h2" id="quick-start" style="margin-bottom:36px;">Run your first crawl<span style="color:var(--accent-ink);">.</span></h2> + <div class="qgrid reveal"> <div class="code-row"> - <pre id="archetype-cmd"><code>mvn archetype:generate -DarchetypeGroupId=org.apache.stormcrawler \ - -DarchetypeArtifactId=stormcrawler-archetype -DarchetypeVersion=3.6.0</code></pre> - <button class="copy-chip" data-copy="#archetype-cmd" hidden>Copy</button> + <pre id="archetype-cmd">$ mvn archetype:generate \ + -DarchetypeGroupId=<span class="hl">org.apache.stormcrawler</span> \ + -DarchetypeArtifactId=stormcrawler-archetype \ + -DarchetypeVersion=<span class="hl">3.7.0</span></pre> + <button class="copy-chip" data-copy="#archetype-cmd" hidden>copy</button> </div> <ol class="steps"> <li class="step"> - <span class="step__num" aria-hidden="true">1</span> - <span class="step__label">Generate a crawler project from the Maven archetype</span> - <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Docs →</a> + <span class="step__num">1</span> + <p>Generate a crawler project from the archetype</p> + <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">QUICK START ↗</a> </li> <li class="step"> - <span class="step__num" aria-hidden="true">2</span> - <span class="step__label">Configure seeds and politeness</span> - <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_configuration">Docs →</a> + <span class="step__num">2</span> + <p>Configure seeds and politeness</p> + <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_configuration">DOCS ↗</a> </li> <li class="step"> - <span class="step__num" aria-hidden="true">3</span> - <span class="step__label">Run locally or submit to a Storm cluster</span> - <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_run_your_first_crawl">Docs →</a> + <span class="step__num">3</span> + <p>Run locally or submit to a Storm cluster</p> + <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_run_your_first_crawl">DOCS ↗</a> </li> </ol> - <div class="chip-row"> - <a class="chip" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Quick Start →</a> - </div> </div> </div> -</section> +</div> <!-- ============================== Powered by ============================== --> -<section class="section section--alt" id="powered-by"> +<div class="section"> <div class="section__inner"> - <div class="section-head reveal"> - <div class="bolt-divider" aria-hidden="true"></div> - <p class="eyebrow"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg> In production</p> - <h2>Who's crawling with StormCrawler?</h2> - </div> - <div class="logo-band reveal"> + <h2 class="display-h2" style="text-align:center;margin-bottom:6px;">Caught in the web<span style="color:var(--accent-ink);">.</span></h2> + <p class="slabel" style="display:block;text-align:center;color:var(--text-secondary);">IN PRODUCTION SINCE 2014 — SEARCH ENGINES, ARCHIVES, RESEARCH & MONITORING</p> + <div class="logo-hang reveal"> <a href="https://pixray.com/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/pixray.png" alt="Pixray"></a> - <a href="https://www.gov.nt.ca/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/gnwt.png" alt="Government of Northwest Territories"></a> + <a href="https://www.gov.nt.ca/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/gnwt.png" alt="Government of Northwest Territories" style="max-height:46px;"></a> <a href="https://www.stolencamerafinder.com/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/stolen-camera-finder.png" alt="StolenCameraFinder"></a> - <a href="https://www.polecat.com/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/polecat.svg" alt="Polecat"></a> + <a href="https://www.polecat.com/" target="_blank" rel="noopener"><img src="{{ site.baseurl }}/img/polecat.svg" alt="Polecat" width="60" height="40" style="width:60px;height:40px;"></a> </div> - <p class="logo-band-more"> - <a class="chip" href="{{ site.baseurl }}/docs/latest/powered-by.html">…and many more <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M9 7h8v8"/></svg></a> - </p> + <p style="text-align:center;margin:24px 0 0;"><a class="chip" href="{{ site.baseurl }}/docs/latest/powered-by.html">…AND MANY MORE ↗</a></p> </div> -</section> +</div> -<!-- ============================== CTA ============================== --> -<section class="cta-band"> +<!-- ============================== Closing band ============================== --> +<div class="cta-band"> + <div class="cta-band__web" aria-hidden="true"><svg id="webCta" viewBox="0 0 720 720" fill="none"></svg></div> <div class="cta-band__inner"> <h2>Ready to unleash the storm?</h2> <p>Spin up a distributed crawler on infrastructure you control.</p> <div class="hero__ctas"> - <a class="btn btn--solid" href="{{ site.baseurl }}/docs/latest/index.html#_quick_start">Get started →</a> - <a class="btn btn--ghost" href="{{ site.baseurl }}/download/">Download →</a> + <a class="btn btn--cta" href="#quick-start">Get started <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style="flex:0 0 auto;"><path d="M13 2 4.5 13.5H11L9.5 22 19 10h-6.5L13 2Z"/></svg></a> + <a class="btn btn--ondark" href="{{ site.baseurl }}/download/">Download →</a> </div> </div> -</section> +</div> diff --git a/js/site.js b/js/site.js index e429eef..a35e926 100644 --- a/js/site.js +++ b/js/site.js @@ -1,46 +1,45 @@ (function () { 'use strict'; - - // JS is running: enable reveal animations document.documentElement.classList.remove('no-js'); - // --- Mobile navigation ----------------------------------------------------- - var navToggle = document.getElementById('nav-toggle'); - var mobileNav = document.getElementById('mobile-nav'); - if (navToggle && mobileNav) { - var closeNav = function () { - mobileNav.classList.remove('open'); - navToggle.setAttribute('aria-expanded', 'false'); - }; - navToggle.addEventListener('click', function () { - var open = mobileNav.classList.toggle('open'); - navToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); + // --- Nav: burger below 1060px ---------------------------------------------- + var header = document.querySelector('.site-header'); + var burger = document.getElementById('nav-toggle'); + if (header && burger) { + burger.addEventListener('click', function () { + var open = header.classList.toggle('menu-open'); + burger.setAttribute('aria-expanded', open ? 'true' : 'false'); }); - mobileNav.addEventListener('click', function (e) { - if (e.target.closest('a')) closeNav(); + document.querySelectorAll('nav.links a').forEach(function (a) { + a.addEventListener('click', function () { + header.classList.remove('menu-open'); + burger.setAttribute('aria-expanded', 'false'); + }); }); document.addEventListener('keydown', function (e) { - if (e.key === 'Escape') closeNav(); + if (e.key === 'Escape') { + header.classList.remove('menu-open'); + burger.setAttribute('aria-expanded', 'false'); + } }); } - // --- Copy-to-clipboard chips ----------------------------------------------- + // --- Copy chips: data-copy="#id", hidden without the clipboard API --------- if (navigator.clipboard) { - document.querySelectorAll('.copy-chip[data-copy]').forEach(function (btn) { - btn.hidden = false; - btn.addEventListener('click', function () { - var el = document.querySelector(btn.getAttribute('data-copy')); + document.querySelectorAll('.copy-chip[data-copy]').forEach(function (b) { + b.hidden = false; + b.addEventListener('click', function () { + var el = document.querySelector(b.getAttribute('data-copy')); if (!el) return; - navigator.clipboard.writeText(el.textContent).then(function () { - var label = btn.textContent; - btn.textContent = 'Copied ⚡'; - setTimeout(function () { btn.textContent = label; }, 1500); + navigator.clipboard.writeText(el.textContent.replace(/^\$ /, '')).then(function () { + b.textContent = 'copied \u26A1'; + setTimeout(function () { b.textContent = 'copy'; }, 1500); }); }); }); } - // --- BlurFade reveals -------------------------------------------------------- + // --- Blur-fade reveals ---------------------------------------------------- var revealed = document.querySelectorAll('.reveal'); if ('IntersectionObserver' in window && revealed.length) { var io = new IntersectionObserver(function (entries) { @@ -52,7 +51,88 @@ }); }, { threshold: 0.15 }); revealed.forEach(function (el) { io.observe(el); }); + // watchdog: if no callback lands (some embedded webviews), show everything + setTimeout(function () { + if (!document.querySelector('.reveal.in-view')) { + revealed.forEach(function (el) { el.classList.add('in-view'); }); + } + }, 700); } else { revealed.forEach(function (el) { el.classList.add('in-view'); }); } + + // --- Orb web (hero + closing band) --------------------------------------- + function orbWeb(svg, nodeColor, accStroke) { + if (!svg) return; + var c = 360, spokes = 14, rings = [58, 96, 136, 178, 222, 268, 316]; + var NS = 'http://www.w3.org/2000/svg'; + function ang(i) { return i * 2 * Math.PI / spokes - Math.PI / 2 + (i % 3) * 0.02; } + function pt(i, r) { return [c + r * Math.cos(ang(i)), c + r * Math.sin(ang(i))]; } + var radii = '', spiral = ''; + for (var i = 0; i < spokes; i++) { + var e = pt(i, 340 + (i % 4) * 6); + radii += 'M' + c + ' ' + c + ' L' + e[0].toFixed(1) + ' ' + e[1].toFixed(1) + ' '; + } + rings.forEach(function (r) { + for (var i = 0; i < spokes; i++) { + var a = pt(i, r), b = pt((i + 1) % spokes, r); + var ma = (ang(i) + ang(i + 1)) / 2; + var m = [c + r * 0.93 * Math.cos(ma), c + r * 0.93 * Math.sin(ma)]; + spiral += 'M' + a[0].toFixed(1) + ' ' + a[1].toFixed(1) + ' Q' + m[0].toFixed(1) + ' ' + m[1].toFixed(1) + ' ' + b[0].toFixed(1) + ' ' + b[1].toFixed(1) + ' '; + } + }); + function path(d, w) { + var el = document.createElementNS(NS, 'path'); + el.setAttribute('d', d); + el.setAttribute('stroke', 'var(--web-stroke)'); + el.setAttribute('stroke-width', w); + el.setAttribute('fill', 'none'); + svg.appendChild(el); + } + path(radii, '1.4'); + path(spiral, '1.1'); + // dew drops + [[2, 136], [5, 222], [9, 178], [12, 268], [7, 96]].forEach(function (n) { + var q = pt(n[0], n[1]), el = document.createElementNS(NS, 'circle'); + el.setAttribute('cx', q[0].toFixed(1)); + el.setAttribute('cy', q[1].toFixed(1)); + el.setAttribute('r', '3.5'); + el.setAttribute('fill', nodeColor); + svg.appendChild(el); + }); + // one accent node + var q = pt(10, 222), acc = document.createElementNS(NS, 'circle'); + acc.setAttribute('cx', q[0].toFixed(1)); + acc.setAttribute('cy', q[1].toFixed(1)); + acc.setAttribute('r', '6.5'); + acc.setAttribute('fill', 'var(--accent)'); + if (accStroke) { + acc.setAttribute('stroke', accStroke); + acc.setAttribute('stroke-width', '1.5'); + } + svg.appendChild(acc); + // impulse running a thread + var dot = document.createElementNS(NS, 'circle'); + dot.setAttribute('r', '4'); + dot.setAttribute('fill', 'var(--accent)'); + var mo = document.createElementNS(NS, 'animateMotion'); + mo.setAttribute('dur', '7s'); + mo.setAttribute('repeatCount', 'indefinite'); + var sp = pt(4, 346); + mo.setAttribute('path', 'M' + c + ' ' + c + ' L' + sp[0].toFixed(1) + ' ' + sp[1].toFixed(1) + ' L' + c + ' ' + c); + dot.appendChild(mo); + svg.appendChild(dot); + } + orbWeb(document.getElementById('webHero'), 'var(--text)', 'var(--text)'); + orbWeb(document.getElementById('webCta'), '#8B8E7D', ''); + + // --- Smooth scroll to #quick-start --------------------------------------- + document.querySelectorAll('a[href="#quick-start"]').forEach(function (a) { + a.addEventListener('click', function (e) { + var t = document.getElementById('quick-start'); + if (!t) return; + e.preventDefault(); + window.scrollTo({ top: t.getBoundingClientRect().top + window.pageYOffset - 90, behavior: 'smooth' }); + }); + }); })(); diff --git a/security/index.html b/security/index.html index feb8c68..829a7c0 100644 --- a/security/index.html +++ b/security/index.html @@ -2,62 +2,51 @@ layout: default slug: security title: Reporting Security Problems to Apache StormCrawler -eyebrow: Security +eyebrow: SECURITY heading: Security description: How to report vulnerabilities privately, and the StormCrawler threat model. --- -<h2>Reporting New Security Problems with Apache StormCrawler</h2> - <p>The Apache Software Foundation takes a very active stance in eliminating security problems and denial of service attacks against its products.</p> - <p>We strongly encourage people to report security problems privately using the security mailing list of the <a href="https://www.apache.org/security/">ASF Security Team</a> before disclosing them in a public forum.</p> - <p>Please note that the security mailing list should only be used for reporting undisclosed security vulnerabilities and managing the process of fixing such vulnerabilities. We cannot accept regular bug reports or other queries at this address. All mail sent to this address that does not relate to an undisclosed security problem in our source code will be ignored.</p> - <div class="callout callout--alert"><p>The private security mailing address is: <a class="externalLink" href="mailto:[email protected]">[email protected]</a></p></div> - - <h2>Threat Model and Security Considerations</h2> - <p>StormCrawler is designed to operate in trusted environments as part of a distributed Apache Storm® cluster. This document outlines the threat model and key security assumptions to help users understand the secure use and deployment of StormCrawler.</p> - - <h3>Trusted Configuration</h3> - <p>The configuration file used by StormCrawler is loaded during topology submission and is treated as a trusted source. It does not involve any user-supplied input at runtime.</p> - <p>If an attacker is able to modify this file, they would already have full access to the system, including:</p> - <ul> - <li>The ability to alter behavior of the topology</li> - <li>Access to credentials and other secrets</li> - <li>Arbitrary control over job execution</li> - </ul> - <p>Securing the configuration file and the environment in which topologies are submitted is essential. However, modification of the file implies full system compromise and is out of scope for runtime protections.</p> - - <h3>Apache Storm® Cluster Security</h3> - <p>StormCrawler runs on an Apache Storm® cluster, which is designed to allow users to:</p> - <ul> - <li>Submit topologies</li> - <li>Execute custom, user-defined code</li> - </ul> - <p>This model inherently trusts cluster users and assumes they are authorized.</p> - - <h4>Security Recommendations:</h4> - <ul> - <li>Access to the Apache Storm® cluster must be strictly restricted to trusted users</li> - <li>Underlying systems should not store secrets or hold elevated privileges beyond those assigned to the authorized users</li> - <li>Avoid deploying StormCrawler in multi-tenant environments without strong isolation guarantees</li> - </ul> - - <h3>Summary</h3> - <p>StormCrawler's security model assumes a trusted deployment environment. Users should:</p> - <ul> - <li>Secure configuration files and deployment infrastructure</li> - <li>Restrict Apache Storm® cluster access</li> - <li>Follow best practices for secret and privilege management</li> - </ul> - - <h2>Asking Questions About Known Security Problems</h2> - <p>Questions about:</p> - <ul> - <li>if a vulnerability applies to your particular application</li> - <li>obtaining further information on a published vulnerability</li> - <li>availability of patches and/or new releases</li> - </ul> - <p>should be addressed to the <a href="https://lists.apache.org/[email protected]">dev mailing list</a>.</p> - - <h2>Known Security Vulnerabilities</h2> - <p>No known security vulnerability yet.</p> - +<div class="callout callout--alert"> + <p><b>Found a vulnerability?</b> Report it privately to <a href="mailto:[email protected]">[email protected]</a> — never in a public forum. The security list is only for undisclosed vulnerabilities and managing their fixes; regular bug reports and other queries cannot be accepted at this address.</p> +</div> + +<h2>How disclosure works</h2> +<p>The Apache Software Foundation takes a very active stance in eliminating security problems and denial of service attacks against its products. Every report follows the <a href="https://www.apache.org/security/" target="_blank" rel="noopener">ASF security process</a>:</p> +<div class="vchain vchain--3"> + <div class="vlink"> + <p class="slabel">01 — REPORT</p> + <h3>Privately</h3> + <p>You write to the ASF Security Team; the report stays confidential.</p> + </div> + <div class="vlink"> + <p class="slabel">02 — FIX</p> + <h3>Quietly</h3> + <p>The PMC develops, reviews and releases a fix before any disclosure.</p> + </div> + <div class="vlink"> + <p class="slabel">03 — DISCLOSE</p> + <h3>Publicly</h3> + <p>The vulnerability is announced once a fixed release is available.</p> + </div> +</div> + +<h2>Threat Model and Security Considerations</h2> +<p>StormCrawler is designed to operate in trusted environments as part of a distributed <a href="https://storm.apache.org/" target="_blank" rel="noopener">Apache Storm®</a> cluster.</p> + +<h3>Trusted Configuration</h3> +<p>The configuration file used by StormCrawler is loaded during topology submission and is treated as a trusted source. If an attacker is able to modify this file, they would already have full access to the system. Securing the configuration file and the environment in which topologies are submitted is essential.</p> + +<h3>Apache Storm® Cluster Security</h3> +<p>StormCrawler runs on an <a href="https://storm.apache.org/" target="_blank" rel="noopener">Apache Storm®</a> cluster, which allows users to submit topologies and execute custom, user-defined code. This model inherently trusts cluster users and assumes they are authorized.</p> +<ul> + <li>Access to the Apache Storm® cluster must be strictly restricted to trusted users</li> + <li>Underlying systems should not store secrets or hold elevated privileges beyond those assigned to the authorized users</li> + <li>Avoid deploying StormCrawler in multi-tenant environments without strong isolation guarantees</li> +</ul> + +<h2>Asking Questions About Known Security Problems</h2> +<p>Questions about whether a vulnerability applies to your application, further information on a published vulnerability, or availability of patches should be addressed to the <a href="https://lists.apache.org/[email protected]" target="_blank" rel="noopener">dev mailing list</a> (<a href="mailto:[email protected]">subscribe</a> · <a href="mailto:[email protected]">unsubscribe</a>).</p> + +<h2>Known Security Vulnerabilities</h2> +<p>No known security vulnerability yet.</p> diff --git a/support/index.html b/support/index.html index aa86f2f..4ac77c2 100644 --- a/support/index.html +++ b/support/index.html @@ -2,23 +2,31 @@ layout: default slug: support title: Need assistance from web crawling experts? -eyebrow: Support +eyebrow: SUPPORT heading: Support description: Community help on GitHub and the mailing lists, plus commercial support options. --- -<p>You can ask questions related to Apache StormCrawler on Github in the <a href="https://github.com/apache/stormcrawler/discussions">discussions section</a>, on <a href="http://stackoverflow.com/questions/tagged/stormcrawler">stackoverflow</a> using the tag 'stormcrawler'.</p> -<p>If you think you've found a bug, please <a href="https://github.com/apache/stormcrawler/issues">open an issue</a> on GitHub.</p> - - <h2>Commercial Support</h2> - <p>The Apache StormCrawler PMC does not endorse or recommend any of the products or services on this page. We love all our supporters equally.</p> - - <h2>Want to be added to this page? </h2> - <p>All submitted information must be factual and informational in nature and not be a marketing statement. Statements that promote your products and services over other offerings on the page will not be tolerated and will be removed. Such marketing statements can be added to your own pages on your own site.</p> - <p>When in doubt, email the Apache StormCrawler PMC and ask. We are happy to help.</p> - - <h2>Companies</h2> - <ul> - <li><a href="http://digitalpebble.com">DigitalPebble Ltd:</a> Commercial Support and Consulting Services</li> - </ul> +<div class="lanes"> + <div class="lane"> + <p class="slabel">COMMUNITY — FREE</p> + <h3>Ask the community</h3> + <p>Questions go to GitHub discussions or Stack Overflow (tag 'stormcrawler'); bugs to GitHub issues. There is also a <b>#stormcrawler</b> channel on the ASF Slack: committers join directly with their @apache.org address, everyone else can ask for a guest invite on the <a href="mailto:[email protected]">dev mailing list</a> (<a href="mailto:[email protected]">subscribe</a>).</p> + <div class="chip-cluster" style="margin-top:14px;"> + <a class="chip" href="https://github.com/apache/stormcrawler/discussions" target="_blank" rel="noopener">DISCUSSIONS ↗</a> + <a class="chip" href="http://stackoverflow.com/questions/tagged/stormcrawler" target="_blank" rel="noopener">STACK OVERFLOW ↗</a> + <a class="chip" href="https://github.com/apache/stormcrawler/issues" target="_blank" rel="noopener">ISSUES ↗</a> + <a class="chip" href="https://infra.apache.org/slack.html" target="_blank" rel="noopener">ASF SLACK ↗</a> + </div> + </div> + <div class="lane"> + <p class="slabel">COMMERCIAL — PAID HELP</p> + <h3>Hire an expert</h3> + <p>The companies below offer paid consulting, custom development and production support for StormCrawler deployments — for when you need guaranteed response times or someone to build and run the crawler for you.</p> + <p style="margin-top:12px;"><a href="http://digitalpebble.com" target="_blank" rel="noopener">DigitalPebble Ltd:</a> Commercial Support and Consulting Services</p> + <p style="margin-top:12px;font-size:12.5px;color:var(--muted);">The Apache StormCrawler PMC does not endorse or recommend any of the products or services on this page. We love all our supporters equally.</p> + </div> +</div> +<h2>Want to be added to this page?</h2> +<p>All submitted information must be factual and informational in nature and not be a marketing statement. Statements that promote your products and services over other offerings on the page will not be tolerated and will be removed. When in doubt, email the Apache StormCrawler PMC and ask. We are happy to help.</p> diff --git a/team/index.html b/team/index.html index 4bd8771..ec72c29 100644 --- a/team/index.html +++ b/team/index.html @@ -2,17 +2,12 @@ layout: default slug: team title: Apache StormCrawler Project Team -eyebrow: Community +eyebrow: COMMUNITY heading: Project Team description: The committers and PMC members behind Apache StormCrawler, and the contributors on our Wall of Fame. --- -<p> - Apache StormCrawler is built and maintained by a community of committers and - PMC members. The list below is generated at build time from the Apache - <a href="https://whimsy.apache.org/" target="_blank" rel="noopener">Whimsy</a> - LDAP exports. - </p> +<p>Apache StormCrawler is built and maintained by a community of committers and PMC members. The list below is generated at build time from the Apache <a href="https://whimsy.apache.org/" target="_blank" rel="noopener">Whimsy</a> LDAP exports.</p> {% if site.data.team and site.data.team.size > 0 %} <div class="contributor-grid">
