diff --git a/content/blog/2026-05-01-clean-hugo-blog-theme-features.md b/content/blog/2026-05-01-clean-hugo-blog-theme-features.md
index 4ca14742..580f6087 100644
--- a/content/blog/2026-05-01-clean-hugo-blog-theme-features.md
+++ b/content/blog/2026-05-01-clean-hugo-blog-theme-features.md
@@ -629,16 +629,16 @@ shortcode form (percent signs).
**Usage:**
```
-{{* swoosh_section background="purple" swoosh="bottom" */>}}
+{{* section_band color="light-purple" divider="bottom" */>}}
{{* section-heading title="Inside a swoosh" */>}}
-{{* /swoosh_section */>}}
+{{* /section_band */>}}
```
**Rendered:**
-{{< swoosh_section background="purple" swoosh="bottom" >}}
+{{< section_band color="light-purple" divider="bottom" >}}
{{< section-heading title="Inside a swoosh section" subtitle="Purple background • optional top or bottom curve" >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
## Intro section
@@ -839,7 +839,18 @@ hierarchy.
The `metrics-bar` shortcode renders a row of key statistics with large
numbers. Use it to highlight impact data.
-**Usage:**
+**Named presets** (live data from site YAML):
+
+```
+{{* metrics-bar set="volunteer" */>}}
+{{* metrics-bar set="packages" */>}}
+```
+
+Or set `metrics_set: volunteer` in page front matter and call
+`{{* metrics-bar */>}}`. Presets live in
+`layouts/partials/metrics/presets/`.
+
+**Manual stats** (static or demo content):
```
{{* metrics-bar
@@ -850,7 +861,7 @@ numbers. Use it to highlight impact data.
*/>}}
```
-**Rendered:**
+**Rendered** (manual example):
{{< metrics-bar
stat1="**128** blog posts migrated"
diff --git a/content/learn-universities-labs.md b/content/learn-universities-labs.md
index 5aaf0b08..51419a27 100644
--- a/content/learn-universities-labs.md
+++ b/content/learn-universities-labs.md
@@ -28,7 +28,7 @@ from open source leaders.
{{< university-benefits-list >}}
-{{% swoosh_section background="purple" swoosh="bottom" %}}
+{{% section_band color="light-purple" divider="bottom" %}}
{{< pullquote color="purple" author="Zach Chandler" event="Director, Stanford Open Source Program Office" >}}
pyOpenSci has proven to be a valuable partner in developing our competencies
@@ -38,7 +38,7 @@ does significant work in Python (which is all universities!) I highly
recommend getting involved with pyOpenSci.
{{< /pullquote >}}
-{{% /swoosh_section %}}
+{{% /section_band %}}
## Research software accelerator programs
@@ -59,7 +59,7 @@ source. Open doors for your researchers to engage with other Python developers,
troubleshoot challenges together, and gain ongoing support as they develop
their scientific open source skills.
-{{% swoosh_section background="purple" swoosh="top" %}}
+{{% section_band color="light-purple" divider="top" %}}
## Don't take our word for it: hear from previous course learners
@@ -67,14 +67,14 @@ their scientific open source skills.
*Our current content is the 10-day Ship It: Python Packaging in the GenAI Era course.*
-{{% /swoosh_section %}}
+{{% /section_band %}}
### Beta Partnership packages
We are currently piloting our partnership program to find the best combination
of resources and events that best support our University partners.
-{{% swoosh_section background="purple" swoosh="top" %}}
+{{% section_band color="light-purple" divider="top" %}}
## Ready to explore a pyOpenSci membership?
@@ -92,7 +92,7 @@ pyOpenSci also offers a wide variety of free training resources for individual
learners. Explore our workshops, webinars, and more on our
[events page](/events/).
-{{% /swoosh_section %}}
+{{% /section_band %}}
## Why partnering with pyOpenSci matters
diff --git a/content/learn.md b/content/learn.md
index 76879fcb..5402ff3c 100644
--- a/content/learn.md
+++ b/content/learn.md
@@ -27,7 +27,7 @@ Are you interested in cohort learning options? Visit our
page to learn more.
{{< /admonition >}}
-{{< swoosh_section background="purple" swoosh="bottom" >}}
+{{< section_band color="light-purple" divider="bottom" >}}
{{< feature-row layout="right"
title="Our process: Community-developed Python tutorials"
@@ -42,7 +42,7 @@ Our tutorials are created through a multi-stage community review process.
review for accuracy, usability and accessibility.
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
@@ -56,7 +56,7 @@ order, but you can always pick a specific topic if you wish to jump around.
-{{< swoosh_section background="purple" swoosh="top" >}}
+{{< section_band color="light-purple" divider="top" >}}
{{< feature-row layout="right"
title="Lessons: Collaborative GitHub for Scientists"
@@ -91,7 +91,7 @@ then work through the contribute path:
[Ways to contribute](https://www.pyopensci.org/lessons/contribute-open-source/ways-to-contribute.html)
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
diff --git a/content/partners.md b/content/partners.md
index e6659823..1327a868 100644
--- a/content/partners.md
+++ b/content/partners.md
@@ -14,7 +14,7 @@ header:
Domain-specific communities can partner with pyOpenSci to leverage both our peer
review process and development of Python packaging guidelines.
-{{< swoosh_section background="purple" swoosh="bottom" >}}
+{{< section_band color="light-purple" divider="bottom" >}}
{{< feature-row layout="right"
title="Community partnerships streamline peer review & packaging standards"
@@ -42,7 +42,7 @@ Through a single review process, community maintainers:
* Are supported in maintaining their tools, with input from our diverse, knowledgeable community, including active members from across the Python, Conda, PyPA and broader Python packaging ecosystem
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
## Partnering with pyOpenSci increases the visibility of your communities' tools
@@ -54,7 +54,7 @@ We will:
* Provide a feed allowing you to cross-list affiliated packages on your website.
* Keep in touch with maintainers to ensure packages are actively maintained.
-{{< swoosh_section background="purple" swoosh="top" >}}
+{{< section_band color="light-purple" divider="top" >}}
{{< feature-row layout="left"
title="Help scientists find the Python software tools that they need"
@@ -66,7 +66,7 @@ Our catalog of vetted open source tools makes it easier for scientists to find
the trusted tools that they need to develop their open science workflows.
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
## Leverage our peer review process
@@ -81,7 +81,7 @@ developed and well-documented peer review process which includes:
Learn more about our partnerships
{{< /button >}}
-{{< swoosh_section background="purple" swoosh="top" >}}
+{{< section_band color="light-purple" divider="top" >}}
{{< feature-row layout="left"
title="Let's make packaging easier for scientists, together"
@@ -102,7 +102,7 @@ across the Python ecosystem. This consistency will lower the barrier of entry fo
new potential contributors.
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
{{< feature-row layout="right"
title="Harness the power of community"
diff --git a/content/python-packaging-science.md b/content/python-packaging-science.md
index 350d529c..dbe0de15 100644
--- a/content/python-packaging-science.md
+++ b/content/python-packaging-science.md
@@ -16,7 +16,7 @@ intro_section:
{{< intro-section >}}
-{{< swoosh_section background="purple" swoosh="bottom" >}}
+{{< section_band color="light-purple" divider="bottom" >}}
{{< feature-row layout="right"
title="Community-created Python packaging guide"
@@ -40,7 +40,7 @@ citation.
{{< packaging-guide-grid >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
@@ -53,7 +53,7 @@ process of creating a Python package, following modern best practices.
-{{< swoosh_section background="purple" swoosh="top" >}}
+{{< section_band color="light-purple" divider="top" >}}
{{< feature-row layout="left"
title="Get involved: Help us improve our Python packaging resources"
@@ -74,4 +74,4 @@ All contributions are recognized both on our website and in the guidebook's
citation.
{{< /feature-row >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
diff --git a/content/volunteer.md b/content/volunteer.md
index 85db3c74..66eba801 100644
--- a/content/volunteer.md
+++ b/content/volunteer.md
@@ -1,98 +1,203 @@
---
-title: "Get involved with pyOpenSci"
+title: "Open science is fueled by people like you"
layout: splash
url: /volunteer/
aliases:
- /volunteer.html
- /get-involved-contact.html
description: >-
- Volunteer with pyOpenSci — review packages, contribute on GitHub, write for
- the blog, and join a diverse community supporting scientific open source.
-cards_layout: wide
+ pyOpenSci runs on volunteer energy. Whether you want to review a Python package, help maintain our tools, or translate resources for your community — there's a role that fits your time and skills.
+metrics_set: volunteer
+cards_align: center
+cards_layout: five
cards:
- - title: "Lend a Hand on GitHub"
+ - modifier: "card--white"
+ title: "Reviewer"
+ label: "Async"
+ label_style: "accent"
+ tags:
+ - "Technical review"
+ - "~5–10 hrs / review"
excerpt: >-
- Got some time to help? Check out our GitHub Project Board for a list of
- current issues that we could use help with. Any issue that is tagged
- help-wanted in our repos is also fair game for anyone to tackle! We add
- anyone who contributes to pyOpenSci to our
- [community page](/our-community/).
- url: "https://github.com/orgs/pyOpenSci/projects/3/views/1"
- cta: "> Check out our GitHub Help Wanted Board"
- - title: "Sign up to review a Python package"
+ Evaluate a Python package for usability, documentation, and packaging
+ best practices. Most reviewers complete one or two reviews per year on
+ their own schedule.
+ url: "https://forms.gle/GHfxvmS47nQFDcBM6"
+ cta: "Sign up to be a reviewer →"
+ - modifier: "card--white"
+ title: "Editor"
+ label: "Async"
+ label_style: "accent"
+ tags:
+ - "Editorial oversight"
+ - "~3–5 hrs / month"
+ excerpt: >-
+ Manage 2–3 active reviews — recruit reviewers, keep submissions on track,
+ and provide light technical guidance.
+ url: "https://docs.google.com/forms/d/e/1FAIpQLScRQHQ7NKVEAG3BKAphiUdVFvQ5nkez0IpyXBMZDzXjuBPloQ/viewform"
+ cta: "Apply to be an editor →"
+ - modifier: "card--white"
+ title: "Editor in Chief"
+ label: "Team role"
+ label_style: "team"
+ tags:
+ - "Leadership"
+ - "6–12 month rotation"
+ excerpt: >-
+ Serve as rotating lead for the editorial board — onboard new editors,
+ handle escalations, and help shape review policies.
+ #url: "https://docs.google.com/forms/d/e/1FAIpQLScRQHQ7NKVEAG3BKAphiUdVFvQ5nkez0IpyXBMZDzXjuBPloQ/viewform"
+ #cta: "Express interest →"
+ - modifier: "card--white"
+ title: "Peer review triage"
+ label: "Async"
+ label_style: "accent"
+ tags:
+ - "Intake & routing"
+ - "~2–4 hrs / month"
+ excerpt: >-
+ Screen new package submissions before they enter the review queue. A good
+ entry point if you want editorial experience with a lighter time
+ commitment.
+ #url: "https://forms.gle/GHfxvmS47nQFDcBM6"
+ cta: "Sign up for triage →"
+ - modifier: "card--stipend"
+ title: "Peer review lead"
+ label: "Stipended"
+ label_style: "stipend"
+ tags:
+ - "Program coordination"
+ - "Part-time"
+ excerpt: >-
+ Coordinate the day-to-day health of the review program — track open
+ submissions, support editors, and iterate on process documentation. This
+ role carries a stipend.
+ #url: ""
+ cta: "Apply for this role →"
+translation_cards_align: center
+translation_cards:
+ - modifier: "card--white"
+ title: "Translation Lead"
+ label: "Coordination"
+ label_style: "team"
+ tags:
+ - "Language coordination"
+ - "~3–5 hrs / month"
excerpt: >-
- We are always looking for new reviewers from a broad range of scientific
- domains. Some reviewers have extensive packaging expertise and others
- have domain expertise or focus on package usability. If you are new to
- reviewing we are happy to support you through our peer review mentorship
- program.
- [Learn more about the reviewer role](https://www.pyopensci.org/software-peer-review/how-to/reviewer-guide.html)
- and sign up using the link below.
+ Own a language track — coordinate contributors, manage review cycles, and
+ keep translations consistent with source updates.
+ url: "mailto:hello@pyopensci.org"
+ cta: "Apply to lead →"
+ - modifier: "card--white"
+ title: "Translation Contributor"
+ label: "Async"
+ label_style: "accent"
+ tags:
+ - "Translating & reviewing"
+ - "Flexible"
+ excerpt: >-
+ Translate or review content for a language you know well. Contribute as
+ much or as little as your schedule allows.
url: "https://forms.gle/GHfxvmS47nQFDcBM6"
- cta: "> Sign up now."
- - title: "Get involved as software peer review Editor"
+ cta: "Sign up to translate →"
+maintainer_cards_align: left
+maintainer_cards:
+ - modifier: "card--white"
+ title: "Infrastructure maintainer"
+ label: "Ongoing"
+ label_style: "team"
+ tags:
+ - "CI & infrastructure"
+ - "Flexible"
+ excerpt: >-
+ Start with triage — labeling issues and reviewing pull requests — then
+ grow into CI, deployments, and codebase ownership as you get comfortable.
+ All skill levels welcome.
+ #url: "https://www.pyopensci.org/handbook/CONTRIBUTING.html"
+ #cta: "Become a maintainer →"
+ - modifier: "card--white"
+ title: "Website contributor"
+ label: "Async"
+ label_style: "accent"
+ tags:
+ - "Content & design"
+ - "Flexible"
excerpt: >-
- We also often recruit new editors to support our peer review process.
- url: "https://www.pyopensci.org/software-peer-review/how-to/editors-guide.html"
- cta: "> Click here to learn more about the editor role."
+ Help keep pyOpenSci.org welcoming and useful — write and edit content,
+ improve accessibility, refine our Hugo theme, and polish page layouts.
+ #url: "https://github.com/pyOpenSci/pyopensci.github.io"
+ cta: "Support our website →"
+impact_destinations:
+ eyebrow: "Your impact"
+ title: "Where your work goes"
+ subtitle: >-
+ Volunteer contributions flow directly into resources scientists across
+ the ecosystem rely on every day.
+ items:
+ - icon: "fa-solid fa-box"
+ title: "Python Packaging Guide"
+ excerpt: "The community reference for packaging"
+ url: "https://www.pyopensci.org/python-package-guide/"
+ - icon: "fa-solid fa-globe"
+ title: "pyOpenSci website"
+ excerpt: "Content, design, and infrastructure"
+ url: "/"
+ - icon: "fa-solid fa-book"
+ title: "Tutorials & lessons"
+ excerpt: "Open learning content for researchers"
+ url: "/learn/"
+ - icon: "fa-solid fa-magnifying-glass"
+ title: "Peer review process"
+ excerpt: "Infrastructure for software quality"
+ url: "/about-peer-review/"
+ - icon: "fa-solid fa-earth-americas"
+ title: "Translations"
+ excerpt: "Resources in more languages"
+ url: "https://www.pyopensci.org/python-package-guide/"
---
-pyOpenSci is a volunteer community that broadens participation in scientific
-open source. We make finding, sharing and contributing to reusable code easier
-for everyone, everywhere.
+{{< feature-row layout="right"
+ title="Volunteers are the core of everything we do"
+ image="/images/people/leah-chase-pavithra-carol.png"
+ alt="Four pyOpenSci community members, including Carol Willing, smiling together outdoors at PyCon US 2023." >}}
+Every package that earns a pyOpenSci badge has been evaluated by a volunteer
+reviewer. Every guide a researcher relies on has been written, edited, or
+translated by a community member. Every sprint has been organized by someone
+who believed open science was worth their time.
+
+When you volunteer with pyOpenSci, your work flows directly into the
+infrastructure the scientific Python ecosystem depends on — and into a community
+of people doing the same.
+{{< /feature-row >}}
+
+{{< section-heading align="left"
+ title="Support software review"
+ subtitle="pyOpenSci runs a community peer review process for research software written in Python. [Learn about peer review →](/about-peer-review/)" >}}
-{{< swoosh_section background="purple" swoosh="bottom" >}}
-{{< section-heading title="Volunteer opportunities with pyOpenSci" subtitle="There are many ways to get involved with pyOpenSci! We're always looking for folks to:" >}}
{{< impact-cards >}}
-{{< /swoosh_section >}}
-## Share your expertise and experience with the broader community through blogging
-And last but not least, we'd also love for you to be a guest blogger on the
-[pyOpenSci blog](/blog/)! If you'd like to write about a pyOpenSci package,
-your experiences with pyOpenSci, or how you're using free and open Python tools
-in your scientific endeavors, we'd love to hear from you! Email our Community
-team at [media@pyopensci.org](mailto:media@pyopensci.org) for more information.
-{{% swoosh_section background="purple" swoosh="bottom" %}}
-{{< figure src="/images/people/pyopensci-sprint-pycon-2023.png" alt="Contributors collaborating at a pyOpenSci sprint during PyCon US 2023." >}}
+{{< section_band color="light-purple" divider="bottom" >}}
+
+{{< section-heading eyebrow="Translation" align="left"
+ title="Help break down language barriers"
+ subtitle="Our community is global. Our resources should be too. Translation volunteers make pyOpenSci's guides and tutorials accessible to scientists who don't work primarily in English. Translations for our [Python Packaging Guide](https://www.pyopensci.org/python-package-guide/) are in progress. Add a new language or support building out an existing on." >}}
-### pyOpenSci volunteers come from diverse backgrounds
+{{< impact-cards section="translation" >}}
-Our volunteers come from a diverse array of backgrounds, including industry,
-academia, agencies, national labs, and more. pyOpenSci volunteers are
-primarily engaged in both the peer review process and developing resources to
-support the scientific Python community. Volunteers help improve the quality,
-maintainability and usability of the software that scientists need for open
-science. They also support maintainers in developing scientific Python
-software.
+{{< /section_band >}}
-{{% /swoosh_section %}}
+{{< section-heading eyebrow="pyOpenSci Maintainer Team" align="left"
+ title="Help build and maintain our infrastructure"
+ subtitle="Our maintainer team keeps the website, guides, and tooling running. Roles follow a triage → merge → issue ownership progression. [Read the contributor handbook →](https://www.pyopensci.org/handbook/CONTRIBUTING.html)" >}}
-{{< figure src="/images/people-building-blocks.jpg" alt="Illustration of people building blocks together, representing community collaboration." >}}
+{{< impact-cards section="maintainer" >}}
-### pyOpenSci volunteers build skills and community
-When you volunteer with pyOpenSci, you're both giving back and developing
-professional skills. As a volunteer you will:
-* **Learn new skills:** you don't have to be a Python expert to get involved
- with pyOpenSci. We can help you level up your packaging game and learn how
- to constructively review both code and copy through contributions to our
- online learning resources.
-* **Get recognized** on the [pyOpenSci website](/our-community/) and in our
- [GitHub repositories](https://github.com/pyOpenSci): your contribution
- matters, and we want to ensure your work is recognized and celebrated in a
- public forum. If you serve as an editor you can also connect with pyOpenSci
- professionally as a volunteer for our organization on LinkedIn.
-{{< swoosh_section background="purple" swoosh="top" >}}
-{{< section-heading title="Meet the most recent pyOpenSci community contributors" >}}
-{{< recent-contributors >}}
-{{< button href="/our-community/" variant="green" icon="arrow-right" iconPosition="right" >}}
-View all contributors
-{{< /button >}}
-{{< /swoosh_section >}}
+{{< metrics-bar >}}
-{{< connect-with-pyos >}}
+{{< impact-destinations >}}
diff --git a/content/ways-to-give.md b/content/ways-to-give.md
index 86ba6c28..c87855a4 100644
--- a/content/ways-to-give.md
+++ b/content/ways-to-give.md
@@ -24,7 +24,7 @@ an individual funder passionate about open science, we offer clear pathways for
you to support our mission.
{{< /feature-row >}}
-{{< swoosh_section background="purple" >}}
+{{< section_band color="light-purple" >}}
## For companies: corporate sponsorship
@@ -80,9 +80,9 @@ with different goals. Contact us to design a package that fits your team.
Inquire to learn more
{{< /button >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
-{{< swoosh_section background="white" >}}
+{{< section_band color="white" >}}
## For funders & community: direct donations
@@ -122,4 +122,4 @@ access to world-class education while sustaining pyOpenSci's mission.
Donate today
{{< /button >}}
-{{< /swoosh_section >}}
+{{< /section_band >}}
diff --git a/hugo.toml b/hugo.toml
index 6841181a..0aeea11e 100644
--- a/hugo.toml
+++ b/hugo.toml
@@ -32,8 +32,8 @@ buildFuture = true
# Upper-left nav: logo image (served from static/images/).
[params.nav_logo]
- src = 'images/logo.png'
- alt = 'pyOpenSci logo: a purple flower opening over the O in Open, representing open science.'
+ src = 'images/logo-white.png'
+ alt = 'pyOpenSci logo: white flower icon above pyOpenSci wordmark.'
blog_title = 'pyOpenSci Blog'
description = 'pyOpenSci broadens participation in scientific open source.'
@@ -133,7 +133,7 @@ buildFuture = true
[params.nav_button]
text = 'Donate'
url = 'https://give.communityin.org/pyopensci_2024'
- variant = 'dark-purple'
+ variant = 'green'
# Site search — Algolia InstantSearch (ported from search branch / PR #788).
[params.search]
diff --git a/static/images/logo-white.png b/static/images/logo-white.png
new file mode 100644
index 00000000..075b0e10
Binary files /dev/null and b/static/images/logo-white.png differ
diff --git a/static/images/people/leah-chase-pavithra-carol.png b/static/images/people/leah-chase-pavithra-carol.png
new file mode 100644
index 00000000..5c05a2b2
Binary files /dev/null and b/static/images/people/leah-chase-pavithra-carol.png differ
diff --git a/static/images/people/leah-chase-pavithra-carol.webp b/static/images/people/leah-chase-pavithra-carol.webp
new file mode 100644
index 00000000..5cb802dc
Binary files /dev/null and b/static/images/people/leah-chase-pavithra-carol.webp differ
diff --git a/themes/clean-hugo/assets/css/_blog.scss b/themes/clean-hugo/assets/css/_blog.scss
index 301f9681..5f5c8c79 100644
--- a/themes/clean-hugo/assets/css/_blog.scss
+++ b/themes/clean-hugo/assets/css/_blog.scss
@@ -3,7 +3,7 @@
position: relative;
padding: $spacing-xl 0;
margin-bottom: $spacing-lg;
- background-color: var(--color-primary-dark);
+ background-color: var(--color-surface-dark-purple);
@media (min-width: $breakpoint-md) {
padding: $spacing-xl 0;
@@ -31,7 +31,7 @@
background-size: cover;
background-repeat: no-repeat;
background-position: center;
- background-color: var(--color-primary-dark);
+ background-color: var(--color-surface-dark-purple);
@media (min-width: $breakpoint-md) {
min-height: 13rem;
diff --git a/themes/clean-hugo/assets/css/_cards.scss b/themes/clean-hugo/assets/css/_cards.scss
index 25ffbe6b..e85f06d7 100644
--- a/themes/clean-hugo/assets/css/_cards.scss
+++ b/themes/clean-hugo/assets/css/_cards.scss
@@ -1,8 +1,22 @@
/* Impact Cards Section - Hero/splash page cards */
.impact-cards {
+ padding: $fluid-space-2xl 0;
+}
+
+.impact-cards__container {
max-width: $container-max-width;
- margin: 0 auto;
- padding: $fluid-space-2xl $fluid-container-padding;
+ width: 100%;
+ margin-inline: auto;
+ padding-inline: $fluid-container-padding;
+ box-sizing: border-box;
+}
+
+.impact-cards--align-left .impact-cards__container {
+ margin-inline: 0 auto 0 0;
+}
+
+.impact-cards--align-right .impact-cards__container {
+ margin-inline: 0 0 0 auto;
}
.impact-cards__grid {
@@ -17,7 +31,9 @@
/* Opt-in from page front matter: cards_layout: wide */
.impact-cards--wide {
- max-width: $container-impact-wide;
+ .impact-cards__container {
+ max-width: $container-impact-wide;
+ }
}
.impact-cards__grid--wide {
@@ -26,6 +42,33 @@
}
}
+/* Opt-in: cards_layout: five — 3 cards, then 2 centered (volunteer peer review) */
+.impact-cards--five {
+ padding-top: 0;
+
+ .impact-cards__container {
+ max-width: $container-impact-wide;
+ }
+}
+
+.impact-cards__grid--five {
+ @media (min-width: $breakpoint-lg) {
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+
+ > .card {
+ grid-column: span 2;
+ }
+
+ > .card:nth-child(4) {
+ grid-column: 2 / span 2;
+ }
+
+ > .card:nth-child(5) {
+ grid-column: 4 / span 2;
+ }
+ }
+}
+
/* Impact cards - fluid padding scales from mobile to desktop */
.impact-cards .card {
padding: $fluid-card-padding-lg;
@@ -59,8 +102,12 @@
}
.impact-cards {
+ padding-inline: 0;
+ }
+
+ .impact-cards__container {
max-width: none;
- margin: 0;
+ margin-inline: 0;
padding-inline: 0;
}
}
@@ -172,6 +219,73 @@
box-shadow: 0 6px 18px rgba(17,24,39,0.06);
}
+ .card__header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 0.75rem;
+ margin-bottom: 0.75rem;
+ }
+
+ .card__header h3 {
+ flex: 1 1 auto;
+ min-width: 0;
+ margin-bottom: 0;
+ }
+
+ .card__label {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.3rem 0.65rem;
+ border-radius: $radius-full;
+ line-height: 1.2;
+ white-space: nowrap;
+ }
+
+ .card__label--accent {
+ background-color: var(--color-admonition-success, var(--color-primary));
+ color: var(--color-white);
+ }
+
+ .card__label--team {
+ background-color: var(--color-brand-light-purple);
+ color: var(--color-brand-purple);
+ }
+
+ .card__label--stipend {
+ background-color: var(--color-secondary);
+ color: var(--color-gray-900);
+ }
+
+ .card__label--default {
+ background-color: var(--color-gray-200);
+ color: var(--color-gray-700);
+ }
+
+ .card__tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.4rem;
+ margin: 0 0 1rem;
+ padding: 0;
+ list-style: none;
+ }
+
+ .card__tag {
+ display: inline-flex;
+ align-items: center;
+ font-size: 0.75rem;
+ font-weight: 500;
+ padding: 0.25rem 0.55rem;
+ border-radius: $radius-full;
+ line-height: 1.2;
+ background-color: var(--color-gray-200);
+ color: var(--color-gray-700);
+ }
+
.card {
@include card-base();
@include responsive-padding(1.25rem, 1.25rem, 2.5rem, 2.5rem);
@@ -188,6 +302,7 @@
font-size: 1.5rem;
}
}
+ > div > .card__excerpt,
> div > h3 + div {
font-size: $card-text-size;
line-height: $card-line-height;
@@ -242,6 +357,25 @@
background-color: var(--color-white);
}
+ /* Stipended volunteer roles — light green fill (after .card base white) */
+ .card--stipend {
+ border-color: var(--color-admonition-success);
+ background-color: color-mix(
+ in srgb,
+ var(--color-admonition-success) 35%,
+ var(--color-white)
+ );
+ }
+
+ .card--featured {
+ border-color: var(--color-secondary);
+ background-color: color-mix(
+ in srgb,
+ var(--color-secondary) 18%,
+ var(--color-white)
+ );
+ }
+
.card__icon {
font-size: $card-icon-size;
margin-bottom: 1rem;
diff --git a/themes/clean-hugo/assets/css/_content.scss b/themes/clean-hugo/assets/css/_content.scss
index 81fcc3ef..3580fad6 100644
--- a/themes/clean-hugo/assets/css/_content.scss
+++ b/themes/clean-hugo/assets/css/_content.scss
@@ -159,8 +159,9 @@
.content-main > .two-card-row,
.content-main > .three-card-row,
.content-main > .impact-cards,
-.content-main > .swoosh-section,
-.content-main > .swoosh-section__divider-wrap,
+.content-main > .section-band,
+.content-main > .section-band__divider-wrap,
+.content-main > .impact-destinations,
.splash-page__content > .metrics-bar,
.splash-page__content > .blog-highlight,
.splash-page__content > .single-card-wide,
@@ -168,8 +169,9 @@
.splash-page__content > .two-card-row,
.splash-page__content > .three-card-row,
.splash-page__content > .impact-cards,
-.splash-page__content > .swoosh-section,
-.splash-page__content > .swoosh-section__divider-wrap {
+.splash-page__content > .section-band,
+.splash-page__content > .section-band__divider-wrap,
+.splash-page__content > .impact-destinations {
width: 100vw;
position: relative;
left: 50%;
diff --git a/themes/clean-hugo/assets/css/_feature-row.scss b/themes/clean-hugo/assets/css/_feature-row.scss
index 543a5fbf..0a29b94d 100644
--- a/themes/clean-hugo/assets/css/_feature-row.scss
+++ b/themes/clean-hugo/assets/css/_feature-row.scss
@@ -12,6 +12,10 @@
display: grid;
gap: 1.5rem;
align-items: start;
+
+ @media (min-width: $breakpoint-md) {
+ align-items: center;
+ }
}
&__media {
@@ -54,8 +58,8 @@
&--left {
.feature-row__inner {
@media (min-width: $breakpoint-md) {
- /* Image ~33%, text ~66% */
- grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
+ /* Image 2/5, text 3/5 */
+ grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
}
}
}
@@ -63,8 +67,8 @@
&--right {
.feature-row__inner {
@media (min-width: $breakpoint-md) {
- /* Text ~66%, image ~33% (body fills col 1 via order) */
- grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
+ /* Text 3/5, image 2/5 (body fills col 1 via order) */
+ grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
.feature-row__media {
order: 2;
diff --git a/themes/clean-hugo/assets/css/_impact-destinations.scss b/themes/clean-hugo/assets/css/_impact-destinations.scss
new file mode 100644
index 00000000..c81c1ddf
--- /dev/null
+++ b/themes/clean-hugo/assets/css/_impact-destinations.scss
@@ -0,0 +1,135 @@
+/* Dark purple band — “where your work goes” */
+
+.impact-destinations {
+ width: 100%;
+ padding: $fluid-space-2xl $fluid-container-padding;
+ background-color: var(--color-surface-dark-purple-card);
+ color: var(--color-white);
+ box-sizing: border-box;
+
+ &__container {
+ max-width: $container-max-width;
+ margin-inline: auto;
+ }
+
+ &__header {
+ max-width: 42rem;
+ margin-bottom: $fluid-space-lg;
+
+ /* Beat .splash-page__content p { color: gray-700 } */
+ .impact-destinations__eyebrow {
+ margin: 0 0 0.5rem;
+ font-family: $font-family-heading;
+ font-size: 0.8125rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--color-admonition-success);
+ }
+
+ .impact-destinations__subtitle {
+ margin: 0;
+ font-size: 1.0625rem;
+ line-height: 1.6;
+ color: var(--color-white);
+ }
+ }
+
+ &__title {
+ margin: 0 0 0.75rem;
+ font-size: clamp(1.75rem, 4vw, 2.5rem);
+ font-weight: 700;
+ line-height: 1.2;
+ color: var(--color-white);
+ }
+
+ &__grid {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: 1fr;
+
+ @media (min-width: $breakpoint-md) {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ @media (min-width: $breakpoint-lg) {
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ }
+ }
+
+ &__card {
+ display: flex;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 1.25rem 1.5rem;
+ border-radius: $radius-lg;
+ border: 1px solid color-mix(
+ in srgb,
+ var(--color-brand-medium-purple) 35%,
+ transparent
+ );
+ background-color: var(--color-surface-footer-purple);
+ color: var(--color-white);
+ text-decoration: none;
+ transition: border-color $transition-base ease,
+ transform $transition-base ease;
+
+ @media (min-width: $breakpoint-lg) {
+ grid-column: span 2;
+ }
+
+ &:nth-child(4) {
+ @media (min-width: $breakpoint-lg) {
+ grid-column: 2 / span 2;
+ }
+ }
+
+ &:nth-child(5) {
+ @media (min-width: $breakpoint-lg) {
+ grid-column: 4 / span 2;
+ }
+ }
+
+ &:hover,
+ &:focus-visible {
+ border-color: color-mix(
+ in srgb,
+ var(--color-admonition-success) 55%,
+ transparent
+ );
+ transform: translateY(-2px);
+ }
+
+ /* Beat .splash-page__content p { color: gray-700 } */
+ .impact-destinations__card-text {
+ margin: 0;
+ font-size: 0.9375rem;
+ line-height: 1.5;
+ color: color-mix(in srgb, var(--color-white) 90%, transparent);
+ }
+ }
+
+ &__icon {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 2.5rem;
+ height: 2.5rem;
+ font-size: 1.25rem;
+ color: var(--color-admonition-success);
+ }
+
+ &__body {
+ min-width: 0;
+ }
+
+ &__card-title {
+ margin: 0 0 0.35rem;
+ font-family: $font-family-heading;
+ font-size: 1.0625rem;
+ font-weight: 700;
+ line-height: 1.3;
+ color: var(--color-white);
+ }
+}
diff --git a/themes/clean-hugo/assets/css/_navigation.scss b/themes/clean-hugo/assets/css/_navigation.scss
index 4d5bcb35..c0c8770e 100644
--- a/themes/clean-hugo/assets/css/_navigation.scss
+++ b/themes/clean-hugo/assets/css/_navigation.scss
@@ -1,8 +1,7 @@
-/* Site navigation */
+/* Site navigation — dark purple banner with light text */
.site-nav {
- border-bottom: 1px solid var(--color-gray-200);
position: relative;
- background-color: var(--color-white);
+ background-color: var(--color-brand-purple);
z-index: 1000;
}
@@ -22,16 +21,16 @@
.site-nav__brand {
font-size: 1.5rem;
font-weight: 700;
- color: var(--color-gray-900);
+ color: var(--color-white);
text-decoration: none;
&:hover {
- color: var(--color-link);
+ color: var(--color-link-on-dark);
}
&:focus {
- color: var(--color-link);
- outline: 2px solid var(--color-link);
+ color: var(--color-link-on-dark);
+ outline: 2px solid var(--color-link-on-dark);
outline-offset: 2px;
}
@@ -64,7 +63,11 @@
width: 2.5rem;
height: 2.5rem;
padding: 0.5rem;
- border: 1px solid var(--color-gray-200);
+ border: 1px solid color-mix(
+ in srgb,
+ var(--color-white) 35%,
+ transparent
+ );
border-radius: 0.25rem;
background-color: transparent;
cursor: pointer;
@@ -72,12 +75,17 @@
flex-shrink: 0;
&:hover {
- background-color: var(--color-gray-50);
+ background-color: color-mix(
+ in srgb,
+ var(--color-white) 10%,
+ transparent
+ );
}
&:focus {
outline: none;
- box-shadow: 0 0 0 2px var(--color-link), 0 0 0 4px var(--color-white);
+ box-shadow: 0 0 0 2px var(--color-link-on-dark),
+ 0 0 0 4px var(--color-brand-purple);
}
&[aria-expanded="true"] {
@@ -108,7 +116,7 @@
width: 1.25rem;
height: 0.125rem;
margin-bottom: 0.25rem;
- background-color: var(--color-gray-900);
+ background-color: var(--color-white);
transition: all 0.2s;
&:last-child {
@@ -124,9 +132,13 @@
left: 0;
right: 0;
flex-direction: column;
- background-color: white;
- border-top: 1px solid var(--color-gray-200);
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
+ background-color: var(--color-brand-purple);
+ border-top: 1px solid color-mix(
+ in srgb,
+ var(--color-white) 12%,
+ transparent
+ );
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.25);
padding-block: 1rem;
&[aria-expanded="true"],
@@ -156,17 +168,18 @@
font-weight: 500;
transition: color 0.2s;
display: block;
- color: var(--color-gray-900);
+ color: var(--color-white);
text-decoration: none;
&:hover {
- color: var(--color-link);
+ color: var(--color-link-on-dark);
}
&:focus {
- color: var(--color-link);
+ color: var(--color-link-on-dark);
outline: none;
- box-shadow: 0 0 0 2px var(--color-link), 0 0 0 4px var(--color-white);
+ box-shadow: 0 0 0 2px var(--color-link-on-dark),
+ 0 0 0 4px var(--color-brand-purple);
}
}
@@ -291,11 +304,30 @@
/* Navigation CTA Button - uses base button system */
.site-nav__button {
- /* Navigation-specific positioning */
margin-top: 1rem;
@media (min-width: $breakpoint-md) {
margin-top: 0;
margin-left: 1rem;
}
+
+ /* Solid pyOpenSci green on dark nav */
+ &.btn--green {
+ background-color: var(--color-admonition-success);
+ color: var(--color-gray-900);
+
+ &:hover {
+ background-color: color-mix(
+ in srgb,
+ var(--color-admonition-success) 85%,
+ var(--color-gray-900)
+ );
+ color: var(--color-gray-900);
+ }
+
+ &:focus {
+ box-shadow: 0 0 0 2px var(--color-brand-purple),
+ 0 0 0 4px var(--color-admonition-success);
+ }
+ }
}
diff --git a/themes/clean-hugo/assets/css/_people.scss b/themes/clean-hugo/assets/css/_people.scss
index 77a92711..725a051b 100644
--- a/themes/clean-hugo/assets/css/_people.scss
+++ b/themes/clean-hugo/assets/css/_people.scss
@@ -94,10 +94,10 @@
margin-top: 3rem;
}
- /* Home / volunteer recent contributors — fluid cards, no awkward 4+1 wrap */
+ /* Home / volunteer recent contributors — compact row of five */
.people-grid.people-grid--recent {
margin-top: 1.5rem;
- gap: clamp(0.75rem, 1.5vw, 1rem);
+ gap: clamp(0.5rem, 1.25vw, 0.875rem);
grid-template-columns: minmax(0, 1fr);
@media (min-width: $breakpoint-sm) {
@@ -109,27 +109,38 @@
}
@media (min-width: $breakpoint-lg) {
- grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ }
+
+ .people_card__image {
+ aspect-ratio: 3 / 2;
}
.people_card__content {
- padding: clamp(0.65rem, 1.25vw, 1rem);
+ padding: clamp(0.5rem, 1vw, 0.75rem);
h3 {
- font-size: clamp(0.9375rem, 1.5vw, 1.125rem);
+ font-size: clamp(0.875rem, 1.35vw, 1rem);
+ margin-bottom: 0.25rem;
}
}
.people_card__title,
.people_card__org {
- font-size: clamp(0.6875rem, 1.1vw, 0.75rem);
+ font-size: clamp(0.625rem, 1vw, 0.6875rem);
+ margin-bottom: 0.25rem;
+ }
+
+ .people_card__org {
+ margin-bottom: 0.375rem;
}
.people_card__social {
- font-size: clamp(0.6875rem, 1vw, 0.75rem);
+ font-size: clamp(0.625rem, 0.95vw, 0.6875rem);
+ gap: 0.375rem;
i {
- font-size: clamp(0.75rem, 1.1vw, 0.875rem);
+ font-size: clamp(0.6875rem, 1vw, 0.75rem);
}
}
}
diff --git a/themes/clean-hugo/assets/css/_search.scss b/themes/clean-hugo/assets/css/_search.scss
index 5f337997..3819757a 100644
--- a/themes/clean-hugo/assets/css/_search.scss
+++ b/themes/clean-hugo/assets/css/_search.scss
@@ -11,23 +11,31 @@
height: 2.5rem;
margin-top: 1rem;
padding: 0;
- border: 1px solid var(--color-gray-200);
+ border: 1px solid color-mix(
+ in srgb,
+ var(--color-white) 35%,
+ transparent
+ );
border-radius: $radius-sm;
background-color: transparent;
- color: var(--color-gray-900);
+ color: var(--color-white);
cursor: pointer;
transition: background-color $transition-base,
color $transition-base;
&:hover {
- background-color: var(--color-gray-50);
- color: var(--color-link);
+ background-color: color-mix(
+ in srgb,
+ var(--color-white) 10%,
+ transparent
+ );
+ color: var(--color-link-on-dark);
}
&:focus {
outline: none;
- box-shadow: 0 0 0 2px var(--color-link),
- 0 0 0 4px var(--color-white);
+ box-shadow: 0 0 0 2px var(--color-link-on-dark),
+ 0 0 0 4px var(--color-brand-purple);
}
@media (min-width: $breakpoint-md) {
diff --git a/themes/clean-hugo/assets/css/_section-band.scss b/themes/clean-hugo/assets/css/_section-band.scss
new file mode 100644
index 00000000..f2924be8
--- /dev/null
+++ b/themes/clean-hugo/assets/css/_section-band.scss
@@ -0,0 +1,173 @@
+/**
+ * Section bands: full-width colored blocks with optional curved SVG dividers.
+ * color param: white | light-purple | soft-purple | green | brand-purple
+ */
+
+.section-band {
+ width: 100vw;
+ position: relative;
+ left: 50%;
+ margin: 0 -50vw;
+ padding: 0;
+}
+
+.section-band__content {
+ max-width: $container-max-width;
+ margin-inline: auto;
+ padding: $fluid-space-2xl $fluid-container-padding;
+}
+
+/* Color modifiers — backgrounds and divider wave fills from theme variables */
+.section-band--white {
+ background-color: var(--color-white);
+ --section-band-fill-1: var(--color-white);
+ --section-band-fill-2: var(--color-white);
+}
+
+.section-band--light-purple {
+ background-color: var(--color-light-purple);
+ --section-band-fill-1: var(--color-soft-purple);
+ --section-band-fill-2: var(--color-light-purple);
+}
+
+.section-band--soft-purple {
+ background-color: var(--color-soft-purple);
+ --section-band-fill-1: color-mix(
+ in srgb,
+ var(--color-brand-medium-purple) 65%,
+ var(--color-soft-purple)
+ );
+ --section-band-fill-2: var(--color-soft-purple);
+}
+
+.section-band--green {
+ background-color: var(--color-primary-light);
+ --section-band-fill-1: color-mix(
+ in srgb,
+ var(--color-primary) 40%,
+ var(--color-primary-light)
+ );
+ --section-band-fill-2: var(--color-primary-light);
+}
+
+.section-band--brand-purple {
+ background-color: var(--color-brand-purple);
+ --section-band-fill-1: var(--color-brand-medium-purple);
+ --section-band-fill-2: var(--color-brand-purple);
+}
+
+/* Divider fill when rendered outside the band (top/bottom partials) */
+.section-band__divider--white {
+ --section-band-fill-1: var(--color-white);
+ --section-band-fill-2: var(--color-white);
+}
+
+.section-band__divider--light-purple {
+ --section-band-fill-1: var(--color-soft-purple);
+ --section-band-fill-2: var(--color-light-purple);
+}
+
+.section-band__divider--soft-purple {
+ --section-band-fill-1: color-mix(
+ in srgb,
+ var(--color-brand-medium-purple) 65%,
+ var(--color-soft-purple)
+ );
+ --section-band-fill-2: var(--color-soft-purple);
+}
+
+.section-band__divider--green {
+ --section-band-fill-1: color-mix(
+ in srgb,
+ var(--color-primary) 40%,
+ var(--color-primary-light)
+ );
+ --section-band-fill-2: var(--color-primary-light);
+}
+
+.section-band__divider--brand-purple {
+ --section-band-fill-1: var(--color-brand-medium-purple);
+ --section-band-fill-2: var(--color-brand-purple);
+}
+
+.section-band__divider-wrap {
+ width: 100vw;
+ position: relative;
+ left: 50%;
+ margin-inline: -50vw;
+}
+
+.section-band--docs,
+.section-band__divider-wrap--docs {
+ width: 100%;
+ max-width: 100%;
+ inset-inline: auto;
+ margin-inline: 0;
+}
+
+.section-band__divider {
+ position: relative;
+ width: 100%;
+ line-height: 0;
+}
+
+.section-band__divider--bottom {
+ margin-top: 0;
+}
+
+.section-band__divider--top {
+ margin-bottom: 0;
+}
+
+.section-band__svg {
+ display: block;
+ width: 100%;
+ height: auto;
+}
+
+.section-band__wave--1 {
+ fill: var(--section-band-fill-1);
+ opacity: 0.9;
+}
+
+.section-band__wave--2 {
+ fill: var(--section-band-fill-2);
+}
+
+.section-band__divider--bottom .section-band__svg {
+ vertical-align: bottom;
+}
+
+.section-band__divider--top .section-band__svg {
+ vertical-align: top;
+}
+
+/* Nested shortcodes: stay contained inside the band (no viewport breakout) */
+.section-band__content {
+ > .impact-cards,
+ > .section-heading,
+ > .metrics-bar,
+ > .two-card-row,
+ > .three-card-row,
+ > .single-card-wide {
+ width: 100%;
+ position: static;
+ left: auto;
+ margin-inline: 0;
+ }
+
+ > .impact-cards {
+ padding-top: 0;
+ padding-inline: 0;
+
+ .impact-cards__container {
+ max-width: 100%;
+ padding-inline: 0;
+ }
+ }
+
+ > .section-heading.container {
+ max-width: 100%;
+ padding-inline: 0;
+ }
+}
diff --git a/themes/clean-hugo/assets/css/_shortcodes.scss b/themes/clean-hugo/assets/css/_shortcodes.scss
index 23ca9f30..4202d20d 100644
--- a/themes/clean-hugo/assets/css/_shortcodes.scss
+++ b/themes/clean-hugo/assets/css/_shortcodes.scss
@@ -40,6 +40,24 @@
}
}
+ /* Metrics + dark band flush above site footer */
+ .splash-page__content > .metrics-bar + .impact-destinations {
+ margin-top: 0;
+ }
+
+ .splash-page__content > .metrics-bar:has(+ .impact-destinations) {
+ margin-bottom: 0;
+ }
+
+ .splash-page__content > .impact-destinations:last-child {
+ margin-bottom: 0;
+ }
+
+ .page-content:has(.splash-page__content > .impact-destinations:last-child)
+ .site-footer {
+ margin-top: 0;
+ }
+
/* Two Card Row */
.two-card-row {
padding: 1.5rem 0;
@@ -264,4 +282,26 @@
@include responsive(font-size, 1.125rem, 1.25rem);
opacity: 0.8;
color: var(--color-gray-600);
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .section-heading--left {
+ text-align: left;
+
+ .section-heading__subtitle {
+ text-align: left;
+ max-width: 42rem;
+ }
+ }
+
+ .section-heading__eyebrow {
+ margin: 0 0 0.5rem;
+ font-size: 0.8125rem;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--color-primary);
}
diff --git a/themes/clean-hugo/assets/css/_swoosh.scss b/themes/clean-hugo/assets/css/_swoosh.scss
deleted file mode 100644
index c50488a9..00000000
--- a/themes/clean-hugo/assets/css/_swoosh.scss
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Swoosh sections: full-width blocks with optional curved SVG dividers.
- * Section is always full viewport width; content is constrained in __content.
- */
-
-/* Full-bleed: section escapes any parent max-width so it's always full width */
-.swoosh-section {
- width: 100vw;
- position: relative;
- left: 50%;
- margin: 0 -50vw;
- padding: 0;
-}
-
-.swoosh-section__content {
- max-width: 80rem; /* 1280px */
- margin-inline: auto;
- padding: $fluid-space-2xl $fluid-container-padding;
-}
-
-/* Background modifiers */
-.swoosh-section--white {
- background-color: var(--color-white, #fff);
-}
-
-.swoosh-section--purple {
- background-color: var(--swoosh-bg-purple, #e1dfed);
- --swoosh-fill-1: #bdb6d6;
- --swoosh-fill-2: #e2dfed;
-}
-
-.swoosh-section--green {
- background-color: var(--swoosh-bg-green, #d4ebe3);
- --swoosh-fill-1: #4e7064; /* darker green highlight (a bit lighter than black-mix) */
- --swoosh-fill-2: #d4ebe3; /* same light green as section, blends */
-}
-
-/* Divider theme: fill vars when divider is outside section (top/bottom partial) */
-.swoosh-section__divider--purple {
- --swoosh-fill-1: #bdb6d6;
- --swoosh-fill-2: #e2dfed;
-}
-
-.swoosh-section__divider--green {
- --swoosh-fill-1: #4e7064; /* darker green highlight (a bit lighter than black-mix) */
- --swoosh-fill-2: #d4ebe3; /* same light green as section, blends */
-}
-
-.swoosh-section__divider--white {
- --swoosh-fill-1: var(--color-white, #fff);
- --swoosh-fill-2: var(--color-white, #fff);
-}
-
-/* Wrapper for standalone divider (above/below section): full width like section */
-.swoosh-section__divider-wrap {
- width: 100vw;
- position: relative;
- left: 50%;
- margin-inline: -50vw;
-}
-
-/* Contained width (layout="docs"): fill content column only, no sidebar bleed */
-.swoosh-section--docs,
-.swoosh-section__divider-wrap--docs {
- width: 100%;
- max-width: 100%;
- inset-inline: auto;
- margin-inline: 0;
-}
-
-/* Divider: full width, contains SVG wave */
-.swoosh-section__divider {
- position: relative;
- width: 100%;
- line-height: 0;
-}
-
-.swoosh-section__divider--bottom {
- margin-top: 0;
-}
-
-.swoosh-section__divider--top {
- margin-bottom: 0;
-}
-
-.swoosh-section__svg {
- display: block;
- width: 100%;
- height: auto;
-}
-
-/* Wave paths: fill from CSS variables (set by section modifier) */
-.swoosh-section__wave--1 {
- fill: var(--swoosh-fill-1);
- opacity: 0.9;
-}
-
-.swoosh-section__wave--2 {
- fill: var(--swoosh-fill-2);
-}
-
-/* Bottom divider: SVG sits at bottom of its wrapper */
-.swoosh-section__divider--bottom .swoosh-section__svg {
- vertical-align: bottom;
-}
-
-.swoosh-section__divider--top .swoosh-section__svg {
- vertical-align: top;
-}
diff --git a/themes/clean-hugo/assets/css/main.scss b/themes/clean-hugo/assets/css/main.scss
index 27ac00bc..f88e6a38 100644
--- a/themes/clean-hugo/assets/css/main.scss
+++ b/themes/clean-hugo/assets/css/main.scss
@@ -114,6 +114,24 @@ xl: 1280px
from site configuration (hugo.toml or theme.toml defaults)
*/
+ :root {
+ --color-surface-dark-purple: color-mix(
+ in srgb,
+ var(--color-brand-purple) 85%,
+ black
+ );
+ --color-surface-dark-purple-card: color-mix(
+ in srgb,
+ var(--color-brand-purple) 68%,
+ black
+ );
+ --color-surface-footer-purple: color-mix(
+ in srgb,
+ var(--color-brand-purple) 92%,
+ black
+ );
+ }
+
body {
font-family: $font-family-body;
color: var(--color-gray-900);
@@ -276,9 +294,10 @@ xl: 1280px
@import 'cards';
@import 'feature-row';
@import 'intro-section';
+@import 'impact-destinations';
@import 'training-feature';
@import 'home-bands';
-@import 'swoosh';
+@import 'section-band';
@import 'footer';
@import 'shortcodes';
@import 'admonition';
diff --git a/themes/clean-hugo/layouts/_default/packages-list.html b/themes/clean-hugo/layouts/_default/packages-list.html
index c7cd1d76..c27d0485 100644
--- a/themes/clean-hugo/layouts/_default/packages-list.html
+++ b/themes/clean-hugo/layouts/_default/packages-list.html
@@ -1,16 +1,10 @@
{{ define "main" }}
-{{ $pkgStats := partial "calculate-package-stats.html" . }}
-
{{ partial "hero.html" . }}
-{{ $stats := slice
- (printf "**%d** Packages Accepted" $pkgStats.total)
- (printf "**%d** Published in JOSS" $pkgStats.joss)
- (printf "**%d** Currently Active" $pkgStats.active)
- (printf "**%d** Unique Maintainers" $pkgStats.maintainers)
-}}
-{{ partial "metrics-bar-render.html" (dict "stats" $stats) }}
+{{ $scratch := newScratch }}
+{{ partial "metrics/build-stats.html" (dict "context" . "set" "packages" "scratch" $scratch) }}
+{{ partial "metrics-bar-render.html" (dict "stats" ($scratch.Get "stats")) }}
diff --git a/themes/clean-hugo/layouts/partials/cards.html b/themes/clean-hugo/layouts/partials/cards.html
index 8177424e..d9c19c8b 100644
--- a/themes/clean-hugo/layouts/partials/cards.html
+++ b/themes/clean-hugo/layouts/partials/cards.html
@@ -3,9 +3,15 @@
- Use text_only: true in front matter to render a plain white, non-interactive text card.
- Otherwise renders the interactive highlight card (supports Font Awesome class strings or raw HTML icons).
- Optional image (map: src, alt) renders a 16:9 strip at the top; optional event_type shows the same pill as event cards.
+ - Optional label + label_style (accent | team | stipend | default) for a top-right pill.
+ - modifier card--stipend: light green background for stipended roles.
+ - Optional tags (list) for secondary grey pills below the title.
-->
{{ $icon := .icon }}
+{{ $label := .label }}
+{{ $labelStyle := .label_style | default "default" }}
+{{ $tags := .tags }}
{{ $textOnly := false }}
{{ if reflect.IsMap . }}
{{ if .text_only }}
@@ -47,13 +53,25 @@
{{ if and $typeLabel (ne $kind "") }}
{{ $typeLabel }}
{{ end }}
- {{ if and $icon (in (printf "%s" $icon) "fa-") }}
-
- {{ else if and $icon (ne (printf "%s" $icon) "") }}
-
{{ $icon | safeHTML }}
- {{ end }} {{ .title }}
+
+ {{ with $tags }}
+
+ {{ end }}
{{ with .excerpt }}
- {{ . | markdownify }}
+ {{ . | markdownify }}
{{ end }}
diff --git a/themes/clean-hugo/layouts/partials/impact-cards.html b/themes/clean-hugo/layouts/partials/impact-cards.html
index b21bae27..27f22cd7 100644
--- a/themes/clean-hugo/layouts/partials/impact-cards.html
+++ b/themes/clean-hugo/layouts/partials/impact-cards.html
@@ -1,15 +1,55 @@
-
-{{ $wide := eq (lower (default "" .Params.cards_layout)) "wide" }}
-
-
- {{ $cards := .Params.cards }}
- {{ if $cards }}
- {{ range $i, $c := $cards }}
- {{ partial "cards.html" (merge $c (dict "page" $)) }}
+ Optional section param: reads {section}_cards (e.g. translation_cards).
+ Optional: cards_layout / {section}_cards_layout: wide.
+ Optional: cards_align / {section}_cards_align: left | center | right (default: center).
+ Shortcode align param overrides page alignment. -->
+
+{{ $page := . }}
+{{ $section := "" }}
+{{ $align := "" }}
+{{ if and (reflect.IsMap .) (isset . "page") }}
+ {{ $page = .page }}
+ {{ $align = .align | default "" }}
+ {{ $section = .section | default "" }}
+{{ end }}
+
+{{ $cardsKey := "cards" }}
+{{ $alignKey := "cards_align" }}
+{{ $layoutKey := "cards_layout" }}
+{{ if ne $section "" }}
+ {{ $cardsKey = printf "%s_cards" $section }}
+ {{ $alignKey = printf "%s_cards_align" $section }}
+ {{ $layoutKey = printf "%s_cards_layout" $section }}
+{{ end }}
+
+{{ if eq $align "" }}
+ {{ $align = index $page.Params $alignKey | default ($page.Params.cards_align | default "center") }}
+{{ end }}
+{{ $align = lower $align }}
+{{ if not (in (slice "left" "center" "right") $align) }}
+ {{ $align = "center" }}
+{{ end }}
+
+{{ $layout := "" }}
+{{ if ne $section "" }}
+ {{ $layout = index $page.Params $layoutKey | default "" }}
+{{ else }}
+ {{ $layout = index $page.Params "cards_layout" | default "" }}
+{{ end }}
+{{ $layoutLower := lower $layout }}
+{{ $wide := eq $layoutLower "wide" }}
+{{ $five := eq $layoutLower "five" }}
+
+
+
+ {{ $cards := index $page.Params $cardsKey }}
+ {{ if $cards }}
+ {{ range $i, $c := $cards }}
+ {{ partial "cards.html" (merge $c (dict "page" $page)) }}
+ {{ end }}
{{ end }}
- {{ end }}
+
diff --git a/themes/clean-hugo/layouts/partials/impact-destinations.html b/themes/clean-hugo/layouts/partials/impact-destinations.html
new file mode 100644
index 00000000..0fa8b0b3
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/impact-destinations.html
@@ -0,0 +1,69 @@
+
+
+{{ $page := . }}
+{{ $section := "" }}
+{{ if and (reflect.IsMap .) (isset . "page") }}
+ {{ $page = .page }}
+ {{ $section = .section | default "" }}
+{{ end }}
+
+{{ $dataKey := "impact_destinations" }}
+{{ if ne $section "" }}
+ {{ $dataKey = printf "%s_impact_destinations" $section }}
+{{ end }}
+
+{{ $data := index $page.Params $dataKey }}
+{{ if $data }}
+
+
+
+
+ {{ with $data.items }}
+
+ {{ end }}
+
+
+{{ end }}
diff --git a/themes/clean-hugo/layouts/partials/metrics-bar-render.html b/themes/clean-hugo/layouts/partials/metrics-bar-render.html
index 33bbcb7f..d8765405 100644
--- a/themes/clean-hugo/layouts/partials/metrics-bar-render.html
+++ b/themes/clean-hugo/layouts/partials/metrics-bar-render.html
@@ -1,7 +1,10 @@
{{/*
Metrics Bar Renderer
A partial that renders the metrics bar HTML structure within a layout
- Usage: {{ partial "metrics-bar-render.html" (dict "stats" (slice "**5** Packages" "**10** Users")) }}
+ Usage:
+ {{ $scratch := newScratch }}
+ {{ partial "metrics/build-stats.html" (dict "context" . "set" "volunteer" "scratch" $scratch) }}
+ {{ partial "metrics-bar-render.html" (dict "stats" ($scratch.Get "stats")) }}
*/}}
{{ $stats := .stats | default slice }}
diff --git a/themes/clean-hugo/layouts/partials/metrics/build-stats.html b/themes/clean-hugo/layouts/partials/metrics/build-stats.html
new file mode 100644
index 00000000..382746fc
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/metrics/build-stats.html
@@ -0,0 +1,21 @@
+{{/*
+ Build a metrics-bar stats slice from a named preset or explicit values.
+ Sets "stats" on the passed scratch map.
+
+ Usage:
+ {{ $scratch := newScratch }}
+ {{ partial "metrics/build-stats.html" (dict
+ "context" .Page
+ "set" "volunteer"
+ "scratch" $scratch
+ ) }}
+ {{ $stats := $scratch.Get "stats" }}
+*/}}
+{{ $set := .set | default "" }}
+{{ if eq $set "volunteer" }}
+ {{ partial "metrics/presets/volunteer.html" . }}
+{{ else if eq $set "packages" }}
+ {{ partial "metrics/presets/packages.html" . }}
+{{ else if .stats }}
+ {{ .scratch.Set "stats" .stats }}
+{{ end }}
diff --git a/themes/clean-hugo/layouts/partials/metrics/presets/packages.html b/themes/clean-hugo/layouts/partials/metrics/presets/packages.html
new file mode 100644
index 00000000..d09df7a6
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/metrics/presets/packages.html
@@ -0,0 +1,12 @@
+{{/*
+ Python packages catalog metrics — live counts from packages data.
+*/}}
+{{ $ctx := .context }}
+{{ $scratch := .scratch }}
+{{ $pkgStats := partial "calculate-package-stats.html" $ctx }}
+{{ $scratch.Set "stats" (slice
+ (printf "**%d** Packages Accepted" $pkgStats.total)
+ (printf "**%d** Published in JOSS" $pkgStats.joss)
+ (printf "**%d** Currently Active" $pkgStats.active)
+ (printf "**%d** Unique Maintainers" $pkgStats.maintainers)
+) }}
diff --git a/themes/clean-hugo/layouts/partials/metrics/presets/volunteer.html b/themes/clean-hugo/layouts/partials/metrics/presets/volunteer.html
new file mode 100644
index 00000000..355f8b62
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/metrics/presets/volunteer.html
@@ -0,0 +1,13 @@
+{{/*
+ Volunteer page metrics — live counts from site data.
+*/}}
+{{ $ctx := .context }}
+{{ $scratch := .scratch }}
+{{ $pkgStats := partial "calculate-package-stats.html" $ctx }}
+{{ $contributors := len ($ctx.Site.Data.contributors | default slice) }}
+{{ $scratch.Set "stats" (slice
+ (printf "**%d** contributors" $contributors)
+ (printf "**%d** maintainers" $pkgStats.maintainers)
+ (printf "**%d** packages accepted" $pkgStats.total)
+ "**3** languages"
+) }}
diff --git a/themes/clean-hugo/layouts/partials/section-band-divider-bottom.html b/themes/clean-hugo/layouts/partials/section-band-divider-bottom.html
new file mode 100644
index 00000000..cc86c54c
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/section-band-divider-bottom.html
@@ -0,0 +1,20 @@
+{{/* Curved divider at bottom of a section band (wave into next section). */}}
+{{ $color := .color | default "light-purple" }}
+{{ if eq $color "purple" }}{{ $color = "light-purple" }}{{ end }}
+
diff --git a/themes/clean-hugo/layouts/partials/section-band-divider-top.html b/themes/clean-hugo/layouts/partials/section-band-divider-top.html
new file mode 100644
index 00000000..04e851c8
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/section-band-divider-top.html
@@ -0,0 +1,19 @@
+{{/* Curved divider at top of a section band (wave down from previous section). */}}
+{{ $color := .color | default "light-purple" }}
+{{ if eq $color "purple" }}{{ $color = "light-purple" }}{{ end }}
+
diff --git a/themes/clean-hugo/layouts/partials/section-band-inner-content.html b/themes/clean-hugo/layouts/partials/section-band-inner-content.html
new file mode 100644
index 00000000..e32e4106
--- /dev/null
+++ b/themes/clean-hugo/layouts/partials/section-band-inner-content.html
@@ -0,0 +1,45 @@
+{{/*
+ Render section band inner content.
+
+ {{< section_band >}} with nested shortcodes only: Hugo pre-renders them to HTML;
+ pass through with safeHTML.
+
+ Unprocessed shortcode markers: RenderString once to execute them.
+
+ Mixed markdown + shortcode HTML (e.g. ways-to-give): split blocks so markdown
+ renders without re-processing HTML into
fences.
+
+ {{% section_band %}} — markdown prose only; do not nest {{< shortcodes >}}.
+*/}}
+{{- $inner := replace .inner "\r\n" "\n" -}}
+{{- $page := .page -}}
+{{- $trimmed := trim $inner " \n\r\t" -}}
+{{- $m := "SECTIONBANDSPLIT" -}}
+{{- $hasShortcodes := findRE `\{\{[%<]` $inner -}}
+
+{{- if $hasShortcodes -}}
+ {{ $inner | $page.RenderString }}
+{{- else if hasPrefix $trimmed "<" -}}
+ {{ $inner | safeHTML }}
+{{- else -}}
+ {{- $inner = replaceRE `\n\n+(<(?:div|section|figure|a |article))` "\n\nSECTIONBANDSPLIT$1" $inner -}}
+ {{- $inner = replaceRE `(?s)(
)\s*\n\n+(### )` "$1\n\nSECTIONBANDSPLIT$2" $inner -}}
+ {{- $inner = replaceRE `(?s)( )\s*\n\n+(## )` "$1\n\nSECTIONBANDSPLIT$2" $inner -}}
+ {{- $inner = replaceRE `(?s)(
)\s*\n\n+(\| )` "$1\n\nSECTIONBANDSPLIT$2" $inner -}}
+ {{- $inner = replaceRE `(?s)(