File size: 23,660 Bytes
045c87e
 
 
 
 
d27cb1d
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d27cb1d
045c87e
 
d27cb1d
 
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68877f9
 
be60cf4
68877f9
be60cf4
d27cb1d
 
 
 
be60cf4
d27cb1d
 
 
 
be60cf4
d27cb1d
 
be60cf4
d27cb1d
 
 
 
 
 
be60cf4
d27cb1d
 
 
 
68877f9
 
045c87e
 
 
 
 
 
 
d27cb1d
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d27cb1d
 
 
 
 
045c87e
 
 
 
 
d27cb1d
 
 
 
 
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e97569f
045c87e
 
 
 
 
 
 
 
 
 
e97569f
 
 
 
 
045c87e
 
 
 
 
d27cb1d
045c87e
d27cb1d
045c87e
 
 
 
 
 
d27cb1d
045c87e
 
d27cb1d
 
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
e97569f
 
 
045c87e
 
 
 
 
e97569f
 
 
 
 
 
 
045c87e
 
 
 
 
 
 
 
 
e97569f
 
 
045c87e
 
 
 
 
 
e97569f
 
 
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d27cb1d
045c87e
 
 
 
 
 
d27cb1d
 
 
045c87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="SoftChart turns music into playable, musical Taiko charts with an instant drum-mix audition.">
  <meta name="theme-color" content="#f6f3ec">
  <title>SoftChart — AI Taiko Chart Studio</title>
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Ccircle cx='32' cy='32' r='25' fill='%23e14b3f'/%3E%3Ccircle cx='32' cy='32' r='18' fill='%23fff8ed'/%3E%3Ccircle cx='32' cy='32' r='9' fill='%23e14b3f'/%3E%3C/svg%3E">
  <link rel="stylesheet" href="./static/app.css">
  <script type="module" src="./static/app.js"></script>
