T1 dark green (#1a6b1a), T2 light blue (#2070b0), T3 red (#a82020), T4 regal purple (#6b2d8e). Cal approved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2238 lines
81 KiB
HTML
2238 lines
81 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Refractor Tier Visual Design — Paper Dynasty</title>
|
|
<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=Open+Sans:wght@300;400;700&family=Source+Sans+3:wght@400;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
/* ═══════════════════════════════════════════════════════
|
|
GLOBAL RESET & THEME
|
|
═══════════════════════════════════════════════════════ */
|
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
|
|
body {
|
|
font-family: 'Open Sans', system-ui, sans-serif;
|
|
background: #0e0e12;
|
|
color: #ccc;
|
|
min-height: 100vh;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
PAGE HEADER
|
|
═══════════════════════════════════════════════════════ */
|
|
.page-header {
|
|
text-align: center;
|
|
padding: 28px 24px 18px;
|
|
border-bottom: 1px solid #1e1e2a;
|
|
}
|
|
.page-header h1 {
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: #fff;
|
|
letter-spacing: 2px;
|
|
text-transform: uppercase;
|
|
margin-bottom: 5px;
|
|
}
|
|
.page-header .subtitle {
|
|
font-size: 12px;
|
|
color: #555;
|
|
letter-spacing: 0.4px;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
ANIMATION TOGGLE BAR
|
|
═══════════════════════════════════════════════════════ */
|
|
.anim-toggle-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 14px;
|
|
padding: 14px 24px 6px;
|
|
}
|
|
.anim-toggle-btn {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.6px;
|
|
text-transform: uppercase;
|
|
padding: 8px 22px;
|
|
border-radius: 4px;
|
|
border: 2px solid #333;
|
|
background: #1a1a26;
|
|
color: #555;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
.anim-toggle-btn.active {
|
|
border-color: #c9a94e;
|
|
background: #1c180a;
|
|
color: #c9a94e;
|
|
box-shadow: 0 0 14px rgba(201,169,78,0.28);
|
|
}
|
|
.anim-toggle-btn:hover:not(.active) {
|
|
border-color: #555;
|
|
color: #888;
|
|
}
|
|
.anim-toggle-hint {
|
|
font-size: 10px;
|
|
color: #3a3a4a;
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
ANIMATION INFO PANEL
|
|
═══════════════════════════════════════════════════════ */
|
|
.anim-info-panel {
|
|
margin: 10px auto 0;
|
|
max-width: 760px;
|
|
background: #0f0f18;
|
|
border: 1px solid #1e1e2e;
|
|
border-left: 3px solid #c9a94e;
|
|
border-radius: 4px;
|
|
padding: 10px 16px;
|
|
font-size: 10px;
|
|
color: #666;
|
|
line-height: 1.65;
|
|
}
|
|
.anim-info-panel strong {
|
|
color: #999;
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-size: 10px;
|
|
letter-spacing: 0.8px;
|
|
text-transform: uppercase;
|
|
}
|
|
.anim-info-panel ul {
|
|
padding-left: 16px;
|
|
}
|
|
.anim-info-panel li {
|
|
margin-bottom: 3px;
|
|
}
|
|
.anim-info-panel .tier-badge {
|
|
display: inline-block;
|
|
font-weight: 700;
|
|
margin-right: 2px;
|
|
}
|
|
.anim-info-panel .tier-badge.t3 { color: #c9a94e; }
|
|
.anim-info-panel .tier-badge.t4 { color: #7fdfdf; }
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
COMPARISON SECTION — single row, all 5 tiers
|
|
═══════════════════════════════════════════════════════ */
|
|
.comparison-section {
|
|
padding: 20px 20px 12px;
|
|
}
|
|
.comparison-section .section-label {
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
color: #444;
|
|
margin-bottom: 14px;
|
|
text-align: center;
|
|
}
|
|
.comparison-row {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-end;
|
|
gap: 12px;
|
|
flex-wrap: nowrap;
|
|
margin-bottom: 18px;
|
|
}
|
|
.comparison-slot {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
transition: transform 0.18s;
|
|
}
|
|
.comparison-slot:hover {
|
|
transform: translateY(-4px);
|
|
}
|
|
.comparison-slot.active .tier-label {
|
|
color: #fff;
|
|
}
|
|
.comparison-slot.active .card-thumb-wrap {
|
|
outline: 2px solid #6c8aff;
|
|
box-shadow: 0 4px 18px rgba(108,138,255,0.25);
|
|
}
|
|
|
|
.tier-label {
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.8px;
|
|
color: #777;
|
|
margin-bottom: 7px;
|
|
transition: color 0.2s;
|
|
white-space: nowrap;
|
|
}
|
|
.tier-added {
|
|
font-size: 9px;
|
|
color: #3a3a4a;
|
|
margin-top: 5px;
|
|
text-align: center;
|
|
max-width: 220px;
|
|
line-height: 1.4;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.comparison-slot:hover .tier-added { color: #555; }
|
|
.comparison-slot.active .tier-added { color: #5a6a8a; }
|
|
|
|
/* Standard thumbnail: scale ~0.19 (1200x600 -> 228x114) */
|
|
.card-thumb-wrap {
|
|
overflow: hidden;
|
|
border-radius: 3px;
|
|
background: #111118;
|
|
transition: box-shadow 0.2s, outline 0.2s;
|
|
}
|
|
.standard-thumb .card-thumb-wrap {
|
|
width: 228px;
|
|
height: 114px;
|
|
}
|
|
.standard-thumb .card-scale-container {
|
|
transform: scale(0.19);
|
|
transform-origin: top left;
|
|
width: 1200px;
|
|
height: 600px;
|
|
}
|
|
|
|
/* T4 featured thumbnail: scale ~0.26 (1200x600 -> 312x156) */
|
|
.featured-thumb .card-thumb-wrap {
|
|
width: 312px;
|
|
height: 156px;
|
|
}
|
|
.featured-thumb .card-scale-container {
|
|
transform: scale(0.26);
|
|
transform-origin: top left;
|
|
width: 1200px;
|
|
height: 600px;
|
|
}
|
|
.featured-thumb .tier-label {
|
|
color: #c9a94e;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
FULL-SIZE DETAIL + CONTROLS LAYOUT
|
|
═══════════════════════════════════════════════════════ */
|
|
.detail-section {
|
|
display: flex;
|
|
gap: 0;
|
|
padding: 0 20px 48px;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
}
|
|
.detail-card-area {
|
|
flex-shrink: 0;
|
|
}
|
|
.detail-card-area .detail-heading {
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
color: #444;
|
|
margin-bottom: 10px;
|
|
}
|
|
.detail-card-area .detail-heading span {
|
|
color: #8a9acc;
|
|
font-weight: 700;
|
|
}
|
|
.detail-card-wrap {
|
|
border: 1px solid #2a2a3a;
|
|
border-radius: 3px;
|
|
overflow: hidden;
|
|
background: #111118;
|
|
width: 1200px;
|
|
height: 600px;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
CONTROLS PANEL
|
|
═══════════════════════════════════════════════════════ */
|
|
#controls {
|
|
width: 310px;
|
|
min-width: 310px;
|
|
background: #13131a;
|
|
border: 1px solid #222230;
|
|
border-radius: 4px;
|
|
margin-left: 18px;
|
|
padding: 14px;
|
|
overflow-y: auto;
|
|
max-height: 620px;
|
|
flex-shrink: 0;
|
|
}
|
|
#controls h2 {
|
|
font-size: 13px;
|
|
color: #ddd;
|
|
margin-bottom: 3px;
|
|
font-weight: 700;
|
|
}
|
|
#controls .panel-subtitle {
|
|
font-size: 10px;
|
|
color: #444;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.preset-row {
|
|
display: flex;
|
|
gap: 4px;
|
|
margin-bottom: 14px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.preset-btn {
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
padding: 5px 9px;
|
|
border: 1px solid #2a2a3a;
|
|
border-radius: 3px;
|
|
background: #1a1a26;
|
|
color: #888;
|
|
cursor: pointer;
|
|
transition: all 0.12s;
|
|
white-space: nowrap;
|
|
}
|
|
.preset-btn:hover {
|
|
background: #242434;
|
|
color: #bbb;
|
|
}
|
|
.preset-btn.active {
|
|
background: #20304a;
|
|
border-color: #4a6aaa;
|
|
color: #aac0f0;
|
|
}
|
|
|
|
.ctrl-group {
|
|
margin-bottom: 12px;
|
|
}
|
|
.ctrl-group h3 {
|
|
font-size: 9px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
color: #555;
|
|
margin-bottom: 5px;
|
|
padding-bottom: 3px;
|
|
border-bottom: 1px solid #1e1e28;
|
|
}
|
|
.ctrl-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
padding: 2px 0;
|
|
font-size: 11px;
|
|
color: #999;
|
|
}
|
|
.ctrl-row label {
|
|
flex: 1;
|
|
font-size: 10px;
|
|
color: #888;
|
|
}
|
|
.ctrl-row input[type="color"] {
|
|
width: 26px;
|
|
height: 18px;
|
|
border: 1px solid #3a3a4a;
|
|
border-radius: 2px;
|
|
background: none;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
}
|
|
.ctrl-row input[type="range"] {
|
|
width: 80px;
|
|
accent-color: #5a7adf;
|
|
}
|
|
.ctrl-row select {
|
|
background: #1a1a26;
|
|
color: #bbb;
|
|
border: 1px solid #2a2a3a;
|
|
border-radius: 2px;
|
|
padding: 2px 4px;
|
|
font-size: 10px;
|
|
}
|
|
.ctrl-row .val {
|
|
font-size: 9px;
|
|
color: #4a4a5a;
|
|
width: 26px;
|
|
text-align: right;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.export-area { margin-top: 12px; }
|
|
.export-btn {
|
|
width: 100%;
|
|
padding: 7px;
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
background: #1e2e4a;
|
|
color: #7a9adf;
|
|
border: 1px solid #3a4a6a;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
transition: background 0.12s;
|
|
}
|
|
.export-btn:hover { background: #283858; }
|
|
#cssOutput {
|
|
margin-top: 7px;
|
|
width: 100%;
|
|
min-height: 100px;
|
|
background: #0c0c12;
|
|
color: #7aaa8a;
|
|
border: 1px solid #1e1e2a;
|
|
border-radius: 3px;
|
|
padding: 7px;
|
|
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
font-size: 9px;
|
|
line-height: 1.5;
|
|
resize: vertical;
|
|
display: none;
|
|
}
|
|
#cssOutput.visible { display: block; }
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
CARD BASE STYLES (faithful to production card HTML)
|
|
═══════════════════════════════════════════════════════ */
|
|
.pd-card {
|
|
width: 1200px;
|
|
height: 600px;
|
|
background: #fff;
|
|
color: #000;
|
|
font-family: 'Open Sans', sans-serif;
|
|
font-weight: 400;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* HEADER */
|
|
.pd-card .card-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 65px;
|
|
align-items: center;
|
|
position: relative;
|
|
background: #fff;
|
|
}
|
|
.pd-card .header-left {
|
|
width: 477px;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .hand-indicator {
|
|
width: 29px;
|
|
font-size: 30px;
|
|
font-weight: 700;
|
|
text-align: center;
|
|
margin-left: 6px;
|
|
}
|
|
.pd-card .header-vline {
|
|
height: 100%;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .player-info {
|
|
padding-left: 6px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
height: 100%;
|
|
flex: 1;
|
|
overflow: hidden;
|
|
}
|
|
.pd-card .player-name {
|
|
font-variant: small-caps;
|
|
font-size: 27px;
|
|
font-weight: 700;
|
|
line-height: 1.15;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .player-position {
|
|
padding-left: 18px;
|
|
font-size: 18px;
|
|
margin-top: -1px;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .header-middle {
|
|
width: 246px;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .rarity-badge {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
letter-spacing: 2px;
|
|
text-transform: uppercase;
|
|
color: #333;
|
|
padding: 4px 18px;
|
|
border: 2px solid #C9A94E;
|
|
border-radius: 3px;
|
|
background: linear-gradient(135deg, #FFF8E7, #F5E6C8);
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .header-right {
|
|
flex: 1;
|
|
height: 100%;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.pd-card .stat-stealing {
|
|
position: absolute;
|
|
left: 4px;
|
|
top: 5px;
|
|
font-size: 22px;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .stat-running {
|
|
position: absolute;
|
|
right: 10px;
|
|
top: 5px;
|
|
font-size: 22px;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .stat-bunting {
|
|
position: absolute;
|
|
left: 4px;
|
|
top: 33px;
|
|
font-size: 22px;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .stat-hitrun {
|
|
position: absolute;
|
|
right: 10px;
|
|
top: 33px;
|
|
font-size: 22px;
|
|
white-space: nowrap;
|
|
}
|
|
.pd-card .stat-cardset {
|
|
position: absolute;
|
|
right: 10px;
|
|
bottom: 4px;
|
|
font-size: 13px;
|
|
color: #666;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* COLUMN HEADERS */
|
|
.pd-card .result-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 30px;
|
|
}
|
|
.pd-card .col-header-left-group {
|
|
display: flex;
|
|
width: 600px;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .col-header-right-group {
|
|
display: flex;
|
|
width: 600px;
|
|
}
|
|
.pd-card .col-header {
|
|
width: 200px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 20px;
|
|
font-weight: 700;
|
|
color: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
/* col-header border-right: controlled inline via style attribute for tier theming */
|
|
.pd-card .col-header.blue-grad {
|
|
background-image: linear-gradient(to right, rgba(0,156,224,1), rgba(0,156,224,0.5), rgba(0,156,224,1));
|
|
}
|
|
.pd-card .col-header.red-grad {
|
|
background-image: linear-gradient(to right, rgba(211,49,21,1), rgba(211,49,21,0.5), rgba(211,49,21,1));
|
|
}
|
|
|
|
/* RESULT AREA */
|
|
.pd-card .result-area {
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 505px;
|
|
font-family: 'Source Sans 3', sans-serif;
|
|
font-size: 26px;
|
|
line-height: 1.3;
|
|
}
|
|
.pd-card .result-left-group {
|
|
display: flex;
|
|
width: 600px;
|
|
background-color: #ACE6FF;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .result-right-group {
|
|
display: flex;
|
|
width: 600px;
|
|
background-color: #EAA49C;
|
|
}
|
|
.pd-card .result-col-wrap {
|
|
width: 200px;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
}
|
|
.pd-card .r-2d6 {
|
|
position: absolute;
|
|
left: 1px;
|
|
width: 35px;
|
|
text-align: right;
|
|
font-weight: 700;
|
|
}
|
|
.pd-card .r-text {
|
|
position: absolute;
|
|
left: 36px;
|
|
right: 65px;
|
|
padding-left: 4px;
|
|
text-align: left;
|
|
}
|
|
.pd-card .r-d20 {
|
|
position: absolute;
|
|
right: 0;
|
|
width: 65px;
|
|
text-align: right;
|
|
padding-right: 4px;
|
|
font-weight: 700;
|
|
}
|
|
.pd-card .r-row {
|
|
position: absolute;
|
|
width: 100%;
|
|
left: 0;
|
|
}
|
|
|
|
/* CORNER ACCENTS (T4) */
|
|
.pd-card .corner-accent {
|
|
position: absolute;
|
|
z-index: 6;
|
|
pointer-events: none;
|
|
}
|
|
.pd-card .corner-accent.tl {
|
|
top: 0; left: 0;
|
|
border-top: 3px solid;
|
|
border-left: 3px solid;
|
|
}
|
|
.pd-card .corner-accent.tr {
|
|
top: 0; right: 0;
|
|
border-top: 3px solid;
|
|
border-right: 3px solid;
|
|
}
|
|
.pd-card .corner-accent.bl {
|
|
bottom: 0; left: 0;
|
|
border-bottom: 3px solid;
|
|
border-left: 3px solid;
|
|
}
|
|
.pd-card .corner-accent.br {
|
|
bottom: 0; right: 0;
|
|
border-bottom: 3px solid;
|
|
border-right: 3px solid;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
T3 GOLD REFRACTOR ANIMATION — "Gold Shimmer Sweep"
|
|
|
|
One subtle animation: a narrow gold light sweeps left-to-right
|
|
across the card header, simulating a refractor catching light.
|
|
Only the header is animated — body and bars remain static.
|
|
Duration: 2.5s loop. Low opacity (0.35) for elegance.
|
|
═══════════════════════════════════════════════════════ */
|
|
|
|
/* Header must clip the shimmer pseudo-element as it exits the edges */
|
|
.pd-card.anim-on.tier-t3 .card-header,
|
|
.pd-card.anim-on.tier-t4 .card-header {
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* T3: single gold shimmer stripe, translateX driven animation */
|
|
@keyframes t3-shimmer {
|
|
0% { transform: translateX(-130%); }
|
|
100% { transform: translateX(230%); }
|
|
}
|
|
|
|
.pd-card.anim-on.tier-t3 .card-header::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
/* Narrow diagonal gold stripe — subtle gleam, not a flashlight */
|
|
background: linear-gradient(
|
|
105deg,
|
|
transparent 38%,
|
|
rgba(255,240,140,0.18) 44%,
|
|
rgba(255,220, 80,0.38) 50%,
|
|
rgba(255,200, 60,0.30) 53%,
|
|
rgba(255,240,140,0.14) 58%,
|
|
transparent 64%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 5;
|
|
animation: t3-shimmer 2.5s ease-in-out infinite;
|
|
will-change: transform;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
T4 SUPERFRACTOR ANIMATION — "Prismatic Shimmer + Glow Pulse"
|
|
|
|
Four layered effects that stack to create an unmistakably
|
|
premium look. T4 should feel qualitatively different from T3:
|
|
where T3 has a single gentle effect, T4 has a rich layered system.
|
|
|
|
Layer 1 — Prismatic header sweep (card-header ::after)
|
|
Layer 2+3 — Gold/Teal dual glow pulse (pd-card ::before)
|
|
Layer 4 — Column bar shimmer (col-header ::before)
|
|
═══════════════════════════════════════════════════════ */
|
|
|
|
/* ── Layer 1: Prismatic rainbow — always visible, wraparound ──
|
|
Two identical rainbow bands in a 200%-wide element. As one band
|
|
exits the right edge, the next is already entering from the left.
|
|
Translating by exactly 50% (one band width) creates a seamless loop. */
|
|
@keyframes t4-prismatic-sweep {
|
|
0% { transform: translateX(0%); }
|
|
100% { transform: translateX(-50%); }
|
|
}
|
|
|
|
.pd-card.anim-on.tier-t4 .card-header::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 200%;
|
|
height: 100%;
|
|
/* Two rainbow bands side by side — each occupies 50% of the element.
|
|
When we translate -50%, band B takes band A's position = seamless loop. */
|
|
background: linear-gradient(
|
|
135deg,
|
|
transparent 2%,
|
|
rgba(255,100,100,0.28) 8%,
|
|
rgba(255,200, 50,0.32) 14%,
|
|
rgba(100,255,150,0.30) 20%,
|
|
rgba( 50,190,255,0.32) 26%,
|
|
rgba(140, 80,255,0.28) 32%,
|
|
rgba(255,100,180,0.24) 38%,
|
|
transparent 44%,
|
|
transparent 52%,
|
|
rgba(255,100,100,0.28) 58%,
|
|
rgba(255,200, 50,0.32) 64%,
|
|
rgba(100,255,150,0.30) 70%,
|
|
rgba( 50,190,255,0.32) 76%,
|
|
rgba(140, 80,255,0.28) 82%,
|
|
rgba(255,100,180,0.24) 88%,
|
|
transparent 94%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
animation: t4-prismatic-sweep 6s linear infinite;
|
|
will-change: transform;
|
|
}
|
|
/* Lift header children above the rainbow overlay */
|
|
.pd-card.anim-on.tier-t4 .card-header > * {
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
/* ── Layer 2+3: Gold/Teal dual glow pulse ──
|
|
A single keyframe encodes both gold (bright→dim) and teal (dim→bright)
|
|
in perfect opposition. When gold fades, teal brightens — a shifting
|
|
dual-tone breathing effect that suggests the card is alive.
|
|
Applied via ::before on the card itself (inset box-shadow overlay).
|
|
This is independent of the inline box-shadow on the .pd-card element. */
|
|
@keyframes t4-dual-pulse {
|
|
0% {
|
|
box-shadow:
|
|
inset 0 0 45px 12px rgba(201,169, 78,0.40),
|
|
inset 0 0 80px 5px rgba( 45,212,191,0.08);
|
|
}
|
|
50% {
|
|
box-shadow:
|
|
inset 0 0 45px 12px rgba(201,169, 78,0.08),
|
|
inset 0 0 80px 5px rgba( 45,212,191,0.38);
|
|
}
|
|
100% {
|
|
box-shadow:
|
|
inset 0 0 45px 12px rgba(201,169, 78,0.40),
|
|
inset 0 0 80px 5px rgba( 45,212,191,0.08);
|
|
}
|
|
}
|
|
|
|
/* .pd-card already has position:relative; overflow:hidden — ::before slots in cleanly */
|
|
.pd-card.anim-on.tier-t4::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 4;
|
|
animation: t4-dual-pulse 2s ease-in-out infinite;
|
|
will-change: box-shadow;
|
|
}
|
|
|
|
/* ── Layer 4: Column bar shimmer ──
|
|
A fast traveling white highlight passes across each column bar.
|
|
Staggered animation-delay per bar creates a ripple effect across
|
|
all 6 columns. Duration 1.6s — noticeably faster than the header sweep. */
|
|
@keyframes t4-bar-shimmer {
|
|
0% { transform: translateX(-120%); opacity: 1; }
|
|
80% { transform: translateX(200%); opacity: 1; }
|
|
100% { transform: translateX(200%); opacity: 0; }
|
|
}
|
|
|
|
/* Enable positioning on col-headers so ::before can be absolute */
|
|
.pd-card.anim-on.tier-t4 .col-header {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.pd-card.anim-on.tier-t4 .col-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 45%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
90deg,
|
|
transparent,
|
|
rgba(255,255,255,0.28),
|
|
transparent
|
|
);
|
|
pointer-events: none;
|
|
animation: t4-bar-shimmer 1.6s ease-in-out infinite;
|
|
will-change: transform;
|
|
}
|
|
|
|
/* Stagger each of the 6 columns so highlights ripple left-to-right */
|
|
.pd-card.anim-on.tier-t4 .col-header-left-group .col-header:nth-child(1)::before { animation-delay: 0.00s; }
|
|
.pd-card.anim-on.tier-t4 .col-header-left-group .col-header:nth-child(2)::before { animation-delay: -0.55s; }
|
|
.pd-card.anim-on.tier-t4 .col-header-left-group .col-header:nth-child(3)::before { animation-delay: -1.10s; }
|
|
.pd-card.anim-on.tier-t4 .col-header-right-group .col-header:nth-child(1)::before { animation-delay: -0.25s; }
|
|
.pd-card.anim-on.tier-t4 .col-header-right-group .col-header:nth-child(2)::before { animation-delay: -0.80s; }
|
|
.pd-card.anim-on.tier-t4 .col-header-right-group .col-header:nth-child(3)::before { animation-delay: -1.35s; }
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
T4b ALTERNATE — Full-card rainbow overlay
|
|
Same as T4 but the prismatic sweep covers the entire
|
|
card height, not just the header.
|
|
═══════════════════════════════════════════════════════ */
|
|
.pd-card.anim-on.tier-t4b .card-header {
|
|
overflow: visible;
|
|
}
|
|
|
|
/* Full-card rainbow: positioned on .pd-card itself via ::after */
|
|
.pd-card.anim-on.tier-t4b::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 200%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
135deg,
|
|
transparent 2%,
|
|
rgba(255,100,100,0.18) 8%,
|
|
rgba(255,200, 50,0.22) 14%,
|
|
rgba(100,255,150,0.20) 20%,
|
|
rgba( 50,190,255,0.22) 26%,
|
|
rgba(140, 80,255,0.18) 32%,
|
|
rgba(255,100,180,0.16) 38%,
|
|
transparent 44%,
|
|
transparent 52%,
|
|
rgba(255,100,100,0.18) 58%,
|
|
rgba(255,200, 50,0.22) 64%,
|
|
rgba(100,255,150,0.20) 70%,
|
|
rgba( 50,190,255,0.22) 76%,
|
|
rgba(140, 80,255,0.18) 82%,
|
|
rgba(255,100,180,0.16) 88%,
|
|
transparent 94%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 6;
|
|
animation: t4-prismatic-sweep 6s linear infinite;
|
|
will-change: transform;
|
|
}
|
|
|
|
/* T4b also gets the dual glow pulse (reuse T4's keyframes) */
|
|
.pd-card.anim-on.tier-t4b > .dual-pulse-overlay {
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
pointer-events: none;
|
|
z-index: 4;
|
|
animation: t4-dual-pulse 2.8s ease-in-out infinite;
|
|
}
|
|
|
|
/* T4b column bar shimmer (reuse T4's bar effect) */
|
|
.pd-card.anim-on.tier-t4b .col-header {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.pd-card.anim-on.tier-t4b .col-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: -60%; width: 40%; height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
|
|
pointer-events: none;
|
|
animation: t4-bar-shimmer 1.6s ease-in-out infinite;
|
|
}
|
|
.pd-card.anim-on.tier-t4b .col-header-left-group .col-header:nth-child(1)::before { animation-delay: 0.00s; }
|
|
.pd-card.anim-on.tier-t4b .col-header-left-group .col-header:nth-child(2)::before { animation-delay: -0.55s; }
|
|
.pd-card.anim-on.tier-t4b .col-header-left-group .col-header:nth-child(3)::before { animation-delay: -1.10s; }
|
|
.pd-card.anim-on.tier-t4b .col-header-right-group .col-header:nth-child(1)::before { animation-delay: -0.25s; }
|
|
.pd-card.anim-on.tier-t4b .col-header-right-group .col-header:nth-child(2)::before { animation-delay: -0.80s; }
|
|
.pd-card.anim-on.tier-t4b .col-header-right-group .col-header:nth-child(3)::before { animation-delay: -1.35s; }
|
|
|
|
/* ═══════════════════════════════════════════════════════
|
|
TIER DIAMOND INDICATOR — 4-quadrant fill at x=600, y=95
|
|
A square rotated 45deg, divided into a 2x2 grid.
|
|
Fill order: 1st(right) → 2nd(top) → 3rd(left) → home(bottom)
|
|
T0=none, T1=1st, T2=1st+2nd, T3=1st+2nd+3rd, T4=all 4
|
|
═══════════════════════════════════════════════════════ */
|
|
.tier-diamond {
|
|
position: absolute;
|
|
left: 600px;
|
|
top: 78.5px;
|
|
transform: translate(-50%, -50%) rotate(45deg);
|
|
display: grid;
|
|
grid-template: 1fr 1fr / 1fr 1fr;
|
|
gap: 2px;
|
|
z-index: 20;
|
|
pointer-events: none;
|
|
/* Dark gap color gives the cross visible definition */
|
|
background: rgba(0,0,0,0.75);
|
|
border-radius: 2px;
|
|
box-shadow:
|
|
0 0 0 1.5px rgba(0,0,0,0.7),
|
|
0 2px 5px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
.diamond-quad {
|
|
background: rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.diamond-quad.filled {
|
|
box-shadow:
|
|
inset 0 1px 2px rgba(255,255,255,0.45),
|
|
inset 0 -1px 2px rgba(0,0,0,0.35),
|
|
inset 1px 0 2px rgba(255,255,255,0.15);
|
|
}
|
|
|
|
/* Diamond glow pulse animation — applied to whole container for T4 */
|
|
@keyframes diamond-glow-pulse {
|
|
0% { box-shadow:
|
|
0 0 0 1.5px rgba(0,0,0,0.7),
|
|
0 2px 5px rgba(0,0,0,0.5),
|
|
0 0 8px 2px var(--diamond-glow-color, rgba(201,169,78,0.6));
|
|
}
|
|
50% { box-shadow:
|
|
0 0 0 1.5px rgba(0,0,0,0.5),
|
|
0 2px 4px rgba(0,0,0,0.3),
|
|
0 0 14px 5px var(--diamond-glow-color, rgba(201,169,78,0.8)),
|
|
0 0 24px 8px var(--diamond-glow-color, rgba(201,169,78,0.3));
|
|
}
|
|
100% { box-shadow:
|
|
0 0 0 1.5px rgba(0,0,0,0.7),
|
|
0 2px 5px rgba(0,0,0,0.5),
|
|
0 0 8px 2px var(--diamond-glow-color, rgba(201,169,78,0.6));
|
|
}
|
|
}
|
|
|
|
.tier-diamond.diamond-glow {
|
|
animation: diamond-glow-pulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
/* Metallic sheen — boosted inset highlights on filled quads */
|
|
.diamond-quad.metallic.filled {
|
|
box-shadow:
|
|
inset 0 1px 3px rgba(255,255,255,0.7),
|
|
inset 0 -1px 2px rgba(0,0,0,0.5),
|
|
inset 1px 0 3px rgba(255,255,255,0.3),
|
|
inset -1px 0 2px rgba(0,0,0,0.2);
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- PAGE HEADER -->
|
|
<div class="page-header">
|
|
<h1>Refractor Tier Visual Design</h1>
|
|
<div class="subtitle">Paper Dynasty — T0 through T4 — Internal border progression, tier gem markers, sacred blue/salmon body</div>
|
|
</div>
|
|
|
|
<!-- ANIMATION TOGGLE -->
|
|
<div class="anim-toggle-bar">
|
|
<button id="animToggleBtn" class="anim-toggle-btn active">Preview Animation: ON</button>
|
|
<span class="anim-toggle-hint">Applies to T3 and T4 only — T0, T1, T2 always static</span>
|
|
</div>
|
|
|
|
<!-- COMPARISON ROW: all 5 tiers -->
|
|
<div class="comparison-section">
|
|
<div class="section-label">T0 Standard → T4 Superfractor — Click any card for full-size view</div>
|
|
<div class="comparison-row" id="compRow"></div>
|
|
|
|
<!-- Animation info panel — visible when preview is ON -->
|
|
<div id="animInfoPanel" class="anim-info-panel">
|
|
<strong>APNG Animation Info</strong>
|
|
<ul>
|
|
<li><span class="tier-badge t3">T3</span> and <span class="tier-badge t4">T4</span> cards render as APNG (Animated PNG) — auto-plays in Discord embeds and all browsers</li>
|
|
<li>Animation is achieved frame-by-frame: <span class="tier-badge t3">T3</span> uses 8 frames (~4.5s render time), <span class="tier-badge t4">T4</span> uses 12 frames (~6s render time)</li>
|
|
<li>T0–T2 remain static PNG — no animation overhead</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- FULL-SIZE DETAIL + CONTROLS -->
|
|
<div class="detail-section">
|
|
<div class="detail-card-area">
|
|
<div class="detail-heading">Full-size — <span id="detailTierName">T0 Standard</span></div>
|
|
<div class="detail-card-wrap" id="detailCard"></div>
|
|
</div>
|
|
|
|
<div id="controls">
|
|
<h2>Tier Controls</h2>
|
|
<div class="panel-subtitle">Adjust the selected tier</div>
|
|
|
|
<div class="preset-row" id="presetRow"></div>
|
|
|
|
<!-- Internal Borders -->
|
|
<div class="ctrl-group">
|
|
<h3>Internal Borders</h3>
|
|
<div class="ctrl-row">
|
|
<label>Color preset</label>
|
|
<select id="ctrlBorderPreset">
|
|
<option value="black">Black (standard)</option>
|
|
<option value="silver">Silver / chrome</option>
|
|
<option value="blue-silver">Blue-silver / prismatic</option>
|
|
<option value="gold">Gold</option>
|
|
<option value="gold-bold">Gold bold (T4)</option>
|
|
</select>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Header bottom width</label>
|
|
<input type="range" id="ctrlHeaderBorderW" min="2" max="8" step="1" value="3">
|
|
<span class="val" id="valHeaderBorderW">3px</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Section divider width</label>
|
|
<input type="range" id="ctrlDividerW" min="3" max="7" step="1" value="3">
|
|
<span class="val" id="valDividerW">3px</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inset Glow -->
|
|
<div class="ctrl-group">
|
|
<h3>Inset Glow (depth)</h3>
|
|
<div class="ctrl-row">
|
|
<label>Color</label>
|
|
<input type="color" id="ctrlGlowColor" value="#000000">
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Blur</label>
|
|
<input type="range" id="ctrlGlowBlur" min="0" max="40" step="1" value="0">
|
|
<span class="val" id="valGlowBlur">0px</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Spread</label>
|
|
<input type="range" id="ctrlGlowSpread" min="0" max="15" step="1" value="0">
|
|
<span class="val" id="valGlowSpread">0px</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Opacity</label>
|
|
<input type="range" id="ctrlGlowOpacity" min="0" max="100" step="5" value="0">
|
|
<span class="val" id="valGlowOpacity">0%</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Dual glow (T4)</label>
|
|
<select id="ctrlDualGlow">
|
|
<option value="off">Off</option>
|
|
<option value="on">On (gold + teal)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Header -->
|
|
<div class="ctrl-group">
|
|
<h3>Header Background</h3>
|
|
<div class="ctrl-row">
|
|
<label>Type</label>
|
|
<select id="ctrlHeaderType">
|
|
<option value="solid">Solid white</option>
|
|
<option value="silver">Silver chrome</option>
|
|
<option value="iridescent">Iridescent / prismatic</option>
|
|
<option value="gold">Gold wash</option>
|
|
<option value="gold-teal">Gold + teal (T4)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Column headers -->
|
|
<div class="ctrl-group">
|
|
<h3>Column Header Gradients</h3>
|
|
<div class="ctrl-row">
|
|
<label>Left side</label>
|
|
<select id="ctrlColLeft">
|
|
<option value="blue">Blue (standard)</option>
|
|
<option value="blue-purple">Blue-purple (refractor)</option>
|
|
<option value="gold">Gold</option>
|
|
</select>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Right side</label>
|
|
<select id="ctrlColRight">
|
|
<option value="red">Red (standard)</option>
|
|
<option value="red-magenta">Red-magenta (refractor)</option>
|
|
<option value="gold">Gold</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Corner accents -->
|
|
<div class="ctrl-group">
|
|
<h3>Corner Accents (T4)</h3>
|
|
<div class="ctrl-row">
|
|
<label>Enabled</label>
|
|
<select id="ctrlCornerEnabled">
|
|
<option value="off">Off</option>
|
|
<option value="on">On</option>
|
|
</select>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Color</label>
|
|
<input type="color" id="ctrlCornerColor" value="#C9A94E">
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Size</label>
|
|
<input type="range" id="ctrlCornerSize" min="15" max="60" step="5" value="30">
|
|
<span class="val" id="valCornerSize">30px</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Thickness</label>
|
|
<input type="range" id="ctrlCornerThick" min="1" max="6" step="1" value="3">
|
|
<span class="val" id="valCornerThick">3px</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tier Diamond -->
|
|
<div class="ctrl-group">
|
|
<h3>Tier Diamond</h3>
|
|
<div class="ctrl-row">
|
|
<label>Quadrants filled</label>
|
|
<select id="ctrlDiamondFill">
|
|
<option value="0">0 (none)</option>
|
|
<option value="1">1 (right)</option>
|
|
<option value="2">2 (right + top)</option>
|
|
<option value="3">3 (right + top + left)</option>
|
|
<option value="4">4 (full diamond)</option>
|
|
</select>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Body color</label>
|
|
<input type="color" id="ctrlDiamondColor" value="#a82020">
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Highlight color</label>
|
|
<input type="color" id="ctrlDiamondHighlight" value="#e85050">
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Size</label>
|
|
<input type="range" id="ctrlDiamondSize" min="16" max="40" step="1" value="26">
|
|
<span class="val" id="valDiamondSize">26px</span>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Glow</label>
|
|
<select id="ctrlDiamondGlow">
|
|
<option value="off">Off</option>
|
|
<option value="on">On (animated pulse)</option>
|
|
</select>
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Glow color</label>
|
|
<input type="color" id="ctrlDiamondGlowColor" value="#c9a94e">
|
|
</div>
|
|
<div class="ctrl-row">
|
|
<label>Effect</label>
|
|
<select id="ctrlDiamondEffect">
|
|
<option value="none">None</option>
|
|
<option value="bloom">Bloom glow</option>
|
|
<option value="metallic">Metallic sheen</option>
|
|
<option value="border">Border accent</option>
|
|
<option value="shadow">Shadow depth</option>
|
|
<option value="escalation">Tiered escalation</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="export-area">
|
|
<button class="export-btn" id="exportBtn">Export CSS for Production</button>
|
|
<textarea id="cssOutput" readonly></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
/* ─────────────────────────────────────────────────────────
|
|
CARD DATA — Mike Trout, batter
|
|
───────────────────────────────────────────────────────── */
|
|
const CARD = {
|
|
hand: 'R',
|
|
name: 'Mike Trout',
|
|
position: 'CF / RF / DH',
|
|
rarity: 'ALL-STAR',
|
|
stealing: 'Stl: 5-6/4-5 (A-B)',
|
|
running: 'Run: A',
|
|
bunting: 'Bnt: D',
|
|
hitAndRun: 'H&R: B',
|
|
cardset: '2005 Live',
|
|
columns: [
|
|
// Col 1 vs L
|
|
[
|
|
{ d6: '2', text: 'HOMERUN', d20: '1-8' },
|
|
{ d6: '3', text: 'DOUBLE', d20: '1-12' },
|
|
{ d6: '4', text: 'SINGLE', d20: '1-16' },
|
|
{ d6: '5', text: 'SINGLE', d20: '1-14' },
|
|
{ d6: '6', text: 'DOUBLE', d20: '1-10' },
|
|
{ d6: '7', text: 'HOMERUN', d20: '1-6' },
|
|
{ d6: '8', text: 'TRIPLE', d20: '1-4' },
|
|
{ d6: '9', text: 'SINGLE', d20: '1-12' },
|
|
{ d6: '10', text: 'WALK', d20: '1-10' },
|
|
{ d6: '11', text: 'DOUBLE', d20: '1-8' },
|
|
{ d6: '12', text: 'HOMERUN', d20: '1-4' },
|
|
],
|
|
// Col 2 vs L
|
|
[
|
|
{ d6: '2', text: 'SINGLE(bp)', d20: '1-4' },
|
|
{ d6: '3', text: 'SINGLE', d20: '1-14' },
|
|
{ d6: '4', text: 'WALK', d20: '1-11' },
|
|
{ d6: '5', text: 'WALK', d20: '12-20'},
|
|
{ d6: '6', text: 'SINGLE', d20: '1-18' },
|
|
{ d6: '7', text: 'HBP', d20: '1-3' },
|
|
{ d6: '8', text: 'STRIKEOUT', d20: '1-16' },
|
|
{ d6: '9', text: 'FLYOUT(a)', d20: '1-8' },
|
|
{ d6: '10', text: 'GROUNDOUT(a)', d20: '1-10' },
|
|
{ d6: '11', text: 'STRIKEOUT', d20: '1-14' },
|
|
{ d6: '12', text: 'FLYOUT(bq)', d20: '1-12' },
|
|
],
|
|
// Col 3 vs L
|
|
[
|
|
{ d6: '2', text: 'STRIKEOUT', d20: '1-20' },
|
|
{ d6: '3', text: 'FLYOUT(a)', d20: '1-9' },
|
|
{ d6: '4', text: 'GROUNDOUT(a)', d20: '1-8' },
|
|
{ d6: '5', text: 'GROUNDOUT(b)', d20: '1-12' },
|
|
{ d6: '6', text: 'FLYOUT(bq)', d20: '1-10' },
|
|
{ d6: '7', text: 'GROUNDOUT(c)', d20: '1-14' },
|
|
{ d6: '8', text: 'FLYOUT(lf)', d20: '1-8' },
|
|
{ d6: '9', text: 'FLYOUT(rf)', d20: '1-8' },
|
|
{ d6: '10', text: 'LINEOUT', d20: '1-6' },
|
|
{ d6: '11', text: 'POPOUT', d20: '1-20' },
|
|
{ d6: '12', text: 'STRIKEOUT', d20: '1-20' },
|
|
],
|
|
// Col 4 vs R
|
|
[
|
|
{ d6: '2', text: 'HOMERUN', d20: '1-5' },
|
|
{ d6: '3', text: 'SINGLE', d20: '1-14' },
|
|
{ d6: '4', text: 'DOUBLE', d20: '1-10' },
|
|
{ d6: '5', text: 'SINGLE', d20: '1-16' },
|
|
{ d6: '6', text: 'WALK', d20: '1-8' },
|
|
{ d6: '7', text: 'HOMERUN', d20: '1-4' },
|
|
{ d6: '8', text: 'SINGLE', d20: '1-12' },
|
|
{ d6: '9', text: 'DOUBLE', d20: '1-6' },
|
|
{ d6: '10', text: 'WALK', d20: '1-8' },
|
|
{ d6: '11', text: 'SINGLE', d20: '1-10' },
|
|
{ d6: '12', text: 'HOMERUN', d20: '1-3' },
|
|
],
|
|
// Col 5 vs R
|
|
[
|
|
{ d6: '2', text: 'SINGLE(bp)', d20: '1-6' },
|
|
{ d6: '3', text: 'SINGLE', d20: '1-12' },
|
|
{ d6: '4', text: 'WALK', d20: '1-8' },
|
|
{ d6: '5', text: 'HBP', d20: '1-4' },
|
|
{ d6: '6', text: 'SINGLE', d20: '1-14' },
|
|
{ d6: '7', text: 'STRIKEOUT', d20: '1-14' },
|
|
{ d6: '8', text: 'GROUNDOUT(a)', d20: '1-12' },
|
|
{ d6: '9', text: 'FLYOUT(a)', d20: '1-10' },
|
|
{ d6: '10', text: 'STRIKEOUT', d20: '1-16' },
|
|
{ d6: '11', text: 'GROUNDOUT(b)', d20: '1-14' },
|
|
{ d6: '12', text: 'FLYOUT(bq)', d20: '1-10' },
|
|
],
|
|
// Col 6 vs R
|
|
[
|
|
{ d6: '2', text: 'STRIKEOUT', d20: '1-20' },
|
|
{ d6: '3', text: 'FLYOUT(a)', d20: '1-10' },
|
|
{ d6: '4', text: 'GROUNDOUT(a)', d20: '1-10' },
|
|
{ d6: '5', text: 'GROUNDOUT(b)', d20: '1-14' },
|
|
{ d6: '6', text: 'FLYOUT(cf)', d20: '1-12' },
|
|
{ d6: '7', text: 'LINEOUT', d20: '1-8' },
|
|
{ d6: '8', text: 'POPOUT', d20: '1-10' },
|
|
{ d6: '9', text: 'FLYOUT(rf)', d20: '1-10' },
|
|
{ d6: '10', text: 'GROUNDOUT(c)', d20: '1-12' },
|
|
{ d6: '11', text: 'STRIKEOUT', d20: '1-18' },
|
|
{ d6: '12', text: 'STRIKEOUT', d20: '1-20' },
|
|
],
|
|
],
|
|
};
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
COLUMN GRADIENT PRESETS
|
|
───────────────────────────────────────────────────────── */
|
|
const COL_GRADIENTS = {
|
|
'blue': 'linear-gradient(to right, rgba(0,156,224,1), rgba(0,156,224,0.5), rgba(0,156,224,1))',
|
|
'blue-purple': 'linear-gradient(to right, rgba(60,110,200,1), rgba(100,55,185,0.55), rgba(60,110,200,1))',
|
|
'gold': 'linear-gradient(to right, rgba(195,160,40,1), rgba(220,185,60,0.55), rgba(195,160,40,1))',
|
|
'red': 'linear-gradient(to right, rgba(211,49,21,1), rgba(211,49,21,0.5), rgba(211,49,21,1))',
|
|
'red-magenta': 'linear-gradient(to right, rgba(190,35,80,1), rgba(165,25,100,0.55), rgba(190,35,80,1))',
|
|
};
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
HEADER BACKGROUND PRESETS
|
|
───────────────────────────────────────────────────────── */
|
|
const HEADER_BGS = {
|
|
'solid': '#ffffff',
|
|
'silver': 'linear-gradient(135deg, rgba(185,195,210,0.25) 0%, rgba(210,218,228,0.35) 50%, rgba(185,195,210,0.25) 100%), #ffffff',
|
|
'iridescent': 'linear-gradient(135deg, rgba(100,155,230,0.28) 0%, rgba(155,90,220,0.18) 25%, rgba(90,200,210,0.24) 50%, rgba(185,80,170,0.16) 75%, rgba(100,155,230,0.28) 100%), #ffffff',
|
|
'gold': 'linear-gradient(135deg, rgba(195,155,35,0.26) 0%, rgba(235,200,70,0.2) 50%, rgba(195,155,35,0.26) 100%), #ffffff',
|
|
'gold-teal': 'linear-gradient(135deg, rgba(195,155,35,0.28) 0%, rgba(235,200,70,0.2) 35%, rgba(38,198,175,0.22) 65%, rgba(195,155,35,0.26) 100%), #ffffff',
|
|
};
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
INTERNAL BORDER PRESETS
|
|
color: the actual CSS color value used on the border properties
|
|
───────────────────────────────────────────────────────── */
|
|
const BORDER_COLORS = {
|
|
'black': 'black',
|
|
'silver': '#8e9baf',
|
|
'blue-silver': '#7a9cc4',
|
|
'gold': '#c9a94e',
|
|
'gold-bold': '#c9a94e',
|
|
};
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
5-TIER PRESETS (T0 through T4)
|
|
borderPreset: key into BORDER_COLORS
|
|
headerBorderW: header bottom border width (px)
|
|
dividerW: result-header bottom + left/right dividers + sub-col dividers
|
|
───────────────────────────────────────────────────────── */
|
|
const TIER_PRESETS = {
|
|
t0: {
|
|
name: 'T0 Standard',
|
|
label: 'T0',
|
|
sublabel: 'Standard',
|
|
addedDesc: 'Base card — black borders, standard columns',
|
|
borderPreset: 'black',
|
|
headerBorderW: 3,
|
|
dividerW: 3,
|
|
glowColor: '#000000',
|
|
glowBlur: 0,
|
|
glowSpread: 0,
|
|
glowOpacity: 0,
|
|
dualGlow: 'off',
|
|
headerType: 'solid',
|
|
colLeft: 'blue',
|
|
colRight: 'red',
|
|
cornerEnabled: 'off',
|
|
cornerColor: '#C9A94E',
|
|
cornerSize: 30,
|
|
cornerThick: 3,
|
|
diamondFill: 0,
|
|
diamondColor: '#000000',
|
|
diamondHighlight: '#000000',
|
|
diamondSize: 19,
|
|
diamondGlow: 'off',
|
|
diamondGlowColor: '#000000',
|
|
diamondEffect: 'none',
|
|
},
|
|
t1: {
|
|
name: 'T1 Base Chrome',
|
|
label: 'T1',
|
|
sublabel: 'Base Chrome',
|
|
addedDesc: 'Added: silver header, silver borders, diamond 1 quadrant (right)',
|
|
borderPreset: 'silver',
|
|
headerBorderW: 4,
|
|
dividerW: 3,
|
|
glowColor: '#000000',
|
|
glowBlur: 0,
|
|
glowSpread: 0,
|
|
glowOpacity: 0,
|
|
dualGlow: 'off',
|
|
headerType: 'silver',
|
|
colLeft: 'blue',
|
|
colRight: 'red',
|
|
cornerEnabled: 'off',
|
|
cornerColor: '#8e9baf',
|
|
cornerSize: 30,
|
|
cornerThick: 3,
|
|
diamondFill: 1,
|
|
diamondColor: '#1a6b1a',
|
|
diamondHighlight: '#40b040',
|
|
diamondSize: 19,
|
|
diamondGlow: 'off',
|
|
diamondGlowColor: '#1a6b1a',
|
|
diamondEffect: 'none',
|
|
},
|
|
t2: {
|
|
name: 'T2 Refractor',
|
|
label: 'T2',
|
|
sublabel: 'Refractor',
|
|
addedDesc: 'Added: prismatic header, shifted bars, inset glow, diamond 2 quadrants (right + top)',
|
|
borderPreset: 'blue-silver',
|
|
headerBorderW: 4,
|
|
dividerW: 4,
|
|
glowColor: '#5a8fcf',
|
|
glowBlur: 14,
|
|
glowSpread: 3,
|
|
glowOpacity: 22,
|
|
dualGlow: 'off',
|
|
headerType: 'iridescent',
|
|
colLeft: 'blue-purple',
|
|
colRight: 'red-magenta',
|
|
cornerEnabled: 'off',
|
|
cornerColor: '#7a9cc4',
|
|
cornerSize: 30,
|
|
cornerThick: 3,
|
|
diamondFill: 2,
|
|
diamondColor: '#2070b0',
|
|
diamondHighlight: '#50a0e8',
|
|
diamondSize: 19,
|
|
diamondGlow: 'off',
|
|
diamondGlowColor: '#2070b0',
|
|
diamondEffect: 'none',
|
|
},
|
|
t3: {
|
|
name: 'T3 Gold Refractor',
|
|
label: 'T3',
|
|
sublabel: 'Gold Refractor',
|
|
addedDesc: 'Added: gold header, gold borders, gold bars, gold glow, diamond 3 quadrants (right + top + left)',
|
|
borderPreset: 'gold',
|
|
headerBorderW: 4,
|
|
dividerW: 4,
|
|
glowColor: '#c8a530',
|
|
glowBlur: 16,
|
|
glowSpread: 4,
|
|
glowOpacity: 22,
|
|
dualGlow: 'off',
|
|
headerType: 'gold',
|
|
colLeft: 'gold',
|
|
colRight: 'gold',
|
|
cornerEnabled: 'off',
|
|
cornerColor: '#c9a94e',
|
|
cornerSize: 30,
|
|
cornerThick: 3,
|
|
diamondFill: 3,
|
|
diamondColor: '#a82020',
|
|
diamondHighlight: '#e85050',
|
|
diamondSize: 19,
|
|
diamondGlow: 'off',
|
|
diamondGlowColor: '#a82020',
|
|
diamondEffect: 'none',
|
|
},
|
|
t4: {
|
|
name: 'T4 Superfractor',
|
|
label: 'T4',
|
|
sublabel: 'Superfractor',
|
|
addedDesc: 'Added: bold gold borders, dual glow, teal accent, corners, full diamond (all 4 quadrants, glowing)',
|
|
borderPreset: 'gold-bold',
|
|
headerBorderW: 6,
|
|
dividerW: 5,
|
|
glowColor: '#2dd4bf',
|
|
glowBlur: 22,
|
|
glowSpread: 6,
|
|
glowOpacity: 28,
|
|
dualGlow: 'on',
|
|
headerType: 'solid',
|
|
colLeft: 'gold',
|
|
colRight: 'gold',
|
|
cornerEnabled: 'on',
|
|
cornerColor: '#c9a94e',
|
|
cornerSize: 35,
|
|
cornerThick: 3,
|
|
diamondFill: 4,
|
|
diamondColor: '#6b2d8e',
|
|
diamondHighlight: '#a060d0',
|
|
diamondSize: 19,
|
|
diamondGlow: 'on',
|
|
diamondGlowColor: '#6b2d8e',
|
|
diamondEffect: 'none',
|
|
},
|
|
t4b: {
|
|
name: 'T4b Superfractor (full-card rainbow)',
|
|
label: 'T4b',
|
|
sublabel: 'SF Alt',
|
|
addedDesc: 'Alt: prismatic rainbow covers the entire card, full diamond (all 4 quadrants, glowing)',
|
|
borderPreset: 'gold-bold',
|
|
headerBorderW: 6,
|
|
dividerW: 5,
|
|
glowColor: '#2dd4bf',
|
|
glowBlur: 22,
|
|
glowSpread: 6,
|
|
glowOpacity: 28,
|
|
dualGlow: 'on',
|
|
headerType: 'gold-teal',
|
|
colLeft: 'gold',
|
|
colRight: 'gold',
|
|
cornerEnabled: 'on',
|
|
cornerColor: '#c9a94e',
|
|
cornerSize: 35,
|
|
cornerThick: 3,
|
|
diamondFill: 4,
|
|
diamondColor: '#6b2d8e',
|
|
diamondHighlight: '#a060d0',
|
|
diamondSize: 19,
|
|
diamondGlow: 'on',
|
|
diamondGlowColor: '#6b2d8e',
|
|
diamondEffect: 'none',
|
|
},
|
|
};
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
STATE
|
|
───────────────────────────────────────────────────────── */
|
|
let activeTier = 't0';
|
|
let animPreviewOn = true; // animation toggle — applies to T3 and T4 only
|
|
|
|
const tierState = {};
|
|
for (const [k, v] of Object.entries(TIER_PRESETS)) {
|
|
tierState[k] = { ...v };
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
RENDER A SINGLE CARD
|
|
All border styling injected inline — no outer card border.
|
|
Internal borders (header-bottom, section dividers, sub-col dividers)
|
|
change color and width per tier.
|
|
T3/T4 receive additional CSS classes for animation when preview is ON:
|
|
.tier-t3 / .tier-t4 — always present for T3/T4
|
|
.anim-on — added when animPreviewOn is true
|
|
───────────────────────────────────────────────────────── */
|
|
function renderCardHTML(tierKey) {
|
|
const t = tierState[tierKey];
|
|
|
|
// Resolve border color
|
|
const borderColor = BORDER_COLORS[t.borderPreset] || 'black';
|
|
const hw = t.headerBorderW; // header bottom border width
|
|
const dw = t.dividerW; // section divider width
|
|
const thin = Math.max(dw - 1, 2); // sub-col thin dividers (slightly thinner)
|
|
|
|
// Inset glow
|
|
const glowAlpha = t.glowOpacity / 100;
|
|
let shadowCSS = 'none';
|
|
if (t.glowBlur > 0 && glowAlpha > 0) {
|
|
const gc = hexToRGBA(t.glowColor, glowAlpha);
|
|
if (t.dualGlow === 'on') {
|
|
const gc2 = hexToRGBA('#c8a530', 0.15);
|
|
shadowCSS = `inset 0 0 ${t.glowBlur}px ${t.glowSpread}px ${gc}, inset 0 0 ${t.glowBlur * 1.8}px ${Math.round(t.glowSpread * 1.5)}px ${gc2}`;
|
|
} else {
|
|
shadowCSS = `inset 0 0 ${t.glowBlur}px ${t.glowSpread}px ${gc}`;
|
|
}
|
|
}
|
|
|
|
// Header background
|
|
const headerBG = HEADER_BGS[t.headerType] || '#ffffff';
|
|
|
|
// Column bar gradients
|
|
const blueGrad = COL_GRADIENTS[t.colLeft] || COL_GRADIENTS['blue'];
|
|
const redGrad = COL_GRADIENTS[t.colRight] || COL_GRADIENTS['red'];
|
|
|
|
// Corner accents
|
|
let cornerHTML = '';
|
|
if (t.cornerEnabled === 'on') {
|
|
const cs = t.cornerSize + 'px';
|
|
const cc = t.cornerColor;
|
|
const cw = t.cornerThick + 'px';
|
|
cornerHTML = `
|
|
<div class="corner-accent tl" style="width:${cs};height:${cs};border-color:${cc};border-top-width:${cw};border-left-width:${cw};"></div>
|
|
<div class="corner-accent tr" style="width:${cs};height:${cs};border-color:${cc};border-top-width:${cw};border-right-width:${cw};"></div>
|
|
<div class="corner-accent bl" style="width:${cs};height:${cs};border-color:${cc};border-bottom-width:${cw};border-left-width:${cw};"></div>
|
|
<div class="corner-accent br" style="width:${cs};height:${cs};border-color:${cc};border-bottom-width:${cw};border-right-width:${cw};"></div>`;
|
|
}
|
|
|
|
// Tier diamond indicator — 4-quadrant fill (baseball base path)
|
|
// Grid reading order: top-left, top-right, bottom-left, bottom-right
|
|
// After rotate(45deg): top-left=LEFT(3rd), top-right=BOTTOM(home), bottom-left=TOP(2nd), bottom-right=RIGHT(1st)
|
|
// Fill order: 1st(right) → 2nd(top) → 3rd(left) → home(bottom)
|
|
let diamondHTML = '';
|
|
if (t.diamondFill > 0) {
|
|
const ds = t.diamondSize;
|
|
const effect = t.diamondEffect || 'none';
|
|
|
|
// Standard gradient for filled quads; metallic uses a different one
|
|
let diamondBG = `linear-gradient(135deg, ${t.diamondHighlight} 0%, ${t.diamondColor} 50%, ${darkenHex(t.diamondColor, 0.75)} 100%)`;
|
|
if (effect === 'metallic') {
|
|
diamondBG = `linear-gradient(135deg, rgba(255,255,255,0.9) 0%, ${t.diamondHighlight} 20%, ${t.diamondColor} 50%, ${darkenHex(t.diamondColor, 0.6)} 80%, ${t.diamondHighlight} 100%)`;
|
|
}
|
|
|
|
const glowClass = t.diamondGlow === 'on' ? ' diamond-glow' : '';
|
|
const glowVar = t.diamondGlow === 'on' ? ` --diamond-glow-color: ${t.diamondGlowColor};` : '';
|
|
|
|
// Build container inline style — effects stack on top of the base box-shadow
|
|
let containerStyle = `width:${ds}px;height:${ds}px;${glowVar}`;
|
|
if (effect === 'bloom') {
|
|
containerStyle += `box-shadow: 0 0 0 1.5px rgba(0,0,0,0.7), 0 0 8px 3px ${hexToRGBA(t.diamondColor, 0.5)}, 0 0 16px 6px ${hexToRGBA(t.diamondColor, 0.25)};`;
|
|
} else if (effect === 'border') {
|
|
containerStyle += `border: 1.5px solid ${t.diamondHighlight}; box-shadow: 0 0 0 1px rgba(0,0,0,0.5), 0 0 4px 1px ${hexToRGBA(t.diamondHighlight, 0.3)};`;
|
|
} else if (effect === 'shadow') {
|
|
containerStyle += `box-shadow: 0 0 0 1.5px rgba(0,0,0,0.7), 0 3px 8px 2px ${hexToRGBA(t.diamondColor, 0.45)}, 0 1px 3px rgba(0,0,0,0.6);`;
|
|
} else if (effect === 'escalation') {
|
|
containerStyle += `border: 1.5px solid ${t.diamondHighlight};`;
|
|
const intensity = t.diamondFill / 4; // 0.25 → 1.0
|
|
const bloomSize = Math.round(4 + intensity * 12);
|
|
const bloomSpread = Math.round(1 + intensity * 5);
|
|
containerStyle += `box-shadow: 0 0 0 1px rgba(0,0,0,0.5), 0 0 ${bloomSize}px ${bloomSpread}px ${hexToRGBA(t.diamondColor, 0.2 + intensity * 0.3)};`;
|
|
}
|
|
// metallic has no container-level effect — it only changes gradient + quad shadows
|
|
|
|
// Extra CSS class for metallic quads
|
|
const metallicClass = effect === 'metallic' ? ' metallic' : '';
|
|
|
|
// Empirically verified grid-to-visual mapping after rotate(45deg):
|
|
// div 1 (grid top-left) → LEFT = 3rd base
|
|
// div 2 (grid top-right) → BOTTOM = home plate
|
|
// div 3 (grid bottom-left) → TOP = 2nd base
|
|
// Verified mapping: div1→TOP, div2→RIGHT, div3→LEFT, div4→BOTTOM
|
|
const firstFilled = t.diamondFill >= 1; // 1st base / right = div 2
|
|
const secFilled = t.diamondFill >= 2; // 2nd base / top = div 1
|
|
const thirdFilled = t.diamondFill >= 3; // 3rd base / left = div 3
|
|
const homeFilled = t.diamondFill >= 4; // home plate / bottom = div 4
|
|
diamondHTML =
|
|
`<div class="tier-diamond${glowClass}" style="${containerStyle}">` +
|
|
`<div class="diamond-quad${secFilled ? ' filled' + metallicClass : ''}" style="${secFilled ? 'background:' + diamondBG : ''}"></div>` +
|
|
`<div class="diamond-quad${firstFilled ? ' filled' + metallicClass : ''}" style="${firstFilled ? 'background:' + diamondBG : ''}"></div>` +
|
|
`<div class="diamond-quad${thirdFilled ? ' filled' + metallicClass : ''}" style="${thirdFilled ? 'background:' + diamondBG : ''}"></div>` +
|
|
`<div class="diamond-quad${homeFilled ? ' filled' + metallicClass : ''}" style="${homeFilled ? 'background:' + diamondBG : ''}"></div>` +
|
|
`</div>`;
|
|
}
|
|
|
|
// Result rows
|
|
function renderCol(colData) {
|
|
const rowH = 505 / colData.length;
|
|
return colData.map((row, i) => {
|
|
const top = Math.round(i * rowH);
|
|
return `<div class="r-row" style="top:${top}px;height:${Math.ceil(rowH)}px;">` +
|
|
`<span class="r-2d6">${row.d6}</span>` +
|
|
`<span class="r-text">${row.text}</span>` +
|
|
`<span class="r-d20">${row.d20}</span>` +
|
|
`</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
// Header vertical line: same color as border
|
|
const headerVlineStyle = `border-left: 3px solid ${borderColor};`;
|
|
|
|
// Column header sub-divider style
|
|
const thinDivStyle = `border-right: ${thin}px solid ${borderColor};`;
|
|
|
|
// Half divider (between left 600 and right 600) in both header and body groups
|
|
const halfDivStyle = `border-right: ${dw + 2}px solid ${borderColor};`;
|
|
|
|
// Animation classes: added to .pd-card for T3 and T4 tiers.
|
|
// All CSS animation rules use .pd-card.anim-on.tier-t3 / .tier-t4 selectors,
|
|
// so animations only activate when both classes are present.
|
|
let animClasses = '';
|
|
if (tierKey === 't3' || tierKey === 't4' || tierKey === 't4b') {
|
|
animClasses = ` tier-${tierKey}`;
|
|
if (animPreviewOn) {
|
|
animClasses += ' anim-on';
|
|
}
|
|
}
|
|
|
|
return `<div class="pd-card${animClasses}" style="box-shadow:${shadowCSS};">
|
|
${cornerHTML}
|
|
${diamondHTML}
|
|
<div class="card-header" style="background:${headerBG}; border-bottom:${hw}px solid ${borderColor};">
|
|
<div class="header-left">
|
|
<div class="hand-indicator">${CARD.hand}</div>
|
|
<div class="header-vline" style="${headerVlineStyle}"></div>
|
|
<div class="player-info">
|
|
<div class="player-name">${CARD.name}</div>
|
|
<div class="player-position">${CARD.position}</div>
|
|
</div>
|
|
</div>
|
|
<div class="header-middle">
|
|
<div class="rarity-badge">${CARD.rarity}</div>
|
|
</div>
|
|
<div class="header-right">
|
|
<div class="stat-stealing">${CARD.stealing}</div>
|
|
<div class="stat-running">${CARD.running}</div>
|
|
<div class="stat-bunting">${CARD.bunting}</div>
|
|
<div class="stat-hitrun">${CARD.hitAndRun}</div>
|
|
<div class="stat-cardset">${CARD.cardset}</div>
|
|
</div>
|
|
</div>
|
|
<div class="result-header" style="border-bottom:${dw}px solid ${borderColor};">
|
|
<div class="col-header-left-group" style="${halfDivStyle}">
|
|
<div class="col-header" style="background-image:${blueGrad}; ${thinDivStyle}">1</div>
|
|
<div class="col-header" style="background-image:${blueGrad}; ${thinDivStyle}">2</div>
|
|
<div class="col-header" style="background-image:${blueGrad};">3</div>
|
|
</div>
|
|
<div class="col-header-right-group">
|
|
<div class="col-header" style="background-image:${redGrad}; ${thinDivStyle}">1</div>
|
|
<div class="col-header" style="background-image:${redGrad}; ${thinDivStyle}">2</div>
|
|
<div class="col-header" style="background-image:${redGrad};">3</div>
|
|
</div>
|
|
</div>
|
|
<div class="result-area">
|
|
<div class="result-left-group" style="background-color:#ACE6FF; ${halfDivStyle}">
|
|
<div class="result-col-wrap" style="${thinDivStyle}">${renderCol(CARD.columns[0])}</div>
|
|
<div class="result-col-wrap" style="${thinDivStyle}">${renderCol(CARD.columns[1])}</div>
|
|
<div class="result-col-wrap">${renderCol(CARD.columns[2])}</div>
|
|
</div>
|
|
<div class="result-right-group" style="background-color:#EAA49C;">
|
|
<div class="result-col-wrap" style="${thinDivStyle}">${renderCol(CARD.columns[3])}</div>
|
|
<div class="result-col-wrap" style="${thinDivStyle}">${renderCol(CARD.columns[4])}</div>
|
|
<div class="result-col-wrap">${renderCol(CARD.columns[5])}</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
COLOR UTILS
|
|
───────────────────────────────────────────────────────── */
|
|
function hexToRGBA(hex, alpha) {
|
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
const b = parseInt(hex.slice(5, 7), 16);
|
|
return `rgba(${r},${g},${b},${alpha.toFixed(2)})`;
|
|
}
|
|
|
|
function darkenHex(hex, factor) {
|
|
const r = Math.round(parseInt(hex.slice(1, 3), 16) * factor);
|
|
const g = Math.round(parseInt(hex.slice(3, 5), 16) * factor);
|
|
const b = Math.round(parseInt(hex.slice(5, 7), 16) * factor);
|
|
return `rgb(${r},${g},${b})`;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
BUILD COMPARISON ROW — all 5 tiers in one row
|
|
T4 gets a larger thumbnail (featured)
|
|
───────────────────────────────────────────────────────── */
|
|
function buildComparisonRow() {
|
|
const tierKeys = ['t0', 't1', 't2', 't3', 't4', 't4b'];
|
|
|
|
function buildSlot(tierKey) {
|
|
const t = tierState[tierKey];
|
|
const isFeatured = tierKey === 't4' || tierKey === 't4b';
|
|
const slot = document.createElement('div');
|
|
slot.className = 'comparison-slot ' + (isFeatured ? 'featured-thumb' : 'standard-thumb') + (tierKey === activeTier ? ' active' : '');
|
|
slot.dataset.tier = tierKey;
|
|
slot.innerHTML = `
|
|
<div class="tier-label">${t.label} — ${t.sublabel}</div>
|
|
<div class="card-thumb-wrap">
|
|
<div class="card-scale-container">${renderCardHTML(tierKey)}</div>
|
|
</div>
|
|
<div class="tier-added">${t.addedDesc}</div>`;
|
|
slot.addEventListener('click', () => selectTier(tierKey));
|
|
return slot;
|
|
}
|
|
|
|
const row = document.getElementById('compRow');
|
|
row.innerHTML = '';
|
|
tierKeys.forEach(k => row.appendChild(buildSlot(k)));
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
DETAIL CARD
|
|
───────────────────────────────────────────────────────── */
|
|
function renderDetailCard() {
|
|
document.getElementById('detailCard').innerHTML = renderCardHTML(activeTier);
|
|
document.getElementById('detailTierName').textContent = tierState[activeTier].name;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
PRESET BUTTONS
|
|
───────────────────────────────────────────────────────── */
|
|
function buildPresetButtons() {
|
|
const row = document.getElementById('presetRow');
|
|
row.innerHTML = '';
|
|
const tiers = ['t0', 't1', 't2', 't3', 't4', 't4b'];
|
|
tiers.forEach(tk => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'preset-btn' + (tk === activeTier ? ' active' : '');
|
|
btn.textContent = TIER_PRESETS[tk].label;
|
|
btn.addEventListener('click', () => selectTier(tk));
|
|
row.appendChild(btn);
|
|
});
|
|
const resetBtn = document.createElement('button');
|
|
resetBtn.className = 'preset-btn';
|
|
resetBtn.textContent = 'Reset';
|
|
resetBtn.style.marginLeft = 'auto';
|
|
resetBtn.addEventListener('click', () => {
|
|
tierState[activeTier] = { ...TIER_PRESETS[activeTier] };
|
|
loadControlsFromState();
|
|
refreshAll();
|
|
});
|
|
row.appendChild(resetBtn);
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
SELECT TIER
|
|
───────────────────────────────────────────────────────── */
|
|
function selectTier(tierKey) {
|
|
activeTier = tierKey;
|
|
loadControlsFromState();
|
|
refreshAll();
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
LOAD CONTROLS FROM STATE
|
|
───────────────────────────────────────────────────────── */
|
|
function loadControlsFromState() {
|
|
const t = tierState[activeTier];
|
|
document.getElementById('ctrlBorderPreset').value = t.borderPreset;
|
|
document.getElementById('ctrlHeaderBorderW').value = t.headerBorderW;
|
|
document.getElementById('ctrlDividerW').value = t.dividerW;
|
|
document.getElementById('ctrlGlowColor').value = t.glowColor;
|
|
document.getElementById('ctrlGlowBlur').value = t.glowBlur;
|
|
document.getElementById('ctrlGlowSpread').value = t.glowSpread;
|
|
document.getElementById('ctrlGlowOpacity').value = t.glowOpacity;
|
|
document.getElementById('ctrlDualGlow').value = t.dualGlow;
|
|
document.getElementById('ctrlHeaderType').value = t.headerType;
|
|
document.getElementById('ctrlColLeft').value = t.colLeft;
|
|
document.getElementById('ctrlColRight').value = t.colRight;
|
|
document.getElementById('ctrlCornerEnabled').value = t.cornerEnabled;
|
|
document.getElementById('ctrlCornerColor').value = t.cornerColor;
|
|
document.getElementById('ctrlCornerSize').value = t.cornerSize;
|
|
document.getElementById('ctrlCornerThick').value = t.cornerThick;
|
|
document.getElementById('ctrlDiamondFill').value = t.diamondFill;
|
|
document.getElementById('ctrlDiamondColor').value = t.diamondColor;
|
|
document.getElementById('ctrlDiamondHighlight').value = t.diamondHighlight;
|
|
document.getElementById('ctrlDiamondSize').value = t.diamondSize;
|
|
document.getElementById('ctrlDiamondGlow').value = t.diamondGlow;
|
|
document.getElementById('ctrlDiamondGlowColor').value = t.diamondGlowColor;
|
|
document.getElementById('ctrlDiamondEffect').value = t.diamondEffect || 'none';
|
|
updateValueLabels();
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
SAVE CONTROLS TO STATE
|
|
───────────────────────────────────────────────────────── */
|
|
function saveControlsToState() {
|
|
const t = tierState[activeTier];
|
|
t.borderPreset = document.getElementById('ctrlBorderPreset').value;
|
|
t.headerBorderW = parseInt(document.getElementById('ctrlHeaderBorderW').value);
|
|
t.dividerW = parseInt(document.getElementById('ctrlDividerW').value);
|
|
t.glowColor = document.getElementById('ctrlGlowColor').value;
|
|
t.glowBlur = parseInt(document.getElementById('ctrlGlowBlur').value);
|
|
t.glowSpread = parseInt(document.getElementById('ctrlGlowSpread').value);
|
|
t.glowOpacity = parseInt(document.getElementById('ctrlGlowOpacity').value);
|
|
t.dualGlow = document.getElementById('ctrlDualGlow').value;
|
|
t.headerType = document.getElementById('ctrlHeaderType').value;
|
|
t.colLeft = document.getElementById('ctrlColLeft').value;
|
|
t.colRight = document.getElementById('ctrlColRight').value;
|
|
t.cornerEnabled = document.getElementById('ctrlCornerEnabled').value;
|
|
t.cornerColor = document.getElementById('ctrlCornerColor').value;
|
|
t.cornerSize = parseInt(document.getElementById('ctrlCornerSize').value);
|
|
t.cornerThick = parseInt(document.getElementById('ctrlCornerThick').value);
|
|
t.diamondFill = parseInt(document.getElementById('ctrlDiamondFill').value);
|
|
t.diamondColor = document.getElementById('ctrlDiamondColor').value;
|
|
t.diamondHighlight = document.getElementById('ctrlDiamondHighlight').value;
|
|
t.diamondSize = parseInt(document.getElementById('ctrlDiamondSize').value);
|
|
t.diamondGlow = document.getElementById('ctrlDiamondGlow').value;
|
|
t.diamondGlowColor = document.getElementById('ctrlDiamondGlowColor').value;
|
|
t.diamondEffect = document.getElementById('ctrlDiamondEffect').value;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
VALUE LABELS
|
|
───────────────────────────────────────────────────────── */
|
|
function updateValueLabels() {
|
|
document.getElementById('valHeaderBorderW').textContent = document.getElementById('ctrlHeaderBorderW').value + 'px';
|
|
document.getElementById('valDividerW').textContent = document.getElementById('ctrlDividerW').value + 'px';
|
|
document.getElementById('valGlowBlur').textContent = document.getElementById('ctrlGlowBlur').value + 'px';
|
|
document.getElementById('valGlowSpread').textContent = document.getElementById('ctrlGlowSpread').value + 'px';
|
|
document.getElementById('valGlowOpacity').textContent = document.getElementById('ctrlGlowOpacity').value + '%';
|
|
document.getElementById('valCornerSize').textContent = document.getElementById('ctrlCornerSize').value + 'px';
|
|
document.getElementById('valCornerThick').textContent = document.getElementById('ctrlCornerThick').value + 'px';
|
|
document.getElementById('valDiamondSize').textContent = document.getElementById('ctrlDiamondSize').value + 'px';
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
REFRESH ALL
|
|
───────────────────────────────────────────────────────── */
|
|
function refreshAll() {
|
|
buildComparisonRow();
|
|
renderDetailCard();
|
|
buildPresetButtons();
|
|
updateValueLabels();
|
|
document.getElementById('cssOutput').classList.remove('visible');
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
CSS EXPORT
|
|
Produces the CSS overrides needed to implement this tier
|
|
in the production card HTML template.
|
|
|
|
For T3 and T4, outputs TWO sections:
|
|
Section 1 — Static CSS: frame 0 appearance / fallback for non-animated
|
|
Section 2 — Animation CSS: --anim-progress approach for Playwright APNG
|
|
frame-by-frame capture. All animated values derive from this
|
|
single CSS custom property (set by JS in the capture loop).
|
|
───────────────────────────────────────────────────────── */
|
|
function exportCSS() {
|
|
const t = tierState[activeTier];
|
|
const cls = `.refractor-${activeTier}`;
|
|
const borderColor = BORDER_COLORS[t.borderPreset] || 'black';
|
|
const hw = t.headerBorderW;
|
|
const dw = t.dividerW;
|
|
const thin = Math.max(dw - 1, 2);
|
|
const headerBG = HEADER_BGS[t.headerType] || '#ffffff';
|
|
const blueGrad = COL_GRADIENTS[t.colLeft];
|
|
const redGrad = COL_GRADIENTS[t.colRight];
|
|
|
|
const glowAlpha = t.glowOpacity / 100;
|
|
let shadowRule = '/* no glow */';
|
|
if (t.glowBlur > 0 && glowAlpha > 0) {
|
|
const gc = hexToRGBA(t.glowColor, glowAlpha);
|
|
if (t.dualGlow === 'on') {
|
|
const gc2 = hexToRGBA('#c8a530', 0.15);
|
|
shadowRule = `box-shadow: inset 0 0 ${t.glowBlur}px ${t.glowSpread}px ${gc}, inset 0 0 ${Math.round(t.glowBlur * 1.8)}px ${Math.round(t.glowSpread * 1.5)}px ${gc2};`;
|
|
} else {
|
|
shadowRule = `box-shadow: inset 0 0 ${t.glowBlur}px ${t.glowSpread}px ${gc};`;
|
|
}
|
|
}
|
|
|
|
// ── Section 1: Static CSS (identical for all tiers) ───────
|
|
const staticCSS = `/* ══════════════════════════════════════
|
|
Paper Dynasty — ${t.name}
|
|
${t.addedDesc}
|
|
══════════════════════════════════════ */
|
|
|
|
/* Card inset glow (no outer border — dimensions unchanged) */
|
|
${cls} #fullCard {
|
|
${shadowRule}
|
|
}
|
|
|
|
/* Header background + bottom border */
|
|
${cls} #header {
|
|
background: ${headerBG};
|
|
border-bottom: ${hw}px solid ${borderColor};
|
|
}
|
|
|
|
/* Header vertical divider */
|
|
${cls} #header .header-vline {
|
|
border-left-color: ${borderColor};
|
|
}
|
|
|
|
/* Result header bottom border */
|
|
${cls} #resultHeader {
|
|
border-bottom: ${dw}px solid ${borderColor};
|
|
}
|
|
|
|
/* Left/right half divider */
|
|
${cls} .col-header-left-group,
|
|
${cls} .result-left-group {
|
|
border-right: ${dw + 2}px solid ${borderColor};
|
|
}
|
|
|
|
/* Sub-column dividers */
|
|
${cls} .col-header.border-r-thin,
|
|
${cls} .result-col-wrap.border-r-thin {
|
|
border-right: ${thin}px solid ${borderColor};
|
|
}
|
|
|
|
/* Left column headers */
|
|
${cls} .blue-gradient {
|
|
background-image: ${blueGrad};
|
|
}
|
|
|
|
/* Right column headers */
|
|
${cls} .red-gradient {
|
|
background-image: ${redGrad};
|
|
}` + (t.diamondFill > 0 ? `
|
|
|
|
/* Tier diamond indicator — 4-quadrant fill at x=600, y=95 */
|
|
${cls} .tier-diamond {
|
|
position: absolute;
|
|
left: 600px;
|
|
top: 78.5px;
|
|
width: ${t.diamondSize}px;
|
|
height: ${t.diamondSize}px;
|
|
transform: translate(-50%, -50%) rotate(45deg);
|
|
display: grid;
|
|
grid-template: 1fr 1fr / 1fr 1fr;
|
|
gap: 2px;
|
|
z-index: 20;
|
|
pointer-events: none;
|
|
background: rgba(0,0,0,0.75);
|
|
border-radius: 2px;
|
|
box-shadow:
|
|
0 0 0 1.5px rgba(0,0,0,0.7),
|
|
0 2px 5px rgba(0,0,0,0.5);
|
|
}
|
|
${cls} .diamond-quad {
|
|
background: rgba(0,0,0,0.3);
|
|
}
|
|
${cls} .diamond-quad.filled {
|
|
background: ${(t.diamondEffect === 'metallic')
|
|
? `linear-gradient(135deg, rgba(255,255,255,0.9) 0%, ${t.diamondHighlight} 20%, ${t.diamondColor} 50%, ${darkenHex(t.diamondColor, 0.6)} 80%, ${t.diamondHighlight} 100%)`
|
|
: `linear-gradient(135deg, ${t.diamondHighlight} 0%, ${t.diamondColor} 50%, ${darkenHex(t.diamondColor, 0.75)} 100%)`};
|
|
box-shadow:
|
|
${(t.diamondEffect === 'metallic')
|
|
? `inset 0 1px 3px rgba(255,255,255,0.7),
|
|
inset 0 -1px 2px rgba(0,0,0,0.5),
|
|
inset 1px 0 3px rgba(255,255,255,0.3),
|
|
inset -1px 0 2px rgba(0,0,0,0.2)`
|
|
: `inset 0 1px 2px rgba(255,255,255,0.45),
|
|
inset 0 -1px 2px rgba(0,0,0,0.35),
|
|
inset 1px 0 2px rgba(255,255,255,0.15)`};
|
|
}` + (() => {
|
|
const eff = t.diamondEffect || 'none';
|
|
let effectCSS = '';
|
|
if (eff === 'bloom') {
|
|
effectCSS = `
|
|
${cls} .tier-diamond {
|
|
box-shadow: 0 0 0 1.5px rgba(0,0,0,0.7), 0 0 8px 3px ${hexToRGBA(t.diamondColor, 0.5)}, 0 0 16px 6px ${hexToRGBA(t.diamondColor, 0.25)};
|
|
}`;
|
|
} else if (eff === 'border') {
|
|
effectCSS = `
|
|
${cls} .tier-diamond {
|
|
border: 1.5px solid ${t.diamondHighlight};
|
|
box-shadow: 0 0 0 1px rgba(0,0,0,0.5), 0 0 4px 1px ${hexToRGBA(t.diamondHighlight, 0.3)};
|
|
}`;
|
|
} else if (eff === 'shadow') {
|
|
effectCSS = `
|
|
${cls} .tier-diamond {
|
|
box-shadow: 0 0 0 1.5px rgba(0,0,0,0.7), 0 3px 8px 2px ${hexToRGBA(t.diamondColor, 0.45)}, 0 1px 3px rgba(0,0,0,0.6);
|
|
}`;
|
|
} else if (eff === 'escalation') {
|
|
const intensity = t.diamondFill / 4;
|
|
const bloomSize = Math.round(4 + intensity * 12);
|
|
const bloomSpread = Math.round(1 + intensity * 5);
|
|
effectCSS = `
|
|
${cls} .tier-diamond {
|
|
border: 1.5px solid ${t.diamondHighlight};
|
|
box-shadow: 0 0 0 1px rgba(0,0,0,0.5), 0 0 ${bloomSize}px ${bloomSpread}px ${hexToRGBA(t.diamondColor, 0.2 + intensity * 0.3)};
|
|
}`;
|
|
}
|
|
return effectCSS;
|
|
})() + (t.diamondGlow === 'on' ? `
|
|
${cls} .tier-diamond.diamond-glow {
|
|
--diamond-glow-color: ${t.diamondGlowColor};
|
|
animation: diamond-glow-pulse 2s ease-in-out infinite;
|
|
}` : '') : '');
|
|
|
|
// ── Section 2: Animation CSS for T3 / T4 ────────────────
|
|
// Uses --anim-progress (0.0 to 1.0) set by Playwright for each frame.
|
|
// All calc() expressions derive visual position/opacity from this value.
|
|
let animCSS = '';
|
|
|
|
if (activeTier === 't3') {
|
|
animCSS = `
|
|
/* ══════════════════════════════════════
|
|
T3 ANIMATION CSS — --anim-progress
|
|
Playwright APNG capture: 8 frames
|
|
Set --anim-progress = frame / 8 for each frame (0.0 → 0.875)
|
|
══════════════════════════════════════ */
|
|
|
|
/* Header must clip the shimmer as it enters and exits */
|
|
${cls} #header {
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
/* Gold shimmer stripe: position driven entirely by --anim-progress.
|
|
At progress=0.0 the stripe is off the left edge.
|
|
At progress=1.0 it has fully exited the right edge. */
|
|
${cls} #header::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background: linear-gradient(
|
|
105deg,
|
|
transparent 0%,
|
|
transparent calc(var(--anim-progress) * 120% - 22%),
|
|
rgba(255,240,140,0.18) calc(var(--anim-progress) * 120% - 14%),
|
|
rgba(255,220, 80,0.38) calc(var(--anim-progress) * 120%),
|
|
rgba(255,200, 60,0.30) calc(var(--anim-progress) * 120% + 3%),
|
|
rgba(255,240,140,0.14) calc(var(--anim-progress) * 120% + 10%),
|
|
transparent calc(var(--anim-progress) * 120% + 22%),
|
|
transparent 100%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 5;
|
|
}
|
|
|
|
/* Playwright capture loop:
|
|
for (let frame = 0; frame < 8; frame++) {
|
|
const progress = frame / 8;
|
|
await page.evaluate(p => {
|
|
document.getElementById('header')
|
|
.style.setProperty('--anim-progress', p);
|
|
}, progress);
|
|
await page.screenshot({ path: \`frame_t3_\${frame}.png\` });
|
|
} */`;
|
|
}
|
|
|
|
if (activeTier === 't4' || activeTier === 't4b') {
|
|
animCSS = `
|
|
/* ══════════════════════════════════════
|
|
T4 ANIMATION CSS — --anim-progress
|
|
Playwright APNG capture: 12 frames
|
|
Set --anim-progress = frame / 12 for each frame (0.0 → 0.917)
|
|
══════════════════════════════════════ */
|
|
|
|
/* ── Layer 1: Prismatic header sweep ── */
|
|
${cls} #header {
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
${cls} #header::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background: linear-gradient(
|
|
105deg,
|
|
transparent 0%,
|
|
transparent calc(var(--anim-progress) * 120% - 30%),
|
|
rgba(255,100,100,0.28) calc(var(--anim-progress) * 120% - 20%),
|
|
rgba(255,200, 50,0.32) calc(var(--anim-progress) * 120% - 12%),
|
|
rgba(100,255,150,0.30) calc(var(--anim-progress) * 120%),
|
|
rgba( 50,190,255,0.32) calc(var(--anim-progress) * 120% + 8%),
|
|
rgba(140, 80,255,0.28) calc(var(--anim-progress) * 120% + 16%),
|
|
rgba(255,100,180,0.24) calc(var(--anim-progress) * 120% + 24%),
|
|
transparent calc(var(--anim-progress) * 120% + 34%),
|
|
transparent 100%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 5;
|
|
}
|
|
|
|
/* ── Layer 2+3: Gold/Teal dual glow pulse ──
|
|
Gold fades from 0.40 → 0.08 over the first half, recovers in second half.
|
|
Teal rises from 0.08 → 0.38 over first half, fades in second half.
|
|
Simple linear approximation using --anim-progress directly. */
|
|
${cls} #fullCard {
|
|
${shadowRule}
|
|
}
|
|
|
|
${cls} #fullCard::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 4;
|
|
box-shadow:
|
|
inset 0 0 45px 12px rgba(201,169, 78,
|
|
calc(0.40 - var(--anim-progress) * 0.64 + var(--anim-progress) * var(--anim-progress) * 0.64)),
|
|
inset 0 0 80px 5px rgba( 45,212,191,
|
|
calc(0.08 + var(--anim-progress) * 0.60 - var(--anim-progress) * var(--anim-progress) * 0.60));
|
|
}
|
|
|
|
/* ── Layer 4: Column bar shimmer ──
|
|
Each column uses --bar-phase to offset its position in the cycle.
|
|
Set --bar-phase on each col-header before capture (or bake into template). */
|
|
${cls} .col-header {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
${cls} .col-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; width: 45%; height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.28), transparent);
|
|
pointer-events: none;
|
|
transform: translateX(calc(((var(--anim-progress) + var(--bar-phase, 0)) * 320%) - 120%));
|
|
}
|
|
|
|
/* Apply per-column phase offsets in the HTML template:
|
|
Col-left 1: style="--bar-phase:0.000"
|
|
Col-left 2: style="--bar-phase:0.167"
|
|
Col-left 3: style="--bar-phase:0.333"
|
|
Col-right 1: style="--bar-phase:0.083"
|
|
Col-right 2: style="--bar-phase:0.250"
|
|
Col-right 3: style="--bar-phase:0.417" */
|
|
|
|
/* Playwright capture loop:
|
|
for (let frame = 0; frame < 12; frame++) {
|
|
const progress = frame / 12;
|
|
await page.evaluate(p => {
|
|
document.getElementById('fullCard')
|
|
.style.setProperty('--anim-progress', p);
|
|
document.getElementById('header')
|
|
.style.setProperty('--anim-progress', p);
|
|
// --bar-phase is baked into each col-header element
|
|
}, progress);
|
|
await page.screenshot({ path: \`frame_t4_\${frame}.png\` });
|
|
} */`;
|
|
}
|
|
|
|
// For T3/T4, wrap in two clearly labeled sections
|
|
const fullOutput = (activeTier === 't3' || activeTier === 't4' || activeTier === 't4b')
|
|
? `/* ═════════════════════════════════════════════════
|
|
SECTION 1 — STATIC CSS (frame 0 / fallback)
|
|
Use for non-animated contexts or as visual baseline.
|
|
═════════════════════════════════════════════════ */
|
|
|
|
${staticCSS}
|
|
|
|
|
|
/* ═════════════════════════════════════════════════
|
|
SECTION 2 — ANIMATION CSS (--anim-progress)
|
|
For Playwright APNG frame-by-frame capture.
|
|
See capture loop comments below.
|
|
═════════════════════════════════════════════════ */
|
|
${animCSS}`
|
|
: staticCSS;
|
|
|
|
const output = document.getElementById('cssOutput');
|
|
output.value = fullOutput;
|
|
output.classList.add('visible');
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
EVENT LISTENERS
|
|
───────────────────────────────────────────────────────── */
|
|
function setupListeners() {
|
|
// Tier control inputs — save state, update preview
|
|
document.querySelectorAll('#controls input, #controls select').forEach(el => {
|
|
const evts = (el.type === 'range' || el.type === 'color') ? ['input'] : ['change'];
|
|
evts.forEach(evt => {
|
|
el.addEventListener(evt, () => {
|
|
saveControlsToState();
|
|
updateValueLabels();
|
|
renderDetailCard();
|
|
// Update only the active thumbnail in the comparison row
|
|
const slot = document.querySelector(`.comparison-slot[data-tier="${activeTier}"]`);
|
|
if (slot) {
|
|
const sc = slot.querySelector('.card-scale-container');
|
|
if (sc) sc.innerHTML = renderCardHTML(activeTier);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// CSS export button
|
|
document.getElementById('exportBtn').addEventListener('click', exportCSS);
|
|
|
|
// Animation toggle — flips animPreviewOn, updates button state and info panel,
|
|
// then re-renders all cards so animation classes are added or removed
|
|
document.getElementById('animToggleBtn').addEventListener('click', () => {
|
|
animPreviewOn = !animPreviewOn;
|
|
const btn = document.getElementById('animToggleBtn');
|
|
btn.textContent = animPreviewOn ? 'Preview Animation: ON' : 'Preview Animation: OFF';
|
|
btn.classList.toggle('active', animPreviewOn);
|
|
document.getElementById('animInfoPanel').style.display = animPreviewOn ? '' : 'none';
|
|
refreshAll();
|
|
});
|
|
}
|
|
|
|
/* ─────────────────────────────────────────────────────────
|
|
INIT
|
|
───────────────────────────────────────────────────────── */
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
buildComparisonRow();
|
|
renderDetailCard();
|
|
buildPresetButtons();
|
|
loadControlsFromState();
|
|
setupListeners();
|
|
// Ensure info panel visibility matches initial animPreviewOn state (true = visible)
|
|
document.getElementById('animInfoPanel').style.display = animPreviewOn ? '' : 'none';
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|