Files
2026-09-04 19:46:58 +08:00

357 lines
35 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "layout"}}<!doctype html>
<html lang="{{.Lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="theme-color" content="#f4eedd">
<title>{{template "title" .}}</title>
<meta name="description" content="{{if .Desc}}{{.Desc}}{{else}}{{T "赛博要饭:行行好,赏口饭吃。用币安 Pay 投币施舍,留一句话上功德簿;不花钱也能丢钢镚;用 X 发一条推就能开自己的要饭站。"}}{{end}}">
<meta property="og:title" content="{{template "title" .}}">
<meta property="og:description" content="{{if .Desc}}{{.Desc}}{{else}}{{T "行行好,赏口饭吃。投币上功德簿,丢钢镚也算爱。"}}{{end}}">
<meta property="og:type" content="website">
{{if .OGImage}}<meta property="og:image" content="{{.OGImage}}">{{end}}
<meta name="twitter:card" content="summary">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E🥣%3C/text%3E%3C/svg%3E">
{{if .NoIndex}}<meta name="robots" content="noindex">{{end}}
<link rel="preload" href="/static/pixel.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face{font-family:"Pixel";src:url(/static/pixel.woff2) format("woff2");font-display:swap}
:root{--ink:#1e1e2e;--paper:#f4eedd;--paper-2:#e9e1ca;--panel:#fffbf0;--panel-2:#fff3cc;--gold:#ffcc00;--gold-2:#e0a800;--red:#ff4b4b;--green:#3ec46d;--blue:#3d8bfd;--purple:#8e5cff;--muted:#4a4954;--muted-2:#6f6d66;--pf:"Pixel","PingFang SC","Noto Sans SC","Microsoft YaHei",sans-serif;--line:#d9d0b8;--frame:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' shape-rendering='crispEdges'%3E%3Cpath fill='%231e1e2e' d='M1 0h4v1H1zM0 1h6v1H0zM0 2h2v2H0zM4 2h2v2H4zM0 4h6v1H0zM1 5h4v1H1z'/%3E%3C/svg%3E")}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;overflow-x:hidden}
html,body{max-width:100%;overscroll-behavior-x:none}
@media(max-width:899px){body{overflow-x:hidden;position:relative}}
body{margin:0;color:var(--ink);background-color:var(--paper);background-image:linear-gradient(45deg,var(--paper-2) 25%,transparent 25%,transparent 75%,var(--paper-2) 75%),linear-gradient(45deg,var(--paper-2) 25%,transparent 25%,transparent 75%,var(--paper-2) 75%);background-size:16px 16px;background-position:0 0,8px 8px;font:15px/1.7 -apple-system,BlinkMacSystemFont,"PingFang SC","Noto Sans SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",Arial,sans-serif;min-height:100vh}
.pf,h1,h2,.brand,.ticker,.lv,.pname,.stat .v,.stat .k,.amt-btn,.btn,.tab,.amtv,.rank,.insert,.barlabel,.tag,.nav a,.name,.av,.section-title,.copy,.anchors a,.step::before,.big,.dialog,.me,.count,.card h2 .sub,.kv .k,.empty .ico{font-family:var(--pf)}
.pf12{font-size:12px}
a{color:var(--blue)}
.num{font-variant-numeric:tabular-nums}
.accent{color:var(--gold-2)}
.page{max-width:1080px;margin:0 auto;padding:0 16px 96px}
@media(min-width:900px){.page{padding-bottom:40px}}
.px{border:4px solid transparent;border-image:var(--frame) 2 / 4px stretch;image-rendering:pixelated;background-clip:padding-box}
.nav{display:flex;align-items:center;gap:6px;height:64px}
.langsel{display:inline-flex;align-items:center;flex:none;position:relative;cursor:pointer}.langsel select{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;font-size:16px}
.langcur{display:inline-flex;align-items:center;gap:2px;font-family:var(--pf);font-size:14px;line-height:1;color:var(--ink);background:#fff;border:2px solid var(--ink);box-shadow:2px 2px 0 var(--ink);padding:5px 6px;min-width:34px;justify-content:center}
.langcur i{font-style:normal;font-size:9px;opacity:.6}
.sfx{background:none;border:0;line-height:0;padding:8px 4px;cursor:pointer;min-width:34px;min-height:36px;display:inline-flex;align-items:center;justify-content:center;flex:none}
.px-spk .spk-x{display:none}.sfx.off .spk-w1,.sfx.off .spk-w2{display:none}.sfx.off .spk-x{display:inline}
.sfx.idle .spk-w2{opacity:.25}
.navlogin{display:inline-flex;align-items:center;gap:5px}.px-key{flex:none}
.navrank{display:inline-flex;align-items:center;gap:5px}.px-trophy{flex:none}
.sfx.idle{animation:sfxnudge 1.2s ease-in-out infinite}@keyframes sfxnudge{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}@media(prefers-reduced-motion:reduce){.sfx.idle{animation:none}}
.nav>*{flex:none}.nav .brand{flex:0 1 auto;min-width:0}.nav .brand span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:430px){.nav{gap:4px}.nav .brand{font-size:20px;margin-right:2px}.nav .brand .px-coin{display:none}.sfx{min-width:28px;padding:6px 2px;font-size:16px}.nav a.link{padding:8px 5px;font-size:15px}.nav a.pill{padding:6px 10px;font-size:15px}
.nav a.navrank span{display:none}.nav .navlogin .lbl{display:none}}
.brand{display:flex;align-items:center;gap:8px;font-size:24px;line-height:1;color:var(--ink);text-decoration:none;white-space:nowrap;margin-right:6px}
.brand .px-coin{width:28px;height:28px;flex:none}
.nav .sp{flex:1}
.nav a.link{color:var(--ink);text-decoration:none;font-size:16px;padding:8px 8px;white-space:nowrap}
.nav a.link:hover{background:var(--panel-2)}
.nav a.pill{color:var(--ink);background:var(--gold);font-size:16px;padding:6px 12px;text-decoration:none;white-space:nowrap;box-shadow:3px 3px 0 var(--ink)}
.nav a.pill:active{transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink)}
@media(max-width:420px){.hide-sm{display:none}.nav a.pill{font-size:12px;padding:6px 10px}.nav a.link{font-size:12px;padding:8px 5px}}
.ticker{overflow:hidden;contain:paint;touch-action:pan-y;background:var(--ink);color:var(--gold);font-size:12px;line-height:1.5;padding:6px 0;margin:0 0 16px;white-space:nowrap;box-shadow:3px 3px 0 rgba(30,30,46,.25)}
.ticker-in{display:inline-block;animation:tick 34s linear infinite}
.ticker-in span{padding:0 10px}
@keyframes tick{to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){.ticker-in,.sprite .bowl,.sprite .eyes,.sprite .tear,.blink,.jump .sprite,.dialog .cursor,.flycoin{animation:none!important}}
.layout{display:grid;gap:18px}
@media(min-width:900px){.layout{grid-template-columns:400px minmax(0,1fr);gap:24px;align-items:start}.col-l{position:sticky;top:16px}.layout.cols2{grid-template-columns:1fr 1fr}}
.col-l,.col-r{display:grid;gap:18px;min-width:0}
.narrow{max-width:560px;margin:0 auto}
.card{background:var(--panel);padding:18px;box-shadow:4px 4px 0 var(--ink)}
.card h2{font-size:18px;margin:0 0 12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;line-height:1.3}
.card h2 .sub{color:var(--muted);font-size:12px}
h1{font-size:24px;margin:0 0 8px;line-height:1.3}
/* 角色卡 */
.char-top{display:flex;gap:16px;align-items:flex-start}
.sprite-wrap{flex:none;width:96px;display:grid;place-items:end center}
.sprite{width:96px;height:auto;image-rendering:pixelated}
@media(min-width:900px){.char-top .sprite-wrap,.char-top .sprite{width:120px}}
.sprite .bowl{animation:shake 1s steps(1) infinite}
@keyframes shake{0%,100%{transform:translateY(0)}50%{transform:translateY(-1px)}}
.sprite .eyes{animation:blink 4s steps(1) infinite}
@keyframes blink{0%,90%{opacity:1}91%,96%{opacity:0}97%,100%{opacity:1}}
.sprite .tear{animation:tear 2.4s steps(1) infinite}
@keyframes tear{0%{transform:translateY(0)}33%{transform:translateY(1px)}66%{transform:translateY(2px)}}
.jump .sprite{animation:jump .9s steps(1) infinite}
@keyframes jump{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
.char-info{flex:1;min-width:0}
.lvrow{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.lv{display:inline-block;background:var(--ink);color:var(--gold);font-size:12px;line-height:1.6;padding:2px 8px}
.lv b{color:#fff;font-weight:normal;margin-left:6px}
.lv.gold{background:var(--gold);color:var(--ink);border:2px solid var(--ink);padding:0 8px}
.pname{font-size:24px;margin:0 0 8px;line-height:1.25;word-break:break-word}
.barlabel{display:flex;justify-content:space-between;align-items:baseline;font-size:12px;color:var(--muted);margin-top:8px;line-height:1.4}
.barlabel b{color:var(--ink);font-weight:normal;margin-left:4px}
.barlabel span{font-size:12px;color:var(--muted);margin-left:8px;text-align:right}
.bar{position:relative;height:14px;border:3px solid var(--ink);background:#fff;margin:2px 0 0;box-shadow:2px 2px 0 var(--ink)}
.bar span{display:block;height:100%;background:var(--green);background-image:repeating-linear-gradient(90deg,transparent 0 6px,rgba(0,0,0,.14) 6px 8px);transition:width .6s steps(12)}
.bar.hp span{background-color:var(--red)}
.xpill{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:700;padding:3px 8px;border:3px solid var(--ink);background:#fff;color:var(--ink);text-decoration:none;box-shadow:2px 2px 0 var(--ink);margin-top:6px}
.dialog{position:relative;margin-top:16px;background:#fff;padding:10px 26px 10px 12px;font-size:16px;line-height:1.6;box-shadow:3px 3px 0 var(--ink);min-height:44px;word-break:break-word}
.dialog::before{content:"▶";position:absolute;left:-2px;top:-14px;background:var(--gold);padding:0 6px;font-size:10px;border:3px solid var(--ink)}
.dialog .cursor{position:absolute;right:8px;bottom:6px;font-size:10px;animation:blinkc .8s steps(1) infinite}
@keyframes blinkc{50%{opacity:0}}
.more{background:none;border:0;color:var(--blue);font-size:12px;cursor:pointer;padding:6px 0 0;font-family:inherit}
/* HUD */
.stats{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.stat{background:var(--panel);padding:10px 12px;min-width:0;box-shadow:3px 3px 0 var(--ink)}
.stat.main{grid-column:1/-1;background:var(--gold);display:flex;align-items:center;gap:12px}
.stat .k{font-size:12px;color:var(--muted);line-height:1.6}
.stat.main .k{color:var(--ink)}
.stat .v{font-size:24px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}
.stat.main .v{font-size:36px;line-height:1.1}
.stat .v small{font-size:12px;color:var(--muted);margin-left:5px}
.stat.main .v small{color:var(--ink)}
.stat.main .bowlv{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 14px;white-space:nowrap;overflow:visible}
.stat.main .bowlv>span:first-child{min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.stat.main .bowlcoins{font-size:22px;display:inline-flex;align-items:center;gap:5px}
.stat.main .bowlcoins .px-coin{width:18px;height:18px;flex:none}
.stat.main .bowlcoins small{margin-left:0}
.stat .px-bowl{width:48px;height:auto;flex:none}
.stat.main .sub2{display:flex;flex-direction:column;gap:8px;align-self:center;flex:none;font-family:var(--pf);color:var(--ink);text-align:left}
.stat.main .sub2 span{display:flex;align-items:baseline;justify-content:flex-start;gap:5px;white-space:nowrap;background:#fff;border:2px solid var(--ink);box-shadow:2px 2px 0 var(--ink);padding:3px 9px;min-width:96px}
.stat.main .sub2 i{font-style:normal;font-size:11px;opacity:.7}.stat.main .sub2 b{font-size:16px;font-weight:400}.stat.main .sub2 small{font-size:11px;opacity:.7}
@media(max-width:430px){.stat.main .v{font-size:30px}.stat.main .bowlcoins{font-size:18px}.stat.main{gap:8px}.stat .px-bowl{width:40px}}
/* 表单 */
label{display:block;font-size:13px;color:var(--ink);font-weight:600;margin:14px 0 6px}
form>label:first-child,.grid2>div>label:first-child{margin-top:0}
input,textarea,select{width:100%;padding:10px 12px;border:3px solid var(--ink);background:#fff;color:var(--ink);font-size:16px;font-family:inherit;line-height:1.4;border-radius:0;box-shadow:inset 3px 3px 0 var(--paper-2)}
input::placeholder,textarea::placeholder{color:#8f8a7c}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--blue);box-shadow:3px 3px 0 var(--blue)}
textarea{resize:vertical;min-height:70px}
.hint{font-size:13px;color:var(--muted);margin-top:6px;line-height:1.6}
.err{color:var(--red);font-size:13px;margin-top:10px}
.ok{color:var(--green);font-size:13px}
.insert{text-align:center;font-size:12px;letter-spacing:.1em;color:var(--red);margin:-4px 0 12px;line-height:1.6}
.blink{animation:blinkc 1s steps(1) infinite}
.amounts{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.amt-btn{display:flex;align-items:center;justify-content:center;gap:5px;padding:10px 4px;border:3px solid var(--ink);background:#fff;color:var(--ink);font-size:16px;white-space:nowrap;cursor:pointer;font-family:inherit;min-height:46px;box-shadow:3px 3px 0 var(--ink)}
.amt-btn .px-coin{width:16px;height:16px}
.amt-btn.on{background:var(--gold);transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink)}
.count{float:right;font-size:12px;color:var(--muted-2)}
.btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:50px;border:4px solid transparent;border-image:var(--frame) 2 / 4px stretch;image-rendering:pixelated;background-clip:padding-box;padding:10px 16px;font-size:18px;cursor:pointer;background-color:var(--gold);color:var(--ink);text-decoration:none;box-shadow:4px 4px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:1px 1px 0 var(--ink)}
.btn.sec{background-color:#fff}
.btn.ghost{background-color:transparent;box-shadow:none;color:var(--muted)}
.btn.danger{background-color:#fff;color:var(--red)}
.btn.sm{display:inline-flex;width:auto;min-height:40px;padding:6px 12px;font-size:14px;box-shadow:3px 3px 0 var(--ink)}
.btns{display:flex;gap:12px;flex-wrap:wrap;margin-top:14px}
.btns .btn{flex:1 1 auto;white-space:nowrap}
/* Tab */
.tabs{display:grid;grid-template-columns:repeat(3,1fr);border:3px solid var(--ink);margin-bottom:14px;background:#fff;box-shadow:3px 3px 0 var(--ink)}
.tab{border:0;border-right:3px solid var(--ink);background:transparent;color:var(--muted);font-size:16px;padding:10px 4px;cursor:pointer;white-space:nowrap}
.tab:last-child{border-right:0}
.tab.on{background:var(--gold);color:var(--ink)}
.panel{display:none}.panel.on{display:block}
/* 榜单 / 功德簿 */
.list{list-style:none;margin:0;padding:0}
.item{display:flex;align-items:center;gap:10px;padding:10px 0;border-top:3px dotted var(--line)}
.item:first-child{border-top:0;padding-top:2px}
.av{width:36px;height:36px;flex:none;display:grid;place-items:center;font-size:16px;background:hsl(var(--h) 75% 86%);color:hsl(var(--h) 55% 28%);border:3px solid var(--ink);box-shadow:2px 2px 0 var(--ink)}
.av.sq{background:#fff;font-size:20px;color:var(--ink)}
.rank{width:30px;flex:none;text-align:center;font-size:16px;color:var(--muted)}
.rank.m{font-size:20px}
.body{flex:1;min-width:0}
.name{display:flex;align-items:center;gap:6px;min-width:0;font-size:16px}
.name>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.name a{color:inherit;text-decoration:none}
.sub{font-size:13px;color:var(--muted);margin-top:1px}
.amtv .px-coin{width:14px;height:14px;vertical-align:-2px}
.amtv{white-space:nowrap;font-size:18px}
.feed .item{flex-wrap:wrap}
.bubble{flex-basis:100%;margin:8px 0 0 46px;background:#fff;border:3px solid var(--ink);padding:6px 10px;font-size:14px;line-height:1.6;word-break:break-word;position:relative;box-shadow:2px 2px 0 var(--ink)}
.bubble::before{content:"";position:absolute;left:-9px;top:8px;border:3px solid transparent;border-right-color:var(--ink);border-width:4px 6px 4px 0}
.bubble.blocked{color:var(--muted-2);letter-spacing:2px}
.time{font-size:12px;color:var(--muted);white-space:nowrap}
.me{font-size:10px;color:var(--ink);background:var(--gold);border:2px solid var(--ink);padding:0 5px;flex:none}
.empty{text-align:center;color:var(--muted);padding:24px 12px;font-size:14px}
.fold>summary{list-style:none;cursor:pointer;-webkit-tap-highlight-color:transparent}.fold>summary::-webkit-details-marker{display:none}
.fold>summary h2{margin:0;display:flex;align-items:center;gap:8px}.fold>summary .caret{margin-left:auto;font-size:18px;transition:transform .15s}.fold[open]>summary .caret{transform:rotate(90deg)}
.fold[open]>summary h2{margin-bottom:12px}
.pager{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:12px;font-family:var(--pf);font-size:14px}
.pager a,.pager span.cur,.pager span.dis{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:36px;padding:0 10px;border:3px solid var(--ink);background:#fff;color:var(--ink);text-decoration:none;box-shadow:2px 2px 0 var(--ink)}
.pager span.cur{background:var(--gold)}.pager span.dis{opacity:.4;box-shadow:none}
.empty .ico{font-size:32px;margin-bottom:6px}
.empty .sprite{width:72px}
.cta{text-align:center}
.cta p{color:var(--ink);margin:0 0 12px;font-size:14px}
.fab{position:fixed;left:16px;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));z-index:20;display:none}
.fab.show{display:block}
@media(min-width:900px){.fab{display:none!important}.only-mobile{display:none!important}}
@media(max-width:899px){.only-desktop{display:none!important}}
.notice{background:var(--panel-2);border:3px solid var(--ink);padding:10px 12px;font-size:14px;line-height:1.6;box-shadow:3px 3px 0 var(--ink)}
.notice.ok{background:#dff7e8}
.notice.bad{background:#ffe1e1}
.code{background:#fff;border:2px solid var(--ink);padding:1px 6px;font-size:13px;word-break:break-all}
.copy{cursor:pointer;border:3px solid var(--ink);background:#fff;color:var(--ink);padding:6px 10px;min-height:36px;font-size:12px;flex:none;box-shadow:2px 2px 0 var(--ink)}
.kv{display:grid;grid-template-columns:auto 1fr;gap:10px 14px;font-size:14px;align-items:center}
.kv .k{color:var(--muted)}
.kv .v{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.qr{display:block;margin:8px auto;background:#fff;padding:8px;width:240px;height:240px;border:3px solid var(--ink);box-shadow:3px 3px 0 var(--ink);image-rendering:pixelated}
.steps{counter-reset:s;display:grid;gap:18px}
.step{position:relative;padding-left:44px}
.step::before{counter-increment:s;content:counter(s);position:absolute;left:0;top:-2px;width:30px;height:30px;background:var(--gold);color:var(--ink);border:3px solid var(--ink);font-size:14px;display:grid;place-items:center;box-shadow:2px 2px 0 var(--ink)}
.step h2{margin-bottom:8px}
.big{font-size:36px;line-height:1.2}
.big small{font-size:12px;color:var(--muted);margin-left:6px}
.spin{width:44px;height:44px;border:4px solid var(--line);border-top-color:var(--ink);margin:12px auto 16px;animation:spin 1s steps(8) infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.center{text-align:center}
.tag{display:inline-block;padding:0 6px;font-size:12px;line-height:1.6;border:2px solid var(--ink);color:var(--ink);white-space:nowrap;background:#fff}
.tag.paid{background:#dff7e8}.tag.claimed{background:var(--panel-2)}.tag.bad{background:#ffe1e1;color:var(--red);border-color:var(--red)}
.grid2{display:grid;gap:0 14px}@media(min-width:600px){.grid2{grid-template-columns:1fr 1fr}}
.emojis{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.emojis button{font-size:20px;background:#fff;border:3px solid var(--ink);padding:3px 7px;cursor:pointer;line-height:1.3;box-shadow:2px 2px 0 var(--ink)}
.anchors{display:flex;gap:10px;overflow-x:auto;padding:2px 2px 6px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.anchors::-webkit-scrollbar{display:none}
.anchors a{flex:none;font-size:12px;line-height:1.8;color:var(--ink);text-decoration:none;border:3px solid var(--ink);padding:4px 10px;background:#fff;box-shadow:2px 2px 0 var(--ink)}
.actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:10px;flex-basis:100%;padding-left:46px}
.actions form,.inline form{display:inline}
.section-title{font-size:12px;color:var(--muted);letter-spacing:.1em;margin:22px 0 8px;line-height:1.6}
.xavatar{width:40px;height:40px;border:3px solid var(--ink);vertical-align:middle;margin-right:6px;image-rendering:auto;object-fit:cover;background:#fff}
.av.img{object-fit:cover;background:#fff}
.xh{font-size:12px;color:var(--muted);font-family:var(--pf);flex:none}
.badge{font-size:14px;flex:none;letter-spacing:-2px}
.coinrow{display:flex;align-items:center;gap:10px;margin-top:14px;flex-wrap:wrap}
.js .col-l>.donate-card{display:none}
#modalForm .donate-card{box-shadow:none;border:0;padding:0;background:transparent}
#modalForm .donate-card h2{padding-right:44px}
.coinrow .coins b{color:var(--ink);font-size:16px}
.coinbtn .px-coin{width:16px;height:16px}
.coinbtn:disabled{opacity:.6;cursor:default}
.flycoin{position:fixed;z-index:50;font-size:28px;animation:fly .9s ease-out forwards;pointer-events:none}
@keyframes fly{0%{transform:translate(-50%,0) scale(1);opacity:1}60%{transform:translate(-50%,-90px) scale(1.3);opacity:1}100%{transform:translate(-50%,-140px) scale(.6);opacity:0}}
.hdr{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.hdr .body{flex:1 1 220px;min-width:0}
.hdr-actions{display:flex;gap:8px;margin-left:auto;flex:none}.hdr-actions form{display:contents}
@media(max-width:599px){.hdr-actions{flex-basis:100%;margin-left:0}.hdr-actions .btn{flex:1}}
.toast{position:fixed;left:50%;bottom:calc(84px + env(safe-area-inset-bottom));transform:translateX(-50%);background:var(--ink);color:var(--gold);padding:8px 14px;font-family:var(--pf);font-size:14px;z-index:60;box-shadow:3px 3px 0 rgba(0,0,0,.25)}
.tabs.four{grid-template-columns:repeat(4,1fr)}
.tabs.four .tab{font-size:14px;padding:10px 2px}
.tab.on::before{content:"▶";font-size:8px;margin-right:3px}
.toggle{display:inline-flex;border:3px solid var(--ink);margin-bottom:10px;background:#fff}
.toggle button{border:0;background:transparent;font-family:var(--pf);font-size:12px;padding:8px 12px;min-height:36px;cursor:pointer;color:var(--muted)}
.toggle button.on{background:var(--ink);color:var(--gold)}
.bubble.reply{background:var(--panel-2);margin-top:6px}
.profrow{display:flex;align-items:center;gap:12px;margin:12px 0}
.profrow .xavatar{width:56px;height:56px;margin:0}
.codebox{display:flex;align-items:center;gap:10px;margin:8px 0 12px}
.codebox .code.big{font-size:24px;padding:6px 12px;font-family:var(--pf)}
.sharebox{margin-top:14px}
.sharebox img{display:block;max-width:100%;border:3px solid var(--ink);box-shadow:4px 4px 0 var(--ink)}
.modal{position:fixed;inset:0;background:rgba(30,30,46,.6);z-index:100;display:flex;align-items:flex-end;justify-content:center}
.modal[hidden]{display:none}
.modal-box{position:relative;width:100%;max-height:92vh;overflow:auto;background:var(--panel);padding:18px 16px calc(18px + env(safe-area-inset-bottom));border-top:4px solid var(--ink);box-shadow:0 -6px 0 var(--ink)}
@media(min-width:600px){.modal{align-items:center;padding:20px}.modal-box{width:520px;max-height:90vh;border:4px solid var(--ink);box-shadow:6px 6px 0 var(--ink)}}
.modal-x{position:absolute;right:10px;top:8px;width:36px;height:36px;border:3px solid var(--ink);background:#fff;font-family:var(--pf);font-size:16px;cursor:pointer;box-shadow:2px 2px 0 var(--ink);z-index:1}
.spin-inline{display:inline-block;width:14px;height:14px;border:3px solid var(--line);border-top-color:var(--ink);border-radius:50%;animation:spin 1s steps(8) infinite;vertical-align:-2px;margin-right:4px}
.paypanel .qr{width:200px;height:200px}
.paypanel .big{font-size:32px}
footer{color:var(--muted);font-size:13px;text-align:center;margin-top:32px;line-height:1.9}
footer a{color:var(--muted)}
</style>
</head>
<body>
{{if .HasBGM}}<audio id="bgm" src="/static/bgm.m4a" loop preload="none" playsinline></audio>{{end}}
<div class="page">
<nav class="nav">
<a class="brand" href="{{.HomePath}}"{{if ne .HomePath "/"}} title="{{T "回到刚才看的碗"}}"{{end}}>{{template "coin"}}<span>{{T .SiteTitle}}</span></a>
<span class="sp"></span>
<label class="langsel" title="Language / 语言"><span class="langcur" aria-hidden="true">{{.LangShort}}<i>▾</i></span><select onchange="location.href='/lang?to='+encodeURIComponent(this.value)" aria-label="Language">{{range .Langs}}<option value="{{.Code}}"{{if eq .Code $.Lang}} selected{{end}}>{{.Name}}</option>{{end}}</select></label>
<button class="sfx" id="sfxBtn" type="button" aria-label="{{T "声音开关"}}" title="{{T "声音"}}">{{template "spk"}}</button>
<a class="link navrank" href="/rank" aria-label="{{T "排行榜"}}" title="{{T "排行榜"}}">{{template "trophy"}}<span>{{T "排行榜"}}</span></a>
{{if .Me}}<a class="link" href="/manage">{{T "我的后台"}}</a>{{else}}<a class="link navlogin" href="/login">{{template "keyicon"}}<span class="hide-sm">{{T "站长"}}</span><span class="lbl">{{T "登录"}}</span></a>{{end}}
{{if .Me}}<a class="pill px" href="/{{.Me.Slug}}">{{T "我的站"}}</a>{{else if .SubsitesEnabled}}<a class="pill px" href="/new">{{T "开通要饭站"}}</a>{{end}}
</nav>
<div class="ticker" aria-hidden="true"><div class="ticker-in"><span>{{T "🙏 行行好,赏口饭吃"}}</span><span>{{T "🪙 一毛也是爱"}}</span><span>{{T "💸 投币就上榜"}}</span><span>{{T "📜 留一句话刻功德簿"}}</span><span>{{T "🏚 发条推就能开自己的要饭站"}}</span><span>{{T "👾 赛博丐帮,人人有碗"}}</span><span>{{T "🙏 行行好,赏口饭吃"}}</span><span>{{T "🪙 一毛也是爱"}}</span><span>{{T "💸 投币就上榜"}}</span><span>{{T "📜 留一句话刻功德簿"}}</span><span>{{T "🏚 发条推就能开自己的要饭站"}}</span><span>{{T "👾 赛博丐帮,人人有碗"}}</span></div></div>
{{template "content" .}}
<footer>
{{T .SiteTitle}} {{T "· 赛博丐帮,行行好"}}<br>
{{if or .AuthorX .AuthorGitHub}}{{T "作者"}}{{if .AuthorX}} <a href="{{.AuthorX}}" target="_blank" rel="noopener">𝕏 @{{lastSeg .AuthorX}}</a>{{end}}{{if .AuthorGitHub}} · <a href="{{.AuthorGitHub}}" target="_blank" rel="noopener">GitHub {{lastSeg .AuthorGitHub}}</a>{{end}}<br>{{end}}
{{T "收款由"}} <a href="{{.RepoURL}}" target="_blank" rel="noopener">BinancePayTool</a> {{T "驱动 · 字体"}} <a href="https://github.com/TakWolf/fusion-pixel-font" target="_blank" rel="noopener">{{T "缝合怪像素字体"}}</a>{{if .SourceURL}}<br>
<a href="{{.SourceURL}}" target="_blank" rel="noopener">{{T "本站开源,欢迎 Star ⭐"}}</a>{{end}}
</footer>
</div>
<script>
// 声音:背景音乐(/static/bgm.m4a 循环)+ 8-bit 音效(Web Audio 现场合成,不加载文件)。一个开关管全部,记 localStorage(sfx),默认开。
// 浏览器不让页面一打开就出声:先尝试自动播放,被拦就等用户第一次触屏/点击/按键再起。音效首次出声也必须在用户手势里创建 AudioContext(iOS 限制)。
// 背景音乐也解码进同一个 AudioContext 里循环播放(AudioBufferSourceNode,无缝循环):和 <audio> 元素分开走的话,iOS 上音效一起音频会话就会把音乐打断重来。
// 解码不了(极少数浏览器没有 AAC 解码器)才退回 <audio> 元素。
var BGM_VOL=0.45;
window.sfx=(function(){var ctx,on=true,C=window.AudioContext||window.webkitAudioContext,buf=null,src=null,el=null,bytes=null,elMode=false,loading=false,unlocked=false;try{on=localStorage.getItem('sfx')!=='0'}catch(e){}
function ac(){if(!C)return null;if(!ctx){try{ctx=new C()}catch(e){return null}}if(ctx.state==='suspended'){try{ctx.resume()}catch(e){}}return ctx}
// 在用户手势里解锁:resume + 放一个空 buffer(老 iOS 的解锁套路)
function unlock(){var c=ac();if(!c)return;try{var b=c.createBuffer(1,1,22050),s=c.createBufferSource();s.buffer=b;s.connect(c.destination);s.start(0);unlocked=true}catch(e){}}
var paint=function(){};
// 下载一次:字节既喂给 <audio>(手势里能立刻响)也喂给 decodeAudioData(解码完切到 Web Audio 无缝循环)
function loadBgm(){if(bytes||loading)return;el=el||document.getElementById('bgm');if(!el)return;loading=true;
fetch(el.getAttribute('src')).then(function(r){if(!r.ok)throw 0;return r.arrayBuffer()}).then(function(ab){bytes=ab;
try{el.src=URL.createObjectURL(new Blob([ab],{type:'audio/mp4'}))}catch(e){}
var c=C&&ac();if(!c){elMode=true;loading=false;startBgm();return}
return new Promise(function(res,rej){var p=c.decodeAudioData(ab.slice(0),res,rej);if(p&&p.then)p.then(res,rej)}).then(function(b){buf=b;loading=false;startBgm()})})
.catch(function(){loading=false;elMode=true;startBgm()})}
function elPlay(){if(!el||!el.paused)return;el.volume=BGM_VOL;var p=el.play();if(p&&p.then)p.then(paint,function(){})}
function startBgm(){if(!on)return;
if(elMode||!buf){ // 还没解码好(或解码不了):先用 <audio> 顶上,只有在手势里才播得出来
if(bytes)elPlay();return}
var c=ac();if(!c)return;
if(c.state!=='running'){var r=c.resume&&c.resume();if(r&&r.then)r.then(function(){if(c.state==='running'&&!src)startBgm();paint()},function(){});return}
if(src)return;
var off=0;if(el&&!el.paused){off=el.currentTime%buf.duration;el.pause()} // 从 <audio> 接力,不从头来
var s=c.createBufferSource(),g=c.createGain();s.buffer=buf;s.loop=true;g.gain.value=BGM_VOL;s.connect(g);g.connect(c.destination);s.start(0,off);src=s;paint()}
function stopBgm(){if(src){try{src.stop()}catch(e){}src=null}if(el)el.pause();paint()}
function playingNow(){if(src)return ctx&&ctx.state==='running';return !!(el&&!el.paused)}
function armBgm(){['pointerdown','touchend','click','keydown'].forEach(function(t){document.addEventListener(t,function(){if(!on)return;unlock();loadBgm();startBgm()},{passive:true})})}
function tone(f,t0,dur,type,vol){var c=ac();if(!c)return;var o=c.createOscillator(),g=c.createGain(),t=c.currentTime+t0;o.type=type||'square';o.frequency.setValueAtTime(f,t);
g.gain.setValueAtTime(0.0001,t);g.gain.exponentialRampToValueAtTime(vol||0.22,t+0.012);g.gain.exponentialRampToValueAtTime(0.0001,t+dur);o.connect(g);g.connect(c.destination);o.start(t);o.stop(t+dur+0.03)}
function play(seq){if(!on)return;try{seq.forEach(function(n){tone(n[0],n[1],n[2],n[3],n[4])})}catch(e){}}
var S={
warm:function(){if(on)ac()},
click:function(){play([[740,0,0.045,'square',0.12]])},
coin:function(){play([[988,0,0.07,'square',0.25],[1319,0.07,0.32,'square',0.25]])},
ok:function(){play([[523,0,0.09],[659,0.09,0.09],[784,0.18,0.09],[1047,0.27,0.38,'square',0.26]])},
fail:function(){play([[196,0,0.1,'sawtooth',0.16],[147,0.1,0.26,'sawtooth',0.16]])},
levelup:function(){play([[659,0,0.08],[784,0.08,0.08],[988,0.16,0.08],[1319,0.24,0.08],[1568,0.32,0.4,'square',0.26]])},
enabled:function(){return on},
playing:function(){return src?'webaudio':(el&&!el.paused?'element':'')},
toggle:function(){on=!on;try{localStorage.setItem('sfx',on?'1':'0')}catch(e){}if(on){S.coin();loadBgm();startBgm()}else{stopBgm()}paint();return on}};
document.addEventListener('DOMContentLoaded',function(){if(on)loadBgm();armBgm();
var b=document.getElementById('sfxBtn');if(!b)return;
// 三态:🔊 在放 / 🔈 开着但还没放(浏览器拦了自动播放,点一下就放)/ 🔇 静音
var hasBgm=!!document.getElementById('bgm'); // 没放音乐文件时只有音效,喇叭只有开/关两态
paint=function(){var p=on&&(!hasBgm||playingNow());b.classList.toggle('off',!on);b.classList.toggle('idle',on&&!p);b.title=!on?{{T "声音:关"}}:(p?{{T "声音:开(点击静音)"}}:{{T "点一下开始播放"}})};paint();
b.addEventListener('click',function(){if(on&&!playingNow()){loadBgm();startBgm();return}S.toggle()})});
document.addEventListener('click',function(ev){var b=ev.target.closest('.btn,.tab,.amt-btn,.copy,.pill,.toggle button,.link');if(!b||b.disabled)return;ac();if(b.id!=='coinbtn')S.click()},true);
return S})();
(function(){
// 复制按钮(全站委托)
document.addEventListener('click',function(ev){var b=ev.target.closest('.copy');if(!b||b.id==='nextText')return;var src=b.dataset.copyFrom;var t=src?document.getElementById(src).value:b.dataset.copy;var o=b.textContent;
function done(){b.textContent={{T "已复制"}};setTimeout(function(){b.textContent=o},1500)}
(navigator.clipboard?navigator.clipboard.writeText(t):Promise.reject()).then(done,function(){var i=document.createElement('input');i.value=t;document.body.appendChild(i);i.select();try{document.execCommand('copy')}catch(e){}i.remove();done()})});
// 付款面板:轮询到账、回填/自报、成功态
window.initPayPanel=function(pp,onDone){
var code=pp.dataset.code,site=pp.dataset.site,amount=pp.dataset.amount,status=pp.dataset.status,stop=false,fails=0;
function success(amt){stop=true;if(window.sfx)sfx.ok();var sp=document.querySelector('.sprite');pp.innerHTML='<div class="center"><div class="sprite-wrap jump" style="margin:0 auto 8px">'+(sp?sp.outerHTML:'🥣')+'</div><div class="insert">★ THANK YOU ★</div><h2 class="center" style="justify-content:center">🙏 '+{{T "功德 +"}}+(amt||amount)+'</h2><p class="hint">'+{{T "你的名号和留言已刻上功德簿"}}+'</p><div class="btns"><a class="btn" href="'+site+'#feed" onclick="if(location.pathname===this.pathname){location.hash=\'feed\';location.reload();return false}">'+{{T "看看功德簿"}}+'</a><a class="btn sec" href="/d/'+code+'">'+{{T "分享卡片"}}+'</a></div></div>';if(onDone)onDone('paid')}
function setStatus(html){var st=pp.querySelector('#ppStatus');if(st)st.innerHTML=html}
function apply(j){if(!j||!j.status)return;if(j.status==='paid'){success(j.amount)}else if(j.status==='claimed'&&status!=='claimed'){status='claimed';setStatus('<div class="notice" style="margin-top:12px"><span class="spin-inline"></span> '+{{T "已提交,等站长确认后上榜。本页会自动更新。"}}+'</div>')}else if(['expired','failed','rejected','closed'].indexOf(j.status)>=0){stop=true;setStatus('<div class="notice bad" style="margin-top:12px">'+{{T "这笔施舍已"}}+({expired:{{T "过期"}},failed:{{T "下单失败"}},rejected:{{T "被站长拒绝"}},closed:{{T "关闭"}}})[j.status]+{{T ",请回到页面重新投币。"}}+'</div>')}}
function tick(){if(stop)return;fetch('/d/'+code+'/status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(j){fails=0;apply(j)}).catch(function(){if(++fails>=20)stop=true})}
var timer=setInterval(function(){if(stop){clearInterval(timer);return}tick()},3000);
pp.addEventListener('submit',function(ev){var f=ev.target.closest('.claimf');if(!f)return;ev.preventDefault();var fd=new FormData(f);fd.append('ajax','1');var btn=f.querySelector('button');btn.disabled=true;var err=f.querySelector('.claim-err');err.textContent='';
fetch(f.getAttribute('action'),{method:'POST',body:fd}).then(function(r){return r.json()}).then(function(j){btn.disabled=false;if(j.status==='paid'){success()}else if(j.ok&&j.status==='claimed'){apply({status:'claimed'})}else{if(window.sfx)sfx.fail();err.textContent=j.msg||{{T "没成功,再试试"}}}}).catch(function(){btn.disabled=false;if(window.sfx)sfx.fail();err.textContent={{T "网络开小差了"}}})});
return function(){stop=true;clearInterval(timer)}
};
var pp=document.getElementById('paypanel');if(pp)window.initPayPanel(pp);
})();
</script>
{{block "scripts" .}}{{end}}
</body>
</html>{{end}}
{{define "pager"}}{{if gt .Pages 1}}<nav class="pager" aria-label="{{T "分页"}}">
{{if .HasPrev}}<a href="{{.URL .Prev}}">{{T "‹ 上一页"}}</a>{{else}}<span class="dis">{{T "‹ 上一页"}}</span>{{end}}
{{range .Nums}}{{if eq . $.Page}}<span class="cur">{{.}}</span>{{else}}<a href="{{$.URL .}}">{{.}}</a>{{end}}{{end}}
{{if .HasNext}}<a href="{{.URL .Next}}">{{T "下一页 ›"}}</a>{{else}}<span class="dis">{{T "下一页 ›"}}</span>{{end}}
<span class="hint" style="margin-left:auto">{{.Page}}/{{.Pages}} {{T "页"}}</span>
</nav>{{end}}{{end}}