</head>
<body>
  <a class="skip-link" href="#main-content">Skip to main content</a>

  <header class="site-header">
    <a class="brand" href="./" aria-label="SoftChart home">
      <svg class="brand-mark" viewBox="0 0 48 48" aria-hidden="true">
        <circle cx="24" cy="24" r="20" fill="currentColor" opacity=".12"/>
        <circle cx="24" cy="24" r="14.5" fill="none" stroke="currentColor" stroke-width="2.5"/>
        <circle cx="24" cy="24" r="5.5" fill="currentColor"/>
        <path d="M8.5 24h5M34.5 24h5" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/>
      </svg>
      <span>SoftChart</span>
      <span class="brand-edition">STUDIO</span>
    </a>
    <div class="connection" id="connection" role="status" aria-live="polite">
      <span class="connection-dot" aria-hidden="true"></span>
      <span id="connection-label">Connecting</span>
    </div>
  </header>

  <main id="main-content">
    <section class="hero" aria-labelledby="hero-title">
      <div class="hero-copy">
        <h1 id="hero-title">Music in.<br><em>Taiko out.</em></h1>
        <p class="hero-lede">Upload a track. Get a playable TJA. Hear it instantly.</p>
      </div>
      <ul class="hero-features" aria-label="Key features">
        <li><strong>Beat-locked</strong></li>
        <li><strong>Structure-aware</strong></li>
        <li><strong>Instant audition</strong></li>
      </ul>
    </section>

    <section class="studio-grid" aria-label="Taiko chart generation studio">
      <form class="panel setup-panel" id="generate-form">
        <div class="panel-heading">
          <h2>Create</h2>
        </div>

        <div class="form-section">
          <div class="label-row">
            <label id="upload-label" for="file-input">Music</label>
            <span class="field-hint">MP3, WAV, FLAC, OGG</span>
          </div>
          <input id="file-input" type="file" accept=".mp3,.wav,.flac,.ogg" tabindex="-1" aria-hidden="true">
          <div class="dropzone" id="dropzone" role="button" tabindex="0" aria-labelledby="upload-label upload-help">
            <svg class="upload-icon" viewBox="0 0 56 56" aria-hidden="true">
              <circle cx="28" cy="28" r="27" fill="currentColor" opacity=".08"/>
              <path d="M28 36V18m0 0-7 7m7-7 7 7M17 38.5h22" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
            <span class="drop-title">Drop audio</span>
            <span class="drop-subtitle" id="upload-help">or browse</span>
          </div>

          <div class="source-card" id="source-card" hidden>
            <div class="source-info">
              <span class="file-badge" aria-hidden="true"></span>
              <span class="file-copy">
                <strong id="file-name"></strong>
                <span id="file-meta"></span>
              </span>
              <button type="button" class="icon-button" id="remove-file" aria-label="Remove music">
                <svg viewBox="0 0 20 20" aria-hidden="true"><path d="m6 6 8 8m0-8-8 8"/></svg>
              </button>
            </div>
            <audio id="source-audio" controls preload="metadata">Your browser does not support audio playback.</audio>
          </div>
          <p class="field-error" id="file-error" role="alert" hidden></p>
        </div>

        <div class="form-section split-rule">
          <div class="field-group">
            <div class="label-row">
              <label for="model">Model</label>
            </div>
            <select id="model" class="model-select">
              <optgroup label="v1.7 — richest patterns (recommended)">
                <option value="v1.7" selected>v1.7 · 7.9M — best quality</option>
                <option value="v1.7-small">v1.7-small · 3.6M</option>
                <option value="v1.7-tiny">v1.7-tiny · 1.4M — fastest</option>
              </optgroup>
              <optgroup label="v1.6 — scaling ladder">
                <option value="v1.6">v1.6 · 7.9M</option>
                <option value="v1.6-small">v1.6-small · 3.6M</option>
                <option value="v1.6-tiny">v1.6-tiny · 1.4M</option>
              </optgroup>
              <optgroup label="v1.5 — single model">
                <option value="v1.5">v1.5 · 7.9M</option>
              </optgroup>
              <!-- BarScript is the experimental preview line, served from its
                   own release package. The group stays disabled and hidden
                   until /api/capabilities reports the package resolved; the
                   label below is a placeholder that app.js overwrites with the
                   group name the API returns. -->
              <optgroup label="BarScript — EXPERIMENTAL PREVIEW, under evaluation" id="experimental-models" hidden disabled></optgroup>
            </select>
            <div class="model-notices" id="model-notices" hidden>
              <strong id="model-notices-title">Known limitations</strong>
              <ul id="model-notices-list"></ul>
            </div>
          </div>

          <fieldset class="field-group">
            <legend>Difficulty</legend>
            <div class="course-selector">
              <label><input type="radio" name="course" value="easy"><span><small>E</small>Easy</span></label>
              <label><input type="radio" name="course" value="normal"><span><small>N</small>Normal</span></label>
              <label><input type="radio" name="course" value="hard"><span><small>H</small>Hard</span></label>
              <label><input type="radio" name="course" value="oni" checked><span><small>O</small>Oni</span></label>
              <label><input type="radio" name="course" value="ura"><span><small>U</small>Ura</span></label>
            </div>
          </fieldset>

          <div class="two-column-fields">
            <div class="field-group">
              <div class="label-row">
                <label for="level">Star level</label>
                <output class="value-output" id="level-output" for="level">★ 9</output>
              </div>
              <input class="range" id="level" type="range" min="1" max="10" value="9" step="1">
            </div>
            <div class="field-group">
              <div class="label-row">
                <label for="bpm">BPM</label>
              </div>
              <div class="input-with-unit">
                <input id="bpm" type="number" min="0" max="400" step="0.1" inputmode="decimal" placeholder="AUTO">
              </div>
            </div>
          </div>
        </div>

        <details class="advanced split-rule" id="advanced-settings">
          <summary>
            <span><strong>Advanced</strong></span>
            <svg viewBox="0 0 20 20" aria-hidden="true"><path d="m6 8 4 4 4-4"/></svg>
          </summary>
          <div class="advanced-body">
            <div class="toggle-list">
              <label class="toggle-card compact">
                <span><strong>Structure</strong></span>
                <input id="auto-plan" type="checkbox" checked>
                <span class="switch" aria-hidden="true"></span>
              </label>
              <label class="toggle-card compact">
                <span><strong>Beat grid</strong></span>
                <input id="use-beat" type="checkbox" checked>
                <span class="switch" aria-hidden="true"></span>
              </label>
              <label class="toggle-card compact">
                <span><strong>AI planner</strong></span>
                <input id="use-planner" type="checkbox" checked>
                <span class="switch" aria-hidden="true"></span>
              </label>
            </div>
            <label class="toggle-card compact">
              <span><strong>Creative sampling</strong></span>
              <input id="sampling" type="checkbox">
              <span class="switch" aria-hidden="true"></span>
            </label>
            <div class="parameter-grid" id="sampling-parameters" aria-disabled="true">
              <div class="field-group">
                <div class="label-row">
                  <label for="temperature">Variation</label>
                  <output class="value-output" id="temperature-output" for="temperature">0.80</output>
                </div>
                <input class="range" id="temperature" type="range" min="0.2" max="1.2" value="0.8" step="0.05" disabled>
              </div>
              <div class="field-group">
                <div class="label-row">
                  <label for="top-p">Candidate range</label>
                  <output class="value-output" id="top-p-output" for="top-p">0.90</output>
                </div>
                <input class="range" id="top-p" type="range" min="0.5" max="1" value="0.9" step="0.01" disabled>
              </div>
            </div>
            <div class="field-group drum-volume-field">
              <div class="label-row">
                <label for="drum-volume">Taiko preview volume</label>
                <output class="value-output" id="drum-volume-output" for="drum-volume">80%</output>
              </div>
              <input class="range" id="drum-volume" type="range" min="0" max="1.5" value="0.8" step="0.05">
            </div>
          </div>
        </details>

        <button class="primary-button" id="generate-button" type="submit">
          <span>Generate chart</span>
          <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14m-5-5 5 5-5 5"/></svg>
        </button>
      </form>

      <div class="output-column">
        <section class="panel process-panel" id="process-panel" aria-labelledby="process-title">
          <div class="panel-heading">
            <h2 id="process-title">Progress</h2>
            <span class="status-chip is-idle" id="status-chip">Ready</span>
          </div>

          <div class="stage-canvas" id="stage-canvas">
            <div class="stage-visual is-active" data-stage="loading" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <g class="loading-grid" stroke="currentColor" opacity=".07"><path d="M0 56h520M0 112h520M0 168h520M0 224h520M104 0v280M208 0v280M312 0v280M416 0v280"/></g>
                <g transform="translate(260 140)">
                  <circle class="loading-orbit orbit-a" r="68" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="10 12" opacity=".25"/>
                  <circle class="loading-orbit orbit-b" r="48" fill="none" stroke="currentColor" stroke-width="2" stroke-dasharray="3 9" opacity=".4"/>
                  <circle r="27" fill="currentColor" opacity=".1"/>
                  <circle class="loading-core" r="12" fill="currentColor"/>
                  <circle class="loading-dot dot-a" cx="0" cy="-68" r="5" fill="currentColor"/>
                  <circle class="loading-dot dot-b" cx="48" cy="0" r="4" fill="currentColor"/>
                </g>
              </svg>
            </div>

            <div class="stage-visual" data-stage="audio" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <path class="audio-line-back" d="M24 140C54 140 61 112 84 112s28 68 51 68 31-115 55-115 30 150 53 150 33-104 57-104 30 59 55 59 29-76 52-76 29 46 49 46h40" fill="none" stroke="currentColor" stroke-width="2" opacity=".14"/>
                <path class="audio-line" pathLength="1" d="M24 140C54 140 61 112 84 112s28 68 51 68 31-115 55-115 30 150 53 150 33-104 57-104 30 59 55 59 29-76 52-76 29 46 49 46h40" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
                <g class="audio-playhead"><path d="M0 48v184" stroke="currentColor" stroke-width="1.5" opacity=".45"/><circle cy="140" r="7" fill="currentColor"/></g>
                <g fill="currentColor" opacity=".22"><circle cx="84" cy="112" r="4"/><circle cx="190" cy="65" r="4"/><circle cx="300" cy="111" r="4"/><circle cx="407" cy="94" r="4"/></g>
              </svg>
            </div>

            <div class="stage-visual" data-stage="beat" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <path d="M72 205h376" stroke="currentColor" stroke-width="2" opacity=".14"/>
                <g class="beat-ticks" stroke="currentColor" opacity=".26"><path d="M100 192v26M180 192v26M260 185v40M340 192v26M420 192v26"/></g>
                <path class="beat-arc" d="M182 199a78 78 0 0 1 156 0" fill="none" stroke="currentColor" stroke-width="3" opacity=".18"/>
                <g class="stage-anchor" transform="translate(260 199)">
                  <g class="metronome">
                    <path d="M0 0 54-118" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
                    <circle cx="54" cy="-118" r="10" fill="currentColor"/>
                  </g>
                </g>
                <g class="beat-pulses" fill="currentColor"><circle cx="100" cy="205" r="7"/><circle cx="180" cy="205" r="7"/><circle cx="260" cy="205" r="10"/><circle cx="340" cy="205" r="7"/><circle cx="420" cy="205" r="7"/></g>
              </svg>
            </div>

            <div class="stage-visual" data-stage="plan" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <g class="plan-bars">
                  <rect x="42" y="166" width="45" height="60" rx="5"/>
                  <rect x="96" y="143" width="45" height="83" rx="5"/>
                  <rect x="150" y="153" width="45" height="73" rx="5"/>
                  <rect x="204" y="105" width="45" height="121" rx="5"/>
                  <rect x="258" y="127" width="45" height="99" rx="5"/>
                  <rect x="312" y="83" width="45" height="143" rx="5"/>
                  <rect x="366" y="53" width="45" height="173" rx="5" class="plan-climax"/>
                  <rect x="420" y="118" width="45" height="108" rx="5"/>
                </g>
                <path class="plan-path" d="M64 158 118 135l54 10 54-49 54 22 54-43 54-31 54 65" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
                <circle class="plan-cursor" cx="64" cy="158" r="7" fill="currentColor"/>
              </svg>
            </div>

            <div class="stage-visual" data-stage="generate" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <path d="M20 94h480M20 186h480" stroke="currentColor" stroke-width="2" opacity=".13"/>
                <path d="M20 140h480" stroke="currentColor" stroke-width="1.5" stroke-dasharray="5 10" opacity=".13"/>
                <g class="note-stream">
                  <g class="note note-1"><circle r="20"/><circle r="11" fill="#fff8ed" opacity=".2"/></g>
                  <g class="note note-2"><circle r="20" class="ka"/><circle r="11" fill="#fff8ed" opacity=".2"/></g>
                  <g class="note note-3"><circle r="27"/><circle r="15" fill="#fff8ed" opacity=".2"/></g>
                  <g class="note note-4"><circle r="20" class="ka"/><circle r="11" fill="#fff8ed" opacity=".2"/></g>
                  <g class="note note-5"><circle r="20"/><circle r="11" fill="#fff8ed" opacity=".2"/></g>
                </g>
                <g class="stage-anchor" transform="translate(93 140)">
                  <g class="generate-gate"><circle r="42" fill="none" stroke="currentColor" stroke-width="2" opacity=".22"/><circle r="31" fill="none" stroke="currentColor" stroke-width="2" opacity=".38"/><path d="M0-49v98" stroke="currentColor" stroke-width="3"/></g>
                </g>
              </svg>
            </div>

            <div class="stage-visual" data-stage="export" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <g class="stage-anchor" transform="translate(175 34)">
                  <g class="export-sheet">
                    <path d="M8 0h126l42 42v189H8z" fill="currentColor" opacity=".08"/>
                    <path d="M134 0v42h42" fill="none" stroke="currentColor" stroke-width="2" opacity=".35"/>
                    <g class="export-lines" stroke="currentColor" stroke-width="3" stroke-linecap="round"><path d="M38 77h108M38 104h78M38 131h108M38 158h91"/></g>
                    <g class="export-dots" fill="currentColor"><circle cx="48" cy="194" r="8"/><circle cx="76" cy="194" r="8" class="ka"/><circle cx="104" cy="194" r="8"/><circle cx="132" cy="194" r="8" class="ka"/></g>
                  </g>
                </g>
                <path class="export-arrow" d="M366 91v105m0 0-20-20m20 20 20-20" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </div>

            <div class="stage-visual" data-stage="mix" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <g class="mix-wave wave-top" fill="none" stroke="currentColor" stroke-width="3"><path d="M35 93c26 0 26-32 52-32s26 64 52 64 26-64 52-64 26 64 52 64 26-64 52-64 26 64 52 64 26-64 52-64 26 32 52 32h46"/></g>
                <g class="mix-wave wave-bottom" fill="none" stroke="currentColor" stroke-width="3"><path d="M35 187c34 0 34-22 68-22s34 44 68 44 34-44 68-44 34 44 68 44 34-44 68-44 34 22 68 22h72"/></g>
                <g class="stage-anchor" transform="translate(260 140)">
                  <g class="mix-drum"><circle r="48" fill="#fff8ed" stroke="currentColor" stroke-width="4"/><circle r="35" fill="currentColor" opacity=".12"/><circle r="13" fill="currentColor"/></g>
                </g>
                <g class="mix-spark" fill="currentColor"><circle cx="201" cy="119" r="5"/><circle cx="319" cy="161" r="5"/><circle cx="312" cy="100" r="3"/><circle cx="211" cy="177" r="3"/></g>
              </svg>
            </div>

            <div class="stage-visual" data-stage="complete" aria-hidden="true">
              <svg viewBox="0 0 520 280">
                <g class="stage-anchor" transform="translate(260 140)">
                  <g class="complete-rings"><circle r="81" fill="none" stroke="currentColor" stroke-width="2" opacity=".12"/><circle r="64" fill="currentColor" opacity=".1"/><circle r="47" fill="currentColor"/></g>
                </g>
                <path class="complete-check" d="m235 140 17 17 35-38" fill="none" stroke="#fff8ed" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
                <g class="confetti" fill="currentColor"><rect x="165" y="65" width="7" height="18" rx="3" transform="rotate(-35 168 74)"/><rect x="350" y="70" width="7" height="18" rx="3" transform="rotate(28 353 79)"/><circle cx="177" cy="201" r="6"/><circle cx="350" cy="207" r="5"/><path d="m387 139 12-7-1 14z"/><path d="m126 132 13-5-3 14z"/></g>
              </svg>
            </div>
          </div>

          <div class="process-copy">
            <div class="process-title-row">
              <div>
                <p class="process-eyebrow" id="stage-eyebrow">STANDBY</p>
                <h3 id="stage-title">Waiting for your music</h3>
              </div>
              <strong class="progress-number" id="progress-number">0%</strong>
            </div>
            <p class="sr-only" id="stage-detail">Upload a track to begin.</p>
            <div class="progress-track" id="progress-track" role="progressbar" aria-label="Chart generation progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
              <span id="progress-bar"></span>
            </div>
            <div class="queue-row">
              <span id="queue-label">Not started</span>
              <button class="text-button" type="button" id="cancel-button" hidden>Cancel generation</button>
            </div>
          </div>
          <div class="process-error" id="process-error" role="alert" hidden>
            <strong>Generation incomplete</strong>
            <span id="process-error-message"></span>
            <button class="text-button" type="button" id="retry-button">Try again</button>
          </div>
        </section>

        <section class="panel result-panel" id="result-panel" aria-labelledby="result-title" hidden>
          <div class="panel-heading">
            <h2 id="result-title">Ready</h2>
          </div>

          <div class="audition-card" id="audition-card">
            <div class="audition-heading">
              <span class="audition-icon" aria-hidden="true">
                <svg viewBox="0 0 32 32"><circle cx="16" cy="16" r="12"/><path d="m13 10 9 6-9 6z"/></svg>
              </span>
              <span><strong>Taiko audition</strong></span>
            </div>
            <audio id="result-audio" controls preload="metadata">Your browser does not support audio playback.</audio>
          </div>

          <dl class="metrics-grid" id="metrics-grid" aria-label="Generation summary"></dl>

          <div class="preview-block">
            <div class="preview-tabs" role="tablist" aria-label="Preview type">
              <button id="chart-tab" class="preview-tab is-active" type="button" role="tab" aria-selected="true" aria-controls="chart-preview">Chart preview</button>
              <button id="plan-tab" class="preview-tab" type="button" role="tab" aria-selected="false" aria-controls="plan-preview" tabindex="-1">Song structure</button>
            </div>
            <div class="preview-frame">
              <div id="chart-preview" role="tabpanel" aria-labelledby="chart-tab">
                <img id="chart-image" alt="Preview of the generated Taiko chart">
                <p class="preview-empty" id="chart-empty" hidden>No chart preview is available.</p>
              </div>
              <div id="plan-preview" role="tabpanel" aria-labelledby="plan-tab" hidden>
                <img id="plan-image" alt="Song energy and chart section plan">
                <p class="preview-empty" id="plan-empty" hidden>No song structure preview is available.</p>
              </div>
            </div>
          </div>

          <div class="download-group">
            <a class="download-button primary-download" id="download-tja" href="#" download hidden>
              <span><strong>Download TJA</strong></span>
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v11m0 0 4-4m-4 4-4-4M5 20h14"/></svg>
            </a>
            <a class="download-button" id="download-audio" href="#" download hidden>
              <span><strong>Download audio</strong></span>
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v11m0 0 4-4m-4 4-4-4M5 20h14"/></svg>
            </a>
          </div>

          <button class="secondary-button" id="new-chart-button" type="button">Create another chart</button>
        </section>
      </div>
    </section>
  </main>

  <footer>
    <span>SoftChart</span>
    <a href="https://github.com/JacobLinCool/SoftChart" target="_blank" rel="noreferrer">GitHub <span aria-hidden="true"></span></a>
  </footer>

  <div class="sr-only" id="announcer" aria-live="assertive" aria-atomic="true"></div>
</body>
</html>