diff --git a/.ai/rules/website.md b/.ai/rules/website.md index f9658f0..5a4df91 100644 --- a/.ai/rules/website.md +++ b/.ai/rules/website.md @@ -6,4 +6,4 @@ paths: # Website ## website/ is the live site, uploaded by hand -website/ is a faithful copy of sealshare.nonameweb.ch (METANET hosting), hand-written HTML/CSS with no build step, uploaded wholesale when it changes. Colours in css/theme.css are copied from the indigo profile in resources/css/material-scheme.json: the standard light and dark values (profiles.indigo.light/dark) and the high-contrast light and dark values (profiles.indigo.contrast.high) — copy them again if indigo is regenerated differently; the site does not follow the admin's colour profile. The comparison tables are dated and every competitor value has a source from the product's own site, docs or repo; an unsourced value is "—", never a guess. Never call SealShare's encryption end-to-end (files are encrypted in the browser with a key the server issues, and the server decrypts them for downloads). Nothing may load from another host except plausible.io. tests/Feature/WebsiteTest.php guards all of this. +website/ is a faithful copy of sealshare.nonameweb.ch (METANET hosting), hand-written HTML/CSS with no build step, uploaded wholesale when it changes. Colours in css/theme.css are copied from the indigo profile in resources/css/material-scheme.json: the standard light and dark values (profiles.indigo.light/dark) and the high-contrast light and dark values (profiles.indigo.contrast.high) — copy them again if indigo is regenerated differently; the site does not follow the admin's colour profile. Light or dark is , written before the first paint by each page's inline head script from the nav's Light/Dark/System toggle (localStorage sealshare-website-theme); the high-contrast values apply under prefers-contrast: more. The comparison tables are dated and every competitor value has a source from the product's own site, docs or repo; an unsourced value is "—", never a guess. Never call SealShare's encryption end-to-end (files are encrypted in the browser with a key the server issues, and the server decrypts them for downloads). Nothing may load from another host except plausible.io. tests/Feature/WebsiteTest.php guards all of this. diff --git a/website/css/device-frame.css b/website/css/device-frame.css index 5e4758a..656dbed 100644 --- a/website/css/device-frame.css +++ b/website/css/device-frame.css @@ -4,6 +4,10 @@ * Pro viewport). The screen keeps the capture's aspect ratio, so the frame holds its shape while an * image loads. The screenshots come from `composer screenshots` (tests/Screenshots/). * + * The bezels and the deck are hardware, drawn in fixed tints of their own; what shows through the + * screen before an image paints and the shadow under a device are colour roles from theme.css, which + * must be loaded first. Dark follows , and the system setting without JavaScript. + * *
*
*
@@ -14,13 +18,21 @@ --bezel-edge: #3a3355; --deck: #c9c2dd; --deck-shadow: #8f88a8; + --device-shadow: color-mix(in srgb, var(--md-sys-color-shadow) 40%, transparent); position: relative; width: 100%; } +:root[data-theme="dark"] .device { + --bezel: #05020f; + --bezel-edge: #2a2244; + --deck: #4b426a; + --deck-shadow: #231649; +} + @media (prefers-color-scheme: dark) { - .device { + :root:not([data-theme]) .device { --bezel: #05020f; --bezel-edge: #2a2244; --deck: #4b426a; @@ -31,7 +43,7 @@ .device__screen { position: relative; overflow: hidden; - background: var(--surface-container-high, #e8deff); + background-color: var(--md-sys-color-surface-container-high); } .device__screen img { @@ -54,7 +66,7 @@ padding: 2.4% 2.4% 3%; border-radius: 3.2% 3.2% 0 0 / 4.8% 4.8% 0 0; background: var(--bezel); - box-shadow: inset 0 0 0 1px var(--bezel-edge), 0 24px 48px -24px rgb(16 6 45 / 0.45); + box-shadow: inset 0 0 0 1px var(--bezel-edge), 0 24px 48px -24px var(--device-shadow); } .device--laptop .device__screen { @@ -79,8 +91,7 @@ .device--laptop::after { content: ''; position: absolute; - left: -4%; - right: -4%; + inset-inline: -4%; bottom: 0; height: 3.2%; border-radius: 0 0 40% 40% / 0 0 100% 100%; @@ -95,7 +106,7 @@ z-index: 1; width: 14%; height: 1.4%; - border-radius: 0 0 12px 12px; + border-radius: 0 0 var(--md-sys-shape-corner-md) var(--md-sys-shape-corner-md); background: var(--deck-shadow); translate: -50% 0; } @@ -106,7 +117,7 @@ padding: 3.4%; border-radius: 15% / 7%; background: var(--bezel); - box-shadow: inset 0 0 0 2px var(--bezel-edge), 0 24px 48px -20px rgb(16 6 45 / 0.5); + box-shadow: inset 0 0 0 2px var(--bezel-edge), 0 24px 48px -20px var(--device-shadow); } .device--phone .device__screen { diff --git a/website/css/theme.css b/website/css/theme.css index 0eee31e..c5b557e 100644 --- a/website/css/theme.css +++ b/website/css/theme.css @@ -1,12 +1,25 @@ /* * SealShare website theme. * - * The colours are not an approximation of the app's: they are copied from - * the indigo profile in resources/css/material-scheme.json (seed #4f46e5, Vibrant, Material 3 spec - * 2025, contrast 0; the file's top-level light and dark), generated by `php artisan material:scheme` - * from the app's colour profiles. When indigo is regenerated differently, copy the roles below again — - * the site does not read the file, and does not follow the colour profile an admin chooses. Light is the default, dark follows the - * visitor's system setting; the site has no theme toggle of its own. + * The site looks like the app: Material 3 Expressive as Livewire Material 2.1 draws it, written by hand + * in plain CSS, because the site has no build step. The tokens carry the app's names (--md-sys-*) and + * its values, copied here rather than imported, so nothing loads from another host: + * + * - Colour roles from the indigo profile in resources/css/material-scheme.json (seed #4f46e5, + * Vibrant, Material 3 spec 2025): profiles.indigo.light and .dark at standard contrast, and + * profiles.indigo.contrast.high.light and .dark for visitors whose system asks for more contrast + * (prefers-contrast: more). The medium contrast level is not used. Only the roles the site draws + * with are copied; copy them again when indigo is regenerated differently. The site does not + * follow the colour profile an admin chooses. + * - Shape, elevation, motion, state, type and spacing from Livewire Material's token files + * (vendor/nonameweb/livewire-material/resources/css/tokens/), only the tokens used below. + * + * Light or dark is , written before the first paint by the inline script in each + * page's from the visitor's choice in the nav (Light, Dark or System, remembered in + * localStorage). Without JavaScript the attribute is absent and the system setting decides. + * + * Widths are M3's window size classes: compact below 600px, medium from 600px, expanded from 840px, + * large from 1200px. * * Shared by index.html and privacy.html. */ @@ -22,85 +35,252 @@ font-display: swap; } +/* ---------------------------------------------------------------- tokens */ + :root { - color-scheme: light dark; + color-scheme: light; - --surface: #faf4ff; - --surface-container-lowest: #ffffff; - --surface-container-low: #f5eeff; - --surface-container: #ede4ff; - --surface-container-high: #e8deff; - --surface-container-highest: #e2d7ff; - --on-surface: #32294f; - --on-surface-variant: #5f557f; - --outline: #7b719c; - --outline-variant: #b2a6d5; - --inverse-surface: #10062d; - --inverse-on-surface: #a296c4; - --primary: #4a3fe2; - --on-primary: #f4f1ff; - --primary-container: #9795ff; - --on-primary-container: #14007e; - --secondary: #6249b2; - --secondary-container: #d8caff; - --on-secondary-container: #4e339c; - --tertiary-container: #fd8bca; - --on-tertiary-container: #610244; - --success: #006c45; - --error: #b41340; + /* Colour: indigo, light, standard contrast (profiles.indigo.light). */ + --md-sys-color-surface: #faf4ff; + --md-sys-color-surface-container-low: #f5eeff; + --md-sys-color-surface-container: #ede4ff; + --md-sys-color-surface-container-high: #e8deff; + --md-sys-color-surface-container-highest: #e2d7ff; + --md-sys-color-on-surface: #32294f; + --md-sys-color-on-surface-variant: #5f557f; + --md-sys-color-outline: #7b719c; + --md-sys-color-outline-variant: #b2a6d5; + --md-sys-color-primary: #4a3fe2; + --md-sys-color-on-primary: #f4f1ff; + --md-sys-color-primary-container: #9795ff; + --md-sys-color-on-primary-container: #14007e; + --md-sys-color-secondary: #6249b2; + --md-sys-color-secondary-container: #d8caff; + --md-sys-color-on-secondary-container: #4e339c; + --md-sys-color-tertiary-container: #fd8bca; + --md-sys-color-on-tertiary-container: #610244; + --md-sys-color-success: #006943; + --md-sys-color-shadow: #000000; - /* The code block reads as a terminal in both themes. */ - --code-surface: #10062d; - --code-ink: #eae1ff; + /* Shape (tokens/shape.css). */ + --md-sys-shape-corner-xs: 4px; + --md-sys-shape-corner-sm: 8px; + --md-sys-shape-corner-md: 12px; + --md-sys-shape-corner-full: 9999px; - --font: 'Google Sans Flex', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; + /* Elevation (tokens/elevation.css). */ + --md-sys-elevation-1: 0 1px 2px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 1px 3px 1px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent); + --md-sys-elevation-2: 0 1px 2px 0 color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent), 0 2px 6px 2px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent); - --corner-sm: 8px; - --corner-md: 12px; - --corner-lg: 16px; - --corner-xl: 28px; - --corner-xxl: 48px; - --corner-full: 999px; + /* Motion (tokens/motion.css): Expressive springs sampled into linear(), each with its duration. */ + --md-sys-motion-spatial-fast: linear(0, 0.0195, 0.0716, 0.1471, 0.2381, 0.3378, 0.4404, 0.5416, 0.6378, 0.7265, 0.806, 0.8754, 0.9342, 0.9826, 1.0211, 1.0503, 1.0713, 1.0849, 1.0924, 1.0948, 1.0931, 1.0882, 1.0812, 1.0726, 1.0632, 1.0534, 1.0438, 1.0347, 1.0263, 1.0187, 1.0121, 1.0064, 1.0018, 0.9981, 0.9953, 0.9933, 0.992, 0.9913, 0.991, 0.9912, 0.9916, 0.9923, 0.9931, 0.994, 0.9949, 0.9958, 0.9967, 0.9975, 1); + --md-sys-motion-spatial-fast-duration: 350ms; + --md-sys-motion-effects-fast: linear(0, 0.0163, 0.0576, 0.1147, 0.1807, 0.2507, 0.3214, 0.3902, 0.4558, 0.5172, 0.5737, 0.6253, 0.6718, 0.7136, 0.7508, 0.7837, 0.8128, 0.8383, 0.8606, 0.8801, 0.897, 0.9117, 0.9244, 0.9353, 0.9448, 0.9529, 0.9599, 0.9658, 0.9709, 0.9753, 0.979, 0.9822, 0.9849, 0.9872, 0.9892, 0.9909, 0.9923, 0.9935, 0.9945, 0.9954, 0.9961, 0.9967, 0.9972, 0.9977, 0.998, 0.9983, 0.9986, 0.9988, 1); + --md-sys-motion-effects-fast-duration: 150ms; + --md-sys-motion-effects-default: linear(0, 0.0124, 0.0446, 0.0902, 0.1443, 0.2032, 0.2642, 0.3253, 0.3849, 0.4422, 0.4963, 0.547, 0.594, 0.6372, 0.6768, 0.7127, 0.7452, 0.7745, 0.8009, 0.8244, 0.8454, 0.8641, 0.8807, 0.8954, 0.9084, 0.9199, 0.93, 0.9389, 0.9467, 0.9536, 0.9596, 0.9648, 0.9694, 0.9734, 0.9769, 0.98, 0.9826, 0.985, 0.987, 0.9887, 0.9902, 0.9916, 0.9927, 0.9937, 0.9946, 0.9953, 0.9959, 0.9965, 1); + --md-sys-motion-effects-default-duration: 200ms; - --shadow-1: 0 1px 2px rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15); - --shadow-3: 0 4px 8px 3px rgb(0 0 0 / 0.15), 0 1px 3px rgb(0 0 0 / 0.3); + /* State (tokens/state.css). */ + --md-sys-state-hover-state-layer-opacity: 0.08; + --md-sys-state-focus-state-layer-opacity: 0.1; + --md-sys-state-pressed-state-layer-opacity: 0.1; + --md-sys-state-disabled-content-opacity: 0.38; + --md-sys-state-disabled-container-opacity: 0.12; - --ease-spatial: cubic-bezier(0.38, 1.21, 0.22, 1); - --ease-effects: cubic-bezier(0.34, 0.8, 0.34, 1); + /* Type (tokens/type.css). */ + --md-ref-typeface-brand: 'Google Sans Flex', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --md-ref-typeface-plain: var(--md-ref-typeface-brand); + --md-ref-typeface-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --md-ref-typeface-weight-regular: 400; + --md-ref-typeface-weight-medium: 500; - --container: 1200px; - --gutter: clamp(16px, 4vw, 32px); + --md-sys-typescale-display-lg: var(--md-ref-typeface-weight-regular) 3.5625rem/4rem var(--md-ref-typeface-brand); + --md-sys-typescale-display-lg-tracking: -0.0125rem; + --md-sys-typescale-display-md: var(--md-ref-typeface-weight-regular) 2.8125rem/3.25rem var(--md-ref-typeface-brand); + --md-sys-typescale-display-md-tracking: 0rem; + --md-sys-typescale-display-sm: var(--md-ref-typeface-weight-regular) 2.25rem/2.75rem var(--md-ref-typeface-brand); + --md-sys-typescale-display-sm-tracking: 0rem; + --md-sys-typescale-headline-lg: var(--md-ref-typeface-weight-regular) 2rem/2.5rem var(--md-ref-typeface-brand); + --md-sys-typescale-headline-lg-tracking: 0rem; + --md-sys-typescale-headline-md: var(--md-ref-typeface-weight-regular) 1.75rem/2.25rem var(--md-ref-typeface-brand); + --md-sys-typescale-headline-md-tracking: 0rem; + --md-sys-typescale-title-lg: var(--md-ref-typeface-weight-regular) 1.375rem/1.75rem var(--md-ref-typeface-brand); + --md-sys-typescale-title-lg-tracking: 0rem; + --md-sys-typescale-title-md: var(--md-ref-typeface-weight-medium) 1rem/1.5rem var(--md-ref-typeface-plain); + --md-sys-typescale-title-md-tracking: 0.0125rem; + --md-sys-typescale-title-sm: var(--md-ref-typeface-weight-medium) 0.875rem/1.25rem var(--md-ref-typeface-plain); + --md-sys-typescale-title-sm-tracking: 0.00625rem; + --md-sys-typescale-body-lg: var(--md-ref-typeface-weight-regular) 1rem/1.5rem var(--md-ref-typeface-plain); + --md-sys-typescale-body-lg-tracking: 0.03125rem; + --md-sys-typescale-body-md: var(--md-ref-typeface-weight-regular) 0.875rem/1.25rem var(--md-ref-typeface-plain); + --md-sys-typescale-body-md-tracking: 0.0125rem; + --md-sys-typescale-label-lg: var(--md-ref-typeface-weight-medium) 0.875rem/1.25rem var(--md-ref-typeface-plain); + --md-sys-typescale-label-lg-tracking: 0.00625rem; + + /* Spacing (tokens/spacing.css). */ + --md-sys-measurement-space25: 2px; + --md-sys-measurement-space50: 4px; + --md-sys-measurement-space75: 6px; + --md-sys-measurement-space100: 8px; + --md-sys-measurement-space200: 16px; + --md-sys-measurement-space300: 24px; + --md-sys-measurement-space400: 32px; + --md-sys-measurement-space500: 40px; + --md-sys-measurement-space600: 48px; + --md-sys-measurement-space700: 56px; + --md-sys-measurement-space800: 64px; + --md-sys-measurement-space900: 72px; + + /* The site's own layout: M3's margins (16px compact, 24px from medium) and the page width. */ + --site-margin: var(--md-sys-measurement-space200); + --site-width: 1200px; + --site-bar-height: var(--md-sys-measurement-space800); + --site-section-space: var(--md-sys-measurement-space700); } -@media (prefers-color-scheme: dark) { - :root { - --surface: #10062d; - --surface-container-lowest: #000000; - --surface-container-low: #160b36; - --surface-container: #1c113f; - --surface-container-high: #231649; - --surface-container-highest: #291c53; - --on-surface: #eae1ff; - --on-surface-variant: #b0a4d3; - --outline: #7a6f9b; - --outline-variant: #4b426a; - --inverse-surface: #fdf7ff; - --inverse-on-surface: #594f78; - --primary: #a7a5ff; - --on-primary: #1c00a0; - --primary-container: #9795ff; - --on-primary-container: #14007e; - --secondary: #a98ffd; - --secondary-container: #4d329b; - --on-secondary-container: #d6c9ff; - --tertiary-container: #fa88c8; - --on-tertiary-container: #5e0042; - --success: #69dca1; - --error: #ff6e84; +/* Colour: indigo, dark, standard contrast (profiles.indigo.dark). */ +:root[data-theme="dark"] { + color-scheme: dark; - --code-surface: #000000; - --code-ink: #eae1ff; + --md-sys-color-surface: #10062d; + --md-sys-color-surface-container-low: #160b36; + --md-sys-color-surface-container: #1c113f; + --md-sys-color-surface-container-high: #231649; + --md-sys-color-surface-container-highest: #291c53; + --md-sys-color-on-surface: #eae1ff; + --md-sys-color-on-surface-variant: #b0a4d3; + --md-sys-color-outline: #7a6f9b; + --md-sys-color-outline-variant: #4b426a; + --md-sys-color-primary: #a7a5ff; + --md-sys-color-on-primary: #1c00a0; + --md-sys-color-primary-container: #9795ff; + --md-sys-color-on-primary-container: #14007e; + --md-sys-color-secondary: #a98ffd; + --md-sys-color-secondary-container: #4d329b; + --md-sys-color-on-secondary-container: #d6c9ff; + --md-sys-color-tertiary-container: #fa88c8; + --md-sys-color-on-tertiary-container: #5e0042; + --md-sys-color-success: #3bb27b; +} + +/* Without JavaScript there is no data-theme, and the system decides: the same dark values. */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + color-scheme: dark; + + --md-sys-color-surface: #10062d; + --md-sys-color-surface-container-low: #160b36; + --md-sys-color-surface-container: #1c113f; + --md-sys-color-surface-container-high: #231649; + --md-sys-color-surface-container-highest: #291c53; + --md-sys-color-on-surface: #eae1ff; + --md-sys-color-on-surface-variant: #b0a4d3; + --md-sys-color-outline: #7a6f9b; + --md-sys-color-outline-variant: #4b426a; + --md-sys-color-primary: #a7a5ff; + --md-sys-color-on-primary: #1c00a0; + --md-sys-color-primary-container: #9795ff; + --md-sys-color-on-primary-container: #14007e; + --md-sys-color-secondary: #a98ffd; + --md-sys-color-secondary-container: #4d329b; + --md-sys-color-on-secondary-container: #d6c9ff; + --md-sys-color-tertiary-container: #fa88c8; + --md-sys-color-on-tertiary-container: #5e0042; + --md-sys-color-success: #3bb27b; + } +} + +/* High contrast, when the system asks for more: profiles.indigo.contrast.high. Light first, which the + dark selectors below outrank by specificity. */ +@media (prefers-contrast: more) { + :root { + --md-sys-color-surface: #faf4ff; + --md-sys-color-surface-container-low: #f5eeff; + --md-sys-color-surface-container: #ede4ff; + --md-sys-color-surface-container-high: #e8deff; + --md-sys-color-surface-container-highest: #e2d7ff; + --md-sys-color-on-surface: #000000; + --md-sys-color-on-surface-variant: #22193f; + --md-sys-color-outline: #433961; + --md-sys-color-outline-variant: #524972; + --md-sys-color-primary: #110075; + --md-sys-color-on-primary: #d3d0ff; + --md-sys-color-primary-container: #3c2ed5; + --md-sys-color-on-primary-container: #ffffff; + --md-sys-color-secondary: #240069; + --md-sys-color-secondary-container: #553ba4; + --md-sys-color-on-secondary-container: #ffffff; + --md-sys-color-tertiary-container: #882964; + --md-sys-color-on-tertiary-container: #ffffff; + --md-sys-color-success: #002515; + } + + :root[data-theme="dark"] { + --md-sys-color-surface: #10062d; + --md-sys-color-surface-container-low: #160b36; + --md-sys-color-surface-container: #1c113f; + --md-sys-color-surface-container-high: #231649; + --md-sys-color-surface-container-highest: #291c53; + --md-sys-color-on-surface: #ffffff; + --md-sys-color-on-surface-variant: #eae1ff; + --md-sys-color-outline: #beb2e2; + --md-sys-color-outline-variant: #a99dcc; + --md-sys-color-primary: #e6e2ff; + --md-sys-color-on-primary: #1a0099; + --md-sys-color-primary-container: #9b99ff; + --md-sys-color-on-primary-container: #000000; + --md-sys-color-secondary: #eae1ff; + --md-sys-color-secondary-container: #ac93ff; + --md-sys-color-on-secondary-container: #000000; + --md-sys-color-tertiary-container: #fa88c8; + --md-sys-color-on-tertiary-container: #000000; + --md-sys-color-success: #89fcbf; + } +} + +@media (prefers-contrast: more) and (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --md-sys-color-surface: #10062d; + --md-sys-color-surface-container-low: #160b36; + --md-sys-color-surface-container: #1c113f; + --md-sys-color-surface-container-high: #231649; + --md-sys-color-surface-container-highest: #291c53; + --md-sys-color-on-surface: #ffffff; + --md-sys-color-on-surface-variant: #eae1ff; + --md-sys-color-outline: #beb2e2; + --md-sys-color-outline-variant: #a99dcc; + --md-sys-color-primary: #e6e2ff; + --md-sys-color-on-primary: #1a0099; + --md-sys-color-primary-container: #9b99ff; + --md-sys-color-on-primary-container: #000000; + --md-sys-color-secondary: #eae1ff; + --md-sys-color-secondary-container: #ac93ff; + --md-sys-color-on-secondary-container: #000000; + --md-sys-color-tertiary-container: #fa88c8; + --md-sys-color-on-tertiary-container: #000000; + --md-sys-color-success: #89fcbf; + } +} + +@media (width >= 600px) { + :root { + --site-margin: var(--md-sys-measurement-space300); + --site-section-space: var(--md-sys-measurement-space900); + } +} + +@media (width >= 1200px) { + :root { + --site-section-space: calc(var(--md-sys-measurement-space600) * 2); + } +} + +/* Reduced motion zeroes every duration, so everything that moves through the tokens turns instant. */ +@media (prefers-reduced-motion: reduce) { + :root { + --md-sys-motion-spatial-fast-duration: 0ms; + --md-sys-motion-effects-fast-duration: 0ms; + --md-sys-motion-effects-default-duration: 0ms; } } @@ -114,18 +294,22 @@ html { scroll-behavior: smooth; - scroll-padding-top: 88px; + scroll-padding-top: calc(var(--site-bar-height) + var(--md-sys-measurement-space200)); -webkit-text-size-adjust: 100%; } +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} + body { margin: 0; - background: var(--surface); - color: var(--on-surface); - font-family: var(--font); - font-size: 16px; - line-height: 1.5; - letter-spacing: 0.01em; + background-color: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); + font: var(--md-sys-typescale-body-lg); + letter-spacing: var(--md-sys-typescale-body-lg-tracking); -webkit-font-smoothing: antialiased; } @@ -136,36 +320,37 @@ svg { } a { - color: var(--primary); - text-underline-offset: 3px; -} - -a:hover { - text-decoration-thickness: 2px; + color: var(--md-sys-color-primary); + text-decoration-line: underline; + text-decoration-thickness: from-font; + text-underline-offset: 0.15em; } +/* M3's focus indicator: 3px in secondary, 2px out, for keyboard focus only. */ :focus-visible { - outline: 3px solid var(--primary); - outline-offset: 3px; - border-radius: var(--corner-sm); + outline: 3px solid var(--md-sys-color-secondary); + outline-offset: 2px; +} + +h1, +h2, +h3, +p { + margin: 0; } h1, h2, h3 { - margin: 0; - font-weight: 500; - letter-spacing: 0; text-wrap: balance; } p { - margin: 0; text-wrap: pretty; } code { - font-family: var(--mono); + font-family: var(--md-ref-typeface-mono); font-size: 0.9em; } @@ -193,242 +378,365 @@ code { width: 1px; height: 1px; overflow: hidden; - clip: rect(0 0 0 0); + clip-path: inset(50%); white-space: nowrap; } -.skip-link { +a.skip-link { position: absolute; - top: 8px; - left: 8px; + inset-block-start: var(--md-sys-measurement-space100); + inset-inline-start: var(--md-sys-measurement-space100); z-index: 100; - padding: 12px 20px; - border-radius: var(--corner-full); - background: var(--primary); - color: var(--on-primary); - font-weight: 500; translate: 0 -200%; } -.skip-link:focus-visible { +a.skip-link:focus-visible { translate: 0 0; } .container { width: 100%; - max-width: var(--container); + max-width: var(--site-width); margin-inline: auto; - padding-inline: var(--gutter); + padding-inline: var(--site-margin); } /* ---------------------------------------------------------------- type */ +/* The hero's line: display, larger as the window grows. */ .display { - font-size: clamp(2.5rem, 1.6rem + 3.6vw, 4.25rem); - line-height: 1.06; - font-weight: 500; - letter-spacing: -0.02em; + font: var(--md-sys-typescale-display-sm); + letter-spacing: var(--md-sys-typescale-display-sm-tracking); } +@media (width >= 600px) { + .display { + font: var(--md-sys-typescale-display-md); + letter-spacing: var(--md-sys-typescale-display-md-tracking); + } +} + +@media (width >= 1200px) { + .display { + font: var(--md-sys-typescale-display-lg); + letter-spacing: var(--md-sys-typescale-display-lg-tracking); + } +} + +/* Page and section titles. */ .headline { - font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem); - line-height: 1.15; - letter-spacing: -0.01em; + font: var(--md-sys-typescale-headline-md); + letter-spacing: var(--md-sys-typescale-headline-md-tracking); } +@media (width >= 600px) { + .headline { + font: var(--md-sys-typescale-headline-lg); + letter-spacing: var(--md-sys-typescale-headline-lg-tracking); + } +} + +/* Card titles. */ .title { - font-size: 1.375rem; - line-height: 1.27; + font: var(--md-sys-typescale-title-lg); + letter-spacing: var(--md-sys-typescale-title-lg-tracking); } .lede { - color: var(--on-surface-variant); - font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem); - line-height: 1.5; + max-inline-size: 60ch; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-lg); + letter-spacing: var(--md-sys-typescale-body-lg-tracking); } +/* A section's label above its title: an icon and a word on a tonal pill, not interactive. */ .eyebrow { display: inline-flex; justify-self: start; width: fit-content; align-items: center; - gap: 8px; - padding: 6px 14px; - border-radius: var(--corner-full); - background: var(--secondary-container); - color: var(--on-secondary-container); - font-size: 0.875rem; - font-weight: 500; - letter-spacing: 0.02em; + gap: var(--md-sys-measurement-space100); + padding-block: var(--md-sys-measurement-space75); + padding-inline: calc(var(--md-sys-measurement-space100) + var(--md-sys-measurement-space50)) var(--md-sys-measurement-space200); + border-radius: var(--md-sys-shape-corner-full); + background-color: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); + font: var(--md-sys-typescale-label-lg); + letter-spacing: var(--md-sys-typescale-label-lg-tracking); } .eyebrow .icon { - width: 18px; - height: 18px; -} - -/* ---------------------------------------------------------------- buttons */ - -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - min-height: 48px; - padding: 12px 24px; - border: 1px solid transparent; - border-radius: var(--corner-full); - font: inherit; - font-size: 1rem; - font-weight: 500; - line-height: 1.25; - text-decoration: none; - white-space: nowrap; - cursor: pointer; - transition: border-radius 250ms var(--ease-spatial), background-color 150ms var(--ease-effects); -} - -.btn:hover { - text-decoration: none; -} - -.btn:active { - border-radius: var(--corner-md); -} - -.btn .icon { width: 20px; height: 20px; } -.btn--filled { - background: var(--primary); - color: var(--on-primary); +/* ---------------------------------------------------------------- interaction */ + +/* M3's state layer: the content colour over the element, 8% on hover, 10% on focus and press. */ +.btn, +.icon-btn, +.nav__links a:not(.btn), +.faq summary { + position: relative; + isolation: isolate; } -.btn--filled:hover { - background: color-mix(in srgb, var(--primary) 88%, var(--on-primary)); +.btn::before, +.icon-btn::before, +.nav__links a:not(.btn)::before, +.faq summary::before { + content: ''; + position: absolute; + inset: 0; + z-index: -1; + border-radius: inherit; + background-color: currentColor; + opacity: 0; + pointer-events: none; + transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } -.btn--tonal { - background: var(--secondary-container); - color: var(--on-secondary-container); +@media (hover: hover) { + .btn:hover::before, + .icon-btn:hover::before, + .nav__links a:not(.btn):hover::before, + .faq summary:hover::before { + opacity: var(--md-sys-state-hover-state-layer-opacity); + } } -.btn--tonal:hover { - background: color-mix(in srgb, var(--secondary-container) 88%, var(--on-secondary-container)); +.btn:focus-visible::before, +.icon-btn:focus-visible::before, +.nav__links a:not(.btn):focus-visible::before, +.faq summary:focus-visible::before { + opacity: var(--md-sys-state-focus-state-layer-opacity); } -.btn--outlined { - border-color: var(--outline-variant); - color: var(--primary); +.btn:active::before, +.icon-btn:active::before, +.nav__links a:not(.btn):active::before, +.faq summary:active::before { + opacity: var(--md-sys-state-pressed-state-layer-opacity); } -.btn--outlined:hover { - background: color-mix(in srgb, var(--primary) 8%, transparent); +/* M3's 48px minimum target around a control drawn smaller. */ +.btn::after, +.icon-btn::after, +.nav__links a:not(.btn)::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + min-width: var(--md-sys-measurement-space600); + min-height: var(--md-sys-measurement-space600); + transform: translate(-50%, -50%); +} + +/* ---------------------------------------------------------------- buttons */ + +/* M3 Expressive buttons. The default is the small size (40px, label-large); .btn--lg is the medium + size (56px, title-medium) for the page's main calls to action. Round at rest, squarer while + pressed, on the fast spatial spring; colours on the fast effects spring. */ +.btn { + --btn-height: 40px; + --btn-padding: var(--md-sys-measurement-space200); + --btn-icon: 20px; + --btn-pressed: var(--md-sys-shape-corner-sm); + + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--md-sys-measurement-space100); + min-block-size: var(--btn-height); + padding-inline: var(--btn-padding); + border: 0; + border-radius: calc(var(--btn-height) / 2); + background-color: transparent; + color: var(--md-sys-color-primary); + font: var(--md-sys-typescale-label-lg); + letter-spacing: var(--md-sys-typescale-label-lg-tracking); + text-decoration: none; + white-space: nowrap; + cursor: pointer; + -webkit-user-select: none; + user-select: none; + transition-property: border-radius, background-color, color, box-shadow; + transition-duration: var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration); + transition-timing-function: var(--md-sys-motion-spatial-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast); +} + +.btn:active { + border-radius: var(--btn-pressed); +} + +.btn .icon { + width: var(--btn-icon); + height: var(--btn-icon); } .btn--lg { - min-height: 56px; - padding: 16px 28px; - font-size: 1.0625rem; + --btn-height: 56px; + --btn-padding: var(--md-sys-measurement-space300); + --btn-icon: 24px; + --btn-pressed: var(--md-sys-shape-corner-md); + + font: var(--md-sys-typescale-title-md); + letter-spacing: var(--md-sys-typescale-title-md-tracking); +} + +.btn--filled { + background-color: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); +} + +.btn--tonal { + background-color: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); +} + +.btn--outlined { + border: 1px solid var(--md-sys-color-outline-variant); + color: var(--md-sys-color-on-surface-variant); +} + +@media (hover: hover) { + .btn--filled:hover, + .btn--tonal:hover { + box-shadow: var(--md-sys-elevation-1); + } +} + +.btn:is(:disabled, [aria-disabled="true"]) { + box-shadow: none; + color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent); + cursor: not-allowed; +} + +.btn:is(.btn--filled, .btn--tonal):is(:disabled, [aria-disabled="true"]) { + background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity) * 100%), transparent); +} + +.btn:is(:disabled, [aria-disabled="true"])::before { + display: none; +} + +/* A standard icon button: 40px drawn, a 48px target, on-surface-variant. */ +.icon-btn { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + padding: 0; + border: 0; + border-radius: 20px; + background-color: transparent; + color: var(--md-sys-color-on-surface-variant); + cursor: pointer; + transition: border-radius var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast); +} + +.icon-btn:active { + border-radius: var(--md-sys-shape-corner-sm); +} + +/* M3 Expressive's connected button group, the successor of segmented buttons: segments 2px apart, + round outer corners and small inner ones, and the chosen segment (aria-pressed) fully round in + secondary-container with a check. */ +.button-group { + display: inline-flex; + gap: var(--md-sys-measurement-space25); +} + +.button-group .btn { + min-inline-size: var(--md-sys-measurement-space600); + border-radius: var(--md-sys-shape-corner-sm); + background-color: var(--md-sys-color-surface-container); + color: var(--md-sys-color-on-surface-variant); +} + +.button-group .btn:first-child { + border-start-start-radius: calc(var(--btn-height) / 2); + border-end-start-radius: calc(var(--btn-height) / 2); +} + +.button-group .btn:last-child { + border-start-end-radius: calc(var(--btn-height) / 2); + border-end-end-radius: calc(var(--btn-height) / 2); +} + +.button-group .btn:active { + border-radius: var(--md-sys-shape-corner-xs); +} + +.button-group .btn:first-child:active { + border-radius: calc(var(--btn-height) / 2) var(--md-sys-shape-corner-xs) var(--md-sys-shape-corner-xs) calc(var(--btn-height) / 2); +} + +.button-group .btn:last-child:active { + border-radius: var(--md-sys-shape-corner-xs) calc(var(--btn-height) / 2) calc(var(--btn-height) / 2) var(--md-sys-shape-corner-xs); +} + +.button-group .btn[aria-pressed="true"] { + border-radius: calc(var(--btn-height) / 2); + background-color: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); +} + +.button-group .btn .icon { + display: none; +} + +.button-group .btn[aria-pressed="true"] .icon { + display: block; } /* ---------------------------------------------------------------- nav */ +/* The top app bar: surface at rest, surface-container once the page scrolls under it. */ .nav { position: sticky; top: 0; z-index: 50; - background: color-mix(in srgb, var(--surface) 86%, transparent); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border-bottom: 1px solid color-mix(in srgb, var(--outline-variant) 45%, transparent); + background-color: var(--md-sys-color-surface); + transition: background-color var(--md-sys-motion-effects-default-duration) var(--md-sys-motion-effects-default); +} + +.nav[data-scrolled] { + background-color: var(--md-sys-color-surface-container); } .nav__bar { display: flex; align-items: center; - gap: 16px; - min-height: 72px; + gap: var(--md-sys-measurement-space100); + min-height: var(--site-bar-height); } .brand { display: inline-flex; align-items: center; - gap: 10px; - color: var(--on-surface); - font-size: 1.375rem; - font-weight: 500; - text-decoration: none; -} - -.brand:hover { + gap: var(--md-sys-measurement-space100); + min-block-size: var(--md-sys-measurement-space600); + color: var(--md-sys-color-on-surface); + font: var(--md-sys-typescale-title-lg); + letter-spacing: var(--md-sys-typescale-title-lg-tracking); text-decoration: none; } .brand svg { width: 32px; height: 32px; - color: var(--primary); -} - -.nav__bar > nav { - margin-left: auto; -} - -.nav__links { - display: flex; - align-items: center; - gap: 4px; - margin: 0; - padding: 0; - list-style: none; -} - -.nav__links a { - display: inline-flex; - align-items: center; - min-height: 40px; - padding: 8px 14px; - border-radius: var(--corner-full); - color: var(--on-surface-variant); - font-size: 0.9375rem; - font-weight: 500; - text-decoration: none; -} - -.nav__links a:hover { - background: color-mix(in srgb, var(--on-surface) 8%, transparent); - color: var(--on-surface); -} - -.nav__links .btn { - margin-left: 8px; - color: var(--on-primary); -} - -.nav__links .btn:hover { - color: var(--on-primary); - background: color-mix(in srgb, var(--primary) 88%, var(--on-primary)); + color: var(--md-sys-color-primary); } .nav__toggle { display: none; - margin-left: auto; - width: 48px; - height: 48px; - align-items: center; - justify-content: center; - border: 0; - border-radius: var(--corner-full); - background: transparent; - color: var(--on-surface); - cursor: pointer; -} - -.nav__toggle:hover { - background: color-mix(in srgb, var(--on-surface) 8%, transparent); + margin-inline-start: auto; + color: var(--md-sys-color-on-surface); } .nav__toggle .icon-close { @@ -443,44 +751,92 @@ code { display: block; } -@media (max-width: 960px) { +.nav__bar > nav { + margin-inline-start: auto; +} + +.nav__links { + display: flex; + align-items: center; + gap: var(--md-sys-measurement-space50); + margin: 0; + padding: 0; + list-style: none; +} + +.nav__links a:not(.btn) { + display: inline-flex; + align-items: center; + min-block-size: 40px; + padding-inline: calc(var(--md-sys-measurement-space100) + var(--md-sys-measurement-space50)); + border-radius: 20px; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-label-lg); + letter-spacing: var(--md-sys-typescale-label-lg-tracking); + text-decoration: none; +} + +.nav__links .btn { + margin-inline-start: var(--md-sys-measurement-space100); +} + +/* Light, Dark or System. Without JavaScript there is nothing to switch, so it stays hidden. */ +.theme-toggle { + display: none; +} + +:root[data-theme-choice] .theme-toggle { + display: inline-flex; +} + +.theme-toggle .icon { + display: none; +} + +:root[data-theme-choice="light"] .theme-toggle .icon-light-mode, +:root[data-theme-choice="dark"] .theme-toggle .icon-dark-mode, +:root[data-theme-choice="system"] .theme-toggle .icon-brightness-auto { + display: block; +} + +/* Below expanded, the links fold into a menu under the bar. */ +@media (width < 840px) { .nav__toggle { display: inline-flex; } + .nav__bar > nav { + margin-inline-start: 0; + } + .nav__links { position: absolute; - top: 100%; - left: var(--gutter); - right: var(--gutter); + inset-block-start: 100%; + inset-inline: var(--site-margin); display: none; flex-direction: column; align-items: stretch; - gap: 2px; - margin: 8px 0 0; - padding: 8px; - border-radius: var(--corner-xl); - background: var(--surface-container); - box-shadow: var(--shadow-3); + gap: 0; + padding-block: var(--md-sys-measurement-space100); + border-radius: var(--md-sys-shape-corner-xs); + background-color: var(--md-sys-color-surface-container); + box-shadow: var(--md-sys-elevation-2); } .nav__links.is-open { display: flex; } - .nav__links a { - min-height: 48px; - padding: 12px 16px; - font-size: 1rem; - } - - .nav__bar > nav { - margin-left: 0; + .nav__links a:not(.btn) { + min-block-size: var(--md-sys-measurement-space600); + padding-inline: var(--md-sys-measurement-space200); + border-radius: 0; + color: var(--md-sys-color-on-surface); } .nav__links .btn { - width: 100%; - margin: 8px 0 0; + width: calc(100% - 2 * var(--md-sys-measurement-space200)); + margin: var(--md-sys-measurement-space100) var(--md-sys-measurement-space200) 0; } } @@ -489,113 +845,129 @@ code { .hero { position: relative; overflow: clip; - padding-block: clamp(40px, 7vw, 96px) clamp(56px, 8vw, 112px); + padding-block: var(--md-sys-measurement-space500) var(--md-sys-measurement-space700); } .hero__grid { display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: center; - gap: clamp(32px, 5vw, 72px); + gap: var(--md-sys-measurement-space500); } .hero__copy { display: grid; - gap: 24px; + gap: var(--md-sys-measurement-space300); justify-items: start; } .hero__actions { display: flex; flex-wrap: wrap; - gap: 12px; - margin-top: 8px; + gap: var(--md-sys-measurement-space200); + margin-block-start: var(--md-sys-measurement-space100); } .hero__facts { display: flex; flex-wrap: wrap; - gap: 8px 20px; - margin: 8px 0 0; + gap: var(--md-sys-measurement-space100) var(--md-sys-measurement-space300); + margin: var(--md-sys-measurement-space100) 0 0; padding: 0; list-style: none; - color: var(--on-surface-variant); - font-size: 0.9375rem; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } .hero__facts li { display: inline-flex; align-items: center; - gap: 6px; + gap: var(--md-sys-measurement-space75); } .hero__facts .icon { - width: 18px; - height: 18px; - color: var(--success); + width: 20px; + height: 20px; + color: var(--md-sys-color-success); } .hero__visual { position: relative; - padding: 0 0 48px; + padding-block-end: var(--md-sys-measurement-space500); } .hero__visual .device--phone { position: absolute; - right: -2%; + inset-inline-end: -2%; bottom: 0; width: 25%; } +/* Two Expressive shapes behind the devices: decoration only. */ .hero__shape { position: absolute; z-index: -1; fill: currentColor; - color: var(--secondary-container); - opacity: 0.7; } .hero__shape--one { top: -8%; - right: -6%; + inset-inline-end: -6%; width: 46%; + color: var(--md-sys-color-secondary-container); rotate: 12deg; } .hero__shape--two { bottom: -4%; - left: -10%; + inset-inline-start: -10%; width: 30%; - color: var(--tertiary-container); - opacity: 0.35; + color: var(--md-sys-color-surface-container-highest); } -@media (max-width: 960px) { +@media (width >= 600px) { + .hero { + padding-block: var(--md-sys-measurement-space800) var(--md-sys-measurement-space900); + } +} + +@media (width >= 840px) { .hero__grid { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); + gap: var(--md-sys-measurement-space600); } .hero__visual { - padding-bottom: 36px; + padding-block-end: var(--md-sys-measurement-space600); + } +} + +@media (width >= 1200px) { + .hero { + padding-block: calc(var(--md-sys-measurement-space600) * 2) calc(var(--md-sys-measurement-space700) * 2); + } + + .hero__grid { + gap: var(--md-sys-measurement-space900); } } /* ---------------------------------------------------------------- sections */ .section { - padding-block: clamp(56px, 8vw, 104px); + padding-block: var(--site-section-space); overflow-x: clip; } .section--tint { - background: var(--surface-container-low); + background-color: var(--md-sys-color-surface-container-low); } .section__head { display: grid; - gap: 16px; + gap: var(--md-sys-measurement-space200); max-width: 760px; - margin-bottom: clamp(32px, 5vw, 56px); + margin-block-end: var(--md-sys-measurement-space400); } .section__head--center { @@ -604,61 +976,73 @@ code { justify-items: center; } +@media (width >= 600px) { + .section__head { + margin-block-end: var(--md-sys-measurement-space600); + } +} + +/* M3's outlined card: surface, an outline-variant edge, the medium corner, and no shape change. */ +.reason, +.step, +.feature, +.faq details, +.contact, +.table-scroll { + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: var(--md-sys-shape-corner-md); + background-color: var(--md-sys-color-surface); +} + /* "Why run your own": a statement beside four reasons. */ .why { display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); - gap: clamp(32px, 5vw, 72px); + gap: var(--md-sys-measurement-space500); align-items: start; } .why__statement { - position: sticky; - top: 104px; display: grid; - gap: 20px; + gap: var(--md-sys-measurement-space300); } .reasons { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px; + gap: var(--md-sys-measurement-space200); } .reason { display: grid; - gap: 12px; + gap: var(--md-sys-measurement-space200); align-content: start; - padding: 28px; - border-radius: var(--corner-xl); - background: var(--surface-container); -} - -.reason:nth-child(1) { - border-radius: var(--corner-xxl) var(--corner-xl) var(--corner-xl) var(--corner-xl); -} - -.reason:nth-child(4) { - border-radius: var(--corner-xl) var(--corner-xl) var(--corner-xxl) var(--corner-xl); + padding: var(--md-sys-measurement-space300); } .reason p { - color: var(--on-surface-variant); + color: var(--md-sys-color-on-surface-variant); } -@media (max-width: 960px) { +@media (width >= 600px) { + .reasons { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (width >= 840px) { .why { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); + gap: var(--md-sys-measurement-space600); } .why__statement { - position: static; + position: sticky; + top: calc(var(--site-bar-height) + var(--md-sys-measurement-space400)); } } -@media (max-width: 600px) { - .reasons { - grid-template-columns: 1fr; +@media (width >= 1200px) { + .why { + gap: var(--md-sys-measurement-space900); } } @@ -669,7 +1053,7 @@ code { place-items: center; width: 56px; height: 56px; - color: var(--on-primary-container); + color: var(--md-sys-color-on-primary-container); } .badge-icon > svg:first-child { @@ -677,37 +1061,34 @@ code { inset: 0; width: 100%; height: 100%; - color: var(--primary-container); + color: var(--md-sys-color-primary-container); fill: currentColor; } .badge-icon .icon { position: relative; - width: 26px; - height: 26px; } .badge-icon--secondary { - color: var(--on-secondary-container); + color: var(--md-sys-color-on-secondary-container); } .badge-icon--secondary > svg:first-child { - color: var(--secondary-container); + color: var(--md-sys-color-secondary-container); } .badge-icon--tertiary { - color: var(--on-tertiary-container); + color: var(--md-sys-color-on-tertiary-container); } .badge-icon--tertiary > svg:first-child { - color: var(--tertiary-container); + color: var(--md-sys-color-tertiary-container); } /* How it works: three steps. */ .steps { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 16px; + gap: var(--md-sys-measurement-space200); margin: 0; padding: 0; list-style: none; @@ -717,60 +1098,45 @@ code { .step { position: relative; display: grid; - gap: 14px; + gap: var(--md-sys-measurement-space200); align-content: start; - padding: 32px 28px 28px; - border-radius: var(--corner-xl); - background: var(--surface-container-lowest); + padding: var(--md-sys-measurement-space300); counter-increment: step; } -@media (prefers-color-scheme: dark) { - .step { - background: var(--surface-container); - } -} - +/* The step's number, a decoration beside its title. */ .step::before { content: counter(step); position: absolute; - top: 24px; - right: 28px; - color: var(--outline-variant); - font-size: 3rem; - font-weight: 500; - line-height: 1; + inset-block-start: var(--md-sys-measurement-space300); + inset-inline-end: var(--md-sys-measurement-space300); + color: var(--md-sys-color-outline); + font: var(--md-sys-typescale-display-md); + letter-spacing: var(--md-sys-typescale-display-md-tracking); } .step p { - color: var(--on-surface-variant); + color: var(--md-sys-color-on-surface-variant); } -@media (max-width: 840px) { +@media (width >= 840px) { .steps { - grid-template-columns: 1fr; + grid-template-columns: repeat(3, minmax(0, 1fr)); } } /* Features. */ .features { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 16px; + gap: var(--md-sys-measurement-space200); } .feature { display: grid; grid-template-columns: auto 1fr; - gap: 4px 16px; + gap: var(--md-sys-measurement-space50) var(--md-sys-measurement-space200); align-content: start; - padding: 24px; - border-radius: var(--corner-xl); - background: var(--surface-container-low); -} - -.section--tint .feature { - background: var(--surface-container); + padding: var(--md-sys-measurement-space300); } .feature .badge-icon { @@ -779,31 +1145,28 @@ code { height: 48px; } -.feature .badge-icon .icon { - width: 22px; - height: 22px; -} - .feature h3 { align-self: end; - font-size: 1.0625rem; - line-height: 1.4; + font: var(--md-sys-typescale-title-md); + letter-spacing: var(--md-sys-typescale-title-md-tracking); } .feature p { - color: var(--on-surface-variant); - font-size: 0.9375rem; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } -@media (max-width: 960px) { +@media (width >= 600px) { .features { grid-template-columns: repeat(2, minmax(0, 1fr)); } } -@media (max-width: 600px) { +/* Three columns only from large: at expanded they leave too little room for the text. */ +@media (width >= 1200px) { .features { - grid-template-columns: 1fr; + grid-template-columns: repeat(3, minmax(0, 1fr)); } } @@ -813,78 +1176,21 @@ code { display: flex; flex-wrap: wrap; justify-content: center; - gap: 12px 16px; - margin-bottom: 40px; -} - -/* M3 segmented buttons. */ -.segmented { - display: inline-flex; - border: 1px solid var(--outline); - border-radius: var(--corner-full); -} - -.segmented button { - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 40px; - padding: 8px 18px; - border: 0; - background: transparent; - color: var(--on-surface); - font: inherit; - font-size: 0.875rem; - font-weight: 500; - cursor: pointer; -} - -.segmented button + button { - border-left: 1px solid var(--outline); -} - -.segmented button:first-child { - border-radius: var(--corner-full) 0 0 var(--corner-full); -} - -.segmented button:last-child { - border-radius: 0 var(--corner-full) var(--corner-full) 0; -} - -.segmented button:hover { - background: color-mix(in srgb, var(--on-surface) 8%, transparent); -} - -.segmented button[aria-pressed="true"] { - background: var(--secondary-container); - color: var(--on-secondary-container); -} - -.segmented .icon { - display: none; - width: 18px; - height: 18px; -} - -.segmented button[aria-pressed="true"] .icon { - display: block; + gap: var(--md-sys-measurement-space200) var(--md-sys-measurement-space300); + margin-block-end: var(--md-sys-measurement-space500); } .gallery { display: grid; - gap: 40px 32px; + gap: var(--md-sys-measurement-space500) var(--md-sys-measurement-space400); margin: 0; padding: 0; list-style: none; } -.gallery--desktop { - grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: clamp(40px, 6vw, 88px); -} - .gallery--phone { - grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--md-sys-measurement-space200); max-width: 1040px; margin-inline: auto; } @@ -895,33 +1201,42 @@ code { .shot { display: grid; - gap: 16px; + gap: var(--md-sys-measurement-space200); margin: 0; } .shot figcaption { display: grid; - gap: 2px; + gap: var(--md-sys-measurement-space25); text-align: center; } .shot figcaption strong { - font-weight: 500; + font: var(--md-sys-typescale-title-md); + letter-spacing: var(--md-sys-typescale-title-md-tracking); } .shot figcaption span { - color: var(--on-surface-variant); - font-size: 0.9375rem; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } -@media (max-width: 840px) { +@media (width >= 840px) { .gallery--desktop { - grid-template-columns: 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--md-sys-measurement-space600); } .gallery--phone { - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 32px 20px; + grid-template-columns: repeat(4, minmax(0, 1fr)); + column-gap: var(--md-sys-measurement-space400); + } +} + +@media (width >= 1200px) { + .gallery--desktop { + column-gap: calc(var(--md-sys-measurement-space400) + var(--md-sys-measurement-space700)); } } @@ -929,137 +1244,117 @@ code { .compare { display: grid; - gap: 48px; + gap: var(--md-sys-measurement-space600); } .compare__block { display: grid; - gap: 16px; + gap: var(--md-sys-measurement-space200); } .compare__block h3 { - font-size: 1.375rem; + font: var(--md-sys-typescale-title-lg); + letter-spacing: var(--md-sys-typescale-title-lg-tracking); } .compare__block > p { - color: var(--on-surface-variant); + color: var(--md-sys-color-on-surface-variant); } .table-scroll { overflow-x: auto; - border: 1px solid var(--outline-variant); - border-radius: var(--corner-xl); - background: var(--surface-container-lowest); -webkit-overflow-scrolling: touch; } -@media (prefers-color-scheme: dark) { - .table-scroll { - background: var(--surface-container-low); - } -} - -.table-scroll:focus-visible { - outline-offset: 2px; - border-radius: var(--corner-xl); -} - .compare table { width: 100%; min-width: 860px; border-collapse: separate; border-spacing: 0; - font-size: 0.9375rem; + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } .compare caption { - padding: 14px 20px; - border-bottom: 1px solid var(--outline-variant); - color: var(--on-surface-variant); - font-size: 0.875rem; - text-align: left; + padding: var(--md-sys-measurement-space200) var(--md-sys-measurement-space300); + border-block-end: 1px solid var(--md-sys-color-outline-variant); + color: var(--md-sys-color-on-surface-variant); + text-align: start; } .compare th, .compare td { - padding: 12px 16px; - border-bottom: 1px solid color-mix(in srgb, var(--outline-variant) 60%, transparent); - text-align: left; + padding: calc(var(--md-sys-measurement-space100) + var(--md-sys-measurement-space50)) var(--md-sys-measurement-space200); + border-block-end: 1px solid var(--md-sys-color-outline-variant); + text-align: start; vertical-align: top; } .compare tbody tr:last-child > * { - border-bottom: 0; + border-block-end: 0; +} + +.compare thead tr { + background-color: var(--md-sys-color-surface); } .compare thead th { - color: var(--on-surface); - font-weight: 500; + color: var(--md-sys-color-on-surface); + font: var(--md-sys-typescale-title-sm); + letter-spacing: var(--md-sys-typescale-title-sm-tracking); white-space: nowrap; } .compare tbody th { position: sticky; - left: 0; + inset-inline-start: 0; z-index: 1; min-width: 170px; - background: var(--surface-container-lowest); - color: var(--on-surface-variant); - font-weight: 500; -} - -@media (prefers-color-scheme: dark) { - .compare tbody th { - background: var(--surface-container-low); - } + background-color: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-title-sm); + letter-spacing: var(--md-sys-typescale-title-sm-tracking); } .compare thead th:first-child { position: sticky; - left: 0; + inset-inline-start: 0; z-index: 2; - background: inherit; + background-color: var(--md-sys-color-surface); } -.compare thead tr { - background: var(--surface-container-lowest); -} - -@media (prefers-color-scheme: dark) { - .compare thead tr { - background: var(--surface-container-low); - } -} - -/* SealShare's own column. */ +/* SealShare's own column: its heading in the secondary-container pair, its cells a tone up. */ .compare .is-us { - background: color-mix(in srgb, var(--secondary-container) 55%, transparent); - color: var(--on-surface); + background-color: var(--md-sys-color-surface-container-high); + color: var(--md-sys-color-on-surface); } .compare thead .is-us { - color: var(--on-secondary-container); + background-color: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); } .compare__sources { display: grid; - gap: 6px; + gap: var(--md-sys-measurement-space100); margin: 0; - padding-left: 20px; - color: var(--on-surface-variant); - font-size: 0.875rem; + padding-inline-start: var(--md-sys-measurement-space300); + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); overflow-wrap: anywhere; } .compare__note { - color: var(--on-surface-variant); - font-size: 0.875rem; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } /* The numbered notes under the tables read as one block. */ .compare__notes { display: grid; - gap: 8px; + gap: var(--md-sys-measurement-space100); } .compare__notes sup, @@ -1071,20 +1366,19 @@ code { .install { display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); - gap: clamp(32px, 5vw, 64px); + gap: var(--md-sys-measurement-space500); align-items: start; } .install__copy { display: grid; - gap: 20px; + gap: var(--md-sys-measurement-space300); justify-items: start; } .install__list { display: grid; - gap: 12px; + gap: var(--md-sys-measurement-space200); margin: 0; padding: 0; list-style: none; @@ -1093,55 +1387,65 @@ code { .install__list li { display: grid; grid-template-columns: auto 1fr; - gap: 12px; - color: var(--on-surface-variant); + gap: var(--md-sys-measurement-space200); + color: var(--md-sys-color-on-surface-variant); } .install__list .icon { - width: 22px; - height: 22px; - color: var(--primary); + color: var(--md-sys-color-primary); } +/* The compose commands: a filled card (surface-container-highest) in the theme's own ink. */ .code { overflow: hidden; - border-radius: var(--corner-xl); - background: var(--code-surface); - color: var(--code-ink); - box-shadow: var(--shadow-1); + border-radius: var(--md-sys-shape-corner-md); + background-color: var(--md-sys-color-surface-container-highest); + color: var(--md-sys-color-on-surface); } .code__bar { display: flex; align-items: center; - gap: 8px; - padding: 14px 20px; - border-bottom: 1px solid rgb(255 255 255 / 0.1); - color: rgb(234 225 255 / 0.7); - font-size: 0.875rem; + gap: var(--md-sys-measurement-space100); + padding: var(--md-sys-measurement-space200) var(--md-sys-measurement-space300); + border-block-end: 1px solid var(--md-sys-color-outline-variant); + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-label-lg); + letter-spacing: var(--md-sys-typescale-label-lg-tracking); } .code__bar .icon { - width: 18px; - height: 18px; + width: 20px; + height: 20px; } .code pre { margin: 0; - padding: 20px; + padding: var(--md-sys-measurement-space300); overflow-x: auto; - font-family: var(--mono); + font-family: var(--md-ref-typeface-mono); font-size: 0.875rem; - line-height: 1.7; + line-height: 1.5rem; +} + +.code pre code { + font-size: inherit; } .code .comment { - color: rgb(234 225 255 / 0.55); + color: var(--md-sys-color-on-surface-variant); } -@media (max-width: 960px) { +@media (width >= 840px) { .install { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); + gap: var(--md-sys-measurement-space600); + } +} + +@media (width >= 1200px) { + .install { + gap: var(--md-sys-measurement-space800); } } @@ -1149,42 +1453,37 @@ code { .faq { display: grid; - gap: 8px; + gap: var(--md-sys-measurement-space100); max-width: 860px; margin-inline: auto; } -.faq details { - border-radius: var(--corner-lg); - background: var(--surface-container); - transition: border-radius 250ms var(--ease-spatial); -} - -.faq details[open] { - border-radius: var(--corner-xl); -} - .faq summary { display: flex; align-items: center; justify-content: space-between; - gap: 16px; - min-height: 64px; - padding: 16px 24px; - border-radius: inherit; - font-size: 1.0625rem; - font-weight: 500; + gap: var(--md-sys-measurement-space200); + min-height: var(--md-sys-measurement-space800); + padding: var(--md-sys-measurement-space200) var(--md-sys-measurement-space300); + border-radius: calc(var(--md-sys-shape-corner-md) - 1px); + font: var(--md-sys-typescale-title-md); + letter-spacing: var(--md-sys-typescale-title-md-tracking); cursor: pointer; list-style: none; } +.faq details[open] summary { + border-end-start-radius: 0; + border-end-end-radius: 0; +} + .faq summary::-webkit-details-marker { display: none; } .faq summary .icon { - color: var(--on-surface-variant); - transition: rotate 250ms var(--ease-spatial); + color: var(--md-sys-color-on-surface-variant); + transition: rotate var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast); } .faq details[open] summary .icon { @@ -1193,9 +1492,9 @@ code { .faq__answer { display: grid; - gap: 12px; - padding: 0 24px 24px; - color: var(--on-surface-variant); + gap: var(--md-sys-measurement-space200); + padding: 0 var(--md-sys-measurement-space300) var(--md-sys-measurement-space300); + color: var(--md-sys-color-on-surface-variant); } /* ---------------------------------------------------------------- contact */ @@ -1205,154 +1504,148 @@ code { flex-wrap: wrap; align-items: center; justify-content: space-between; - gap: 24px; - padding: clamp(28px, 5vw, 48px); - border-radius: var(--corner-xxl); - background: var(--primary-container); - color: var(--on-primary-container); + gap: var(--md-sys-measurement-space300); + padding: var(--md-sys-measurement-space400) var(--md-sys-measurement-space300); } .contact > div { display: grid; - gap: 10px; + gap: var(--md-sys-measurement-space100); } .contact p { - opacity: 0.9; + color: var(--md-sys-color-on-surface-variant); } .contact a:not(.btn) { display: inline-flex; align-items: center; - gap: 8px; - color: inherit; - font-weight: 500; + gap: var(--md-sys-measurement-space100); + min-block-size: var(--md-sys-measurement-space600); + width: fit-content; + font: var(--md-sys-typescale-title-md); + letter-spacing: var(--md-sys-typescale-title-md-tracking); } -.contact .btn--filled { - background: var(--on-primary-container); - color: var(--primary-container); -} - -.contact .btn--filled:hover { - background: color-mix(in srgb, var(--on-primary-container) 88%, var(--primary-container)); +@media (width >= 840px) { + .contact { + padding: var(--md-sys-measurement-space600); + } } /* ---------------------------------------------------------------- footer */ .footer { - padding-block: 56px 40px; - background: var(--surface-container-low); - color: var(--on-surface-variant); - font-size: 0.9375rem; + padding-block: var(--md-sys-measurement-space700) var(--md-sys-measurement-space500); + background-color: var(--md-sys-color-surface-container-low); + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } .footer__grid { display: grid; - grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr)); - gap: 32px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--md-sys-measurement-space400); +} + +.footer__grid > :first-child { + grid-column: 1 / -1; } .footer__blurb { max-width: 380px; - margin-top: 12px; + margin-block-start: var(--md-sys-measurement-space100); } .footer h2 { - margin-bottom: 12px; - color: var(--on-surface); - font-size: 1rem; + margin-block-end: var(--md-sys-measurement-space100); + color: var(--md-sys-color-on-surface); + font: var(--md-sys-typescale-title-sm); + letter-spacing: var(--md-sys-typescale-title-sm-tracking); } .footer ul { display: grid; - gap: 8px; margin: 0; padding: 0; list-style: none; } +.footer ul a { + display: inline-flex; + align-items: center; + min-block-size: var(--md-sys-measurement-space500); +} + .footer a { - color: var(--on-surface-variant); + color: var(--md-sys-color-on-surface-variant); text-decoration: none; } .footer a:hover { - color: var(--on-surface); - text-decoration: underline; + color: var(--md-sys-color-on-surface); + text-decoration-line: underline; } .footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; - gap: 8px 24px; - margin-top: 40px; - padding-top: 24px; - border-top: 1px solid var(--outline-variant); - font-size: 0.875rem; + gap: var(--md-sys-measurement-space100) var(--md-sys-measurement-space300); + margin-block-start: var(--md-sys-measurement-space500); + padding-block-start: var(--md-sys-measurement-space300); + border-block-start: 1px solid var(--md-sys-color-outline-variant); } -@media (max-width: 720px) { +@media (width >= 600px) { .footer__grid { - grid-template-columns: 1fr 1fr; + grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr)); } .footer__grid > :first-child { - grid-column: 1 / -1; + grid-column: auto; } } /* ---------------------------------------------------------------- legal page */ .legal { - padding-block: clamp(40px, 6vw, 80px); + padding-block: var(--md-sys-measurement-space500) var(--md-sys-measurement-space800); } .legal article { display: grid; - gap: 16px; + gap: var(--md-sys-measurement-space200); max-width: 760px; margin-inline: auto; } .legal h1 { - margin-bottom: 4px; + margin-block-end: var(--md-sys-measurement-space50); } .legal h2 { - margin-top: 24px; - font-size: 1.375rem; -} - -.legal p, -.legal li { - color: var(--on-surface-variant); + margin-block-start: var(--md-sys-measurement-space300); + font: var(--md-sys-typescale-title-lg); + letter-spacing: var(--md-sys-typescale-title-lg-tracking); } .legal ul { display: grid; - gap: 8px; + gap: var(--md-sys-measurement-space100); margin: 0; - padding-left: 22px; + padding-inline-start: var(--md-sys-measurement-space300); } .legal .meta { - color: var(--on-surface-variant); - font-size: 0.9375rem; + color: var(--md-sys-color-on-surface-variant); + font: var(--md-sys-typescale-body-md); + letter-spacing: var(--md-sys-typescale-body-md-tracking); } -/* ---------------------------------------------------------------- motion */ - -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } - - *, - *::before, - *::after { - transition-duration: 0.01ms !important; - animation-duration: 0.01ms !important; +@media (width >= 600px) { + .legal { + padding-block: var(--md-sys-measurement-space800) calc(var(--md-sys-measurement-space500) * 2); } } diff --git a/website/index.html b/website/index.html index 7748b0e..8f08196 100644 --- a/website/index.html +++ b/website/index.html @@ -7,6 +7,83 @@ + @@ -25,7 +102,7 @@ - + @@ -112,8 +197,9 @@
- - + + SealShare's upload page with files selected and a generated password in the share options @@ -123,8 +209,9 @@
- - + + The download page on a phone @@ -286,13 +373,13 @@

The same interface on a laptop and a phone, in light or dark.