Files
Kaj SchittecatandClaude Opus 5.5 b7408bdf8e web: own sync cursor per browser + phone keyboard on iOS
Each browser profile and origin keeps its own sync id (browsers that synced
under the shared id keep it), so every copy gets every message it has not
had. iOS: open the keyboard from touchend too, and a Keyboard button when the
UI edits a field without the phone keyboard up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:56:32 +02:00

221 lines
12 KiB
CSS

/* wadamesh in the browser: the page around the device canvas.
Tokens are the site's Night palette (deploy/site/theme.css), dark only. */
:root{
color-scheme:dark;
--bg:#000400;
--card:rgba(11,14,16,.74);
--solid:#0B0E10;
--ink:#E6EBED; --ink2:#D2D9DD; --mut:#B0BAC0; --dim:#8C979D;
--line:#1C2428;
--control:#161D21; --control-hi:#222C31;
--teal:#15B6A6; --cyan:#19D6C2;
--accent-fill:#15B6A6; --on-accent:#00120F;
--accent-soft:rgba(25,214,194,.13);
--bar:rgba(0,0,0,.5);
--frame:#16191D; --frame-edge:#2A3036;
--warn:#F2A33A; --warn-bg:rgba(242,163,58,.13); --warn-line:rgba(242,163,58,.38);
--bad:#F06A6E;
--shadow:0 22px 50px -26px rgba(0,0,0,.85);
--bar-h:56px;
--gutter:16px;
}
*{box-sizing:border-box}
html,body{margin:0; height:100%}
html{background:var(--bg); overscroll-behavior:none; -webkit-text-size-adjust:100%}
body{overscroll-behavior:none}
body{
min-height:100%; display:flex; flex-direction:column;
color:var(--ink); background:
radial-gradient(1200px 600px at 20% -10%, rgba(25,214,194,.10), transparent 60%),
radial-gradient(900px 500px at 90% 110%, rgba(90,169,255,.07), transparent 60%),
var(--bg);
font-family:'Montserrat',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; font-weight:500;
-webkit-font-smoothing:antialiased;
}
::selection{background:rgba(25,214,194,.32)}
/* ---- the bar ---- */
.bar{
display:flex; align-items:center; gap:10px;
min-height:var(--bar-h); padding:8px var(--gutter);
padding-left:max(var(--gutter), env(safe-area-inset-left));
padding-right:max(var(--gutter), env(safe-area-inset-right));
background:var(--bar); border-bottom:1px solid var(--line);
-webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
position:relative; z-index:30; /* its menu opens over the stage */
padding-top:max(8px, env(safe-area-inset-top));
flex-wrap:nowrap; min-width:0;
}
.bar > *{flex-shrink:0}
.bar .badge{flex-shrink:1; min-width:0}
.brand{display:inline-flex; align-items:center; gap:8px; color:var(--ink); text-decoration:none;
font-weight:700; letter-spacing:.02em; font-size:17px}
.brand img{display:block}
.badge{display:inline-flex; align-items:center; height:24px; padding:0 10px; border-radius:12px;
background:var(--warn-bg); color:var(--warn); box-shadow:inset 0 0 0 1px var(--warn-line);
font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase}
.spacer{flex:1}
.btn{
height:36px; padding:0 14px; border:0; border-radius:18px; cursor:pointer;
background:var(--control); color:var(--ink2); box-shadow:inset 0 0 0 1px var(--line);
font:inherit; font-size:14px; font-weight:600; white-space:nowrap;
transition:background .15s, box-shadow .15s, color .15s;
}
.btn:hover{background:var(--control-hi)}
.btn:focus-visible{outline:none; box-shadow:inset 0 0 0 1px var(--teal), 0 0 0 3px var(--accent-soft)}
.btn.primary{background:var(--accent-fill); color:var(--on-accent); box-shadow:none}
.btn.primary:hover{background:var(--cyan)}
.btn.icon{width:36px; padding:0; display:inline-grid; place-items:center}
.btn.icon svg{width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round}
.btn[hidden]{display:none}
/* ---- the stage: the canvas, scaled by app.js (aspect kept, integer device pixels when it can) ---- */
#stage{
flex:1; min-height:0; display:flex; align-items:center; justify-content:center;
padding:var(--gutter);
padding-left:max(var(--gutter), env(safe-area-inset-left));
padding-right:max(var(--gutter), env(safe-area-inset-right));
}
#frame{position:relative; line-height:0; border-radius:10px; background:#000;
box-shadow:0 0 0 1px var(--frame-edge), 0 0 0 7px var(--frame), 0 0 0 8px var(--frame-edge), var(--shadow)}
#screen{display:block; width:800px; height:480px; border-radius:2px; touch-action:none; outline:none;
-webkit-user-select:none; user-select:none; -webkit-tap-highlight-color:transparent; cursor:default}
/* a field the UI focused by itself (a screen opened with it): the phone shows its keyboard
only for a tap, so this is the tap (app.js "phone keyboard") */
#kb-pill{position:absolute; left:50%; bottom:72px; transform:translateX(-50%); z-index:5;
display:inline-flex; align-items:center; gap:8px; line-height:1.2; padding:10px 18px;
border-radius:999px; box-shadow:0 6px 20px rgba(0,0,0,.45)}
#kb-pill[hidden]{display:none}
#kb-pill svg{width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round}
#screen.crisp{image-rendering:crisp-edges; image-rendering:pixelated} /* integer device-pixel scale: nearest */
#overlay{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
gap:6px; padding:24px; text-align:center; line-height:1.45; background:var(--solid); border-radius:2px}
#overlay[hidden]{display:none}
#overlay img{opacity:.95; margin-bottom:8px}
#status{margin:0; font-size:16px; font-weight:600; color:var(--ink)}
#overlay .detail{margin:0; max-width:520px; font-size:13px; color:var(--mut)}
#overlay .detail code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; color:var(--ink2)}
#overlay.busy #status::after{content:""; display:inline-block; width:1.2em; text-align:left;
animation:dots 1.2s steps(4,end) infinite}
@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}
#overlay.fatal #status{color:var(--bad)}
#overlay .btn{margin-top:10px; line-height:normal}
.hint{margin:0 auto; padding:0 var(--gutter) 18px; max-width:820px; text-align:center;
color:var(--dim); font-size:13px; line-height:1.5}
#toast{position:fixed; left:50%; top:calc(var(--bar-h) + 12px); transform:translateX(-50%);
max-width:min(460px, calc(100vw - 32px)); padding:12px 16px; border-radius:12px;
background:var(--solid); color:var(--ink2); box-shadow:inset 0 0 0 1px var(--line), var(--shadow);
font-size:14px; line-height:1.45; z-index:10}
/* fullscreen: only the device, on black */
#stage:fullscreen{padding:0; background:#000; align-items:center}
#stage:-webkit-full-screen{padding:0; background:#000; align-items:center}
#stage:fullscreen #frame, #stage:-webkit-full-screen #frame{box-shadow:none; border-radius:0}
/* a narrower window: the bar stays one row (labels shorten, the brand name goes first) */
@media (max-width:720px){
.wide{display:none}
.brand span{display:none}
.bar{gap:8px}
}
@media (max-width:420px){
.btn{height:34px; padding:0 10px; font-size:13px}
.btn.icon{width:34px}
#btn-reset{display:none} /* the More menu has it too */
}
/* ---- a phone or tablet, or a tall window: the canvas IS the screen (app.js "fill") ----
One slim bar row, then the canvas edge to edge; the safe areas (notch, home indicator,
rounded corners on a phone on its side) stay clear. */
body.fill{height:100vh; height:100dvh; min-height:0; overflow:hidden}
body.fill .bar{min-height:0; gap:8px; padding-top:max(6px, env(safe-area-inset-top)); padding-bottom:6px}
body.fill #stage{flex:1 1 auto; padding:0; align-items:flex-start; overflow:hidden;
padding-left:env(safe-area-inset-left); padding-right:env(safe-area-inset-right);
padding-bottom:env(safe-area-inset-bottom)}
body.fill #frame{border-radius:0; box-shadow:none; transition:transform .18s ease-out}
body.fill #screen{border-radius:0}
body.fill .hint{display:none}
/* a phone on its side: every pixel of height counts */
@media (max-height:500px){
body.fill .bar{padding-top:max(4px, env(safe-area-inset-top)); padding-bottom:4px}
body.fill .btn{height:30px; font-size:13px}
body.fill .btn.icon{width:30px}
body.fill .brand img{width:26px; height:17px}
}
body.fill #toast{top:calc(env(safe-area-inset-top) + 58px)}
body.fill .link-help{position:fixed; z-index:25; left:max(8px, env(safe-area-inset-left)); right:max(8px, env(safe-area-inset-right));
bottom:calc(env(safe-area-inset-bottom) + 72px); margin:0;
background:linear-gradient(var(--warn-bg), var(--warn-bg)), var(--solid)}
.rotate-hint{display:none}
@media (max-width:560px){
:root{--bar-h:auto}
.wide{display:none}
#stage{align-items:flex-start; flex:0 0 auto}
.bar{gap:8px}
.brand span{display:none}
.btn{height:34px; padding:0 12px; font-size:13px}
.btn.icon{width:34px}
#frame{border-radius:6px; box-shadow:0 0 0 1px var(--frame-edge), 0 0 0 4px var(--frame), 0 0 0 5px var(--frame-edge)}
}
@media (prefers-reduced-motion:reduce){ #overlay.busy #status::after{animation:none; content:"..."} }
/* ---- the node link ---- */
.badge{max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.badge.wait{background:var(--control); color:var(--mut); box-shadow:inset 0 0 0 1px var(--line)}
.badge.up{background:var(--accent-soft); color:var(--cyan); box-shadow:inset 0 0 0 1px rgba(25,214,194,.38)}
.badge.bad{background:var(--warn-bg); color:var(--warn); box-shadow:inset 0 0 0 1px var(--warn-line)}
.link-help{display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:10px auto 0; max-width:900px;
padding:10px 14px; border-radius:12px; background:var(--warn-bg); box-shadow:inset 0 0 0 1px var(--warn-line);
color:var(--ink2); font-size:14px; line-height:1.45}
.link-help[hidden]{display:none}
.link-help p{flex:1 1 320px; margin:0; min-width:0}
dialog#connect-dlg{
width:min(460px, calc(100vw - 2*var(--gutter))); padding:22px; border:0; border-radius:18px;
background:var(--solid); color:var(--ink); box-shadow:inset 0 0 0 1px var(--line), var(--shadow);
}
dialog#connect-dlg::backdrop{background:rgba(0,0,0,.6)}
#connect-dlg h2{margin:0 0 6px; font-size:19px}
#connect-dlg .lead{margin:0 0 16px; color:var(--mut); font-size:14px; line-height:1.45}
#connect-dlg .choices{display:flex; flex-direction:column; gap:10px}
#connect-dlg .choice{
display:flex; flex-direction:column; align-items:flex-start; gap:3px; width:100%;
padding:12px 14px; border:0; border-radius:12px; text-align:left; cursor:pointer;
background:var(--control); color:var(--ink); box-shadow:inset 0 0 0 1px var(--line); font:inherit;
}
#connect-dlg .choice[hidden]{display:none}
#connect-dlg .choice.wifi{cursor:default}
#connect-dlg button.choice:hover{background:var(--control-hi)}
#connect-dlg .choice strong{font-size:15px}
#connect-dlg .choice span{color:var(--dim); font-size:13px}
#connect-dlg .row{display:flex; gap:8px; width:100%; margin-top:8px}
#connect-dlg input{
flex:1; min-width:0; height:36px; padding:0 12px; border:0; border-radius:10px;
background:var(--bg); color:var(--ink); box-shadow:inset 0 0 0 1px var(--line); font:inherit; font-size:14px;
}
#connect-dlg input:focus{outline:none; box-shadow:inset 0 0 0 1px var(--teal)}
#connect-dlg .note{margin:14px 0 0; color:var(--mut); font-size:13px; line-height:1.45}
#connect-dlg .note .link-line{display:block; margin-top:6px}
#connect-dlg .note a{color:var(--cyan)}
#connect-dlg .actions{display:flex; justify-content:flex-end; margin-top:16px}
/* the hidden field the phone's own keyboard types into (app.js "phone keyboard") */
#ime{position:fixed; left:0; top:0; width:1px; height:1px; opacity:0; border:0; padding:0; margin:0;
font-size:16px; resize:none; background:transparent; color:transparent; caret-color:transparent; z-index:-1}
/* the bar's "More" menu: browser backup / restore */
.more{position:relative}
.btn.icon svg circle{fill:currentColor; stroke:none}
.menu{position:absolute; right:0; top:calc(100% + 6px); z-index:20; min-width:260px; padding:6px;
background:var(--solid); border:1px solid var(--line); border-radius:12px;
box-shadow:0 10px 30px rgba(0,0,0,.5); display:flex; flex-direction:column; gap:2px}
.menu[hidden], .menu button[hidden]{display:none}
.menu button{all:unset; cursor:pointer; padding:9px 12px; border-radius:8px; color:var(--ink);
font:600 14px/1.25 Montserrat, system-ui, sans-serif}
.menu button span{display:block; font-weight:500; font-size:12px; opacity:.7; margin-top:2px}
.menu button:hover, .menu button:focus-visible{background:var(--control-hi)}
#connect-dlg #wifi-handoff{margin-top:10px; width:100%}
#connect-dlg #wifi-handoff p{margin:0; color:var(--mut); font-size:13px; line-height:1.45}