- Mascot badge 64→128px (Lottie 56→112, emoji fallback 32→64); zzz glyph scaled + repositioned to match. It read too small on screen. - Awake companions (no sleeping clip, e.g. Happy Dog) no longer show a 😴 face or drifting "z" when the engine naps them — they hold their idle pose via a renderMood redirect. Only the always-asleep cat (real sleep clip) keeps the zzz, preserving that gag. Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
256 lines
7.9 KiB
CSS
256 lines
7.9 KiB
CSS
@import "tailwindcss";
|
|
|
|
/* Petal design tokens — soft, warm, bubbly stationery aesthetic.
|
|
Defined in @theme so they generate Tailwind utilities (bg-bg, text-plum,
|
|
rounded-card, font-ui, etc.). See petal-spec.md → Design System. */
|
|
@theme {
|
|
/* Palette */
|
|
--color-bg: #FDF6F0; /* warm cream canvas */
|
|
--color-surface: #FFFFFF; /* card/panel surfaces */
|
|
--color-surface-alt: #FFF0F5; /* rose-tinted alt surface */
|
|
--color-border: #F0E0EB; /* soft pink-grey border */
|
|
--color-plum: #3D2E39; /* warm dark plum text (not harsh black) */
|
|
--color-muted: #9B8CA3; /* muted lavender-grey */
|
|
--color-accent: #E8A0BF; /* soft rose — primary interactive */
|
|
--color-accent-hover: #D98AAF;/* deeper rose on hover */
|
|
|
|
/* Suggestion type colors */
|
|
--color-mint: #A8D8C8; /* grammar */
|
|
--color-peach: #F4B8A0; /* phrasing */
|
|
--color-lavender: #C5B4E8; /* idiom */
|
|
--color-sky: #A8CCE8; /* clarity */
|
|
--color-honey: #CE9B4F; /* voice */
|
|
--color-success: #8FCFA8; /* saved / accepted */
|
|
|
|
/* Typography */
|
|
--font-ui: "Nunito", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
|
--font-body: "Lora", Georgia, serif;
|
|
--font-mono: "JetBrains Mono", ui-monospace, monospace;
|
|
|
|
/* Shape language */
|
|
--radius-card: 16px;
|
|
--radius-modal: 24px;
|
|
--radius-pill: 999px;
|
|
--radius-input: 12px;
|
|
|
|
/* Soft, warm, lifted shadow */
|
|
--shadow-soft: 0 4px 20px rgba(180, 130, 160, 0.12);
|
|
}
|
|
|
|
html, body, #root {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--color-bg);
|
|
color: var(--color-plum);
|
|
font-family: var(--font-ui);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* Gentle, consistent motion across interactive elements */
|
|
button, a, input {
|
|
transition: all 200ms ease;
|
|
}
|
|
|
|
/* Editor body — serif Lora for prose, warm plum ink, roomy line height. The
|
|
title and headings use the Nunito UI face for contrast. */
|
|
.petal-prose {
|
|
font-family: var(--font-body);
|
|
font-size: 1.125rem;
|
|
line-height: 1.75;
|
|
color: var(--color-plum);
|
|
}
|
|
.petal-prose > * + * {
|
|
margin-top: 0.9em;
|
|
}
|
|
.petal-prose h1,
|
|
.petal-prose h2,
|
|
.petal-prose h3 {
|
|
font-family: var(--font-ui);
|
|
font-weight: 800;
|
|
line-height: 1.3;
|
|
}
|
|
.petal-prose h1 { font-size: 1.6em; }
|
|
.petal-prose h2 { font-size: 1.3em; }
|
|
.petal-prose h3 { font-size: 1.1em; }
|
|
.petal-prose ul,
|
|
.petal-prose ol {
|
|
padding-left: 1.4em;
|
|
}
|
|
.petal-prose ul { list-style: disc; }
|
|
.petal-prose ol { list-style: decimal; }
|
|
.petal-prose a {
|
|
color: var(--color-accent-hover);
|
|
text-decoration: underline;
|
|
}
|
|
.petal-prose blockquote {
|
|
border-left: 3px solid var(--color-border);
|
|
padding-left: 1em;
|
|
color: var(--color-muted);
|
|
font-style: italic;
|
|
}
|
|
.petal-prose code {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.9em;
|
|
background: var(--color-surface-alt);
|
|
padding: 0.1em 0.35em;
|
|
border-radius: 6px;
|
|
}
|
|
/* Placeholder shown on the empty first paragraph (Tiptap Placeholder ext). */
|
|
.petal-prose p.is-editor-empty:first-child::before {
|
|
content: attr(data-placeholder);
|
|
color: var(--color-muted);
|
|
float: left;
|
|
height: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Suggestion decorations -------------------------------------------------
|
|
Inline highlights anchored by string at render time (not stored marks). Each
|
|
type gets a soft underline in its palette color; hovering opens its card. */
|
|
.petal-suggestion {
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: 2px;
|
|
cursor: pointer;
|
|
transition: background 200ms ease;
|
|
/* gentle fade + slight upward float as decorations appear */
|
|
animation: petal-suggestion-in 260ms ease both;
|
|
}
|
|
.petal-suggestion:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
.petal-suggestion-grammar { border-bottom-color: var(--color-mint); }
|
|
.petal-suggestion-phrasing { border-bottom-color: var(--color-peach); }
|
|
.petal-suggestion-idiom { border-bottom-color: var(--color-lavender); }
|
|
.petal-suggestion-clarity { border-bottom-color: var(--color-sky); }
|
|
.petal-suggestion-voice { border-bottom-color: var(--color-honey); }
|
|
|
|
@keyframes petal-suggestion-in {
|
|
from { opacity: 0; transform: translateY(4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.petal-suggestion-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- Spell check ------------------------------------------------------------
|
|
Browser-side nspell flags misspellings with a soft rose wavy underline (a
|
|
gentler take on the classic red squiggle, to fit the pastel palette). Like the
|
|
suggestion layer these are ProseMirror decorations, never stored marks.
|
|
Clicking a flagged word opens its MisspellCard with corrections. */
|
|
.petal-misspelling {
|
|
cursor: pointer;
|
|
text-decoration: underline wavy var(--color-accent);
|
|
text-decoration-skip-ink: none;
|
|
text-underline-offset: 2px;
|
|
}
|
|
.petal-misspelling:hover {
|
|
background: var(--color-surface-alt);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.petal-misspell-card {
|
|
animation: petal-suggestion-in 200ms ease both;
|
|
}
|
|
|
|
/* --- Accept confetti --------------------------------------------------------
|
|
A tiny CSS-only burst played where a suggestion is accepted: four colored
|
|
dots spray up-and-out, then fade. No JS animation — each dot reads its
|
|
direction from --dx/--dy custom properties set inline. (Spec → Signature.) */
|
|
.petal-confetti {
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
.petal-confetti-dot {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: var(--radius-pill);
|
|
animation: petal-confetti 680ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
|
|
}
|
|
@keyframes petal-confetti {
|
|
0% { transform: translate(0, 0) scale(0.3); opacity: 0; }
|
|
18% { opacity: 1; }
|
|
100% { transform: translate(var(--dx), var(--dy)) scale(1); opacity: 0; }
|
|
}
|
|
|
|
/* --- Distraction-free mode ---------------------------------------------------
|
|
The doc-list sidebar slides left and collapses to zero width; the editor
|
|
canvas (centered, max-width) re-centers into the full pane. Width + transform
|
|
animate together for a smooth slide. (Spec → Distraction-free mode.) */
|
|
.petal-sidebar {
|
|
width: 260px;
|
|
overflow: hidden;
|
|
transition: width 280ms ease, transform 280ms ease, opacity 200ms ease;
|
|
}
|
|
.petal-sidebar-hidden {
|
|
width: 0;
|
|
transform: translateX(-24px);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Companion kitten -------------------------------------------------------
|
|
The cozy corner mascot. Gently bobs while awake, settles and sways slowly
|
|
while napping; its speech bubble pops in; little zzz drift up when asleep. */
|
|
.petal-companion {
|
|
animation: petal-bob 3.2s ease-in-out infinite;
|
|
transition: transform 200ms ease;
|
|
}
|
|
.petal-companion:hover {
|
|
transform: translateY(-2px) scale(1.04);
|
|
}
|
|
.petal-companion-sleep {
|
|
animation: petal-bob-slow 5s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-bob {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-4px); }
|
|
}
|
|
@keyframes petal-bob-slow {
|
|
0%, 100% { transform: translateY(0) rotate(-1deg); }
|
|
50% { transform: translateY(-2px) rotate(1deg); }
|
|
}
|
|
|
|
.petal-bubble {
|
|
animation: petal-bubble-in 240ms cubic-bezier(0.2, 0.8, 0.3, 1.2) both;
|
|
transform-origin: bottom right;
|
|
}
|
|
@keyframes petal-bubble-in {
|
|
from { opacity: 0; transform: translateY(6px) scale(0.92); }
|
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
}
|
|
|
|
.petal-zzz {
|
|
top: 6px;
|
|
right: 14px;
|
|
font-weight: 800;
|
|
font-size: 1.1rem;
|
|
animation: petal-zzz 2.4s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-zzz {
|
|
0% { opacity: 0; transform: translateY(0) scale(0.8); }
|
|
30% { opacity: 0.9; }
|
|
100% { opacity: 0; transform: translateY(-14px) scale(1.1); }
|
|
}
|
|
|
|
/* Blinking caret in the Ask Petal bubble while awaiting the first token. */
|
|
.petal-chat-caret {
|
|
animation: petal-breathe 1s ease-in-out infinite;
|
|
color: var(--color-accent);
|
|
}
|
|
|
|
/* Breathing rose dot shown in the StatusBar while a checkpoint runs. */
|
|
.petal-checkpoint-dot {
|
|
animation: petal-breathe 2s ease-in-out infinite;
|
|
}
|
|
@keyframes petal-breathe {
|
|
0%, 100% { opacity: 0.4; }
|
|
50% { opacity: 1; }
|
|
}
|