t0ng7u d146e45e2f ⚖️ chore(web/default): add reusable copyright header tooling
Add a Bun script to apply and normalize AGPL copyright headers across the default frontend source files.

The script keeps headers idempotent, upgrades existing headers to the 2023-2026 QuantumNous range, and is exposed through `bun run copyright` for future maintenance.
2026-05-09 11:35:07 +08:00

586 lines
13 KiB
CSS
Vendored

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
@import 'tailwindcss';
@import 'tw-animate-css';
@import 'shadcn/tailwind.css';
@import '@fontsource-variable/public-sans';
@import './theme.css';
@import './theme-presets.css';
/* Shiki dual themes: token colors follow dark theme (pre background stays `bg-background` on the block) */
@layer components {
.dark .shiki span {
color: var(--shiki-dark) !important;
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
}
@layer base {
* {
@apply border-border outline-ring/50;
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
html {
@apply overflow-x-hidden font-sans;
}
body {
@apply bg-background text-foreground has-[div[data-variant='inset']]:bg-sidebar min-h-svh w-full font-sans;
}
/* Keep sticky headers stable while primitives lock body scrolling. */
body[data-scroll-locked] {
overflow: unset !important;
}
/* Cursor pointer for buttons */
button:not(:disabled),
[role='button']:not(:disabled) {
cursor: pointer;
}
/* Prevent focus zoom on mobile devices */
@media screen and (max-width: 767px) {
input,
select,
textarea {
font-size: 16px !important;
}
}
}
/* Vercel Geist-style skeleton shimmer */
.skeleton-shimmer {
background: linear-gradient(
90deg,
var(--skeleton-base) 0%,
var(--skeleton-base) 33%,
var(--skeleton-highlight) 50%,
var(--skeleton-base) 67%,
var(--skeleton-base) 100%
);
background-size: 300% 100%;
animation: skeleton-shimmer 1.8s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
0% {
background-position: 100% 50%;
}
100% {
background-position: -100% 50%;
}
}
/* Override auto-skeleton-react inline styles with Vercel shimmer */
.auto-skeleton-fade [class^='skeleton-'] {
background: linear-gradient(
90deg,
var(--skeleton-base) 0%,
var(--skeleton-base) 33%,
var(--skeleton-highlight) 50%,
var(--skeleton-base) 67%,
var(--skeleton-base) 100%
) !important;
background-size: 300% 100% !important;
animation: skeleton-shimmer 1.8s ease-in-out infinite !important;
}
@media (prefers-reduced-motion: reduce) {
.skeleton-shimmer,
.auto-skeleton-fade [class^='skeleton-'] {
animation: none !important;
background: var(--skeleton-base) !important;
}
}
@utility container {
margin-inline: auto;
padding-inline: 2rem;
}
@utility max-w-container {
max-width: 1280px;
}
@utility max-w-container-lg {
max-width: 1536px;
}
@utility no-scrollbar {
/* Hide scrollbar for Chrome, Safari and Opera */
&::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
@utility hover-scrollbar {
/* Hide scrollbar by default */
scrollbar-width: thin;
scrollbar-color: transparent transparent;
/* Show scrollbar on hover */
&:hover {
scrollbar-color: var(--border) transparent;
}
/* Webkit browsers (Chrome, Safari, Edge) */
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 4px;
}
&:hover::-webkit-scrollbar-thumb {
background-color: var(--border);
}
&:hover::-webkit-scrollbar-thumb:hover {
background-color: color-mix(in oklch, var(--border) 60%, transparent);
}
}
@utility faded-bottom {
@apply after:pointer-events-none after:absolute after:start-0 after:bottom-0 after:hidden after:h-32 after:w-full after:rounded-b-2xl after:bg-[linear-gradient(180deg,_transparent_10%,_var(--background)_70%)] md:after:block;
}
.CollapsibleContent {
overflow: hidden;
}
@media (prefers-reduced-motion: no-preference) {
.CollapsibleContent[data-open] {
animation: slideDown 300ms ease-out;
}
.CollapsibleContent:not([data-open]) {
animation: slideUp 300ms ease-out;
}
}
@keyframes slideDown {
from {
height: 0;
}
to {
height: var(--collapsible-panel-height);
}
}
@keyframes slideUp {
from {
height: var(--collapsible-panel-height);
}
to {
height: 0;
}
}
/* Launch UI Animations and Effects - Matching Template Exactly */
@layer utilities {
/* Gradient utilities */
.bg-radial {
background-image: radial-gradient(var(--tw-gradient-stops));
}
/* Glass morphism effects - matching Launch UI template */
.glass-1 {
@apply border-border from-card/80 to-card/40 dark:border-border/10 dark:border-b-border/5 dark:border-t-border/20 dark:from-card/5 dark:to-card/0 border bg-linear-to-b;
}
.glass-2 {
@apply border-border from-card/100 to-card/80 dark:border-border/10 dark:border-b-border/5 dark:border-t-border/20 dark:from-card/10 dark:to-card/5 border bg-linear-to-b;
}
.glass-3 {
@apply border-border from-card/30 to-card/20 dark:border-border/10 dark:border-t-border/20 dark:border-b-border/5 dark:from-primary/5 dark:to-primary/2 border bg-linear-to-b;
}
.glass-4 {
@apply border-border border-b-input/90 from-card/60 to-card/20 dark:border-border/10 dark:border-t-border/30 dark:from-primary/10 dark:to-primary/5 border bg-linear-to-b dark:border-b-0;
}
.glass-5 {
@apply border-border border-b-input from-card/100 to-card/20 dark:border-border/10 dark:border-t-border/30 dark:from-primary/15 dark:to-primary/5 border bg-linear-to-b dark:border-b-0;
}
/* Fade effects - matching Launch UI template */
.fade-x {
mask-image: linear-gradient(
to right,
transparent 0%,
black 25%,
black 75%,
transparent 100%
);
}
.fade-y {
mask-image: linear-gradient(
to top,
transparent 0%,
black 25%,
black 75%,
transparent 100%
);
}
.fade-top {
mask-image: linear-gradient(to bottom, transparent 0%, black 35%);
}
.fade-bottom {
mask-image: linear-gradient(to top, transparent 0%, black 35%);
}
.fade-top-lg {
mask-image: linear-gradient(to bottom, transparent 15%, black 100%);
}
.fade-bottom-lg {
mask-image: linear-gradient(to top, transparent 15%, black 100%);
}
.fade-left {
mask-image: linear-gradient(to right, transparent 0%, black 35%);
}
.fade-right {
mask-image: linear-gradient(to left, transparent 0%, black 35%);
}
.fade-left-lg {
mask-image: linear-gradient(to right, transparent 15%, black 100%);
}
.fade-right-lg {
mask-image: linear-gradient(to left, transparent 15%, black 100%);
}
/* Animations */
.animate-appear {
animation: appear 0.6s forwards ease-out;
}
.animate-appear-zoom {
animation: appear-zoom 0.6s forwards ease-out;
}
.animation-delay-100 {
animation-delay: 100ms;
}
.animation-delay-300 {
animation-delay: 300ms;
}
.animation-delay-700 {
animation-delay: 700ms;
}
.animation-delay-1000 {
animation-delay: 1000ms;
}
@keyframes appear {
0% {
opacity: 0;
transform: translateY(1rem);
filter: blur(0.5rem);
}
50% {
filter: blur(0);
}
100% {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
@keyframes appear-zoom {
0% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* Vertical scroll animation for icon lists */
@keyframes scroll-up {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
.animate-scroll-up {
animation: scroll-up 20s linear infinite;
will-change: transform;
}
.animate-scroll-down {
animation: scroll-up 20s linear infinite reverse;
will-change: transform;
}
/* Pause animation on hover */
.scroll-container:hover .animate-scroll-up,
.scroll-container:hover .animate-scroll-down {
animation-play-state: paused;
}
}
@media (prefers-reduced-motion: reduce) {
.animate-appear,
.animate-appear-zoom,
.animate-scroll-up,
.animate-scroll-down,
[data-slot='table'] tbody tr {
animation: none !important;
opacity: 1;
transform: none;
}
}
/* ── Landing page scroll-triggered animations ── */
@keyframes landing-fade-up {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes landing-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes landing-scale-in {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes landing-fade-left {
from {
opacity: 0;
transform: translateX(24px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes landing-fade-right {
from {
opacity: 0;
transform: translateX(-24px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.landing-animate-fade-up {
animation: landing-fade-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.landing-animate-fade-in {
animation: landing-fade-in 0.6s ease-out both;
}
.landing-animate-scale-in {
animation: landing-scale-in 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.landing-animate-fade-left {
animation: landing-fade-left 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.landing-animate-fade-right {
animation: landing-fade-right 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@media (prefers-reduced-motion: reduce) {
.landing-animate-fade-up,
.landing-animate-fade-in,
.landing-animate-scale-in,
.landing-animate-fade-left,
.landing-animate-fade-right {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
}
/* Micro-interactions — Vercel-style subtle hover/active feedback */
@media (prefers-reduced-motion: no-preference) {
[data-slot='card'] {
transition:
transform 150ms ease,
box-shadow 150ms ease;
}
@media (min-width: 641px) {
[data-slot='card']:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgb(0 0 0 / 0.06);
}
.dark [data-slot='card']:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.3);
}
}
button:not(:disabled):active,
[role='button']:not(:disabled):active {
transform: scale(0.98);
}
[data-slot='table'] tr {
transition: background-color 120ms ease;
}
/* Table row stagger-in animation */
[data-slot='table'] tbody tr {
animation: tableRowEnter 0.2s cubic-bezier(0.33, 1, 0.68, 1) both;
}
[data-slot='table'] tbody tr:nth-child(2) {
animation-delay: 25ms;
}
[data-slot='table'] tbody tr:nth-child(3) {
animation-delay: 50ms;
}
[data-slot='table'] tbody tr:nth-child(4) {
animation-delay: 75ms;
}
[data-slot='table'] tbody tr:nth-child(5) {
animation-delay: 100ms;
}
[data-slot='table'] tbody tr:nth-child(6) {
animation-delay: 125ms;
}
[data-slot='table'] tbody tr:nth-child(7) {
animation-delay: 150ms;
}
[data-slot='table'] tbody tr:nth-child(8) {
animation-delay: 175ms;
}
[data-slot='table'] tbody tr:nth-child(9) {
animation-delay: 200ms;
}
[data-slot='table'] tbody tr:nth-child(10) {
animation-delay: 225ms;
}
[data-slot='table'] tbody tr:nth-child(n + 11) {
animation-delay: 250ms;
}
}
@keyframes tableRowEnter {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Tabular numbers for data-heavy contexts */
[data-slot='table'] td,
.tabular-nums {
font-variant-numeric: tabular-nums;
}
/* Long list rendering optimization */
.content-auto {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
/* ── Hero terminal demo animations ── */
@keyframes terminal-demo-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
.terminal-demo-blink {
animation: terminal-demo-blink 1s step-end infinite;
}
@keyframes terminal-demo-spin {
to {
transform: rotate(360deg);
}
}
.terminal-demo-spin {
animation: terminal-demo-spin 0.8s linear infinite;
}
@keyframes terminal-demo-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
.terminal-demo-pulse {
animation: terminal-demo-pulse 1.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.terminal-demo-blink,
.terminal-demo-spin,
.terminal-demo-pulse {
animation: none !important;
}
}