Initial commit. Basic spirit selector plus recipes data crossing.

This commit is contained in:
2026-02-01 13:45:21 +01:00
commit 4528583b71
10 changed files with 2340 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

10
.pretierrc Normal file
View File

@@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Jose Jimenez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

51
index.html Normal file
View File

@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=Nunito:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel="stylesheet">
<!-- styles -->
<link rel="stylesheet" href="./src/styles.css">
<title>Licores son Amores</title>
</head>
<body>
<div class="container">
<header>
<h1>Licores son Amores</h1>
<p class="subtitle">Descubre qué cócteles puedes preparar</p>
</header>
<div class="section">
<h2>Tus Licores</h2>
<div class="spirits-grid" id="spiritsGrid">
</div>
<button class="mix-button" id="mix-button" disabled="true">
¡A Mezclar!
</button>
</div>
<div class="section">
<h2>Recetas Disponibles</h2>
<div id="recipesContainer" class="recipes-container">
<p class="no-recipes">Selecciona tus licores y presiona "¡A Mezclar!" para descubrir qué cócteles puedes preparar.</p>
</div>
</div>
<div class="section">
<h2>Advertencia ⚠️</h2>
<div id="warningContainer">
<p class="warning-msg">El consumo de alcohol está prohibido a menores de 18 años 🔞</p>
<p class="warning-msg">No te recomiendo el consumo de alcohol en general 🚱</p>
<p class="warning-msg">Si vas a consumir alcohol siempre con moderación 🍷✋</p>
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1121
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "licores-son-amores",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"format": "prettier --write \"src/**/*.{js,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{js,json,css,md}\""
},
"devDependencies": {
"prettier": "^3.8.1",
"vite": "^7.2.4"
}
}

1
public/vite.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

534
src/data.json Normal file
View File

