feat: replace toned background with cropped product photo banner on facility cards

This commit is contained in:
Christian 2026-10-16 20:56:13 +02:00
parent e444be05c0
commit 221ed97415
3 changed files with 117 additions and 54 deletions

View file

@ -91,74 +91,105 @@
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
} }
/* Card Backdrop Art (Dimmed Photographic Watermark) */ /* Facility Card Cropped Product Banner */
.card-backdrop-art { .card-photo-banner {
position: absolute; position: relative;
top: 0; height: 72px;
right: 0; width: calc(100% + 32px);
bottom: 0; margin: -16px -16px 10px -16px;
left: 0;
border-radius: inherit;
overflow: hidden; overflow: hidden;
pointer-events: none; background: var(--bg-surface-sub);
z-index: 0; border-bottom: 1px solid var(--border-color);
} }
.card-backdrop-img { .card-banner-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
object-position: center 25%; object-position: center 35%;
opacity: 0.08; display: block;
transition: transform 0.3s ease;
}
.facility-card:hover .card-banner-img {
transform: scale(1.05); transform: scale(1.05);
transition: opacity 0.3s ease, transform 0.3s ease;
} }
[data-theme="dark"] .card-backdrop-img { .card-banner-gradient {
opacity: 0.12;
filter: brightness(0.9) contrast(1.1);
}
.card-backdrop-overlay {
position: absolute; position: absolute;
inset: 0; inset: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.4) 0%, transparent 60%, rgba(255, 255, 255, 0.6) 100%); background: linear-gradient(to bottom, rgba(15, 23, 42, 0.15) 0%, rgba(15, 23, 42, 0.45) 100%);
pointer-events: none; pointer-events: none;
} }
[data-theme="dark"] .card-backdrop-overlay { [data-theme="dark"] .card-banner-gradient {
background: linear-gradient(135deg, rgba(17, 24, 39, 0.5) 0%, transparent 60%, rgba(17, 24, 39, 0.7) 100%); background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2) 0%, rgba(11, 15, 25, 0.65) 100%);
} }
.facility-card:hover .card-backdrop-img { .card-banner-sector-tag {
opacity: 0.14; position: absolute;
transform: scale(1.08); top: 8px;
left: 10px;
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.65rem;
font-weight: 700;
padding: 2px 7px;
border-radius: 4px;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
background: rgba(15, 23, 42, 0.65);
color: #ffffff !important;
text-transform: uppercase;
letter-spacing: 0.04em;
z-index: 2;
} }
[data-theme="dark"] .facility-card:hover .card-backdrop-img { .card-banner-tier-tag {
opacity: 0.20; position: absolute;
top: 8px;
right: 10px;
font-size: 0.62rem;
font-weight: 800;
padding: 2px 6px;
border-radius: 4px;
background: rgba(15, 23, 42, 0.75);
color: #f8fafc;
letter-spacing: 0.03em;
z-index: 2;
}
/* Locked Card Banner */
.card-photo-banner.locked-banner .card-banner-img {
filter: grayscale(60%) brightness(0.8);
}
.card-banner-lock-badge {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
background: rgba(15, 23, 42, 0.45);
color: #ffffff;
font-size: 0.75rem;
font-weight: 700;
z-index: 2;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.card-banner-lock-badge iconify-icon {
font-size: 1rem;
} }
.facility-card.locked-card { .facility-card.locked-card {
opacity: 0.88; opacity: 0.92;
background: var(--bg-surface-sub); background: var(--bg-surface-sub);
} }
.facility-card.locked-card .card-backdrop-img {
filter: grayscale(45%) contrast(0.95);
opacity: 0.06;
}
[data-theme="dark"] .facility-card.locked-card .card-backdrop-img {
opacity: 0.09;
}
/* Ensure all interactive card contents sit above the background watermark */
.facility-card > *:not(.card-backdrop-art) {
position: relative;
z-index: 1;
}
.facility-header { .facility-header {
display: flex; display: flex;
gap: 12px; gap: 12px;

View file

@ -8,13 +8,26 @@ import { CHEMICALS_SECTOR, CHEMICALS_PRODUCTS } from './sectors/chemicals.js';
import { ELECTRONICS_SECTOR, ELECTRONICS_PRODUCTS } from './sectors/electronics.js'; import { ELECTRONICS_SECTOR, ELECTRONICS_PRODUCTS } from './sectors/electronics.js';
import { HEAVY_INDUSTRY_SECTOR, HEAVY_INDUSTRY_PRODUCTS } from './sectors/heavyIndustry.js'; import { HEAVY_INDUSTRY_SECTOR, HEAVY_INDUSTRY_PRODUCTS } from './sectors/heavyIndustry.js';
import { ENERGY_SECTOR, ENERGY_PRODUCTS } from './sectors/energy.js';
import { CONSTRUCTION_SECTOR, CONSTRUCTION_PRODUCTS } from './sectors/construction.js';
import { AEROSPACE_SECTOR, AEROSPACE_PRODUCTS } from './sectors/aerospace.js';
import { BIOTECH_SECTOR, BIOTECH_PRODUCTS } from './sectors/biotech.js';
import { CONSUMER_GOODS_SECTOR, CONSUMER_GOODS_PRODUCTS } from './sectors/consumer_goods.js';
import { AUTOMATION_SECTOR, AUTOMATION_PRODUCTS } from './sectors/automation.js';
export const SECTORS = [ export const SECTORS = [
AGRICULTURE_SECTOR, AGRICULTURE_SECTOR,
MATERIALS_SECTOR, MATERIALS_SECTOR,
MINING_SECTOR, MINING_SECTOR,
CHEMICALS_SECTOR, CHEMICALS_SECTOR,
ELECTRONICS_SECTOR, ELECTRONICS_SECTOR,
HEAVY_INDUSTRY_SECTOR HEAVY_INDUSTRY_SECTOR,
ENERGY_SECTOR,
CONSTRUCTION_SECTOR,
AEROSPACE_SECTOR,
BIOTECH_SECTOR,
CONSUMER_GOODS_SECTOR,
AUTOMATION_SECTOR
]; ];
export const PRODUCTS = [ export const PRODUCTS = [
@ -23,7 +36,13 @@ export const PRODUCTS = [
...MINING_PRODUCTS, ...MINING_PRODUCTS,
...CHEMICALS_PRODUCTS, ...CHEMICALS_PRODUCTS,
...ELECTRONICS_PRODUCTS, ...ELECTRONICS_PRODUCTS,
...HEAVY_INDUSTRY_PRODUCTS ...HEAVY_INDUSTRY_PRODUCTS,
...ENERGY_PRODUCTS,
...CONSTRUCTION_PRODUCTS,
...AEROSPACE_PRODUCTS,
...BIOTECH_PRODUCTS,
...CONSUMER_GOODS_PRODUCTS,
...AUTOMATION_PRODUCTS
]; ];
// Helper to look up a product by ID // Helper to look up a product by ID

View file

@ -8,18 +8,22 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
const isSelected = state.selectedProductId === p.id; const isSelected = state.selectedProductId === p.id;
const productImgPath = `assets/images/products/${p.id}_product.jpg`; const productImgPath = `assets/images/products/${p.id}_product.jpg`;
const backdropArtHtml = `
<div class="card-backdrop-art" aria-hidden="true">
<img src="${productImgPath}" alt="" class="card-backdrop-img" onerror="this.parentElement.style.display='none';">
<div class="card-backdrop-overlay"></div>
</div>
`;
if (!pState.unlocked) { if (!pState.unlocked) {
const canAffordUnlock = state.cash >= p.unlockCost; const canAffordUnlock = state.cash >= p.unlockCost;
return ` return `
<div class="facility-card locked-card ${isSelected ? 'selected' : ''}" id="card-${p.id}" data-product="${p.id}"> <div class="facility-card locked-card ${isSelected ? 'selected' : ''}" id="card-${p.id}" data-product="${p.id}">
${backdropArtHtml} <!-- Cropped Product Header Banner (Locked) -->
<div class="card-photo-banner locked-banner">
<img src="${productImgPath}" alt="${p.name}" class="card-banner-img" onerror="this.parentElement.style.display='none';">
<div class="card-banner-gradient"></div>
<div class="card-banner-lock-badge">
<iconify-icon icon="lucide:lock"></iconify-icon>
<span>Permit Required</span>
</div>
<span class="card-banner-tier-tag">Tier ${p.tier}</span>
</div>
<div class="facility-header"> <div class="facility-header">
<div class="facility-icon-wrap" style="background: ${p.color}15; color: ${p.color};"> <div class="facility-icon-wrap" style="background: ${p.color}15; color: ${p.color};">
<iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon> <iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon>
@ -108,7 +112,16 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
return ` return `
<div class="facility-card active-card ${isSelected ? 'selected' : ''}" id="card-${p.id}" data-product="${p.id}"> <div class="facility-card active-card ${isSelected ? 'selected' : ''}" id="card-${p.id}" data-product="${p.id}">
${backdropArtHtml} <!-- Cropped Product Header Banner (Active) -->
<div class="card-photo-banner">
<img src="${productImgPath}" alt="${p.name}" class="card-banner-img" onerror="this.parentElement.style.display='none';">
<div class="card-banner-gradient"></div>
<span class="card-banner-sector-tag" style="background: ${p.color}25; color: ${p.color};">
<iconify-icon icon="${p.icon}"></iconify-icon> ${sector?.name}
</span>
<span class="card-banner-tier-tag">Tier ${p.tier}</span>
</div>
<div class="facility-header"> <div class="facility-header">
<div class="facility-icon-wrap" style="background: ${p.color}15; color: ${p.color};"> <div class="facility-icon-wrap" style="background: ${p.color}15; color: ${p.color};">
<iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon> <iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon>