diff --git a/astro.config.mjs b/astro.config.mjs
index 42c40982..e4e9333a 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -20,18 +20,49 @@ export default defineConfig({
// Same-page and relative links are fine; only flag links whose
// target page doesn't exist.
errorOnRelativeLinks: false,
+ // Not all pages are translated; English routes intentionally fall
+ // back to the Dutch content, so links to them are valid.
+ errorOnFallbackPages: false,
}),
],
title: {
nl: "RoQua Documentatie",
en: "RoQua Documentation",
},
+ // The top bar is always dark, so the white wordmark works in both themes.
logo: {
- src: "./src/assets/roqua-q.png",
- alt: "RoQua Logo",
+ src: "./src/assets/wordmark-white.svg",
+ alt: "RoQua",
+ replacesTitle: true,
},
favicon: "/img/favicon.ico",
+ components: {
+ Header: "./src/components/Header.astro",
+ Hero: "./src/components/Hero.astro",
+ PageTitle: "./src/components/PageTitle.astro",
+ },
+
+ // Code blocks are dark in both themes, per the design; the exact navy
+ // differs per scheme, so the values live in CSS variables in custom.css.
+ expressiveCode: {
+ themes: ["github-dark"],
+ styleOverrides: {
+ borderRadius: "0.5rem",
+ borderColor: "var(--sl-color-hairline)",
+ codeBackground: "var(--rq-code-bg)",
+ frames: {
+ editorTabBarBackground: "var(--rq-code-tabbar-bg)",
+ editorActiveTabBackground: "var(--rq-code-bg)",
+ editorActiveTabIndicatorTopColor: "transparent",
+ editorActiveTabIndicatorBottomColor: "var(--rq-topbar-active)",
+ terminalBackground: "var(--rq-code-bg)",
+ terminalTitlebarBackground: "var(--rq-code-tabbar-bg)",
+ terminalTitlebarBorderBottomColor: "rgb(255 255 255 / 0.08)",
+ },
+ },
+ },
+
defaultLocale: "root",
locales: {
root: { label: "Nederlands", lang: "nl" },
@@ -42,7 +73,11 @@ export default defineConfig({
baseUrl: "https://github.com/roqua/documentation/edit/master/",
},
- customCss: ["./src/styles/custom.css"],
+ customCss: [
+ "@fontsource-variable/roboto/standard.css",
+ "@fontsource-variable/roboto/standard-italic.css",
+ "./src/styles/custom.css",
+ ],
// Recovers the Docusaurus category labels for autogenerated groups.
routeMiddleware: "./src/starlightRouteData.ts",
diff --git a/package-lock.json b/package-lock.json
index 1b1a8f39..45ccc37f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@astrojs/starlight": "^0.41.4",
+ "@fontsource-variable/roboto": "^5.3.0",
"astro": "^7.1.3",
"sharp": "^0.34.5"
},
@@ -1281,6 +1282,15 @@
"@expressive-code/core": "^0.44.1"
}
},
+ "node_modules/@fontsource-variable/roboto": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@fontsource-variable/roboto/-/roboto-5.3.0.tgz",
+ "integrity": "sha512-BoaaZiQf8fgtxv07KXkTwx1bKz/EoqIlTXq2OOKKXINxp6qXWdnlChqV+hSaDANS49yyUASFl3OVBMMDTCIHFA==",
+ "license": "OFL-1.1",
+ "funding": {
+ "url": "https://github.com/sponsors/ayuhito"
+ }
+ },
"node_modules/@img/colour": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
diff --git a/package.json b/package.json
index 699b218c..fa4ff8bd 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
},
"dependencies": {
"@astrojs/starlight": "^0.41.4",
+ "@fontsource-variable/roboto": "^5.3.0",
"astro": "^7.1.3",
"sharp": "^0.34.5"
},
diff --git a/src/assets/wordmark-white.svg b/src/assets/wordmark-white.svg
new file mode 100644
index 00000000..ef3774e2
--- /dev/null
+++ b/src/assets/wordmark-white.svg
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/wordmark.svg b/src/assets/wordmark.svg
new file mode 100644
index 00000000..e33d6f67
--- /dev/null
+++ b/src/assets/wordmark.svg
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Header.astro b/src/components/Header.astro
new file mode 100644
index 00000000..28db4ed1
--- /dev/null
+++ b/src/components/Header.astro
@@ -0,0 +1,113 @@
+---
+import LanguageSelect from "@astrojs/starlight/components/LanguageSelect.astro";
+import Search from "@astrojs/starlight/components/Search.astro";
+import SiteTitle from "@astrojs/starlight/components/SiteTitle.astro";
+import ThemeSelect from "@astrojs/starlight/components/ThemeSelect.astro";
+
+/**
+ * The dark top bar from the "Ronde 1" design: wordmark, the four documentation
+ * areas, search, and the theme/language pickers. The active area gets a blue
+ * underline across the full bar height.
+ */
+
+const { locale } = Astro.locals.starlightRoute;
+const base = locale ? `/${locale}` : "";
+const isEnglish = locale === "en";
+
+const sections = [
+ { label: isEnglish ? "Manual" : "Handleiding", href: `${base}/docs/`, prefixes: [`${base}/docs/`] },
+ { label: isEnglish ? "Admin" : "Beheer", href: `${base}/docs/admin/`, prefixes: [`${base}/docs/admin/`] },
+ { label: "API", href: `${base}/technical/`, prefixes: [`${base}/technical/`] },
+ {
+ label: isEnglish ? "Data export" : "Data-export",
+ href: `${base}/technical/csv_export/`,
+ prefixes: [`${base}/technical/csv_export/`, `${base}/technical/sqlite_export/`],
+ },
+];
+
+// The most specific matching prefix wins, so /technical/csv_export/ highlights
+// "Data-export" rather than "API".
+const path = Astro.url.pathname.endsWith("/") ? Astro.url.pathname : `${Astro.url.pathname}/`;
+const active = sections.reduce(
+ (best, section) => {
+ const match = section.prefixes.find((prefix) => path.startsWith(prefix));
+ return match && match.length > best.length ? { length: match.length, section } : best;
+ },
+ { length: 0, section: undefined as (typeof sections)[number] | undefined },
+).section;
+
+---
+
+
+
+
diff --git a/src/components/Hero.astro b/src/components/Hero.astro
new file mode 100644
index 00000000..02352778
--- /dev/null
+++ b/src/components/Hero.astro
@@ -0,0 +1,159 @@
+---
+/**
+ * The blue search hero from the "Ronde 1" design, shown on pages with `hero`
+ * frontmatter (the two landing pages). The search box and the quick chips are
+ * the primary way in; the box opens Starlight's search dialog.
+ */
+
+const { entry, locale } = Astro.locals.starlightRoute;
+const { title = entry.data.title, tagline } = entry.data.hero ?? {};
+
+const base = locale ? `/${locale}` : "";
+const isEnglish = locale === "en";
+
+const placeholder = isEnglish
+ ? "Search for a page, field name or endpoint"
+ : "Zoek een pagina, veldnaam of endpoint";
+
+const chips = [
+ {
+ label: isEnglish ? "preparing questionnaires" : "vragenlijst klaarzetten",
+ href: `${base}/docs/epd/measure/measurements/klaarzetten/`,
+ },
+ { label: isEnglish ? "API token" : "API-token", href: `${base}/docs/admin/integration/api_tokens/` },
+ { label: isEnglish ? "non-response" : "non-respons", href: `${base}/docs/epd/measure/measurements/non-respons/` },
+ { label: isEnglish ? "CSV field names" : "CSV-veldnamen", href: `${base}/technical/csv_export/` },
+];
+---
+
+
+
+
+ {tagline &&
}
+
+
+
+
+
+ {placeholder}
+ Ctrl K
+
+
+
+
+
+
+
+
diff --git a/src/components/PageTitle.astro b/src/components/PageTitle.astro
new file mode 100644
index 00000000..1e0fad4c
--- /dev/null
+++ b/src/components/PageTitle.astro
@@ -0,0 +1,64 @@
+---
+/**
+ * Adds the "not yet fully verified" badge from the "Ronde 1" design under the
+ * title of pages whose frontmatter carries `status: draft`.
+ */
+
+const { entry, locale } = Astro.locals.starlightRoute;
+const isDraft = entry.data.status === "draft";
+const label = locale === "en" ? "Not yet fully verified" : "Nog niet volledig geverifieerd";
+---
+
+{/* "_top" is Starlight's PAGE_TITLE_ID, which its package doesn't export. */}
+{entry.data.title}
+{
+ isDraft && (
+
+
+
+
+
+ {label}
+
+ )
+}
+
+
diff --git a/src/content/docs/en/index.mdx b/src/content/docs/en/index.mdx
index 67106697..58fa8c6b 100644
--- a/src/content/docs/en/index.mdx
+++ b/src/content/docs/en/index.mdx
@@ -6,15 +6,41 @@ head:
- tag: title
content: RoQua Documentation
hero:
- tagline: >-
- This site contains the user documentation for RoQua, as well as the technical,
- integration-partner documentation.
- actions:
- - text: User Manual
- link: /en/docs/
- icon: right-arrow
- - text: Technical documentation
- link: /en/technical/
- icon: right-arrow
- variant: minimal
+ title: What can we help you with?
+ tagline: User manual, admin documentation and technical integration documentation for RoQua.
---
+
+
+
+
+
I work with clients
+
Preparing questionnaires, viewing outcomes, reading the timeline.
+
Go to the manual
+
+
+
+
I administer RoQua
+
Configuring protocols and measurements, managing users and roles.
+
Go to admin
+
+
+
+
I'm building an integration
+
REST API, HL7, FHIR and the CSV and SQLite export formats.
+
Go to the API docs
+
+
+
+
+
+
New in RoQua
+
Respondents can now start certain questionnaires themselves, as often as they like — without anything having to be prepared for them.
+
+
Read about self-initiation
+
diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx
index 1ab267c5..f0c39fca 100644
--- a/src/content/docs/index.mdx
+++ b/src/content/docs/index.mdx
@@ -8,19 +8,41 @@ head:
- tag: title
content: RoQua Documentatie
hero:
- tagline: >-
- Op deze site vindt u de gebruikershandleiding voor RoQua. De handleiding is
- opgesplitst in twee delen: de EPD-interface waarop u als medewerker inlogt vanuit
- uw EPD-software, en de Admin-interface waar coördinatoren, managers en
- applicatiebeheer de configuratie van RoQua kunnen beheren. Naast deze handleiding
- voor eindgebruikers hebben we ook API-documentatie voor ontwikkelaars waarin staat
- beschreven hoe u uw software met RoQua kunt integreren.
- actions:
- - text: Gebruikershandleiding
- link: /docs/
- icon: right-arrow
- - text: Technische handleiding
- link: /technical/
- icon: right-arrow
- variant: minimal
+ title: Waar kunnen we je mee helpen?
+ tagline: Handleiding, beheerdocumentatie en technische koppelingsdocumentatie voor RoQua.
---
+
+
+
+
+
Ik werk met cliënten
+
Vragenlijsten klaarzetten, uitkomsten bekijken, tijdlijn lezen.
+
Naar de handleiding
+
+
+
+
Ik beheer RoQua
+
Protocollen en metingen inrichten, gebruikers en rollen beheren.
+
Naar beheer
+
+
+
+
Ik bouw een koppeling
+
REST API, HL7, FHIR en de export-formaten CSV en SQLite.
+
Naar de API-docs
+
+
+
+
+
+
Nieuw in RoQua
+
Respondenten kunnen bepaalde vragenlijsten nu zelf opstarten, zo vaak als ze willen — zonder dat er iets klaargezet hoeft te worden.
+
+
Lees over eigen initiatief
+
diff --git a/src/styles/custom.css b/src/styles/custom.css
index 1d8b5770..9b2ad8eb 100644
--- a/src/styles/custom.css
+++ b/src/styles/custom.css
@@ -1,16 +1,379 @@
/*
- * Site accent colour, carried over from the Docusaurus theme, which set
- * --ifm-color-primary to oklch(0.454 0.135 254) in light mode and
- * oklch(0.68 0.16 254) in dark mode. Starlight's `:root` holds the dark theme.
+ * "Ronde 1" theme: blue accent carried over from the Docusaurus site, a dark
+ * top bar on every page, and the grey/blue token set from the design.
+ */
+
+/*
+ * Starlight's `:root` holds the dark theme. The dark palette is "Ronde 2" of
+ * the design: a blue-grey base on the same blue hue, primary blue lifted to
+ * oklch(0.74 0.13 254) for contrast on dark, and the hero gradient deepened
+ * rather than lightened.
*/
:root {
- --sl-color-accent-low: oklch(0.3 0.07 254);
- --sl-color-accent: oklch(0.68 0.16 254);
- --sl-color-accent-high: oklch(0.86 0.07 254);
+ --sl-font: "Roboto Variable", system-ui, sans-serif;
+
+ --sl-color-accent-low: oklch(0.36 0.12 256);
+ --sl-color-accent: oklch(0.46 0.14 254);
+ --sl-color-accent-high: oklch(0.74 0.13 254);
+
+ --sl-color-white: oklch(0.94 0.008 250);
+ --sl-color-gray-1: oklch(0.88 0.012 251);
+ --sl-color-gray-2: oklch(0.8 0.015 253);
+ --sl-color-gray-3: oklch(0.7 0.018 255);
+ --sl-color-gray-4: oklch(0.5 0.024 259);
+ --sl-color-gray-5: oklch(0.34 0.028 262);
+ --sl-color-gray-6: oklch(0.195 0.024 262);
+ --sl-color-gray-7: oklch(0.155 0.022 262);
+ --sl-color-black: oklch(0.245 0.026 262);
+
+ /* Design tokens that differ per scheme but not along Starlight's gray axis. */
+ --rq-border: oklch(0.34 0.028 262);
+ --rq-topbar-bg: oklch(0.155 0.022 262);
+ /* Accent for underlines on dark surfaces (top bar, code frame tabs). */
+ --rq-topbar-active: oklch(0.46 0.14 254);
+ --rq-hero-gradient: linear-gradient(180deg, oklch(0.46 0.14 254), oklch(0.36 0.12 256));
+ --rq-code-bg: oklch(0.155 0.022 262);
+ --rq-code-tabbar-bg: oklch(0.205 0.024 262);
+
+ /* One border colour everywhere, per the design. */
+ --sl-color-hairline-light: var(--rq-border);
+ --sl-color-hairline: var(--rq-border);
+ /* Inline code chips use the recessed surface in both schemes. */
+ --sl-color-bg-inline-code: var(--sl-color-gray-6);
}
:root[data-theme="light"] {
--sl-color-accent-low: oklch(0.92 0.04 254);
--sl-color-accent: oklch(0.454 0.135 254);
--sl-color-accent-high: oklch(0.32 0.1 254);
+
+ --sl-color-white: oklch(0.21 0.04 266);
+ --sl-color-gray-1: oklch(0.28 0.035 266);
+ --sl-color-gray-2: oklch(0.35 0.03 266);
+ --sl-color-gray-3: hsl(215.4 16.3% 46.9%);
+ --sl-color-gray-4: hsl(215.4 16.3% 66%);
+ --sl-color-gray-5: oklch(0.9 0.013 255.5);
+ --sl-color-gray-6: oklch(0.95 0.0034 247.86);
+ --sl-color-gray-7: oklch(0.97 0.003 247.86);
+ --sl-color-black: #ffffff;
+
+ --rq-border: oklch(0.9 0.013 255.5);
+ --rq-topbar-bg: oklch(0.21 0.04 266);
+ --rq-topbar-active: oklch(0.68 0.16 254);
+ --rq-hero-gradient: linear-gradient(180deg, oklch(0.68 0.16 254), oklch(0.6 0.17 254));
+ --rq-code-bg: oklch(0.21 0.04 266);
+ --rq-code-tabbar-bg: oklch(0.26 0.035 266);
+}
+
+/* ---------------------------------------------------------------- top bar */
+
+/*
+ * The top bar is dark in both color schemes. Remapping the palette variables
+ * inside the header makes Starlight's own search button and pickers adopt the
+ * right on-dark colors without overriding their components.
+ */
+header.header {
+ --sl-color-bg-nav: var(--rq-topbar-bg);
+ --sl-color-white: #ffffff;
+ --sl-color-gray-1: rgb(255 255 255 / 0.85);
+ --sl-color-gray-2: rgb(255 255 255 / 0.72);
+ --sl-color-gray-3: rgb(255 255 255 / 0.6);
+ --sl-color-gray-4: rgb(255 255 255 / 0.45);
+ --sl-color-gray-5: rgb(255 255 255 / 0.25);
+ --sl-color-gray-6: rgb(255 255 255 / 0.1);
+ --sl-color-gray-7: rgb(255 255 255 / 0.06);
+ --sl-color-black: var(--rq-topbar-bg);
+ --sl-color-text-accent: #ffffff;
+ --sl-color-hairline-shade: transparent;
+
+ border-bottom: 1px solid transparent;
+}
+
+header.header site-search button {
+ background: rgb(255 255 255 / 0.1);
+ border-color: rgb(255 255 255 / 0.16);
+ color: rgb(255 255 255 / 0.6);
+}
+header.header site-search button:hover {
+ border-color: rgb(255 255 255 / 0.4);
+ color: #fff;
+}
+header.header site-search kbd {
+ background: transparent;
+ border: 1px solid rgb(255 255 255 / 0.25);
+}
+
+/* ---------------------------------------------------------------- sidebar */
+
+:root {
+ --sl-color-bg-sidebar: var(--sl-color-bg);
+}
+
+@media (min-width: 50rem) {
+ .sidebar-pane {
+ border-inline-end: 1px solid var(--sl-color-hairline);
+ }
+}
+
+/* Group labels as small uppercase headings. */
+#starlight__sidebar .group-label > span {
+ font-size: var(--sl-text-xs);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ color: var(--sl-color-gray-3);
+}
+
+/* The active page: no pill, just a blue edge on the list's guide line. */
+#starlight__sidebar a[aria-current="page"],
+#starlight__sidebar a[aria-current="page"]:hover,
+#starlight__sidebar a[aria-current="page"]:focus {
+ background: transparent;
+ color: var(--sl-color-text-accent);
+ font-weight: 500;
+ /* A straight edge: without this the shadow follows the link's rounded corners. */
+ border-radius: 0;
+ box-shadow: inset 3px 0 0 0 var(--sl-color-text-accent);
+}
+[dir="rtl"] #starlight__sidebar a[aria-current="page"],
+[dir="rtl"] #starlight__sidebar a[aria-current="page"]:hover,
+[dir="rtl"] #starlight__sidebar a[aria-current="page"]:focus {
+ box-shadow: inset -3px 0 0 0 var(--sl-color-text-accent);
+}
+
+/* ---------------------------------------------------- table of contents */
+
+.right-sidebar {
+ border-inline-start: 1px solid var(--sl-color-hairline);
+}
+
+.right-sidebar starlight-toc h2 {
+ font-size: var(--sl-text-xs);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ color: var(--sl-color-gray-3);
+}
+
+.right-sidebar starlight-toc a {
+ border-inline-start: 2px solid var(--sl-color-hairline);
+ border-radius: 0;
+ color: var(--sl-color-gray-3);
+ --pad-inline: 0.75rem;
+}
+.right-sidebar starlight-toc a:hover {
+ color: var(--sl-color-white);
+}
+.right-sidebar starlight-toc a[aria-current="true"] {
+ border-inline-start-color: var(--sl-color-text-accent);
+ color: var(--sl-color-text-accent);
+ font-weight: 500;
+ background: transparent;
+}
+
+/* ----------------------------------------------------------------- prose */
+
+.sl-markdown-content a:not(:where(.not-content *)) {
+ text-underline-offset: 3px;
+}
+.sl-markdown-content a:not(:where(.not-content *)):hover {
+ text-decoration-style: dotted;
+}
+
+.sl-markdown-content :is(h1, h2, h3, h4):not(:where(.not-content *)) {
+ font-weight: 600;
+ letter-spacing: -0.005em;
+}
+
+/* Callouts: bordered card with a colored edge, per the design. */
+.starlight-aside {
+ border: 1px solid var(--sl-color-hairline);
+ border-inline-start: 3px solid var(--sl-color-asides-border);
+ border-radius: 0.375rem;
+ padding: 1rem 1.125rem;
+}
+.starlight-aside--note {
+ --sl-color-asides-border: var(--sl-color-text-accent);
+ --sl-color-asides-text-accent: var(--sl-color-text-accent);
+ background: var(--sl-color-gray-6);
+ color: var(--sl-color-gray-1);
+}
+.starlight-aside--caution {
+ --sl-color-asides-border: oklch(0.82 0.15 95);
+ --sl-color-asides-text-accent: oklch(0.82 0.15 95);
+ background: oklch(0.82 0.15 95 / 0.12);
+ color: var(--sl-color-gray-1);
+}
+:root[data-theme="light"] .starlight-aside--caution {
+ --sl-color-asides-border: oklch(0.6 0.225 95);
+ --sl-color-asides-text-accent: oklch(0.6 0.225 95);
+ background: oklch(0.9 0.165 98 / 0.14);
+}
+
+/* Tables: full-width bordered cards with a grey header row. */
+.sl-markdown-content table:not(:where(.not-content *)) {
+ display: table;
+ width: 100%;
+ border: 1px solid var(--sl-color-hairline);
+ border-radius: 0.5rem;
+ border-collapse: separate;
+ border-spacing: 0;
+ overflow: hidden;
+ font-size: var(--sl-text-sm);
+ line-height: 1.5;
+}
+.sl-markdown-content table:not(:where(.not-content *)) th {
+ background: var(--sl-color-gray-6);
+ font-size: var(--sl-text-xs);
+ font-weight: 600;
+ color: var(--sl-color-gray-3);
+ text-align: start;
+ padding: 0.625rem 1rem;
+}
+.sl-markdown-content table:not(:where(.not-content *)) td {
+ padding: 0.75rem 1rem;
+ border-top: 1px solid var(--sl-color-hairline);
+}
+.sl-markdown-content :is(th, td):not(:where(.not-content *)) {
+ border-inline: 0;
+ border-bottom: 0;
+}
+.sl-markdown-content tr:nth-child(2n):not(:where(.not-content *)) {
+ background: transparent;
+}
+
+/* Prev/next: cards with a small grey label, no arrows. */
+.pagination-links a {
+ border-radius: 0.5rem;
+ padding: 0.875rem 1rem;
+ box-shadow: none;
+ gap: 0.25rem;
+}
+.pagination-links a svg {
+ display: none;
+}
+.pagination-links a > span {
+ font-size: var(--sl-text-xs);
+ color: var(--sl-color-gray-3);
+}
+.pagination-links .link-title {
+ font-size: 0.9375rem;
+ font-weight: 500;
+ color: var(--sl-color-text-accent);
+}
+
+/* ---------------------------------------------------------- landing page */
+
+/* Let the hero band bleed to the viewport edges without a horizontal scrollbar. */
+[data-has-hero] .main-pane {
+ overflow-x: clip;
+}
+[data-has-hero] .main-frame .content-panel + .content-panel {
+ border-top: 0;
+}
+[data-has-hero] main {
+ background: var(--sl-color-gray-6);
+}
+
+.rq-cards {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1rem;
+ margin-top: 1.25rem;
+}
+@media (max-width: 50rem) {
+ .rq-cards {
+ grid-template-columns: 1fr;
+ }
+}
+
+.rq-card {
+ background: var(--sl-color-bg);
+ border: 1px solid var(--sl-color-hairline);
+ border-radius: 0.5rem;
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ padding: 1.375rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.625rem;
+}
+.rq-card-icon {
+ width: 2.25rem;
+ height: 2.25rem;
+ border-radius: 0.375rem;
+ background: var(--sl-color-gray-6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--sl-color-text-accent);
+}
+.rq-card h2 {
+ margin: 0;
+ font-size: 1.0625rem;
+ font-weight: 600;
+ color: var(--sl-color-white);
+}
+.rq-card p {
+ margin: 0;
+ font-size: var(--sl-text-sm);
+ line-height: 1.5;
+ color: var(--sl-color-gray-3);
+ text-wrap: pretty;
+}
+.rq-card > a {
+ margin-top: auto;
+ padding-top: 0.375rem;
+ font-size: var(--sl-text-sm);
+ font-weight: 500;
+ color: var(--sl-color-text-accent);
+ text-decoration: none;
+}
+.rq-card > a:hover {
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
+
+.rq-banner {
+ margin-top: 1.5rem;
+ background: var(--sl-color-bg);
+ border: 1px solid var(--sl-color-hairline);
+ border-radius: 0.5rem;
+ padding: 1.5rem;
+ display: flex;
+ gap: 2.5rem;
+ align-items: center;
+ flex-wrap: wrap;
+}
+.rq-banner-text {
+ flex: 1;
+ min-width: 16rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.375rem;
+}
+.rq-banner h2 {
+ margin: 0;
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--sl-color-white);
+}
+.rq-banner p {
+ margin: 0;
+ font-size: var(--sl-text-sm);
+ line-height: 1.5;
+ color: var(--sl-color-gray-3);
+ text-wrap: pretty;
+}
+.rq-banner > a {
+ display: inline-flex;
+ align-items: center;
+ height: 2.5rem;
+ padding: 0 1.125rem;
+ border-radius: 0.375rem;
+ background: var(--sl-color-text-accent);
+ color: #fff;
+ font-size: 0.9375rem;
+ font-weight: 500;
+ text-decoration: none;
+ flex: none;
}