feat(website): adopt expressive material design
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AppleIcon from "~icons/simple-icons/apple";
|
import AppleIcon from "~icons/simple-icons/apple";
|
||||||
import GooglePlayIcon from "~icons/simple-icons/googleplay";
|
import GooglePlayIcon from "~icons/simple-icons/googleplay";
|
||||||
import LinuxIcon from "~icons/simple-icons/linux";
|
import LinuxIcon from "~icons/devicon-plain/linux";
|
||||||
import AmazonIcon from "~icons/cib/amazon";
|
import AmazonIcon from "~icons/cib/amazon";
|
||||||
import ChevronDownIcon from "~icons/heroicons/chevron-down-solid";
|
import ChevronDownIcon from "~icons/heroicons/chevron-down-solid";
|
||||||
import WindowsIcon from "./WindowsIcon.svelte";
|
import WindowsIcon from "./WindowsIcon.svelte";
|
||||||
@@ -133,80 +133,82 @@
|
|||||||
.download-buttons {
|
.download-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-buttons,
|
.store-buttons,
|
||||||
.desktop-buttons {
|
.desktop-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
gap: 0.5rem;
|
||||||
|
|
||||||
.store-buttons {
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktop-buttons {
|
|
||||||
gap: 0.625rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button,
|
.store-button,
|
||||||
.desktop-button {
|
.desktop-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
min-height: 2.875rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 9999px;
|
justify-content: center;
|
||||||
font-size: 0.875rem;
|
gap: 0.625rem;
|
||||||
line-height: 1.25rem;
|
border-radius: var(--radius-pill);
|
||||||
transition: background-color 150ms ease, color 150ms ease;
|
padding-inline: 1rem;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
transition:
|
||||||
|
border-radius var(--motion-expressive) var(--ease-standard),
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button {
|
.store-button {
|
||||||
gap: 0.625rem;
|
color: var(--color-on-primary);
|
||||||
padding: 0.75rem 1.25rem;
|
background: var(--color-text);
|
||||||
color: #111827;
|
|
||||||
background: #fff;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button:hover {
|
.store-button:hover,
|
||||||
background: #f3f4f6;
|
.store-button:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: #fff;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button :global(svg) {
|
.store-button :global(svg) {
|
||||||
width: 1.25rem;
|
width: 1.125rem;
|
||||||
height: 1.25rem;
|
height: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-button {
|
.desktop-button {
|
||||||
gap: 0.5rem;
|
color: var(--color-text-muted);
|
||||||
padding: 0.5rem 1rem;
|
background: var(--color-surface-highest);
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
background: color-mix(in srgb, var(--color-surface) 80%, transparent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-button:hover,
|
.desktop-button:hover,
|
||||||
|
.desktop-button:focus-visible,
|
||||||
.linux-button.active {
|
.linux-button.active {
|
||||||
|
color: var(--color-text);
|
||||||
background: var(--color-surface-hover);
|
background: var(--color-surface-hover);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-button:hover,
|
||||||
|
.desktop-button:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.desktop-button :global(svg) {
|
.desktop-button :global(svg) {
|
||||||
width: 0.875rem;
|
width: 0.9375rem;
|
||||||
height: 0.875rem;
|
height: 0.9375rem;
|
||||||
opacity: 0.7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-control {
|
.linux-control {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-button {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chevron {
|
.chevron {
|
||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
height: 0.75rem;
|
height: 0.75rem;
|
||||||
transition: transform 300ms ease;
|
transition: transform var(--motion-normal) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chevron.open {
|
.chevron.open {
|
||||||
@@ -216,55 +218,73 @@
|
|||||||
.chevron :global(svg) {
|
.chevron :global(svg) {
|
||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
height: 0.75rem;
|
height: 0.75rem;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-menu {
|
.linux-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: calc(100% + 0.5rem);
|
||||||
left: 0;
|
right: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
width: 14rem;
|
width: min(16rem, calc(100vw - 2rem));
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--color-border);
|
border-radius: var(--radius-lg);
|
||||||
border-radius: 1rem;
|
padding: 0.375rem;
|
||||||
background: var(--color-surface);
|
background: var(--color-surface-highest);
|
||||||
box-shadow:
|
|
||||||
0 20px 25px -5px rgb(0 0 0 / 0.1),
|
|
||||||
0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 150ms ease, visibility 150ms ease;
|
transform: translateY(0.5rem);
|
||||||
|
transition:
|
||||||
|
opacity var(--motion-normal) var(--ease-standard),
|
||||||
|
transform var(--motion-normal) var(--ease-standard),
|
||||||
|
visibility var(--motion-normal) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-menu.open {
|
.linux-menu.open {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-separator {
|
.linux-separator {
|
||||||
border-top: 1px solid var(--color-border);
|
height: var(--group-gap);
|
||||||
|
margin-block: 0.375rem;
|
||||||
|
background: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-arch-label {
|
.linux-arch-label {
|
||||||
padding: 0.625rem 1rem 0.25rem;
|
padding: 0.625rem 0.75rem 0.375rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-subtle);
|
||||||
font-size: 0.75rem;
|
font-family: var(--font-utility);
|
||||||
font-weight: 600;
|
font-size: 0.6875rem;
|
||||||
letter-spacing: 0.025em;
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-menu-item {
|
.linux-menu-item {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0.5rem 1rem;
|
border-radius: var(--radius-md);
|
||||||
font-size: 0.875rem;
|
padding: 0.625rem 0.75rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
transition: background-color 150ms ease;
|
transition:
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.linux-menu-item:hover {
|
.linux-menu-item:hover,
|
||||||
background: var(--color-surface-hover);
|
.linux-menu-item:focus-visible {
|
||||||
|
color: var(--color-text);
|
||||||
|
background: rgb(237 237 237 / 0.12);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 460px) {
|
||||||
|
.store-button,
|
||||||
|
.desktop-button {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
|
|
||||||
<div class="faq-list">
|
<div class="faq-list">
|
||||||
{#each faqs as faq, i}
|
{#each faqs as faq, i}
|
||||||
<ScrollReveal delay={i * 50}>
|
<ScrollReveal delay={i * 50} class="faq-row">
|
||||||
<div id={faq.id} class="glass-card faq-card">
|
<div id={faq.id} class="flat-card faq-card">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="faq-toggle"
|
class="faq-toggle"
|
||||||
@@ -72,118 +72,154 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.faq-section {
|
.faq-section {
|
||||||
max-width: 64rem;
|
width: min(100%, var(--page-width));
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding: 4rem 1.5rem;
|
padding: clamp(4rem, 9vw, 8rem) var(--page-gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-label {
|
.section-label {
|
||||||
margin-bottom: 0.75rem;
|
width: fit-content;
|
||||||
color: var(--color-accent);
|
margin-bottom: 1rem;
|
||||||
font-size: 0.875rem;
|
border-radius: var(--radius-full);
|
||||||
font-weight: 500;
|
padding: 0.5rem 0.875rem;
|
||||||
letter-spacing: 0.025em;
|
color: var(--color-text-muted);
|
||||||
line-height: 1.25rem;
|
background: var(--color-surface);
|
||||||
text-transform: uppercase;
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
|
max-width: 12ch;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-size: 2.25rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(2.5rem, 7vw, 4.75rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2.5rem;
|
letter-spacing: -0.045em;
|
||||||
|
line-height: 1;
|
||||||
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-description {
|
.section-description {
|
||||||
max-width: 32rem;
|
max-width: 34rem;
|
||||||
margin-bottom: 2.5rem;
|
margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 1.125rem;
|
font-size: clamp(1rem, 2vw, 1.125rem);
|
||||||
line-height: 1.75rem;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-list {
|
.faq-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.625rem;
|
gap: var(--group-gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-card {
|
.faq-card {
|
||||||
border-radius: 1rem;
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius-xs);
|
||||||
|
background: var(--color-surface);
|
||||||
|
transition: background-color var(--motion-normal) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-card:has(.faq-toggle[aria-expanded="true"]) {
|
||||||
|
background: var(--color-surface-high);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.faq-row:first-child) .faq-card {
|
||||||
|
border-radius: var(--radius-lg) var(--radius-lg) var(--radius-xs) var(--radius-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.faq-row:last-child) .faq-card {
|
||||||
|
border-radius: var(--radius-xs) var(--radius-xs) var(--radius-lg) var(--radius-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-toggle {
|
.faq-toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 4.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
padding: 1rem 1.5rem;
|
padding: 0.75rem 1rem 0.75rem clamp(1rem, 4vw, 1.5rem);
|
||||||
|
border-radius: inherit;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
transition: background-color var(--motion-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-toggle:not([aria-expanded="true"]):hover {
|
||||||
|
background: rgb(237 237 237 / 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-toggle:focus-visible {
|
||||||
|
background: rgb(237 237 237 / 0.14);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-question {
|
.faq-question {
|
||||||
font-weight: 500;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(0.9375rem, 2vw, 1.0625rem);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-icon {
|
.faq-icon {
|
||||||
|
display: flex;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 1.25rem;
|
align-items: center;
|
||||||
height: 1.25rem;
|
justify-content: center;
|
||||||
color: var(--color-accent);
|
border-radius: var(--radius-full);
|
||||||
|
color: var(--color-text);
|
||||||
|
background: rgb(237 237 237 / 0.1);
|
||||||
|
transition:
|
||||||
|
border-radius var(--motion-normal) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-toggle[aria-expanded="true"] .faq-icon {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: rgb(237 237 237 / 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-icon :global(svg) {
|
.faq-icon :global(svg) {
|
||||||
width: 1.25rem;
|
width: 1.125rem;
|
||||||
height: 1.25rem;
|
height: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-answer {
|
.faq-answer {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 0fr;
|
grid-template-rows: 0fr;
|
||||||
transition: grid-template-rows 200ms ease;
|
transition: grid-template-rows var(--motion-normal) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-answer.open {
|
.faq-answer.open {
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-answer > div {
|
.faq-answer > div {
|
||||||
overflow: hidden;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-answer-content {
|
.faq-answer-content {
|
||||||
padding: 0 1.5rem 1rem;
|
max-width: 50rem;
|
||||||
|
padding: 0 clamp(1rem, 4vw, 1.5rem) 1.5rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
line-height: 1.625;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-answer-content :global(a) {
|
.faq-answer-content :global(a) {
|
||||||
color: var(--color-accent);
|
color: var(--color-text);
|
||||||
}
|
|
||||||
|
|
||||||
.faq-answer-content :global(a:hover) {
|
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-text-subtle);
|
||||||
|
text-underline-offset: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
.faq-answer-content :global(a:hover),
|
||||||
.faq-section {
|
.faq-answer-content :global(a:focus-visible) {
|
||||||
padding-block: 6rem;
|
text-decoration-color: var(--color-text);
|
||||||
}
|
outline: none;
|
||||||
|
|
||||||
.section-description {
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.faq-section {
|
|
||||||
padding-block: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-heading {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,48 +3,33 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<div class="footer-inner">
|
<div class="footer-content">
|
||||||
<!-- Gradient rule -->
|
<div class="footer-brand">
|
||||||
<div class="gradient-rule"></div>
|
<span class="footer-logo"><Logo /></span>
|
||||||
|
<span>Plezy</span>
|
||||||
<div class="footer-content">
|
|
||||||
<!-- Logo + name -->
|
|
||||||
<div class="footer-brand">
|
|
||||||
<span class="footer-logo"><Logo /></span>
|
|
||||||
<span>Plezy</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Links -->
|
|
||||||
<nav class="footer-nav">
|
|
||||||
<a href="https://github.com/edde746/plezy" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
||||||
<a href="/privacy">Privacy Policy</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<nav class="footer-nav" aria-label="Footer navigation">
|
||||||
|
<a href="https://github.com/edde746/plezy" target="_blank" rel="noopener noreferrer">GitHub</a>
|
||||||
|
<a href="/privacy">Privacy</a>
|
||||||
|
<a href="#home">Back to top</a>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.site-footer {
|
.site-footer {
|
||||||
padding: 2.5rem 1.5rem;
|
padding: clamp(3rem, 7vw, 5rem) var(--page-gutter);
|
||||||
}
|
|
||||||
|
|
||||||
.footer-inner {
|
|
||||||
max-width: 64rem;
|
|
||||||
margin-inline: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gradient-rule {
|
|
||||||
height: 1px;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
background: linear-gradient(to right, transparent, var(--color-border), transparent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-content {
|
.footer-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: min(100%, var(--page-width));
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 2rem;
|
gap: 1.5rem;
|
||||||
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-brand {
|
.footer-brand {
|
||||||
@@ -52,42 +37,43 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.625rem;
|
gap: 0.625rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-weight: 500;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-logo :global(svg) {
|
.footer-logo :global(svg) {
|
||||||
width: 1.75rem;
|
width: 1.5rem;
|
||||||
height: 1.75rem;
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-nav {
|
.footer-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2rem;
|
gap: 0.25rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
font-size: 0.8125rem;
|
||||||
line-height: 1.25rem;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-nav a {
|
.footer-nav a {
|
||||||
transition: color 150ms ease;
|
border-radius: var(--radius-full);
|
||||||
|
padding: 0.625rem 0.875rem;
|
||||||
|
transition:
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-nav a:hover {
|
.footer-nav a:hover,
|
||||||
|
.footer-nav a:focus-visible {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
background: rgb(237 237 237 / 0.12);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.site-footer {
|
|
||||||
padding-block: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gradient-rule {
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-content {
|
.footer-content {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,65 +6,74 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section id="home" class="hero-section">
|
<section id="home" class="hero-section">
|
||||||
<!-- Giant background logo watermark -->
|
<div class="hero-shell">
|
||||||
<div class="hero-watermark" aria-hidden="true">
|
<div class="hero-grid">
|
||||||
<Logo gradient={false} />
|
<div class="hero-copy">
|
||||||
</div>
|
<div class="brand-row fade-in">
|
||||||
|
<span class="brand-logo"><Logo /></span>
|
||||||
|
<span>Plezy</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Ambient glow -->
|
<h1 class="hero-title fade-in" style="animation-delay: 60ms;">
|
||||||
<div class="ambient-glow"></div>
|
A beautiful client for <span>Plex & Jellyfin</span>
|
||||||
|
</h1>
|
||||||
|
<p class="hero-description fade-in" style="animation-delay: 120ms;">
|
||||||
|
Stream your media library with HDR, Dolby Vision, offline downloads, and an experience built for every
|
||||||
|
screen.
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- Content: two-column on desktop -->
|
<div id="download" class="download-area fade-in" style="animation-delay: 180ms;">
|
||||||
<div class="hero-content">
|
<DownloadButtons />
|
||||||
<!-- Left: text + buttons -->
|
</div>
|
||||||
<div class="hero-copy">
|
|
||||||
<div class="brand-row fade-in" style="animation-delay: 0ms;">
|
|
||||||
<span class="brand-logo"><Logo /></span>
|
|
||||||
<span class="brand-name">Plezy</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1
|
<div class="hero-visual fade-in" style="animation-delay: 240ms;">
|
||||||
class="hero-title fade-in"
|
<div class="shape-field" aria-hidden="true">
|
||||||
style="animation-delay: 100ms;"
|
<svg
|
||||||
>
|
class="hero-shape arch-shape"
|
||||||
<span class="title-line">A beautiful client for</span>
|
viewBox="0 0 1 1"
|
||||||
<span class="title-line gradient-text">Plex & Jellyfin</span>
|
preserveAspectRatio="none"
|
||||||
</h1>
|
data-material-shape="Arch"
|
||||||
|
>
|
||||||
<p
|
<path
|
||||||
class="hero-description fade-in"
|
d="M0.146447 0.146447C0.236929 0.055964 0.361929 0 0.5 0C0.776142 0 1 0.223858 1 0.5C1 0.619526 1 0.739052 1 0.858579C1 0.936684 0.936683 1 0.858579 1C0.619526 1 0.380474 1 0.141421 1C0.063317 1 0 0.936683 0 0.858579C0 0.739052 0 0.619526 0 0.5C0 0.361929 0.055964 0.236929 0.146447 0.146447Z"
|
||||||
style="animation-delay: 200ms;"
|
/>
|
||||||
>
|
</svg>
|
||||||
Stream your media library with HDR, Dolby Vision, offline downloads, and a gorgeous experience across all your
|
<svg
|
||||||
devices.
|
class="hero-shape cookie-shape"
|
||||||
</p>
|
viewBox="0 0 1 1"
|
||||||
|
data-material-shape="Cookie6Sided"
|
||||||
<div class="fade-in" style="animation-delay: 300ms;">
|
>
|
||||||
<DownloadButtons />
|
<path
|
||||||
</div>
|
d="M0.716525 0.8729C0.699648 0.882624 0.683956 0.894405 0.669829 0.908026C0.669329 0.908507 0.66883 0.908989 0.66833 0.90947C0.574432 1 0.425694 0.999869 0.331956 0.909174C0.303749 0.881883 0.269272 0.861938 0.231554 0.851089C0.230887 0.850897 0.23022 0.850705 0.229553 0.850514C0.104204 0.81446 0.029948 0.685584 0.061623 0.559056C0.071154 0.520984 0.071189 0.481153 0.061725 0.443063C0.061558 0.44239 0.06139 0.441717 0.061223 0.441043C0.029771 0.31446 0.104254 0.185715 0.229667 0.149882C0.267404 0.1391 0.301917 0.119215 0.330171 0.091974C0.330671 0.091493 0.33117 0.091011 0.331669 0.09053C0.425568 0 0.574306 0.000131 0.668044 0.090826C0.696251 0.118116 0.730728 0.138062 0.768446 0.148911C0.769113 0.149103 0.76978 0.149294 0.770446 0.149486C0.895796 0.18554 0.970052 0.314416 0.938377 0.440944C0.928846 0.479016 0.928811 0.518847 0.938275 0.556937C0.938442 0.55761 0.93861 0.558283 0.938777 0.558957C0.970229 0.685539 0.895746 0.814285 0.770333 0.850118C0.751464 0.855509 0.733402 0.863176 0.716525 0.8729Z"
|
||||||
</div>
|
/>
|
||||||
|
</svg>
|
||||||
<!-- Right: stacked phone mockup -->
|
<svg
|
||||||
<div class="phone-stage fade-in" style="animation-delay: 400ms;">
|
class="hero-shape diamond-shape"
|
||||||
<div class="phone-stack">
|
viewBox="0 0 1 1"
|
||||||
<!-- Shadow phone behind -->
|
data-material-shape="Diamond"
|
||||||
<div class="shadow-phone">
|
>
|
||||||
|
<path
|
||||||
|
d="M0.5 1C0.472713 1 0.445426 0.992561 0.421304 0.977684C0.398028 0.963329 0.371729 0.929255 0.319131 0.861106C0.252 0.774128 0.184869 0.68715 0.117738 0.600171C0.073881 0.543348 0.073881 0.46409 0.117738 0.407267C0.184869 0.320289 0.252 0.233311 0.319131 0.146333C0.371729 0.078184 0.398028 0.044109 0.421304 0.029754C0.469549 0 0.530451 0 0.578696 0.029754C0.601972 0.044109 0.628271 0.078184 0.680869 0.146333C0.748 0.233311 0.815131 0.320289 0.882262 0.407267C0.926119 0.46409 0.926119 0.543348 0.882262 0.600171C0.815131 0.68715 0.748 0.774128 0.680869 0.861106C0.628271 0.929255 0.601972 0.963329 0.578696 0.977684C0.554573 0.992561 0.527287 1 0.5 1Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="phone secondary-phone">
|
||||||
<enhanced:img
|
<enhanced:img
|
||||||
src={libraryImage}
|
src={libraryImage}
|
||||||
alt=""
|
alt="Plezy library screen on phone"
|
||||||
class="phone-image"
|
class="phone-image"
|
||||||
loading="lazy"
|
loading="eager"
|
||||||
sizes="(min-width: 768px) 300px, (min-width: 640px) 280px, 224px"
|
sizes="(min-width: 1024px) 250px, (min-width: 640px) 34vw, 42vw"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Main phone -->
|
<div class="phone primary-phone">
|
||||||
<div class="main-phone">
|
|
||||||
<enhanced:img
|
<enhanced:img
|
||||||
src={homeImage}
|
src={homeImage}
|
||||||
alt="Plezy home screen"
|
alt="Plezy home screen on phone"
|
||||||
class="phone-image"
|
class="phone-image"
|
||||||
fetchpriority="high"
|
fetchpriority="high"
|
||||||
sizes="(min-width: 768px) 300px, (min-width: 640px) 280px, 224px"
|
sizes="(min-width: 1024px) 280px, (min-width: 640px) 38vw, 48vw"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,196 +83,159 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.hero-section {
|
.hero-section {
|
||||||
position: relative;
|
overflow-x: clip;
|
||||||
display: flex;
|
padding-inline: var(--page-gutter);
|
||||||
min-height: 100dvh;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-watermark {
|
.hero-shell {
|
||||||
position: absolute;
|
width: min(100%, var(--page-width));
|
||||||
top: 50%;
|
|
||||||
right: -5rem;
|
|
||||||
width: 37.5rem;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
opacity: 0.04;
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-watermark :global(svg) {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ambient-glow {
|
|
||||||
position: absolute;
|
|
||||||
top: 33.3333%;
|
|
||||||
right: 25%;
|
|
||||||
width: 31.25rem;
|
|
||||||
height: 31.25rem;
|
|
||||||
border-radius: 9999px;
|
|
||||||
background: color-mix(in srgb, var(--color-accent) 10%, transparent);
|
|
||||||
filter: blur(120px);
|
|
||||||
animation: pulse-glow 3s ease-in-out infinite;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content {
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 80rem;
|
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding: 4rem 1.5rem;
|
padding-block: clamp(3rem, 8vw, 7rem) clamp(5rem, 10vw, 9rem);
|
||||||
gap: 3rem;
|
}
|
||||||
|
|
||||||
|
.hero-grid {
|
||||||
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: clamp(4rem, 9vw, 8rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy {
|
.hero-copy {
|
||||||
max-width: 36rem;
|
min-width: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.fade-in {
|
|
||||||
animation: fade-in-up 0.6s ease-out both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-row {
|
.brand-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: clamp(2rem, 5vw, 3rem);
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-logo :global(svg) {
|
.brand-logo :global(svg) {
|
||||||
width: 2.5rem;
|
width: 2.25rem;
|
||||||
height: 2.5rem;
|
height: 2.25rem;
|
||||||
}
|
|
||||||
|
|
||||||
.brand-name {
|
|
||||||
color: var(--color-text-muted);
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-size: 2.25rem;
|
max-width: 10ch;
|
||||||
line-height: 1.05;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(3rem, 10vw, 5.75rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: -0.025em;
|
letter-spacing: -0.055em;
|
||||||
|
line-height: 0.98;
|
||||||
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-line {
|
.hero-title span {
|
||||||
display: block;
|
color: var(--color-text-muted);
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gradient-text {
|
|
||||||
color: transparent;
|
|
||||||
background: linear-gradient(to right, var(--color-accent-dim), var(--color-accent));
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
background-clip: text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-description {
|
.hero-description {
|
||||||
max-width: 28rem;
|
max-width: 31rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1.5rem;
|
||||||
margin-bottom: 2rem;
|
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 1rem;
|
font-size: clamp(1rem, 2vw, 1.125rem);
|
||||||
line-height: 1.625;
|
line-height: 1.65;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phone-stage {
|
.download-area {
|
||||||
|
margin-top: 2rem;
|
||||||
|
scroll-margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-visual {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: min(100%, 35rem);
|
||||||
|
align-items: flex-start;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: clamp(0.5rem, 2vw, 1.25rem);
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: 0.5rem 0 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phone-stack {
|
.shape-field {
|
||||||
position: relative;
|
|
||||||
width: 14rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-phone {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.75rem;
|
z-index: 0;
|
||||||
left: -2rem;
|
inset: -1.5rem -1.5rem -1rem;
|
||||||
width: 100%;
|
pointer-events: none;
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid color-mix(in srgb, var(--color-border) 30%, transparent);
|
|
||||||
border-radius: 2.5rem;
|
|
||||||
opacity: 0.4;
|
|
||||||
filter: blur(1px);
|
|
||||||
transform: rotate(-4deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-phone {
|
.hero-shape {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
fill: currentColor;
|
||||||
|
opacity: 0.82;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arch-shape {
|
||||||
|
top: -4%;
|
||||||
|
right: -12%;
|
||||||
|
width: 78%;
|
||||||
|
height: 96%;
|
||||||
|
color: var(--color-surface-high);
|
||||||
|
transform: rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-shape {
|
||||||
|
top: 6%;
|
||||||
|
left: -10%;
|
||||||
|
width: clamp(11rem, 32vw, 17rem);
|
||||||
|
color: color-mix(in srgb, var(--color-brand) 10%, var(--color-surface-high));
|
||||||
|
transform: rotate(-10deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-shape {
|
||||||
|
bottom: -8%;
|
||||||
|
left: -10%;
|
||||||
|
width: clamp(10rem, 28vw, 15rem);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
color: var(--color-surface-highest);
|
||||||
|
transform: rotate(-18deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.phone {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2px solid color-mix(in srgb, var(--color-border) 60%, transparent);
|
border-radius: clamp(1.75rem, 4vw, 2.75rem);
|
||||||
border-radius: 2.5rem;
|
background: #000;
|
||||||
box-shadow: 0 0 80px -20px color-mix(in srgb, var(--color-accent) 20%, transparent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.secondary-phone {
|
||||||
|
width: 44%;
|
||||||
|
margin-top: clamp(3rem, 8vw, 6rem);
|
||||||
|
transform: rotate(-2.5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-phone {
|
||||||
|
width: 50%;
|
||||||
|
transform: rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone :global(picture),
|
||||||
.phone-image {
|
.phone-image {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
.fade-in {
|
||||||
.hero-content {
|
animation: fade-in-up var(--motion-expressive) var(--ease-standard) both;
|
||||||
padding-block: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-row {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-description {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
margin-bottom: 2.5rem;
|
|
||||||
font-size: 1.125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.phone-stack {
|
|
||||||
width: 17.5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 960px) {
|
||||||
.hero-watermark {
|
.hero-grid {
|
||||||
width: 50rem;
|
grid-template-columns: minmax(28rem, 1fr) minmax(28rem, 0.9fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-title {
|
.hero-visual {
|
||||||
font-size: 3.75rem;
|
justify-self: end;
|
||||||
}
|
|
||||||
|
|
||||||
.phone-stack {
|
|
||||||
width: 18.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.hero-watermark {
|
|
||||||
width: 62.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content {
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
padding-inline: 3rem;
|
|
||||||
gap: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.phone-stage {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
|
||||||
import ScrollReveal from "./ScrollReveal.svelte";
|
import ScrollReveal from "./ScrollReveal.svelte";
|
||||||
import AppleIcon from "~icons/simple-icons/apple";
|
import AppleIcon from "~icons/simple-icons/apple";
|
||||||
import GooglePlayIcon from "~icons/simple-icons/googleplay";
|
import GooglePlayIcon from "~icons/simple-icons/googleplay";
|
||||||
import RedditIcon from "~icons/fa6-brands/reddit-alien";
|
import RedditIcon from "~icons/fa6-brands/reddit-alien";
|
||||||
import ArrowUpIcon from "~icons/heroicons/arrow-up-solid";
|
import ArrowUpIcon from "~icons/heroicons/arrow-up-solid";
|
||||||
import StarIcon from "~icons/heroicons/star-solid";
|
import StarIcon from "~icons/heroicons/star-solid";
|
||||||
|
import ChevronLeftIcon from "~icons/heroicons/chevron-left-solid";
|
||||||
|
import ChevronRightIcon from "~icons/heroicons/chevron-right-solid";
|
||||||
|
|
||||||
type SourceIcon = typeof AppleIcon | typeof GooglePlayIcon | typeof RedditIcon;
|
type SourceIcon = typeof AppleIcon | typeof GooglePlayIcon | typeof RedditIcon;
|
||||||
|
|
||||||
@@ -78,8 +79,8 @@
|
|||||||
upvotes: 50,
|
upvotes: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "If you're having troubles with the default plex app just ditch it and switch to this. Plezy is faster, and easier to use. Worth the $5",
|
text: "Design and theme are much better. Love having the buffer size setting. Clean and intuitive. Single developer app that is consistently updated, thank you for your hard work.",
|
||||||
name: "boyknight53",
|
name: "Corrykid",
|
||||||
source: "App Store",
|
source: "App Store",
|
||||||
icon: AppleIcon,
|
icon: AppleIcon,
|
||||||
},
|
},
|
||||||
@@ -96,10 +97,10 @@
|
|||||||
icon: GooglePlayIcon,
|
icon: GooglePlayIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "College student here this is a godsend. Cannot recommend enough. For mediaheads on the go this is the best $4 you could ever spend.",
|
text: "Perfect. I'm so excited for the development of this app. It's exactly what Plex should have been a decade ago. The client-side MPV player with full codec support fixes playback issues and removes the need for server-side lossy transcoding",
|
||||||
name: "djmeowmeow",
|
name: "John Capasso",
|
||||||
source: "App Store",
|
source: "Play Store",
|
||||||
icon: AppleIcon,
|
icon: GooglePlayIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Much better than the official app! The sticking point for me is that downloads actually work reliably. The interface is great and snappy.",
|
text: "Much better than the official app! The sticking point for me is that downloads actually work reliably. The interface is great and snappy.",
|
||||||
@@ -108,10 +109,10 @@
|
|||||||
icon: GooglePlayIcon,
|
icon: GooglePlayIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "This has to be my new favorite plex client. Starts up and connects quickly. Quality is amazing. Well worth the $5.",
|
text: "Just brilliant. Using on all my Android TV boxes. UI and UX are great. I was having intermittent audio drops with the latest build of the official Plex app. No issues with Plezy.",
|
||||||
name: "nasukaren",
|
name: "Jon Evans",
|
||||||
source: "App Store",
|
source: "Play Store",
|
||||||
icon: AppleIcon,
|
icon: GooglePlayIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Fantastic and super lean looking! Must have if you have a Plex server.",
|
text: "Fantastic and super lean looking! Must have if you have a Plex server.",
|
||||||
@@ -121,10 +122,58 @@
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let mounted = $state(false);
|
const featuredReviews = [row1[3]!, row2[0]!, row2[4]!, row1[5]!];
|
||||||
|
const featuredReviewSet = new Set(featuredReviews);
|
||||||
|
const supportingReviews = [...row1, ...row2].filter((review) => !featuredReviewSet.has(review));
|
||||||
|
|
||||||
onMount(() => {
|
let featuredIndex = $state(0);
|
||||||
mounted = true;
|
let carouselHovered = $state(false);
|
||||||
|
let carouselFocused = $state(false);
|
||||||
|
let prefersReducedMotion = $state(false);
|
||||||
|
|
||||||
|
const featuredReview = $derived(featuredReviews[featuredIndex]!);
|
||||||
|
const carouselPaused = $derived(carouselHovered || carouselFocused || prefersReducedMotion);
|
||||||
|
|
||||||
|
function showReview(index: number) {
|
||||||
|
featuredIndex = (index + featuredReviews.length) % featuredReviews.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function previousReview() {
|
||||||
|
showReview(featuredIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextReview() {
|
||||||
|
showReview(featuredIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCarouselFocusOut(event: FocusEvent) {
|
||||||
|
const currentTarget = event.currentTarget;
|
||||||
|
carouselFocused =
|
||||||
|
currentTarget instanceof HTMLElement &&
|
||||||
|
event.relatedTarget instanceof Node &&
|
||||||
|
currentTarget.contains(event.relatedTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||||
|
const updatePreference = () => {
|
||||||
|
prefersReducedMotion = media.matches;
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePreference();
|
||||||
|
media.addEventListener("change", updatePreference);
|
||||||
|
return () => media.removeEventListener("change", updatePreference);
|
||||||
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
const currentIndex = featuredIndex;
|
||||||
|
if (carouselPaused) return;
|
||||||
|
|
||||||
|
const timeout = window.setTimeout(() => {
|
||||||
|
showReview(currentIndex + 1);
|
||||||
|
}, 8000);
|
||||||
|
|
||||||
|
return () => window.clearTimeout(timeout);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -139,177 +188,292 @@
|
|||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 1: scrolls left -->
|
<div class="featured-wrap">
|
||||||
<div class="marquee-row row-gap">
|
<ScrollReveal>
|
||||||
<div class="marquee-track marquee-left">
|
<article
|
||||||
{#each row1 as review}
|
class="featured-review"
|
||||||
{@const SourceIcon = review.icon}
|
aria-label="Featured user reviews"
|
||||||
<div class="review-card glass-card">
|
aria-roledescription="carousel"
|
||||||
{#if review.source === "Reddit"}
|
onpointerenter={() => carouselHovered = true}
|
||||||
|
onpointerleave={() => carouselHovered = false}
|
||||||
|
onfocusin={() => carouselFocused = true}
|
||||||
|
onfocusout={handleCarouselFocusOut}
|
||||||
|
>
|
||||||
|
<div class="featured-top">
|
||||||
|
{#if featuredReview.source === "Reddit"}
|
||||||
<div class="upvote">
|
<div class="upvote">
|
||||||
<ArrowUpIcon />
|
<ArrowUpIcon />
|
||||||
<span>{review.upvotes}</span>
|
<span>{featuredReview.upvotes}</span>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="stars">
|
<div class="stars" aria-label="5 out of 5 stars">
|
||||||
{#each Array(5) as _}
|
{#each Array(5) as _}
|
||||||
<StarIcon />
|
<StarIcon aria-hidden="true" />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<p class="review-text">"{review.text}"</p>
|
|
||||||
<div class="review-footer">
|
<div class="carousel-controls">
|
||||||
<span class="review-name">{review.name}</span>
|
<button type="button" class="carousel-arrow" onclick={previousReview} aria-label="Previous featured review">
|
||||||
<div class="review-source">
|
<ChevronLeftIcon />
|
||||||
<SourceIcon />
|
</button>
|
||||||
<span>{review.source}</span>
|
<div class="carousel-dots" role="group" aria-label="Choose featured review">
|
||||||
|
{#each featuredReviews as _, index}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="carousel-dot"
|
||||||
|
class:active={featuredIndex === index}
|
||||||
|
onclick={() => showReview(index)}
|
||||||
|
aria-label={`Show featured review ${index + 1}`}
|
||||||
|
aria-current={featuredIndex === index ? "true" : undefined}
|
||||||
|
></button>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<button type="button" class="carousel-arrow" onclick={nextReview} aria-label="Next featured review">
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
|
||||||
{#if mounted}
|
<div class="featured-slide" aria-live={carouselPaused ? "polite" : "off"}>
|
||||||
<!-- Duplicate for seamless loop -->
|
{#key featuredIndex}
|
||||||
<div aria-hidden="true" class="duplicate-cards">
|
{@const FeaturedSourceIcon = featuredReview.icon}
|
||||||
{#each row1 as review}
|
<div class="featured-content">
|
||||||
{@const SourceIcon = review.icon}
|
<blockquote>"{featuredReview.text}"</blockquote>
|
||||||
<div class="review-card glass-card">
|
|
||||||
{#if review.source === "Reddit"}
|
|
||||||
<div class="upvote">
|
|
||||||
<ArrowUpIcon />
|
|
||||||
<span>{review.upvotes}</span>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="stars">
|
|
||||||
{#each Array(5) as _}
|
|
||||||
<StarIcon />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<p class="review-text">"{review.text}"</p>
|
|
||||||
<div class="review-footer">
|
<div class="review-footer">
|
||||||
<span class="review-name">{review.name}</span>
|
<span class="review-name">{featuredReview.name}</span>
|
||||||
<div class="review-source">
|
<div class="review-source">
|
||||||
<SourceIcon />
|
<FeaturedSourceIcon />
|
||||||
<span>{review.source}</span>
|
<span>{featuredReview.source}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</article>
|
||||||
</div>
|
</ScrollReveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Row 2: scrolls right -->
|
<div class="review-strip content-pad" role="list" aria-label="More user reviews">
|
||||||
<div class="marquee-row">
|
{#each supportingReviews as review}
|
||||||
<div class="marquee-track marquee-right">
|
{@const SourceIcon = review.icon}
|
||||||
{#each row2 as review}
|
<article class="review-card" role="listitem">
|
||||||
{@const SourceIcon = review.icon}
|
{#if review.source === "Reddit"}
|
||||||
<div class="review-card glass-card">
|
<div class="upvote">
|
||||||
{#if review.source === "Reddit"}
|
<ArrowUpIcon />
|
||||||
<div class="upvote">
|
<span>{review.upvotes}</span>
|
||||||
<ArrowUpIcon />
|
</div>
|
||||||
<span>{review.upvotes}</span>
|
{:else}
|
||||||
</div>
|
<div class="stars" aria-label="5 out of 5 stars">
|
||||||
{:else}
|
{#each Array(5) as _}
|
||||||
<div class="stars">
|
<StarIcon aria-hidden="true" />
|
||||||
{#each Array(5) as _}
|
{/each}
|
||||||
<StarIcon />
|
</div>
|
||||||
{/each}
|
{/if}
|
||||||
</div>
|
<p class="review-text">"{review.text}"</p>
|
||||||
{/if}
|
<div class="review-footer">
|
||||||
<p class="review-text">"{review.text}"</p>
|
<span class="review-name">{review.name}</span>
|
||||||
<div class="review-footer">
|
<div class="review-source">
|
||||||
<span class="review-name">{review.name}</span>
|
<SourceIcon />
|
||||||
<div class="review-source">
|
<span>{review.source}</span>
|
||||||
<SourceIcon />
|
|
||||||
<span>{review.source}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
</article>
|
||||||
{#if mounted}
|
{/each}
|
||||||
<!-- Duplicate for seamless loop -->
|
|
||||||
<div aria-hidden="true" class="duplicate-cards">
|
|
||||||
{#each row2 as review}
|
|
||||||
{@const SourceIcon = review.icon}
|
|
||||||
<div class="review-card glass-card">
|
|
||||||
{#if review.source === "Reddit"}
|
|
||||||
<div class="upvote">
|
|
||||||
<ArrowUpIcon />
|
|
||||||
<span>{review.upvotes}</span>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="stars">
|
|
||||||
{#each Array(5) as _}
|
|
||||||
<StarIcon />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<p class="review-text">"{review.text}"</p>
|
|
||||||
<div class="review-footer">
|
|
||||||
<span class="review-name">{review.name}</span>
|
|
||||||
<div class="review-source">
|
|
||||||
<SourceIcon />
|
|
||||||
<span>{review.source}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.reviews-section {
|
.reviews-section {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-block: 4rem;
|
padding-block: clamp(4rem, 9vw, 8rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reviews-header,
|
||||||
|
.featured-wrap {
|
||||||
|
width: min(100%, var(--page-width));
|
||||||
|
margin-inline: auto;
|
||||||
|
padding-inline: var(--page-gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reviews-header {
|
.reviews-header {
|
||||||
max-width: 64rem;
|
margin-bottom: clamp(2rem, 5vw, 3.5rem);
|
||||||
margin-inline: auto;
|
|
||||||
margin-bottom: 2.5rem;
|
|
||||||
padding-inline: 1.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-label {
|
.section-label {
|
||||||
margin-bottom: 0.75rem;
|
width: fit-content;
|
||||||
color: var(--color-accent);
|
margin-bottom: 1rem;
|
||||||
font-size: 0.875rem;
|
border-radius: var(--radius-full);
|
||||||
font-weight: 500;
|
padding: 0.5rem 0.875rem;
|
||||||
letter-spacing: 0.025em;
|
color: var(--color-text-muted);
|
||||||
line-height: 1.25rem;
|
background: var(--color-surface);
|
||||||
text-transform: uppercase;
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
|
max-width: 12ch;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-size: 2.25rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(2.5rem, 7vw, 4.75rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2.5rem;
|
letter-spacing: -0.045em;
|
||||||
|
line-height: 1;
|
||||||
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-description {
|
.section-description {
|
||||||
max-width: 32rem;
|
max-width: 34rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 1.125rem;
|
font-size: clamp(1rem, 2vw, 1.125rem);
|
||||||
line-height: 1.75rem;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-gap {
|
.featured-review {
|
||||||
margin-bottom: 1rem;
|
display: flex;
|
||||||
|
min-height: clamp(22rem, 42vw, 31rem);
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
padding: clamp(1.5rem, 6vw, 4rem);
|
||||||
|
background: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-top .upvote {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-controls,
|
||||||
|
.carousel-dots {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-controls {
|
||||||
|
gap: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-dots {
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding-inline: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-arrow,
|
||||||
|
.carousel-dot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
background: var(--color-surface-highest);
|
||||||
|
transition:
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-arrow {
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-arrow :global(svg) {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-dot {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--color-text-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-dot.active {
|
||||||
|
background: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-arrow:hover,
|
||||||
|
.carousel-arrow:focus-visible,
|
||||||
|
.carousel-dot:hover,
|
||||||
|
.carousel-dot:focus-visible {
|
||||||
|
color: var(--color-text);
|
||||||
|
background: rgb(237 237 237 / 0.18);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-dot:focus-visible {
|
||||||
|
box-shadow: 0 0 0 0.25rem rgb(237 237 237 / 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-slide {
|
||||||
|
display: grid;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
animation: featured-review-enter var(--motion-normal) var(--ease-standard) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes featured-review-enter {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(0.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-review blockquote {
|
||||||
|
max-width: 24ch;
|
||||||
|
margin: 2rem 0;
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(1.75rem, 4.5vw, 3.25rem);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-strip {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-strip::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-card {
|
.review-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 300px;
|
width: min(82vw, 20rem);
|
||||||
|
min-height: 14rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1.25rem;
|
border-radius: var(--radius-lg);
|
||||||
border-radius: 1rem;
|
padding: 1.5rem;
|
||||||
|
background: var(--color-surface-high);
|
||||||
|
scroll-snap-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-card:nth-child(3n) {
|
||||||
|
background: var(--color-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.upvote,
|
.upvote,
|
||||||
@@ -321,23 +485,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.upvote {
|
.upvote {
|
||||||
|
width: fit-content;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 1.25rem;
|
||||||
color: var(--color-accent);
|
border-radius: var(--radius-full);
|
||||||
font-size: 0.875rem;
|
padding: 0.375rem 0.625rem;
|
||||||
font-weight: 500;
|
color: #ff9a7d;
|
||||||
line-height: 1.25rem;
|
background: rgb(255 118 82 / 0.14);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upvote :global(svg) {
|
.upvote :global(svg) {
|
||||||
width: 1rem;
|
width: 0.875rem;
|
||||||
height: 1rem;
|
height: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stars {
|
.stars {
|
||||||
gap: 0.125rem;
|
gap: 0.125rem;
|
||||||
margin-bottom: 0.75rem;
|
color: var(--color-brand);
|
||||||
color: var(--color-accent);
|
}
|
||||||
|
|
||||||
|
.review-card .stars {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stars :global(svg),
|
.stars :global(svg),
|
||||||
@@ -348,116 +518,32 @@
|
|||||||
|
|
||||||
.review-text {
|
.review-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1.5rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
font-size: 0.9375rem;
|
||||||
line-height: 1.625;
|
line-height: 1.65;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-footer {
|
.review-footer {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-name {
|
.review-name {
|
||||||
font-size: 0.875rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-source {
|
.review-source {
|
||||||
|
flex-shrink: 0;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
color: color-mix(in srgb, var(--color-text-muted) 60%, transparent);
|
color: var(--color-text-subtle);
|
||||||
font-size: 0.75rem;
|
font-size: 0.6875rem;
|
||||||
line-height: 1rem;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duplicate-cards {
|
.content-pad {
|
||||||
display: contents;
|
padding-left: max(var(--page-gutter), calc((100vw - var(--page-width)) / 2 + var(--page-gutter)));
|
||||||
}
|
padding-right: var(--page-gutter);
|
||||||
|
|
||||||
.marquee-row {
|
|
||||||
overflow: hidden;
|
|
||||||
mask-image: linear-gradient(
|
|
||||||
to right,
|
|
||||||
transparent,
|
|
||||||
black 5%,
|
|
||||||
black 95%,
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
-webkit-mask-image: linear-gradient(
|
|
||||||
to right,
|
|
||||||
transparent,
|
|
||||||
black 5%,
|
|
||||||
black 95%,
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.marquee-track {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
width: max-content;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marquee-left {
|
|
||||||
animation: scroll-left 60s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marquee-right {
|
|
||||||
animation: scroll-right 60s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marquee-row:hover .marquee-track {
|
|
||||||
animation-play-state: paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes scroll-left {
|
|
||||||
from {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes scroll-right {
|
|
||||||
from {
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.marquee-track {
|
|
||||||
animation-play-state: paused;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
.reviews-section {
|
|
||||||
padding-block: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reviews-header {
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.review-card {
|
|
||||||
width: 340px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.reviews-section {
|
|
||||||
padding-block: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-heading {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -249,82 +249,110 @@
|
|||||||
<style>
|
<style>
|
||||||
.screenshots-section {
|
.screenshots-section {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-block: 4rem;
|
padding-block: clamp(4rem, 9vw, 8rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshots-header {
|
.screenshots-header {
|
||||||
max-width: 64rem;
|
max-width: 64rem;
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: clamp(2rem, 5vw, 3.5rem);
|
||||||
padding-inline: 1.5rem;
|
padding-inline: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-label {
|
.section-label {
|
||||||
margin-bottom: 0.75rem;
|
width: fit-content;
|
||||||
color: var(--color-accent);
|
margin-bottom: 1rem;
|
||||||
font-size: 0.875rem;
|
border-radius: var(--radius-full);
|
||||||
font-weight: 500;
|
padding: 0.5rem 0.875rem;
|
||||||
letter-spacing: 0.025em;
|
color: var(--color-text-muted);
|
||||||
line-height: 1.25rem;
|
background: var(--color-surface);
|
||||||
text-transform: uppercase;
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
|
max-width: 12ch;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-size: 2.25rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(2.5rem, 7vw, 4.75rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2.5rem;
|
letter-spacing: -0.045em;
|
||||||
|
line-height: 1;
|
||||||
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-description {
|
.section-description {
|
||||||
max-width: 32rem;
|
max-width: 34rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 2rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 1.125rem;
|
font-size: clamp(1rem, 2vw, 1.125rem);
|
||||||
line-height: 1.75rem;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot-controls {
|
.screenshot-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tabs {
|
.device-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
gap: 0.25rem;
|
max-width: 100%;
|
||||||
border-radius: 0.75rem;
|
gap: var(--group-gap);
|
||||||
background: rgb(255 255 255 / 0.04);
|
overflow-x: auto;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
background: var(--color-surface);
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-tabs::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-button {
|
.device-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
min-height: 2.75rem;
|
||||||
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: var(--radius-pill);
|
||||||
padding: 0.5rem 1rem;
|
padding-inline: 0.875rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 500;
|
font-weight: 700;
|
||||||
line-height: 1.25rem;
|
transition:
|
||||||
transition: background-color 150ms ease, color 150ms ease;
|
border-radius var(--motion-expressive) var(--ease-standard),
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-button:not(.active):hover {
|
.device-button:not(.active):hover,
|
||||||
color: color-mix(in srgb, var(--color-text) 80%, transparent);
|
.device-button:not(.active):focus-visible {
|
||||||
|
color: var(--color-text);
|
||||||
|
background: rgb(237 237 237 / 0.12);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-button.active {
|
.device-button.active {
|
||||||
color: var(--color-text);
|
color: var(--color-on-primary);
|
||||||
background: rgb(255 255 255 / 0.1);
|
background: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-button.active:hover,
|
||||||
|
.device-button.active:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: #fff;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-button :global(svg),
|
.device-button :global(svg),
|
||||||
.scroll-arrow :global(svg) {
|
.scroll-arrow :global(svg) {
|
||||||
width: 1rem;
|
width: 1.125rem;
|
||||||
height: 1rem;
|
height: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-label {
|
.device-label {
|
||||||
@@ -334,28 +362,34 @@
|
|||||||
.scroll-arrows {
|
.scroll-arrows {
|
||||||
display: none;
|
display: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: var(--group-gap);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-arrow {
|
.scroll-arrow {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 2rem;
|
width: 2.75rem;
|
||||||
height: 2rem;
|
height: 2.75rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 0.5rem;
|
border-radius: var(--radius-pill);
|
||||||
background: rgb(255 255 255 / 0.04);
|
color: var(--color-text-subtle);
|
||||||
color: color-mix(in srgb, var(--color-text-muted) 30%, transparent);
|
background: var(--color-surface);
|
||||||
transition: background-color 150ms ease, color 150ms ease;
|
transition:
|
||||||
|
border-radius var(--motion-expressive) var(--ease-standard),
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-arrow.enabled {
|
.scroll-arrow.enabled {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-arrow.enabled:hover {
|
.scroll-arrow.enabled:hover,
|
||||||
background: rgb(255 255 255 / 0.1);
|
.scroll-arrow.enabled:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-surface-highest);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot-panels {
|
.screenshot-panels {
|
||||||
@@ -371,10 +405,11 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
opacity: 0;
|
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 220ms ease;
|
scroll-snap-type: x mandatory;
|
||||||
|
transition: opacity var(--motion-normal) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot-strip.panel-active {
|
.screenshot-strip.panel-active {
|
||||||
@@ -393,8 +428,6 @@
|
|||||||
.screenshot-frame {
|
.screenshot-frame {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid color-mix(in srgb, var(--color-border) 40%, transparent);
|
|
||||||
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.phone-frame {
|
.phone-frame {
|
||||||
@@ -410,60 +443,35 @@
|
|||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tv-frame {
|
|
||||||
border-width: 2px;
|
|
||||||
border-color: color-mix(in srgb, var(--color-border) 30%, transparent);
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.screenshot-image {
|
.screenshot-image {
|
||||||
display: block;
|
display: block;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-pad {
|
.content-pad {
|
||||||
/* Left padding aligns with max-w-5xl (64rem) + px-6 */
|
|
||||||
padding-left: max(1.5rem, calc((100vw - 64rem) / 2 + 1.5rem));
|
padding-left: max(1.5rem, calc((100vw - 64rem) / 2 + 1.5rem));
|
||||||
padding-right: 1.5rem;
|
padding-right: 1.5rem;
|
||||||
|
scroll-padding-left: max(1.5rem, calc((100vw - 64rem) / 2 + 1.5rem));
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-hide {
|
.scrollbar-hide {
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-hide::-webkit-scrollbar {
|
.scrollbar-hide::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 560px) {
|
||||||
.screenshots-section {
|
|
||||||
padding-block: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.screenshots-header {
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-description {
|
|
||||||
margin-bottom: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-label {
|
.device-label {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.screenshots-section {
|
|
||||||
padding-block: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-heading {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-arrows {
|
.scroll-arrows {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -471,11 +479,11 @@
|
|||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.screenshot-panels {
|
.screenshot-panels {
|
||||||
min-height: calc(480px + 1rem);
|
min-height: calc(500px + 1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenshot-item {
|
.screenshot-item {
|
||||||
height: 480px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -25,15 +25,21 @@
|
|||||||
<div
|
<div
|
||||||
bind:this={el}
|
bind:this={el}
|
||||||
class="{className} scroll-reveal"
|
class="{className} scroll-reveal"
|
||||||
style="opacity: {visible ? 1 : 0}; transform: translateY({visible ? 0 : 24}px); transition-delay: {delay}ms;"
|
style="opacity: {visible ? 1 : 0}; transform: translateY({visible ? 0 : 16}px); transition-delay: {delay}ms;"
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.scroll-reveal {
|
.scroll-reveal {
|
||||||
transition-property: opacity, transform;
|
transition:
|
||||||
transition-duration: 600ms;
|
opacity var(--motion-expressive) var(--ease-standard),
|
||||||
transition-timing-function: ease-out;
|
transform var(--motion-expressive) var(--ease-standard);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.scroll-reveal {
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -27,33 +27,45 @@
|
|||||||
<style>
|
<style>
|
||||||
.error-page {
|
.error-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 4rem 1.5rem;
|
padding: var(--page-gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-card {
|
.error-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: 24rem;
|
width: min(100%, 32rem);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
padding: clamp(2rem, 8vw, 4rem);
|
||||||
|
background: var(--color-surface);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-logo {
|
.error-logo {
|
||||||
margin-bottom: 1.5rem;
|
display: flex;
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background: var(--color-surface-highest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-logo :global(svg) {
|
.error-logo :global(svg) {
|
||||||
width: 4rem;
|
width: 2.5rem;
|
||||||
height: 4rem;
|
height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-code {
|
.error-code {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
font-size: 3rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(3.5rem, 16vw, 6rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.06em;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,19 +76,23 @@
|
|||||||
|
|
||||||
.home-link {
|
.home-link {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
min-height: 3rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
border-radius: var(--radius-pill);
|
||||||
padding: 0.75rem 1.25rem;
|
padding-inline: 1.25rem;
|
||||||
border-radius: 9999px;
|
color: var(--color-on-primary);
|
||||||
background: var(--color-accent);
|
background: var(--color-text);
|
||||||
color: #fff;
|
font-size: 0.8125rem;
|
||||||
font-size: 0.875rem;
|
font-weight: 700;
|
||||||
font-weight: 600;
|
transition:
|
||||||
line-height: 1.25rem;
|
border-radius var(--motion-expressive) var(--ease-standard),
|
||||||
transition: background-color 150ms ease;
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-link:hover {
|
.home-link:hover,
|
||||||
background: color-mix(in srgb, var(--color-accent) 90%, transparent);
|
.home-link:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: #fff;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import Reviews from '$lib/components/Reviews.svelte';
|
import Reviews from '$lib/components/Reviews.svelte';
|
||||||
import FAQ from '$lib/components/FAQ.svelte';
|
import FAQ from '$lib/components/FAQ.svelte';
|
||||||
import Footer from '$lib/components/Footer.svelte';
|
import Footer from '$lib/components/Footer.svelte';
|
||||||
import NoiseOverlay from '$lib/components/NoiseOverlay.svelte';
|
|
||||||
import { faqSchemaMainEntity } from '$lib/content/faqs';
|
import { faqSchemaMainEntity } from '$lib/content/faqs';
|
||||||
|
|
||||||
const { data } = $props();
|
const { data } = $props();
|
||||||
@@ -95,7 +94,6 @@
|
|||||||
{@html `<script type="application/ld+json">${JSON.stringify(faqSchema)}</script>`}
|
{@html `<script type="application/ld+json">${JSON.stringify(faqSchema)}</script>`}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<NoiseOverlay />
|
|
||||||
<Hero />
|
<Hero />
|
||||||
<Features />
|
<Features />
|
||||||
<Screenshots />
|
<Screenshots />
|
||||||
|
|||||||
@@ -1,26 +1,49 @@
|
|||||||
:root {
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
/* Mirrors the app's mono Material 3 theme. */
|
||||||
--color-bg: #0e0f12;
|
--color-bg: #0e0f12;
|
||||||
--color-surface: #15171c;
|
--color-surface: #15171c;
|
||||||
--color-surface-hover: #1a1c22;
|
--color-surface-high: #1d1f25;
|
||||||
--color-border: #2a2d35;
|
--color-surface-highest: #25272e;
|
||||||
--color-accent: #ff6b45;
|
--color-surface-hover: #2b2e35;
|
||||||
--color-accent-glow: #ff6b4533;
|
--color-border: rgb(237 237 237 / 0.12);
|
||||||
--color-accent-dim: #ab543a;
|
|
||||||
--color-text: #ededed;
|
--color-text: #ededed;
|
||||||
--color-text-muted: #9ca3af;
|
--color-text-muted: rgb(237 237 237 / 0.6);
|
||||||
|
--color-text-subtle: rgb(237 237 237 / 0.4);
|
||||||
|
--color-brand: #ff7652;
|
||||||
|
--color-accent: var(--color-brand);
|
||||||
|
--color-accent-dim: #b9573b;
|
||||||
|
--color-on-primary: #0e0f12;
|
||||||
|
|
||||||
--font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
|
--radius-xs: 0.3125rem;
|
||||||
|
--radius-sm: 0.5rem;
|
||||||
|
--radius-md: 0.75rem;
|
||||||
|
--radius-lg: 1.25rem;
|
||||||
|
--radius-xl: 2.25rem;
|
||||||
|
--radius-full: 999px;
|
||||||
|
--radius-pill: 1.5rem;
|
||||||
|
--group-gap: 0.1875rem;
|
||||||
|
|
||||||
--animate-fade-in-up: fade-in-up 0.6s ease-out both;
|
--space: 0.75rem;
|
||||||
--animate-pulse-glow: pulse-glow 3s ease-in-out infinite;
|
--page-width: 76rem;
|
||||||
--animate-float: float 6s ease-in-out infinite;
|
--page-gutter: clamp(1rem, 4vw, 2rem);
|
||||||
|
|
||||||
|
--motion-fast: 120ms;
|
||||||
|
--motion-normal: 200ms;
|
||||||
|
--motion-slow: 300ms;
|
||||||
|
--motion-expressive: 350ms;
|
||||||
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||||
|
|
||||||
|
--font-display: "Avenir Next", "Segoe UI Variable Display", "SF Pro Display", ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--font-body: "Inter", "Segoe UI Variable Text", "SF Pro Text", ui-sans-serif, system-ui, sans-serif;
|
||||||
|
--font-utility: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 0 solid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
@@ -35,18 +58,26 @@ ul {
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
background-color: var(--color-bg);
|
scroll-padding-top: 6rem;
|
||||||
|
background: var(--color-bg);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
font-family: var(--font-sans);
|
overflow-x: hidden;
|
||||||
|
font-family: var(--font-body);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
background-color: var(--color-bg);
|
background: var(--color-bg);
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: var(--color-on-primary);
|
||||||
|
background: var(--color-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -54,6 +85,11 @@ a {
|
|||||||
text-decoration: inherit;
|
text-decoration: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
button {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
@@ -63,6 +99,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -97,84 +134,33 @@ ul {
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flat-card {
|
||||||
|
background: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes fade-in-up {
|
@keyframes fade-in-up {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(24px);
|
transform: translateY(1rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-glow {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
0%, 100% {
|
html {
|
||||||
opacity: 0.6;
|
scroll-behavior: auto;
|
||||||
filter: blur(60px);
|
|
||||||
}
|
}
|
||||||
50% {
|
|
||||||
opacity: 1;
|
|
||||||
filter: blur(80px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes float {
|
*,
|
||||||
0%, 100% {
|
*::before,
|
||||||
transform: translateY(0);
|
*::after {
|
||||||
}
|
scroll-behavior: auto !important;
|
||||||
50% {
|
animation-duration: 0.01ms !important;
|
||||||
transform: translateY(-12px);
|
animation-iteration-count: 1 !important;
|
||||||
}
|
transition-duration: 0.01ms !important;
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes gradient-shift {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translate(0, 0) scale(1);
|
|
||||||
}
|
|
||||||
33% {
|
|
||||||
transform: translate(30px, -20px) scale(1.1);
|
|
||||||
}
|
|
||||||
66% {
|
|
||||||
transform: translate(-20px, 15px) scale(0.95);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Glassmorphism card utility */
|
|
||||||
.glass-card {
|
|
||||||
background: color-mix(in srgb, var(--color-surface) 60%, transparent);
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
transition: box-shadow 0.3s ease, border-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glass-card:hover {
|
|
||||||
border-color: var(--color-accent-glow);
|
|
||||||
box-shadow:
|
|
||||||
0 8px 32px rgba(0, 0, 0, 0.3),
|
|
||||||
0 0 0 1px var(--color-accent-glow),
|
|
||||||
0 0 40px -12px var(--color-accent-glow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Noise overlay texture */
|
|
||||||
.noise-overlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 50;
|
|
||||||
opacity: 0.035;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
|
||||||
background-repeat: repeat;
|
|
||||||
background-size: 256px 256px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scroll indicator */
|
|
||||||
@keyframes scroll-bounce {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(8px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,93 +165,109 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.privacy-article {
|
.privacy-article {
|
||||||
max-width: 64rem;
|
width: min(100%, 58rem);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
padding: 4rem 1.5rem;
|
padding: clamp(2rem, 7vw, 6rem) var(--page-gutter) clamp(5rem, 10vw, 9rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-link {
|
.back-link {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
min-height: 2.75rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.625rem;
|
gap: 0.625rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: clamp(3rem, 8vw, 6rem);
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
padding-inline: 0.875rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
background: var(--color-surface);
|
||||||
font-weight: 500;
|
font-size: 0.8125rem;
|
||||||
line-height: 1.25rem;
|
font-weight: 700;
|
||||||
transition: color 150ms ease;
|
transition:
|
||||||
|
color var(--motion-fast) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-link:hover {
|
.back-link:hover,
|
||||||
|
.back-link:focus-visible {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
background: var(--color-surface-highest);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-logo :global(svg) {
|
.back-logo :global(svg) {
|
||||||
width: 1.5rem;
|
width: 1.375rem;
|
||||||
height: 1.5rem;
|
height: 1.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-heading {
|
.privacy-heading {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.75rem;
|
||||||
font-size: 1.875rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(2.75rem, 9vw, 5rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2.25rem;
|
letter-spacing: -0.05em;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.last-updated {
|
.last-updated {
|
||||||
margin-bottom: 3rem;
|
margin-bottom: clamp(3rem, 7vw, 5rem);
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font-size: 0.875rem;
|
font-family: var(--font-utility);
|
||||||
line-height: 1.25rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose {
|
.prose {
|
||||||
|
max-width: 48rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
line-height: 1.75;
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
.prose h2 {
|
|
||||||
color: var(--color-text);
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-top: 2.5rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
.prose h3 {
|
|
||||||
color: var(--color-text);
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
line-height: 1.8;
|
||||||
margin-top: 1.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose h2 {
|
||||||
|
margin-top: 3.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(1.5rem, 4vw, 2rem);
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose h3 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 0.625rem;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 1.0625rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.prose p {
|
.prose p {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose ul {
|
.prose ul {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: 1.25rem 1.25rem 1.25rem 2.75rem;
|
||||||
|
background: var(--color-surface);
|
||||||
list-style: disc;
|
list-style: disc;
|
||||||
padding-left: 1.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
}
|
||||||
.prose li {
|
|
||||||
margin-bottom: 0.25rem;
|
.prose li + li {
|
||||||
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose a {
|
.prose a {
|
||||||
color: var(--color-accent);
|
color: var(--color-text);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-underline-offset: 2px;
|
text-decoration-color: var(--color-text-subtle);
|
||||||
}
|
text-underline-offset: 0.2em;
|
||||||
.prose a:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
.prose a:hover,
|
||||||
.privacy-article {
|
.prose a:focus-visible {
|
||||||
padding-block: 6rem;
|
text-decoration-color: var(--color-text);
|
||||||
}
|
outline: none;
|
||||||
|
|
||||||
.privacy-heading {
|
|
||||||
font-size: 2.25rem;
|
|
||||||
line-height: 2.5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -75,68 +75,87 @@
|
|||||||
<style>
|
<style>
|
||||||
.scan-page {
|
.scan-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 4rem 1.5rem;
|
padding: var(--page-gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-card {
|
.scan-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: 24rem;
|
width: min(100%, 32rem);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
padding: clamp(2rem, 8vw, 4rem);
|
||||||
|
background: var(--color-surface);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-logo {
|
.scan-logo {
|
||||||
margin-bottom: 1.5rem;
|
display: flex;
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
background: var(--color-surface-highest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-logo :global(svg) {
|
.scan-logo :global(svg) {
|
||||||
width: 4rem;
|
width: 2.5rem;
|
||||||
height: 4rem;
|
height: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-heading {
|
.scan-heading {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.75rem;
|
||||||
font-size: 1.5rem;
|
font-family: var(--font-display);
|
||||||
|
font-size: clamp(2rem, 8vw, 3.25rem);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 2rem;
|
letter-spacing: -0.045em;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-description {
|
.scan-description {
|
||||||
|
max-width: 24rem;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
|
line-height: 1.65;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-buttons {
|
.store-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.75rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button {
|
.store-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
min-height: 3rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.625rem;
|
gap: 0.625rem;
|
||||||
padding: 0.75rem 1.25rem;
|
border-radius: var(--radius-pill);
|
||||||
border-radius: 9999px;
|
padding-inline: 1.25rem;
|
||||||
background: #fff;
|
color: var(--color-on-primary);
|
||||||
color: #111827;
|
background: var(--color-text);
|
||||||
font-size: 0.875rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
line-height: 1.25rem;
|
transition:
|
||||||
transition: background-color 150ms ease;
|
border-radius var(--motion-expressive) var(--ease-standard),
|
||||||
|
background-color var(--motion-fast) var(--ease-standard);
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button:hover {
|
.store-button:hover,
|
||||||
background: #f3f4f6;
|
.store-button:focus-visible {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: #fff;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.store-button :global(svg) {
|
.store-button :global(svg) {
|
||||||
width: 1.25rem;
|
width: 1.125rem;
|
||||||
height: 1.25rem;
|
height: 1.125rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user