Files
crossword/site/index.html
T
plparkandClaude Opus 5 f771a6b3e5 Add a 휴게실 tank game tab under the literature list
The crossword shell only carried literature tabs. Add a second nav
section, 휴게실, holding a standalone canvas tank game: two stages
(fortress with 4 enemies, maze with 6), arrow keys to move, space to
fire, WebAudio for the shots and explosions.

The page shares nothing with the crossword engine, so the Dockerfile's
asset-hash stamp leaves it alone. Inside the shell's iframe a keydown
only lands once the frame has focus, so the canvas takes a tabindex and
the click handler focuses it; the start overlay says to click first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:01:04 +09:00

230 lines
8.1 KiB
HTML

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>가로세로 낱말풀이</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Gothic+A1:wght@300;500;700;900&display=swap" rel="stylesheet">
<style>
:root{
--ink:#12100E;
--panel:#171410;
--paper:#E7E1D3;
--amber:#E8B23C;
--vermilion:#C6362A;
--slate:#8A8272;
--bone:#D8D2C4;
--line:#2A251F;
--ui:"Gothic A1","Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",sans-serif;
--display:"Black Han Sans","Gothic A1","Apple SD Gothic Neo",sans-serif;
--rail:238px;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;background:var(--ink);color:var(--bone);
font-family:var(--ui);font-weight:300;
display:grid;grid-template-columns:var(--rail) minmax(0,1fr);
-webkit-text-size-adjust:100%;
}
/* ---------- 왼쪽 탭 ---------- */
.rail{
background:var(--panel);border-right:1px solid var(--line);
display:flex;flex-direction:column;min-height:0;overflow:auto;
}
.brand{padding:24px 20px 20px;border-bottom:2px solid var(--vermilion)}
.brand .mark{
font-size:10px;letter-spacing:.3em;color:var(--slate);
font-weight:700;text-transform:uppercase;margin-bottom:9px;
}
.brand h1{
font-family:var(--display);font-weight:400;margin:0;
font-size:26px;line-height:1.06;color:var(--paper);letter-spacing:-.01em;
}
.brand h1 em{font-style:normal;color:var(--vermilion);display:block}
.nav{padding:16px 0;flex:1 0 auto}
.nav .label{
font-size:10px;font-weight:900;letter-spacing:.28em;color:var(--slate);
padding:0 20px 10px;
}
.nav .label.rest{
margin-top:14px;padding-top:15px;border-top:1px solid var(--line);
}
.nav a{
display:block;text-decoration:none;color:inherit;
padding:12px 18px 12px 17px;border-left:3px solid transparent;
border-bottom:1px solid #221E19;transition:background .12s linear;
}
.nav a .kr{display:block;font-size:15px;font-weight:700;color:var(--bone)}
.nav a .og{display:block;font-size:10.5px;letter-spacing:.14em;color:var(--slate);margin-top:3px}
.nav a:hover{background:#1F1B16}
.nav a:hover .kr{color:var(--amber)}
.nav a[aria-current="page"]{background:var(--vermilion);border-left-color:var(--amber)}
.nav a[aria-current="page"] .kr,
.nav a[aria-current="page"] .og{color:#fff}
.nav a:focus-visible{outline:2px solid var(--amber);outline-offset:-2px}
.rail-foot{padding:16px 20px 22px;border-top:1px solid var(--line)}
.rail-foot a{
font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--slate);
text-decoration:none;border-bottom:1px solid transparent;
}
.rail-foot a:hover{color:var(--amber);border-bottom-color:var(--amber)}
/* ---------- 본 프레임 ---------- */
.stage{border:0;width:100%;height:100%;display:block;background:var(--ink)}
/* ---------- 좁은 화면: 위쪽 가로 탭 ---------- */
@media (max-width:820px){
body{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr)}
.rail{
border-right:0;border-bottom:2px solid var(--vermilion);
flex-direction:row;align-items:stretch;overflow:visible;
}
.brand{
padding:12px 14px;border-bottom:0;border-right:1px solid var(--line);
display:flex;flex-direction:column;justify-content:center;flex:none;
}
.brand .mark{display:none}
.brand h1{font-size:17px}
.brand h1 em{display:inline;margin-left:6px}
.nav{display:flex;padding:0;flex:1 1 auto;min-width:0}
.nav .label{display:none}
.nav a{
flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:center;
border-bottom:0;border-left:0;border-top:3px solid transparent;
padding:10px 12px;text-align:center;
}
.nav a[aria-current="page"]{border-left-color:transparent;border-top-color:var(--amber)}
.nav a .kr{font-size:13.5px}
.nav a .og{display:none}
.rail-foot{display:none}
}
/* 아주 좁은 화면: 탭이 셋 이상이면 상표를 접고 탭에 폭을 몰아 준다 */
@media (max-width:560px){
.brand{display:none}
.nav a{padding:10px 4px}
.nav a .kr{font-size:13px;white-space:nowrap}
.nav a .suffix{display:none} /* 탭이 다섯이면 나라 이름만 남긴다 */
}
@media (prefers-reduced-motion:reduce){
*{animation:none !important;transition:none !important}
}
</style>
</head>
<body>
<nav class="rail">
<div class="brand">
<div class="mark">Crossword</div>
<h1>가로세로<em>낱말풀이</em></h1>
</div>
<div class="nav">
<div class="label">문학 편</div>
<a href="korean.html" target="stage" data-key="korean">
<span class="kr">한국<span class="suffix"> 문학</span></span>
<span class="og">Korean literature</span>
</a>
<a href="russian.html" target="stage" data-key="russian">
<span class="kr">러시아<span class="suffix"> 문학</span></span>
<span class="og">Русская литература</span>
</a>
<a href="british.html" target="stage" data-key="british">
<span class="kr">영국<span class="suffix"> 문학</span></span>
<span class="og">British literature</span>
</a>
<a href="french.html" target="stage" data-key="french">
<span class="kr">프랑스<span class="suffix"> 문학</span></span>
<span class="og">Littérature française</span>
</a>
<a href="german.html" target="stage" data-key="german">
<span class="kr">독일<span class="suffix"> 문학</span></span>
<span class="og">Deutsche Literatur</span>
</a>
<a href="japanese.html" target="stage" data-key="japanese">
<span class="kr">일본<span class="suffix"> 문학</span></span>
<span class="og">日本文学</span>
</a>
<div class="label rest">휴게실</div>
<a href="tank.html" target="stage" data-key="tank">
<span class="kr">탱크게임</span>
<span class="og">Tank Battle</span>
</a>
</div>
<div class="rail-foot">
<a id="popout" href="korean.html" target="_blank" rel="noopener">새 창에서 열기 ↗</a>
</div>
</nav>
<!-- src 은 아래 스크립트가 해시를 보고 넣는다 (미리 넣으면 두 번 읽어들이게 된다) -->
<iframe class="stage" id="stage" name="stage" title="낱말풀이 게임판"></iframe>
<script>
(function(){
"use strict";
var PAGES = {
korean: {file:'korean.html', name:'한국 문학'},
russian: {file:'russian.html', name:'러시아 문학'},
british: {file:'british.html', name:'영국 문학'},
french: {file:'french.html', name:'프랑스 문학'},
german: {file:'german.html', name:'독일 문학'},
japanese:{file:'japanese.html', name:'일본 문학'},
tank: {file:'tank.html', name:'휴게실 · 탱크게임'}
};
var DEFAULT = 'korean';
var stage = document.getElementById('stage');
var popout = document.getElementById('popout');
var links = Array.prototype.slice.call(document.querySelectorAll('.nav a[data-key]'));
function keyFromHash(){
var k = decodeURIComponent(String(location.hash || '').replace(/^#\/?/, ''));
return PAGES[k] ? k : DEFAULT;
}
function apply(){
var key = keyFromHash();
var page = PAGES[key];
// 같은 페이지면 다시 읽어들이지 않는다 (풀던 내용 유지)
if (stage.getAttribute('src') !== page.file) stage.setAttribute('src', page.file);
links.forEach(function(a){
if (a.dataset.key === key) a.setAttribute('aria-current', 'page');
else a.removeAttribute('aria-current');
});
popout.setAttribute('href', page.file);
document.title = '가로세로 낱말풀이 · ' + page.name;
}
links.forEach(function(a){
a.addEventListener('click', function(ev){
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) return;
ev.preventDefault(); // 스크립트가 살아 있으면 해시로 처리
var k = a.dataset.key;
if (keyFromHash() === k && location.hash) return;
location.hash = '#' + k;
if (!location.hash) apply(); // 해시 변화가 없을 때를 위한 보험
});
});
window.addEventListener('hashchange', apply);
if (!location.hash) {
try { history.replaceState(null, '', '#' + DEFAULT); } catch(_) {}
}
apply();
})();
</script>
</body>
</html>