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);
}
/* Card Backdrop Art (Dimmed Photographic Watermark) */
.card-backdrop-art {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: inherit;
/* Facility Card Cropped Product Banner */
.card-photo-banner {
position: relative;
height: 72px;
width: calc(100% + 32px);
margin: -16px -16px 10px -16px;
overflow: hidden;
pointer-events: none;
z-index: 0;
background: var(--bg-surface-sub);
border-bottom: 1px solid var(--border-color);
}
.card-backdrop-img {
.card-banner-img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center 25%;
opacity: 0.08;
object-position: center 35%;
display: block;
transition: transform 0.3s ease;
}
.facility-card:hover .card-banner-img {
transform: scale(1.05);
transition: opacity 0.3s ease, transform 0.3s ease;
}
[data-theme="dark"] .card-backdrop-img {
opacity: 0.12;
filter: brightness(0.9) contrast(1.1);
}
.card-backdrop-overlay {
.card-banner-gradient {
position: absolute;
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;
}
[data-theme="dark"] .card-backdrop-overlay {
background: linear-gradient(135deg, rgba(17, 24, 39, 0.5) 0%, transparent 60%, rgba(17, 24, 39, 0.7) 100%);
[data-theme="dark"] .card-banner-gradient {
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 {
opacity: 0.14;
transform: scale(1.08);
.card-banner-sector-tag {
position: absolute;
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 {
opacity: 0.20;
.card-banner-tier-tag {
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 {
opacity: 0.88;
opacity: 0.92;
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 {
display: flex;
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 { 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 = [
AGRICULTURE_SECTOR,
MATERIALS_SECTOR,
MINING_SECTOR,
CHEMICALS_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 = [
@ -23,7 +36,13 @@ export const PRODUCTS = [
...MINING_PRODUCTS,
...CHEMICALS_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

View file

@ -8,18 +8,22 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
const isSelected = state.selectedProductId === p.id;
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) {
const canAffordUnlock = state.cash >= p.unlockCost;
return `
<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-icon-wrap" style="background: ${p.color}15; color: ${p.color};">
<iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon>
@ -108,7 +112,16 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
return `
<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-icon-wrap" style="background: ${p.color}15; color: ${p.color};">
<iconify-icon icon="${p.icon}" class="icon-lg"></iconify-icon>