@@ -49,38 +49,64 @@ export function renderApp(root: HTMLElement): void {
4949 </li>` ,
5050 ) . join ( '' ) }
5151 </ul>
52+
53+ <!-- On the camera, not in the action bar: it is a control about the camera, the same
54+ argument that put the body-part chips here. Off, it would be a button that does
55+ nothing visible. -->
56+ <button class="stage__flip" id="flip" type="button" aria-label="Cambiar a cámara trasera">
57+ <svg viewBox="0 0 24 24" width="21" height="21" fill="none" stroke="currentColor"
58+ stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
59+ <path d="M4 9h3l1.6-2.2h6.8L17 9h3v10H4z" />
60+ <path d="M9.6 14.2a2.6 2.6 0 0 0 4.9.9M14.4 13a2.6 2.6 0 0 0-4.9-.9" />
61+ <path d="M9.4 10.6v1.5h1.5M14.6 15.6v-1.5h-1.5" />
62+ </svg>
63+ </button>
5264 </div>
5365
5466 <div class="transcript" id="transcript" aria-live="polite"></div>
5567
5668 <p class="status" id="status" role="status"></p>
5769
58- <div class="actions actions--bar">
59- <button class="button" id="toggle" type="button">Empezar a leer</button>
60- <button class="button button--quiet" id="undo" type="button">Borrar último</button>
61- <button class="button button--quiet" id="clear" type="button">Limpiar</button>
62- <button class="button button--quiet" id="flip" type="button">Cámara trasera</button>
70+ <!-- One row: the four buttons this replaces wrapped to two on a 390 px phone and to
71+ three at 320 px, and two of them acted on a transcript that did not exist yet. -->
72+ <div class="actions actions--bar" id="actions">
73+ <button class="button button--grow" id="toggle" type="button">Empezar a leer</button>
74+ <div class="actions__edit" id="edit" hidden>
75+ <button class="button button--quiet" id="undo" type="button">Deshacer</button>
76+ <button class="button button--quiet" id="clear" type="button">Limpiar</button>
77+ </div>
6378 </div>
6479 </div>
6580
6681 <div id="teach"></div>
6782 <div id="storage"></div>
6883 <div id="diagnostics"></div>
6984
70- <section class="card">
71- <h2 class="card__title">Qué reconoce, y con qué fiabilidad</h2>
72- <p class="card__body">
73- <strong>Vocabulario LSE:</strong> 238 signos de ámbito sanitario, entrenados sobre
74- SWL-LSE. Acierta el signo exacto en torno a <strong>2 de cada 3 veces</strong>, y está
75- entre sus tres primeras opciones en <strong>8 de cada 10</strong>. Es un modelo real,
76- no infalible: revisa el texto antes de darlo por bueno.
77- </p>
78- <p class="card__body" style="margin-top: 10px">
79- <strong>Alfabeto dactilológico:</strong> para deletrear cualquier palabra fuera de ese
80- vocabulario. Todavía no distingue <strong>${ UNSUPPORTED_LETTERS . join ( ', ' ) } </strong>:
81- unas se trazan con movimiento y otras dependen de la orientación de la palma.
82- </p>
83- </section>
85+ <!--
86+ The reliability figure lives in the summary, not behind it. Folding this card away
87+ saves 363 px, but what made it worth having was saying out loud that the model is
88+ fallible — so that sentence has to survive the fold.
89+ -->
90+ <details class="card">
91+ <summary class="card__summary">
92+ <h2 class="card__title">Qué reconoce</h2>
93+ <span class="card__note">238 signos LSE y el alfabeto — acierta 2 de cada 3 veces</span>
94+ </summary>
95+
96+ <div class="card__content">
97+ <p class="card__body">
98+ <strong>Vocabulario LSE:</strong> 238 signos de ámbito sanitario, entrenados sobre
99+ SWL-LSE. Acierta el signo exacto en torno a <strong>2 de cada 3 veces</strong>, y está
100+ entre sus tres primeras opciones en <strong>8 de cada 10</strong>. Es un modelo real,
101+ no infalible: revisa el texto antes de darlo por bueno.
102+ </p>
103+ <p class="card__body" style="margin-top: 10px">
104+ <strong>Alfabeto dactilológico:</strong> para deletrear cualquier palabra fuera de ese
105+ vocabulario. Todavía no distingue <strong>${ UNSUPPORTED_LETTERS . join ( ', ' ) } </strong>:
106+ unas se trazan con movimiento y otras dependen de la orientación de la palma.
107+ </p>
108+ </div>
109+ </details>
84110
85111 <p class="footnote">
86112 v${ __APP_VERSION__ } · Vocabulario LSE sobre
@@ -95,6 +121,15 @@ export function renderApp(root: HTMLElement): void {
95121 const transcriptEl = must < HTMLElement > ( root , '#transcript' ) ;
96122 const toggle = must < HTMLButtonElement > ( root , '#toggle' ) ;
97123 const status = must < HTMLElement > ( root , '#status' ) ;
124+ const edit = must < HTMLElement > ( root , '#edit' ) ;
125+
126+ // In camera mode the bar is fixed, so the page reserves its height at the bottom. Measured
127+ // rather than hard-coded: it grows a second row on a narrow phone once the transcript has
128+ // text, and a stale constant would leave the last panel hidden underneath it.
129+ const actions = must < HTMLElement > ( root , '#actions' ) ;
130+ new ResizeObserver ( ( ) => {
131+ root . style . setProperty ( '--bar' , `${ actions . offsetHeight } px` ) ;
132+ } ) . observe ( actions ) ;
98133
99134 const parts = must < HTMLElement > ( root , '#parts' ) ;
100135 const container = new Container ( video ) ;
@@ -114,6 +149,9 @@ export function renderApp(root: HTMLElement): void {
114149
115150 const render = ( text : string , candidates : readonly { gloss : { text : string } } [ ] ) => {
116151 transcriptEl . textContent = text ;
152+ // Undo and clear exist only once there is something to undo or clear. Every path that
153+ // changes the transcript comes through here, so this is the single place that decides.
154+ edit . hidden = text . length === 0 ;
117155 const top = candidates [ 0 ] ?. gloss . text ;
118156 hint . hidden = ! top ;
119157 if ( top ) hint . textContent = top . toUpperCase ( ) ;
@@ -204,7 +242,11 @@ export function renderApp(root: HTMLElement): void {
204242 const mirrored = next === 'user' ;
205243 video . classList . toggle ( 'is-flipped' , ! mirrored ) ;
206244 overlayCanvas . classList . toggle ( 'is-flipped' , ! mirrored ) ;
207- flip . textContent = mirrored ? 'Cámara trasera' : 'Cámara frontal' ;
245+ // The icon says "switch", so the name has to say which way — it is the only label.
246+ flip . setAttribute (
247+ 'aria-label' ,
248+ mirrored ? 'Cambiar a cámara trasera' : 'Cambiar a cámara frontal' ,
249+ ) ;
208250 status . textContent = mirrored
209251 ? 'Cámara frontal: para signar tú.'
210252 : 'Cámara trasera: para leer a quien tienes delante.' ;
0 commit comments