feat: nový vzhled Totoro
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 27s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 40s
CI / Playwright E2E tests (push) Successful in 1m57s
CI / Build and push Docker image (push) Successful in 46s
CI / Notify (push) Successful in 2s

This commit is contained in:
mates committed 2026-10-27 22:09:53 +01:00
1 parent 05d27b6693
commit 4de71d017c
10 files changed
+1012 -6

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@500;800&family=Nunito:wght@600;800;900&family=Fredoka:wght@500;600;700&family=Shippori+Mincho:wght@600;700;800&family=Zen+Maru+Gothic:wght@500;700;900&family=Yusei+Magic&family=Dela+Gothic+One&family=Barlow+Condensed:wght@500;600;700&family=Kiwi+Maru:wght@400;500&family=Playfair+Display:ital,wght@0,800;1,800&family=Courier+Prime:wght@700&family=Cormorant+Garamond:wght@600;700&family=Baloo+2:wght@600;800&family=Metal+Mania&family=Klee+One:wght@600&family=Exo+2:wght@700;800&family=Almendra:wght@700&family=Alegreya:wght@500;700;800&family=Hachi+Maru+Pop&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@500;800&family=Nunito:wght@600;800;900&family=Fredoka:wght@500;600;700&family=Shippori+Mincho:wght@600;700;800&family=Zen+Maru+Gothic:wght@500;700;900&family=Yusei+Magic&family=Dela+Gothic+One&family=Barlow+Condensed:wght@500;600;700&family=Kiwi+Maru:wght@400;500&family=Playfair+Display:ital,wght@0,800;1,800&family=Courier+Prime:wght@700&family=Cormorant+Garamond:wght@600;700&family=Baloo+2:wght@600;800&family=Metal+Mania&family=Klee+One:wght@600&family=Exo+2:wght@700;800&family=Almendra:wght@700&family=Alegreya:wght@500;700;800&family=Hachi+Maru+Pop&family=Yomogi&display=swap" rel="stylesheet" />
<title>Luncher</title> <title>Luncher</title>
<script> <script>
(function() { (function() {
@@ -28,7 +28,7 @@
} }
document.documentElement.setAttribute('data-bs-theme', theme); document.documentElement.setAttribute('data-bs-theme', theme);
var style = localStorage.getItem('style_theme'); var style = localStorage.getItem('style_theme');
var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren', 'mlpg5', 'aggretsuko', 'sumikko', 'gudetama', 'miku', 'dungeonmeshi', 'chiikawa']; var styles = ['umamusume', 'mlp', 'hellgirl', 'kon', 'bocchi', 'evangelion', 'yurucamp', 'spyfamily', 'apothecary', 'frieren', 'mlpg5', 'aggretsuko', 'sumikko', 'gudetama', 'miku', 'dungeonmeshi', 'chiikawa', 'totoro'];
document.documentElement.setAttribute('data-luncher-theme', styles.indexOf(style) >= 0 ? style : 'simple'); document.documentElement.setAttribute('data-luncher-theme', styles.indexOf(style) >= 0 ? style : 'simple');
} catch (e) { } catch (e) {
// Fallback pokud localStorage není dostupný // Fallback pokud localStorage není dostupný
+1
View File
@@ -27,6 +27,7 @@ import './themes/gudetama.scss';
import './themes/miku.scss'; import './themes/miku.scss';
import './themes/dungeonmeshi.scss'; import './themes/dungeonmeshi.scss';
import './themes/chiikawa.scss'; import './themes/chiikawa.scss';
import './themes/totoro.scss';
import { faCircleCheck, faNoteSticky, faTrashCan, faComment } from '@fortawesome/free-regular-svg-icons'; import { faCircleCheck, faNoteSticky, faTrashCan, faComment } from '@fortawesome/free-regular-svg-icons';
import { useSettings } from './context/settings'; import { useSettings } from './context/settings';
import Footer from './components/Footer'; import Footer from './components/Footer';
+11
View File
@@ -208,6 +208,17 @@
box-shadow: 0 2px 0 #4a3f3a; box-shadow: 0 2px 0 #4a3f3a;
} }
// Styl Můj soused Totoro: emoji na krémovém bříšku v šedém kroužku
.eater-badge-totoro {
display: inline-grid;
place-items: center;
font-size: 15px;
line-height: 1;
border-color: #fffdf3;
background: radial-gradient(circle at 40% 35%, #fffdf3, #f2ead3);
box-shadow: 0 0 0 2px #7d8a8c, 0 2px 4px rgb(46 58 44 / 0.3);
}
// Odznak a jméno drží na jednom řádku (úzký sloupec „Člen" na stránce objednávek) // Odznak a jméno drží na jednom řádku (úzký sloupec „Člen" na stránce objednávek)
.user-info:has(.eater-badge) { .user-info:has(.eater-badge) {
display: inline-flex; display: inline-flex;
+8 -1
View File
@@ -109,6 +109,12 @@ const CK_LEVELS = [
{ minItems: 2, emoji: '🍮', title: 'Úroveň Chiikawy — po pletí plevele si zasloužíš pudink za odměnu' }, { minItems: 2, emoji: '🍮', title: 'Úroveň Chiikawy — po pletí plevele si zasloužíš pudink za odměnu' },
]; ];
/** Úrovně ve stylu Můj soused Totoro — emoji na krémovém bříšku v šedém kroužku. Seřazeno od nejvyšší úrovně. */
const TT_LEVELS = [
{ minItems: 3, emoji: '🌽', title: 'Úroveň Mei — k obědu ještě kukuřice, kterou nese mamince' },
{ minItems: 2, emoji: '🌰', title: 'Úroveň Totora — kapsa plná žaludů na přilepšenou' },
];
/** Úrovně podle stylu pro odznaky s emoji. */ /** Úrovně podle stylu pro odznaky s emoji. */
const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = { const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
mlp: MLP_LEVELS, mlp: MLP_LEVELS,
@@ -127,6 +133,7 @@ const EMOJI_LEVELS: Partial<Record<StyleTheme, typeof MLP_LEVELS>> = {
miku: MIKU_LEVELS, miku: MIKU_LEVELS,
dungeonmeshi: DM_LEVELS, dungeonmeshi: DM_LEVELS,
chiikawa: CK_LEVELS, chiikawa: CK_LEVELS,
totoro: TT_LEVELS,
}; };
type Props = { type Props = {
@@ -134,7 +141,7 @@ type Props = {
items: number, items: number,
}; };
/** Odznak s postavou u strávníků s více jídly — jen ve stylech Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi a Chiikawa. */ /** Odznak s postavou u strávníků s více jídly — jen ve stylech Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi, Chiikawa a Můj soused Totoro. */
export default function EaterBadge({ items }: Readonly<Props>) { export default function EaterBadge({ items }: Readonly<Props>) {
const styleTheme = useSettings()?.styleTheme; const styleTheme = useSettings()?.styleTheme;
const isUma = styleTheme === 'umamusume'; const isUma = styleTheme === 'umamusume';
+81
View File
@@ -1445,3 +1445,84 @@
} }
} }
} }
// ============================================
// Konfety ve stylu Můj soused Totoro
// ============================================
.confetti-tt {
filter: drop-shadow(0 3px 3px rgb(46 58 44 / 0.3));
}
// Štítky jako cedulky Kočkobusu — zaoblený obdélník s krémovým lemem
.confetti-tt-badge {
padding: 3px 14px 4px;
border-radius: 10px;
border: 2px solid #fffdf3;
outline: 1.5px solid rgb(46 58 44 / 0.35);
color: #fffdf3;
font-family: 'Yomogi', 'Kiwi Maru', sans-serif;
font-size: 1.15rem;
white-space: nowrap;
text-shadow: 0 1px 0 rgb(0 0 0 / 0.3);
}
// Nápis „🌳 となりのトトロ 🍃" — cedule zastávky v lese, lehce se pohupuje ve větru
.confetti-tt-banner {
position: absolute;
top: 38%;
left: 50%;
transform: translate(-50%, -50%);
padding: 16px 48px 20px;
font-family: 'Yomogi', 'Kiwi Maru', sans-serif;
font-size: clamp(2rem, 7vw, 4.2rem);
white-space: nowrap;
color: #2e3a2c;
background: linear-gradient(180deg, #fffdf3, #f3ecd6);
border: 5px solid #3f6b2f;
border-radius: 28px 28px 10px 10px;
box-shadow: inset 0 0 0 3px #fffdf3, inset 0 0 0 6px rgb(63 107 47 / 0.25), 0 14px 40px rgb(46 58 44 / 0.45);
animation: tt-banner 2.6s ease both;
}
@keyframes tt-banner {
0% { transform: translate(-50%, -30%) scale(0.5); opacity: 0; }
12% { transform: translate(-50%, -52%) scale(1.04); opacity: 1; }
20% { transform: translate(-50%, -50%) scale(1) rotate(0deg); }
// Ve větru se cedule pohupuje
35%, 65% { transform: translate(-50%, -50%) rotate(-1.5deg); }
50% { transform: translate(-50%, -50%) rotate(1.5deg); }
80% { transform: translate(-50%, -50%) rotate(0deg); opacity: 1; }
100% { transform: translate(-50%, -50%) scale(0.92); opacity: 0; }
}
// Obyvatel lesa s hláškou — krémové bříško v šedém kroužku a bublina jako list papíru
.confetti-totoro-voice {
.confetti-mlp-avatar {
border: 3px solid #fffdf3;
background: radial-gradient(circle at 40% 35%, #fffdf3, #f2ead3);
box-shadow: 0 0 0 4px var(--mlp-speaker, #7d8a8c), 0 4px 12px rgb(46 58 44 / 0.35);
}
.confetti-mlp-bubble {
background: #fffdf6;
color: #2e3a2c;
border: 1px solid #d6e2c8;
border-top: 5px solid var(--mlp-speaker, #3f6b2f);
border-radius: 16px;
box-shadow: 0 8px 20px rgb(0 0 0 / 0.25);
font-family: 'Kiwi Maru', 'Nunito', sans-serif;
p {
font-weight: 500;
}
small {
font-family: 'Yomogi', 'Kiwi Maru', sans-serif;
color: var(--mlp-speaker, #3f6b2f);
}
&::before {
border-right-color: #d6e2c8;
}
}
}
+17 -1
View File
@@ -254,6 +254,21 @@ const CK_SPEAKERS = [
{ name: 'Rakko', emoji: '🦦', color: '#8a7a6a', quote: 'Dobrá volba. Po obědě ale zpátky do tréninku.' }, { name: 'Rakko', emoji: '🦦', color: '#8a7a6a', quote: 'Dobrá volba. Po obědě ale zpátky do tréninku.' },
{ name: 'Shisa', emoji: '🦁', color: '#e6a23c', quote: 'Hlavně se pořádně najíst! A pak se zase snažit v práci!' }, { name: 'Shisa', emoji: '🦁', color: '#e6a23c', quote: 'Hlavně se pořádně najíst! A pak se zase snažit v práci!' },
]; ];
const TT_EMOJI = ['🌰', '🍃', '🌳', '☂️', '🌧️', '🚌', '🐱', '🌽', '🌱', '💧', '🌙', '🍂', '🍙', '🥒', '🍅', '⚫'];
// Motivy z filmu (Kočkobus, sazoví skřítci, žaludy, deštník na zastávce, tanec pro semínka...)
const TT_BADGES = ['となりのトトロ', 'Totoro!', 'Kočkobus', 'Sazoví skřítci', 'Žaludy', 'Deštník', 'Kafrovník', 'Rosťte, semínka!', 'Mei!', 'Satsuki!', 'Zastávka'];
const TT_BADGE_COLORS = ['#3f6b2f', '#7d8a8c', '#e39b2f', '#8a5a2b', '#5b8fb0', '#d97a8a'];
/** Obyvatelé lesa a vesnice s „hláškou" po finálním výsledku. */
const TT_SPEAKERS = [
{ name: 'Totoro', emoji: '🌳', color: '#7d8a8c', quote: 'ROAAAAR! (spokojeně zařve a usměje se od ucha k uchu)' },
{ name: 'Mei', emoji: '🌽', color: '#d97a8a', quote: 'Já to vybrala! A mamince přinesu kukuřici!' },
{ name: 'Satsuki', emoji: '🍙', color: '#e39b2f', quote: 'Výborně! Ráno jsem stejně dělala bento pro tátu, Mei i sebe.' },
{ name: 'Kočkobus', emoji: '🚌', color: '#e39b2f', quote: 'Mňaaau! (cedulka na čele se přetočí na název podniku)' },
{ name: 'Kanta', emoji: '☂️', color: '#5b8fb0', quote: '…T-tady máš deštník. A… dobrou chuť.' },
{ name: 'Babička', emoji: '🥒', color: '#8a5a2b', quote: 'Jen se pořádně najezte. Zelenina z pole je nejlepší na světě!' },
{ name: 'Tatínek', emoji: '📚', color: '#3f6b2f', quote: 'Skvělá volba! Hahaha! Smějte se nahlas, ať se strachy rozutečou!' },
{ name: 'Sazoví skřítci', emoji: '⚫', color: '#2b2b2b', quote: '(cupity cupity… a rychle se schovají do kouta)' },
];
/** Styly, jejichž konfety tvoří emoji a samolepky s hláškami (a po finále promluví postava s emoji). */ /** Styly, jejichž konfety tvoří emoji a samolepky s hláškami (a po finále promluví postava s emoji). */
const EMOJI_CONFETTI: Partial<Record<WheelTheme, { const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
prefix: string, badges: string[], colors: string[], emoji: string[], prefix: string, badges: string[], colors: string[], emoji: string[],
@@ -276,6 +291,7 @@ const EMOJI_CONFETTI: Partial<Record<WheelTheme, {
miku: { prefix: 'confetti-miku', badges: MIKU_BADGES, colors: MIKU_BADGE_COLORS, emoji: MIKU_EMOJI, speakers: MIKU_SPEAKERS, banner: '♪ 初音ミク ♪' }, miku: { prefix: 'confetti-miku', badges: MIKU_BADGES, colors: MIKU_BADGE_COLORS, emoji: MIKU_EMOJI, speakers: MIKU_SPEAKERS, banner: '♪ 初音ミク ♪' },
dungeonmeshi: { prefix: 'confetti-dm', badges: DM_BADGES, colors: DM_BADGE_COLORS, emoji: DM_EMOJI, speakers: DM_SPEAKERS, banner: '🍲 ダンジョン飯 🔥' }, dungeonmeshi: { prefix: 'confetti-dm', badges: DM_BADGES, colors: DM_BADGE_COLORS, emoji: DM_EMOJI, speakers: DM_SPEAKERS, banner: '🍲 ダンジョン飯 🔥' },
chiikawa: { prefix: 'confetti-ck', badges: CK_BADGES, colors: CK_BADGE_COLORS, emoji: CK_EMOJI, speakers: CK_SPEAKERS, banner: '✨ ちいかわ ✨' }, chiikawa: { prefix: 'confetti-ck', badges: CK_BADGES, colors: CK_BADGE_COLORS, emoji: CK_EMOJI, speakers: CK_SPEAKERS, banner: '✨ ちいかわ ✨' },
totoro: { prefix: 'confetti-tt', badges: TT_BADGES, colors: TT_BADGE_COLORS, emoji: TT_EMOJI, speakers: TT_SPEAKERS, banner: '🌳 となりのトトロ 🍃' },
}; };
const DURATION_MS = 6400; const DURATION_MS = 6400;
@@ -429,7 +445,7 @@ export default function WheelConfetti({ theme, burstKey, big = false }: Readonly
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [burstKey]); }, [burstKey]);
// Ostatní tematické styly: hlášku pronese náhodná postava (Mane 6, Ai Enma, Houkago Tea Time, Kessoku Band, piloti NERV, Nokuru, Forgerovi, vnitřní palác, družina Frieren, poníci z Maretime Bay, kancelář Retsuko, postavičky z koutků, Gudetama, Vocaloidi, družina z Dungeon Meshi, Chiikawa a kamarádi...) // Ostatní tematické styly: hlášku pronese náhodná postava (Mane 6, Ai Enma, Houkago Tea Time, Kessoku Band, piloti NERV, Nokuru, Forgerovi, vnitřní palác, družina Frieren, poníci z Maretime Bay, kancelář Retsuko, postavičky z koutků, Gudetama, Vocaloidi, družina z Dungeon Meshi, Chiikawa a kamarádi, obyvatelé Totorova lesa...)
const emojiConfetti = EMOJI_CONFETTI[theme]; const emojiConfetti = EMOJI_CONFETTI[theme];
const emojiSpeaker = useMemo( const emojiSpeaker = useMemo(
() => big && emojiConfetti ? pick(emojiConfetti.speakers) : undefined, () => big && emojiConfetti ? pick(emojiConfetti.speakers) : undefined,
@@ -4710,6 +4710,270 @@
} }
} }
// ============================================
// STYL MŮJ SOUSED TOTORO kola štěstí (dle stylu zvoleného v nastavení).
// Výseče v barvách filmu (les, Totoro, Kočkobus, žalud, nebe, Mei),
// krémové linky, okraj tmavě zelený a střed jako Totorovo bříško.
// ============================================
.spin-wheel-modal.spin-theme-totoro {
--tt-font: 'Kiwi Maru', 'Nunito', sans-serif;
--tt-display: 'Yomogi', 'Kiwi Maru', sans-serif;
--tt-forest: #3f6b2f;
--tt-forest-deep: #2f5223;
--tt-moss: #8bb06a;
--tt-catbus: #e39b2f;
--tt-line: #d6e2c8;
--tt-panel: #fffdf6;
--tt-text: #2e3a2c;
--wheel-c0: #4f7f3a;
--wheel-c1: #7d8a8c;
--wheel-c2: #d98a1f;
--wheel-c3: #8a5a2b;
--wheel-c4: #5b8fb0;
--wheel-c5: #c96a7c;
--wheel-rim: #2f5223;
--wheel-hub: #f2ead3;
font-family: var(--tt-font);
.modal-content {
border: 1px solid var(--tt-line);
border-radius: 22px;
overflow: hidden;
background:
radial-gradient(ellipse 140px 50px at 85% 4%, rgb(255 255 255 / 0.9) 60%, transparent 62%),
linear-gradient(180deg, #dff0f3, #f4f8ee 40%);
color: var(--tt-text);
}
.modal-header {
background:
linear-gradient(var(--tt-moss), var(--tt-moss)) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #4b7c38, var(--tt-forest-deep));
border-bottom: none;
padding-bottom: calc(var(--bs-modal-header-padding-y, 1rem) + 4px);
h2 {
color: #fbf7e6;
font-family: var(--tt-display);
font-weight: 400;
}
.btn-close {
filter: invert(1) grayscale(1) brightness(2);
opacity: 0.9;
}
}
.modal-footer {
border-top: 1px solid var(--tt-line);
}
.spin-steps span {
font-family: var(--tt-display);
background: #e6ecdc;
color: #8c9786;
border-radius: 10px;
padding: 3px 16px;
&.active {
background: var(--tt-forest);
color: #fbf7e6;
}
&.done {
background: var(--tt-catbus);
color: #3a2a12;
}
}
.btn {
font-family: var(--tt-font);
font-weight: 500;
border-radius: 10px;
}
.btn-primary {
background: linear-gradient(180deg, #4b7c38, var(--tt-forest));
border: 1px solid var(--tt-forest-deep);
color: #fbf7e6;
&:hover:not(:disabled) {
background: linear-gradient(180deg, #55893f, #3a6529);
color: #fff;
box-shadow: 0 4px 12px rgb(63 107 47 / 0.35);
}
&:disabled {
background: #dfe6d6;
border-color: #d0d9c5;
color: #8c9786;
}
}
// Tlačítko roztočení — přijíždí Kočkobus
.spin-button {
font-family: var(--tt-display);
font-size: 1.3rem;
padding: 10px 38px;
&::before {
content: '🚌 ';
font-size: 0.85em;
}
}
.btn-outline-primary {
background: var(--tt-panel);
border: 1px solid var(--tt-forest);
color: var(--tt-forest);
&:hover {
background: #e6f0dc;
color: var(--tt-forest);
}
}
.btn-secondary {
background: var(--tt-catbus);
border: 1px solid #c47f1a;
color: #3a2a12;
}
.spin-source {
background: #e6ecdc;
border-radius: 12px;
> .btn {
border-radius: 10px !important;
}
> .btn-outline-primary {
background: transparent;
color: var(--tt-text);
&:hover {
background: var(--tt-panel);
}
}
}
.spin-result {
font-weight: 500;
strong {
color: var(--tt-forest);
font-family: var(--tt-display);
}
}
.spin-options {
background: var(--tt-panel);
border: 1px solid var(--tt-line);
border-radius: 14px;
li:hover {
background: #f1f6e8;
}
}
.spin-options-header {
border-bottom: 2px solid var(--tt-moss);
color: var(--tt-forest);
font-family: var(--tt-display);
}
.spin-empty {
border-color: var(--tt-line);
}
.spin-wheel-pointer {
border-top-color: var(--tt-catbus);
filter: drop-shadow(0 2px 2px rgb(46 58 44 / 0.4));
}
.spin-wheel-hub {
stroke: #7d8a8c;
stroke-width: 3;
}
.spin-seg {
stroke: #fffdf3;
stroke-width: 2;
}
.spin-seg-label {
font-family: var(--tt-display);
font-weight: 400;
}
}
// Tmavý režim — noc u kafrovníku
[data-bs-theme="dark"] .spin-wheel-modal.spin-theme-totoro {
--tt-forest: #9fd07a;
--tt-line: #2a3d33;
--tt-panel: #172a27;
--tt-text: #eef2e4;
--wheel-rim: #0d211b;
--wheel-hub: #4d5a5c;
.modal-content {
border-color: #2a3d33;
background:
radial-gradient(circle at 90% 6%, #fdf6d8 0 14px, rgb(253 246 216 / 0.2) 16px, transparent 50px),
linear-gradient(180deg, #0c1826, #10221f);
box-shadow: 0 0 30px rgb(216 242 122 / 0.1);
}
.modal-header {
background:
linear-gradient(#2f5223, #2f5223) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #1f3a2a, #142a1f);
}
.spin-steps span:not(.active):not(.done) {
background: #1e3530;
color: #84968a;
}
.spin-steps span.active {
color: #10221a;
}
.btn-primary {
background: #9fd07a;
border-color: #7fb35a;
color: #10221a;
&:hover:not(:disabled) {
background: #b9e09a;
color: #10221a;
}
&:disabled {
background: #1e3530;
border-color: #2a3d33;
color: #84968a;
}
}
.btn-outline-primary:hover {
background: #1f3524;
}
.spin-source {
background: #1e3530;
}
.spin-options li:hover {
background: #1e3530;
}
.spin-seg {
stroke: #0f1c22;
}
}
// Ikona kola se při najetí pootočí // Ikona kola se při najetí pootočí
.spin-wheel-icon { .spin-wheel-icon {
transition: transform 0.4s ease !important; transition: transform 0.4s ease !important;
+3 -2
View File
@@ -13,7 +13,7 @@ const AUTO_ORDER_CHOICE_KEY = 'auto_order_choice';
export type ThemePreference = 'system' | 'light' | 'dark'; export type ThemePreference = 'system' | 'light' | 'dark';
/** Vizuální styl aplikace (nezávislý na světlém/tmavém režimu). */ /** Vizuální styl aplikace (nezávislý na světlém/tmavém režimu). */
export type StyleTheme = 'simple' | 'umamusume' | 'mlp' | 'hellgirl' | 'kon' | 'bocchi' | 'evangelion' | 'yurucamp' | 'spyfamily' | 'apothecary' | 'frieren' | 'mlpg5' | 'aggretsuko' | 'sumikko' | 'gudetama' | 'miku' | 'dungeonmeshi' | 'chiikawa'; export type StyleTheme = 'simple' | 'umamusume' | 'mlp' | 'hellgirl' | 'kon' | 'bocchi' | 'evangelion' | 'yurucamp' | 'spyfamily' | 'apothecary' | 'frieren' | 'mlpg5' | 'aggretsuko' | 'sumikko' | 'gudetama' | 'miku' | 'dungeonmeshi' | 'chiikawa' | 'totoro';
export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [ export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [
{ value: 'simple', label: 'Jednoduchý' }, { value: 'simple', label: 'Jednoduchý' },
@@ -34,6 +34,7 @@ export const STYLE_THEMES: { value: StyleTheme, label: string }[] = [
{ value: 'miku', label: 'Hatsune Miku' }, { value: 'miku', label: 'Hatsune Miku' },
{ value: 'dungeonmeshi', label: 'Dungeon Meshi' }, { value: 'dungeonmeshi', label: 'Dungeon Meshi' },
{ value: 'chiikawa', label: 'Chiikawa' }, { value: 'chiikawa', label: 'Chiikawa' },
{ value: 'totoro', label: 'Můj soused Totoro' },
]; ];
export type SettingsContextProps = { export type SettingsContextProps = {
@@ -265,7 +266,7 @@ function useProvideSettings(): SettingsContextProps {
}, [autoOrderChoice]); }, [autoOrderChoice]);
// Aplikuje accent barvy při změně hue nebo přepnutí světlý/tmavý. // Aplikuje accent barvy při změně hue nebo přepnutí světlý/tmavý.
// Tematické styly (Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi, Chiikawa) mají vlastní pevnou paletu, accent barvu proto uplatňuje jen jednoduchý styl. // Tematické styly (Umamusume, My Little Pony, Hell Girl, K-ON!, Bocchi the Rock!, Evangelion, Yuru Camp△, Spy × Family, Apothecary Diaries, Frieren, My Little Pony G5, Aggretsuko, Sumikko Gurashi, Gudetama, Hatsune Miku, Dungeon Meshi, Chiikawa, Můj soused Totoro) mají vlastní pevnou paletu, accent barvu proto uplatňuje jen jednoduchý styl.
useEffect(() => { useEffect(() => {
localStorage.setItem(ACCENT_HUE_KEY, String(accentHue)); localStorage.setItem(ACCENT_HUE_KEY, String(accentHue));
if (styleTheme === 'simple') { if (styleTheme === 'simple') {
+2
View File
@@ -39,6 +39,8 @@ const ICONS: Record<StyleTheme, Record<GameId, string>> = {
dungeonmeshi: { drawing: '📖' }, dungeonmeshi: { drawing: '📖' },
// Výtvarná chvilka po pletí plevele — kreslí se klacíkem i pastelkou // Výtvarná chvilka po pletí plevele — kreslí se klacíkem i pastelkou
chiikawa: { drawing: '🌱' }, chiikawa: { drawing: '🌱' },
// Žalud, který Mei najde na cestě k Totorovi
totoro: { drawing: '🌰' },
}; };
/** Styl z <html data-luncher-theme> — pro místa mimo React kontext nastavení. */ /** Styl z <html data-luncher-theme> — pro místa mimo React kontext nastavení. */
+623
View File
@@ -0,0 +1,623 @@
// ============================================
// STYL "MŮJ SOUSED TOTORO" (volitelný, výchozí je jednoduchý styl)
// Inspirováno filmem Můj soused Totoro (となりのトトロ) studia Ghibli.
// Světlý režim = letní venkov: nebe s kupovitými mraky, dole kopce s lesem
// kafrovníků, lesní zelená, šedý Totoro s krémovým bříškem a oranžová
// Kočkobusu. Titulek dne je cedule zastávky, u které čeká Totoro s listem
// na hlavě; z hlaviček karet vykukují sazoví skřítci.
// Tmavý režim = noc u kafrovníku s měsícem a světluškami.
// Aktivní, pokud má <html> atribut data-luncher-theme="totoro".
// ============================================
$tt: 'html[data-luncher-theme="totoro"]';
// Totoro, sazový skřítek (susuwatari), kopce s lesem (den / noc) a světlušky
$tt-totoro: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='48' viewBox='0 0 40 48'%3E%3Cpath d='M10 14L12 1L18 12Z' fill='%237d8a8c'/%3E%3Cpath d='M30 14L28 1L22 12Z' fill='%237d8a8c'/%3E%3Cellipse cx='20' cy='30' rx='16' ry='17.5' fill='%237d8a8c'/%3E%3Cellipse cx='20' cy='35' rx='10.5' ry='11' fill='%23f2ead3'/%3E%3Cg fill='none' stroke='%237d8a8c' stroke-width='1.3' stroke-linecap='round'%3E%3Cpath d='M15.5 29l2-1.6 2 1.6'/%3E%3Cpath d='M20.5 29l2-1.6 2 1.6'/%3E%3Cpath d='M13 33l2-1.6 2 1.6'/%3E%3Cpath d='M18 33l2-1.6 2 1.6'/%3E%3Cpath d='M23 33l2-1.6 2 1.6'/%3E%3C/g%3E%3Ccircle cx='13.5' cy='19' r='2.8' fill='%23fff'/%3E%3Ccircle cx='26.5' cy='19' r='2.8' fill='%23fff'/%3E%3Ccircle cx='13.5' cy='19' r='1.3' fill='%232b2b2b'/%3E%3Ccircle cx='26.5' cy='19' r='1.3' fill='%232b2b2b'/%3E%3Cpath d='M18.3 21.5h3.4l-1.7 1.3z' fill='%232b2b2b'/%3E%3Cg stroke='%232b2b2b' stroke-width='.7' stroke-linecap='round'%3E%3Cpath d='M9 23L2 22M9 25L2 26M31 23L38 22M31 25L38 26'/%3E%3C/g%3E%3C/svg%3E");
$tt-soot: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M29.0 16.0L25.4 17.6L28.2 20.4L24.2 20.8L26.0 24.4L22.1 23.3L22.5 27.3L19.2 24.9L18.3 28.8L16.0 25.5L13.7 28.8L12.8 24.9L9.5 27.3L9.9 23.3L6.0 24.4L7.8 20.8L3.8 20.4L6.6 17.6L3.0 16.0L6.6 14.4L3.8 11.6L7.8 11.2L6.0 7.6L9.9 8.7L9.5 4.7L12.8 7.1L13.7 3.2L16.0 6.5L18.3 3.2L19.2 7.1L22.5 4.7L22.1 8.7L26.0 7.6L24.2 11.3L28.2 11.6L25.4 14.4Z' fill='%231d1d1d'/%3E%3Ccircle cx='12' cy='15' r='3.6' fill='%23fff'/%3E%3Ccircle cx='20' cy='15' r='3.6' fill='%23fff'/%3E%3Ccircle cx='12.6' cy='15.4' r='1.6' fill='%231d1d1d'/%3E%3Ccircle cx='20.6' cy='15.4' r='1.6' fill='%231d1d1d'/%3E%3C/svg%3E");
$tt-hills: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='90' viewBox='0 0 320 90'%3E%3Cpath d='M0 90V46Q40 26 80 40T160 36T240 42T320 46V90Z' fill='%23b9d79c'/%3E%3Cg fill='%235f8b45'%3E%3Ccircle cx='60' cy='40' r='14'/%3E%3Ccircle cx='74' cy='34' r='16'/%3E%3Ccircle cx='90' cy='42' r='12'/%3E%3Ccircle cx='210' cy='42' r='11'/%3E%3Ccircle cx='222' cy='36' r='13'/%3E%3C/g%3E%3Cpath d='M0 90V66Q50 52 110 62T220 60T320 66V90Z' fill='%237fa85e'/%3E%3C/svg%3E");
$tt-hills-night: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='90' viewBox='0 0 320 90'%3E%3Cpath d='M0 90V46Q40 26 80 40T160 36T240 42T320 46V90Z' fill='%23173229'/%3E%3Cg fill='%2310281f'%3E%3Ccircle cx='60' cy='40' r='14'/%3E%3Ccircle cx='74' cy='34' r='16'/%3E%3Ccircle cx='90' cy='42' r='12'/%3E%3Ccircle cx='210' cy='42' r='11'/%3E%3Ccircle cx='222' cy='36' r='13'/%3E%3C/g%3E%3Cpath d='M0 90V66Q50 52 110 62T220 60T320 66V90Z' fill='%230d211b'/%3E%3C/svg%3E");
$tt-fireflies: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Ccircle cx='30' cy='50' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='30' cy='50' r='1.6' fill='%23eaff9c'/%3E%3Ccircle cx='140' cy='30' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='140' cy='30' r='1.6' fill='%23eaff9c'/%3E%3Ccircle cx='190' cy='120' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='190' cy='120' r='1.6' fill='%23eaff9c'/%3E%3Ccircle cx='80' cy='150' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='80' cy='150' r='1.6' fill='%23eaff9c'/%3E%3Ccircle cx='120' cy='200' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='120' cy='200' r='1.6' fill='%23eaff9c'/%3E%3Ccircle cx='20' cy='190' r='5' fill='%23d8f27a' fill-opacity='.18'/%3E%3Ccircle cx='20' cy='190' r='1.6' fill='%23eaff9c'/%3E%3C/svg%3E");
#{$tt} {
--tt-font: 'Kiwi Maru', 'Nunito', sans-serif;
--tt-display: 'Yomogi', 'Kiwi Maru', sans-serif;
--tt-forest: #3f6b2f; // les kafrovníků
--tt-forest-deep: #2f5223;
--tt-moss: #8bb06a;
--tt-grey: #7d8a8c; // Totoro
--tt-belly: #f2ead3; // Totorovo bříško
--tt-catbus: #e39b2f; // Kočkobus
--tt-acorn: #8a5a2b; // žaludy
--tt-sky: #cfe6ee;
--tt-ink: #2e3a2c;
--tt-line: #d6e2c8;
--luncher-primary: var(--tt-forest);
--luncher-primary-hover: var(--tt-forest-deep);
--luncher-primary-light: #e6f0dc;
--luncher-bg: #f4f8ee;
--luncher-bg-card: #fffdf6;
--luncher-bg-hover: #f1f6e8;
--luncher-text: var(--tt-ink);
--luncher-text-secondary: #55634f;
--luncher-text-muted: #8c9786;
--luncher-border: var(--tt-line);
--luncher-border-light: #e7eedd;
--luncher-navbar-bg: var(--tt-forest);
--luncher-navbar-text: #fbf7e6;
--luncher-action-icon: var(--tt-forest);
--luncher-buyer-icon: var(--tt-acorn);
--luncher-success: #4f8a3a;
--luncher-warning: var(--tt-catbus);
--luncher-danger: #c4513b;
--luncher-radius-sm: 10px;
--luncher-radius: 14px;
--luncher-radius-lg: 18px;
--luncher-radius-xl: 22px;
--luncher-shadow: 0 4px 14px rgb(46 58 44 / 0.1);
--luncher-shadow-lg: 0 10px 26px rgb(46 58 44 / 0.18);
&[data-bs-theme="dark"] {
// Noc u kafrovníku — měsíc a světlušky
--tt-ink: #eef2e4;
--tt-line: #2a3d33;
--luncher-primary: #9fd07a;
--luncher-primary-hover: #b9e09a;
--luncher-primary-light: #1f3524;
--luncher-bg: #0f1c22;
--luncher-bg-card: #172a27;
--luncher-bg-hover: #1e3530;
--luncher-text: var(--tt-ink);
--luncher-text-secondary: #c3cfbb;
--luncher-text-muted: #84968a;
--luncher-border-light: #1e3530;
--luncher-navbar-bg: #12261d;
--luncher-navbar-text: #eef2e4;
--luncher-action-icon: #9fd07a;
--luncher-buyer-icon: #f2b866;
--luncher-success: #9fd07a;
--luncher-warning: #f2b866;
--luncher-danger: #ef7a62;
--luncher-shadow: 0 4px 16px rgb(0 0 0 / 0.45);
--luncher-shadow-lg: 0 0 0 1px rgb(159 208 122 / 0.35), 0 10px 28px rgb(216 242 122 / 0.12);
}
body {
font-family: var(--tt-font);
background-color: var(--luncher-bg);
// Letní nebe s kupovitými mraky a dole kopce s lesem
background-image:
#{$tt-hills},
radial-gradient(ellipse 160px 60px at 12% 9%, rgb(255 255 255 / 0.9) 60%, transparent 62%),
radial-gradient(ellipse 110px 50px at 18% 6%, rgb(255 255 255 / 0.9) 60%, transparent 62%),
radial-gradient(ellipse 180px 64px at 82% 13%, rgb(255 255 255 / 0.85) 60%, transparent 62%),
radial-gradient(ellipse 120px 56px at 88% 9%, rgb(255 255 255 / 0.85) 60%, transparent 62%),
linear-gradient(180deg, #bfe0ef, #e8f3ea 45%, #f4f8ee);
background-size: 320px 90px, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%;
background-position: left bottom, 0 0, 0 0, 0 0, 0 0, 0 0;
background-repeat: repeat-x, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat;
background-attachment: fixed;
}
&[data-bs-theme="dark"] body {
background-image:
#{$tt-hills-night},
radial-gradient(circle at 84% 10%, #fdf6d8 0 26px, rgb(253 246 216 / 0.22) 28px, transparent 80px),
#{$tt-fireflies},
#{$tt-fireflies},
linear-gradient(180deg, #0c1826, #10221f 70%, #0f1c22);
// Dvě vrstvy světlušek s různou velikostí dlaždice, aby nebyla vidět pravidelná mřížka
background-size: 320px 90px, 100% 100%, 220px 220px, 340px 340px, 100% 100%;
background-position: left bottom, 0 0, 0 0, 90px 60px, 0 0;
background-repeat: repeat-x, no-repeat, repeat, repeat, no-repeat;
}
.app-container {
background: transparent;
}
h1, h2, h3, h4, .navbar-brand {
font-family: var(--tt-display);
font-weight: 400;
}
.btn {
font-family: var(--tt-font);
}
// ---------- Navbar: lesní zelená, u loga se pohupuje Totoro ----------
.navbar {
background:
linear-gradient(var(--tt-moss), var(--tt-moss)) left bottom / 100% 3px no-repeat,
linear-gradient(180deg, #4b7c38, var(--luncher-navbar-bg)) !important;
padding-bottom: calc(var(--bs-navbar-padding-y, 0.5rem) + 3px);
border-bottom: none;
box-shadow: 0 3px 12px rgb(46 58 44 / 0.25);
.navbar-brand {
letter-spacing: 0.02em;
&::after {
content: '';
display: inline-block;
width: 26px;
height: 31px;
margin-left: 8px;
margin-right: 0.6em;
vertical-align: -5px;
background: $tt-totoro center / contain no-repeat;
transform-origin: 50% 100%;
animation: tt-sway 3.6s ease-in-out infinite;
}
}
.dropdown-menu {
border: 1px solid var(--tt-line);
border-radius: var(--luncher-radius);
box-shadow: var(--luncher-shadow-lg);
}
}
&[data-bs-theme="dark"] .navbar {
background:
linear-gradient(#2f5223, #2f5223) left bottom / 100% 3px no-repeat,
var(--luncher-navbar-bg) !important;
}
// ---------- Titulek dne: cedule autobusové zastávky, vedle čeká Totoro ----------
.day-navigator .title {
position: relative;
display: inline-block;
padding: 6px 28px 8px;
margin-top: 8px;
font-family: var(--tt-display);
font-size: 1.7rem;
color: var(--tt-ink) !important;
background: linear-gradient(180deg, #fffdf3, #f3ecd6);
border: 3px solid var(--tt-forest);
border-radius: 16px 16px 6px 6px;
box-shadow: inset 0 0 0 2px #fffdf3, inset 0 0 0 4px rgb(63 107 47 / 0.25), 0 6px 14px rgb(46 58 44 / 0.18);
// Totoro čeká na autobus
&::after {
content: '';
position: absolute;
right: -62px;
bottom: -6px;
width: 50px;
height: 60px;
background: $tt-totoro center bottom / contain no-repeat;
transform-origin: 50% 100%;
animation: tt-sway 3.6s ease-in-out infinite;
}
// List, který má Totoro na hlavě místo deštníku
&::before {
content: '🍃';
position: absolute;
right: -55px;
top: -32px;
font-size: 0.7em;
transform-origin: 30% 90%;
animation: tt-leaf 3.6s ease-in-out infinite;
}
&.text-muted {
color: var(--luncher-text-muted) !important;
background: #f1efe6;
border-color: #b3bfaa;
box-shadow: inset 0 0 0 2px #f8f6ee;
// Jiný den — na zastávce nikdo nečeká
&::after,
&::before {
display: none;
}
}
}
&[data-bs-theme="dark"] .day-navigator .title {
color: #fdf6d8 !important;
background: linear-gradient(180deg, #24392f, #1a2c25);
border-color: #9fd07a;
box-shadow: inset 0 0 0 2px #1a2c25, inset 0 0 0 4px rgb(159 208 122 / 0.25), 0 0 24px rgb(253 246 216 / 0.15);
&.text-muted {
color: var(--luncher-text-muted) !important;
background: #172a27;
border-color: #2a3d33;
box-shadow: none;
}
}
// ---------- Karty podniků: lesní hlavička, z rohu vykukuje sazový skřítek ----------
.restaurant-card {
border: 1px solid var(--tt-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
overflow: hidden;
background: var(--luncher-bg-card);
&:hover {
transform: translateY(-2px);
border-color: var(--tt-moss);
box-shadow: var(--luncher-shadow-lg);
}
.restaurant-header {
background:
linear-gradient(var(--tt-moss), var(--tt-moss)) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #4b7c38, var(--tt-forest-deep));
// Sazový skřítek (susuwatari)
&::after {
content: '';
flex: none;
width: 36px;
height: 36px;
background: $tt-soot center / contain no-repeat;
}
// Při najetí se skřítek vyleká a rozklepe
&:hover {
background:
linear-gradient(var(--tt-catbus), var(--tt-catbus)) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #55893f, #34602a);
&::after {
animation: tt-jitter 0.25s linear infinite;
}
}
h3 {
color: #fbf7e6;
letter-spacing: 0.02em;
}
small {
color: rgb(251 247 230 / 0.78);
}
}
}
&[data-bs-theme="dark"] .restaurant-card .restaurant-header:not(:hover) {
background:
linear-gradient(#2f5223, #2f5223) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #1f3a2a, #142a1f);
}
&[data-bs-theme="dark"] .restaurant-card .restaurant-header::after {
// Ve tmě skřítkovi svítí jen oči
filter: drop-shadow(0 0 4px rgb(255 255 255 / 0.25));
}
.food-table {
--bs-table-bg: transparent;
}
.food-table tbody tr:hover > td {
background: #f1f6e8;
}
&[data-bs-theme="dark"] .food-table tbody tr:hover > td {
background: #1e3530;
}
.food-price {
color: var(--tt-acorn);
font-weight: 500;
}
&[data-bs-theme="dark"] .food-price {
color: #f2b866;
}
// ---------- Panely a tabulka voleb ----------
.choice-section,
.choices-table,
.pizza-section,
.no-votes {
border: 1px solid var(--tt-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
}
.choices-table,
.choices-table .nested-table {
--bs-table-bg: var(--luncher-bg-card);
}
.choices-table {
border-collapse: separate;
border-spacing: 0;
}
// Sloupec s podnikem: Totorovo bříško
.choices-table > tbody > tr > td:first-child {
background: var(--tt-belly);
color: var(--tt-forest);
font-family: var(--tt-display);
border-right: 3px solid var(--tt-grey);
}
&[data-bs-theme="dark"] .choices-table > tbody > tr > td:first-child {
background: #24302b;
color: #9fd07a;
border-right-color: #4d5a5c;
}
// Bootstrap karty (skupinové objednávky apod.) a seznamy
.card {
border: 1px solid var(--tt-line);
border-radius: var(--luncher-radius-lg);
box-shadow: var(--luncher-shadow);
overflow: hidden;
background: var(--luncher-bg-card);
// Rozbalovací výběr z nabídky nesmí karta oříznout
&:has(.select-search-container) {
overflow: visible;
}
> .card-header {
background: var(--luncher-primary-light);
border-bottom: 2px solid var(--tt-moss);
font-family: var(--tt-display);
&:first-child {
border-radius: calc(var(--luncher-radius-lg) - 1px) calc(var(--luncher-radius-lg) - 1px) 0 0;
}
}
.table {
--bs-table-bg: transparent;
margin-bottom: 0;
> :last-child > tr:last-child > * {
border-bottom-width: 0;
}
}
}
.list-group {
border-radius: var(--luncher-radius);
overflow: hidden;
border: 1px solid var(--tt-line);
> .list-group-item {
border-left: none;
border-right: none;
&:first-child {
border-top: none;
}
&:last-child {
border-bottom: none;
}
}
}
.form-select, .form-control,
input[type="text"], input[type="email"], input[type="password"] {
border-radius: var(--luncher-radius-sm);
border: 1px solid var(--tt-line);
&:focus {
border-color: var(--tt-moss);
box-shadow: 0 0 0 3px rgb(139 176 106 / 0.35);
}
}
// ---------- Tlačítka: lesní zelená, sekundární oranžová Kočkobusu ----------
.btn {
border-radius: var(--luncher-radius-sm);
font-weight: 500;
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.btn-primary {
background: linear-gradient(180deg, #4b7c38, var(--tt-forest));
border: 1px solid var(--tt-forest-deep);
color: #fbf7e6;
&:hover {
background: linear-gradient(180deg, #55893f, #3a6529);
border-color: var(--tt-forest-deep);
color: #fff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgb(63 107 47 / 0.35);
}
&:active {
transform: translateY(1px) !important;
}
&:disabled {
background: #dfe6d6;
border-color: #d0d9c5;
color: #8c9786;
box-shadow: none;
}
}
.btn-outline-primary {
background: var(--luncher-bg-card);
border: 1px solid var(--tt-forest);
color: var(--luncher-primary);
&:hover {
background: var(--luncher-primary-light);
border-color: var(--tt-forest);
color: var(--luncher-primary);
}
}
.btn-secondary {
background: var(--tt-catbus);
border: 1px solid #c47f1a;
color: #3a2a12;
&:hover {
background: #eba944;
border-color: #c47f1a;
color: #3a2a12;
box-shadow: 0 0 12px rgb(227 155 47 / 0.45);
}
}
.btn-danger {
background: #c4513b;
border: 1px solid #a3402c;
}
.action-icon:hover {
background: var(--luncher-primary-light);
color: var(--luncher-primary);
}
// Tmavý režim: světle zelený akcent potřebuje tmavý text
&[data-bs-theme="dark"] {
.btn-primary {
background: #9fd07a;
border-color: #7fb35a;
color: #10221a;
&:hover {
background: #b9e09a;
border-color: #7fb35a;
color: #10221a;
box-shadow: 0 0 14px rgb(216 242 122 / 0.35);
}
&:disabled {
background: #1e3530;
border-color: #2a3d33;
color: #84968a;
}
}
.btn-outline-primary {
border-color: #9fd07a;
&:hover {
border-color: #9fd07a;
}
}
.react-datepicker__day--selected,
.react-datepicker__day--keyboard-selected {
color: #10221a;
}
}
// ---------- Modaly: krémový list s lesní hlavičkou ----------
.modal-content {
border: 1px solid var(--tt-line);
border-radius: var(--luncher-radius-xl);
box-shadow: 0 16px 40px rgb(46 58 44 / 0.3);
overflow: hidden;
}
.modal-header {
background:
linear-gradient(var(--tt-moss), var(--tt-moss)) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #4b7c38, var(--tt-forest-deep));
border-bottom: none;
padding-bottom: calc(var(--bs-modal-header-padding-y, 1rem) + 4px);
.modal-title, .modal-title h2 {
color: #fbf7e6;
font-family: var(--tt-display);
font-weight: 400;
}
.btn-close {
filter: invert(1) grayscale(1) brightness(2);
}
}
&[data-bs-theme="dark"] {
.modal-content {
border-color: #2a3d33;
box-shadow: 0 0 30px rgb(216 242 122 / 0.1);
}
.modal-header {
background:
linear-gradient(#2f5223, #2f5223) left bottom / 100% 4px no-repeat,
linear-gradient(160deg, #1f3a2a, #142a1f);
}
}
// Patička — tmavá lesní půda pod kopci z pozadí
.footer {
border-top: 4px solid var(--tt-moss);
background: var(--tt-forest-deep);
a {
color: #f2d27a;
}
}
&[data-bs-theme="dark"] .footer {
border-top-color: #2f5223;
background: var(--luncher-navbar-bg);
a {
color: #f2b866;
}
}
}
// Totoro se pohupuje
@keyframes tt-sway {
0%, 100% { transform: rotate(0deg) scaleY(1); }
25% { transform: rotate(-4deg) scaleY(0.97); }
50% { transform: rotate(0deg) scaleY(1.02); }
75% { transform: rotate(4deg) scaleY(0.97); }
}
// List na hlavě se kolébá s ním
@keyframes tt-leaf {
0%, 100% { transform: rotate(-8deg); }
25% { transform: rotate(-16deg) translateY(1px); }
75% { transform: rotate(2deg) translateY(1px); }
}
// Vylekaný sazový skřítek
@keyframes tt-jitter {
0% { transform: translate(0, 0); }
25% { transform: translate(-1px, 1px); }
50% { transform: translate(1px, -1px); }
75% { transform: translate(-1px, -1px); }
100% { transform: translate(1px, 1px); }
}
@media (max-width: 575.98px) {
#{$tt} .day-navigator .title {
font-size: 1.3rem;
padding: 5px 18px 6px;
&::after {
right: -44px;
width: 36px;
height: 43px;
}
&::before {
right: -40px;
top: -22px;
}
}
}
@media (prefers-reduced-motion: reduce) {
#{$tt} .navbar .navbar-brand::after,
#{$tt} .day-navigator .title::after,
#{$tt} .day-navigator .title::before,
#{$tt} .restaurant-card .restaurant-header:hover::after {
animation: none;
}
}