Security Improvements (P0.0-P0.4): - P0.0: Migrate to Gemini-only AI stack (simplified, single billing) - P0.1: Fix CORS to restrict allowed origins from env (was *) - P0.2: Remove hardcoded dev password, require env var - P0.3: Add rate limiting (slowapi) - 3-10 req/min on sensitive endpoints - P0.4: Add request size limits (100MB default via middleware) New Features: - Unified LLM service with Google Gemini priority - OXML geometry extractor for layout parsing - TSX validator for generated React components - Client ID support in presentation requests with access control - Configurable LLM/image timeouts via env vars Modern Design System (P0.9 - partial): - Enhanced CSS design tokens (primary, semantic colors, shadows) - Typography scale (h1-h4, body variants, caption) - Modern animations (fadeIn, slideIn, scaleIn) - Updated Button component with better variants and hover effects - Created unified Card and StatusBadge components - Applied design system to Dashboard and Settings pages Backend Improvements: - Master deck parser simplification - Slide-to-HTML endpoint cleanup (325 lines removed) - Better error handling in prompts endpoint Frontend Improvements: - Settings UI simplified to show only Google/Gemini - Dashboard uses CSS variables instead of hardcoded colors - Improved button transitions and hover states Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
867 lines
No EOL
17 KiB
CSS
867 lines
No EOL
17 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
|
|
body {
|
|
font-family: var(--font-inter), var(--font-roboto), sans-serif;
|
|
}
|
|
|
|
@layer utilities {
|
|
.text-balance {
|
|
text-wrap: balance;
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
:root {
|
|
/* ========================================
|
|
OLIVER DECKFORGE DESIGN SYSTEM
|
|
Modern, professional design tokens
|
|
======================================== */
|
|
|
|
/* Primary Brand Colors */
|
|
--brand-primary: #5146E5;
|
|
--brand-secondary: #E9E8F8;
|
|
--brand-accent: #3D35B0;
|
|
|
|
/* Primary (Brand Purple) */
|
|
--primary: 250 70% 60%; /* #5146E5 */
|
|
--primary-hover: 250 70% 55%;
|
|
--primary-light: 250 70% 95%;
|
|
--primary-foreground: 0 0% 100%;
|
|
|
|
/* Semantic Colors */
|
|
--success: 142 76% 36%; /* Green */
|
|
--success-foreground: 0 0% 100%;
|
|
--warning: 38 92% 50%; /* Amber */
|
|
--warning-foreground: 0 0% 100%;
|
|
--error: 0 84% 60%; /* Red */
|
|
--error-foreground: 0 0% 100%;
|
|
--info: 199 89% 48%; /* Blue */
|
|
--info-foreground: 0 0% 100%;
|
|
|
|
/* Neutrals */
|
|
--background: 0 0% 100%; /* White */
|
|
--foreground: 0 0% 10%; /* Near black */
|
|
--surface: 0 0% 98%; /* Light gray bg */
|
|
--surface-hover: 0 0% 95%;
|
|
--text-primary: 0 0% 10%;
|
|
--text-secondary: 0 0% 45%;
|
|
--text-tertiary: 0 0% 65%;
|
|
|
|
/* Shadcn UI compatibility */
|
|
--card: 0 0% 100%;
|
|
--card-foreground: 0 0% 10%;
|
|
--popover: 0 0% 100%;
|
|
--popover-foreground: 0 0% 10%;
|
|
--secondary: 0 0% 96%;
|
|
--secondary-foreground: 0 0% 10%;
|
|
--muted: 0 0% 96%;
|
|
--muted-foreground: 0 0% 45%;
|
|
--accent: 250 70% 95%;
|
|
--accent-foreground: 250 70% 20%;
|
|
--destructive: 0 84% 60%;
|
|
--destructive-foreground: 0 0% 98%;
|
|
|
|
/* Borders & Inputs */
|
|
--border: 0 0% 90%;
|
|
--input: 0 0% 90%;
|
|
--ring: 250 70% 60%;
|
|
|
|
/* Charts */
|
|
--chart-1: 250 70% 60%; /* Brand purple */
|
|
--chart-2: 142 76% 36%; /* Success green */
|
|
--chart-3: 38 92% 50%; /* Warning amber */
|
|
--chart-4: 199 89% 48%; /* Info blue */
|
|
--chart-5: 0 84% 60%; /* Error red */
|
|
|
|
/* Shadows (HSL format for Tailwind) */
|
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
|
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
|
|
|
|
/* Radius */
|
|
--radius: 0.75rem; /* 12px - more modern */
|
|
}
|
|
|
|
.dark {
|
|
--background: 222 47% 11%; /* Dark blue-gray */
|
|
--foreground: 0 0% 95%;
|
|
--surface: 217 33% 17%;
|
|
--surface-hover: 217 33% 20%;
|
|
--text-primary: 0 0% 95%;
|
|
--text-secondary: 0 0% 65%;
|
|
--text-tertiary: 0 0% 45%;
|
|
|
|
/* Shadcn UI dark mode */
|
|
--card: 217 33% 17%;
|
|
--card-foreground: 0 0% 95%;
|
|
--popover: 217 33% 17%;
|
|
--popover-foreground: 0 0% 95%;
|
|
--primary: 250 70% 60%;
|
|
--primary-foreground: 0 0% 100%;
|
|
--secondary: 217 33% 25%;
|
|
--secondary-foreground: 0 0% 95%;
|
|
--muted: 217 33% 25%;
|
|
--muted-foreground: 0 0% 65%;
|
|
--accent: 250 70% 30%;
|
|
--accent-foreground: 0 0% 95%;
|
|
--destructive: 0 62% 40%;
|
|
--destructive-foreground: 0 0% 98%;
|
|
|
|
--border: 217 33% 25%;
|
|
--input: 217 33% 25%;
|
|
--ring: 250 70% 60%;
|
|
|
|
--chart-1: 250 70% 60%;
|
|
--chart-2: 142 76% 46%;
|
|
--chart-3: 38 92% 60%;
|
|
--chart-4: 199 89% 58%;
|
|
--chart-5: 0 84% 70%;
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
}
|
|
|
|
/* Typography Scale */
|
|
.h1 {
|
|
@apply text-4xl font-bold tracking-tight;
|
|
}
|
|
|
|
.h2 {
|
|
@apply text-3xl font-semibold tracking-tight;
|
|
}
|
|
|
|
.h3 {
|
|
@apply text-2xl font-semibold;
|
|
}
|
|
|
|
.h4 {
|
|
@apply text-xl font-semibold;
|
|
}
|
|
|
|
.body-lg {
|
|
@apply text-lg leading-relaxed;
|
|
}
|
|
|
|
.body {
|
|
@apply text-base leading-normal;
|
|
}
|
|
|
|
.body-sm {
|
|
@apply text-sm leading-snug;
|
|
}
|
|
|
|
.caption {
|
|
@apply text-xs text-muted-foreground;
|
|
}
|
|
}
|
|
|
|
strong {
|
|
@apply font-black;
|
|
}
|
|
|
|
/* Modern UI Animations */
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(-100%);
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@keyframes scaleIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0.95);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
.animate-fadeIn {
|
|
animation: fadeIn 0.3s ease-out;
|
|
}
|
|
|
|
.animate-slideIn {
|
|
animation: slideIn 0.3s ease-out;
|
|
}
|
|
|
|
.animate-scaleIn {
|
|
animation: scaleIn 0.2s ease-out;
|
|
}
|
|
|
|
|
|
|
|
::selection {
|
|
background-color: hsl(var(--chart-1));
|
|
color: white;
|
|
}
|
|
|
|
/* Hide input number arrows */
|
|
input[type="number"]::-webkit-inner-spin-button,
|
|
input[type="number"]::-webkit-outer-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
|
|
input[type="number"] {
|
|
-moz-appearance: textfield;
|
|
}
|
|
|
|
thead,
|
|
tbody tr {
|
|
display: table;
|
|
width: 100%;
|
|
table-layout: fixed;
|
|
/* even columns width , fix width of table too*/
|
|
}
|
|
|
|
thead {
|
|
width: calc(100% - 1em)
|
|
/* scrollbar is average 1em/16px width, remove it from thead width */
|
|
}
|
|
|
|
/* Add this to your global CSS or a specific CSS module */
|
|
@keyframes typing {
|
|
from {
|
|
width: 0;
|
|
}
|
|
|
|
to {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.typing-effect {
|
|
overflow: hidden;
|
|
/* Ensures the text is hidden until revealed */
|
|
white-space: nowrap;
|
|
/* Prevents text from wrapping */
|
|
display: inline-block;
|
|
/* Ensures the width is respected */
|
|
animation: typing 2s steps(10, end);
|
|
/* Adjust duration and steps for effect */
|
|
animation-fill-mode: forwards;
|
|
/* Retain the final state of the animation */
|
|
animation-delay: 1s;
|
|
/* Optional: delay before starting the animation */
|
|
}
|
|
|
|
.typing-effect-complete {
|
|
border-right: none;
|
|
/* Remove the cursor after animation */
|
|
}
|
|
|
|
.blinking-cursor {
|
|
animation: blink 1s step-end infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
|
|
from,
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.hide-scrollbar::-webkit-scrollbar {
|
|
@apply hidden;
|
|
}
|
|
|
|
.hide-scrollbar {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
|
|
.custom_scrollbar::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.custom_scrollbar::-webkit-scrollbar-thumb {
|
|
background-color: #d1d5db;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.custom_scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background-color: #9ca3af;
|
|
}
|
|
|
|
.custom_scrollbar::-webkit-scrollbar-track {
|
|
background-color: #f9fafb;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.custom_scrollbar::-webkit-scrollbar-corner {
|
|
background-color: transparent;
|
|
}
|
|
|
|
/* Firefox scrollbar styling */
|
|
.custom_scrollbar {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: #d1d5db #f9fafb;
|
|
}
|
|
|
|
|
|
/* word animation */
|
|
|
|
@keyframes slideUp {
|
|
0% {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
100% {
|
|
transform: translateY(-50%);
|
|
}
|
|
}
|
|
|
|
@keyframes slideDown {
|
|
0% {
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
100% {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.animate-slideUp {
|
|
animation: slideUp 20s linear infinite;
|
|
}
|
|
|
|
.animate-slideDown {
|
|
animation: slideDown 20s linear infinite;
|
|
}
|
|
|
|
/* Add hover pause */
|
|
.animate-slideUp:hover,
|
|
.animate-slideDown:hover {
|
|
animation-play-state: paused;
|
|
}
|
|
|
|
/* box animation */
|
|
|
|
.research-mode-bg {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.research-mode-bg::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
background: radial-gradient(circle, rgba(56, 48, 161, 0.1) 0%, transparent 70%);
|
|
transform: translate(-50%, -50%);
|
|
border-radius: 50%;
|
|
animation: rippleEffect 0.6s ease-out forwards;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@keyframes rippleEffect {
|
|
0% {
|
|
width: 0;
|
|
height: 0;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
100% {
|
|
width: 200%;
|
|
height: 200%;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Markdown Styles */
|
|
.markdown-content {
|
|
@apply prose prose-slate max-w-none;
|
|
}
|
|
|
|
/* .markdown-content h1 {
|
|
@apply text-xl font-bold mb-4 text-gray-900;
|
|
}
|
|
|
|
.markdown-content h2 {
|
|
@apply text-lg font-bold mb-3 text-gray-900;
|
|
}
|
|
|
|
.markdown-content h3 {
|
|
@apply text-base font-bold mb-2 text-gray-900;
|
|
}
|
|
|
|
.markdown-content h4 {
|
|
@apply text-sm font-bold mb-2 text-gray-900;
|
|
}
|
|
|
|
.markdown-content h5 {
|
|
@apply text-xs font-bold mb-1 text-gray-900;
|
|
}
|
|
|
|
.markdown-content h6 {
|
|
@apply text-xs font-semibold mb-1 text-gray-900;
|
|
}
|
|
|
|
.markdown-content p {
|
|
@apply mb-4 text-base text-gray-700;
|
|
}
|
|
|
|
.markdown-content ul {
|
|
@apply list-disc pl-6 mb-4;
|
|
}
|
|
|
|
.markdown-content ol {
|
|
@apply list-decimal pl-6 mb-4;
|
|
}
|
|
|
|
.markdown-content li {
|
|
@apply mb-1;
|
|
}
|
|
|
|
.markdown-content strong,
|
|
.markdown-content b {
|
|
@apply font-bold text-gray-900;
|
|
}
|
|
|
|
.markdown-content em {
|
|
@apply italic;
|
|
}
|
|
|
|
.markdown-content blockquote {
|
|
@apply border-l-4 border-gray-300 pl-4 italic my-4;
|
|
}
|
|
|
|
.markdown-content code {
|
|
@apply bg-gray-100 px-1 py-0.5 rounded font-mono text-sm;
|
|
}
|
|
|
|
.markdown-content pre {
|
|
@apply bg-gray-100 p-4 rounded-lg my-4 overflow-x-auto;
|
|
}
|
|
|
|
.markdown-content a {
|
|
@apply text-blue-600 hover:text-blue-800 underline;
|
|
}
|
|
|
|
.markdown-content table {
|
|
@apply min-w-full border border-gray-300 my-4;
|
|
}
|
|
|
|
.markdown-content th {
|
|
@apply bg-gray-100 border border-gray-300 px-4 py-2 font-bold;
|
|
}
|
|
|
|
.markdown-content td {
|
|
@apply border border-gray-300 px-4 py-2;
|
|
} */
|
|
|
|
/* Override Tailwind Typography prose heading sizes for markdown editor */
|
|
.prose h1 {
|
|
font-size: 18px !important;
|
|
font-weight: bold !important;
|
|
margin-bottom: 1rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
.prose h2 {
|
|
font-size: 16px !important;
|
|
font-weight: bold !important;
|
|
margin-bottom: 0.75rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
.prose h3 {
|
|
font-size: 14px !important;
|
|
font-weight: bold !important;
|
|
margin-bottom: 0.5rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
.prose h4 {
|
|
font-size: 12px !important;
|
|
font-weight: bold !important;
|
|
margin-bottom: 0.5rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
.prose h5 {
|
|
font-size: 11px !important;
|
|
font-weight: bold !important;
|
|
margin-bottom: 0.25rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
.prose h6 {
|
|
font-size: 10px !important;
|
|
font-weight: 600 !important;
|
|
margin-bottom: 0.25rem !important;
|
|
color: rgb(17 24 39) !important;
|
|
}
|
|
|
|
/* MDXEditor styles */
|
|
.mdxeditor-toolbar-group {
|
|
@apply flex items-center gap-1 p-1;
|
|
}
|
|
|
|
.mdxeditor-toolbar {
|
|
@apply flex items-center gap-2 p-2 border-b;
|
|
}
|
|
|
|
.mdxeditor-button {
|
|
@apply p-1 rounded hover:bg-gray-100 transition-colors;
|
|
}
|
|
|
|
.mdxeditor-button[data-active=true] {
|
|
@apply bg-gray-100;
|
|
}
|
|
|
|
/* tippy-box */
|
|
.tippy-box {
|
|
max-width: 100% !important;
|
|
}
|
|
|
|
.is-editor-empty:first-child::before {
|
|
color: #adb5bd;
|
|
content: attr(data-placeholder);
|
|
float: left;
|
|
height: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* code editor */
|
|
.container_editor_area {
|
|
tab-size: 4ch;
|
|
max-height: 400px;
|
|
overflow: auto;
|
|
margin: 1.67em 0;
|
|
}
|
|
|
|
.container__content {
|
|
width: 440px;
|
|
max-width: 100%;
|
|
padding: 10px;
|
|
text-align: center;
|
|
}
|
|
|
|
.container__content_area {
|
|
tab-size: 4ch;
|
|
/* max-height: 600px; */
|
|
overflow: auto;
|
|
margin: 1.67em 0;
|
|
}
|
|
|
|
.container__editor {
|
|
|
|
font-variant-ligatures: common-ligatures;
|
|
background-color: #fafafa;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.container__editor textarea {
|
|
outline: 0;
|
|
}
|
|
/* Syntax highlighting */
|
|
.token.comment,
|
|
.token.prolog,
|
|
.token.doctype,
|
|
.token.cdata {
|
|
color: #90a4ae;
|
|
}
|
|
.token.punctuation {
|
|
color: #9e9e9e;
|
|
}
|
|
.namespace {
|
|
opacity: 0.7;
|
|
}
|
|
.token.property,
|
|
.token.tag,
|
|
.token.boolean,
|
|
.token.number,
|
|
.token.constant,
|
|
.token.symbol,
|
|
.token.deleted {
|
|
color: #e91e63;
|
|
}
|
|
.token.selector,
|
|
.token.attr-name,
|
|
.token.string,
|
|
.token.char,
|
|
.token.builtin,
|
|
.token.inserted {
|
|
color: #4caf50;
|
|
}
|
|
.token.operator,
|
|
.token.entity,
|
|
.token.url,
|
|
.language-css .token.string,
|
|
.style .token.string {
|
|
color: #795548;
|
|
}
|
|
.token.atrule,
|
|
.token.attr-value,
|
|
.token.keyword {
|
|
color: #3f51b5;
|
|
}
|
|
.token.function {
|
|
color: #f44336;
|
|
}
|
|
.token.regex,
|
|
.token.important,
|
|
.token.variable {
|
|
color: #ff9800;
|
|
}
|
|
.token.important,
|
|
.token.bold {
|
|
font-weight: bold;
|
|
}
|
|
.token.italic {
|
|
font-style: italic;
|
|
}
|
|
.token.entity {
|
|
cursor: help;
|
|
}
|
|
|
|
/* Hamster wheel loading animation — from Uiverse.io by Nawsome */
|
|
.wheel-and-hamster {
|
|
--dur: 1s;
|
|
position: relative;
|
|
width: 12em;
|
|
height: 12em;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.wheel,
|
|
.hamster,
|
|
.hamster div,
|
|
.spoke {
|
|
position: absolute;
|
|
}
|
|
|
|
.wheel,
|
|
.spoke {
|
|
border-radius: 50%;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.wheel {
|
|
background: radial-gradient(100% 100% at center,hsla(0,0%,60%,0) 47.8%,hsl(0,0%,60%) 48%);
|
|
z-index: 2;
|
|
}
|
|
|
|
.hamster {
|
|
animation: hamster var(--dur) ease-in-out infinite;
|
|
top: 50%;
|
|
left: calc(50% - 3.5em);
|
|
width: 7em;
|
|
height: 3.75em;
|
|
transform: rotate(4deg) translate(-0.8em,1.85em);
|
|
transform-origin: 50% 0;
|
|
z-index: 1;
|
|
}
|
|
|
|
.hamster__head {
|
|
animation: hamsterHead var(--dur) ease-in-out infinite;
|
|
background: hsl(30,90%,55%);
|
|
border-radius: 70% 30% 0 100% / 40% 25% 25% 60%;
|
|
box-shadow: 0 -0.25em 0 hsl(30,90%,80%) inset,
|
|
0.75em -1.55em 0 hsl(30,90%,90%) inset;
|
|
top: 0;
|
|
left: -1.5em;
|
|
width: 2.75em;
|
|
height: 2.5em;
|
|
transform-origin: 100% 50%;
|
|
}
|
|
|
|
.hamster__ear {
|
|
animation: hamsterEar var(--dur) ease-in-out infinite;
|
|
background: hsl(0,90%,85%);
|
|
border-radius: 50%;
|
|
box-shadow: -0.25em 0 hsl(30,90%,55%) inset;
|
|
top: -0.25em;
|
|
right: -0.25em;
|
|
width: 0.75em;
|
|
height: 0.75em;
|
|
transform-origin: 50% 75%;
|
|
}
|
|
|
|
.hamster__eye {
|
|
animation: hamsterEye var(--dur) linear infinite;
|
|
background-color: hsl(0,0%,0%);
|
|
border-radius: 50%;
|
|
top: 0.375em;
|
|
left: 1.25em;
|
|
width: 0.5em;
|
|
height: 0.5em;
|
|
}
|
|
|
|
.hamster__nose {
|
|
background: hsl(0,90%,75%);
|
|
border-radius: 35% 65% 85% 15% / 70% 50% 50% 30%;
|
|
top: 0.75em;
|
|
left: 0;
|
|
width: 0.2em;
|
|
height: 0.25em;
|
|
}
|
|
|
|
.hamster__body {
|
|
animation: hamsterBody var(--dur) ease-in-out infinite;
|
|
background: hsl(30,90%,90%);
|
|
border-radius: 50% 30% 50% 30% / 15% 60% 40% 40%;
|
|
box-shadow: 0.1em 0.75em 0 hsl(30,90%,55%) inset,
|
|
0.15em -0.5em 0 hsl(30,90%,80%) inset;
|
|
top: 0.25em;
|
|
left: 1.5em;
|
|
width: 4.5em;
|
|
height: 3em;
|
|
transform-origin: 17% 50%;
|
|
transform-style: preserve-3d;
|
|
}
|
|
|
|
.hamster__limb--fr,
|
|
.hamster__limb--fl {
|
|
clip-path: polygon(0 0,100% 0,70% 80%,60% 100%,0% 100%,40% 80%);
|
|
top: 2em;
|
|
left: 0.5em;
|
|
width: 1em;
|
|
height: 1.5em;
|
|
transform-origin: 50% 0;
|
|
}
|
|
|
|
.hamster__limb--fr {
|
|
animation: hamsterFRLimb var(--dur) linear infinite;
|
|
background: linear-gradient(hsl(30,90%,80%) 80%,hsl(0,90%,75%) 80%);
|
|
transform: rotate(15deg) translateZ(-1px);
|
|
}
|
|
|
|
.hamster__limb--fl {
|
|
animation: hamsterFLLimb var(--dur) linear infinite;
|
|
background: linear-gradient(hsl(30,90%,90%) 80%,hsl(0,90%,85%) 80%);
|
|
transform: rotate(15deg);
|
|
}
|
|
|
|
.hamster__limb--br,
|
|
.hamster__limb--bl {
|
|
border-radius: 0.75em 0.75em 0 0;
|
|
clip-path: polygon(0 0,100% 0,100% 30%,70% 90%,70% 100%,30% 100%,40% 90%,0% 30%);
|
|
top: 1em;
|
|
left: 2.8em;
|
|
width: 1.5em;
|
|
height: 2.5em;
|
|
transform-origin: 50% 30%;
|
|
}
|
|
|
|
.hamster__limb--br {
|
|
animation: hamsterBRLimb var(--dur) linear infinite;
|
|
background: linear-gradient(hsl(30,90%,80%) 90%,hsl(0,90%,75%) 90%);
|
|
transform: rotate(-25deg) translateZ(-1px);
|
|
}
|
|
|
|
.hamster__limb--bl {
|
|
animation: hamsterBLLimb var(--dur) linear infinite;
|
|
background: linear-gradient(hsl(30,90%,90%) 90%,hsl(0,90%,85%) 90%);
|
|
transform: rotate(-25deg);
|
|
}
|
|
|
|
.hamster__tail {
|
|
animation: hamsterTail var(--dur) linear infinite;
|
|
background: hsl(0,90%,85%);
|
|
border-radius: 0.25em 50% 50% 0.25em;
|
|
box-shadow: 0 -0.2em 0 hsl(0,90%,75%) inset;
|
|
top: 1.5em;
|
|
right: -0.5em;
|
|
width: 1em;
|
|
height: 0.5em;
|
|
transform: rotate(30deg) translateZ(-1px);
|
|
transform-origin: 0.25em 0.25em;
|
|
}
|
|
|
|
.spoke {
|
|
animation: spoke var(--dur) linear infinite;
|
|
background: radial-gradient(100% 100% at center,hsl(0,0%,60%) 4.8%,hsla(0,0%,60%,0) 5%),
|
|
linear-gradient(hsla(0,0%,55%,0) 46.9%,hsl(0,0%,65%) 47% 52.9%,hsla(0,0%,65%,0) 53%) 50% 50% / 99% 99% no-repeat;
|
|
}
|
|
|
|
@keyframes hamster {
|
|
from, to { transform: rotate(4deg) translate(-0.8em,1.85em); }
|
|
50% { transform: rotate(0) translate(-0.8em,1.85em); }
|
|
}
|
|
|
|
@keyframes hamsterHead {
|
|
from, 25%, 50%, 75%, to { transform: rotate(0); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(8deg); }
|
|
}
|
|
|
|
@keyframes hamsterEye {
|
|
from, 90%, to { transform: scaleY(1); }
|
|
95% { transform: scaleY(0); }
|
|
}
|
|
|
|
@keyframes hamsterEar {
|
|
from, 25%, 50%, 75%, to { transform: rotate(0); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(12deg); }
|
|
}
|
|
|
|
@keyframes hamsterBody {
|
|
from, 25%, 50%, 75%, to { transform: rotate(0); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(-2deg); }
|
|
}
|
|
|
|
@keyframes hamsterFRLimb {
|
|
from, 25%, 50%, 75%, to { transform: rotate(50deg) translateZ(-1px); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(-30deg) translateZ(-1px); }
|
|
}
|
|
|
|
@keyframes hamsterFLLimb {
|
|
from, 25%, 50%, 75%, to { transform: rotate(-30deg); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(50deg); }
|
|
}
|
|
|
|
@keyframes hamsterBRLimb {
|
|
from, 25%, 50%, 75%, to { transform: rotate(-60deg) translateZ(-1px); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(20deg) translateZ(-1px); }
|
|
}
|
|
|
|
@keyframes hamsterBLLimb {
|
|
from, 25%, 50%, 75%, to { transform: rotate(20deg); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(-60deg); }
|
|
}
|
|
|
|
@keyframes hamsterTail {
|
|
from, 25%, 50%, 75%, to { transform: rotate(30deg) translateZ(-1px); }
|
|
12.5%, 37.5%, 62.5%, 87.5% { transform: rotate(10deg) translateZ(-1px); }
|
|
}
|
|
|
|
@keyframes spoke {
|
|
from { transform: rotate(0); }
|
|
to { transform: rotate(-1turn); }
|
|
} |