diff --git a/mobile.html b/mobile.html index b8fce9f..78425f7 100644 --- a/mobile.html +++ b/mobile.html @@ -60,16 +60,18 @@ @media (display-mode:standalone){ #app{ --pad:26px; padding-top:max(20px,env(safe-area-inset-top)); padding-bottom:max(16px,env(safe-area-inset-bottom)); } } /* ---- top ---- */ - #top{ flex:0 0 auto; display:flex; flex-direction:column; gap:9px; } - #brandrow{ display:flex; align-items:center; } + #top{ flex:0 0 auto; display:flex; flex-direction:column; gap:11px; } + #brandrow{ display:flex; align-items:center; gap:10px; } #logoLink{ display:inline-flex; opacity:.9; } .brandlogo{ height:clamp(17px,3.4vmin,24px); width:auto; display:block; } + .hicons{ display:flex; align-items:center; gap:8px; margin-left:auto; } + .hicons .icon{ width:36px; height:36px; font-size:16px; } .sels{ display:flex; gap:8px; align-items:flex-end; } .sel{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:3px; } .sel > span{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); padding-left:3px; } .sel select{ width:100%; background:var(--field-bg); color:var(--txt); border:1px solid var(--field-bd); border-radius:10px; padding:10px 8px; font-size:15px; } .trow{ display:flex; align-items:center; gap:10px; } - .vol{ flex:1 1 auto; display:flex; align-items:center; gap:10px; color:var(--muted); min-width:0; } + .vol{ width:100%; display:flex; align-items:center; gap:12px; color:var(--muted); min-width:0; } .vol input{ flex:1 1 auto; min-width:0; accent-color:var(--cyan); } .dyn{ flex:0 0 auto; font-family:Georgia,"Times New Roman",serif; font-style:italic; font-weight:700; font-size:17px; color:var(--muted); line-height:1; } .icon{ flex:0 0 auto; width:42px; height:42px; border-radius:50%; display:flex; align-items:center; justify-content:center; @@ -77,7 +79,7 @@ .icon:active{ background:rgba(127,139,154,.30); } /* ---- middle: pulse + track panel + lanes + transport, centered as one block ---- */ - #mid{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; align-items:center; justify-content:safe center; gap:clamp(10px,2.4vmin,22px); } + #mid{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:clamp(12px,2.8vmin,24px); } #stage{ flex:0 0 auto; display:flex; flex-direction:column; align-items:center; width:100%; } /* tempo plate: [TAP] [big BPM] [thumbwheel] — flashes on the beat */ #pulse{ position:relative; width:100%; padding:clamp(8px,1.8vmin,14px); border-radius:16px; @@ -89,7 +91,13 @@ .tapbtn{ flex:0 0 auto; align-self:stretch; min-width:clamp(58px,16vmin,100px); border-radius:12px; background:linear-gradient(180deg,var(--btn1),var(--btn2)); border:1px solid var(--btn-bd); color:var(--txt); font-size:clamp(13px,2.8vmin,18px); font-weight:600; letter-spacing:.14em; cursor:pointer; + transition:box-shadow .1s ease-out, border-color .1s ease-out, color .1s ease-out; box-shadow:0 3px 0 rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.06); } + /* TAP button glows in time with the beat (rides the #pulse flash) */ + #pulse.hit .tapbtn{ border-color:var(--cyan); color:var(--cyan); + box-shadow:0 3px 0 rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.06), 0 0 16px var(--glow), inset 0 0 12px var(--glow); } + #pulse.hit.acc .tapbtn{ border-color:var(--amber); color:var(--amber); + box-shadow:0 3px 0 rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.06), 0 0 18px var(--aglow), inset 0 0 14px var(--aglow); } .tapbtn:active{ transform:translateY(2px); box-shadow:0 1px 0 rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.06); } #bpm{ flex:1 1 auto; display:flex; flex-direction:column; align-items:center; justify-content:center; line-height:.82; cursor:pointer; min-width:0; } #bpmNum{ font-size:clamp(44px,15vmin,120px); font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.01em; } @@ -98,11 +106,13 @@ background:transparent; color:var(--txt); border:none; border-bottom:2px solid var(--cyan); outline:none; font-variant-numeric:tabular-nums; -moz-appearance:textfield; } #bpmIn::-webkit-outer-spin-button, #bpmIn::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; } /* thumbwheel encoder — drag up/down to scrape the tempo */ - #wheel{ flex:0 0 auto; align-self:stretch; width:clamp(30px,7.5vmin,46px); border-radius:11px; cursor:ns-resize; touch-action:none; + #wheel{ flex:0 0 auto; align-self:stretch; width:clamp(30px,7.5vmin,46px); border-radius:11px; cursor:ns-resize; touch-action:none; overflow:hidden; border:1px solid var(--btn-bd); - background:repeating-linear-gradient(to bottom, rgba(0,0,0,.16) 0 1px, transparent 1px 6px), - linear-gradient(to right, rgba(0,0,0,.26), rgba(255,255,255,.14) 48%, rgba(0,0,0,.26)), var(--field-bg); - box-shadow:inset 0 0 6px rgba(0,0,0,.25); } + background:repeating-linear-gradient(to bottom, rgba(0,0,0,.22) 0 1px, transparent 1px 5px), + linear-gradient(to bottom, rgba(0,0,0,.55), rgba(0,0,0,0) 18%, rgba(255,255,255,.16) 50%, rgba(0,0,0,0) 82%, rgba(0,0,0,.55)), + linear-gradient(to right, rgba(0,0,0,.18), rgba(255,255,255,.10) 50%, rgba(0,0,0,.18)), + var(--field-bg); + box-shadow:inset 0 10px 9px -8px rgba(0,0,0,.75), inset 0 -10px 9px -8px rgba(0,0,0,.75), inset 0 0 4px rgba(0,0,0,.3); } #wheel:active{ border-color:var(--cyan); } #meterline{ font-size:clamp(12px,2.1vmin,16px); color:var(--muted); text-align:center; min-height:1.2em; letter-spacing:.02em; } @@ -163,13 +173,13 @@ /* ---- transport: tempo row (−10/−/+/+10) then nav+play row (prev/play/practice/next) ---- */ /* grows to fill freed space; SHRINKS (rather than the page scrolling) when the panel expands */ - #transport{ flex:1 1 auto; min-height:0; max-height:clamp(150px,42vh,300px); display:flex; flex-direction:column; align-items:stretch; width:100%; padding-top:6px; gap:clamp(6px,1.4vmin,11px); } + #transport{ flex:0 1 auto; min-height:0; max-height:clamp(118px,30vh,210px); margin-top:auto; display:flex; flex-direction:column; align-items:stretch; width:100%; padding-top:6px; gap:clamp(5px,1.2vmin,9px); } .tgrid{ display:grid; width:100%; flex:1 1 auto; min-height:0; gap:clamp(7px,1.7vmin,14px); grid-template-columns:1fr 1.5fr 1.5fr 1fr; grid-template-rows:1fr 1fr; grid-template-areas:"dn10 dn up up10" "prev play prac next"; } .tbtn{ background:linear-gradient(180deg,var(--btn1),var(--btn2)); color:var(--txt); border:1px solid var(--btn-bd); - border-radius:14px; height:auto; min-height:42px; font-size:clamp(18px,4.4vmin,30px); cursor:pointer; + border-radius:13px; height:auto; min-height:30px; font-size:clamp(15px,3.6vmin,25px); cursor:pointer; box-shadow:0 3px 0 rgba(0,0,0,.28), inset 0 1px 0 rgba(255,255,255,.06); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px; } - .journal{ flex:0 0 auto; width:100%; height:clamp(34px,7vmin,46px); border-radius:11px; cursor:pointer; + .journal{ flex:0 0 auto; width:100%; height:clamp(30px,6vmin,42px); border-radius:11px; cursor:pointer; background:rgba(127,139,154,.10); border:1px solid var(--panel-bd); color:var(--muted); font-size:13px; display:flex; align-items:center; justify-content:center; gap:8px; } .journal.rec{ background:rgba(192,57,43,.12); border-color:#c0392b; color:var(--txt); cursor:default; } @@ -188,16 +198,18 @@ pulse + transport on the left, panel + lanes on the right */ @media (orientation:landscape){ #app{ --maxw:1060px; } - #top{ flex-direction:row-reverse; align-items:flex-end; gap:14px; } - #top .sels{ flex:3 1 0; min-width:0; } - #top .trow{ flex:2 1 0; min-width:0; } + #top{ flex-direction:row; flex-wrap:wrap; align-items:center; gap:8px 14px; } + #brandrow{ flex:1 1 100%; } + #top .vol{ order:2; flex:2 1 0; width:auto; min-width:0; } + #top .sels{ order:3; flex:3 1 0; min-width:0; } #mid{ display:grid; align-items:center; gap:8px 4vw; - grid-template-columns:40% 60%; grid-template-rows:1fr auto; - grid-template-areas:"stage detail" "transport detail"; } + grid-template-columns:40% 60%; grid-template-rows:auto 1fr auto; + grid-template-areas:"panel detail" "stage detail" "transport detail"; } + #trackpanel{ grid-area:panel; align-self:start; } #stage{ grid-area:stage; align-self:center; } #detail{ grid-area:detail; align-self:stretch; width:auto; max-width:none; max-height:none; min-height:0; overflow-y:auto; } - #transport{ grid-area:transport; align-self:end; max-height:none; } - .tbtn{ height:clamp(38px,12vmin,62px); } + #transport{ grid-area:transport; align-self:end; max-height:none; margin-top:0; } + .tbtn{ height:clamp(36px,11vmin,58px); } } [data-theme="light"] .logo-dark{ display:none; } [data-theme="dark"] .logo-light{ display:none; } @@ -254,14 +266,14 @@
+
+
+
?
+
+
+
-
-
-
-
?
-
-
-
+
@@ -270,6 +282,16 @@
+
+
+ + + +
+
Play bars, then
+
→ + bpm / bars
+
play / mute bars
+
@@ -281,16 +303,6 @@
-
-
- - - -
-
Play bars, then
-
→ + bpm / bars
-
play / mute bars
-
@@ -829,12 +841,12 @@ function loadFromHash(text){ /* ========================= HELP TOUR ========================================= */ const TOUR=[ - {sel:"#utilrow", title:"Controls", text:"Volume (soft p → loud f), ◐ light/dark theme, ⛶ full screen, the ↑ share menu, and ? to replay this tour anytime."}, + {sel:"#utilrow", title:"Controls", text:"Up top: ↑ share menu, ? to replay this tour, ◐ light/dark theme, ⛶ full screen. The volume slider (soft p → loud f) runs full-width just below."}, {sel:".sels", title:"Pick what to play", text:"Choose a set list and the track within it. Tracks are your practice items — name them for whatever you're working on, even if two share the same beat."}, {sel:"#saveBtn", title:"Save & library", text:"Save the current track — “Save as new”, or “Update” one of yours. The same sheet is your library: make set lists and rename / reorder / delete tracks. It all lives with the full editor too."}, {sel:"#pulse", title:"Tempo", text:"Tap the BPM to tap-tempo, press-and-hold to type an exact value, or drag up/down to scrub. ±10 / ±1 buttons nudge it."}, {sel:"#lanes", title:"Edit the beat", text:"Each lane is a row of pads that blink on the beat — tap a pad to cycle rest → beat → accent → ghost. Tap a lane's label to set its note value (eighths, triplets, sixteenths…), sound, grouping, mute or polymeter. “+ Add lane” for more."}, - {sel:"#trackpanel", title:"Track settings", text:"Optional per-track extras (under the lanes): Repeat for N bars then stop / next / prev track, a tempo ramp, and practice gaps."}, + {sel:"#trackpanel", title:"Track settings", text:"Optional per-track extras (above the tempo): Repeat for N bars then stop / next / prev track, a tempo ramp, and practice gaps."}, {sel:"#bPrac", title:"Practice = a timed session", text:"Play just runs the metronome. Practice times your playing and logs it (not audio): it starts a session clock and Play becomes Stop — start/pause each track, then Stop to save the session."}, {sel:"#bJournal", title:"Practice journal", text:"Opens your saved practice sessions — notes and a per-track breakdown across days. While Practice is recording, this shows the live session timer."}, ];