@@ -0,0 +1,534 @@
{
"spirits": [
{
"name": "Tanqueray",
"brand": "Tanqueray",
"type": "ginebra"
},
{
"name": "Beefeater",
"brand": "Beefeater",
"type": "ginebra"
},
{
"name": "Hendrick's",
"brand": "Hendrick's",
"type": "ginebra"
},
{
"name": "Bacardí Blanco",
"brand": "Bacardí",
"type": "ron"
},
{
"name": "Havana Club",
"brand": "Havana Club",
"type": "ron"
},
{
"name": "Absolut",
"brand": "Absolut",
"type": "vodka"
},
{
"name": "Smirnoff",
"brand": "Smirnoff",
"type": "vodka"
},
{
"name": "Jack Daniel's",
"brand": "Jack Daniel's",
"type": "whisky"
},
{
"name": "Johnnie Walker",
"brand": "Johnnie Walker",
"type": "whisky"
},
{
"name": "José Cuervo",
"brand": "José Cuervo",
"type": "tequila"
},
{
"name": "Patrón",
"brand": "Patrón",
"type": "tequila"
},
{
"name": "Martini Rosso",
"brand": "Martini",
"type": "vermut"
},
{
"name": "Martini Bianco",
"brand": "Martini",
"type": "vermut"
},
{
"name": "Baileys",
"brand": "Baileys",
"type": "licor-crema"
},
{
"name": "Cointreau",
"brand": "Cointreau",
"type": "licor-naranja"
},
{
"name": "Campari",
"brand": "Campari",
"type": "aperitivo"
},
{
"name": "Jägermeister",
"brand": "Jägermeister",
"type": "licor-hierbas"
},
{
"name": "Licor 43",
"brand": "Licor 43",
"type": "licor-hierbas"
},
{
"name": "Kahlúa",
"brand": "Kahlúa",
"type": "licor-cafe"
}
],
"recipes": [
{
"name": "Gin Tonic",
"ingredients": [
{
"type": "ginebra",
"name": "Ginebra",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "tonica",
"name": "Tónica",
"amount": "150 ml",
"isAlcohol": false
}
],
"instructions": "Servir la ginebra en un vaso largo con hielo, añadir la tónica y decorar con rodaja de limón o pepino."
},
{
"name": "Mojito",
"ingredients": [
{
"type": "ron",
"name": "Ron blanco",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "menta",
"name": "Hojas de menta",
"isAlcohol": false
},
{
"type": "azucar",
"name": "Azúcar",
"amount": "2 cditas",
"isAlcohol": false
},
{
"type": "lima",
"name": "Lima",
"amount": "1/2 unidad",
"isAlcohol": false
},
{
"type": "soda",
"name": "Soda",
"amount": "Top",
"isAlcohol": false
}
],
"instructions": "Machacar menta con azúcar y lima, añadir el ron, hielo y soda. Remover suavemente."
},
{
"name": "Moscow Mule",
"ingredients": [
{
"type": "vodka",
"name": "Vodka",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "lima",
"name": "Zumo de lima",
"amount": "15 ml",
"isAlcohol": false
},
{
"type": "ginger-beer",
"name": "Ginger beer",
"amount": "120 ml",
"isAlcohol": false
}
],
"instructions": "Mezclar vodka con zumo de lima y ginger beer en vaso de cobre con hielo. Decorar con lima."
},
{
"name": "Whisky Sour",
"ingredients": [
{
"type": "whisky",
"name": "Whisky",
"amount": "60 ml",
"isAlcohol": true
},
{
"type": "limon",
"name": "Zumo de limón",
"amount": "30 ml",
"isAlcohol": false
},
{
"type": "jarabe",
"name": "Jarabe simple",
"amount": "15 ml",
"isAlcohol": false
},
{
"type": "clara-huevo",
"name": "Clara de huevo",
"amount": "Opcional",
"isAlcohol": false
}
],
"instructions": "Mezclar whisky con zumo de limón y jarabe simple. Agitar con hielo y servir. Opcional: clara de huevo."
},
{
"name": "Margarita",
"ingredients": [
{
"type": "tequila",
"name": "Tequila",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "licor-naranja",
"name": "Cointreau",
"amount": "25 ml",
"isAlcohol": true
},
{
"type": "lima",
"name": "Zumo de lima",
"amount": "25 ml",
"isAlcohol": false
},
{
"type": "sal",
"name": "Sal",
"amount": "Para escarchar",
"isAlcohol": false
}
],
"instructions": "Mezclar tequila, Cointreau y zumo de lima. Servir en copa con borde escarchado de sal."
},
{
"name": "Negroni",
"ingredients": [
{
"type": "ginebra",
"name": "Ginebra",
"amount": "30 ml",
"isAlcohol": true
},
{
"type": "vermut",
"name": "Vermut rosso",
"amount": "30 ml",
"isAlcohol": true
},
{
"type": "aperitivo",
"name": "Campari",
"amount": "30 ml",
"isAlcohol": true
},
{
"type": "naranja",
"name": "Rodaja de naranja",
"isAlcohol": false
}
],
"instructions": "Mezclar partes iguales en vaso con hielo. Decorar con naranja."
},
{
"name": "Americano",
"ingredients": [
{
"type": "vermut",
"name": "Vermut rosso",
"amount": "45 ml",
"isAlcohol": true
},
{
"type": "aperitivo",
"name": "Campari",
"amount": "45 ml",
"isAlcohol": true
},
{
"type": "soda",
"name": "Soda",
"amount": "Top",
"isAlcohol": false
},
{
"type": "naranja",
"name": "Rodaja de naranja",
"isAlcohol": false
}
],
"instructions": "Mezclar vermut y Campari en vaso con hielo, completar con soda. Decorar con naranja."
},
{
"name": "White Russian",
"ingredients": [
{
"type": "vodka",
"name": "Vodka",
"amount": "40 ml",
"isAlcohol": true
},
{
"type": "licor-cafe",
"name": "Kahlúa",
"amount": "20 ml",
"isAlcohol": true
},
{
"type": "nata",
"name": "Nata fresca",
"amount": "30 ml",
"isAlcohol": false
}
],
"instructions": "Mezclar vodka y Kahlúa con hielo, añadir nata fresca flotando encima."
},
{
"name": "Espresso Martini",
"ingredients": [
{
"type": "vodka",
"name": "Vodka",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "licor-cafe",
"name": "Kahlúa",
"amount": "25 ml",
"isAlcohol": true
},
{
"type": "cafe",
"name": "Café expreso",
"amount": "30 ml",
"isAlcohol": false
}
],
"instructions": "Agitar vodka, Kahlúa y café expreso recién hecho con hielo. Servir en copa de martini."
},
{
"name": "Carajillo",
"ingredients": [
{
"type": "licor-hierbas",
"name": "Licor 43",
"amount": "40 ml",
"isAlcohol": true
},
{
"type": "cafe",
"name": "Café expreso",
"amount": "30 ml",
"isAlcohol": false
}
],
"instructions": "Servir Licor 43 con café expreso caliente. Se puede flamear opcionalmente."
},
{
"name": "Baileys con Café",
"ingredients": [
{
"type": "licor-crema",
"name": "Baileys",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "cafe",
"name": "Café caliente",
"amount": "120 ml",
"isAlcohol": false
}
],
"instructions": "Añadir Baileys a café caliente. Perfecto como postre líquido."
},
{
"name": "Old Fashioned",
"ingredients": [
{
"type": "whisky",
"name": "Whisky",
"amount": "60 ml",
"isAlcohol": true
},
{
"type": "azucar",
"name": "Azúcar",
"amount": "1 terrón",
"isAlcohol": false
},
{
"type": "angostura",
"name": "Angostura",
"amount": "2 gotas",
"isAlcohol": false
},
{
"type": "naranja",
"name": "Rodaja de naranja",
"isAlcohol": false
}
],
"instructions": "Mezclar whisky con azúcar, angostura y una rodaja de naranja. Servir con hielo grande."
},
{
"name": "Tequila Sunrise",
"ingredients": [
{
"type": "tequila",
"name": "Tequila",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "naranja",
"name": "Zumo de naranja",
"amount": "120 ml",
"isAlcohol": false
},
{
"type": "granadina",
"name": "Granadina",
"amount": "15 ml",
"isAlcohol": false
}
],
"instructions": "Mezclar tequila con zumo de naranja, añadir granadina lentamente para crear efecto amanecer."
},
{
"name": "Daiquiri",
"ingredients": [
{
"type": "ron",
"name": "Ron blanco",
"amount": "60 ml",
"isAlcohol": true
},
{
"type": "lima",
"name": "Zumo de lima",
"amount": "30 ml",
"isAlcohol": false
},
{
"type": "jarabe",
"name": "Jarabe simple",
"amount": "15 ml",
"isAlcohol": false
}
],
"instructions": "Agitar ron con zumo de lima y jarabe simple. Servir en copa fría."
},
{
"name": "Martini",
"ingredients": [
{
"type": "ginebra",
"name": "Ginebra",
"amount": "60 ml",
"isAlcohol": true
},
{
"type": "vermut",
"name": "Vermut seco",
"amount": "10 ml",
"isAlcohol": true
},
{
"type": "aceituna",
"name": "Aceituna o twist de limón",
"isAlcohol": false
}
],
"instructions": "Mezclar ginebra y vermut con hielo, colar en copa fría. Decorar con aceituna o twist de limón."
},
{
"name": "Cosmopolitan",
"ingredients": [
{
"type": "vodka",
"name": "Vodka",
"amount": "40 ml",
"isAlcohol": true
},
{
"type": "licor-naranja",
"name": "Cointreau",
"amount": "15 ml",
"isAlcohol": true
},
{
"type": "lima",
"name": "Zumo de lima",
"amount": "15 ml",
"isAlcohol": false
},
{
"type": "arandanos",
"name": "Zumo de arándanos",
"amount": "30 ml",
"isAlcohol": false
}
],
"instructions": "Agitar vodka, Cointreau, zumo de lima y arándanos. Servir en copa de martini."
},
{
"name": "Cuba Libre",
"ingredients": [
{
"type": "ron",
"name": "Ron",
"amount": "50 ml",
"isAlcohol": true
},
{
"type": "coca-cola",
"name": "Coca-cola",
"amount": "120 ml",
"isAlcohol": false
},
{
"type": "lima",
"name": "Zumo de lima",
"amount": "10 ml",
"isAlcohol": false
}
],
"instructions": "Mezclar ron con coca-cola y zumo de lima. Servir con hielo y rodaja de lima."
}
]
}

