SOURCE / PINNED RELEASE
Made of little things.
Pickle Rustlers
- Release
- 039e51f55e34…
- Author-recorded commit
- 40608586a8a1…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-bad44e7ab35
Archive hash verified: f38e99afa0992052…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/*
Pickle Rustlers: "wanted poster" HUD over a sunset diorama.
Colors are app-owned (see docs/brief.md); the host theme never recolors them.
*/
:root {
--paper: #f3e3c3;
--paper-dark: #e2c99a;
--ink: #3b2412;
--rust: #b5541c;
--pickle: #5a9e2f;
--gold: #f2c230;
--onion: #a4508f;
--sauce: #d62828;
--tear: #6ec6ff;
--tank: #c9a96e;
--brass: #c9a227;
--slab: 'Rockwell', 'Rockwell Nova', 'Roboto Slab', 'Clarendon', 'Georgia', serif;
--hud-size: 15px;
--touch-stick: 128px;
--touch-btn: 78px;
color-scheme: light;
}
* {
box-sizing: border-box;
}
html,
body,
#app {
height: 100%;
margin: 0;
}
body {
overflow: hidden;
background: #5b3a74;
color: var(--ink);
font-family: var(--slab);
user-select: none;
-webkit-user-select: none;
-webkit-tap-highlight-color: transparent;
}
.app {
position: relative;
width: 100%;
overflow: hidden;
}
#scene {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
touch-action: none;
}
button {
font: inherit;
color: inherit;
cursor: pointer;
}
button:focus-visible {
outline: 3px solid var(--gold);
outline-offset: 2px;
}
[hidden] {
display: none !important;
}
/* ---------- HUD ---------- */
.hud {
position: absolute;
inset: 0;
pointer-events: none;
font-size: var(--hud-size);
}
.hud-top {
position: absolute;
top: max(8px, env(safe-area-inset-top));
left: max(8px, env(safe-area-inset-left));
right: max(8px, env(safe-area-inset-right));
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px 8px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 9px 4px;
background: var(--paper);
border: 2px solid var(--ink);
border-radius: 3px;
box-shadow: 2px 2px 0 rgba(59, 36, 18, 0.55);
line-height: 1.1;
white-space: nowrap;
}
.badge:nth-child(odd) {
transform: rotate(-1.5deg);
}
.badge:nth-child(even) {
transform: rotate(1deg);
}
.badge b {
font-size: 1.15em;
font-variant-numeric: tabular-nums;
}
.badge .k {
font-size: 0.75em;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--rust);
font-weight: 700;
}
.badge .of {
opacity: 0.6;
}
.icon-pickle {
width: 9px;
height: 18px;
border-radius: 6px;
background: var(--pickle);
border: 1.5px solid #2c4f14;
transform: rotate(20deg);
}
.sun-dial {
position: relative;
width: 22px;
height: 11px;
overflow: hidden;
border-radius: 11px 11px 0 0;
background: linear-gradient(90deg, #8fd3e8, #f6c177 55%, #e86f51 80%, #5b3a74);
border: 1.5px solid var(--ink);
border-bottom: 0;
}
.sun-dial i {
position: absolute;
left: 50%;
bottom: 0;
width: 2px;
height: 10px;
margin-left: -1px;
background: var(--ink);
transform-origin: 50% 100%;
}
.sun.late {
background: #ffd9c2;
animation: pulse 0.8s ease-in-out infinite alternate;
}
.tears {
display: inline-flex;
gap: 4px;
padding: 4px 6px;
background: rgba(59, 36, 18, 0.55);
border-radius: 14px;
}
.tear {
width: 12px;
height: 12px;
border: 2px solid var(--tear);
border-radius: 0 50% 50% 50%;
transform: rotate(45deg);
background: transparent;
}
.tear.cried {
background: var(--tear);
box-shadow: 0 0 6px var(--tear);
}
.icon-btn {
pointer-events: auto;
margin-left: auto;
min-width: 36px;
height: 36px;
padding: 0 8px;
background: var(--paper);
border: 2px solid var(--ink);
border-radius: 50%;
box-shadow: 2px 2px 0 rgba(59, 36, 18, 0.55);
font-size: 12px;
letter-spacing: -2px;
}
.hud-bottom {
position: absolute;
left: 50%;
bottom: max(10px, env(safe-area-inset-bottom));
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
}
.gear,
.power {
display: inline-flex;
align-items: center;
gap: 5px;
}
.gear {
padding: 5px 8px;
background: rgba(59, 36, 18, 0.62);
border-radius: 4px;
min-height: 28px;
}
.gear:empty,
.power:empty {
display: none;
}
.shell {
width: 9px;
height: 20px;
border-radius: 3px 3px 1px 1px;
background: linear-gradient(var(--sauce) 68%, var(--brass) 68%);
border: 1px solid #2a0d0d;
}
.shell.big {
width: 14px;
height: 24px;
background: linear-gradient(#6b705c 60%, var(--brass) 60%);
}
.shell.spent {
opacity: 0.25;
}
.rope {
width: 18px;
height: 18px;
border-radius: 50%;
border: 3px solid #d1a660;
}
.rope.used {
background: radial-gradient(circle, var(--pickle) 45%, transparent 48%);
}
.lbl {
color: var(--paper);
font-size: 0.72em;
letter-spacing: 0.1em;
}
.blink {
animation: pulse 0.35s steps(2) infinite;
}
.pw {
padding: 4px 9px;
border: 2px solid var(--ink);
border-radius: 3px;
font-weight: 700;
font-size: 0.8em;
letter-spacing: 0.06em;
box-shadow: 2px 2px 0 rgba(59, 36, 18, 0.55);
}
.pw.tank {
background: var(--tank);
}
.pw.sauce {
background: var(--sauce);
color: var(--paper);
animation: pulse 0.3s ease-in-out infinite alternate;
}
.toast {
position: absolute;
top: 28%;
left: 50%;
max-width: 92%;
padding: 6px 14px 8px;
background: var(--paper);
border: 3px solid var(--ink);
font-size: clamp(14px, 4.2vmin, 26px);
font-weight: 700;
text-align: center;
opacity: 0;
transform: translateX(-50%) rotate(-3deg);
box-shadow: 3px 3px 0 rgba(59, 36, 18, 0.6);
}
.toast.show {
opacity: 1;
}
.toast.stamp {
animation: stamp 0.25s ease-out;
}
.toast.good {
border-color: #2c4f14;
color: #2c4f14;
}
.toast.bad {
border-color: #6b1d5c;
color: #6b1d5c;
}
/* ---------- touch controls ---------- */
.touch {
position: absolute;
inset: auto 0 0 0;
height: 0;
}
.stick {
position: absolute;
left: max(14px, env(safe-area-inset-left));
bottom: max(14px, env(safe-area-inset-bottom));
width: var(--touch-stick);
height: var(--touch-stick);
border-radius: 50%;
background: radial-gradient(circle, rgba(243, 227, 195, 0.25), rgba(243, 227, 195, 0.45));
border: 3px dashed rgba(59, 36, 18, 0.65);
touch-action: none;
display: grid;
place-items: center;
}
.knob {
width: 44%;
height: 44%;
border-radius: 50%;
background: var(--paper);
border: 3px solid var(--ink);
box-shadow: 2px 2px 0 rgba(59, 36, 18, 0.55);
pointer-events: none;
}
.tbuttons {
position: absolute;
right: max(14px, env(safe-area-inset-right));
bottom: max(14px, env(safe-area-inset-bottom));
display: flex;
align-items: flex-end;
gap: 12px;
}
.tbtn {
width: var(--touch-btn);
height: var(--touch-btn);
border-radius: 50%;
border: 3px solid var(--ink);
font-weight: 700;
font-size: calc(var(--touch-btn) * 0.19);
letter-spacing: 0.04em;
touch-action: none;
box-shadow: 3px 3px 0 rgba(59, 36, 18, 0.6);
}
.tbtn.down {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 rgba(59, 36, 18, 0.6);
}
.tbtn.bang {
background: radial-gradient(circle at 35% 30%, #ff6b5b, var(--sauce));
color: var(--paper);
width: calc(var(--touch-btn) * 1.15);
height: calc(var(--touch-btn) * 1.15);
}
.tbtn.lasso {
background: radial-gradient(circle at 35% 30%, #f0d39b, #c99a4f);
margin-bottom: calc(var(--touch-btn) * 0.5);
}
/* With touch controls on a narrow frame, lift the gear row clear of them.
Wider frames keep it at the bottom, between the stick and the buttons. */
@media (max-width: 640px) {
.app:has(.touch:not([hidden])) .hud-bottom {
bottom: calc(var(--touch-stick) + 26px);
}
}
/* ---------- posters ---------- */
.poster-wrap {
position: absolute;
inset: 0;
display: grid;
place-items: center;
padding: 12px;
overflow: auto;
background: radial-gradient(ellipse at center, rgba(59, 36, 18, 0.1), rgba(59, 36, 18, 0.5));
}
.poster {
width: min(430px, 100%);
margin: auto;
padding: 18px 22px 20px;
text-align: center;
background:
radial-gradient(circle at 15% 10%, rgba(255, 255, 255, 0.35), transparent 40%),
radial-gradient(circle at 90% 85%, rgba(181, 84, 28, 0.18), transparent 45%),
linear-gradient(var(--paper), var(--paper-dark));
border: 3px solid var(--ink);
outline: 3px dashed #c99a4f;
outline-offset: -11px;
box-shadow: 5px 6px 0 rgba(59, 36, 18, 0.55);
transform: rotate(-1deg);
}
.wanted {
margin: 0;
font-size: clamp(34px, 11vmin, 64px);
font-weight: 900;
letter-spacing: 0.12em;
line-height: 1;
color: var(--ink);
}
.wanted.small {
font-size: clamp(24px, 8vmin, 44px);
letter-spacing: 0.06em;
}
.sub {
margin: 6px 0 10px;
font-style: italic;
color: var(--rust);
}
.mugshots {
display: flex;
justify-content: center;
align-items: flex-end;
gap: 18px;
margin: 8px 0 12px;
}
.mug {
display: block;
border: 2px solid var(--ink);
}
.mug.pickle {
width: 22px;
height: 48px;
border-radius: 12px;
background: var(--pickle);
transform: rotate(-12deg);
}
.mug.onion {
width: 44px;
height: 42px;
border-radius: 50% 50% 46% 46%;
background: radial-gradient(circle at 35% 35%, #e7a4d4, var(--onion));
position: relative;
}
.mug.onion::before {
content: '';
position: absolute;
left: 50%;
top: -14px;
width: 38px;
height: 8px;
margin-left: -19px;
border-radius: 50%;
background: var(--ink);
box-shadow: 0 -6px 0 -1px var(--ink);
}
.mug.tank {
width: 58px;
height: 22px;
border-radius: 4px 10px 6px 6px;
background: var(--tank);
position: relative;
}
.mug.tank::after {
content: '';
position: absolute;
left: 40px;
top: 2px;
width: 30px;
height: 4px;
background: var(--ink);
}
.rules {
margin: 0 0 10px;
padding: 0;
list-style: none;
text-align: left;
font-size: 14px;
line-height: 1.35;
}
.rules li {
padding: 3px 0 3px 18px;
position: relative;
}
.rules li::before {
content: '\2605';
position: absolute;
left: 0;
color: var(--rust);
}
.keys {
margin: 0 0 10px;
font-size: 12px;
opacity: 0.85;
}
kbd {
font-family: inherit;
padding: 0 4px;
border: 1.5px solid var(--ink);
border-radius: 3px;
background: rgba(255, 255, 255, 0.4);
}
.stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 6px 14px;
margin: 8px 0 10px;
}
.stats div {
border-bottom: 1.5px dashed rgba(59, 36, 18, 0.4);
padding-bottom: 3px;
}
.stats dt {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--rust);
}
.stats dd {
margin: 0;
font-size: 22px;
font-weight: 700;
}
.best {
margin: 0 0 10px;
}
.stamp-new {
display: inline-block;
margin: 0 0 10px;
padding: 2px 10px;
border: 3px solid var(--sauce);
color: var(--sauce);
font-weight: 900;
letter-spacing: 0.1em;
transform: rotate(-6deg);
}
.go {
min-width: 170px;
min-height: 48px;
padding: 8px 22px;
background: var(--rust);
color: var(--paper);
border: 3px solid var(--ink);
border-radius: 4px;
font-size: 20px;
font-weight: 900;
letter-spacing: 0.08em;
text-transform: uppercase;
box-shadow: 3px 3px 0 rgba(59, 36, 18, 0.6);
}
.go:hover {
background: #c9632a;
}
.go:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 rgba(59, 36, 18, 0.6);
}
.no-gl {
position: absolute;
inset: 0;
margin: 0;
display: grid;
place-items: center;
padding: 20px;
text-align: center;
background: var(--paper);
}
@keyframes stamp {
from {
transform: translateX(-50%) rotate(-8deg) scale(1.5);
opacity: 0;
}
to {
transform: translateX(-50%) rotate(-3deg) scale(1);
opacity: 1;
}
}
@keyframes pulse {
to {
filter: brightness(1.15);
transform: scale(1.05);
}
}
/* ---------- sizes ---------- */
@media (pointer: coarse) {
.icon-btn {
min-width: 44px;
height: 44px;
}
}
@media (min-width: 1400px) and (min-height: 800px) {
:root {
--hud-size: 19px;
}
.icon-btn {
min-width: 46px;
height: 46px;
}
}
@media (max-width: 560px), (max-height: 420px) {
:root {
--hud-size: 13px;
--touch-stick: 112px;
--touch-btn: 66px;
}
.badge.score .k {
display: none;
}
}
@media (max-height: 420px) {
.rules,
.mugshots {
display: none;
}
.poster {
padding: 12px 16px 14px;
}
}
/* Tiny frames: keep the counters that matter and shrink everything else. */
@media (max-width: 340px), (max-height: 260px) {
:root {
--hud-size: 11px;
--touch-stick: 84px;
--touch-btn: 50px;
}
.hud-top {
top: 4px;
left: 4px;
right: 4px;
gap: 3px 4px;
}
.badge {
padding: 1px 5px 2px;
border-width: 1.5px;
}
.badge.day .k,
.sun-dial {
display: none;
}
.tear {
width: 8px;
height: 8px;
border-width: 1.5px;
}
.tears {
padding: 2px 4px;
gap: 2px;
}
.icon-btn {
min-width: 30px;
height: 30px;
}
.hud-bottom {
bottom: 4px;
gap: 4px;
}
.gear {
padding: 2px 5px;
min-height: 20px;
}
.rope {
width: 12px;
height: 12px;
border-width: 2px;
}
.shell {
width: 7px;
height: 14px;
}
.tbuttons {
gap: 6px;
}
.stick,
.tbuttons {
bottom: 6px;
}
.stick {
left: 6px;
}
.tbuttons {
right: 6px;
}
.keys,
.sub {
font-size: 11px;
}
.poster {
padding: 10px 12px 12px;
outline-offset: -7px;
}
.go {
min-width: 120px;
min-height: 40px;
font-size: 15px;
}
.stats dd {
font-size: 16px;
}
}
@media (max-height: 200px) {
.keys,
.best,
.sub {
display: none;
}
.wanted {
font-size: 26px;
}
.stats {
margin: 4px 0;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
}
}
/* ---------- campaign additions ---------- */
.badge.pickles.done {
background: #d8efb8;
border-color: #2c4f14;
}
/* The boss bar is the last item in the top HUD row, so it always wraps below the badges. */
.boss {
flex: 0 0 100%;
margin-top: 2px;
display: grid;
justify-items: center;
gap: 2px;
text-align: center;
}
.boss-name {
font-weight: 900;
font-size: 0.85em;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--paper);
text-shadow: 1px 1px 0 var(--ink), -1px 1px 0 var(--ink), 1px -1px 0 var(--ink), -1px -1px 0 var(--ink);
}
.boss-bar {
display: block;
width: min(360px, 100%);
height: 12px;
background: rgba(59, 36, 18, 0.7);
border: 2px solid var(--ink);
border-radius: 3px;
overflow: hidden;
}
.boss-bar i {
display: block;
height: 100%;
width: 100%;
background: repeating-linear-gradient(90deg, var(--onion) 0 14px, #c06aab 14px 16px);
transition: width 0.2s ease-out;
}
.banner {
position: absolute;
top: 31%;
left: 50%;
display: grid;
justify-items: center;
gap: 2px;
width: max-content;
max-width: 92%;
padding: 10px 22px 12px;
background: var(--paper);
border: 3px solid var(--ink);
box-shadow: 4px 4px 0 rgba(59, 36, 18, 0.6);
text-align: center;
opacity: 0;
transform: translate(-50%, -50%) rotate(-2deg);
pointer-events: none;
}
.banner.show {
animation: banner 3.4s ease-in-out forwards;
}
.b-day {
font-size: 0.75em;
font-weight: 700;
letter-spacing: 0.2em;
color: var(--rust);
}
.b-name {
font-size: clamp(20px, 6vmin, 38px);
font-weight: 900;
line-height: 1.05;
}
.b-blurb {
font-style: italic;
font-size: 0.9em;
}
.hurt {
position: absolute;
inset: 0;
opacity: 0;
pointer-events: none;
background: radial-gradient(ellipse at center, transparent 40%, rgba(110, 198, 255, 0.85) 100%);
}
.hurt.show {
animation: hurt 0.7s ease-out forwards;
}
.heal {
margin: 0 0 8px;
color: #1f6fa3;
}
.next {
display: grid;
gap: 1px;
margin: 0 0 12px;
padding: 6px 10px;
border: 2px dashed rgba(59, 36, 18, 0.45);
background: rgba(255, 255, 255, 0.25);
}
.next b {
font-size: 1.15em;
}
.next span {
font-size: 0.85em;
}
.next .next-day {
font-size: 0.7em;
letter-spacing: 0.18em;
color: var(--rust);
font-weight: 700;
}
.actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
}
.go.alt {
background: var(--paper);
color: var(--ink);
}
.go.alt:hover {
background: #fff4dc;
}
.star {
color: var(--brass);
}
.badge-star {
display: grid;
place-items: center;
width: 64px;
height: 64px;
margin: 0 auto 6px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #ffe48a, var(--brass));
border: 3px solid var(--ink);
box-shadow: 3px 3px 0 rgba(59, 36, 18, 0.55);
font-size: 40px;
line-height: 1;
color: var(--ink);
animation: spin-in 0.8s ease-out;
}
/* Score-card composer: preview, note, status. */
.poster[data-kind='share'] {
width: min(520px, 100%);
}
.poster[data-kind='share'] .wanted.small {
font-size: clamp(20px, 6vmin, 34px);
}
.share-preview {
display: grid;
place-items: center;
aspect-ratio: 16 / 9;
max-height: 42vh;
margin: 8px auto 10px;
background: rgba(59, 36, 18, 0.18);
border: 2px solid var(--ink);
}
.share-preview canvas {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
.share-wait {
font-style: italic;
color: var(--rust);
}
.share-label {
display: block;
text-align: left;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--rust);
}
.share-text {
display: block;
width: 100%;
margin: 3px 0 6px;
padding: 6px 8px;
resize: vertical;
font: 15px/1.35 var(--slab);
color: var(--ink);
background: #fff8e8;
border: 2px solid var(--ink);
border-radius: 3px;
}
.share-text:focus-visible {
outline: 3px solid var(--brass);
outline-offset: 1px;
}
.share-status {
min-height: 1.3em;
margin: 0 0 8px;
font-size: 14px;
font-style: italic;
color: var(--ink);
overflow-wrap: anywhere;
}
.share-status.good {
color: var(--pickle);
font-weight: 700;
}
.share-status.bad {
color: var(--sauce);
}
.go:disabled {
opacity: 0.6;
cursor: default;
transform: none;
}
/* On wide frames the victory poster steps aside so the celebration shows. */
@media (min-width: 900px) and (min-aspect-ratio: 13/10) {
.poster-wrap:has(.poster[data-kind='victory']),
.poster-wrap:has(.poster[data-kind='share']) {
justify-items: start;
padding-left: 6%;
background: linear-gradient(90deg, rgba(59, 36, 18, 0.45), transparent 60%);
}
.poster[data-kind='victory'],
.poster[data-kind='share'] {
margin-left: 0;
}
}
@keyframes banner {
0% {
opacity: 0;
transform: translate(-50%, -50%) rotate(-6deg) scale(1.4);
}
10%,
80% {
opacity: 1;
transform: translate(-50%, -50%) rotate(-2deg) scale(1);
}
100% {
opacity: 0;
transform: translate(-50%, -60%) rotate(-2deg) scale(0.96);
}
}
@keyframes hurt {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes spin-in {
from {
transform: rotate(-200deg) scale(0.2);
}
to {
transform: none;
}
}
@media (max-width: 340px), (max-height: 260px) {
.boss-bar {
width: 70%;
}
.boss-bar {
height: 7px;
}
.boss-name {
font-size: 0.7em;
}
.banner {
padding: 4px 10px 6px;
border-width: 2px;
}
.b-blurb,
.heal {
display: none;
}
.next {
padding: 3px 6px;
margin-bottom: 6px;
}
.next span:not(.next-day) {
display: none;
}
.badge-star {
width: 34px;
height: 34px;
font-size: 20px;
}
}
@media (prefers-reduced-motion: reduce) {
.banner.show {
animation: none;
opacity: 1;
}
}
/* Short frames (phone landscape, small widgets): keep the poster buttons in view. */
@media (max-height: 460px) {
.badge-star,
.poster[data-kind='victory'] .sub,
.poster[data-kind='dayEnd'] .heal {
display: none;
}
.stats {
grid-template-columns: repeat(4, 1fr);
gap: 4px 8px;
margin: 4px 0 6px;
}
.stats dt {
font-size: 9px;
}
.stats dd {
font-size: 17px;
}
.sub {
margin: 2px 0 6px;
}
.next {
margin-bottom: 8px;
padding: 4px 8px;
}
.stamp-new {
margin-bottom: 6px;
}
.actions .go {
min-height: 42px;
font-size: 17px;
} .share-preview {
max-height: 30vh;
margin: 4px auto 6px;
}
.share-text {
font-size: 14px;
}
}
@media (max-height: 260px) {
.poster[data-kind='victory'] .stats,
.poster[data-kind='dayEnd'] .stats,
.poster[data-kind='dayEnd'] .next,
.poster[data-kind='dayEnd'] .sub,
.poster[data-kind='victory'] .stamp-new,
.poster[data-kind='victory'] .best {
display: none;
}
.share-preview,
.share-label,
.poster[data-kind='share'] .wanted {
display: none;
}
.share-text {
height: 4.2em;
margin-top: 0;
font-size: 12px;
}
.share-status {
margin-bottom: 4px;
font-size: 11px;
}
.wanted.small {
font-size: 20px;
}
.actions {
gap: 6px;
}
.actions .go {
min-width: 0;
min-height: 34px;
padding: 4px 10px;
font-size: 13px;
}
}
