/* MTG Life Counter — Full CSS - modern player cards with depleting ring - contained die slot with wooden (or black) bowl - no duplicate rules / cleaned media queries */ /* ===== Base / Resets =================================================== */ *, *::before, *::after { box-sizing: border-box; } html, body { height: 100%; } html { min-height: 100%; margin: 0; } body { background: radial-gradient(circle at 50% 40%, #792f22 0%, #4f644f 47%, #6c7f83 100%); margin: 0; color: #333; user-select: none; font-family: 'Lucida Grande','Lucida Sans Unicode','Lucida Sans',Geneva,Verdana,sans-serif; } /* Animatable property for the ring sweep */ @property --life-deg { syntax: ''; inherits: true; initial-value: 360deg; } /* ===== Theme Variables ================================================= */ :root{ /* layout */ --border-radius: 30px; /* dice / animation */ --roll-ms: 1500ms; /* change to slow/fast the roll */ --die-size: 96px; /* change to scale die & bowl together */ /* cards */ --card-border: hsl(0 0% 100% / 0.16); --ring-thickness: 10px; /* bowl (wood theme) */ --wood-h: 30; /* hue 20–40 = warm brown */ --wood-s: 55%; /* saturation 40–70% */ --wood-l: 35%; /* lightness 30–45% */ --wood-sheen: .05; /* tiny linear grain */ --wood-rim: .18; /* rim highlight strength */ } /* ===== Main Layout ===================================================== */ main{ display: flex; flex-wrap: wrap; justify-content: center; align-content: center; width: 100vw; min-height: 100vh; padding-top: 100px; /* space for toolbar */ gap: clamp(18px, 4vw, 44px); /* gap between cards */ } /* ===== Toolbar ========================================================= */ #buttonWrapper{ position: fixed; top: 10px; left: 50%; transform: translateX(-50%); width: min(92%, 760px); display: flex; justify-content: space-evenly; align-items: center; padding: 6px; border-radius: 20px; background: #eee; opacity: .9; box-shadow: 0 4px 8px rgba(0,0,0,.2); z-index: 10; } .icons{ font-size: clamp(18px, 1.6vw, 26px); padding: 10px; color: #697069; cursor: pointer; transition: color .2s, transform .2s; } .icons:hover { color: #333; transform: scale(1.08); } .icons:active { transform: scale(0.94); } /* ===== Dice slot + bowl =============================================== */ /* Dice hitbox – no visual box */ .dice{ position: relative; display: inline-flex; align-items: center; justify-content: center; width: var(--bowl-size); height: var(--bowl-size); background: none !important; border: none !important; box-shadow: none !important; } /* 3D canvas for die (sized by --die-size) */ .dice .content{ position: relative; width: var(--die-size); height: var(--die-size); perspective: calc(var(--die-size) * 15); } /* --- Wooden bowl (default) --- */ .dice .content::before{ content:""; position: absolute; inset: 6px; border-radius: 50%; z-index: 0; background: /* concentric rings */ repeating-conic-gradient( at 50% 52%, hsl(var(--wood-h) var(--wood-s) calc(var(--wood-l) + 7%)) 0deg 5deg, hsl(var(--wood-h) var(--wood-s) calc(var(--wood-l) - 7%)) 5deg 10deg ), /* subtle straight grain */ repeating-linear-gradient( 14deg, rgba(255,255,255,var(--wood-sheen)) 0 2px, rgba(0,0,0,var(--wood-sheen)) 2px 4px ), /* concave shading */ radial-gradient(120% 120% at 50% 42%, rgba(255,255,255,.18) 0%, rgba(255,255,255,.10) 30%, rgba(0,0,0,.22) 70%, rgba(0,0,0,.34) 100%); background-blend-mode: multiply, overlay, normal; box-shadow: inset 0 12px 26px rgba(0,0,0,.45), inset 0 -8px 18px rgba(255,255,255,.08), 0 2px 8px rgba(0,0,0,.28); } /* rim highlight */ .dice .content::after{ content:""; position: absolute; inset: 0; border-radius: 50%; z-index: 0; pointer-events: none; background: radial-gradient(80% 80% at 50% 25%, rgba(255,255,255,var(--wood-rim)) 0%, rgba(255,255,255,0) 60%); box-shadow: 0 0 0 2px hsl(var(--wood-h) var(--wood-s) calc(var(--wood-l) - 4%) / .45), inset 0 0 18px rgba(255,255,255,.08); } /* --- Black bowl variant (add class .bowl--black to .dice) --- */ .content::before{ content:""; position:absolute; inset:6px; / inner lip */ border-radius:50%; z-index:0; background: radial-gradient(circle at 50% 45%, #4a4a4a 0%, #2a2a2a 62%, #111 80%, #000 100%); box-shadow: inset 0 10px 24px rgba(255,255,255,.08), inset 0 -12px 24px rgba(0,0,0,.65), 0 2px 10px rgba(0,0,0,.55); } /* Subtle rim highlight */ .content::after{ content:""; position:absolute; inset:0; border-radius:50%; z-index:0; background: radial-gradient(circle at 50% 30%, rgba(255,255,255,.14) 0%, rgba(0,0,0,0) 60%); box-shadow: 0 0 0 2px rgba(255,255,255,.08), inset 0 0 20px rgba(0,0,0,.6); pointer-events:none; } /* ===== Die (D20) ====================================================== */ /* wrapper that owns the animation */ .roller{ position: absolute; inset: 0; transform-style: preserve-3d; z-index: 1; /* above bowl */ } /* elliptical path that eases back to center */ @keyframes orbit-wobble { 0% { transform: translate3d(0,0,2px) rotateZ(0deg); } 15% { transform: translate3d(16px,10px,-4px) rotateZ(.6deg); } 35% { transform: translate3d(-14px,12px,-2px) rotateZ(-.7deg); } 55% { transform: translate3d(12px,-10px,1px) rotateZ(.5deg); } 75% { transform: translate3d(-6px,4px,0) rotateZ(-.25deg); } 100% { transform: translate3d(0,0,0) rotateZ(0deg); } } /* clean 0→100% spin */ @keyframes roll { 0% { transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg); } 25% { transform: rotateX(180deg) rotateY(360deg) rotateZ(0deg); } 50% { transform: rotateX(360deg) rotateY(720deg) rotateZ(0deg); } 75% { transform: rotateX(540deg) rotateY(1080deg) rotateZ(0deg); } 100% { transform: rotateX(720deg) rotateY(1440deg) rotateZ(0deg); } } /* run both animations together */ .roller.rolling{ animation: orbit-wobble var(--roll-ms) ease-in-out, roll var(--roll-ms) linear; } .die{ position: absolute; inset: 0; transform-style: preserve-3d; transition: transform .5s ease-out; transform-origin: 50% 50% 10px; cursor: pointer; /* FIX: reset a local counter so numbering starts at 1 here */ counter-reset: d20num 0; } .die:not([data-face]) { transform: rotateX(-53deg) rotateY(0deg); } .die[data-face="1"] { transform: rotateX(-53deg) rotateY(0deg); } .die[data-face="2"] { transform: rotateX(-53deg) rotateY(72deg); } .die[data-face="3"] { transform: rotateX(-53deg) rotateY(144deg); } .die[data-face="4"] { transform: rotateX(-53deg) rotateY(216deg); } .die[data-face="5"] { transform: rotateX(-53deg) rotateY(288deg); } .die[data-face="16"] { transform: rotateX(127deg) rotateY(-72deg); } .die[data-face="17"] { transform: rotateX(127deg) rotateY(-144deg); } .die[data-face="18"] { transform: rotateX(127deg) rotateY(-216deg); } .die[data-face="19"] { transform: rotateX(127deg) rotateY(-288deg); } .die[data-face="20"] { transform: rotateX(127deg) rotateY(-360deg); } .die[data-face="6"] { transform: rotateX(11deg) rotateZ(180deg) rotateY(0deg); } .die[data-face="7"] { transform: rotateX(11deg) rotateZ(180deg) rotateY(72deg); } .die[data-face="8"] { transform: rotateX(11deg) rotateZ(180deg) rotateY(144deg); } .die[data-face="9"] { transform: rotateX(11deg) rotateZ(180deg) rotateY(216deg); } .die[data-face="10"] { transform: rotateX(11deg) rotateZ(180deg) rotateY(288deg); } .die[data-face="11"] { transform: rotateX(11deg) rotateY(-252deg); } .die[data-face="12"] { transform: rotateX(11deg) rotateY(-324deg); } .die[data-face="13"] { transform: rotateX(11deg) rotateY(-396deg); } .die[data-face="14"] { transform: rotateX(11deg) rotateY(-468deg); } .die[data-face="15"] { transform: rotateX(11deg) rotateY(-540deg); } .die .face{ position: absolute; left: 50%; top: 0; margin: 0 -12.5px; border-left: 12.5px solid transparent; border-right: 12.5px solid transparent; border-bottom: 21.5px solid rgba(94,134,91,0.9); width: 0; height: 0; transform-style: preserve-3d; backface-visibility: hidden; /* FIX: unique counter to avoid clashes (was steps) */ counter-increment: d20num 1; } .die .face::before{ content: counter(d20num); position: absolute; top: 5.375px; left: -25px; width: 50px; height: 21.5px; color: #fff; text-shadow: 1px 1px 3px #000; font-size: 10.75px; text-align: center; line-height: 19.35px; transform: translateZ(0.1px); /* avoid z-fighting */ } /* Face placement */ .die .face:nth-child(1) { transform: rotateY(0deg) translateZ(8.375px) translateY(-3.225px) rotateX(53deg); } .die .face:nth-child(2) { transform: rotateY(-72deg) translateZ(8.375px) translateY(-3.225px) rotateX(53deg); } .die .face:nth-child(3) { transform: rotateY(-144deg) translateZ(8.375px) translateY(-3.225px) rotateX(53deg); } .die .face:nth-child(4) { transform: rotateY(-216deg) translateZ(8.375px) translateY(-3.225px) rotateX(53deg); } .die .face:nth-child(5) { transform: rotateY(-288deg) translateZ(8.375px) translateY(-3.225px) rotateX(53deg); } .die .face:nth-child(16) { transform: rotateY(-108deg) translateZ(8.375px) translateY(30.315px) rotateZ(180deg) rotateX(53deg); } .die .face:nth-child(17) { transform: rotateY(-36deg) translateZ(8.375px) translateY(30.315px) rotateZ(180deg) rotateX(53deg); } .die .face:nth-child(18) { transform: rotateY(36deg) translateZ(8.375px) translateY(30.315px) rotateZ(180deg) rotateX(53deg); } .die .face:nth-child(19) { transform: rotateY(108deg) translateZ(8.375px) translateY(30.315px) rotateZ(180deg) rotateX(53deg); } .die .face:nth-child(20) { transform: rotateY(180deg) translateZ(8.375px) translateY(30.315px) rotateZ(180deg) rotateX(53deg); } .die .face:nth-child(6) { transform: rotateY(360deg) translateZ(18.75px) translateY(13.545px) rotateZ(180deg) rotateX(-11deg); } .die .face:nth-child(7) { transform: rotateY(288deg) translateZ(18.75px) translateY(13.545px) rotateZ(180deg) rotateX(-11deg); } .die .face:nth-child(8) { transform: rotateY(216deg) translateZ(18.75px) translateY(13.545px) rotateZ(180deg) rotateX(-11deg); } .die .face:nth-child(9) { transform: rotateY(144deg) translateZ(18.75px) translateY(13.545px) rotateZ(180deg) rotateX(-11deg); } .die .face:nth-child(10) { transform: rotateY(72deg) translateZ(18.75px) translateY(13.545px) rotateZ(180deg) rotateX(-11deg); } .die .face:nth-child(11) { transform: rotateY(252deg) translateZ(18.75px) translateY(13.545px) rotateX(-11deg); } .die .face:nth-child(12) { transform: rotateY(324deg) translateZ(18.75px) translateY(13.545px) rotateX(-11deg); } .die .face:nth-child(13) { transform: rotateY(396deg) translateZ(18.75px) translateY(13.545px) rotateX(-11deg); } .die .face:nth-child(14) { transform: rotateY(468deg) translateZ(18.75px) translateY(13.545px) rotateX(-11deg); } .die .face:nth-child(15) { transform: rotateY(540deg) translateZ(18.75px) translateY(13.545px) rotateX(-11deg); } /* Optional micro-alignment of face 1 (tweak if needed) */ :root{ --face1-ry: 0deg; --face1-dy: 32px; --face1-rz: .6deg; } .die .face:nth-child(1){ transform: rotateY(calc(0deg + var(--face1-ry))) translateZ(8.375px) translateY(calc(-3.225px + var(--face1-dy))) rotateX(53deg) rotateZ(var(--face1-rz)); } /* ===== Player Cards ==================================================== */ /* varied accents */ main .player:nth-child(6n+1){ --accent: hsl(200 100% 65%); } main .player:nth-child(6n+2){ --accent: hsl(280 100% 70%); } main .player:nth-child(6n+3){ --accent: hsl(150 95% 54%); } main .player:nth-child(6n+4){ --accent: hsl(20 100% 65%); } main .player:nth-child(6n+5){ --accent: hsl(45 100% 62%); } main .player:nth-child(6n+6){ --accent: hsl(325 100% 68%); } /* 2-player: distinct */ body.is-two-player-mode main .player:nth-child(1){ --accent: #45c5ff; } body.is-two-player-mode main .player:nth-child(2){ --accent: #b86bff; } .player{ position: relative; display: flex; flex-direction: column; gap: 14px; padding: 18px 18px 16px; border-radius: 20px; background: linear-gradient(180deg, hsl(0 0% 100% / .10), hsl(0 0% 100% / .05)) border-box; border: 1px solid var(--card-border); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); box-shadow: 0 10px 30px hsl(0 0% 0% / .25), inset 0 1px 0 hsl(0 0% 100% / .15); transition: transform .18s, box-shadow .18s; max-width: clamp(540px, 46vw, 960px); } .player:hover{ transform: translateY(-2px); box-shadow: 0 16px 40px hsl(0 0% 0% / .33), inset 0 1px 0 hsl(0 0% 100% / .18); } /* accent edge glow */ .player::after{ content:""; position:absolute; inset:-1px; border-radius: 20px; pointer-events:none; background: linear-gradient(135deg, transparent 20%, color-mix(in oklab, var(--accent), white 25%) 50%, transparent 80%); opacity:.25; } /* name pill */ .name{ width: 100%; max-width: 360px; padding: 10px 16px; border-radius: 14px; background: hsl(0 0% 100% / .08); border: 1px solid hsl(0 0% 100% / .18); color: #fff; text-align: center; font-size: clamp(16px, 2.2vw, 20px); outline: none; caret-color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: box-shadow .18s, border-color .18s, background .18s; } .name:focus{ background: hsl(0 0% 100% / .12); border-color: color-mix(in oklab, var(--accent), white 30%); box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent), transparent 80%); } /* life area */ .life{ display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 12px; background: transparent; border: 0; padding: 6px 0; } .vert{ display:flex; flex-direction:column; gap: 8px; align-items:center; } /* ringed number */ .life-count{ position: relative; width: clamp(96px, 14vw, 142px); height: clamp(96px, 14vw, 142px); border-radius: 50%; display: grid; place-items: center; color: #fff; font-weight: 800; text-shadow: 0 1px 0 hsl(0 0% 0% / .35); font-size: clamp(36px, 8vh, 64px); isolation: isolate; transition: --life-deg 280ms linear; } .life-count::before{ content:""; position:absolute; inset:0; border-radius: inherit; z-index:-2; background: conic-gradient(from -90deg, var(--accent) 0 var(--life-deg, calc(var(--life-pct,1)*1turn)), hsl(0 0% 100% / .10) 0 360deg); filter: drop-shadow(0 4px 10px color-mix(in oklab, var(--accent), black 70%)); } .life-count::after{ content:""; position:absolute; inset: var(--ring-thickness); border-radius: inherit; z-index:-1; background: hsl(0 0% 100% / .06); border: 1px solid hsl(0 0% 100% / .12); } /* life buttons */ .life-btn{ width: 44px; height: 44px; border-radius: 50%; display:grid; place-items:center; color:#fff; font-size: 20px; cursor: pointer; background: linear-gradient(180deg, hsl(0 0% 100% / .18), hsl(0 0% 100% / .06)); border: 1px solid hsl(0 0% 100% / .18); box-shadow: 0 6px 16px hsl(0 0% 0% / .25), inset 0 1px 0 hsl(0 0% 100% / .25); transition: transform .12s, box-shadow .12s, background .12s; } .life-btn:hover{ transform: translateY(-1px); box-shadow: 0 10px 20px hsl(0 0% 0% / .35), inset 0 1px 0 hsl(0 0% 100% / .30); } .life-btn:active{ transform: translateY(0) scale(.97); } .life-up-1,.life-up-5{ color: color-mix(in oklab, var(--accent), white 10%); } .life-down-1,.life-down-5{ color: hsl(0 90% 60%); } /* special counters */ .special-counters{ width: 100%; max-width: 520px; padding-top: 10px; background: none; gap: 10px; } .poison-counter, .commander-damage-counter{ display:flex; align-items:center; gap:10px; background: hsl(0 0% 100% / .07); border: 1px solid hsl(0 0% 100% / .14); border-radius: 12px; padding: 8px 10px; color:#fff; font-size: clamp(12px, 1.6vw, 16px); } .commander-name{ flex:1 1 auto; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; opacity:.9; } .commander-controls{ display:inline-flex; align-items:center; gap:6px; } .poison-btn, .commander-btn{ width:28px; height:28px; min-width:28px; min-height:28px; display:grid; place-items:center; font-size:14px; line-height:1; padding:0; border-radius:8px; color:#fff; cursor:pointer; background: hsl(0 0% 100% / .12); border: 1px solid hsl(0 0% 100% / .18); transition: transform .1s, background .1s; } .poison-btn:hover, .commander-btn:hover{ transform: translateY(-1px); } .poison-value, .commander-value{ min-width: 1.8ch; text-align:center; color:#fff; } /* poison: label left, controls dock right */ .poison-counter .counter-label{ margin-right: 8px; } .poison-counter .poison-down{ margin-left: auto; } .poison-counter .poison-value{ min-width:2ch; } /* commander panel show/hide */ body.is-commander-mode .commander-damage { display: block; } .commander-damage { display: none; } /* zero-life style */ .player[data-life-zero="true"] .life-count{ color: hsl(0 100% 70%); filter: drop-shadow(0 0 18px hsl(0 100% 60% / .45)); } .player[data-life-zero="true"] .life-count::before{ background: conic-gradient(hsl(0 100% 62%) 360deg, transparent 0); } /* ===== Responsive ====================================================== */ @media (orientation: landscape) and (max-height: 500px){ .player{ padding: 20px; } .life{ gap: 12px; } .life-count{ width:150px; height:150px; font-size: clamp(44px,5vw,78px); --ring-thickness:12px; } .life-btn{ width:52px; height:52px; font-size:24px; } .name{ font-size: clamp(16px, 2.4vw, 22px); } } @media (min-width: 768px){ .player{ padding: 22px; border-radius: 22px; } .life{ gap: 14px; } .life-count{ width:180px; height:180px; font-size: clamp(52px,4.6vw,86px); --ring-thickness:14px; } .life-btn{ width:58px; height:58px; font-size:26px; } .special-counters{ max-width: 640px; } .poison-counter,.commander-damage-counter{ font-size:16px; } .poison-btn,.commander-btn{ width:32px; height:32px; font-size:16px; } } @media (min-width: 1200px){ .life-count{ width:220px; height:220px; font-size: clamp(64px,3.6vw,96px); --ring-thickness:16px; } .life-btn{ width:64px; height:64px; font-size:30px; } } @media (min-width: 1600px){ .life-count{ width:260px; height:260px; font-size: clamp(72px,3vw,112px); --ring-thickness:18px; } .life-btn{ width:70px; height:70px; font-size:32px; } } /* ===== Infect Mode (secret) ===== */ body.infect-mode { filter: hue-rotate(-25deg) saturate(1.25); } body.infect-mode .life-count::before{ background: conic-gradient( from -90deg, #35ff96 0 var(--life-deg, calc(var(--life-pct, 1)*1turn)), rgba(255,255,255,0.10) 0 360deg ); filter: drop-shadow(0 6px 16px rgba(11, 232, 129, 0.55)); } body.infect-mode .poison-counter{ border-color: rgba(53, 255, 150, 0.35); box-shadow: 0 0 12px rgba(53, 255, 150, 0.25) inset; } body.infect-mode .poison-btn{ background: linear-gradient(180deg, rgba(53,255,150,.22), rgba(53,255,150,.12)); border-color: rgba(53,255,150,.45); } /* Skull hint while holding */ #poison-toggle.egg-hold{ animation: skull-hold 1.2s linear; filter: drop-shadow(0 0 10px rgba(53,255,150,.45)); } @keyframes skull-hold{ 0%{ transform: scale(1); } 90%{ transform: scale(1.12); } 100%{ transform: scale(1.08); } }