172
src/main.js Normal file
View File

@@ -0,0 +1,172 @@
import "./styles.css";
import data from "./data.json";
let spiritsData = [];
let recipesData = [];
// Cargar datos desde JSON
async function loadData() {
console.log("data");
console.log(data);
try {
spiritsData = data.spirits;
recipesData = data.recipes;
renderSpirits();
} catch (error) {
console.error("Error cargando datos:", error);
document.getElementById("spiritsGrid").innerHTML =
'<p class="no-recipes">Error al cargar los datos. Asegúrate de que data.json está disponible.</p>';
}
}
function renderSpirits() {
const grid = document.getElementById("spiritsGrid");
grid.innerHTML = spiritsData
.map(
(spirit, index) => `
<div class="spirit-card" id="spirit-${index}">
<input type="checkbox" id="check-${index}">
<div class="spirit-name">${spirit.name}</div>
<div class="spirit-brand">${spirit.brand}</div>
<div class="spirit-type">${spirit.type}</div>
</div>
`,
)
.join("");
// Add toggle logic to cards
spiritsData.map((spirit, index) => {
grid.querySelector(`#spirit-${index}`).addEventListener("click", (e) => {
toggleSpirit(index);
});
});
// Add recipes crossing to mix button
const section = document.getElementsByClassName("section");
if (section && section.length > 0) {
section[0].querySelector(".mix-button").addEventListener("click", (e) => {
findRecipes();
});
}
}
function toggleSpirit(index) {
const card = document.getElementById(`spirit-${index}`);
const checkbox = document.getElementById(`check-${index}`);
checkbox.checked = !checkbox.checked;
card.classList.toggle("selected");
// Check enable/disable mix button on card toggle
updateMixButtonStatus();
}
function updateMixButtonStatus() {
const section = document.getElementsByClassName("section");
const selected = getSelectedSpirits();
if (section && section.length > 0) {
section[0].querySelector(".mix-button").disabled = selected.length === 0;
}
}
function getSelectedSpirits() {
const selectedSpirits = [];
spiritsData.forEach((spirit, index) => {
const checkbox = document.getElementById(`check-${index}`);
if (checkbox.checked) {
selectedSpirits.push(spirit.type);
}
});
return selectedSpirits;
}
function findRecipes() {
const selectedSpirits = getSelectedSpirits();
if (selectedSpirits.length === 0) {
document.getElementById("recipesContainer").innerHTML =
'<p class="no-recipes">Por favor, selecciona al menos un licor para encontrar recetas.</p>';
return;
}
const availableRecipes = recipesData.filter((recipe) => {
const alcoholIngredients = recipe.ingredients.filter(
(ing) => ing.isAlcohol,
);
return alcoholIngredients.every((ingredient) =>
selectedSpirits.includes(ingredient.type),
);
});
renderRecipes(availableRecipes);
document.getElementById("recipesContainer").scrollIntoView({
behavior: "smooth",
block: "start",
});
}
function renderRecipes(recipes) {
const container = document.getElementById("recipesContainer");
if (recipes.length === 0) {
container.innerHTML =
'<p class="no-recipes">No se encontraron recetas con los licores seleccionados. ¡Intenta agregar más licores!</p>';
return;
}
container.innerHTML = recipes
.map((recipe) => {
const alcoholIngredients = recipe.ingredients.filter(
(ing) => ing.isAlcohol,
);
const nonAlcoholIngredients = recipe.ingredients.filter(
(ing) => !ing.isAlcohol,
);
return `
<div class="recipe-card">
<div class="recipe-name">${recipe.name}</div>
<div class="recipe-ingredients">
${alcoholIngredients
.map(
(ing) => `
<div class="ingredient alcohol-ingredient">
<span class="ingredient-amount">${ing.amount}</span>
<span>${ing.name}</span>
</div>
`,
)
.join("")}
${
nonAlcoholIngredients.length > 0
? `
<div class="non-alcohol-section">
<div class="non-alcohol-title">Otros ingredientes:</div>
${nonAlcoholIngredients
.map(
(ing) => `
<div class="ingredient non-alcohol-ingredient">
${ing.amount ? `<span class="ingredient-amount">${ing.amount}</span>` : ""}
<span>${ing.name}</span>
</div>
`,
)
.join("")}
</div>
`
: ""
}
</div>
${
recipe.instructions
? `
<div class="recipe-instructions">
<strong>Preparación:</strong> ${recipe.instructions}
</div>
`
: ""
}
</div>
`;
})
.join("");
}
// Inicializar al cargar la página
document.addEventListener("DOMContentLoaded", loadData);

