Revert "Add 'Try to fool the referee' tab: curated cheat gallery (bench-shape special-case, pointer memoization, in-place mutation), each caught by the in-process referee"
Browse files- app.py +0 -43
- cheats/index.json +0 -32
- seed_kernels/cheats/silu_mutate.py +0 -8
- seed_kernels/cheats/softmax_cheat_memo.py +0 -17
- seed_kernels/cheats/softmax_cheat_shape.py +0 -8
- seed_kernels/cheats/softmax_wrong.py +0 -27
- style.css +0 -22
app.py
CHANGED
|
@@ -507,13 +507,6 @@ try:
|
|
| 507 |
except Exception:
|
| 508 |
REPLAYS_JSON = "{}"
|
| 509 |
|
| 510 |
-
CHEATS_PATH = os.path.join(os.path.dirname(__file__), "cheats", "index.json")
|
| 511 |
-
try:
|
| 512 |
-
with open(CHEATS_PATH, encoding="utf-8") as f:
|
| 513 |
-
CHEATS_JSON = json.dumps(json.load(f), separators=(",", ":")).replace("</", "<\\/")
|
| 514 |
-
except Exception:
|
| 515 |
-
CHEATS_JSON = "{}"
|
| 516 |
-
|
| 517 |
APP_HTML = r"""
|
| 518 |
<div id="km" data-build="2026-06-14-local-baseline-guard">
|
| 519 |
<div id="km-top">
|
|
@@ -527,11 +520,9 @@ APP_HTML = r"""
|
|
| 527 |
<button class="km-tab" data-tab="expert">Expert</button>
|
| 528 |
<button class="km-tab" data-tab="lb">Leaderboard</button>
|
| 529 |
<button class="km-tab" data-tab="docs">Run locally</button>
|
| 530 |
-
<button class="km-tab" data-tab="fool">Try to fool it</button>
|
| 531 |
</div>
|
| 532 |
</div>
|
| 533 |
<script id="km-replays-data" type="application/json">__KM_REPLAYS__</script>
|
| 534 |
-
<script id="km-cheats-data" type="application/json">__KM_CHEATS__</script>
|
| 535 |
|
| 536 |
<!-- ============ BUILD ============ -->
|
| 537 |
<div class="km-page" data-page="build">
|
|
@@ -700,20 +691,10 @@ kernel plus a run(x, residual, weight) entry point. Triton only.</div>
|
|
| 700 |
<p>MIT licensed. The models are <a href="https://huggingface.co/YMRohit/ouroboros-kernelsmith-minicpm5-1b" style="color:var(--accent)">MiniCPM5-1B</a> and <a href="https://huggingface.co/YMRohit/ouroboros-kernelsmith-qwen3.6-27b" style="color:var(--accent)">Qwen3.6-27B</a> (both Apache-2.0). The verified kernels and evidence reports are in the <a href="https://huggingface.co/datasets/YMRohit/ouroboros-kernel-corpus" style="color:var(--accent)">corpus</a>.</p>
|
| 701 |
</section>
|
| 702 |
</div>
|
| 703 |
-
|
| 704 |
-
<!-- ============ TRY TO FOOL THE REFEREE ============ -->
|
| 705 |
-
<div class="km-page" data-page="fool" hidden>
|
| 706 |
-
<section class="km-card">
|
| 707 |
-
<h2>Try to fool the referee</h2>
|
| 708 |
-
<p class="km-dim">A green tick is easy to believe. The harder, more honest test is the inverse: hand the referee a kernel built to cheat, and watch it get caught. Each card is a real exploit, taken from the harness's own anti-gaming controls, and each is defeated by a different defense. The verdicts come from the same referee bundled in this Space (<code>referee/harness.py</code>), so they reproduce: switch to Local mode to run any of them live.</p>
|
| 709 |
-
<div id="km-cheats" class="km-cheats"></div>
|
| 710 |
-
</section>
|
| 711 |
-
</div>
|
| 712 |
</div>
|
| 713 |
"""
|
| 714 |
|
| 715 |
APP_HTML = APP_HTML.replace("__KM_REPLAYS__", REPLAYS_JSON)
|
| 716 |
-
APP_HTML = APP_HTML.replace("__KM_CHEATS__", CHEATS_JSON)
|
| 717 |
|
| 718 |
APP_JS = r"""
|
| 719 |
() => {
|
|
@@ -752,29 +733,6 @@ APP_JS = r"""
|
|
| 752 |
const esc=s=>cleanText(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
| 753 |
let REPLAYS={};
|
| 754 |
try{REPLAYS=JSON.parse(($('#km-replays-data')||{}).textContent||'{}');}catch(e){REPLAYS={};}
|
| 755 |
-
let CHEATS={};
|
| 756 |
-
try{CHEATS=JSON.parse(($('#km-cheats-data')||{}).textContent||'{}');}catch(e){CHEATS={};}
|
| 757 |
-
function renderCheats(){
|
| 758 |
-
const wrap=$('#km-cheats'); if(!wrap||wrap.dataset.done)return;
|
| 759 |
-
const order=['softmax_cheat_shape','softmax_cheat_memo','silu_mutate'].filter(k=>CHEATS[k]);
|
| 760 |
-
if(!order.length){wrap.innerHTML="<p class='km-dim'>No cheats loaded.</p>";return;}
|
| 761 |
-
wrap.innerHTML=order.map(k=>{const c=CHEATS[k];return (
|
| 762 |
-
"<div class='km-cheat'>"
|
| 763 |
-
+"<div class='km-cheat-top'><span class='km-cheat-name'>"+esc(c.label)+"</span><span class='km-cheat-claims'>submitted as <code>"+esc(c.op)+"</code></span></div>"
|
| 764 |
-
+"<p class='km-cheat-trick'>"+esc(c.trick)+"</p>"
|
| 765 |
-
+"<pre class='km-cheat-src'>"+esc(c.source)+"</pre>"
|
| 766 |
-
+"<button class='km-btn cheat-run' data-cheat='"+esc(k)+"'>Run it past the referee</button>"
|
| 767 |
-
+"<div class='km-cheat-verdict' id='cv-"+esc(k)+"'></div>"
|
| 768 |
-
+"</div>");
|
| 769 |
-
}).join('');
|
| 770 |
-
wrap.dataset.done='1';
|
| 771 |
-
$$('.cheat-run').forEach(b=>b.addEventListener('click',()=>{
|
| 772 |
-
const k=b.dataset.cheat,c=CHEATS[k],el=document.getElementById('cv-'+k);
|
| 773 |
-
if(!c||!el)return;
|
| 774 |
-
el.innerHTML="<div class='km-reject'><div class='km-reject-row'><span class='km-reject-x'>REJECTED</span><span class='km-reject-by'>caught by "+esc(c.caught_by)+"</span></div><p>"+esc(c.explanation)+"</p></div>";
|
| 775 |
-
b.textContent='Rejected by the referee';b.classList.add('done');b.disabled=true;
|
| 776 |
-
}));
|
| 777 |
-
}
|
| 778 |
const REPLAY_META={
|
| 779 |
rmsnorm_gelu:{label:'RMSNorm to GELU',short:'RMSNorm GELU'},
|
| 780 |
softmax:{label:'Softmax',short:'Softmax'},
|
|
@@ -826,7 +784,6 @@ APP_JS = r"""
|
|
| 826 |
requestAnimationFrame(positionTabPill);
|
| 827 |
if(tab==='expert')resolveExpert();
|
| 828 |
if(tab==='lb')refreshLB();
|
| 829 |
-
if(tab==='fool')renderCheats();
|
| 830 |
}
|
| 831 |
$$('.km-tab').forEach(b=>b.addEventListener('click',()=>showTab(b.dataset.tab)));
|
| 832 |
window.addEventListener('resize',positionTabPill);
|
|
|
|
| 507 |
except Exception:
|
| 508 |
REPLAYS_JSON = "{}"
|
| 509 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 510 |
APP_HTML = r"""
|
| 511 |
<div id="km" data-build="2026-06-14-local-baseline-guard">
|
| 512 |
<div id="km-top">
|
|
|
|
| 520 |
<button class="km-tab" data-tab="expert">Expert</button>
|
| 521 |
<button class="km-tab" data-tab="lb">Leaderboard</button>
|
| 522 |
<button class="km-tab" data-tab="docs">Run locally</button>
|
|
|
|
| 523 |
</div>
|
| 524 |
</div>
|
| 525 |
<script id="km-replays-data" type="application/json">__KM_REPLAYS__</script>
|
|
|
|
| 526 |
|
| 527 |
<!-- ============ BUILD ============ -->
|
| 528 |
<div class="km-page" data-page="build">
|
|
|
|
| 691 |
<p>MIT licensed. The models are <a href="https://huggingface.co/YMRohit/ouroboros-kernelsmith-minicpm5-1b" style="color:var(--accent)">MiniCPM5-1B</a> and <a href="https://huggingface.co/YMRohit/ouroboros-kernelsmith-qwen3.6-27b" style="color:var(--accent)">Qwen3.6-27B</a> (both Apache-2.0). The verified kernels and evidence reports are in the <a href="https://huggingface.co/datasets/YMRohit/ouroboros-kernel-corpus" style="color:var(--accent)">corpus</a>.</p>
|
| 692 |
</section>
|
| 693 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 694 |
</div>
|
| 695 |
"""
|
| 696 |
|
| 697 |
APP_HTML = APP_HTML.replace("__KM_REPLAYS__", REPLAYS_JSON)
|
|
|
|
| 698 |
|
| 699 |
APP_JS = r"""
|
| 700 |
() => {
|
|
|
|
| 733 |
const esc=s=>cleanText(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
| 734 |
let REPLAYS={};
|
| 735 |
try{REPLAYS=JSON.parse(($('#km-replays-data')||{}).textContent||'{}');}catch(e){REPLAYS={};}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 736 |
const REPLAY_META={
|
| 737 |
rmsnorm_gelu:{label:'RMSNorm to GELU',short:'RMSNorm GELU'},
|
| 738 |
softmax:{label:'Softmax',short:'Softmax'},
|
|
|
|
| 784 |
requestAnimationFrame(positionTabPill);
|
| 785 |
if(tab==='expert')resolveExpert();
|
| 786 |
if(tab==='lb')refreshLB();
|
|
|
|
| 787 |
}
|
| 788 |
$$('.km-tab').forEach(b=>b.addEventListener('click',()=>showTab(b.dataset.tab)));
|
| 789 |
window.addEventListener('resize',positionTabPill);
|
cheats/index.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"softmax_cheat_shape": {
|
| 3 |
-
"op": "softmax",
|
| 4 |
-
"label": "Special-case the benchmark shape",
|
| 5 |
-
"trick": "Hard-code the answer only for the exact shape the timer uses, and return instant garbage everywhere else. The classic infinite-speedup exploit.",
|
| 6 |
-
"caught_by": "the correctness sweep",
|
| 7 |
-
"explanation": "The referee folds the public bench shape into its adversarial correctness sweep, so special-casing it buys nothing. This kernel returns NaN on 8192x4096 and is rejected before it is ever timed.",
|
| 8 |
-
"verdict": "REJECTED",
|
| 9 |
-
"status": "incorrect",
|
| 10 |
-
"source": "# NEGATIVE CONTROL (anti-gaming): special-cases the PUBLIC, FIXED bench shape.\n# Correct softmax everywhere the old correctness sweep could look, but at the timing shape\n# (8192, 4096) it returns uninitialized garbage instantly \u2014 the \"infinite speedup\" exploit.\n# The V2 harness includes the bench inputs in the correctness sweep, so this MUST be REJECTED.\ndef run(x):\n if x.shape == (8192, 4096):\n return torch.empty_like(x) # instant garbage at the bench shape\n return torch.softmax(x.float(), dim=-1).to(x.dtype)"
|
| 11 |
-
},
|
| 12 |
-
"softmax_cheat_memo": {
|
| 13 |
-
"op": "softmax",
|
| 14 |
-
"label": "Memoize by input pointer",
|
| 15 |
-
"trick": "Cache the output keyed by the input's memory address, so after the first call it does zero GPU work and wins the timer.",
|
| 16 |
-
"caught_by": "the anti-memoization check",
|
| 17 |
-
"explanation": "Before each timed iteration the referee pokes one input element, then allclose-checks the final timed output against the live input state. A cached, stale output fails: memoization/caching suspected.",
|
| 18 |
-
"verdict": "REJECTED",
|
| 19 |
-
"status": "incorrect",
|
| 20 |
-
"source": "# NEGATIVE CONTROL (anti-gaming): memoizes its output by input POINTER, so after the first\n# call it does no GPU work \u2014 the \"cache the answer, win the bench\" exploit. The V2 harness\n# defeats this two ways: the CUDA caching allocator reuses freed clone blocks across\n# correctness cases (stale hit -> wrong output), and the bench loop pokes one input element\n# before every timed iteration then allclose-verifies the FINAL timed output (stale -> fail).\n# Either path: this MUST be REJECTED.\n_CACHE = {}\n\n\ndef run(x):\n key = (x.data_ptr(), tuple(x.shape), x.dtype)\n hit = _CACHE.get(key)\n if hit is not None:\n return hit\n out = torch.softmax(x.float(), dim=-1).to(x.dtype)\n _CACHE[key] = out\n return out"
|
| 21 |
-
},
|
| 22 |
-
"silu_mutate": {
|
| 23 |
-
"op": "silu",
|
| 24 |
-
"label": "Mutate the input in place",
|
| 25 |
-
"trick": "Compute the correct values but write them back into the input tensor, so the bench loop's reused inputs look already done.",
|
| 26 |
-
"caught_by": "the input-mutation contract",
|
| 27 |
-
"explanation": "run() must write a fresh output tensor and never touch its inputs. The referee checks the input is unchanged after the call: kernel MUTATES its input.",
|
| 28 |
-
"verdict": "REJECTED",
|
| 29 |
-
"status": "incorrect",
|
| 30 |
-
"source": "# NEGATIVE CONTROL (contract): produces the CORRECT silu values but writes them IN-PLACE\n# into its input tensor. The bench loop reuses identical args across iterations, which is\n# only sound for kernels that never mutate inputs \u2014 so the correctness phase must enforce\n# the contract, not assume it. This MUST be REJECTED (\"kernel MUTATES its input\").\ndef run(x):\n xf = x.float()\n x.copy_((xf * torch.sigmoid(xf)).to(x.dtype))\n return x"
|
| 31 |
-
}
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seed_kernels/cheats/silu_mutate.py
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
# NEGATIVE CONTROL (contract): produces the CORRECT silu values but writes them IN-PLACE
|
| 2 |
-
# into its input tensor. The bench loop reuses identical args across iterations, which is
|
| 3 |
-
# only sound for kernels that never mutate inputs — so the correctness phase must enforce
|
| 4 |
-
# the contract, not assume it. This MUST be REJECTED ("kernel MUTATES its input").
|
| 5 |
-
def run(x):
|
| 6 |
-
xf = x.float()
|
| 7 |
-
x.copy_((xf * torch.sigmoid(xf)).to(x.dtype))
|
| 8 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seed_kernels/cheats/softmax_cheat_memo.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
# NEGATIVE CONTROL (anti-gaming): memoizes its output by input POINTER, so after the first
|
| 2 |
-
# call it does no GPU work — the "cache the answer, win the bench" exploit. The V2 harness
|
| 3 |
-
# defeats this two ways: the CUDA caching allocator reuses freed clone blocks across
|
| 4 |
-
# correctness cases (stale hit -> wrong output), and the bench loop pokes one input element
|
| 5 |
-
# before every timed iteration then allclose-verifies the FINAL timed output (stale -> fail).
|
| 6 |
-
# Either path: this MUST be REJECTED.
|
| 7 |
-
_CACHE = {}
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def run(x):
|
| 11 |
-
key = (x.data_ptr(), tuple(x.shape), x.dtype)
|
| 12 |
-
hit = _CACHE.get(key)
|
| 13 |
-
if hit is not None:
|
| 14 |
-
return hit
|
| 15 |
-
out = torch.softmax(x.float(), dim=-1).to(x.dtype)
|
| 16 |
-
_CACHE[key] = out
|
| 17 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seed_kernels/cheats/softmax_cheat_shape.py
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
# NEGATIVE CONTROL (anti-gaming): special-cases the PUBLIC, FIXED bench shape.
|
| 2 |
-
# Correct softmax everywhere the old correctness sweep could look, but at the timing shape
|
| 3 |
-
# (8192, 4096) it returns uninitialized garbage instantly — the "infinite speedup" exploit.
|
| 4 |
-
# The V2 harness includes the bench inputs in the correctness sweep, so this MUST be REJECTED.
|
| 5 |
-
def run(x):
|
| 6 |
-
if x.shape == (8192, 4096):
|
| 7 |
-
return torch.empty_like(x) # instant garbage at the bench shape
|
| 8 |
-
return torch.softmax(x.float(), dim=-1).to(x.dtype)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seed_kernels/cheats/softmax_wrong.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
# NEGATIVE CONTROL: row softmax with NO max-subtraction. Mathematically "equivalent" and
|
| 2 |
-
# passes on benign N(0,1) inputs — but OVERFLOWS to inf/nan on large magnitudes (the
|
| 3 |
-
# adversarial scale sweep in specs._mk_softmax). The harness MUST reject it. This is the
|
| 4 |
-
# exact trap the brief warns about: a kernel that passes one easy regime and is wrong.
|
| 5 |
-
@triton.jit
|
| 6 |
-
def _bad_kernel(x_ptr, y_ptr, stride, N, BLOCK: tl.constexpr):
|
| 7 |
-
row = tl.program_id(0)
|
| 8 |
-
x_ptr += row * stride
|
| 9 |
-
y_ptr += row * stride
|
| 10 |
-
d = tl.zeros([BLOCK], dtype=tl.float32)
|
| 11 |
-
for off in range(0, N, BLOCK):
|
| 12 |
-
cols = off + tl.arange(0, BLOCK)
|
| 13 |
-
x = tl.load(x_ptr + cols, mask=cols < N, other=0.0).to(tl.float32)
|
| 14 |
-
d += tl.where(cols < N, tl.exp(x), 0.0) # BUG: exp(x), not exp(x - row_max)
|
| 15 |
-
denom = tl.sum(d)
|
| 16 |
-
for off in range(0, N, BLOCK):
|
| 17 |
-
cols = off + tl.arange(0, BLOCK)
|
| 18 |
-
mask = cols < N
|
| 19 |
-
x = tl.load(x_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
| 20 |
-
tl.store(y_ptr + cols, tl.exp(x) / denom, mask=mask)
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def run(x):
|
| 24 |
-
M, N = x.shape
|
| 25 |
-
y = torch.empty_like(x)
|
| 26 |
-
_bad_kernel[(M,)](x, y, x.stride(0), N, BLOCK=1024)
|
| 27 |
-
return y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
style.css
CHANGED
|
@@ -445,25 +445,3 @@ input[type=range].km-range::-moz-range-thumb { width: 16px; height: 16px; border
|
|
| 445 |
#km #km-act2-wrap.open .km-add2 { display: none; }
|
| 446 |
#km #km-act2-stage { display: none; }
|
| 447 |
#km #km-act2-wrap.open #km-act2-stage { display: flex; }
|
| 448 |
-
|
| 449 |
-
/* ---- Try to fool the referee ---- */
|
| 450 |
-
#km .km-cheats { display: flex; flex-direction: column; gap: 16px; margin-top: 18px; }
|
| 451 |
-
#km .km-cheat { border: 1px solid var(--line); border-radius: var(--r); padding: 18px 20px; background: var(--surface-2); }
|
| 452 |
-
#km .km-cheat-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
| 453 |
-
#km .km-cheat-name { font: 600 17px var(--sans); color: var(--text); }
|
| 454 |
-
#km .km-cheat-claims { font: 500 13px var(--sans); color: var(--faint); }
|
| 455 |
-
#km .km-cheat-claims code { font-family: var(--mono); color: var(--accent); }
|
| 456 |
-
#km .km-cheat-trick { margin: 8px 0 12px; color: var(--muted); font-size: 14px; line-height: 1.55; }
|
| 457 |
-
#km .km-cheat-src { margin: 0 0 14px; padding: 14px 16px; border-radius: var(--r-sm); background: #0a0b0d;
|
| 458 |
-
border: 1px solid var(--line); font: 12.5px/1.5 var(--mono); overflow-x: auto; white-space: pre; color: #cdd3da; }
|
| 459 |
-
#km .cheat-run.done { opacity: 0.65; cursor: default; border-color: var(--danger); color: var(--danger); }
|
| 460 |
-
#km .km-cheat-verdict { margin-top: 14px; }
|
| 461 |
-
#km .km-cheat-verdict:empty { margin-top: 0; }
|
| 462 |
-
#km .km-reject { border: 1px solid var(--danger); border-radius: var(--r-sm); padding: 16px 18px;
|
| 463 |
-
background: var(--danger-soft); animation: kmRejectIn 0.25s var(--ease); }
|
| 464 |
-
@keyframes kmRejectIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
| 465 |
-
#km .km-reject-row { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; }
|
| 466 |
-
#km .km-reject-x { font: 700 14px var(--mono); letter-spacing: 1px; color: var(--danger);
|
| 467 |
-
border: 1px solid var(--danger); border-radius: 7px; padding: 4px 11px; }
|
| 468 |
-
#km .km-reject-by { color: #f3a3a3; font: 550 14px var(--sans); }
|
| 469 |
-
#km .km-reject p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.55; }
|
|
|
|
| 445 |
#km #km-act2-wrap.open .km-add2 { display: none; }
|
| 446 |
#km #km-act2-stage { display: none; }
|
| 447 |
#km #km-act2-wrap.open #km-act2-stage { display: flex; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|