diff --git a/src/control.html b/src/control.html
index d1cec78..25c66de 100644
--- a/src/control.html
+++ b/src/control.html
@@ -252,6 +252,12 @@
+
+
diff --git a/src/control.js b/src/control.js
index c6a5773..eddcf1e 100644
--- a/src/control.js
+++ b/src/control.js
@@ -525,6 +525,7 @@ $('#video-blend').addEventListener('change', (e) => send({ type: 'videoBlend', v
$('#ticker-text').addEventListener('input', (e) => send({ type: 'tickerText', text: e.target.value.replace(/\s*\n\s*/g, ' · ') }));
$('#ticker-on').addEventListener('change', (e) => send({ type: 'tickerOn', on: e.target.checked }));
$('#ticker-pos').addEventListener('change', (e) => send({ type: 'tickerPos', pos: e.target.value }));
+$('#ticker-dir').addEventListener('change', (e) => send({ type: 'tickerDir', value: e.target.value }));
$('#ticker-speed').addEventListener('input', (e) => {
const m = parseFloat(e.target.value);
$('#ticker-speed-val').textContent = m.toFixed(1) + '×';
diff --git a/src/output.js b/src/output.js
index c40d3d7..5e0ad94 100644
--- a/src/output.js
+++ b/src/output.js
@@ -247,6 +247,7 @@ function setTickerText(t) { const h = buildLetters(t); tickCopies.forEach(c => c
function setTickerSpeed(m) { tickerTrack.style.setProperty('--ticker-dur', (18 / m) + 's'); }
function tickerLetters() { return tickerTrack.querySelectorAll('.tl'); }
ticker.classList.add('pos-bottom');
+ticker.classList.add('dir-h');
setTickerSpeed(1);
function hideHint() { $('#drop-hint').classList.add('hidden'); }
@@ -382,6 +383,10 @@ djv.onControl(async (m) => {
ticker.classList.add('pos-' + m.pos);
break;
case 'tickerSpeed': setTickerSpeed(m.mult); break;
+ case 'tickerDir':
+ ticker.classList.remove('dir-h', 'dir-vup', 'dir-vdown');
+ ticker.classList.add('dir-' + m.value);
+ break;
case 'tickerFont': tickerTrack.style.fontFamily = m.value; break;
case 'tickerSize': tickerTrack.style.fontSize = m.value + 'vh'; break;
case 'tickerWeight': tickerTrack.style.fontWeight = m.on ? '800' : '400'; break;
diff --git a/src/style.css b/src/style.css
index 6d4bc57..4d35eab 100644
--- a/src/style.css
+++ b/src/style.css
@@ -484,9 +484,38 @@ body.output { cursor: none; background: #000; }
font-weight: 800;
color: #fff;
text-transform: uppercase;
- animation: ticker-scroll var(--ticker-dur, 18s) linear infinite;
}
.tick-copy { display: inline-flex; padding: 0 2vw; }
+
+/* Horizontal scroll (default) */
+#ticker.dir-h #ticker-track {
+ animation: ticker-scroll var(--ticker-dur, 18s) linear infinite;
+}
+
+/* Vertical scroll: full-height column, the two copies stacked and scrolling */
+#ticker.dir-vup, #ticker.dir-vdown {
+ top: 0 !important;
+ bottom: 0 !important;
+ height: 100vh;
+ padding: 0;
+ background: none;
+ transform: none !important;
+}
+#ticker.dir-vup.show, #ticker.dir-vdown.show {
+ display: flex;
+ align-items: flex-start;
+ justify-content: center;
+}
+#ticker.dir-vup #ticker-track, #ticker.dir-vdown #ticker-track {
+ flex-direction: column;
+ align-items: center;
+ white-space: nowrap;
+}
+#ticker.dir-vup .tick-copy, #ticker.dir-vdown .tick-copy { padding: 32vh 0; }
+#ticker.dir-vup #ticker-track { animation: ticker-scroll-vup var(--ticker-dur, 18s) linear infinite; }
+#ticker.dir-vdown #ticker-track { animation: ticker-scroll-vdown var(--ticker-dur, 18s) linear infinite; }
+@keyframes ticker-scroll-vup { from { transform: translateY(0); } to { transform: translateY(-50%); } }
+@keyframes ticker-scroll-vdown { from { transform: translateY(-50%); } to { transform: translateY(0); } }
.tl {
display: inline-block;
white-space: pre;