Files
portfoli-ulleres/index.html
T
2026-04-07 23:30:33 +02:00

511 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KAPVOE Portfolio · Blood Bros Sports</title>
<style>
:root{
--bg-1:#040b16;
--bg-2:#071425;
--bg-3:#091a30;
--card:#0d1729;
--card-2:#101d35;
--line:rgba(255,255,255,.10);
--line-strong:rgba(119,173,255,.28);
--text:#f4f7fb;
--muted:#9fb0ca;
--muted-2:#c7d4ea;
--accent:#64c4ff;
--accent-2:#4d7fff;
--green:#28d267;
--orange:#ff9b3d;
--red:#ff5a62;
--shadow:0 16px 44px rgba(0,0,0,.34);
--shadow-soft:0 10px 30px rgba(0,0,0,.22);
--radius-xl:30px;
--radius-lg:24px;
--radius-md:18px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
color:var(--text);
background:
radial-gradient(circle at 12% 10%, rgba(52,122,255,.18), transparent 24%),
radial-gradient(circle at 82% 12%, rgba(100,196,255,.12), transparent 24%),
radial-gradient(circle at 50% 0%, rgba(255,255,255,.03), transparent 30%),
linear-gradient(180deg, var(--bg-1) 0%, var(--bg-2) 38%, var(--bg-3) 100%);
min-height:100vh;
}
.wrap{max-width:1660px;margin:0 auto;padding:20px 18px 42px}
.hero{
display:block;
margin-bottom:18px;
padding:10px 0 4px;
}
.brand-block{display:flex;align-items:center;gap:28px;min-width:0}
.brand-logo-wrap{
width:148px;height:148px;border-radius:38px;
background:linear-gradient(135deg, rgba(255,255,255,.08), rgba(255,255,255,.03));
border:1px solid rgba(255,255,255,.10);
box-shadow:var(--shadow-soft);
display:flex;align-items:center;justify-content:center;overflow:hidden;flex:0 0 auto;
position:relative;
}
.brand-logo-wrap::before{
content:"";position:absolute;inset:0;
background:radial-gradient(circle at 30% 25%, rgba(100,196,255,.22), transparent 38%);
pointer-events:none;
}
.brand-logo{max-width:88%;max-height:88%;object-fit:contain;display:none;position:relative;z-index:1}
.brand-fallback{
width:96px;height:96px;border-radius:28px;
display:flex;align-items:center;justify-content:center;
background:linear-gradient(135deg,#66d6ff,#5b83ff);
color:#04101f;font-weight:1000;font-size:28px;letter-spacing:.04em;
box-shadow:0 10px 20px rgba(0,0,0,.2);
position:relative;z-index:1;
}
.hero-copy{min-width:0}
.hero-copy h1{
margin:0;
font-size:clamp(54px, 8.1vw, 108px);
line-height:.92;
letter-spacing:-.055em;
font-weight:1000;
text-wrap:balance;
}
.hero-copy p{
margin:8px 0 0;
color:var(--muted);
font-size:18px;
line-height:1.42;
max-width:900px
}
.controls{
display:grid;
grid-template-columns:minmax(300px,1.8fr) minmax(190px,.8fr) minmax(190px,.9fr) minmax(190px,.9fr) auto;
gap:12px;
margin-bottom:14px;
padding:14px;
border-radius:28px;
border:1px solid rgba(255,255,255,.08);
background:linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,.02));
box-shadow:var(--shadow-soft);
}
.field,.button{
min-height:58px;border-radius:20px;border:1px solid rgba(255,255,255,.10);
background:linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.03));
color:var(--text);padding:0 18px;font-size:15px;outline:none;box-shadow:var(--shadow-soft);
}
.field{width:100%;-webkit-appearance:none;appearance:none;color-scheme:dark}
select.field{
background:
linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.03));
color:var(--text);
}
select.field option{
background:#14233d;
color:#f4f7fb;
}
select.field option:checked{
background:#234a86;
color:#ffffff;
}
.field:focus,.button:focus{border-color:rgba(100,196,255,.45);box-shadow:0 0 0 4px rgba(100,196,255,.12), var(--shadow-soft)}
.button{
cursor:pointer;font-weight:900;
background:linear-gradient(180deg, rgba(93,154,255,.34), rgba(52,115,214,.28));
transition:transform .16s ease, border-color .16s ease, filter .16s ease;
}
.button:hover{transform:translateY(-1px);filter:brightness(1.04);border-color:rgba(100,196,255,.36)}
.stats{
display:grid;
grid-template-columns:repeat(3,1fr);
gap:14px;
margin-bottom:22px
}
.stat{
min-height:98px;
padding:18px 20px;
border-radius:24px;
background:
radial-gradient(circle at 62% 28%, rgba(91,129,255,.15), transparent 24%),
linear-gradient(180deg, rgba(255,255,255,.045), rgba(255,255,255,.028));
border:1px solid rgba(255,255,255,.09);
box-shadow:var(--shadow-soft);
}
.stat .k{color:var(--muted);font-size:11px;letter-spacing:.16em;text-transform:uppercase;margin-bottom:8px}
.stat .v{font-size:34px;font-weight:1000;line-height:1}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:22px}
.card{
position:relative;overflow:hidden;display:flex;flex-direction:column;
border-radius:32px;border:1px solid rgba(255,255,255,.10);
background:
radial-gradient(circle at 75% 15%, rgba(104,133,255,.18), transparent 28%),
linear-gradient(180deg, rgba(17,29,52,.95), rgba(12,20,36,.98));
box-shadow:var(--shadow);
min-height:780px;
transition:transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.card:hover{
transform:translateY(-6px);
box-shadow:0 24px 56px rgba(0,0,0,.42);
border-color:rgba(100,196,255,.22);
}
.card::before{
content:"";position:absolute;inset:0;pointer-events:none;
background:linear-gradient(180deg, rgba(255,255,255,.04), transparent 22%, transparent 75%, rgba(255,255,255,.02));
}
.top-badge{
position:absolute;
top:14px;
left:-60px;
z-index:4;
width:218px;
text-align:center;
transform:rotate(-31deg);
background:linear-gradient(90deg, #ffb347 0%, #ff7a3d 44%, #ff555f 100%);
color:#fff;
font-weight:1000;
letter-spacing:.14em;
font-size:12px;
padding:10px 0;
box-shadow:0 10px 22px rgba(255,100,60,.30);
text-transform:uppercase;
transform-origin:center;
overflow:visible;
}
.card-media{
position:relative;height:340px;display:flex;align-items:center;justify-content:center;
padding:26px 26px 0;overflow:hidden;
}
.card-actions{
display:flex;gap:12px;
padding:0 26px 6px;
margin-top:2px;
}
.media-stage{
width:100%;height:100%;border-radius:26px;
background:
radial-gradient(circle at 50% 18%, rgba(255,255,255,.12), transparent 34%),
linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.02));
border:1px solid rgba(255,255,255,.08);
display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;
}
.media-stage img{width:100%;height:100%;object-fit:contain;padding:22px;display:block;transition:transform .26s ease}
.card:hover .media-stage img{transform:scale(1.035)}
.media-placeholder{padding:30px;text-align:center;color:var(--muted)}
.media-placeholder strong{display:block;color:var(--text);font-size:18px;margin-bottom:8px}
.media-actions{
display:flex;gap:12px;justify-content:stretch;z-index:3;
pointer-events:none;width:100%;
}
.action-btn{
pointer-events:auto;
display:inline-flex;align-items:center;justify-content:center;gap:10px;
min-height:48px;padding:0 18px;border-radius:999px;border:1px solid rgba(255,255,255,.12);
text-decoration:none;color:#fff;font-weight:900;font-size:14px;backdrop-filter:blur(10px);
box-shadow:0 8px 18px rgba(0,0,0,.22);
transition:transform .16s ease, filter .16s ease, border-color .16s ease;
cursor:pointer;flex:1;
}
.action-btn:hover{transform:translateY(-1px);filter:brightness(1.04)}
.action-wa:hover{animation-play-state:paused}
.action-view{background:rgba(8,15,29,.66)}
.action-wa{
background:linear-gradient(180deg, rgba(37,211,102,.92), rgba(26,171,79,.92));
border-color:rgba(255,255,255,.18);
animation:waPulse 2.2s ease-in-out infinite;
}
@keyframes waPulse{
0%,100%{transform:translateY(0) scale(1); box-shadow:0 8px 18px rgba(0,0,0,.22);}
50%{transform:translateY(-1px) scale(1.02); box-shadow:0 12px 24px rgba(22,163,74,.32);}
}
.wa-icon{
width:18px;height:18px;display:inline-block;flex:0 0 18px;
background-repeat:no-repeat;background-position:center;background-size:contain;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23ffffff' d='M19.11 17.21c-.29-.15-1.71-.84-1.97-.94-.26-.1-.45-.15-.64.15-.19.29-.74.94-.91 1.13-.17.19-.34.22-.63.08-.29-.15-1.24-.46-2.36-1.47-.87-.78-1.46-1.75-1.63-2.04-.17-.29-.02-.45.13-.6.13-.13.29-.34.43-.51.14-.17.19-.29.29-.49.1-.19.05-.37-.02-.52-.08-.15-.64-1.54-.88-2.11-.23-.56-.47-.48-.64-.49h-.54c-.19 0-.49.07-.74.34-.26.27-.98.95-.98 2.31s1 2.67 1.14 2.85c.15.19 1.97 3.13 4.88 4.26 2.92 1.13 2.92.75 3.45.7.53-.05 1.71-.7 1.95-1.38.24-.68.24-1.26.17-1.38-.07-.12-.26-.19-.55-.34Z'/%3E%3Cpath fill='%23ffffff' d='M16.03 3.2c-7.06 0-12.79 5.73-12.79 12.79 0 2.25.59 4.45 1.7 6.39L3 29l6.82-1.79a12.75 12.75 0 0 0 6.21 1.61h.01c7.05 0 12.78-5.74 12.78-12.79S23.09 3.2 16.03 3.2Zm0 23.46h-.01a10.6 10.6 0 0 1-5.4-1.48l-.39-.23-4.05 1.06 1.08-3.95-.25-.4a10.62 10.62 0 1 1 9.02 5Z'/%3E%3C/svg%3E");
}
.body{padding:18px 22px 22px;display:flex;flex-direction:column;gap:16px;flex:1;position:relative;z-index:1}
.title-row{display:flex;justify-content:flex-start;gap:12px;align-items:flex-start}
.title-stack{min-width:0}
.product-code{font-size:16px;font-weight:1000;letter-spacing:.02em;color:#edf4ff}
.model{font-size:34px;font-weight:1000;line-height:1.02;letter-spacing:-.03em;margin-top:8px}
.family-pill{
display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
min-height:42px;padding:0 16px;border-radius:999px;
background:linear-gradient(180deg, rgba(100,196,255,.18), rgba(77,127,255,.14));
border:1px solid rgba(100,196,255,.22);color:#ddf4ff;font-weight:900;font-size:13px;text-transform:uppercase;
letter-spacing:.08em;white-space:nowrap;
}
.price-stock{
display:flex;
justify-content:space-between;
gap:16px;
align-items:flex-end;
padding:4px 0 2px;
}
.price-box{display:flex;flex-direction:column;gap:6px}
.price-label{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.16em}
.price{font-size:38px;font-weight:1000;line-height:1}
.stock-pill{
padding:11px 16px;border-radius:999px;background:rgba(40,210,103,.12);
border:1px solid rgba(40,210,103,.24);color:#b9ffd2;font-weight:900;white-space:nowrap;
box-shadow:0 8px 18px rgba(0,0,0,.16);
}
.sales-hook{
margin-top:10px;
display:flex;align-items:center;gap:10px;flex-wrap:wrap;
color:#eef6ff;font-size:14px;font-weight:800;
}
.sales-hook-badge{
display:inline-flex;align-items:center;justify-content:center;
min-height:28px;padding:0 10px;border-radius:999px;
background:rgba(255,255,255,.06);
border:1px solid rgba(255,255,255,.10);
color:#dfeaff;font-size:11px;letter-spacing:.08em;text-transform:uppercase;
}
.sales-hook-text{color:#dfeaff}
.stock-pill.stock-last{
background:rgba(255,90,98,.14);
border-color:rgba(255,90,98,.28);
color:#ffd6d9;
}
.stock-pill.stock-low{
background:rgba(255,155,61,.14);
border-color:rgba(255,155,61,.28);
color:#ffe2c2;
}
.stock-pill.stock-ok{
background:rgba(40,210,103,.12);
border-color:rgba(40,210,103,.24);
color:#b9ffd2;
}
.chips{display:flex;gap:8px;flex-wrap:wrap}
.chip{
padding:8px 12px;border-radius:999px;border:1px solid rgba(255,255,255,.10);
background:rgba(255,255,255,.04);color:var(--muted-2);font-size:12px;font-weight:800;
}
.desc-box{
border-radius:20px;padding:18px 18px 16px;
background:linear-gradient(180deg, rgba(36,61,112,.88), rgba(29,53,102,.92));
border:1px solid rgba(100,196,255,.22);
box-shadow:inset 0 1px 0 rgba(255,255,255,.05), 0 10px 22px rgba(0,0,0,.15);
}
.desc-title{font-size:15px;font-weight:1000;margin:0 0 12px;color:#fff}
.desc-list{margin:0;padding-left:22px;color:#fff;display:grid;gap:10px;font-size:15px;line-height:1.45}
.desc-list li::marker{color:#ffffff}
.desc-plain{margin:0;color:#fff;font-size:15px;line-height:1.55}
.footer-row{
margin-top:auto;display:grid;grid-template-columns:1fr 1fr;gap:12px;color:var(--muted);font-size:13px;
}
.footer-box{
min-height:62px;border-radius:18px;padding:12px 14px;
border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.03);
display:flex;flex-direction:column;justify-content:center;gap:6px;
}
.footer-box strong{font-size:12px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted)}
.footer-box span{color:#edf4ff;font-weight:800}
.error,.empty{
border:1px dashed rgba(255,255,255,.16);border-radius:28px;padding:40px 24px;text-align:center;color:var(--muted);
background:rgba(255,255,255,.03);box-shadow:var(--shadow-soft);margin-bottom:18px;
}
.error strong,.empty strong{display:block;color:var(--text);font-size:24px;margin-bottom:10px}
.modal{
position:fixed;inset:0;background:rgba(0,0,0,.80);display:none;align-items:center;justify-content:center;
padding:30px;z-index:999;backdrop-filter:blur(8px);
}
.modal.open{display:flex}
.modal-box{
position:relative;max-width:min(1280px,96vw);max-height:92vh;border-radius:24px;overflow:hidden;
background:#09101c;border:1px solid rgba(255,255,255,.12);box-shadow:0 24px 70px rgba(0,0,0,.55);
}
.modal-box img{display:block;max-width:100%;max-height:92vh;object-fit:contain;background:#09101c}
.modal-close{
position:absolute;top:14px;right:14px;width:44px;height:44px;border-radius:50%;cursor:pointer;
border:1px solid rgba(255,255,255,.18);background:rgba(0,0,0,.45);color:#fff;font-size:24px;
}
.action-buy{
background:linear-gradient(180deg, rgba(93,154,255,.34), rgba(52,115,214,.28));
border-color:rgba(100,196,255,.24);
}
.buy-modal{
position:fixed;inset:0;display:none;align-items:center;justify-content:center;
background:rgba(0,0,0,.72);backdrop-filter:blur(8px);z-index:1200;padding:20px;
}
.buy-modal.open{display:flex}
.buy-box{
width:min(760px,96vw);max-height:92vh;overflow:auto;
background:linear-gradient(180deg,#12203a,#0b1630);
border:1px solid rgba(255,255,255,.12);
border-radius:28px;padding:24px;box-shadow:0 24px 70px rgba(0,0,0,.45)
}
.buy-title{margin:0 0 8px;font-size:28px;font-weight:1000}
.buy-subtitle{margin:0 0 18px;color:var(--muted)}
.buy-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.buy-grid .full{grid-column:1 / -1}
.buy-input{
width:100%;min-height:54px;border-radius:16px;border:1px solid rgba(255,255,255,.10);
background:rgba(255,255,255,.04);color:#fff;padding:14px 16px;font-size:15px;outline:none
}
.buy-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}
.buy-btn{
min-height:50px;padding:0 18px;border:none;border-radius:999px;cursor:pointer;
font-weight:900;font-size:15px
}
.buy-btn-secondary{background:rgba(255,255,255,.08);color:#fff}
.buy-btn-primary{background:#2563eb;color:#fff}
.buy-summary{
margin:0 0 18px;padding:14px 16px;border-radius:18px;
background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08)
}
.buy-error{color:#fecaca;font-size:14px;margin-top:10px;display:none}
@media (max-width:1320px){
.controls{grid-template-columns:1fr 1fr 1fr 1fr}
.controls .button{grid-column:1 / -1}
.stats{grid-template-columns:1fr 1fr}
}
@media (max-width:920px){
.controls{grid-template-columns:1fr;padding:12px}
.stats{grid-template-columns:1fr}
.cards{grid-template-columns:1fr}
.card{min-height:auto}
.card-media{height:300px}
.title-row{flex-direction:column;align-items:flex-start}
.family-pill{white-space:normal}
.price-stock{flex-direction:column;align-items:flex-start}
.card-actions{padding:0 18px 6px}
.media-actions{justify-content:stretch}
.action-btn{flex:1}
}
</style>
</head>
<body>
<div class="wrap">
<section class="hero">
<div class="brand-block">
<div class="brand-logo-wrap">
<img id="brandLogo" class="brand-logo" alt="Blood Bros Sports logo">
<div id="brandFallback" class="brand-fallback">BB</div>
</div>
<div class="hero-copy">
<h1>KAPVOE Portfolio</h1>
<p>Catàleg de les ulleres que estan marcant tendència. Explora cada model, descobreix-ne les característiques, amplia la imatge per veure-les en detall i compra-les per WhatsApp en segons.</p>
</div>
</div>
</section>
<div style="margin:8px 0 10px;color:var(--muted);font-size:12px;letter-spacing:.18em;text-transform:uppercase;font-weight:800;">Explora el catàleg</div>
<section class="controls">
<input id="searchInput" class="field" type="text" placeholder="🔎 Cerca per codi, model, família, colors o descripció..." />
<select id="topFilter" class="field">
<option value="all">🏷️ Tots</option>
<option value="top">Només TOP VENDES</option>
<option value="normal">Sense TOP VENDES</option>
</select>
<select id="sortFilter" class="field">
<option value="code-asc">↕️ Codi A → Z</option>
<option value="code-desc">Codi Z → A</option>
<option value="price-asc">Preu menor → major</option>
<option value="price-desc">Preu major → menor</option>
<option value="stock-desc">Més stock</option>
</select>
<select id="priceFilter" class="field">
<option value="all">💶 Tots els preus</option>
<option value="0-60">Fins a 60 €</option>
<option value="60-80">De 60 € a 80 €</option>
<option value="80+">Més de 80 €</option>
</select>
<button id="refreshBtn" class="button">Actualitza</button>
</section>
<div style="margin:2px 0 10px;color:var(--muted);font-size:12px;letter-spacing:.18em;text-transform:uppercase;font-weight:800;">Resum en viu</div>
<section class="stats">
<div class="stat"><div class="k">Models visibles</div><div class="v" id="statVisible">0</div></div>
<div class="stat"><div class="k">Top vendes visibles</div><div class="v" id="statTop">0</div></div>
<div class="stat"><div class="k">Darrera actualització</div><div class="v" id="statUpdated">--:--</div></div>
</section>
<div id="errorBox" class="error" style="display:none;">
<strong>No s'han pogut carregar les dades</strong>
<div id="errorText">Revisa l'endpoint JSON i la publicació del Web App.</div>
</div>
<div id="emptyBox" class="empty" style="display:none;">
<strong>No hi ha cap model visible</strong>
<div>No hi ha cap producte que compleixi els filtres actuals o amb stock suficient.</div>
</div>
<section id="cards" class="cards"></section>
</div>
<div id="imageModal" class="modal" aria-hidden="true">
<div class="modal-box">
<button class="modal-close" id="modalClose" aria-label="Tanca">×</button>
<img id="modalImage" src="" alt="">
</div>
</div>
<div id="buyModal" class="buy-modal" aria-hidden="true">
<div class="buy-box">
<h2 class="buy-title">Comprar ara</h2>
<p class="buy-subtitle">Omple les teves dades i et redirigirem al pagament segur amb Stripe.</p>
<div id="buySummary" class="buy-summary"></div>
<form id="buyForm">
<div class="buy-grid">
<input class="buy-input full" name="customer_name" placeholder="Nom complet" required>
<input class="buy-input full" name="address" placeholder="Adreça postal" required>
<input class="buy-input" name="postal_code" placeholder="Codi postal" required>
<input class="buy-input" name="city" placeholder="Ciutat" required>
<input class="buy-input" name="province" placeholder="Província" required>
<input class="buy-input" name="phone" placeholder="Telèfon" required>
<input class="buy-input full" type="email" name="email" placeholder="Correu electrònic" required>
<input type="hidden" name="product_code">
<input type="hidden" name="product_name">
<input type="hidden" name="price">
<input type="hidden" name="quantity" value="1">
</div>
<div id="buyError" class="buy-error"></div>
<div class="buy-actions">
<button type="button" class="buy-btn buy-btn-secondary" id="buyCancel">Cancel·la</button>
<button type="submit" class="buy-btn buy-btn-primary">Continuar al pagament</button>
</div>
</form>
</div>
</div>
<script type="module" src="js/app.js"></script>
</body>
</html>