<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Video requirements for the zConnect Gallery</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root {
--ink: #000000;
--paper: #FFFFFF;
--lime: #AFF44A;
--lav: #D5B3FF;
--mist: #F4F4F1;
--rule: #E3E3DF;
--muted: #5C5C58;
--red: #C9352B;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--paper); }
body {
font-family: "Inter Tight", "Helvetica Neue", Arial, sans-serif;
color: var(--ink);
font-size: 17px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
.kb {
max-width: 920px;
margin: 0 auto;
padding: 40px 24px 64px;
}
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }
a { color: var(--ink); text-decoration-thickness: 2px; text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--lime); }
:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; border-radius: 2px; }
code {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.92em;
background: var(--mist);
padding: 2px 6px;
border-radius: 4px;
}
/* ---------- Hero ---------- */
.hero {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
gap: clamp(24px, 5%, 48px);
padding-bottom: 56px;
}
.hero-copy { flex: 3 1 280px; min-width: 0; }
h1 {
font-size: clamp(38px, 6vw, 60px);
line-height: 1.02;
font-weight: 800;
letter-spacing: -0.025em;
margin: 0 0 28px;
}
h2 {
font-size: 30px;
line-height: 1.15;
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 20px;
}
h3 {
font-size: 21px;
line-height: 1.25;
font-weight: 700;
letter-spacing: -0.01em;
margin: 0 0 10px;
}
/* Checklist */
.specs { list-style: none; margin: 0; padding: 0; }
.specs li {
display: grid;
grid-template-columns: 28px 1fr;
gap: 14px;
align-items: start;
padding: 12px 0;
border-top: 1px solid var(--rule);
font-size: 19px;
font-weight: 500;
}
.specs li:last-child { border-bottom: 1px solid var(--rule); }
.specs li small {
display: block;
font-size: 15px;
font-weight: 400;
color: var(--muted);
margin-top: 2px;
}
.tick {
width: 28px; height: 28px;
background: var(--lime);
border-radius: 6px;
display: grid; place-items: center;
margin-top: 1px;
}
.tick svg { width: 16px; height: 16px; display: block; }
/* Device with a screen inside; the dimension lines measure the screen */
.frame-wrap {
flex: 1 1 220px;
min-width: 170px;
max-width: 300px;
}
.frame-box {
position: relative;
padding: 40px 48px 0 0;
}
.device {
position: relative;
background: #1A1A1A;
border-radius: 16px;
padding: 24px 11px 36px;
box-shadow: inset 0 0 0 1px #3A3A3A;
}
.device .cam {
position: absolute;
top: 9px; left: 50%;
width: 6px; height: 6px;
margin-left: -3px;
border-radius: 50%;
background: #050505;
box-shadow: inset 0 0 0 1px #4A4A4A;
}
.screen {
position: relative;
width: 100%;
aspect-ratio: 600 / 1024;
background: #000;
border-radius: 3px;
box-shadow: 0 0 0 1px #3A3A3A;
display: grid;
place-items: center;
}
.screen .play {
width: 64px; height: 64px;
border-radius: 50%;
background: var(--lime);
display: grid; place-items: center;
}
.screen .play svg { width: 22px; height: 22px; margin-left: 4px; }
.screen .fps {
position: absolute;
left: 14px; bottom: 12px;
color: var(--paper);
font-size: 13px;
font-weight: 500;
opacity: .85;
}
.screen .fps b { color: var(--lime); font-weight: 700; }
/* Dimension lines hang off the screen box, so they line up with the screen edges */
.dim-x, .dim-y {
position: absolute;
font-size: 13px;
font-weight: 600;
color: var(--ink);
}
.dim-x {
top: -66px; left: 0; right: 0;
height: 22px;
display: flex; align-items: center; justify-content: center;
}
.dim-x::before {
content: ""; position: absolute; left: 0; right: 0; top: 50%;
border-top: 1px solid var(--ink);
}
.dim-x::after {
content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 10px;
transform: translateY(-50%);
border-left: 1px solid var(--ink); border-right: 1px solid var(--ink);
}
.dim-x span, .dim-y span {
position: relative; z-index: 1;
background: var(--paper);
padding: 0 8px;
}
.dim-y {
top: 0; bottom: 0; right: -48px;
width: 22px;
display: flex; align-items: center; justify-content: center;
}
.dim-y::before {
content: ""; position: absolute; top: 0; bottom: 0; left: 50%;
border-left: 1px solid var(--ink);
}
.dim-y::after {
content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 10px;
transform: translateX(-50%);
border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink);
}
.dim-y span { writing-mode: vertical-rl; padding: 8px 0; }
.frame-caption {
font-size: 14px;
color: var(--muted);
margin-top: 12px;
padding-right: 48px;
}
/* ---------- Sections ---------- */
.sec {
padding: 48px 0 56px;
border-top: 2px solid var(--ink);
}
.sec:last-child { padding-bottom: 0; }
.prose { max-width: 40em; }
/* Error toast recreation */
.toast {
position: relative;
display: grid;
grid-template-columns: 34px 1fr;
gap: 18px;
align-items: center;
background: var(--red);
color: var(--paper);
border-radius: 10px;
padding: 26px 56px 26px 26px;
font-size: 19px;
line-height: 1.4;
max-width: 560px;
margin: 0 0 28px;
box-shadow: 0 6px 24px rgba(201,53,43,.22);
}
.toast .x {
width: 34px; height: 34px; border-radius: 50%;
background: var(--paper);
display: grid; place-items: center;
}
.toast .x svg { width: 16px; height: 16px; }
.toast .close {
position: absolute; right: 18px; top: 14px;
font-size: 22px; font-weight: 700; line-height: 1;
}
.good-news {
background: var(--lav);
border-radius: 12px;
padding: 20px 24px;
font-size: 18px;
font-weight: 500;
max-width: 40em;
margin: 0 0 28px;
}
/* File diagram */
.files {
display: grid;
gap: 28px;
margin: 36px 0 8px;
}
.file {
background: var(--mist);
border-radius: 14px;
padding: 22px 24px 20px;
}
.file h3 { font-size: 18px; margin-bottom: 14px; }
.bar {
position: relative;
display: flex;
gap: 3px;
height: 46px;
}
.seg {
display: grid; place-items: center;
font-size: 13px; font-weight: 600;
border-radius: 7px;
white-space: nowrap;
overflow: hidden;
}
.seg.video { flex: 1 1 auto; background: var(--ink); color: var(--paper); }
.seg.codec { flex: 0 0 var(--codec-w); background: var(--lime); color: var(--ink); }
.bar { --codec-w: 15%; }
/* Dashed box = the first 2 MB. Sized to the codec block so the two line up exactly. */
.scan {
position: absolute;
left: -7px; top: -8px; bottom: -8px;
width: calc(var(--codec-w) + 14px);
border: 2px dashed var(--ink);
border-radius: 10px;
pointer-events: none;
}
.scan-label {
display: flex;
align-items: center;
gap: 8px;
margin-top: 14px;
font-size: 14px;
color: var(--ink);
padding-left: 2px;
}
.scan-label::before {
content: "";
flex: 0 0 14px;
height: 0;
border-top: 2px dashed var(--ink);
}
.verdict {
margin-top: 12px;
font-size: 15px;
font-weight: 600;
}
.verdict.bad { color: var(--red); }
.verdict.ok { color: #2E7D1F; }
/* Fix options */
.options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
align-items: start;
gap: 24px;
margin-top: 28px;
}
.option {
min-width: 0;
border: 2px solid var(--ink);
border-radius: 16px;
padding: 26px 26px 24px;
}
.option .tag {
display: inline-block;
background: var(--lime);
font-size: 14px;
font-weight: 700;
padding: 4px 10px;
border-radius: 999px;
margin-bottom: 14px;
}
.option p { font-size: 16px; }
.names { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 8px; }
.names li {
border: 1.5px solid var(--ink);
border-radius: 999px;
padding: 5px 12px;
font-size: 14px;
font-weight: 600;
}
.names li em { font-style: normal; font-weight: 400; color: var(--muted); }
ol.steps { margin: 14px 0 0; padding: 0; list-style: none; counter-reset: step; }
ol.steps li {
counter-increment: step;
position: relative;
padding: 0 0 14px 38px;
font-size: 16px;
}
ol.steps li::before {
content: counter(step);
position: absolute; left: 0; top: 0;
width: 26px; height: 26px;
border-radius: 50%;
background: var(--ink); color: var(--lime);
font-size: 13px; font-weight: 700;
display: grid; place-items: center;
}
ol.steps li:last-child { padding-bottom: 0; }
pre {
margin: 10px 0 0;
background: var(--ink);
color: var(--paper);
border-radius: 10px;
padding: 14px 16px;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 13.5px;
line-height: 1.55;
overflow-x: auto;
white-space: pre-wrap;
overflow-wrap: break-word;
}
.dumps pre { font-size: 12.5px; white-space: pre; }
pre .hl { color: var(--lime); white-space: nowrap; }
pre .nb { white-space: nowrap; }
pre .c { color: #A9A9A4; }
.note {
font-size: 15px;
color: var(--muted);
margin-top: 12px;
}
/* Under the hood */
.dumps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 20px; margin-top: 20px; max-width: 720px; }
.dumps > div { min-width: 0; }
.dumps h4 { margin: 0 0 8px; font-size: 15px; font-weight: 600; }
/* ---------- Responsive ---------- */
@media (max-width: 700px) {
h2 { font-size: 26px; }
.toast { font-size: 17px; padding: 22px 48px 22px 20px; }
.bar { --codec-w: 26%; }
}
</style>
</head>
<body>
<div class="kb">
<!-- ================= HERO ================= -->
<div class="hero">
<div class="hero-copy">
<h1>Getting Your Video <span style="white-space:nowrap">Ziosk-Ready</span></h1>
<ul class="specs">
<li>
<span class="tick"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span>.MP4 file format<small>No .MOV, .AVI, or .WMV.</small></span>
</li>
<li>
<span class="tick"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span>H.264/AVC video codec</span>
</li>
<li>
<span class="tick"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span>24 frames per second</span>
</li>
<li>
<span class="tick"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span>600 x 1024 pixels<small>Portrait, like the tablet.</small></span>
</li>
<li>
<span class="tick"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span>Fast start turned on<small>The sneaky one. Details below.</small></span>
</li>
</ul>
</div>
<div class="frame-wrap" aria-hidden="true">
<div class="frame-box">
<div class="device">
<div class="cam"></div>
<div class="screen">
<div class="dim-x"><span>600 px</span></div>
<div class="dim-y"><span>1024 px</span></div>
<div class="play"><svg viewBox="0 0 24 24" fill="#000"><path d="M6 4l14 8-14 8z"/></svg></div>
<div class="fps"><b>24</b> fps</div>
</div>
</div>
</div>
<p class="frame-caption">600 x 1024 is the screen, not the whole device. Build it portrait from the start.</p>
</div>
</div>
<!-- ================= THE ERROR ================= -->
<div class="sec">
<h2>Seeing this error?</h2>
<div class="toast" role="img" aria-label="zConnect error: Unable to detect video codec. The video may be corrupted. Please use H.264/AVC codec.">
<span class="x"><svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8" stroke="#C9352B" stroke-width="2.6" stroke-linecap="round"/></svg></span>
<span>Unable to detect video codec. The video may be corrupted. Please use H.264/AVC codec.</span>
<span class="close" aria-hidden="true">×</span>
</div>
<div class="good-news">Good news: your video is almost certainly fine. Its codec information is just sitting in the wrong spot, and that takes about ten seconds to fix.</div>
<div class="prose">
<p>Every MP4 carries a small block of codec information, a table of contents that tells the player how to read the video. Some editors save it at the very end of the file. zConnect only checks the first 2 MB, so if it isn't there, the upload gets flagged as corrupted even though every frame is intact.</p>
<p>An export setting called <strong>fast start</strong> moves that table of contents to the front. Picture, sound, and quality stay exactly the same.</p>
</div>
<div class="files" aria-label="How zConnect reads an MP4 with and without fast start">
<div class="file">
<h3>Without fast start</h3>
<div class="bar">
<div class="seg video">Video</div>
<div class="seg codec">Codec info</div>
<div class="scan"></div>
</div>
<div class="scan-label">zConnect only reads this far (2 MB)</div>
<div class="verdict bad">Codec info never found. Error.</div>
</div>
<div class="file">
<h3>With fast start</h3>
<div class="bar">
<div class="seg codec">Codec info</div>
<div class="seg video">Video</div>
<div class="scan"></div>
</div>
<div class="scan-label">zConnect only reads this far (2 MB)</div>
<div class="verdict ok">Codec info found. Video uploads.</div>
</div>
</div>
</div>
<!-- ================= THE FIX ================= -->
<div class="sec">
<h2>How to turn on fast start</h2>
<div class="options">
<div class="option">
<span class="tag">Still have the project open?</span>
<h3>Re-export with fast start on</h3>
<p>Look for it in your export settings. Different tools call it different things:</p>
<ul class="names">
<li>Fast start</li>
<li>Web optimized <em>(HandBrake)</em></li>
<li>Optimize for streaming</li>
<li>Progressive download</li>
</ul>
<p class="note">Turn it on, export as .MP4 with H.264, done.</p>
</div>
<div class="option">
<span class="tag">Only have the finished .MP4?</span>
<h3>Fix the file you already have</h3>
<p>FFmpeg can move the codec information without touching the video.</p>
<ol class="steps">
<li>Download FFmpeg (free) from <a href="https://ffmpeg.org" rel="noopener">ffmpeg.org</a>.</li>
<li>Put <code>ffmpeg.exe</code> and your video in the same folder.</li>
<li>Open a command prompt in that folder and run:
<pre>ffmpeg -i <span class="hl">your-video.mp4</span> -c copy <span class="nb">-movflags +faststart</span> <span class="hl">your-video-fixed.mp4</span></pre>
</li>
<li>Upload <code>your-video-fixed.mp4</code> to the Gallery.</li>
</ol>
<p class="note">Swap in your own file names. Nothing gets re-encoded, so no quality loss.</p>
</div>
</div>
</div>
<!-- ================= UNDER THE HOOD ================= -->
<div class="sec">
<h2>Curious what's actually happening inside the file?</h2>
<div class="prose">
<p>MP4s are built from boxes. Codec info lives in <code>moov</code>, the video in <code>mdat</code>. Same file, before and after fast start:</p>
</div>
<div class="dumps">
<div>
<h4>Before</h4>
<pre>[ftyp] size=8+12
[wide] size=8+0
[mdat] size=8+6530124 <span class="c">video</span>
<span class="hl">[moov] size=8+6645 codec info</span></pre>
</div>
<div>
<h4>After fast start</h4>
<pre>[ftyp] size=8+24
<span class="hl">[moov] size=8+5619 codec info</span>
[free] size=8+0
[mdat] size=8+6464292 <span class="c">video</span></pre>
</div>
</div>
</div>
</div>
</body>
</html>