2026-10-20 22:29:58 +00:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, viewport-fit=cover" >
2026-10-21 02:33:56 +00:00
< meta http-equiv = "Content-Security-Policy" content = "default-src 'self'; connect-src https://api.venice.ai https://inference.yolopreneur.lol; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src blob: data:; font-src 'self'" >
2026-10-21 04:38:27 +00:00
< title > g*ndora</ title >
2026-10-20 22:29:58 +00:00
< meta name = "description" content = "Type a seed. Get a station. Every song is made for you as it plays, and every cover is drawn from the song's own settings." >
< link rel = "icon" href = "favicon.svg" >
2026-10-20 23:40:58 +00:00
< link rel = "apple-touch-icon" href = "icons/apple-touch-icon.png" >
< link rel = "manifest" href = "manifest.json" >
< meta name = "theme-color" content = "#0b0b0e" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black-translucent" >
< meta name = "apple-mobile-web-app-title" content = "gandora" >
2026-10-20 22:29:58 +00:00
< style >
: root {
2026-10-20 23:53:19 +00:00
--bg : #0b0b0e ; --ink : #ededf0 ; --dim : #8a8a96 ; --line : #232329 ; --card : #121216 ; --field : #0a0a0d ;
--acc : var ( -- ink ); --onacc : var ( -- bg ); --veil : rgba ( 11 , 11 , 14 , .55 ); --veil2 : rgba ( 11 , 11 , 14 , .65 ); --shade : 8 , 8 , 10 ;
--font : ui-monospace , "SF Mono" , Menlo , Consolas , monospace ; --sans : - apple-system , "Inter" , system-ui , sans-serif ;
color-scheme : dark ;
2026-10-20 22:29:58 +00:00
}
2026-10-20 23:53:19 +00:00
: root [ data-theme = "light" ] {
--bg : #f4f4f6 ; --ink : #17171a ; --dim : #6b6b76 ; --line : #dcdce2 ; --card : #ffffff ; --field : #ffffff ;
--veil : rgba ( 244 , 244 , 246 , .6 ); --veil2 : rgba ( 244 , 244 , 246 , .75 ); --shade : 244 , 244 , 246 ; color-scheme : light ;
}
@ media ( prefers-color-scheme : light ) {: root : not ([ data-theme = "dark" ]) {
--bg : #f4f4f6 ; --ink : #17171a ; --dim : #6b6b76 ; --line : #dcdce2 ; --card : #ffffff ; --field : #ffffff ;
--veil : rgba ( 244 , 244 , 246 , .6 ); --veil2 : rgba ( 244 , 244 , 246 , .75 ); --shade : 244 , 244 , 246 ; color-scheme : light ;
}}
2026-10-20 22:29:58 +00:00
* { box-sizing : border-box } html , body { height : 100 % }
body { margin : 0 ; background : var ( -- bg ); color : var ( -- ink ); font : 15 px / 1.5 var ( -- sans ); -webkit- font-smoothing : antialiased ; overflow-x : hidden }
a { color : inherit }
button { font : inherit ; color : inherit ; background : none ; border : 0 ; cursor : pointer }
input , textarea { font : inherit ; color : inherit }
. hidden { display : none !important }
/* ---------- seed screen ---------- */
# seed { min-height : 100 dvh ; display : flex ; flex-direction : column ; align-items : center ; justify-content : center ; padding : 24 px 16 px ; position : relative }
2026-10-21 03:49:24 +00:00
# seed . wordmark { position : absolute ; top : calc ( 26 px + env ( safe - area - inset - top )); left : 0 ; right : 0 ; text-align : center ; font : 600 15 px var ( -- font ); letter-spacing : .18 em ; color : var ( -- ink )}
2026-10-20 23:40:58 +00:00
# seed . wordmark . ast { color : var ( -- ink ); font-weight : 700 }
2026-10-20 22:29:58 +00:00
# seed form { width : min ( 680 px , 100 % ); text-align : center }
# seedIn { width : 100 % ; background : transparent ; border : 0 ; border-bottom : 1 px solid var ( -- line ); padding : 14 px 4 px ; text-align : center ; font : 400 clamp ( 24 px , 5 vw , 38 px ) / 1.2 var ( -- sans ); outline : none ; caret-color : var ( -- acc ); transition : border-color .3 s }
# seedIn : focus { border-color : var ( -- acc )}
# seedIn :: placeholder { color : #3a3a44 }
# seed . hint { margin-top : 18 px ; color : var ( -- dim ); font-size : 13 px ; min-height : 1.5 em }
# seedCover { width : 120 px ; height : 120 px ; margin : 0 auto 28 px ; border-radius : 14 px ; overflow : hidden ; background : var ( -- card ); opacity : 0 ; transform : scale ( .92 ); transition : opacity .5 s , transform .5 s }
# seedCover . on { opacity : 1 ; transform : none }
# seedCover canvas { width : 100 % ; height : 100 % ; display : block }
# nowRow { position : absolute ; top : 44 px ; left : 0 ; right : 0 ; display : flex ; justify-content : center }
# now { display : flex ; align-items : center ; gap : 10 px ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 999 px ; padding : 6 px 14 px 6 px 6 px ; font-size : 13 px ; cursor : pointer ; max-width : calc ( 100 % - 32 px )}
# now canvas { width : 28 px ; height : 28 px ; border-radius : 50 % ; display : block }
# now span { white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# now b { font : 600 11 px var ( -- font ); letter-spacing : .14 em ; color : var ( -- dim ); margin-left : 4 px }
# stations { width : min ( 680 px , 100 % ); margin-top : 40 px }
# stations h3 { font : 600 11 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 0 0 12 px ; text-align : center }
# stations . row { display : flex ; gap : 14 px ; overflow-x : auto ; padding : 4 px 4 px 10 px ; scrollbar-width : none ; justify-content : safe center }
# stations . s { flex : 0 0 96 px ; cursor : pointer ; text-align : center }
# stations . s canvas { width : 72 px ; height : 72 px ; border-radius : 50 % ; display : block ; margin : 0 auto 6 px ; background : var ( -- card )}
# stations . s . on canvas { outline : 2 px solid var ( -- acc ); outline-offset : 3 px }
# stations . s b { display : block ; font-size : 12 px ; font-weight : 600 ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# stations . s small { color : var ( -- dim ); font-size : 11 px }
# keyRow { position : absolute ; bottom : 18 px ; left : 0 ; right : 0 ; text-align : center ; font-size : 12 px ; color : var ( -- dim ); display : flex ; gap : 18 px ; justify-content : center ; flex-wrap : wrap }
# keyRow button { text-decoration : underline ; text-underline-offset : 3 px }
# keyRow . nav { font : 600 11 px var ( -- font ); letter-spacing : .18 em ; text-decoration : none }
# keyBox { position : absolute ; bottom : 44 px ; left : 50 % ; transform : translateX ( -50 % ); width : min ( 420 px , calc ( 100 % - 32 px )); background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 12 px ; padding : 14 px ; font-size : 13 px }
2026-10-20 23:53:19 +00:00
# keyBox input { width : 100 % ; margin : 8 px 0 ; background : var ( -- field ); border : 1 px solid var ( -- line ); border-radius : 8 px ; padding : 9 px 10 px ; font-family : var ( -- font ); font-size : 12 px ; outline : none }
2026-10-20 22:29:58 +00:00
# keyBox . row { display : flex ; gap : 8 px ; justify-content : flex-end }
# keyBox . row button { padding : 6 px 12 px ; border-radius : 8 px ; border : 1 px solid var ( -- line )}
2026-10-20 23:53:19 +00:00
# keyBox . row button . go { background : var ( -- acc ); color : var ( -- onacc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# keyBox p { margin : 0 0 4 px ; color : var ( -- dim )}
# keyBox label { display : block ; margin-top : 6 px ; color : var ( -- dim )}
/* ---------- player ---------- */
2026-10-21 03:49:24 +00:00
# player { min-height : 100 dvh ; display : grid ; grid-template-rows : auto 1 fr auto ; padding : calc ( 22 px + env ( safe - area - inset - top )) 16 px calc ( 16 px + env ( safe - area - inset - bottom ))}
2026-10-20 22:29:58 +00:00
# top { display : flex ; align-items : center ; justify-content : space-between ; font : 600 12 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim )}
2026-10-21 08:32:08 +00:00
# stage . station { font : 600 16 px var ( -- font ); letter-spacing : .18 em ; color : var ( -- ink ); max-width : min ( 86 vw , 420 px ); white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis ; cursor : pointer ; text-transform : uppercase ; margin-bottom : -2 px }
# stage . station : hover { color : var ( -- dim )}
2026-10-20 22:29:58 +00:00
# stage . station : hover { color : var ( -- acc )}
# stations . s { position : relative }
# stations . s . dots { position : absolute ; top : -4 px ; right : 2 px ; width : 22 px ; height : 22 px ; border-radius : 50 % ; background : var ( -- card ); border : 1 px solid var ( -- line ); color : var ( -- dim ); font-size : 12 px ; line-height : 1 }
# scrim { position : fixed ; inset : 0 ; z-index : 5 ; background : rgba ( 0 , 0 , 0 , .25 )}
# stationSheet , # picker , # tapePick { z-index : 6 }
2026-10-21 08:40:53 +00:00
# stationSheet { position : fixed ; left : 50 % ; bottom : 0 ; transform : translateX ( -50 % ); width : min ( 480 px , 100 % ); max-height : 88 vh ; max-height : 88 dvh ; overflow-y : auto ; -webkit- overflow-scrolling : touch ; overscroll-behavior : contain ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-bottom : 0 ; border-radius : 18 px 18 px 0 0 ; padding : 14 px 14 px calc ( 14 px + env ( safe - area - inset - bottom )); z-index : 6 ; box-shadow : 0 -20 px 60 px rgba ( 0 , 0 , 0 , .6 )}
# stationSheet details { margin-top : 10 px }# stationSheet summary { cursor : pointer ; color : var ( -- dim ); font-size : 12 px ; list-style : none ; display : flex ; align-items : center ; gap : 6 px }# stationSheet summary :: -webkit-details-marker { display : none }# stationSheet summary :: before { content : "▸" ; font-size : 10 px }# stationSheet details [ open ] summary :: before { content : "▾" }
2026-10-20 22:29:58 +00:00
# stationSheet h4 { font : 600 10 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 0 0 10 px }
2026-10-21 03:40:36 +00:00
# upd { position : fixed ; top : calc ( 10 px + env ( safe - area - inset - top )); left : 50 % ; transform : translateX ( -50 % ); z-index : 9 ; padding : 9 px 16 px ; border-radius : 999 px ; background : var ( -- ink ); color : var ( -- bg ); font : 600 11 px var ( -- font ); letter-spacing : .16 em ; box-shadow : 0 10 px 30 px -10 px rgba ( 0 , 0 , 0 , .6 )}
2026-10-21 08:41:08 +00:00
# stationSheet . x { position : sticky ; top : -8 px ; float : right ; margin : -8 px -6 px 0 0 ; z-index : 1 ; background : var ( -- card ); width : 36 px ; height : 36 px ; border-radius : 50 % ; display : grid ; place-items : center ; color : var ( -- dim ); font : 300 22 px var ( -- sans ); line-height : 1 }
2026-10-21 03:15:46 +00:00
# stationSheet . x : hover { color : var ( -- ink ); background : var ( -- bg )}
2026-10-20 22:29:58 +00:00
# stationSheet label { display : block ; color : var ( -- dim ); font-size : 12 px ; margin : 8 px 0 4 px }
2026-10-20 23:53:19 +00:00
# stationSheet input { width : 100 % ; background : var ( -- field ); border : 1 px solid var ( -- line ); border-radius : 10 px ; padding : 10 px 12 px ; outline : none }
2026-10-20 22:29:58 +00:00
# stationSheet . row { display : flex ; gap : 8 px ; flex-wrap : wrap ; margin-top : 12 px }
2026-10-21 04:32:36 +00:00
# stationSheet . sl { display : flex ; align-items : center ; gap : 12 px ; margin-top : 6 px }
# stationSheet . sl label { margin : 0 ; width : 84 px }
# stationSheet input [ type = range ] { flex : 1 ; accent-color : var ( -- ink ); margin : 0 }
# stationSheet . sl span { font : 11 px var ( -- font ); color : var ( -- dim ); width : 32 px ; text-align : right }
2026-10-20 22:29:58 +00:00
# stationSheet . row button { padding : 9 px 14 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); font : 600 11 px var ( -- font ); letter-spacing : .14 em }
2026-10-20 23:53:19 +00:00
# stationSheet . row button . go { background : var ( -- acc ); color : var ( -- onacc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# stationSheet . row button . bad { color : hsl ( 0 60 % 70 % ); border-color : hsl ( 0 30 % 35 % )}
# top button { color : var ( -- dim ); letter-spacing : .08 em }
# stage { display : flex ; flex-direction : column ; align-items : center ; justify-content : center ; gap : 18 px ; padding : 14 px 0 }
2026-10-20 22:45:46 +00:00
/* the flow: station, cover (trail on hover), transport, title, bar, then the DJ line, NEXT, lyrics/keep/share, tags */
# stationName { order : 0 }# cover { order : 1 }# controls { order : 2 }# meta { order : 4 }# bar { order : 5 }# meta2 { order : 7 }
2026-10-20 22:40:04 +00:00
# stage # controls { margin-top : 2 px }
2026-10-20 22:45:46 +00:00
# meta2 # pills { margin : 10 px 0 8 px }
2026-10-20 22:29:58 +00:00
# cover { width : min ( 78 vw , 380 px ); aspect-ratio : 1 ; position : relative ; perspective : 1200 px }
# cover . card { position : absolute ; inset : 0 ; transform-style : preserve-3d ; transition : transform .7 s cubic-bezier ( .4 , .1 , .2 , 1 )}
# cover . flipped . card { transform : rotateY ( 180 deg )}
# cover . face { position : absolute ; inset : 0 ; border-radius : 18 px ; overflow : hidden ; background : var ( -- card ); box-shadow : 0 30 px 80 px -30 px rgba ( 0 , 0 , 0 , .9 ); backface-visibility : hidden ; -webkit- backface-visibility : hidden }
2026-10-20 23:53:19 +00:00
# cover . back { transform : rotateY ( 180 deg ); background : var ( -- card ); border : 1 px solid var ( -- line ); display : flex ; flex-direction : column }
2026-10-20 22:29:58 +00:00
# cover canvas { width : 100 % ; height : 100 % ; display : block }
# cover . back . backTitle { font : 600 10 px var ( -- font ); letter-spacing : .22 em ; color : var ( -- dim ); padding : 14 px 16 px 6 px ; text-align : center }
2026-10-20 23:53:19 +00:00
# cover . veil { position : absolute ; inset : 0 ; display : flex ; align-items : center ; justify-content : center ; background : var ( -- veil ); backdrop-filter : blur ( 2 px ); font : 12 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- ink ); opacity : 0 ; transition : opacity .3 s ; pointer-events : none }
2026-10-21 05:16:09 +00:00
# cover . cooking . veil , # cover . tap . veil { opacity : 1 }
2026-10-20 22:29:58 +00:00
# meta { text-align : center ; max-width : 520 px }
# title { font-size : 20 px ; font-weight : 600 ; letter-spacing : -.01 em ; margin : 0 }
# tags { color : var ( -- dim ); font : 11 px var ( -- font ); margin : 4 px 0 0 ; letter-spacing : .06 em }
# lyric { color : var ( -- dim ); font-size : 13 px ; margin : 8 px 0 0 ; min-height : 1.5 em ; font-style : italic ; cursor : text }
# lyric : hover { color : var ( -- ink )}
# meta2 { text-align : center ; max-width : 560 px ; width : 100 % }
# meta2 # lyric { margin-top : 0 }
# tagRow { display : flex ; flex-wrap : wrap ; gap : 6 px ; justify-content : center ; margin : 10 px auto 0 ; max-width : 560 px }
# tagList { display : flex ; flex-wrap : wrap ; gap : 6 px ; justify-content : center ; margin : 8 px auto 0 ; max-width : 560 px }
# tagList : empty { display : none }
# tagList button { font : 12 px var ( -- font ); letter-spacing : .02 em ; padding : 5 px 10 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); color : var ( -- dim ); background : transparent ; transition : all .15 s }
2026-10-20 23:53:19 +00:00
# tagList button . like { color : var ( -- onacc ); background : var ( -- acc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# tagList button . avoid { color : hsl ( 220 40 % 70 % ); border-color : hsl ( 220 30 % 40 % ); text-decoration : line-through ; opacity : .8 }
# tagList button . add { border-style : dashed }
# tagList input { font : 12 px var ( -- font ); width : 110 px ; padding : 5 px 10 px ; border-radius : 999 px ; border : 1 px solid var ( -- acc ); background : var ( -- card ); outline : none }
# meta2 # next { margin : 8 px 0 0 }
# tagRow button . meta { color : var ( -- dim ); border-style : dotted ; cursor : default }
# tagRow button . fold { color : var ( -- acc ); border-color : transparent ; font-weight : 600 ; letter-spacing : .12 em }
# stage { gap : 14 px }
# tagRow button { font : 12 px var ( -- font ); letter-spacing : .02 em ; padding : 5 px 10 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); color : var ( -- dim ); background : transparent ; transition : all .15 s }
2026-10-20 23:53:19 +00:00
# tagRow button . like { color : var ( -- onacc ); background : var ( -- acc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# tagRow button . avoid { color : hsl ( 220 40 % 70 % ); border-color : hsl ( 220 30 % 40 % ); text-decoration : line-through ; opacity : .8 }
# tagRow button . add { border-style : dashed }
# tagRow input { font : 12 px var ( -- font ); width : 110 px ; padding : 5 px 10 px ; border-radius : 999 px ; border : 1 px solid var ( -- acc ); background : var ( -- card ); outline : none }
# lyrics { white-space : pre-wrap ; text-align : center ; font-size : 13 px ; line-height : 1.65 ; margin : 0 ; color : var ( -- ink ); flex : 1 ; overflow : auto ; padding : 4 px 16 px 18 px ; font-family : var ( -- sans ); -webkit- mask-image : linear-gradient ( #000 85 % , transparent ); mask-image : linear-gradient ( #000 85 % , transparent )}
# lyrics . none { color : var ( -- dim ); font-style : italic ; display : grid ; place-items : center }
# lyrics . sec { display : block ; color : var ( -- dim ); font : 600 10 px var ( -- font ); letter-spacing : .2 em ; margin : 12 px 0 2 px }
# lyrics . sec : first-child { margin-top : 0 }
# cover { cursor : pointer }
2026-10-21 03:07:11 +00:00
# bar { width : min ( 78 vw , 380 px ); height : 2 px ; background : var ( -- line ); border-radius : 2 px ; position : relative ; cursor : pointer ; touch-action : none }
# bar :: before { content : "" ; position : absolute ; left : 0 ; right : 0 ; top : -14 px ; bottom : -14 px } /* a fat finger target around a thin line */
# bar i { display : block ; height : 100 % ; width : 0 ; background : var ( -- acc ); border-radius : 2 px ; pointer-events : none }
# bar : hover , # bar . drag { height : 4 px ; margin : -1 px 0 }
2026-10-21 07:34:38 +00:00
# bar em { position : absolute ; top : -20 px ; transform : translateX ( -50 % ); font-size : 11 px ; font-style : normal ; line-height : 1 ; cursor : pointer ; opacity : .95 ; white-space : nowrap ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 999 px ; padding : 2 px 5 px }
# bar em . below { top : auto ; bottom : -20 px }
# bar em : hover { border-color : var ( -- ink )}
2026-10-21 03:07:11 +00:00
# bar b { position : absolute ; top : -28 px ; transform : translateX ( -50 % ); font : 11 px var ( -- font ); letter-spacing : .1 em ; color : var ( -- dim ); background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 6 px ; padding : 2 px 6 px ; pointer-events : none ; display : none ; white-space : nowrap }
# bar . drag b { display : block }
2026-10-20 22:29:58 +00:00
# controls { display : flex ; align-items : center ; gap : 18 px }
# controls button { width : 44 px ; height : 44 px ; border-radius : 50 % ; display : grid ; place-items : center ; color : var ( -- ink ); opacity : .85 }
# controls # backTrack svg , # controls # skip svg { width : 24 px ; height : 24 px }
# controls # prev { opacity : .6 }
# pills { display : flex ; gap : 10 px ; justify-content : center ; flex-wrap : wrap }
# pills button { display : inline-flex ; align-items : center ; gap : 8 px ; padding : 9 px 18 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); font : 600 12 px var ( -- font ); letter-spacing : .14 em ; color : var ( -- ink ); background : var ( -- card ); transition : background .2 s , color .2 s , border-color .2 s }
2026-10-20 23:53:19 +00:00
# pills button . on { background : var ( -- acc ); color : var ( -- onacc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# pills button : disabled { opacity : .4 ; cursor : default }
# controls button : hover { opacity : 1 }
2026-10-20 23:53:19 +00:00
# controls # play { width : 62 px ; height : 62 px ; background : var ( -- ink ); color : var ( -- bg )}
2026-10-20 22:45:46 +00:00
/* the trail: a strip along the bottom of the cover; shows on hover (always on touch screens) */
2026-10-21 01:31:16 +00:00
# trail { position : absolute ; left : 0 ; right : 0 ; bottom : 0 ; z-index : 3 ; display : flex ; gap : 10 px ; align-items : flex-end ; overflow-x : auto ; padding : 40 px 14 px 14 px ; border-radius : 0 0 18 px 18 px ; scrollbar-width : none ; -webkit- overflow-scrolling : touch ;
2026-10-20 23:53:19 +00:00
background : linear-gradient ( to top , rgba ( var ( -- shade ), .92 ), rgba ( var ( -- shade ), .55 ) 60 % , transparent ); opacity : 0 ; pointer-events : none ; transition : opacity .25 s }
2026-10-20 22:45:46 +00:00
# cover : hover # trail , # cover . flipped : hover # trail { opacity : 1 ; pointer-events : auto }
@ media ( hover : none ) {# trail { opacity : 1 ; pointer-events : auto ; padding-top : 18 px }}
2026-10-20 22:29:58 +00:00
# trail :: -webkit-scrollbar { display : none }
# trail : empty { display : none }
2026-10-21 01:31:16 +00:00
# trail canvas { width : 64 px ; height : 64 px ; border-radius : 12 px ; flex : none ; opacity : .6 ; cursor : pointer ; transition : opacity .2 s , transform .2 s }
# trail canvas : hover { opacity : .95 ; transform : translateY ( -3 px )}
# trail canvas . on { opacity : 1 ; transform : scale ( 1.12 ); box-shadow : 0 0 0 2 px var ( -- ink )}
2026-10-20 22:29:58 +00:00
# trail canvas . kept { box-shadow : 0 0 0 2 px var ( -- acc )}
# trail canvas . on . kept { box-shadow : 0 0 0 2 px var ( -- acc ), 0 0 0 4 px var ( -- ink )}
# controls svg { width : 22 px ; height : 22 px ; fill : currentColor }
# controls # loop , # controls # shuffle , # controls # remix { opacity : .6 }
# controls # remix : hover { opacity : 1 }
# controls # shuffle . on { opacity : 1 ; color : var ( -- acc ); background : rgba ( 233 , 216 , 166 , .12 )}
# controls # loop svg { width : 18 px ; height : 18 px }
2026-10-21 07:37:53 +00:00
# controls # loop . on { opacity : 1 ; color : var ( -- onacc ); background : var ( -- acc ); border-radius : 999 px }
2026-10-20 22:29:58 +00:00
# next { font : 11 px var ( -- font ); letter-spacing : .14 em ; color : var ( -- dim ); min-height : 1.4 em ; text-align : center }
# react { display : flex ; flex-direction : column ; gap : 10 px ; align-items : center }
# emojis { display : flex ; gap : 6 px ; justify-content : center ; flex-wrap : wrap }
# emojis button { font-size : 22 px ; width : 44 px ; height : 44 px ; border-radius : 12 px ; border : 1 px solid transparent ; transition : transform .15 s , border-color .15 s , background .15 s }
# emojis button : hover { background : var ( -- card ); border-color : var ( -- line )}
# emojis button . hit { transform : scale ( 1.25 ); border-color : var ( -- acc )}
# emojis # more { font-size : 20 px ; color : var ( -- dim ); background : var ( -- card ); border : 1 px solid var ( -- line )}
2026-10-21 07:08:39 +00:00
# picker { position : fixed ; left : 50 % ; bottom : 0 ; transform : translateX ( -50 % ); width : min ( 560 px , 100 % ); max-height : 55 dvh ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-bottom : 0 ; border-radius : 18 px 18 px 0 0 ; padding : 12 px 12 px calc ( 12 px + env ( safe - area - inset - bottom )); overflow : auto ; z-index : 6 ; box-shadow : 0 -20 px 60 px rgba ( 0 , 0 , 0 , .6 )}
2026-10-20 22:29:58 +00:00
# picker h4 { font : 600 10 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 10 px 4 px 6 px }
# picker . g { display : grid ; grid-template-columns : repeat ( auto - fill , minmax ( 42 px , 1 fr )); gap : 2 px }
# picker . g button { font-size : 24 px ; height : 42 px ; border-radius : 10 px }
# picker . g button : hover { background : #1a1a20 }
# picker . top { display : flex ; gap : 8 px ; align-items : center ; margin-bottom : 4 px }
# picker . top input { flex : 1 ; background : #0a0a0d ; border : 1 px solid var ( -- line ); border-radius : 10 px ; padding : 8 px 12 px ; font-size : 16 px ; outline : none }
# picker . top button { padding : 8 px 12 px ; border-radius : 10 px ; border : 1 px solid var ( -- line ); color : var ( -- dim ); font : 600 11 px var ( -- font ); letter-spacing : .14 em }
# sayRow { display : flex ; gap : 8 px ; width : min ( 520 px , 100 % )}
# say { flex : 1 ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 12 px ; padding : 10 px 14 px ; outline : none ; font-size : 14 px }
# say : focus { border-color : var ( -- acc )}
# sayRow button { padding : 0 14 px ; border : 1 px solid var ( -- line ); border-radius : 12 px ; color : var ( -- dim )}
# toast { position : fixed ; left : 50 % ; bottom : 110 px ; transform : translateX ( -50 % ); background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 10 px ; padding : 8 px 14 px ; font-size : 13 px ; opacity : 0 ; transition : opacity .25 s ; pointer-events : none ; max-width : calc ( 100 % - 32 px ); text-align : center }
# toast . on { opacity : 1 }
/* ---------- taste ---------- */
# taste { position : fixed ; inset : 0 ; background : rgba ( 11 , 11 , 14 , .97 ); padding : 18 px 16 px calc ( 18 px + env ( safe - area - inset - bottom )); overflow : auto }
# taste header { display : flex ; justify-content : space-between ; align-items : center ; font : 600 12 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin-bottom : 12 px }
# tasteTabs { display : flex ; gap : 8 px ; overflow-x : auto ; padding : 2 px 0 12 px ; scrollbar-width : none }
# tasteTabs button { display : flex ; align-items : center ; gap : 8 px ; padding : 6 px 12 px 6 px 6 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); font-size : 12 px ; white-space : nowrap ; color : var ( -- dim )}
# tasteTabs button . on { color : var ( -- ink ); border-color : var ( -- acc )}
# tasteTabs canvas { width : 22 px ; height : 22 px ; border-radius : 50 % }
# tasteMap { width : 100 % ; max-width : 640 px ; aspect-ratio : 1 ; display : block ; margin : 0 auto ; border-radius : 18 px ; background : var ( -- card )}
# brainMap { width : 100 % ; max-width : 640 px ; aspect-ratio : 2.2 ; display : block ; margin : 0 auto 14 px ; border-radius : 18 px ; background : var ( -- card )}
2026-10-21 00:27:27 +00:00
# tastePalette { max-width : 640 px ; margin : 6 px auto 18 px }
2026-10-20 22:29:58 +00:00
# tastePalette h3 { font : 600 11 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 0 0 10 px ; text-align : center }
# tastePalette . row { display : flex ; gap : 14 px ; overflow-x : auto ; padding : 4 px ; scrollbar-width : none ; justify-content : safe center }
# tastePalette . s { flex : 0 0 96 px ; cursor : pointer ; text-align : center }
# tastePalette . s canvas { width : 80 px ; height : 80 px ; border-radius : 14 px ; display : block ; margin : 0 auto 6 px }
# tastePalette . s b { display : block ; font-size : 11 px ; font-weight : 600 ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# tasteNote { color : var ( -- dim ); font-size : 12 px ; text-align : center ; margin : 12 px auto 0 ; max-width : 520 px }
# tasteLegend { display : flex ; justify-content : center ; gap : 16 px ; font : 11 px var ( -- font ); letter-spacing : .08 em ; color : var ( -- dim ); margin-top : 10 px ; flex-wrap : wrap }
/* ---------- tapes ---------- */
# lib . t . addTape { position : absolute ; right : 6 px ; bottom : 6 px ; width : 26 px ; height : 26 px ; border-radius : 50 % ; background : rgba ( 11 , 11 , 14 , .75 ); border : 1 px solid var ( -- line ); color : var ( -- ink ); font-size : 16 px ; line-height : 1 ; display : grid ; place-items : center }
2026-10-21 00:30:28 +00:00
# lib . t . addTape : hover { background : var ( -- acc ); color : var ( -- onacc )}
# lib . t . fromT { position : absolute ; left : 6 px ; top : 6 px ; width : 26 px ; height : 26 px ; border-radius : 50 % ; background : var ( -- veil2 ); border : 1 px solid var ( -- line ); color : var ( -- ink ); font : 700 16 px var ( -- font ); line-height : 1 ; display : grid ; place-items : center ; opacity : 0 ; transition : opacity .2 s }
# lib . t : hover . fromT { opacity : 1 }
@ media ( hover : none ) {# lib . t . fromT { opacity : .8 }}
# lib . t . fromT : hover { background : var ( -- acc ); color : var ( -- onacc )}
2026-10-20 22:29:58 +00:00
# tapeRow { display : flex ; gap : 14 px ; overflow-x : auto ; padding : 4 px 4 px 10 px ; scrollbar-width : none ; grid-column : 1 /- 1 }
# tapeRow . s { flex : 0 0 110 px ; cursor : pointer ; text-align : center }
# tapeRow . s . tc { width : 88 px ; height : 88 px ; border-radius : 14 px ; margin : 0 auto 6 px ; position : relative ; overflow : hidden ; background : var ( -- card ); display : grid ; grid-template-columns : 1 fr 1 fr ; grid-template-rows : 1 fr 1 fr ; gap : 2 px }
# tapeRow . s . tc canvas { width : 100 % ; height : 100 % ; display : block }
# tapeRow . s . tc . one { grid-template-columns : 1 fr ; grid-template-rows : 1 fr }
# tapeRow . s . new . tc { display : grid ; place-items : center ; border : 1 px dashed var ( -- line ); font-size : 26 px ; color : var ( -- dim )}
# tapeRow . s b { display : block ; font-size : 12 px ; font-weight : 600 ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# tapeRow . s small { color : var ( -- dim ); font-size : 11 px }
# tapePick { position : fixed ; left : 50 % ; bottom : 0 ; transform : translateX ( -50 % ); width : min ( 480 px , 100 % ); background : var ( -- card ); border : 1 px solid var ( -- line ); border-bottom : 0 ; border-radius : 18 px 18 px 0 0 ; padding : 14 px 14 px calc ( 14 px + env ( safe - area - inset - bottom )); z-index : 6 ; box-shadow : 0 -20 px 60 px rgba ( 0 , 0 , 0 , .6 )}
# tapePick h4 { font : 600 10 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 0 0 10 px }
# tapePick button { display : block ; width : 100 % ; text-align : left ; padding : 10 px 12 px ; border-radius : 10 px ; border : 1 px solid var ( -- line ); margin-bottom : 6 px }
# tapePick button : hover { background : #1a1a20 }
# tapePick input { width : 100 % ; background : #0a0a0d ; border : 1 px solid var ( -- line ); border-radius : 10 px ; padding : 10 px 12 px ; outline : none ; margin-bottom : 6 px }
2026-10-21 04:33:54 +00:00
# tape { position : fixed ; inset : 0 ; background : var ( -- bg ); padding : 18 px 16 px calc ( 18 px + env ( safe - area - inset - bottom )); overflow : auto ; z-index : 4 }
2026-10-20 22:29:58 +00:00
# tape header { display : flex ; justify-content : space-between ; align-items : center ; font : 600 12 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin-bottom : 12 px ; gap : 10 px }
# tape header input { flex : 1 ; background : transparent ; border : 0 ; border-bottom : 1 px solid var ( -- line ); font : 600 18 px var ( -- sans ); letter-spacing : 0 ; color : var ( -- ink ); outline : none ; padding : 4 px 0 ; min-width : 0 }
2026-10-21 04:33:54 +00:00
# tape . acts { display : flex ; gap : 8 px ; flex-wrap : wrap ; margin-bottom : 16 px ; justify-content : center }
# tapeCover { display : flex ; justify-content : center ; margin : 0 0 16 px }
# tapeCover canvas { width : min ( 50 vw , 220 px ); height : min ( 50 vw , 220 px ); border-radius : 18 px ; box-shadow : 0 30 px 60 px -30 px rgba ( 0 , 0 , 0 , .8 )}
2026-10-20 22:29:58 +00:00
# tape . acts button { padding : 9 px 16 px ; border-radius : 999 px ; border : 1 px solid var ( -- line ); font : 600 12 px var ( -- font ); letter-spacing : .14 em ; background : var ( -- card )}
2026-10-20 23:53:19 +00:00
# tape . acts button . go { background : var ( -- acc ); color : var ( -- onacc ); border-color : var ( -- acc )}
2026-10-20 22:29:58 +00:00
# tape . list { display : flex ; flex-direction : column ; gap : 8 px ; max-width : 640 px ; margin : 0 auto }
# tape . row { display : flex ; align-items : center ; gap : 12 px ; padding : 8 px ; border-radius : 14 px ; background : var ( -- card ); border : 1 px solid var ( -- line )}
# tape . row . on { border-color : var ( -- acc )}
# tape . row canvas { width : 52 px ; height : 52 px ; border-radius : 10 px ; flex : 0 0 52 px ; cursor : pointer }
# tape . row . m { flex : 1 ; min-width : 0 ; cursor : pointer }
# tape . row b { display : block ; font-size : 14 px ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# tape . row small { color : var ( -- dim ); font-size : 11 px ; font-family : var ( -- font )}
# tape . row . ctl { display : flex ; gap : 2 px }
# tape . row . ctl button { width : 32 px ; height : 32 px ; border-radius : 8 px ; color : var ( -- dim ); font-size : 14 px }
# tape . row . ctl button : hover { background : #1a1a20 ; color : var ( -- ink )}
# tape . empty { color : var ( -- dim ); text-align : center ; padding : 40 px 0 }
/* ---------- library ---------- */
2026-10-21 03:53:18 +00:00
# lib { position : fixed ; inset : 0 ; background : var ( -- bg ); padding : calc ( 18 px + env ( safe - area - inset - top )) 16 px calc ( 90 px + env ( safe - area - inset - bottom )); overflow : auto }
# lib . t . now . art { box-shadow : 0 0 0 2 px var ( -- ink )}
# lib . t . now . art :: after { content : "▶" ; position : absolute ; left : 50 % ; top : 50 % ; transform : translate ( -50 % , -50 % ); width : 34 px ; height : 34 px ; border-radius : 50 % ; background : var ( -- ink ); color : var ( -- bg ); display : grid ; place-items : center ; font-size : 13 px ; padding-left : 2 px }
# lib . t . now . paused . art :: after { content : "‖" ; padding-left : 0 }
# libNow { position : fixed ; left : 50 % ; bottom : calc ( 14 px + env ( safe - area - inset - bottom )); transform : translateX ( -50 % ); width : min ( 560 px , calc ( 100 % - 24 px )); display : flex ; align-items : center ; gap : 10 px ; background : var ( -- card ); border : 1 px solid var ( -- line ); border-radius : 16 px ; padding : 8 px 8 px 8 px 8 px ; z-index : 4 ; box-shadow : 0 20 px 50 px -20 px rgba ( 0 , 0 , 0 , .6 )}
# libNow canvas { width : 44 px ; height : 44 px ; border-radius : 9 px ; flex : none }
# libNow . t { flex : 1 ; min-width : 0 ; display : flex ; flex-direction : column ; gap : 2 px }
# libNow b { font : 600 13 px var ( -- sans ); white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# libNow small { font : 11 px var ( -- font ); letter-spacing : .14 em ; color : var ( -- dim ); white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# libNow button { width : 40 px ; height : 40 px ; border-radius : 50 % ; display : grid ; place-items : center ; color : var ( -- ink )}
# libNow button . go { background : var ( -- ink ); color : var ( -- bg )}
# libNow button . open { width : auto ; border-radius : 999 px ; padding : 0 12 px ; font : 600 11 px var ( -- font ); letter-spacing : .14 em ; border : 1 px solid var ( -- line )}
2026-10-20 22:29:58 +00:00
# lib header { display : flex ; justify-content : space-between ; align-items : center ; font : 600 12 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin-bottom : 16 px }
# lib . grid { display : grid ; grid-template-columns : repeat ( auto - fill , minmax ( 130 px , 1 fr )); gap : 14 px }
# lib h3 { font : 600 11 px var ( -- font ); letter-spacing : .2 em ; color : var ( -- dim ); margin : 22 px 0 10 px ; display : flex ; align-items : center ; gap : 10 px }
# lib h3 canvas { width : 22 px ; height : 22 px ; border-radius : 50 % }
# lib h3 small { font-weight : 400 ; letter-spacing : .08 em ; opacity : .7 }
# lib . t . on canvas { outline : 2 px solid var ( -- acc ); outline-offset : 2 px }
# lib . t . art { position : relative ; aspect-ratio : 1 ; border-radius : 12 px ; overflow : hidden ; background : var ( -- card )}
# lib . t . art canvas { width : 100 % ; height : 100 % ; display : block }
2026-10-20 23:53:19 +00:00
# lib . t . kept { position : absolute ; top : 6 px ; right : 6 px ; font : 600 9 px var ( -- font ); letter-spacing : .18 em ; color : var ( -- onacc ); background : var ( -- acc ); border-radius : 999 px ; padding : 3 px 7 px }
# lib . t . re { position : absolute ; left : 6 px ; bottom : 6 px ; font-size : 13 px ; letter-spacing : 1 px ; background : var ( -- veil2 ); border-radius : 8 px ; padding : 2 px 6 px ; max-width : calc ( 100 % - 12 px ); white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
2026-10-20 22:29:58 +00:00
# lib . t small { display : block ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# lib . t { cursor : pointer }
# lib . t canvas { width : 100 % ; aspect-ratio : 1 ; border-radius : 12 px ; display : block ; background : var ( -- card )}
# lib . t b { display : block ; font-size : 12 px ; margin-top : 6 px ; white-space : nowrap ; overflow : hidden ; text-overflow : ellipsis }
# lib . t small { color : var ( -- dim ); font-size : 11 px }
# lib . empty { color : var ( -- dim ); text-align : center ; padding : 40 px 0 }
# lib . cost { color : var ( -- dim ); font : 11 px var ( -- font ); margin-top : 24 px ; text-align : center }
</ style >
</ head >
< body >
<!-- ============ screen 1: the seed ============ -->
< section id = "seed" >
2026-10-20 23:40:58 +00:00
< div class = "wordmark" > g< span class = "ast" > *</ span > ndora</ div >
2026-10-20 22:29:58 +00:00
< div id = "nowRow" class = "hidden" >< div id = "now" title = "back to the station" >< canvas width = "56" height = "56" ></ canvas >< span id = "nowTitle" ></ span >< b > ▶ PLAYING</ b ></ div ></ div >
< form id = "seedForm" autocomplete = "off" >
< div id = "seedCover" >< canvas width = "240" height = "240" ></ canvas ></ div >
2026-10-20 23:40:58 +00:00
< input id = "seedIn" placeholder = "*" maxlength = "200" autofocus >
2026-10-20 22:29:58 +00:00
< div class = "hint" id = "seedHint" > a mood, a line, a place, a word</ div >
</ form >
< div id = "stations" class = "hidden" >< h3 > YOUR STATIONS</ h3 >< div class = "row" id = "stationRow" ></ div ></ div >
2026-10-21 03:40:36 +00:00
< div id = "keyRow" >< button class = "nav" id = "libBtn2" type = "button" > LIBRARY</ button >< button class = "nav" id = "tasteBtn2" type = "button" > TASTE</ button >< button class = "nav theme" type = "button" title = "light / dark" > LIGHT</ button >< button class = "nav reload" type = "button" title = "reload the app" > RELOAD</ button >< button id = "keyBtn" type = "button" class = "hidden" > shared budget · add a venice key</ button ></ div >
2026-10-20 22:29:58 +00:00
< div id = "keyBox" class = "hidden" >
< p > Optional. Without a key, gandora runs on a shared budget through inference.yolopreneur.lol (a few tracks a day per person). With your own Venice key it goes straight to api.venice.ai from this browser and the key goes nowhere else.</ p >
< input id = "keyIn" placeholder = "sk-…" spellcheck = "false" >
< label > DJ model < input id = "djIn" placeholder = "claude-haiku-5-5" spellcheck = "false" ></ label >
< div class = "row" >< button id = "keyCancel" type = "button" > cancel</ button >< button id = "keySave" class = "go" type = "button" > save</ button ></ div >
</ div >
</ section >
<!-- ============ screen 2: the station ============ -->
< section id = "player" class = "hidden" >
< div id = "top" >
< button id = "back" title = "new seed" > ◂ SEED</ button >
2026-10-20 23:53:19 +00:00
< div >< button id = "tasteBtn" > TASTE</ button > < button id = "libBtn" > LIBRARY</ button > < button class = "theme" title = "light / dark" > LIGHT</ button ></ div >
2026-10-20 22:29:58 +00:00
</ div >
< div id = "stage" >
< div class = "station" id = "stationName" title = "rename, look, reset or delete" ></ div >
< div id = "cover" >< div class = "card" >
< div class = "face" >< canvas width = "760" height = "760" ></ canvas >< div class = "veil" > COOKING</ div ></ div >
< div class = "face back" >< div class = "backTitle" > LYRICS</ div >< pre id = "lyrics" ></ pre ></ div >
2026-10-20 22:45:46 +00:00
</ div >< div id = "trail" title = "what this station has played · tap to jump" ></ div ></ div >
2026-10-20 22:29:58 +00:00
< div id = "meta" >< h1 id = "title" > …</ h1 ></ div >
2026-10-21 03:07:11 +00:00
< div id = "bar" title = "scrub" >< i ></ i >< b ></ b ></ div >
2026-10-21 03:04:23 +00:00
< div id = "meta2" >< p id = "lyric" title = "reply to the DJ" ></ p >< div id = "next" ></ div >< div id = "pills" >< button id = "lyricsBtn" > ⇄ LYRICS</ button >< button id = "keep" disabled > ➕ KEEP</ button >< button id = "share" disabled > ↗ SHARE</ button ></ div >< p id = "tags" class = "hidden" ></ p >< div id = "tagRow" ></ div >< div id = "tagList" ></ div ></ div >
2026-10-20 22:29:58 +00:00
< div id = "controls" >
< button id = "backTrack" title = "previous track" >
< svg viewBox = "0 0 24 24" >< path d = "M18 6l-8.5 6 8.5 6V6zM6 6v12h2V6H6z" /></ svg >
</ button >
< button id = "shuffle" title = "shuffle: mix the station's own library in" >
< svg viewBox = "0 0 24 24" >< path d = "M17 4h4v4l-1.5-1.5L16 10l-1.4-1.4 3.5-3.5L17 4zM3 6h4.5l3 3-1.4 1.4L7 8H3V6zm14.6 10.6 1.5-1.5v4h-4l1.5-1.5-3.1-3.1 1.4-1.4 2.7 2.7zM3 18v-2h4l9.5-9.5L18 8l-10 10H3z" /></ svg >
</ button >
< button id = "prev" title = "replay from the start" >
< svg viewBox = "0 0 24 24" >< path d = "M12 5V2L7 7l5 5V8c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6H4c0 4.4 3.6 8 8 8s8-3.6 8-8-3.6-8-8-8z" /></ svg >
</ button >
< button id = "play" title = "play / pause" >< svg id = "playIco" viewBox = "0 0 24 24" >< path d = "M8 5v14l11-7z" /></ svg ></ button >
< button id = "loop" title = "repeat this track" >
< svg viewBox = "0 0 24 24" >< path d = "M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z" /></ svg >
</ button >
< button id = "remix" title = "remix this track: same bones, your changes" >
< svg viewBox = "0 0 24 24" >< path d = "M12 3a9 9 0 1 0 9 9h-2a7 7 0 1 1-7-7v3l4-4-4-4v3zm0 5a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm0 2a2 2 0 1 1 0 4 2 2 0 0 1 0-4z" /></ svg >
</ button >
< button id = "skip" title = "next" >
< svg viewBox = "0 0 24 24" >< path d = "M6 18l8.5-6L6 6v12zm10-12v12h2V6h-2z" /></ svg >
</ button >
</ div >
</ div >
< div id = "react" >
< div id = "emojis" >
< button data-e = "🔥" title = "more of this" > 🔥</ button >
< button data-e = "❤️" title = "love it" > ❤️</ button >
< button data-e = "😴" title = "too sleepy" > 😴</ button >
< button data-e = "🌧️" title = "sadder" > 🌧️</ button >
< button data-e = "⚡" title = "faster, louder" > ⚡</ button >
< button data-e = "🎸" title = "more guitar" > 🎸</ button >
< button data-e = "🚫" title = "not this" > 🚫</ button >
< button id = "more" title = "more emoji" > + </ button >
</ div >
< div id = "picker" class = "hidden" ></ div >
< form id = "sayRow" >< input id = "say" placeholder = "or say it: less snare, sadder but faster…" maxlength = "240" >< button > send</ button ></ form >
</ div >
< div id = "toast" ></ div >
</ section >
< section id = "taste" class = "hidden" >
< header >< span > TASTE</ span >< button id = "tasteClose" > CLOSE</ button ></ header >
< div id = "tasteTabs" ></ div >
2026-10-21 00:27:27 +00:00
< div id = "tastePalette" ></ div >
2026-10-20 22:29:58 +00:00
< canvas id = "brainMap" width = "880" height = "400" ></ canvas >
< canvas id = "tasteMap" width = "900" height = "900" ></ canvas >
< div id = "tasteLegend" >< span > ← slow · tempo · fast →</ span >< span > ↑ energy</ span >< span > colour = key</ span >< span > ring = 🔥❤️ warm · 🚫⏭ cold</ span ></ div >
< div id = "tasteNote" ></ div >
</ section >
< section id = "tape" class = "hidden" >
< header >< span > TAPE</ span >< input id = "tapeName" maxlength = "60" >< button id = "tapeClose" > CLOSE</ button ></ header >
2026-10-21 04:33:54 +00:00
< div id = "tapeCover" >< canvas width = "480" height = "480" ></ canvas ></ div >
2026-10-21 04:46:31 +00:00
< div class = "acts" >< button class = "go" id = "tapePlay" > ▶ PLAY ALL</ button >< button id = "tapeLoop" > 🔁 REPEAT ALL</ button >< button id = "tapeShare" > ↗ SHARE</ button >< button id = "tapeMash" title = "one new track out of up to three of these" > ⚡ MASHUP</ button >< button id = "tapeDelete" > DELETE TAPE</ button ></ div >
2026-10-20 22:29:58 +00:00
< div class = "list" id = "tapeList" ></ div >
< div class = "empty hidden" id = "tapeEmpty" > Empty tape. Add tracks from the library with the + on a tile.</ div >
</ section >
< div id = "tapePick" class = "hidden" ></ div >
2026-10-21 03:40:36 +00:00
< button id = "upd" class = "hidden" type = "button" > NEW VERSION · RELOAD</ button >
2026-10-20 22:29:58 +00:00
< div id = "stationSheet" class = "hidden" ></ div >
< div id = "scrim" class = "hidden" ></ div >
< section id = "lib" class = "hidden" >
< header >< span > LIBRARY</ span >< button id = "libClose" > CLOSE</ button ></ header >
< div class = "grid" id = "libGrid" ></ div >
< div class = "empty hidden" id = "libEmpty" > Nothing played yet. Every track lands here; KEEP one to pin it for good.</ div >
< div class = "cost" id = "libCost" ></ div >
2026-10-21 08:24:31 +00:00
< div class = "cost" >< a href = "stats.html" title = "your numbers" style = "text-decoration:none;font-size:16px;opacity:.6;margin-right:10px" > 📈</ a >< a id = "llama" href = "webamp.html" title = "it really whips" style = "text-decoration:none;font-size:18px;opacity:.6" > 🦙</ a ></ div >
2026-10-21 03:53:18 +00:00
< div id = "libNow" class = "hidden" >< canvas width = "120" height = "120" ></ canvas >< div class = "t" >< b ></ b >< small ></ small ></ div >< button class = "go" id = "libPlay" title = "play / pause" >< svg viewBox = "0 0 24 24" style = "width:20px;height:20px;fill:currentColor" >< path d = "M8 5v14l11-7z" /></ svg ></ button >< button class = "open" id = "libOpen" title = "back to the player" > PLAYER ▸</ button ></ div >
2026-10-20 22:29:58 +00:00
</ section >
2026-10-21 22:31:34 +00:00
< script type = "module" src = "app.js" ></ script >
2026-10-21 22:18:36 +00:00
< script src = "brain.js" defer ></ script >
2026-10-20 22:29:58 +00:00
</ body >
</ html >