#explorer { width: 100%; height: 100%; display: flex; overflow: hidden; .dim { opacity: 0.5; } @container aside (max-width: 767px) { overflow-y: auto; padding: var(--main-padding) 0; flex-direction: column; } --cube: 4.5rem; /* Iso geometry. --iso-scale is the half-width ratio of the hex silhouette (= cos(30deg) = sqrt(3)/2); the silhouette spans 2·iso-scale wide and 2 tall in cube-face units. The per-face transforms use the HTML-demo math with ox=oy=0, so the outer compensation on `.face` is just translateY(50%). */ --iso-scale: calc(sqrt(3) / 2); --cube-w: calc(var(--cube) * 2 * var(--iso-scale)); --cube-h: calc(var(--cube) * 2); --face-step: 0.033; /* Cube face-color derivations, resolved once at the #explorer level so .cube state changes (hover / selected) don't force Safari to re-evaluate `oklch(from var …)` on every paint — a known source of jank there. Each interaction state gets its own set; the .cube rule below just swaps which set --face-right / --face-left / --face-top / --face-bottom reference. */ --cube-neutral-right: light-dark( oklch(from var(--light-gray) calc(l - var(--face-step) * 2) c h), var(--dark-gray) ); --cube-neutral-left: light-dark( oklch(from var(--light-gray) calc(l - var(--face-step)) c h), oklch(from var(--dark-gray) calc(l + var(--face-step)) c h) ); --cube-neutral-top: light-dark( var(--light-gray), oklch(from var(--dark-gray) calc(l + var(--face-step) * 2) c h) ); --cube-neutral-bottom: oklch( from var(--border-color) calc(l - var(--face-step) * 3) c h ); --cube-hover-right: light-dark( oklch(from var(--dark-gray) calc(l - var(--face-step) * 2) c h), var(--light-gray) ); --cube-hover-left: light-dark( oklch(from var(--dark-gray) calc(l - var(--face-step)) c h), oklch(from var(--light-gray) calc(l + var(--face-step)) c h) ); --cube-hover-top: light-dark( var(--dark-gray), oklch(from var(--light-gray) calc(l + var(--face-step) * 2) c h) ); --cube-hover-bottom: oklch( from var(--inv-border-color) calc(l - var(--face-step) * 3) c h ); --cube-selected-right: light-dark( oklch(from var(--orange) calc(l - var(--face-step) * 2) c h), var(--orange) ); --cube-selected-left: light-dark( oklch(from var(--orange) calc(l - var(--face-step)) c h), oklch(from var(--orange) calc(l + var(--face-step)) c h) ); --cube-selected-top: light-dark( var(--orange), oklch(from var(--orange) calc(l + var(--face-step) * 2) c h) ); --cube-selected-bottom: oklch( from var(--orange) calc(l - var(--face-step) * 3) c h ); > * { padding: 0 var(--main-padding); @container aside (min-width: 768px) { padding: var(--main-padding); } } #chain { flex-shrink: 0; @container aside (max-width: 767px) { overflow-x: auto; padding-bottom: 1rem; } @container aside (min-width: 768px) { height: 100%; overflow-y: auto; padding-right: calc(var(--main-padding) / 2); } .blocks { display: flex; flex-direction: column-reverse; --min-gap: calc(var(--cube) * -1); --max-gap: calc(var(--cube) * 6); --min-dt: 0; --max-dt: 10800; @container aside (max-width: 767px) { --min-gap: 0rem; flex-direction: row-reverse; width: max-content; } @container aside (min-width: 768px) { padding-bottom: 6rem; } } .cube { --t: pow( clamp( 0, (var(--dt, 600) - var(--min-dt)) / (var(--max-dt) - var(--min-dt)), 1 ), 0.7 ); --block-gap: calc( var(--min-gap) + var(--t) * (var(--max-gap) - var(--min-gap)) ); --empty-alpha: 0.4; /* Face colors reference the precomputed sets on #explorer (see top of file). Hover / selected rules just switch which set each --face-* points at. */ --face-right: var(--cube-neutral-right); --face-left: var(--cube-neutral-left); --face-top: var(--cube-neutral-top); --face-bottom: var(--cube-neutral-bottom); /* Fill-driven state. --liquid-y is the liquid's vertical scale; --glass-y the glass-above-liquid's. --is-full / --is-empty are 1 only at the exact endpoints (fill == 1 / fill == 0) and drive opacity so those roles hide cleanly there. */ --liquid-y: calc(var(--iso-scale) * var(--fill)); --glass-y: calc(var(--iso-scale) * (1 - var(--fill))); --is-full: round(down, var(--fill), 1); --is-empty: round(down, calc(1 - var(--fill)), 1); flex-shrink: 0; position: relative; cursor: pointer; width: var(--cube-w); height: var(--cube-h); /* .cube is an ; reset the global anchor styles in elements.css that would clip the iso silhouette (overflow:hidden) and underline the empty link. */ overflow: visible; text-decoration: none; --state-ease: 50ms cubic-bezier(0.4, 0, 0.2, 1); color: var(--color); transition: color var(--state-ease); user-select: none; pointer-events: none; &:hover { color: var(--background-color); --face-right: var(--cube-hover-right); --face-left: var(--cube-hover-left); --face-top: var(--cube-hover-top); --face-bottom: var(--cube-hover-bottom); } &:active, &.selected { color: var(--black); --face-right: var(--cube-selected-right); --face-left: var(--cube-selected-left); --face-top: var(--cube-selected-top); --face-bottom: var(--cube-selected-bottom); } /* Skeleton state (cube painted but data is stale while a new chunk loads): hide text AND the pool logo. Using visibility rather than color:transparent so the raw logo hides too. */ &.skeleton .face-text { visibility: hidden; } /* Shared face-transform template. Each face div sets --orient, --x, --y, --sx, --sy and its role (liquid/glass/face-text) supplies --y-offset. Faces extend outside .cube's layout box — the iso silhouette spans ~2·iso·cube × 2·cube, offset into what would be the next cube's space. Clicks land only on the transformed face rectangles, not the .cube's empty corners. */ .face { position: absolute; transform-origin: 0 0; box-sizing: border-box; width: var(--cube); height: var(--cube); transform: translateY(50%) var(--orient) translate(calc(var(--cube) * var(--x)), calc(var(--cube) * var(--y))) scale(var(--sx, 1), var(--sy)); pointer-events: auto; } /* Roles: .liquid opaque liquid (scales with fill) .glass translucent glass shell .face-text text overlay spanning the full rhombus will-change is on the painted roles only (not .face-text, whose background never changes) so each liquid/glass gets its own compositor layer for snappy hover/select repaints. */ .liquid, .glass { will-change: background-color; transition: background-color var(--state-ease); } .liquid { background: var(--fc); opacity: calc(1 - var(--is-empty)); --sy: var(--liquid-y); --y-offset: var(--glass-y); } .glass { background: oklch(from var(--fc) l c h / var(--empty-alpha)); --sy: var(--glass-y); --y-offset: 0; } .glass.top { opacity: calc(1 - var(--is-full)); } .face-text { --sy: var(--iso-scale); --y-offset: 0; pointer-events: none; padding: 0.1rem; font-family: var(--font-mono); font-size: var(--font-size-xs); font-weight: 450; } .face-text.top, .face-text.right { display: flex; flex-direction: column; align-items: center; text-align: center; } .face-text.top { justify-content: center; text-transform: uppercase; } .face-text.right { justify-content: space-between; } .face-text p { margin: 0; } .face-text .height { font-size: var(--font-size-sm); font-weight: normal; } .face-text .fees { display: flex; flex-direction: column; height: 100%; justify-content: center; align-items: center; } /* Pool line: raw (un-tinted) logo + miner name, ellipsis-clipped. */ .face-text .pool { display: flex; align-items: center; justify-content: center; gap: 0.1em; width: 100%; } .face-text .pool img { width: 1.25em; height: 1.25em; flex-shrink: 0; } .face-text .pool span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; } /* Per-face geometry. 3 orientations × 4 vertical anchors. Each face picks one of each (plus its color/horizontal anchor). */ .top, .bottom { --orient: rotate(30deg) skewX(-30deg); --sy: var(--iso-scale); } .right, .rear-left { --orient: rotate(-30deg) skewX(-30deg); } .left, .rear-right { --orient: rotate(30deg) skewX(30deg); } /* Vertical anchors (cube-face units from the layout origin). --y-offset is the role-specific fill shift (liquid sides get glass-y, everything else 0). */ .top, .rear-right { --y: calc(var(--y-offset) - var(--iso-scale)); } .left, .rear-left { --y: var(--y-offset); } .right { --y: calc(var(--y-offset) + var(--iso-scale)); } .bottom { --y: 0; } /* Per-face color + horizontal anchor. */ .top { --fc: var(--face-top); --x: var(--top-x-shift, 0); } .bottom { --fc: var(--face-bottom); --x: 1; } .right { --fc: var(--face-right); --x: 1; } .left { --fc: var(--face-left); --x: 0; } .rear-right { --fc: var(--face-left); --x: 1; } .rear-left { --fc: var(--face-top); --x: 1; --sx: -1; } .liquid.top { --top-x-shift: calc(1 - var(--fill)); } & + & { margin-bottom: var(--block-gap); &::before { content: ""; position: absolute; top: 100%; left: 50%; width: 1px; height: var(--block-gap); background: var(--border-color); z-index: -1; } @container aside (max-width: 767px) { margin-bottom: 0; margin-right: var(--block-gap); &::before { bottom: auto; left: auto; right: calc(-1 * var(--block-gap)); top: 50%; width: var(--block-gap); height: 1px; } } } } } #block-details, #tx-details, #addr-details { flex: 1; font-size: var(--font-size-sm); line-height: var(--line-height-sm); @container aside (min-width: 768px) { overflow-y: auto; padding-left: calc(var(--main-padding) / 2); } h1 { margin-bottom: 1rem; code { font-size: 1.5rem; font-weight: 300; font-family: Lilex; color: var(--off-color); letter-spacing: -0.05rem; } } .row { display: flex; justify-content: space-between; gap: 1rem; padding: 0.25rem 0; border-bottom: 1px solid var(--border-color); } .label { color: var(--off-color); white-space: nowrap; } .value { text-align: right; word-break: break-all; } .transactions { margin-top: 1rem; .tx-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; h2 { font-size: var(--font-size-sm); line-height: var(--line-height-sm); } } .pagination { display: flex; align-items: center; gap: 0.5rem; button { color: var(--off-color); &:disabled { opacity: 0.25; pointer-events: none; } } } .tx { border: 1px solid var(--border-color); padding: 0.5rem; margin-bottom: 0.5rem; content-visibility: auto; contain-intrinsic-block-size: auto 8rem; .tx-head { display: flex; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.5rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border-color); .txid { font-family: Lilex; font-size: var(--font-size-xs); line-height: var(--line-height-xs); color: var(--off-color); overflow: hidden; text-overflow: ellipsis; } .tx-time { flex-shrink: 0; color: var(--off-color); } } .tx-body { display: flex; gap: 0.5rem; font-size: var(--font-size-xs); line-height: var(--line-height-xs); } .tx-inputs, .tx-outputs { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.125rem; } .tx-outputs { padding-left: 0.5rem; border-left: 1px solid var(--border-color); } .tx-io { display: flex; justify-content: space-between; gap: 0.5rem; .addr { display: flex; min-width: 0; white-space: nowrap; color: var(--off-color); a { display: flex; min-width: 0; } .addr-head { overflow: hidden; text-overflow: ellipsis; } .addr-tail { flex-shrink: 0; } &.coinbase { color: var(--orange); } .coinbase-sig { font-family: Lilex; font-size: var(--font-size-xs); color: var(--off-color); display: block; overflow: hidden; text-overflow: ellipsis; } &.op-return { color: var(--off-color); } } .amount { flex-shrink: 0; text-align: right; } } .show-more { color: var(--off-color); font-size: var(--font-size-xs); padding: 0.25rem 0; } .tx-foot { display: flex; justify-content: space-between; gap: 0.5rem; margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border-color); color: var(--off-color); .total { color: var(--orange); } } } } } }