389
src/styles.css Normal file
View File

@@ -0,0 +1,389 @@
:root {
--cognac: #8b4513;
--amber: #d4a574;
--cream: #f5e6d3;
--olive: #556b2f;
--deep-red: #8b1538;
--shadow: rgba(107, 70, 40, 0.3);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Nunito", sans-serif;
background: linear-gradient(
135deg,
var(--cream) 0%,
#fff8e7 50%,
var(--amber) 100%
);
background-attachment: fixed;
color: #2c1810;
line-height: 1.6;
min-height: 100vh;
position: relative;
overflow-x: hidden;
}
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(
circle at 20% 30%,
rgba(139, 69, 19, 0.03) 0%,
transparent 50%
),
radial-gradient(
circle at 80% 70%,
rgba(213, 165, 116, 0.05) 0%,
transparent 50%
);
pointer-events: none;
z-index: 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem 2rem;
position: relative;
z-index: 1;
}
header {
text-align: center;
margin-bottom: 4rem;
position: relative;
}
h1 {
font-family: "Fredoka", sans-serif;
font-size: 4.5rem;
font-weight: 700;
color: var(--cognac);
margin-bottom: 0.5rem;
letter-spacing: -0.02em;
text-shadow:
2px 2px 0 var(--amber),
4px 4px 0 var(--cream);
animation: fadeInDown 0.8s ease-out;
}
.subtitle {
font-size: 1.4rem;
color: var(--olive);
font-style: italic;
animation: fadeIn 1s ease-out 0.3s backwards;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.section {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2.5rem;
margin-bottom: 2rem;
box-shadow: 0 10px 40px var(--shadow);
border: 2px solid rgba(139, 69, 19, 0.1);
animation: slideUp 0.6s ease-out backwards;
}
.section:nth-child(2) {
animation-delay: 0.2s;
}
.section:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
h2 {
font-family: "Fredoka", sans-serif;
font-size: 2.5rem;
font-weight: 600;
color: var(--deep-red);
margin-bottom: 1.5rem;
border-bottom: 3px solid var(--amber);
padding-bottom: 0.5rem;
}
.spirits-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.spirit-card {
background: linear-gradient(135deg, #fff 0%, var(--cream) 100%);
border: 2px solid var(--amber);
border-radius: 12px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.spirit-card::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 0.8) 0%,
transparent 70%
);
opacity: 0;
transition: opacity 0.3s;
}
.spirit-card:hover::before {
opacity: 1;
}
.spirit-card:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 8px 25px var(--shadow);
border-color: var(--cognac);
}
.spirit-card.selected {
background: linear-gradient(135deg, var(--cognac) 0%, var(--deep-red) 100%);
color: white;
border-color: var(--cognac);
transform: scale(1.05);
}
.spirit-card input[type="checkbox"] {
display: none;
}
.spirit-name {
font-family: "Fredoka", sans-serif;
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 0.3rem;
}
.spirit-brand {
font-size: 0.95rem;
opacity: 0.8;
}
.spirit-type {
font-size: 0.85rem;
font-style: italic;
margin-top: 0.3rem;
opacity: 0.7;
}
.mix-button {
display: block;
width: 100%;
max-width: 400px;
margin: 2rem auto;
padding: 1.2rem 3rem;
font-family: "Fredoka", sans-serif;
font-size: 1.8rem;
font-weight: 600;
background: linear-gradient(135deg, var(--deep-red) 0%, var(--cognac) 100%);
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 8px 20px rgba(139, 21, 56, 0.4);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.mix-button:hover {
transform: translateY(-3px);
box-shadow: 0 12px 30px rgba(139, 21, 56, 0.5);
}
.mix-button:active {
transform: translateY(-1px);
}
.mix-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.recipes-container {
min-height: 200px;
}
.recipe-card {
background: linear-gradient(135deg, #fff 0%, #fff8f0 100%);
border-left: 5px solid var(--deep-red);
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
animation: recipeAppear 0.5s ease-out backwards;
}
.recipe-card:nth-child(1) {
animation-delay: 0.1s;
}
.recipe-card:nth-child(2) {
animation-delay: 0.2s;
}
.recipe-card:nth-child(3) {
animation-delay: 0.3s;
}
.recipe-card:nth-child(4) {
animation-delay: 0.4s;
}
.recipe-card:nth-child(5) {
animation-delay: 0.5s;
}
@keyframes recipeAppear {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.recipe-name {
font-family: "Fredoka", sans-serif;
font-size: 2rem;
font-weight: 600;
color: var(--deep-red);
margin-bottom: 0.8rem;
}
.recipe-ingredients {
margin-bottom: 1rem;
}
.ingredient {
padding: 0.4rem 0;
font-size: 1.1rem;
border-bottom: 1px dashed rgba(139, 69, 19, 0.2);
}
.ingredient:last-child {
border-bottom: none;
}
.alcohol-ingredient {
font-weight: 600;
}
.non-alcohol-section {
margin-top: 1rem;
padding-top: 1rem;
border-top: 2px solid rgba(139, 69, 19, 0.15);
}
.non-alcohol-title {
font-family: "Fredoka", sans-serif;
font-size: 1.1rem;
color: var(--olive);
margin-bottom: 0.5rem;
font-weight: 600;
}
.non-alcohol-ingredient {
font-weight: 400;
opacity: 0.85;
font-size: 1rem;
}
.ingredient-amount {
color: var(--cognac);
font-weight: 600;
margin-right: 0.5rem;
}
.recipe-instructions {
background: rgba(212, 165, 116, 0.15);
padding: 1rem;
border-radius: 8px;
font-style: italic;
margin-top: 1rem;
}
.no-recipes {
text-align: center;
padding: 3rem;
color: var(--olive);
font-size: 1.3rem;
font-style: italic;
}
.loading {
text-align: center;
padding: 2rem;
font-size: 1.2rem;
color: var(--cognac);
}
@media (max-width: 768px) {
h1 {
font-size: 3rem;
}
.spirits-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.section {
padding: 1.5rem;
}
}
.warning-msg {
font-family: "Fredoka", sans-serif;
font-size: 1.1rem;
color: var(--olive);
}