diff --git a/src/gateway/routes-music.ts b/src/gateway/routes-music.ts
index 1ba4e34..961c49b 100644
--- a/src/gateway/routes-music.ts
+++ b/src/gateway/routes-music.ts
@@ -248,7 +248,7 @@ app.get('/api/music/workspace-file/:dir/:file', (req, res) => {
res.status(403).json({ error: 'Forbidden' }); return;
}
if (!fs.existsSync(filePath)) { res.status(404).json({ error: 'Not found' }); return; }
- const safeName = Buffer.from(req.params.file).toString('ascii').replace(/[^\x20-\x7E]/g, '_');
+ const safeName = req.params.file.replace(/[^\x20-\x7E]/g, '_').replace(/["\\]/g, '_');
res.setHeader('Content-Type', 'audio/midi');
res.setHeader('Content-Disposition', `attachment; filename="${safeName}"; filename*=UTF-8''${encodeURIComponent(req.params.file)}`);
const stream = fs.createReadStream(filePath);
diff --git a/web-ui/accountant-app.html b/web-ui/accountant-app.html
index 1ccfbbe..872e3f8 100644
--- a/web-ui/accountant-app.html
+++ b/web-ui/accountant-app.html
@@ -106,6 +106,35 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
/* ── Scrollbars ── */
.ac-files::-webkit-scrollbar,.ac-msgs::-webkit-scrollbar{width:3px;}
.ac-files::-webkit-scrollbar-thumb,.ac-msgs::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px;}
+
+/* ── Mobile ── */
+@media(max-width:640px){
+ .ac-resize{display:none;}
+ .ac-sb{
+ position:fixed;top:0;left:0;bottom:0;z-index:500;
+ width:80vw;max-width:300px;
+ transform:translateX(-100%);transition:transform .25s ease;
+ box-shadow:4px 0 24px rgba(0,0,0,.4);
+ }
+ .ac-sb.mob-open{transform:translateX(0);}
+ .ac-chat{
+ position:fixed;top:0;right:0;bottom:0;z-index:500;
+ width:92vw;max-width:380px;
+ transform:translateX(100%);transition:transform .25s ease;
+ box-shadow:-4px 0 24px rgba(0,0,0,.4);
+ }
+ .ac-chat.mob-open{transform:translateX(0);}
+ #mob-backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:499;}
+ #mob-backdrop.show{display:block;}
+ .ac-center{width:100%;}
+ .ac-center-body{padding-bottom:64px;}
+ #mob-tabbar{display:flex !important;position:fixed;bottom:0;left:0;right:0;z-index:400;background:var(--panel);border-top:1px solid var(--line);height:52px;}
+ .ac-textarea,input,textarea{font-size:16px !important;}
+}
+@media(min-width:641px){
+ #mob-tabbar{display:none !important;}
+ #mob-backdrop{display:none !important;}
+}
@@ -119,6 +148,23 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
+
+
+
+
+
+
+
+
@@ -194,6 +240,26 @@ async function checkAuth(){
catch{location.href='/login.html?redirect='+encodeURIComponent(location.pathname);return false;}
}
+// ── Mobile overlay ────────────────────────────────────────────────────────────
+function mobCloseAll(){
+ document.querySelector('.ac-sb').classList.remove('mob-open');
+ document.getElementById('chat-panel').classList.remove('mob-open');
+ document.getElementById('mob-backdrop').classList.remove('show');
+ ['files','chat'].forEach(t=>document.getElementById('mobtab-'+t)?.classList.remove('active'));
+ document.getElementById('mobtab-home').style.cssText='color:var(--brand,#22c55e);border-top:2px solid var(--brand,#22c55e)';
+}
+function mobToggle(which){
+ const sb=document.querySelector('.ac-sb');
+ const cp=document.getElementById('chat-panel');
+ const bd=document.getElementById('mob-backdrop');
+ if(which==='home'){mobCloseAll();return;}
+ const sbOpen=sb.classList.contains('mob-open');
+ const cpOpen=cp.classList.contains('mob-open');
+ mobCloseAll();
+ if(which==='files'&&!sbOpen){sb.classList.add('mob-open');bd.classList.add('show');document.getElementById('mobtab-files').classList.add('active');document.getElementById('mobtab-home').style.cssText='';}
+ if(which==='chat'&&!cpOpen){cp.classList.add('mob-open');bd.classList.add('show');document.getElementById('mobtab-chat').classList.add('active');document.getElementById('mobtab-home').style.cssText='';}
+}
+
// ── Theme ─────────────────────────────────────────────────────────────────────
function toggleTheme(){const d=document.documentElement;const n=d.getAttribute('data-theme')==='dark'?'light':'dark';d.setAttribute('data-theme',n);try{localStorage.setItem('cherryclaw_theme',n);}catch{}}
@@ -369,11 +435,16 @@ async function sendMessage(){
const msg=input.value.trim();
if(!msg||document.getElementById('send-btn').disabled)return;
input.value='';autoResize(input);
+ if(window.innerWidth<=640) mobToggle('chat');
addMsg('user',escHtml(msg));
await callChat(msg);
}
-function sendQuick(msg){document.getElementById('chat-input').value=msg;sendMessage();}
+function sendQuick(msg){
+ if(window.innerWidth<=640) mobCloseAll();
+ document.getElementById('chat-input').value=msg;
+ sendMessage();
+}
async function callChat(message){
const btn=document.getElementById('send-btn');
diff --git a/web-ui/dental-agent.html b/web-ui/dental-agent.html
index 3099a4b..f82f1b9 100644
--- a/web-ui/dental-agent.html
+++ b/web-ui/dental-agent.html
@@ -631,6 +631,49 @@ button.danger:hover { background: #fecaca; }
.theme-dot:hover { transform: scale(1.2); }
.theme-dot.active { border-color: var(--text); }
+/* ── Mobile ── */
+@media(max-width:640px){
+ .header{flex-wrap:wrap;padding:6px 10px;gap:6px;}
+ .header h1{font-size:13px;}
+ .theme-picker{display:none;}
+ .header-actions{gap:5px;}
+ .header-actions button{padding:4px 8px;font-size:11px;}
+
+ /* Side panels → overlays */
+ .left-panel{
+ position:fixed;top:0;left:0;bottom:0;z-index:500;
+ width:82vw;max-width:300px;
+ transform:translateX(-100%);transition:transform .25s ease;
+ box-shadow:4px 0 28px rgba(0,0,0,.5);
+ resize:none;
+ }
+ .left-panel.mob-open{transform:translateX(0);}
+ .right-panel{
+ position:fixed;top:0;right:0;bottom:0;z-index:500;
+ width:92vw;max-width:380px;
+ transform:translateX(100%);transition:transform .25s ease;
+ box-shadow:-4px 0 28px rgba(0,0,0,.5);
+ }
+ .right-panel.mob-open{transform:translateX(0);}
+
+ #mob-backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:499;}
+ #mob-backdrop.show{display:block;}
+
+ /* Bottom tabbar */
+ #mob-tabbar{display:flex !important;}
+
+ /* Center: leave bottom room for tabbar */
+ .main{padding-bottom:52px;}
+ .center-panel{border-right:none;}
+
+ /* iOS zoom */
+ input,textarea,select{font-size:16px !important;}
+}
+@media(min-width:641px){
+ #mob-backdrop{display:none !important;}
+ #mob-tabbar{display:none !important;}
+}
+
/* Image lightbox */
#lightbox {
display: none;
@@ -651,6 +694,19 @@ button.danger:hover { background: #fecaca; }
+
+
+
+
+
+
+