/* Galmuri11 (SIL OFL 1.1, Lee Minseo) is drawn for 11px: every pixel label uses exactly that size. */
@font-face{font-family:"Galmuri11";font-weight:400;font-display:swap;src:url('https://cdn.jsdelivr.net/npm/galmuri@2.40.3/dist/Galmuri11.woff2') format('woff2')}
:root{font-family:Arial,"Apple SD Gothic Neo","Noto Sans KR",sans-serif;color:#202223;background:#202223;font-synthesis:none;--px:"Galmuri11",ui-monospace,Menlo,monospace}
*{box-sizing:border-box}
/* The canvas, stage and background carry pointer handlers too, so the tap flash is
   removed globally; every control has its own :active press instead. */
html{-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%}
html,body{margin:0;width:100%;height:100%;overflow:hidden;overscroll-behavior:none}
button,a{touch-action:manipulation}
button{font:inherit;cursor:pointer;border:0;border-radius:0;background:none;color:inherit;min-height:44px;position:relative}
button:disabled{opacity:.28!important;cursor:default}
button:focus-visible,a:focus-visible{outline:2px solid currentColor;outline-offset:4px}
svg{fill:none;stroke:currentColor;stroke-width:1.25;stroke-linecap:round;stroke-linejoin:round}
/* Control glyphs are drawn on a 12px grid and shown only at 12px or 24px. */
svg.px{fill:currentColor;stroke:none;shape-rendering:crispEdges}
canvas{display:block;width:100%;height:100%}
[hidden]{display:none!important}
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* Cabinet colours follow the selected ink. Bezel, key shade and engraved lines are
   mixed from it, so every material keeps one consistent machine. */
.app{--surface:#202223;--mark:#f8f8f5;--bezel:color-mix(in srgb,var(--surface) 70%,#000);--panel:color-mix(in srgb,var(--surface) 58%,#000);--shade:color-mix(in srgb,var(--surface) 45%,#000);--line:color-mix(in srgb,var(--mark) 26%,var(--surface));--engrave:color-mix(in srgb,var(--mark) 52%,var(--bezel));--glass:color-mix(in srgb,var(--surface) 42%,#000);--glass-ink:#f1f1ea;--inset:40px;--band-top:22px;--band-bottom:14px;--top:calc(100px + env(safe-area-inset-top));--foot:calc(150px + env(safe-area-inset-bottom));position:relative;isolation:isolate;width:100%;height:100dvh;overflow:hidden;touch-action:pinch-zoom;background:var(--surface);color:var(--mark);transition:background-color .12s steps(2),color .12s steps(2)}
.app[data-material=red]{--surface:#951102}
.app[data-material=soap]{--surface:#f8f8f5;--mark:#202223;--bezel:#dcddd6;--panel:#d0d1c9;--shade:#b3b5ad;--line:#a9aba3;--engrave:#6f726b;--glass:#2b2d2c}
.app,.app *{user-select:none;-webkit-user-select:none;-webkit-user-drag:none;-webkit-touch-callout:none}
.app:before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;background:url('/assets/mineral-background-v1.jpg') center/cover;background-image:image-set(url('/assets/mineral-background-v1.webp') type('image/webp'),url('/assets/mineral-background-v1.jpg') type('image/jpeg'));opacity:.065}
.app[data-material=soap]:before{opacity:.09}

/* Marquee */
.top{position:absolute;left:max(var(--inset),env(safe-area-inset-left,0px));right:max(var(--inset),env(safe-area-inset-right,0px));top:calc(22px + env(safe-area-inset-top));display:flex;align-items:center;justify-content:space-between;z-index:5}
.top-logo{font-size:30px;font-weight:900;letter-spacing:-.085em;line-height:1}
.shop-link{display:flex;align-items:center;gap:10px;min-height:44px;color:inherit;text-decoration:none;font-size:13px;letter-spacing:.06em}
.shop-link span{opacity:.6;font-size:15px}
.shop-link:active{translate:0 1px;opacity:.7}

/* Screen: the paper is never filtered. Bezel and engraving live outside it. */
.screen{position:absolute;left:50%;top:calc(var(--top) + var(--band-top) + (100dvh - var(--top) - var(--foot) - var(--band-top) - var(--band-bottom))/2);transform:translate(-50%,-50%);height:min(calc(100dvh - var(--top) - var(--foot) - var(--band-top) - var(--band-bottom)),calc((100vw - 80px)*1.414286),877px);aspect-ratio:210/297;border-radius:3px;box-shadow:0 0 0 14px var(--bezel),0 -8px 0 14px var(--bezel),0 0 0 15px var(--line),0 -8px 0 15px var(--line),0 22px 44px #00000024}
.screen-label{position:absolute;left:0;right:0;top:-20px;display:flex;justify-content:space-between;font:11px/12px var(--px);letter-spacing:.08em;color:var(--engrave);pointer-events:none}
.stage{position:absolute;inset:0;overflow:hidden;touch-action:none;cursor:crosshair;background:#f8f8f5;animation:power-on .42s steps(7) .08s backwards}
.stage:before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;box-shadow:inset 0 2px 4px #22222226,inset 0 0 0 1px #3333331a}
.stage:after{content:"";position:absolute;inset:0;pointer-events:none;opacity:.05;background:url('/assets/washi-paper-v1.webp') center/cover}
@keyframes power-on{0%{clip-path:inset(50% 0 50% 0);filter:brightness(1.6)}30%{clip-path:inset(49.6% 0 49.6% 0);filter:brightness(1.6)}100%{clip-path:inset(0);filter:none}}
.empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;color:#6f726b}
.empty p{margin:0;font:11px/1 var(--px);letter-spacing:.14em;animation:attract 1.2s steps(1) infinite}
.empty.hidden{visibility:hidden}
.empty.hidden p{animation:none}
@keyframes attract{0%,62%{opacity:1}63%,100%{opacity:0}}
.wind-mode{cursor:grab}
.wind-mode:active{cursor:grabbing}
.stylus-mode{cursor:crosshair}
.stylus-tip{position:absolute;left:0;top:0;width:6px;height:6px;border:1px solid #fff;border-radius:50%;box-shadow:0 0 0 1px #30332f;transform:translate(-50%,-50%);pointer-events:none;z-index:3}

/* Messages: a small readout over the bottom of the screen, only while there is something to say. */
.lcd{position:absolute;left:50%;bottom:calc(var(--foot) + var(--band-bottom) + 10px);transform:translateX(-50%);max-width:calc(100vw - 32px);pointer-events:none;z-index:4}
#work-status,#material-feedback{max-width:100%;padding:4px 8px;border-radius:2px;background:var(--glass);color:var(--glass-ink);box-shadow:0 0 0 1px var(--line);font:11px/14px var(--px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#work-status:empty{display:none}
#material-feedback{display:none}
#material-feedback.visible{display:block}
#work-status:not(:empty)~#material-feedback{display:none}

/* Control deck */
.toolbar{position:absolute;left:50%;bottom:calc(72px + env(safe-area-inset-bottom));transform:translateX(-50%);display:flex;gap:16px;align-items:center}
.palette,.shaping{display:flex;gap:8px}
.shaping{padding-left:16px;border-left:1px solid var(--line)}
.toolbar button{display:grid;place-items:center;width:44px;height:48px;padding:0}
/* Every deck key is the same square cap; engaged keys latch down and invert.
   Ink keys carry a flat square of their colour, tool keys a 12px-grid glyph. */
.key:before{content:"";position:absolute;left:50%;top:50%;width:36px;height:34px;margin:-19px 0 0 -18px;border-radius:2px;background:var(--bezel);box-shadow:0 0 0 1px var(--line),0 3px 0 var(--shade);transition:translate 40ms steps(1),box-shadow 40ms steps(1)}
.key svg,.key i{position:relative;margin-top:-4px;transition:translate 40ms steps(1)}
.key svg{width:24px;height:24px}
.key i{display:block;width:14px;height:14px;background:var(--ink);box-shadow:0 0 0 1px color-mix(in srgb,currentColor 45%,transparent)}
.swatch[data-ink=black]{--ink:#202223}
.swatch[data-ink=red]{--ink:#951102}
.swatch[data-ink=soap]{--ink:#f8f8f5}
.key[aria-pressed=true]:before{background:var(--mark);box-shadow:0 0 0 1px var(--mark)}
/* While wind or stylus is engaged, the chosen ink stays latched but not lit, so
   exactly one key reads as the active tool. */
.app:not([data-tool=ink]) .swatch[aria-pressed=true]{color:var(--mark)}
.app:not([data-tool=ink]) .swatch[aria-pressed=true]:before{background:var(--bezel);box-shadow:0 0 0 1px var(--mark)}
.key[aria-pressed=true]{color:var(--surface)}
.key[aria-pressed=true]:before,.key:active:before,.key[aria-pressed=true] svg,.key:active svg,.key[aria-pressed=true] i,.key:active i{translate:0 3px}
.key:active:before{box-shadow:0 0 0 1px var(--line)}

.bottom-actions{position:absolute;left:50%;bottom:calc(10px + env(safe-area-inset-bottom));transform:translateX(-50%);display:flex;align-items:center;gap:4px}
/* Function keys and the START-style pill share one press: 2–3px straight down. */
.fn{width:40px;padding:0;display:grid;place-items:center}
.fn:before{content:"";position:absolute;left:50%;top:50%;width:32px;height:24px;margin:-13px 0 0 -16px;border-radius:2px;background:var(--bezel);box-shadow:0 0 0 1px var(--line),0 2px 0 var(--shade)}
.fn svg{position:relative;width:12px;height:12px;margin-top:-2px}
.fn:active:not(:disabled):before,.fn:active:not(:disabled) svg{translate:0 2px}
.fn:active:not(:disabled):before{box-shadow:0 0 0 1px var(--line)}
.start{isolation:isolate;padding:0 16px;color:var(--surface);font:11px/1 var(--px);letter-spacing:.04em;white-space:nowrap}
.start:before{content:"";position:absolute;inset:8px 0 11px;z-index:-1;border-radius:13px;background:var(--mark);box-shadow:0 3px 0 var(--shade)}
.start:active:not(:disabled){translate:0 3px}
.start:active:not(:disabled):before{box-shadow:none}
#finish{margin-left:8px}
.fn-text{padding:0 2px;font:11px/1 var(--px);letter-spacing:.04em;white-space:nowrap}
.fn-text:after{content:"";position:absolute;left:2px;right:2px;bottom:12px;height:2px;background:var(--line)}
.fn-text[aria-pressed=true]:after{background:var(--mark)}
.fn-text:active:not(:disabled){translate:0 1px}

.brand-mark{position:absolute;bottom:calc(46px + env(safe-area-inset-bottom));min-height:44px;display:grid;place-items:center;padding:0;z-index:5}
.brand-left{left:max(var(--inset),env(safe-area-inset-left,0px));min-width:44px}
.brand-right{right:max(var(--inset),env(safe-area-inset-right,0px));pointer-events:none}
/* The cross marks are the cabinet's two pads: the brand geometry unchanged, set
   large on the deck and centred between its two rows. */
.brand-mark svg{width:80px;height:40px;fill:currentColor;stroke:none}
/* Control deck: one slab under the screen that holds keys, pads and actions. */
.deck-panel{position:absolute;left:0;right:0;bottom:0;height:var(--foot);background:var(--panel);box-shadow:0 -1px 0 var(--line),inset 0 1px 0 color-mix(in srgb,var(--mark) 8%,transparent);pointer-events:none}
#open-gallery:active:not(:disabled){translate:0 1px}

/* Result scenes */
.is-finished .screen,.is-finished .toolbar,.is-finished .bottom-actions{visibility:hidden;pointer-events:none}
.app[data-view=share] .lcd,.app[data-view=gallery] .lcd{display:none}
.scene{position:absolute;inset:0;animation:scene-cut .16s steps(2) both}
@keyframes scene-cut{from{opacity:0}to{opacity:1}}
/* The print leaves the machine like paper from an output slot. */
.print-image{animation:print-out .36s steps(6) .06s both}
@keyframes print-out{from{clip-path:inset(0 0 100% 0);translate:0 -14px}to{clip-path:inset(0);translate:0 0}}
.print-image canvas{object-fit:contain}
/* Every result scene uses the cabinet: content in the screen slot, controls on
   the deck. The slot has exactly the live screen's geometry. */
.slot{position:absolute;left:50%;top:calc(var(--top) + var(--band-top) + (100dvh - var(--top) - var(--foot) - var(--band-top) - var(--band-bottom))/2);transform:translate(-50%,-50%);height:min(calc(100dvh - var(--top) - var(--foot) - var(--band-top) - var(--band-bottom)),calc((100vw - 80px)*1.414286),877px);aspect-ratio:210/297}
.slot canvas{object-fit:contain}
.deck-row{position:absolute;left:50%;transform:translateX(-50%);display:flex;align-items:center;justify-content:center;gap:12px;white-space:nowrap;z-index:5}
.deck-row-1{bottom:calc(72px + env(safe-area-inset-bottom));min-height:48px;gap:16px}
.deck-row-2{bottom:calc(10px + env(safe-area-inset-bottom));min-height:44px}
.deck-divider{width:1px;height:20px;background:var(--line)}
.deck-label{gap:12px;font:11px/1 var(--px);letter-spacing:.08em;color:var(--engrave)}
.deck-label h1{margin:0;font:inherit;color:var(--mark);outline:none}

/* Gallery: borderless squares, newest first, the grid fills the screen slot. */
.gallery-content{aspect-ratio:auto;width:min(960px,calc(100vw - 80px));overflow:auto;touch-action:pan-y pinch-zoom;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.gallery-content::-webkit-scrollbar{width:6px}
.gallery-content::-webkit-scrollbar-thumb{background:var(--line)}
.gallery-content>p{position:absolute;inset:0;display:grid;place-items:center;margin:0;padding:0 24px;text-align:center;font:11px/1.8 var(--px);color:var(--engrave);pointer-events:none}
.gallery-content>p:empty{display:none}
#gallery-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:2px}
.gallery-work{display:block;aspect-ratio:1;overflow:hidden;background:var(--bezel)}
.gallery-work img{display:block;width:100%;height:100%;object-fit:cover;transition:opacity .12s steps(2)}
#gallery-more{display:none}

/* Share: the chosen image fills the slot without a frame; its message sits over
   the slot's lower edge like the water scene's. */
#share-status{position:absolute;left:50%;bottom:calc(var(--foot) + var(--band-bottom) + 10px);transform:translateX(-50%);max-width:calc(100vw - 32px);margin:0;padding:4px 8px;border-radius:2px;background:var(--glass);color:var(--glass-ink);box-shadow:0 0 0 1px var(--line);font:11px/14px var(--px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;z-index:4}
#share-status:empty{display:none}
.share-kind{display:flex;gap:12px}

@media(hover:hover) and (pointer:fine){#open-gallery:hover{opacity:.8}.gallery-work:hover img{opacity:.82}}

/* Phones: the paper always spans the full width. Marquee (46px, logo 10px below the
   top) and deck (34 + 4 + 28) take 112px, which fits a 390×664 Safari viewport; only
   shorter screens fall back to fitting the paper by height. */
@media(max-width:650px){
.app{--inset:clamp(20px,6vw,40px);--band-top:0px;--band-bottom:0px;--top:calc(46px + env(safe-area-inset-top));--foot:calc(66px + env(safe-area-inset-bottom))}
/* Balanced cabinet: the screen is centred, so the marquee above and the deck
   below get the same height. Each keeps its minimum (46px marquee, 66px deck
   plus the home-indicator inset); on short phones the full-width paper wins. */
.app{--screen-h:min(calc(100dvh - var(--top) - var(--foot)),141.4286vw);--deck-h:max(var(--foot),calc((100dvh - var(--screen-h))/2));--deck-lift:calc((var(--deck-h) - var(--foot))/2)}
/* Marquee: logo and shop sit centred in all the height above the screen, so a
   tall phone gets a balanced band instead of a logo stuck to the top edge. */
.top{top:env(safe-area-inset-top);padding-top:10px;height:max(46px,calc(100dvh - var(--deck-h) - var(--screen-h) - env(safe-area-inset-top)))}
.top-logo{font-size:28px}
.shop-link{min-height:36px}
/* The screen sits directly on the deck, which grows to match the marquee. */
.screen{height:var(--screen-h);top:calc(100dvh - var(--deck-h) - var(--screen-h)/2);border-radius:0;box-shadow:0 -1px 0 var(--line),0 1px 0 var(--line)}
.screen-label{display:none}
.stage:before{box-shadow:inset 0 3px 3px -2px #22222229,inset 0 -3px 3px -2px #22222229}
/* Ink row sits 4px clear of the history/완성 row below it. */
.deck-panel{height:var(--deck-h)}
.lcd{bottom:calc(var(--deck-h) + 10px)}
/* Deck rows and pads are centred in the deck's height. */
.toolbar{gap:10px;bottom:calc(32px + env(safe-area-inset-bottom) + var(--deck-lift))}
.toolbar button{height:34px;min-height:34px}
.key:before{width:32px;height:30px;margin:-16px 0 0 -16px}
.key svg,.key i{margin-top:-2px}
.shaping{padding-left:10px}
.palette,.shaping{gap:2px}
.bottom-actions{gap:0;bottom:calc(env(safe-area-inset-bottom) + var(--deck-lift))}
.bottom-actions button,.brand-mark{min-height:28px}
.fn:before{width:28px;height:22px;margin:-12px 0 0 -14px}
.start:before{inset:2px 0 4px;border-radius:11px}
.brand-mark{bottom:calc(16px + env(safe-area-inset-bottom) + var(--deck-lift));min-height:34px}
.brand-left{left:max(10px,env(safe-area-inset-left,0px))}
.brand-right{right:max(10px,env(safe-area-inset-right,0px))}
.brand-mark svg{width:52px;height:26px}
#cancel-transfer{bottom:calc(env(safe-area-inset-bottom) + var(--deck-lift));min-height:28px}
/* Result scenes: the slot is the phone screen slot, the rows are the deck rows. */
.slot{left:0;width:100vw;height:var(--screen-h);top:calc(100dvh - var(--deck-h) - var(--screen-h)/2);transform:translateY(-50%);aspect-ratio:auto}
.deck-row{gap:8px}
.deck-row-1{bottom:calc(32px + env(safe-area-inset-bottom) + var(--deck-lift));min-height:34px}
.deck-row-2{bottom:calc(env(safe-area-inset-bottom) + var(--deck-lift));min-height:28px}
.deck-row-1 .fn-text,.deck-row-1 .start{min-height:34px}
.deck-row-1 .fn-text:after{bottom:8px}
#share-status{bottom:calc(var(--deck-h) + 10px)}
.start{padding:0 12px;min-height:28px}
.fn-text{min-height:28px}
.fn-text:after{bottom:6px}
#gallery-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}

@media(max-height:500px) and (orientation:landscape){
.deck-panel{display:none}
.app{--top:64px;--foot:48px;--band-top:20px;--band-bottom:12px}
.top{top:8px}
.lcd{bottom:auto;top:12px}
.toolbar{left:auto;right:calc(64px + env(safe-area-inset-right,0px));bottom:50%;transform:translateY(50%);flex-direction:column;gap:4px}
.palette,.shaping{gap:0}
.shaping{border-left:0;padding-left:0}
.screen{left:calc((100% - 180px)/2);height:min(calc(100dvh - 116px),calc((100vw - 260px)*1.414286))}
.slot{left:calc((100% - 180px)/2);height:min(calc(100dvh - 116px),calc((100vw - 260px)*1.414286))}
.deck-row{left:auto;right:calc(24px + env(safe-area-inset-right,0px));transform:none}
.deck-row-1{bottom:44px}
.deck-row-2{bottom:0}
#share-status{bottom:auto;top:12px}
}

@media(prefers-reduced-motion:reduce){*,*:before,*:after{transition:none!important;animation:none!important}}

/* The visible paper front freezes each contacted band of the live water. */
#transfer-layer{position:absolute;inset:0;z-index:4;pointer-events:none;overflow:hidden}
#transfer-paper{width:100%;height:100%}
#transfer-front{position:absolute;left:0;right:0;top:0;height:10px;transform:translateY(-5px);background:linear-gradient(transparent,#ffffff66 40%,#24272213 60%,transparent);pointer-events:none}
.is-transferring .toolbar{opacity:.2;pointer-events:none}
.is-transferring .bottom-actions{visibility:hidden}
#cancel-transfer{position:absolute;left:50%;bottom:calc(10px + env(safe-area-inset-bottom));transform:translateX(-50%);z-index:5}
.is-transferring .empty{visibility:hidden}
@media(prefers-reduced-motion:reduce){#transfer-front{display:none}}
